Skip to content

Commit 256fde5

Browse files
committed
Merge branch 'master' into add-confdb-handler-devicemgmt
2 parents 981450e + d7c520b commit 256fde5

175 files changed

Lines changed: 7592 additions & 858 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/ci-test.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,6 @@ jobs:
160160
matrix:
161161
build-config:
162162
- { system: "ubuntu-26.04-64", runs-on: '["ubuntu-latest"]', os: ubuntu, os-version: '26.04' }
163-
- { system: "ubuntu-25.10-64", runs-on: '["ubuntu-latest"]', os: ubuntu, os-version: '25.10' }
164163
- { system: "ubuntu-25.04-64", runs-on: '["ubuntu-latest"]', os: ubuntu, os-version: '25.04' }
165164
- { system: "ubuntu-24.04-64", runs-on: '["ubuntu-latest"]', os: ubuntu, os-version: '24.04' }
166165
- { system: "ubuntu-22.04-64", runs-on: '["ubuntu-latest"]', os: ubuntu, os-version: '22.04' }

.github/workflows/data-fundamental-systems.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
},
2727
{
2828
"runs-on": "['self-hosted', 'spread-enabled']",
29-
"group": "ubuntu-daily",
29+
"group": "ubuntu-resolute",
3030
"backend": "openstack",
3131
"systems": "ubuntu-26.04-64",
3232
"tasks": "tests/...",

.github/workflows/data-non-fundamental-systems.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,15 +116,15 @@
116116
"runs-on": "['self-hosted', 'spread-enabled']",
117117
"group": "ubuntu-deb",
118118
"backend": "openstack",
119-
"systems": "ubuntu-24.04-64",
119+
"systems": "ubuntu-24.04-64 ubuntu-26.04-64",
120120
"tasks": "tests/...",
121121
"rules": "main"
122122
},
123123
{
124124
"runs-on": "['self-hosted', 'spread-enabled']",
125-
"group": "ubuntu-interim",
125+
"group": "ubuntu-daily",
126126
"backend": "openstack",
127-
"systems": "ubuntu-25.10-64",
127+
"systems": "ubuntu-26.10-64",
128128
"tasks": "tests/...",
129129
"rules": "main"
130130
},
@@ -162,9 +162,9 @@
162162
},
163163
{
164164
"runs-on": "['ubuntu-latest']",
165-
"group": "questing (garden)",
165+
"group": "stonking (garden)",
166166
"backend": "garden",
167-
"systems": "ubuntu-25.10-64",
167+
"systems": "ubuntu-26.10-64",
168168
"tasks": "tests/main/microk8s-smoke tests/main/download-timeout tests/main/snap-network-errors tests/main/snapd-snap:lxd tests/main/apparmor-prompting-support tests/main/proxy",
169169
"rules": ""
170170
},

NEWS.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,20 @@
1+
# New in snapd 2.76.3
2+
* FDE: support keyboard configuration at install-time for first-boot
3+
* FDE: re-enable passphrases/PINs at install-time
4+
* FDE: require volumes authentication if HWROT is missing
5+
* FDE: bump secboot to rev 457b03a16d19
6+
* FDE: use new secboot API for reprovision TPM
7+
* Cross-distro: modify SELinux policy to use init_named_socket_activation() for allowing systemd to start snapd through socket activation
8+
* packaging: make sure that usr/bin/snap is built with correct build tags on debian sid
9+
* Ensure profiles are setup before running prepare-{slot, plug}* hooks
10+
11+
# New in snapd 2.76.2
12+
* interfaces: steam-support, docker-support | fix mountinfo denial
13+
14+
# New in snapd 2.76.1
15+
* LP: #2067006 CVE-2024-5300
16+
* CVE-2026-3888
17+
118
# New in snapd 2.76
219
* assertions: add helper for validating integrity data
320
* assertions: drop incorrect/non-standard Ed25519 support

