github.com/ethanhsieh/snapd@v0.0.0-20210615102523-3db9b8e4edc5/overlord/snapstate/check_snap.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2014-2016 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package snapstate 21 22 import ( 23 "fmt" 24 "regexp" 25 "strconv" 26 "strings" 27 28 "github.com/snapcore/snapd/arch" 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/logger" 31 "github.com/snapcore/snapd/osutil" 32 "github.com/snapcore/snapd/overlord/snapstate/backend" 33 "github.com/snapcore/snapd/overlord/state" 34 "github.com/snapcore/snapd/release" 35 seccomp_compiler "github.com/snapcore/snapd/sandbox/seccomp" 36 "github.com/snapcore/snapd/snap" 37 "github.com/snapcore/snapd/snapdtool" 38 ) 39 40 // featureSet contains the flag values that can be listed in assumes entries 41 // that this ubuntu-core actually provides. 42 var featureSet = map[string]bool{ 43 // Support for common data directory across revisions of a snap. 44 "common-data-dir": true, 45 // Support for the "Environment:" feature in snap.yaml 46 "snap-env": true, 47 // Support for the "command-chain" feature for apps and hooks in snap.yaml 48 "command-chain": true, 49 // Support for "kernel-assets" in gadget.yaml. I.e. having volume 50 // content of the style $kernel:ref` 51 "kernel-assets": true, 52 } 53 54 // supportedSystemUsernames for now contains the hardcoded list of system 55 // users (and implied system group of same name) that snaps may specify. This 56 // will eventually be moved out of here into the store. 57 // 58 // Since the snap is mounted read-only and to avoid problems associated with 59 // different systems using different uids and gids for the same user name and 60 // group name, snapd will create system-usernames where 'scope' is not 61 // 'external' (currently snapd only supports 'scope: shared') with the 62 // following characteristics: 63 // 64 // - uid and gid shall match for the specified system-username 65 // - a snapd-allocated [ug]id for a user/group name shall never change 66 // - snapd should avoid [ug]ids that are known to overlap with uid ranges of 67 // common use cases and user namespace container managers so that DAC and 68 // AppArmor owner match work as intended. 69 // - [ug]id shall be < 2^31 to avoid (at least) broken devpts kernel code 70 // - [ug]id shall be >= 524288 (0x00080000) to give plenty of room for large 71 // sites, default uid/gid ranges for docker (231072-296608), LXD installs 72 // that setup a default /etc/sub{uid,gid} (100000-165536) and podman whose 73 // tutorials reference setting up a specific default user and range 74 // (100000-165536) 75 // - [ug]id shall be < 1,000,000 and > 1,001,000,000 (ie, 1,000,000 subordinate 76 // uid with 1,000,000,000 range) to avoid overlapping with LXD's minimum and 77 // maximum id ranges. LXD allows for any id range >= 65536 and doesn't 78 // perform any [ug]id overlap detection with current users 79 // - [ug]ids assigned by snapd initially will fall within a 65536 (2^16) range 80 // (see below) where the first [ug]id in the range has the 16 lower bits all 81 // set to zero. This allows snapd to conveniently be bitwise aligned, follows 82 // sensible conventions (see https://systemd.io/UIDS-GIDS.html) but also 83 // potentially discoverable by systemd-nspawn (it assigns a different 65536 84 // range to each container. Its allocation algorithm is not sequential and 85 // may choose anything within its range that isn't already allocated. It's 86 // detection algorithm includes (effectively) performing a getpwent() 87 // operation on CANDIDATE_UID & 0XFFFF0000 and selecting another range if it 88 // is assigned). 89 // 90 // What [ug]id range(s) should snapd use? 91 // 92 // While snapd does not employ user namespaces, it will operate on systems with 93 // container managers that do and will assign from a range of [ug]ids. It is 94 // desirable that snapd assigns [ug]ids that minimally conflict with the system 95 // and other software (potential conflicts with admin-assigned ranges in 96 // /etc/subuid and /etc/subgid cannot be avoided, but can be documented as well 97 // as detected/logged). Overlapping with container managers is non-fatal for 98 // snapd and the container, but introduces the possibility that a uid in the 99 // container matches a uid a snap is using, which is undesirable in terms of 100 // security (eg, DoS via ulimit, same ownership of files between container and 101 // snap (even if the other's files are otherwise inaccessible), etc). 102 // 103 // snapd shall assign [ug]ids from range(s) of 65536 where the lowest value in 104 // the range has the 16 lower bits all set to zero (initially just one range, 105 // but snapd can add more as needed). 106 // 107 // To avoid [ug]id overlaps, snapd shall only assign [ug]ids >= 524288 108 // (0x00080000) and <= 983040 (0x000F0000, ie the first 65536 range under LXD's 109 // minimum where the lower 16 bits are all zeroes). While [ug]ids >= 1001062400 110 // (0x3BAB0000, the first 65536 range above LXD's maximum where the lower 16 111 // bits are all zeroes) would also avoid overlap, considering nested containers 112 // (eg, LXD snap runs a container that runs a container that runs snapd), 113 // choosing >= 1001062400 would mean that the admin would need to increase the 114 // LXD id range for these containers for snapd to be allowed to create its 115 // [ug]ids in the deeply nested containers. The requirements would both be an 116 // administrative burden and artificially limit the number of deeply nested 117 // containers the host could have. 118 // 119 // Looking at the LSB and distribution defaults for login.defs, we can observe 120 // uids and gids in the system's initial 65536 range (ie, 0-65536): 121 // 122 // - 0-99 LSB-suggested statically assigned range (eg, root, daemon, 123 // etc) 124 // - 0 mandatory 'root' user 125 // - 100-499 LSB-suggested dynamically assigned range for system users 126 // (distributions often prefer a higher range, see below) 127 // - 500-999 typical distribution default for dynamically assigned range 128 // for system users (some distributions use a smaller 129 // SYS_[GU]ID_MIN) 130 // - 1000-60000 typical distribution default for dynamically assigned range 131 // for regular users 132 // - 65535 (-1) should not be assigned since '-1' might be evaluated as this 133 // with set[ug]id* and chown families of functions 134 // - 65534 (-2) nobody/nogroup user for NFS/etc [ug]id anonymous squashing 135 // - 65519-65533 systemd recommended reserved range for site-local anonymous 136 // additions, etc 137 // 138 // To facilitate potential future use cases within the 65536 range snapd will 139 // assign from, snapd will only assign from the following subset of ranges 140 // relative to the range minimum (ie, its 'base' which has the lower 16 bits 141 // all set to zero): 142 // 143 // - 60500-60999 'scope: shared' system-usernames 144 // - 61000-65519 'scope: private' system-usernames 145 // 146 // Since the first [ug]id range must be >= 524288 and <= 983040 (see above) and 147 // following the above guide for system-usernames [ug]ids within this 65536 148 // range, the lowest 'scope: shared' user in this range is 584788 (0x0008EC54). 149 // 150 // Since this number is within systemd-nspawn's range of 524288-1879048191 151 // (0x00080000-0x6FFFFFFF), the number's lower 16 bits are not all zeroes so 152 // systemd-nspawn won't detect this allocation and could potentially assign the 153 // 65536 range starting at 0x00080000 to a container. snapd will therefore also 154 // create the 'snapd-range-524288-root' user and group with [ug]id 524288 to 155 // work within systemd-nspawn's collision detection. This user/group will not 156 // be assigned to snaps at this time. 157 // 158 // In short (phew!), use the following: 159 // 160 // $ snappy-debug.id-range 524288 # 0x00080000 161 // Host range: 524288-589823 (00080000-0008ffff; 0-65535) 162 // LSB static range: 524288-524387 (00080000-00080063; 0-99) 163 // Useradd system range: 524788-525287 (000801f4-000803e7; 500-999) 164 // Useradd regular range: 525288-584288 (000803e8-0008ea60; 1000-60000) 165 // Snapd system range: 584788-585287 (0008ec54-0008ee47; 60500-60999) 166 // Snapd private range: 585288-589807 (0008ee48-0008ffef; 61000-65519) 167 // 168 // Snapd is of course free to add more ranges (eg, 589824 (0x00090000)) with 169 // new snapd-range-<base>-root users, or to allocate differently within its 170 // 65536 range in the future (sequentially assigned [ug]ids are not required), 171 // but for now start very regimented to avoid as many problems as possible. 172 // 173 // References: 174 // https://forum.snapcraft.io/t/multiple-users-and-groups-in-snaps/ 175 // https://systemd.io/UIDS-GIDS.html 176 // https://docs.docker.com/engine/security/userns-remap/ 177 // https://github.com/lxc/lxd/blob/master/doc/userns-idmap.md 178 var supportedSystemUsernames = map[string]uint32{ 179 "snap_daemon": 584788, 180 } 181 182 func checkAssumes(si *snap.Info) error { 183 missing := ([]string)(nil) 184 for _, flag := range si.Assumes { 185 if strings.HasPrefix(flag, "snapd") && checkVersion(flag[5:]) { 186 continue 187 } 188 if !featureSet[flag] { 189 missing = append(missing, flag) 190 } 191 } 192 if len(missing) > 0 { 193 hint := "try to refresh the core or snapd snaps" 194 if release.OnClassic { 195 hint = "try to update snapd and refresh the core snap" 196 } 197 return fmt.Errorf("snap %q assumes unsupported features: %s (%s)", si.InstanceName(), strings.Join(missing, ", "), hint) 198 } 199 return nil 200 } 201 202 // regular expression which matches a version expressed as groups of digits 203 // separated with dots, with optional non-numbers afterwards 204 var versionExp = regexp.MustCompile(`^(?:[1-9][0-9]*)(?:\.(?:[0-9]+))*`) 205 206 func checkVersion(version string) bool { 207 // double check that the input looks like a snapd version 208 reqVersionNumMatch := versionExp.FindStringSubmatch(version) 209 if reqVersionNumMatch == nil { 210 return false 211 } 212 // this check ensures that no one can use an assumes like snapd2.48.3~pre2 213 // or snapd2.48.5+20.10, as modifiers past the version number are not meant 214 // to be relied on for snaps via assumes, however the check against the real 215 // snapd version number below allows such non-numeric modifiers since real 216 // snapds do have versions like that (for example debian pkg of snapd) 217 if reqVersionNumMatch[0] != version { 218 return false 219 } 220 221 req := strings.Split(reqVersionNumMatch[0], ".") 222 223 if snapdtool.Version == "unknown" { 224 return true // Development tree. 225 } 226 227 // We could (should?) use strutil.VersionCompare here and simplify 228 // this code (see PR#7344). However this would change current 229 // behavior, i.e. "2.41~pre1" would *not* match [snapd2.41] anymore 230 // (which the code below does). 231 curVersionNumMatch := versionExp.FindStringSubmatch(snapdtool.Version) 232 if curVersionNumMatch == nil { 233 return false 234 } 235 cur := strings.Split(curVersionNumMatch[0], ".") 236 237 for i := range req { 238 if i == len(cur) { 239 // we hit the end of the elements of the current version number and have 240 // more required version numbers left, so this doesn't match, if the 241 // previous element was higher we would have broken out already, so the 242 // only case left here is where we have version requirements that are 243 // not met 244 return false 245 } 246 reqN, err1 := strconv.Atoi(req[i]) 247 curN, err2 := strconv.Atoi(cur[i]) 248 if err1 != nil || err2 != nil { 249 panic("internal error: version regexp is broken") 250 } 251 if curN != reqN { 252 return curN > reqN 253 } 254 } 255 256 return true 257 } 258 259 type SnapNeedsDevModeError struct { 260 Snap string 261 } 262 263 func (e *SnapNeedsDevModeError) Error() string { 264 return fmt.Sprintf("snap %q requires devmode or confinement override", e.Snap) 265 } 266 267 type SnapNeedsClassicError struct { 268 Snap string 269 } 270 271 func (e *SnapNeedsClassicError) Error() string { 272 return fmt.Sprintf("snap %q requires classic confinement", e.Snap) 273 } 274 275 type SnapNeedsClassicSystemError struct { 276 Snap string 277 } 278 279 func (e *SnapNeedsClassicSystemError) Error() string { 280 return fmt.Sprintf("snap %q requires classic confinement which is only available on classic systems", e.Snap) 281 } 282 283 type SnapNotClassicError struct { 284 Snap string 285 } 286 287 func (e *SnapNotClassicError) Error() string { 288 return fmt.Sprintf("snap %q is not a classic confined snap", e.Snap) 289 } 290 291 // determine whether the flags (and system overrides thereof) are 292 // compatible with the given *snap.Info 293 func validateFlagsForInfo(info *snap.Info, snapst *SnapState, flags Flags) error { 294 if flags.Classic && !info.NeedsClassic() { 295 return &SnapNotClassicError{Snap: info.InstanceName()} 296 } 297 298 switch c := info.Confinement; c { 299 case snap.StrictConfinement, "": 300 // strict is always fine 301 return nil 302 case snap.DevModeConfinement: 303 // --devmode needs to be specified every time (==> ignore snapst) 304 if flags.DevModeAllowed() { 305 return nil 306 } 307 return &SnapNeedsDevModeError{ 308 Snap: info.InstanceName(), 309 } 310 case snap.ClassicConfinement: 311 if !release.OnClassic { 312 return &SnapNeedsClassicSystemError{Snap: info.InstanceName()} 313 } 314 315 if flags.Classic { 316 return nil 317 } 318 319 if snapst != nil && snapst.Flags.Classic { 320 return nil 321 } 322 323 return &SnapNeedsClassicError{ 324 Snap: info.InstanceName(), 325 } 326 default: 327 return fmt.Errorf("unknown confinement %q", c) 328 } 329 } 330 331 // do a reasonably lightweight check that a snap described by Info, 332 // with the given SnapState and the user-specified Flags should be 333 // installable on the current system. 334 func validateInfoAndFlags(info *snap.Info, snapst *SnapState, flags Flags) error { 335 if err := validateFlagsForInfo(info, snapst, flags); err != nil { 336 return err 337 } 338 339 // verify we have a valid architecture 340 if !arch.IsSupportedArchitecture(info.Architectures) { 341 return fmt.Errorf("snap %q supported architectures (%s) are incompatible with this system (%s)", info.InstanceName(), strings.Join(info.Architectures, ", "), arch.DpkgArchitecture()) 342 } 343 344 // check assumes 345 if err := checkAssumes(info); err != nil { 346 return err 347 } 348 349 // check and create system-usernames 350 if err := checkAndCreateSystemUsernames(info); err != nil { 351 return err 352 } 353 354 return nil 355 } 356 357 var openSnapFile = backend.OpenSnapFile 358 359 func validateContainer(c snap.Container, s *snap.Info, logf func(format string, v ...interface{})) error { 360 err := snap.ValidateContainer(c, s, logf) 361 if err == nil { 362 return nil 363 } 364 return fmt.Errorf("%v; contact developer", err) 365 } 366 367 // checkSnap ensures that the snap can be installed. 368 func checkSnap(st *state.State, snapFilePath, instanceName string, si *snap.SideInfo, curInfo *snap.Info, flags Flags, deviceCtx DeviceContext) error { 369 // This assumes that the snap was already verified or --dangerous was used. 370 371 s, c, err := openSnapFile(snapFilePath, si) 372 if err != nil { 373 return err 374 } 375 376 if err := validateInfoAndFlags(s, nil, flags); err != nil { 377 return err 378 } 379 380 if err := validateContainer(c, s, logger.Noticef); err != nil { 381 return err 382 } 383 384 snapName, instanceKey := snap.SplitInstanceName(instanceName) 385 // update instance key to what was requested 386 s.InstanceKey = instanceKey 387 388 st.Lock() 389 defer st.Unlock() 390 391 // allow registered checks to run first as they may produce more 392 // precise errors 393 for _, check := range checkSnapCallbacks { 394 err := check(st, s, curInfo, c, flags, deviceCtx) 395 if err != nil { 396 return err 397 } 398 } 399 400 if snapName != s.SnapName() { 401 return fmt.Errorf("cannot install snap %q using instance name %q", s.SnapName(), instanceName) 402 } 403 404 return nil 405 } 406 407 // CheckSnapCallback defines callbacks for checking a snap for installation or refresh. 408 type CheckSnapCallback func(st *state.State, snap, curSnap *snap.Info, snapf snap.Container, flags Flags, deviceCtx DeviceContext) error 409 410 var checkSnapCallbacks []CheckSnapCallback 411 412 // AddCheckSnapCallback installs a callback to check a snap for installation or refresh. 413 func AddCheckSnapCallback(check CheckSnapCallback) { 414 checkSnapCallbacks = append(checkSnapCallbacks, check) 415 } 416 417 func MockCheckSnapCallbacks(checks []CheckSnapCallback) (restore func()) { 418 prev := checkSnapCallbacks 419 checkSnapCallbacks = checks 420 return func() { 421 checkSnapCallbacks = prev 422 } 423 } 424 425 func checkSnapdName(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error { 426 if snapInfo.Type() != snap.TypeSnapd { 427 // not a relevant check 428 return nil 429 } 430 if snapInfo.InstanceName() != "snapd" { 431 return fmt.Errorf(`cannot install snap %q of type "snapd" with a name other than "snapd"`, snapInfo.InstanceName()) 432 } 433 434 return nil 435 } 436 437 func checkCoreName(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error { 438 if snapInfo.Type() != snap.TypeOS { 439 // not a relevant check 440 return nil 441 } 442 if curInfo != nil { 443 // already one of these installed 444 return nil 445 } 446 core, err := coreInfo(st) 447 if err == state.ErrNoState { 448 return nil 449 } 450 if err != nil { 451 return err 452 } 453 454 // Allow installing "core" even if "ubuntu-core" is already 455 // installed. Ideally we should only allow this if we know 456 // this install is part of the ubuntu-core->core transition 457 // (e.g. via a flag) because if this happens outside of this 458 // transition we will end up with not connected interface 459 // connections in the "core" snap. But the transition will 460 // kick in automatically quickly so an extra flag is overkill. 461 if snapInfo.InstanceName() == "core" && core.InstanceName() == "ubuntu-core" { 462 return nil 463 } 464 465 // but generally do not allow to have two cores installed 466 if core.InstanceName() != snapInfo.InstanceName() { 467 return fmt.Errorf("cannot install core snap %q when core snap %q is already present", snapInfo.InstanceName(), core.InstanceName()) 468 } 469 470 return nil 471 } 472 473 func checkGadgetOrKernel(st *state.State, snapInfo, curInfo *snap.Info, snapf snap.Container, flags Flags, deviceCtx DeviceContext) error { 474 typ := snapInfo.Type() 475 kind := "" 476 var whichName func(*asserts.Model) string 477 switch typ { 478 case snap.TypeGadget: 479 kind = "gadget" 480 whichName = (*asserts.Model).Gadget 481 case snap.TypeKernel: 482 kind = "kernel" 483 whichName = (*asserts.Model).Kernel 484 default: 485 // not a relevant check 486 return nil 487 } 488 489 ok, err := HasSnapOfType(st, typ) 490 if err != nil { 491 return fmt.Errorf("cannot detect original %s snap: %v", kind, err) 492 } 493 // in firstboot we have no gadget/kernel yet - that is ok 494 // first install rules are in devicestate! 495 if !ok { 496 return nil 497 } 498 499 currentSnap, err := infoForDeviceSnap(st, deviceCtx, kind, whichName) 500 if err == state.ErrNoState { 501 // check if we are in the remodel case 502 if deviceCtx != nil && deviceCtx.ForRemodeling() { 503 if whichName(deviceCtx.Model()) == snapInfo.InstanceName() { 504 return nil 505 } 506 } 507 return fmt.Errorf("internal error: no state for %s snap %q", kind, snapInfo.InstanceName()) 508 } 509 if err != nil { 510 return fmt.Errorf("cannot find original %s snap: %v", kind, err) 511 } 512 513 if currentSnap.SnapID != "" && snapInfo.SnapID == "" { 514 return fmt.Errorf("cannot replace signed %s snap with an unasserted one", kind) 515 } 516 517 if currentSnap.SnapID != "" && snapInfo.SnapID != "" { 518 if currentSnap.SnapID == snapInfo.SnapID { 519 // same snap 520 return nil 521 } 522 return fmt.Errorf("cannot replace %s snap with a different one", kind) 523 } 524 525 if currentSnap.InstanceName() != snapInfo.InstanceName() { 526 return fmt.Errorf("cannot replace %s snap with a different one", kind) 527 } 528 529 return nil 530 } 531 532 func checkBases(st *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, flags Flags, deviceCtx DeviceContext) error { 533 // check if this is relevant 534 if snapInfo.Type() != snap.TypeApp && snapInfo.Type() != snap.TypeGadget { 535 return nil 536 } 537 if snapInfo.Base == "" { 538 return nil 539 } 540 if snapInfo.Base == "none" { 541 return nil 542 } 543 544 snapStates, err := All(st) 545 if err != nil { 546 return err 547 } 548 for otherSnap, snapst := range snapStates { 549 typ, err := snapst.Type() 550 if err != nil { 551 return err 552 } 553 if typ == snap.TypeBase && otherSnap == snapInfo.Base { 554 return nil 555 } 556 // core can be used instead for core16 557 if snapInfo.Base == "core16" && otherSnap == "core" { 558 return nil 559 } 560 } 561 562 return fmt.Errorf("cannot find required base %q", snapInfo.Base) 563 } 564 565 func checkEpochs(_ *state.State, snapInfo, curInfo *snap.Info, _ snap.Container, _ Flags, deviceCtx DeviceContext) error { 566 if curInfo == nil { 567 return nil 568 } 569 if snapInfo.Epoch.CanRead(curInfo.Epoch) { 570 return nil 571 } 572 desc := "local snap" 573 if snapInfo.SideInfo.Revision.Store() { 574 desc = fmt.Sprintf("new revision %s", snapInfo.SideInfo.Revision) 575 } 576 577 return fmt.Errorf("cannot refresh %q to %s with epoch %s, because it can't read the current epoch of %s", snapInfo.InstanceName(), desc, snapInfo.Epoch, curInfo.Epoch) 578 } 579 580 // check that the snap installed in the system (via snapst) can be 581 // upgraded to info (i.e. that info's epoch can read sanpst's epoch) 582 func earlyEpochCheck(info *snap.Info, snapst *SnapState) error { 583 if snapst == nil { 584 // no snapst, no problem 585 return nil 586 } 587 cur, err := snapst.CurrentInfo() 588 if err != nil { 589 if err == ErrNoCurrent { 590 // refreshing a disabled snap (maybe via InstallPath) 591 return nil 592 } 593 return err 594 } 595 596 return checkEpochs(nil, info, cur, nil, Flags{}, nil) 597 } 598 599 func earlyChecks(st *state.State, snapst *SnapState, update *snap.Info, flags Flags) (Flags, error) { 600 flags, err := ensureInstallPreconditions(st, update, flags, snapst) 601 if err != nil { 602 return flags, err 603 } 604 605 if err := earlyEpochCheck(update, snapst); err != nil { 606 return flags, err 607 } 608 return flags, nil 609 } 610 611 // check that the listed system users are valid 612 var osutilEnsureUserGroup = osutil.EnsureUserGroup 613 614 func validateSystemUsernames(si *snap.Info) error { 615 for _, user := range si.SystemUsernames { 616 if _, ok := supportedSystemUsernames[user.Name]; !ok { 617 return fmt.Errorf(`snap %q requires unsupported system username "%s"`, si.InstanceName(), user.Name) 618 } 619 620 switch user.Scope { 621 case "shared": 622 // this is supported 623 continue 624 case "private", "external": 625 // not supported yet 626 return fmt.Errorf(`snap %q requires unsupported user scope "%s" for this version of snapd`, si.InstanceName(), user.Scope) 627 default: 628 return fmt.Errorf(`snap %q requires unsupported user scope "%s"`, si.InstanceName(), user.Scope) 629 } 630 } 631 return nil 632 } 633 634 func checkAndCreateSystemUsernames(si *snap.Info) error { 635 // No need to check support if no system-usernames 636 if len(si.SystemUsernames) == 0 { 637 return nil 638 } 639 640 // Run /.../snap-seccomp version-info 641 vi, err := seccomp_compiler.CompilerVersionInfo(snapdtool.InternalToolPath) 642 if err != nil { 643 return fmt.Errorf("cannot obtain seccomp compiler information: %v", err) 644 } 645 646 // If the system doesn't support robust argument filtering then we 647 // can't support system-usernames 648 if err := vi.SupportsRobustArgumentFiltering(); err != nil { 649 if re, ok := err.(*seccomp_compiler.BuildTimeRequirementError); ok { 650 return fmt.Errorf("snap %q system usernames require a snapd built against %s", si.InstanceName(), re.RequirementsString()) 651 } 652 return err 653 } 654 655 // first validate 656 if err := validateSystemUsernames(si); err != nil { 657 return err 658 } 659 660 // then create 661 // TODO: move user creation to a more appropriate place like "link-snap" 662 extrausers := !release.OnClassic 663 for _, user := range si.SystemUsernames { 664 id := supportedSystemUsernames[user.Name] 665 switch user.Scope { 666 case "shared": 667 // Create the snapd-range-<base>-root user and group so 668 // systemd-nspawn can avoid our range. Our ranges will always 669 // be in 65536 chunks, so mask off the lower bits to obtain our 670 // base (see above) 671 rangeStart := id & 0xFFFF0000 672 rangeName := fmt.Sprintf("snapd-range-%d-root", rangeStart) 673 if err := osutilEnsureUserGroup(rangeName, rangeStart, extrausers); err != nil { 674 return fmt.Errorf(`cannot ensure users for snap %q required system username "%s": %v`, si.InstanceName(), user.Name, err) 675 } 676 677 // Create the requested user and group 678 if err := osutilEnsureUserGroup(user.Name, id, extrausers); err != nil { 679 return fmt.Errorf(`cannot ensure users for snap %q required system username "%s": %v`, si.InstanceName(), user.Name, err) 680 } 681 } 682 } 683 return nil 684 } 685 686 func init() { 687 AddCheckSnapCallback(checkCoreName) 688 AddCheckSnapCallback(checkSnapdName) 689 AddCheckSnapCallback(checkGadgetOrKernel) 690 AddCheckSnapCallback(checkBases) 691 AddCheckSnapCallback(checkEpochs) 692 }