github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/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/overlord/snapstate" 33 "github.com/snapcore/snapd/overlord/state" 34 "github.com/snapcore/snapd/release" 35 "github.com/snapcore/snapd/snap" 36 ) 37 38 // Add the given assertion to the system assertion database. 39 func Add(s *state.State, a asserts.Assertion) error { 40 // TODO: deal together with asserts itself with (cascading) side effects of possible assertion updates 41 return cachedDB(s).Add(a) 42 } 43 44 // AddBatch adds the given assertion batch to the system assertion database. 45 func AddBatch(s *state.State, batch *asserts.Batch, opts *asserts.CommitOptions) error { 46 return batch.CommitTo(cachedDB(s), opts) 47 } 48 49 func findError(format string, ref *asserts.Ref, err error) error { 50 if asserts.IsNotFound(err) { 51 return fmt.Errorf(format, ref) 52 } else { 53 return fmt.Errorf(format+": %v", ref, err) 54 } 55 } 56 57 // RefreshSnapDeclarations refetches all the current snap declarations and their prerequisites. 58 func RefreshSnapDeclarations(s *state.State, userID int) error { 59 deviceCtx, err := snapstate.DevicePastSeeding(s, nil) 60 if err != nil { 61 return err 62 } 63 modelAs := deviceCtx.Model() 64 65 snapStates, err := snapstate.All(s) 66 if err != nil { 67 return nil 68 } 69 fetching := func(f asserts.Fetcher) error { 70 for _, snapst := range snapStates { 71 info, err := snapst.CurrentInfo() 72 if err != nil { 73 return err 74 } 75 if info.SnapID == "" { 76 continue 77 } 78 if err := snapasserts.FetchSnapDeclaration(f, info.SnapID); err != nil { 79 if notRetried, ok := err.(*httputil.PerstistentNetworkError); ok { 80 return notRetried 81 } 82 return fmt.Errorf("cannot refresh snap-declaration for %q: %v", info.InstanceName(), err) 83 } 84 } 85 86 // fetch store assertion if available 87 if modelAs.Store() != "" { 88 err := snapasserts.FetchStore(f, modelAs.Store()) 89 if err != nil && !asserts.IsNotFound(err) { 90 return err 91 } 92 } 93 94 return nil 95 } 96 return doFetch(s, userID, deviceCtx, fetching) 97 } 98 99 type refreshControlError struct { 100 errs []error 101 } 102 103 func (e *refreshControlError) Error() string { 104 if len(e.errs) == 1 { 105 return e.errs[0].Error() 106 } 107 l := []string{""} 108 for _, e := range e.errs { 109 l = append(l, e.Error()) 110 } 111 return fmt.Sprintf("refresh control errors:%s", strings.Join(l, "\n - ")) 112 } 113 114 // ValidateRefreshes validates the refresh candidate revisions represented by 115 // the snapInfos, looking for the needed refresh control validation assertions, 116 // it returns a validated subset in validated and a summary error if not all 117 // candidates validated. ignoreValidation is a set of snap-instance-names that 118 // should not be gated. 119 func ValidateRefreshes(s *state.State, snapInfos []*snap.Info, ignoreValidation map[string]bool, userID int, deviceCtx snapstate.DeviceContext) (validated []*snap.Info, err error) { 120 // maps gated snap-ids to gating snap-ids 121 controlled := make(map[string][]string) 122 // maps gating snap-ids to their snap names 123 gatingNames := make(map[string]string) 124 125 db := DB(s) 126 snapStates, err := snapstate.All(s) 127 if err != nil { 128 return nil, err 129 } 130 for instanceName, snapst := range snapStates { 131 info, err := snapst.CurrentInfo() 132 if err != nil { 133 return nil, err 134 } 135 if info.SnapID == "" { 136 continue 137 } 138 gatingID := info.SnapID 139 if gatingNames[gatingID] != "" { 140 continue 141 } 142 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 143 "series": release.Series, 144 "snap-id": gatingID, 145 }) 146 if err != nil { 147 return nil, fmt.Errorf("internal error: cannot find snap declaration for installed snap %q: %v", instanceName, err) 148 } 149 decl := a.(*asserts.SnapDeclaration) 150 control := decl.RefreshControl() 151 if len(control) == 0 { 152 continue 153 } 154 gatingNames[gatingID] = decl.SnapName() 155 for _, gatedID := range control { 156 controlled[gatedID] = append(controlled[gatedID], gatingID) 157 } 158 } 159 160 var errs []error 161 for _, candInfo := range snapInfos { 162 if ignoreValidation[candInfo.InstanceName()] { 163 validated = append(validated, candInfo) 164 continue 165 } 166 gatedID := candInfo.SnapID 167 gating := controlled[gatedID] 168 if len(gating) == 0 { // easy case, no refresh control 169 validated = append(validated, candInfo) 170 continue 171 } 172 173 var validationRefs []*asserts.Ref 174 175 fetching := func(f asserts.Fetcher) error { 176 for _, gatingID := range gating { 177 valref := &asserts.Ref{ 178 Type: asserts.ValidationType, 179 PrimaryKey: []string{release.Series, gatingID, gatedID, candInfo.Revision.String()}, 180 } 181 err := f.Fetch(valref) 182 if notFound, ok := err.(*asserts.NotFoundError); ok && notFound.Type == asserts.ValidationType { 183 return fmt.Errorf("no validation by %q", gatingNames[gatingID]) 184 } 185 if err != nil { 186 return fmt.Errorf("cannot find validation by %q: %v", gatingNames[gatingID], err) 187 } 188 validationRefs = append(validationRefs, valref) 189 } 190 return nil 191 } 192 err := doFetch(s, userID, deviceCtx, fetching) 193 if err != nil { 194 errs = append(errs, fmt.Errorf("cannot refresh %q to revision %s: %v", candInfo.InstanceName(), candInfo.Revision, err)) 195 continue 196 } 197 198 var revoked *asserts.Validation 199 for _, valref := range validationRefs { 200 a, err := valref.Resolve(db.Find) 201 if err != nil { 202 return nil, findError("internal error: cannot find just fetched %v", valref, err) 203 } 204 if val := a.(*asserts.Validation); val.Revoked() { 205 revoked = val 206 break 207 } 208 } 209 if revoked != nil { 210 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())) 211 continue 212 } 213 214 validated = append(validated, candInfo) 215 } 216 217 if errs != nil { 218 return validated, &refreshControlError{errs} 219 } 220 221 return validated, nil 222 } 223 224 // BaseDeclaration returns the base-declaration assertion with policies governing all snaps. 225 func BaseDeclaration(s *state.State) (*asserts.BaseDeclaration, error) { 226 // TODO: switch keeping this in the DB and have it revisioned/updated 227 // via the store 228 baseDecl := asserts.BuiltinBaseDeclaration() 229 if baseDecl == nil { 230 return nil, &asserts.NotFoundError{Type: asserts.BaseDeclarationType} 231 } 232 return baseDecl, nil 233 } 234 235 // SnapDeclaration returns the snap-declaration for the given snap-id if it is present in the system assertion database. 236 func SnapDeclaration(s *state.State, snapID string) (*asserts.SnapDeclaration, error) { 237 db := DB(s) 238 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 239 "series": release.Series, 240 "snap-id": snapID, 241 }) 242 if err != nil { 243 return nil, err 244 } 245 return a.(*asserts.SnapDeclaration), nil 246 } 247 248 // Publisher returns the account assertion for publisher of the given snap-id if it is present in the system assertion database. 249 func Publisher(s *state.State, snapID string) (*asserts.Account, error) { 250 db := DB(s) 251 a, err := db.Find(asserts.SnapDeclarationType, map[string]string{ 252 "series": release.Series, 253 "snap-id": snapID, 254 }) 255 if err != nil { 256 return nil, err 257 } 258 snapDecl := a.(*asserts.SnapDeclaration) 259 a, err = db.Find(asserts.AccountType, map[string]string{ 260 "account-id": snapDecl.PublisherID(), 261 }) 262 if err != nil { 263 return nil, fmt.Errorf("internal error: cannot find account assertion for the publisher of snap %q: %v", snapDecl.SnapName(), err) 264 } 265 return a.(*asserts.Account), nil 266 } 267 268 // Store returns the store assertion with the given name/id if it is 269 // present in the system assertion database. 270 func Store(s *state.State, store string) (*asserts.Store, error) { 271 db := DB(s) 272 a, err := db.Find(asserts.StoreType, map[string]string{ 273 "store": store, 274 }) 275 if err != nil { 276 return nil, err 277 } 278 return a.(*asserts.Store), nil 279 } 280 281 // AutoAliases returns the explicit automatic aliases alias=>app mapping for the given installed snap. 282 func AutoAliases(s *state.State, info *snap.Info) (map[string]string, error) { 283 if info.SnapID == "" { 284 // without declaration 285 return nil, nil 286 } 287 decl, err := SnapDeclaration(s, info.SnapID) 288 if err != nil { 289 return nil, fmt.Errorf("internal error: cannot find snap-declaration for installed snap %q: %v", info.InstanceName(), err) 290 } 291 explicitAliases := decl.Aliases() 292 if len(explicitAliases) != 0 { 293 return explicitAliases, nil 294 } 295 // XXX: old header fallback, just to keep edge working while we fix the 296 // store, to remove before next release! 297 oldAutoAliases := decl.AutoAliases() 298 if len(oldAutoAliases) == 0 { 299 return nil, nil 300 } 301 res := make(map[string]string, len(oldAutoAliases)) 302 for _, alias := range oldAutoAliases { 303 app := info.LegacyAliases[alias] 304 if app == nil { 305 // not a known alias anymore or yet, skip 306 continue 307 308 } 309 res[alias] = app.Name 310 } 311 return res, nil 312 } 313 314 func delayedCrossMgrInit() { 315 // hook validation of refreshes into snapstate logic 316 snapstate.ValidateRefreshes = ValidateRefreshes 317 // hook auto refresh of assertions into snapstate 318 snapstate.AutoRefreshAssertions = AutoRefreshAssertions 319 // hook retrieving auto-aliases into snapstate logic 320 snapstate.AutoAliases = AutoAliases 321 } 322 323 // AutoRefreshAssertions tries to refresh all assertions 324 func AutoRefreshAssertions(s *state.State, userID int) error { 325 return RefreshSnapDeclarations(s, userID) 326 }