RELEASE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,3 +615,5 @@ Prerequisites:
615615
Request that snapd QA promotes snapd to stable. The release is done progressively. Once the progressive release has completed, update the [snapd roadmap](https://forum.snapcraft.io/t/the-snapd-roadmap/1973).
616616
617617
#### 2. Request move to `-updates`
618+
619+
Once the move is complete, change all milestone bugs to "Fix released."

asserts/asserts.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,8 @@ func init() {
222222
// 4: support for plug-names/slot-names constraints
223223
// 5: alt attr matcher usage (was unused before, has new behavior now)
224224
// 6: support for $PLUG_PUBLISHER_ID/$SLOT_PUBLISHER_ID in attr constraints
225-
maxSupportedFormat[SnapDeclarationType.Name] = 6
225+
// 7: support for on-classic distro/variant constraints
226+
maxSupportedFormat[SnapDeclarationType.Name] = 7
226227

227228
// 1: support to limit to device serials
228229
// 2: support for user-presence constraint

asserts/export_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ var DecodePrivateKeyInTest = decodePrivateKey
4747
// readOpenPGPRSAPublicKey exposed for tests
4848
var ReadOpenPGPRSAPublicKeyInTest = readOpenPGPRSAPublicKey
4949

50+
// CompileOnClassicSystemConstraintForTest exposes the on-classic system parser for focused tests.
51+
var CompileOnClassicSystemConstraintForTest = compileOnClassicSystemConstraint
52+
5053
// NewDecoderStressed makes a Decoder with a stressed setup with the given buffer and maximum sizes.
5154
func NewDecoderStressed(r io.Reader, bufSize, maxHeadersSize, maxBodySize, maxSigSize int) *Decoder {
5255
return (&Decoder{

asserts/ifacedecls.go

Lines changed: 108 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ const (
4444
deviceScopeConstraintsFeature = "device-scope-constraints"
4545
// feature label for plug-names/slot-names constraints
4646
nameConstraintsFeature = "name-constraints"
47+
// feature label for on-classic distro/variant constraints
48+
onClassicVariantConstraintsFeature = "on-classic-variant-constraints"
4749
)
4850

4951
// AttributeConstraints implements a set of constraints on the attributes of a slot or plug.
@@ -184,7 +186,97 @@ var (
184186
// OnClassicConstraint specifies a constraint based whether the system is classic and optional specific distros' sets.
185187
type OnClassicConstraint struct {
186188
Classic bool
187-
SystemIDs []string
189+
SystemIDs []OnClassicSystemConstraint
190+
}
191+
192+
// OnClassicSystemConstraint specifies an operating system distro/variant matcher.
193+
//
194+
// VariantAny is true for constraints that accept any VARIANT_ID, including the
195+
// legacy distro-only form (for example "ubuntu") and the explicit wildcard form
196+
// (for example "ubuntu/*"). VariantID preserves the explicit wildcard so format
197+
// detection can distinguish old and new syntax.
198+
type OnClassicSystemConstraint struct {
199+
DistroID string
200+
VariantID string
201+
VariantAny bool
202+
}
203+
204+
func (c *OnClassicConstraint) feature(flabel string) bool {
205+
if flabel != onClassicVariantConstraintsFeature {
206+
return false
207+
}
208+
for _, systemID := range c.SystemIDs {
209+
// Explicit variant syntax requires format 7, even when it still matches any
210+
// variant (for example "ubuntu/*"). The legacy distro-only form keeps the
211+
// zero VariantID while still setting VariantAny.
212+
if !systemID.VariantAny || systemID.VariantID == "*" {
213+
return true
214+
}
215+
}
216+
return false
217+
}
218+
219+
func compileOnClassicConstraint(context *subruleContext, onClassic any) (*OnClassicConstraint, error) {
220+
what := fmt.Sprintf("on-classic in %s", context)
221+
syntaxError := fmt.Errorf("%s must be 'true', 'false' or a list of operating system IDs with optional /variant IDs", what)
222+
223+
switch x := onClassic.(type) {
224+
case string:
225+
switch x {
226+
case "true":
227+
return &OnClassicConstraint{Classic: true}, nil
228+
case "false":
229+
return &OnClassicConstraint{Classic: false}, nil
230+
default:
231+
return nil, syntaxError
232+
}
233+
case []any:
234+
systems := make([]OnClassicSystemConstraint, len(x))
235+
for i, v := range x {
236+
s, ok := v.(string)
237+
if !ok {
238+
return nil, fmt.Errorf("%s must be a list of strings", what)
239+
}
240+
systemID, err := compileOnClassicSystemConstraint(s)
241+
if err != nil {
242+
return nil, fmt.Errorf("%s contains an invalid element: %q: %v", what, s, err)
243+
}
244+
systems[i] = systemID
245+
}
246+
return &OnClassicConstraint{Classic: true, SystemIDs: systems}, nil
247+
default:
248+
return nil, syntaxError
249+
}
250+
}
251+
252+
func compileOnClassicSystemConstraint(s string) (OnClassicSystemConstraint, error) {
253+
if strings.Count(s, "/") > 1 {
254+
return OnClassicSystemConstraint{}, fmt.Errorf("invalid operating system constraint: too many '/' separators")
255+
}
256+
if distroID, variantID, hasVariant := strings.Cut(s, "/"); hasVariant {
257+
if !validDistro.MatchString(distroID) {
258+
return OnClassicSystemConstraint{}, fmt.Errorf("invalid operating system constraint: invalid distro ID")
259+
}
260+
constraint := OnClassicSystemConstraint{
261+
DistroID: distroID,
262+
}
263+
switch {
264+
case variantID == "*":
265+
constraint.VariantAny = true
266+
constraint.VariantID = "*"
267+
case variantID == "":
268+
// Match only when VARIANT_ID is unset.
269+
case validDistro.MatchString(variantID):
270+
constraint.VariantID = variantID
271+
default:
272+
return OnClassicSystemConstraint{}, fmt.Errorf("invalid operating system constraint: invalid variant ID")
273+
}
274+
return constraint, nil
275+
}
276+
if !validDistro.MatchString(s) {
277+
return OnClassicSystemConstraint{}, fmt.Errorf("invalid operating system constraint: invalid distro ID")
278+
}
279+
return OnClassicSystemConstraint{DistroID: s, VariantAny: true}, nil
188280
}
189281

190282
// OnCoreDesktopConstraint specifies a constraint based whether the system is core desktop.
@@ -387,24 +479,9 @@ func baseCompileConstraints(context *subruleContext, cDef constraintsDef, target
387479
if onClassic == nil {
388480
defaultUsed++
389481
} else {
390-
var c *OnClassicConstraint
391-
switch x := onClassic.(type) {
392-
case string:
393-
switch x {
394-
case "true":
395-
c = &OnClassicConstraint{Classic: true}
396-
case "false":
397-
c = &OnClassicConstraint{Classic: false}
398-
}
399-
case []any:
400-
lst, err := checkStringListInMap(cMap, "on-classic", fmt.Sprintf("on-classic in %s", context), validDistro)
401-
if err != nil {
402-
return err
403-
}
404-
c = &OnClassicConstraint{Classic: true, SystemIDs: lst}
405-
}
406-
if c == nil {
407-
return fmt.Errorf("on-classic in %s must be 'true', 'false' or a list of operating system IDs", context)
482+
c, err := compileOnClassicConstraint(context, onClassic)
483+
if err != nil {
484+
return err
408485
}
409486
target.setOnClassicConstraint(c)
410487
}
@@ -667,6 +744,9 @@ func (c *PlugInstallationConstraints) feature(flabel string) bool {
667744
if flabel == deviceScopeConstraintsFeature {
668745
return c.DeviceScope != nil
669746
}
747+
if flabel == onClassicVariantConstraintsFeature {
748+
return c.OnClassic != nil && c.OnClassic.feature(flabel)
749+
}
670750
if flabel == nameConstraintsFeature {
671751
return c.PlugNames != nil
672752
}
@@ -754,6 +834,9 @@ func (c *PlugConnectionConstraints) feature(flabel string) bool {
754834
if flabel == deviceScopeConstraintsFeature {
755835
return c.DeviceScope != nil
756836
}
837+
if flabel == onClassicVariantConstraintsFeature {
838+
return c.OnClassic != nil && c.OnClassic.feature(flabel)
839+
}
757840
if flabel == nameConstraintsFeature {
758841
return c.PlugNames != nil || c.SlotNames != nil
759842
}
@@ -986,6 +1069,9 @@ func (c *SlotInstallationConstraints) feature(flabel string) bool {
9861069
if flabel == deviceScopeConstraintsFeature {
9871070
return c.DeviceScope != nil
9881071
}
1072+
if flabel == onClassicVariantConstraintsFeature {
1073+
return c.OnClassic != nil && c.OnClassic.feature(flabel)
1074+
}
9891075
if flabel == nameConstraintsFeature {
9901076
return c.SlotNames != nil
9911077
}
@@ -1087,6 +1173,9 @@ func (c *SlotConnectionConstraints) feature(flabel string) bool {
10871173
if flabel == deviceScopeConstraintsFeature {
10881174
return c.DeviceScope != nil
10891175
}
1176+
if flabel == onClassicVariantConstraintsFeature {
1177+
return c.OnClassic != nil && c.OnClassic.feature(flabel)
1178+
}
10901179
if flabel == nameConstraintsFeature {
10911180
return c.PlugNames != nil || c.SlotNames != nil
10921181
}

asserts/ifacedecls_test.go

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -872,7 +872,48 @@ func (s *plugSlotRulesSuite) TestCompilePlugRuleInstallationConstraintsOnClassic
872872
rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]any))
873873
c.Assert(err, IsNil)
874874

875-
c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []string{"ubuntu", "debian"}})
875+
c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []asserts.OnClassicSystemConstraint{{DistroID: "ubuntu", VariantAny: true}, {DistroID: "debian", VariantAny: true}}})
876+
877+
m, err = asserts.ParseHeaders([]byte("iface:\n allow-installation:\n on-classic:\n - ubuntu/touch\n - ubuntu/*\n - ubuntu/"))
878+
c.Assert(err, IsNil)
879+
880+
rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]any))
881+
c.Assert(err, IsNil)
882+
883+
c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []asserts.OnClassicSystemConstraint{{DistroID: "ubuntu", VariantID: "touch"}, {DistroID: "ubuntu", VariantID: "*", VariantAny: true}, {DistroID: "ubuntu"}}})
884+
}
885+
886+
func (s *plugSlotRulesSuite) TestCompileOnClassicSystemConstraint(c *C) {
887+
validTests := []struct {
888+
input string
889+
expected asserts.OnClassicSystemConstraint
890+
}{
891+
{input: "ubuntu", expected: asserts.OnClassicSystemConstraint{DistroID: "ubuntu", VariantAny: true}},
892+
{input: "ubuntu/touch", expected: asserts.OnClassicSystemConstraint{DistroID: "ubuntu", VariantID: "touch"}},
893+
{input: "ubuntu/*", expected: asserts.OnClassicSystemConstraint{DistroID: "ubuntu", VariantID: "*", VariantAny: true}},
894+
{input: "ubuntu/", expected: asserts.OnClassicSystemConstraint{DistroID: "ubuntu"}},
895+
}
896+
897+
for _, test := range validTests {
898+
constraint, err := asserts.CompileOnClassicSystemConstraintForTest(test.input)
899+
c.Assert(err, IsNil)
900+
c.Check(constraint, DeepEquals, test.expected)
901+
}
902+
903+
invalidTests := []struct {
904+
input string
905+
err string
906+
}{
907+
{input: "ubuntu/touch/stable", err: "invalid operating system constraint: too many '/' separators"},
908+
{input: "ubuntu/!desktop", err: "invalid operating system constraint: invalid variant ID"},
909+
{input: "ubuntu//", err: "invalid operating system constraint: too many '/' separators"},
910+
{input: "/touch", err: "invalid operating system constraint: invalid distro ID"},
911+
{input: "*", err: "invalid operating system constraint: invalid distro ID"},
912+
}
913+
for _, test := range invalidTests {
914+
_, err := asserts.CompileOnClassicSystemConstraintForTest(test.input)
915+
c.Check(err, ErrorMatches, test.err)
916+
}
876917
}
877918

