github.com/freetocompute/snapd@v0.0.0-20210618182524-2fb355d72fd9/overlord/assertstate/assertstate.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2016-2019 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 assertstate implements the manager and state aspects responsible 21 // for the enforcement of assertions in the system and manages the system-wide 22 // assertion database. 23 package assertstate 24 25 import ( 26 "fmt" 27 "strings" 28 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/asserts/snapasserts" 31 "github.com/snapcore/snapd/httputil" 32 "github.com/snapcore/snapd/logger" 33 "github.com/snapcore/snapd/overlord/snapstate" 34 "github.com/snapcore/snapd/overlord/state" 35 "github.com/snapcore/snapd/release" 36 "github.com/snapcore/snapd/snap" 37 ) 38 39 // Add the given assertion to the system assertion database. 40 func Add(s *state.State, a asserts.Assertion) error { 41 // TODO: deal together with asserts itself with (cascading) side effects of possible assertion updates 42 return cachedDB(s).Add(a) 43 } 44 45 // AddBatch adds the given assertion batch to the system assertion database. 46 func AddBatch(s *state.State, batch *asserts.Batch, opts *asserts.CommitOptions) error { 47 return batch.CommitTo(cachedDB(s), opts) 48 } 49 50 func findError(format string, ref *asserts.Ref, err error) error { 51 if asserts.IsNotFound(err) { 52 return fmt.Errorf(format, ref) 53 } else { 54 return fmt.Errorf(format+": %v", ref, err) 55 } 56 } 57 58 // RefreshSnapDeclarations refetches all the current snap declarations and their prerequisites. 59 func RefreshSnapDeclarations(s *state.State, userID int) error { 60 deviceCtx, err := snapstate.DevicePastSeeding(s, nil) 61 if err != nil { 62 return err 63 } 64 65 snapStates, err := snapstate.All(s) 66 if err != nil { 67 return nil 68 } 69 70 err = bulkRefreshSnapDeclarations(s, snapStates, userID, deviceCtx) 71 if err == nil { 72 // done 73 return nil 74 } 75 if _, ok := err.(*bulkAssertionFallbackError); !ok { 76 // not an error that indicates the server rejecting/failing 77 // the bulk request itself 78 return err 79 } 80 logger.Noticef("bulk refresh of snap-declarations failed, falling back to one-by-one assertion fetching: %v", err) 81 82 modelAs := deviceCtx.Model() 83 84 fetching := func(f asserts.Fetcher) error { 85 for instanceName, snapst := range snapStates { 86 sideInfo := snapst.CurrentSideInfo() 87 if sideInfo.SnapID == "" { 88 continue 89 } 90 if err := snapasserts.FetchSnapDeclaration(f, sideInfo.SnapID); err != nil { 91 if notRetried, ok := err.(*httputil.PersistentNetworkError); ok { 92 return notRetried 93 } 94 return fmt.Errorf("cannot refresh snap-declaration for %q: %v", instanceName, err) 95 } 96 } 97 98 // fetch store assertion if available 99 if modelAs.Store() != "" { 100 err := snapasserts.FetchStore(f, modelAs.Store()) 101 if err != nil && !asserts.IsNotFound(err) { 102 return err 103 } 104 } 105 106 return nil 107 } 108 return doFetch(s, userID, deviceCtx, fetching) 109 } 110 111 type refreshControlError struct { 112 errs []error 113 } 114 115 func (e *refreshControlError) Error() string { 116 if len(e.errs) == 1 { 117 return e.errs[0].Error() 118 } 119 l := []string{""} 120 for _, e := range e.errs { 121 l = append(l, e.Error()) 122 } 123 return fmt.Sprintf("refresh control errors:%s", strings.Join(l, "\n - ")) 124 } 125 126 // ValidateRefreshes validates the refresh candidate revisions represented by 127 // the snapInfos, looking for the needed refresh control validation assertions, 128 // it returns a validated subset in validated and a summary error if not all 129 // candidates validated. ignoreValidation is a set of snap-instance-names that 130 // should not be gated. 131 func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) (validated []*snap.Info, err error) { 132 // maps gated snap-ids to gating snap-ids 133 controlled := make(map[string][]string) 134 // maps gating snap-ids to their snap names 135 gatingNames := make(map[string]string) 136 137 db := DB(s) 138 snapStates, err := snapstate.All(s) 139 if err != nil { 140 return nil, err 141 } 142 for instanceName, snapst := range snapStates { 143 info, err := snapst.CurrentInfo() 144 if err != nil { 145 return nil, err 146 } 147 if info.SnapID == "" { 148 continue 149 } 150 gatingID := info.SnapID 151 if gatingNames[gatingID] != "" { 152 continue 153 } 154 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 155 "series": release.Series, 156 "snap-id": gatingID, 157 }) 158 if err != nil { 159 return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q: %v", instanceName, err) 160 } 161 decl := a.(*asserts.SnapDeclaration) 162 control := decl.RefreshControl() 163 if len(control) == 0 { 164 continue 165 } 166 gatingNames[gatingID] = decl.SnapName() 167 for _, gatedID := range control { 168 controlled[gatedID] = append(controlled[gatedID], gatingID) 169 } 170 } 171 172 var errs []error 173 for _, candInfo := range snapInfos { 174 if ignoreValidation[candInfo.InstanceName()] { 175 validated = append(validated, candInfo) 176 continue 177 } 178 gatedID := candInfo.SnapID 179 gating := controlled[gatedID] 180 if len(gating) == 0 { // easy case, no refresh control 181 validated = append(validated, candInfo) 182 continue 183 } 184 185 var validationRefs []*asserts.Ref 186 187 fetching := func(f asserts.Fetcher) error { 188 for _, gatingID := range gating { 189 valref := &asserts.Ref{ 190 Type: asserts.ValidationType, 191 PrimaryKey: []string{release.Series, gatingID, gatedID, candInfo.Revision.String()}, 192 } 193 err := f.Fetch(valref) 194 if notFound, ok := err.(*asserts.NotFoundError); ok && notFound.Type == asserts.ValidationType { 195 return fmt.Errorf("no validation by %q", gatingNames[gatingID]) 196 } 197 if err != nil { 198 return fmt.Errorf("cannot find validation by %q: %v", gatingNames[gatingID], err) 199 } 200 validationRefs = append(validationRefs, valref) 201 } 202 return nil 203 } 204 err := doFetch(s, userID, deviceCtx, fetching) 205 if err != nil { 206 errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: %v", candInfo.InstanceName(), candInfo.Revision, err)) 207 continue 208 } 209 210 var revoked *asserts.Validation 211 for _, valref := range validationRefs { 212 a, err := valref.Resolve(db.Find) 213 if err != nil { 214 return nil, findError("internal error: cannot find just fetched %v", valref, err) 215 } 216 if val := a.(*asserts.Validation); val.Revoked() { 217 revoked = val 218 break 219 } 220 } 221 if revoked != nil { 222 errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: validation by %q (id %q) revoked", candInfo.InstanceName(), candInfo.Revision, gatingNames[revoked.SnapID()], revoked.SnapID())) 223 continue 224 } 225 226 validated = append(validated, candInfo) 227 } 228 229 if errs != nil { 230 return validated, &refreshControlError{errs} 231 } 232 233 return validated, nil 234 } 235 236 // BaseDeclaration returns the base-declaration assertion with policies governing all snaps. 237 func BaseDeclaration(s *state.State) (*asserts.BaseDeclaration, error) { 238 // TODO: switch keeping this in the DB and have it revisioned/updated 239 // via the store 240 baseDecl := asserts.BuiltinBaseDeclaration() 241 if baseDecl == nil { 242 return nil, &asserts.NotFoundError{Type: asserts.BaseDeclarationType} 243 } 244 return baseDecl, nil 245 } 246 247 // SnapDeclaration returns the snap-declaration for the given snap-id if it is present in the system assertion database. 248 func SnapDeclaration(s *state.State, snapID string) (*asserts.SnapDeclaration, error) { 249 db := DB(s) 250 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 251 "series": release.Series, 252 "snap-id": snapID, 253 }) 254 if err != nil { 255 return nil, err 256 } 257 return a.(*asserts.SnapDeclaration), nil 258 } 259 260 // Publisher returns the account assertion for publisher of the given snap-id if it is present in the system assertion database. 261 func Publisher(s *state.State, snapID string) (*asserts.Account, error) { 262 db := DB(s) 263 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 264 "series": release.Series, 265 "snap-id": snapID, 266 }) 267 if err != nil { 268 return nil, err 269 } 270 snapDecl := a.(*asserts.SnapDeclaration) 271 a, err = db.Find(asserts.AccountType, map[string]string{ 272 "account-id": snapDecl.PublisherID(), 273 }) 274 if err != nil { 275 return nil, fmt.Errorf("internal error: cannot find account assertion for the publisher of snap %q: %v", snapDecl.SnapName(), err) 276 } 277 return a.(*asserts.Account), nil 278 } 279 280 // Store returns the store assertion with the given name/id if it is 281 // present in the system assertion database. 282 func Store(s *state.State, store string) (*asserts.Store, error) { 283 db := DB(s) 284 a, err := db.Find(asserts.StoreType, map[string]string{ 285 "store": store, 286 }) 287 if err != nil { 288 return nil, err 289 } 290 return a.(*asserts.Store), nil 291 } 292 293 // AutoAliases returns the explicit automatic aliases alias=>app mapping for the given installed snap. 294 func AutoAliases(s *state.State, info *snap.Info) (map[string]string, error) { 295 if info.SnapID == "" { 296 // without declaration 297 return nil, nil 298 } 299 decl, err := SnapDeclaration(s, info.SnapID) 300 if err != nil { 301 return nil, fmt.Errorf("internal error: cannot find snap-declaration for installed snap %q: %v", info.InstanceName(), err) 302 } 303 explicitAliases := decl.Aliases() 304 if len(explicitAliases) != 0 { 305 return explicitAliases, nil 306 } 307 // XXX: old header fallback, just to keep edge working while we fix the 308 // store, to remove before next release! 309 oldAutoAliases := decl.AutoAliases() 310 if len(oldAutoAliases) == 0 { 311 return nil, nil 312 } 313 res := make(map[string]string, len(oldAutoAliases)) 314 for _, alias := range oldAutoAliases { 315 app := info.LegacyAliases[alias] 316 if app == nil { 317 // not a known alias anymore or yet, skip 318 continue 319 320 } 321 res[alias] = app.Name 322 } 323 return res, nil 324 } 325 326 func delayedCrossMgrInit() { 327 // hook validation of refreshes into snapstate logic 328 snapstate.ValidateRefreshes = ValidateRefreshes 329 // hook auto refresh of assertions (snap declarations) into snapstate 330 snapstate.AutoRefreshAssertions = AutoRefreshAssertions 331 // hook retrieving auto-aliases into snapstate logic 332 snapstate.AutoAliases = AutoAliases 333 } 334 335 // AutoRefreshAssertions tries to refresh all assertions 336 func AutoRefreshAssertions(s *state.State, userID int) error { 337 if err := RefreshSnapDeclarations(s, userID); err != nil { 338 return err 339 } 340 return RefreshValidationSetAssertions(s, userID) 341 } 342 343 // RefreshValidationSetAssertions tries to refresh all validation set 344 // assertions. 345 func RefreshValidationSetAssertions(s *state.State, userID int) error { 346 deviceCtx, err := snapstate.DevicePastSeeding(s, nil) 347 if err != nil { 348 return err 349 } 350 351 vsets, err := ValidationSets(s) 352 if err != nil { 353 return err 354 } 355 if len(vsets) == 0 { 356 return nil 357 } 358 359 if err := bulkRefreshValidationSetAsserts(s, vsets, userID, deviceCtx); err != nil { 360 return err 361 } 362 363 // update validation set tracking state 364 for _, vs := range vsets { 365 // TODO: for enforce mode check that the validation-sets don't conflict 366 // and are usable before moving Current. 367 if vs.Mode == Monitor && vs.PinnedAt == 0 { 368 headers := map[string]string{ 369 "series": release.Series, 370 "account-id": vs.AccountID, 371 "name": vs.Name, 372 } 373 db := DB(s) 374 as, err := db.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat()) 375 if err != nil { 376 return fmt.Errorf("internal error: cannot find assertion %v when refreshing validation-set assertions", headers) 377 } 378 if vs.Current != as.Sequence() { 379 vs.Current = as.Sequence() 380 UpdateValidationSet(s, vs) 381 } 382 } 383 } 384 385 return nil 386 } 387 388 // ResolveOptions carries extra options for ValidationSetAssertionForMonitor. 389 type ResolveOptions struct { 390 AllowLocalFallback bool 391 } 392 393 // ValidationSetAssertionForMonitor tries to fetch or refresh the validation 394 // set assertion with accountID/name/sequence (sequence is optional) using pool. 395 // If assertion cannot be fetched but exists locally and opts.AllowLocalFallback 396 // is set then the local one is returned 397 func ValidationSetAssertionForMonitor(st *state.State, accountID, name string, sequence int, pinned bool, userID int, opts *ResolveOptions) (as *asserts.ValidationSet, local bool, err error) { 398 if opts == nil { 399 opts = &ResolveOptions{} 400 } 401 deviceCtx, err := snapstate.DevicePastSeeding(st, nil) 402 if err != nil { 403 return nil, false, err 404 } 405 406 var vs asserts.Assertion 407 headers := map[string]string{ 408 "series": release.Series, 409 "account-id": accountID, 410 "name": name, 411 } 412 413 db := cachedDB(st) 414 415 // try to get existing one from db 416 if sequence > 0 { 417 headers["sequence"] = fmt.Sprintf("%d", sequence) 418 vs, err = db.Find(asserts.ValidationSetType, headers) 419 } else { 420 // find latest 421 vs, err = db.FindSequence(asserts.ValidationSetType, headers, -1, -1) 422 } 423 if err != nil && !asserts.IsNotFound(err) { 424 return nil, false, err 425 } 426 if err == nil { 427 as = vs.(*asserts.ValidationSet) 428 } 429 430 // try to resolve or update with pool 431 pool := asserts.NewPool(db, maxGroups) 432 atSeq := &asserts.AtSequence{ 433 Type: asserts.ValidationSetType, 434 SequenceKey: []string{release.Series, accountID, name}, 435 Sequence: sequence, 436 Pinned: pinned, 437 } 438 if as != nil { 439 atSeq.Revision = as.Revision() 440 } else { 441 atSeq.Revision = asserts.RevisionNotKnown 442 } 443 444 // resolve if not found locally, otherwise add for update 445 if as == nil { 446 if err := pool.AddUnresolvedSequence(atSeq, atSeq.Unique()); err != nil { 447 return nil, false, err 448 } 449 } else { 450 atSeq.Sequence = as.Sequence() 451 // found locally, try to update 452 atSeq.Revision = as.Revision() 453 if err := pool.AddSequenceToUpdate(atSeq, atSeq.Unique()); err != nil { 454 return nil, false, err 455 } 456 } 457 458 if err := resolvePoolNoFallback(st, pool, userID, deviceCtx); err != nil { 459 rerr, ok := err.(*resolvePoolError) 460 if ok && as != nil && opts.AllowLocalFallback { 461 if e := rerr.errors[atSeq.Unique()]; asserts.IsNotFound(e) { 462 // fallback: support the scenario of local assertion (snap ack) 463 // not available in the store. 464 return as, true, nil 465 } 466 } 467 return nil, false, err 468 } 469 470 // fetch the requested assertion again 471 if pinned { 472 vs, err = db.Find(asserts.ValidationSetType, headers) 473 } else { 474 vs, err = db.FindSequence(asserts.ValidationSetType, headers, -1, asserts.ValidationSetType.MaxSupportedFormat()) 475 } 476 if err == nil { 477 as = vs.(*asserts.ValidationSet) 478 } 479 return as, false, err 480 }