878919
func (s *plugSlotRulesSuite) TestCompilePlugRuleInstallationConstraintsDeviceScope(c *C) {
@@ -1038,7 +1079,15 @@ func (s *plugSlotRulesSuite) TestCompilePlugRuleConnectionConstraintsOnClassic(c
10381079
rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]any))
10391080
c.Assert(err, IsNil)
10401081

1041-
c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []string{"ubuntu", "debian"}})
1082+
c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []asserts.OnClassicSystemConstraint{{DistroID: "ubuntu", VariantAny: true}, {DistroID: "debian", VariantAny: true}}})
1083+
1084+
m, err = asserts.ParseHeaders([]byte("iface:\n allow-connection:\n on-classic:\n - ubuntu/touch\n - ubuntu/*\n - ubuntu/"))
1085+
c.Assert(err, IsNil)
1086+
1087+
rule, err = asserts.CompilePlugRule("iface", m["iface"].(map[string]any))
1088+
c.Assert(err, IsNil)
1089+
1090+
c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []asserts.OnClassicSystemConstraint{{DistroID: "ubuntu", VariantID: "touch"}, {DistroID: "ubuntu", VariantID: "*", VariantAny: true}, {DistroID: "ubuntu"}}})
10421091
}
10431092

10441093
func (s *plugSlotRulesSuite) TestCompilePlugRuleConnectionConstraintsDeviceScope(c *C) {
@@ -1718,7 +1767,15 @@ func (s *plugSlotRulesSuite) TestCompileSlotRuleInstallationConstraintsOnClassic
17181767
rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]any))
17191768
c.Assert(err, IsNil)
17201769

1721-
c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []string{"ubuntu", "debian"}})
1770+
c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []asserts.OnClassicSystemConstraint{{DistroID: "ubuntu", VariantAny: true}, {DistroID: "debian", VariantAny: true}}})
1771+
1772+
m, err = asserts.ParseHeaders([]byte("iface:\n allow-installation:\n on-classic:\n - ubuntu/touch\n - ubuntu/*\n - ubuntu/"))
1773+
c.Assert(err, IsNil)
1774+
1775+
rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]any))
1776+
c.Assert(err, IsNil)
1777+
1778+
c.Check(rule.AllowInstallation[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []asserts.OnClassicSystemConstraint{{DistroID: "ubuntu", VariantID: "touch"}, {DistroID: "ubuntu", VariantID: "*", VariantAny: true}, {DistroID: "ubuntu"}}})
17221779
}
17231780

17241781
func (s *plugSlotRulesSuite) TestCompileSlotRuleInstallationConstraintsDeviceScope(c *C) {
@@ -1885,7 +1942,15 @@ func (s *plugSlotRulesSuite) TestCompileSlotRuleConnectionConstraintsOnClassic(c
18851942
rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]any))
18861943
c.Assert(err, IsNil)
18871944

1888-
c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []string{"ubuntu", "debian"}})
1945+
c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []asserts.OnClassicSystemConstraint{{DistroID: "ubuntu", VariantAny: true}, {DistroID: "debian", VariantAny: true}}})
1946+
1947+
m, err = asserts.ParseHeaders([]byte("iface:\n allow-connection:\n on-classic:\n - ubuntu/touch\n - ubuntu/*\n - ubuntu/"))
1948+
c.Assert(err, IsNil)
1949+
1950+
rule, err = asserts.CompileSlotRule("iface", m["iface"].(map[string]any))
1951+
c.Assert(err, IsNil)
1952+
1953+
c.Check(rule.AllowConnection[0].OnClassic, DeepEquals, &asserts.OnClassicConstraint{Classic: true, SystemIDs: []asserts.OnClassicSystemConstraint{{DistroID: "ubuntu", VariantID: "touch"}, {DistroID: "ubuntu", VariantID: "*", VariantAny: true}, {DistroID: "ubuntu"}}})
18891954
}
18901955

18911956
func (s *plugSlotRulesSuite) TestCompileSlotRuleConnectionConstraintsDeviceScope(c *C) {
@@ -2134,14 +2199,11 @@ func (s *plugSlotRulesSuite) TestCompileSlotRuleErrors(c *C) {
21342199
allow-connection:
21352200
plug-snap-type:
21362201
- xapp`, `plug-snap-type in allow-connection in slot rule for interface "iface" contains an invalid element: "xapp"`},
2137-
{`iface:
2138-
allow-connection:
2139-
on-classic:
2140-
x: 1`, `on-classic in allow-connection in slot rule for interface \"iface\" must be 'true', 'false' or a list of operating system IDs`},
2141-
{`iface:
2142-
allow-connection:
2143-
on-classic:
2144-
- zoom!`, `on-classic in allow-connection in slot rule for interface \"iface\" contains an invalid element: \"zoom!\"`},
2202+
{"iface:\n allow-connection:\n on-classic:\n x: 1", `on-classic in allow-connection in slot rule for interface \"iface\" must be 'true', 'false' or a list of operating system IDs with optional /variant IDs`},
2203+
{"iface:\n allow-connection:\n on-classic:\n -\n distro: ubuntu", `on-classic in allow-connection in slot rule for interface "iface" must be a list of strings`},
2204+
{"iface:\n allow-connection:\n on-classic:\n - zoom!", `on-classic in allow-connection in slot rule for interface "iface" contains an invalid element: "zoom!": invalid operating system constraint: invalid distro ID`},
2205+
{"iface:\n allow-connection:\n on-classic:\n - ubuntu/touch/stable", `on-classic in allow-connection in slot rule for interface "iface" contains an invalid element: "ubuntu/touch/stable": invalid operating system constraint: too many '/' separators`},
2206+
{"iface:\n allow-connection:\n on-classic:\n - ubuntu/!desktop", `on-classic in allow-connection in slot rule for interface "iface" contains an invalid element: "ubuntu/!desktop": invalid operating system constraint: invalid variant ID`},
21452207
{`iface:
21462208
allow-connection:
21472209
plug-snap-ids:

0 commit comments

Comments
 (0)