github.com/freetocompute/snapd@v0.0.0-20210618182524-2fb355d72fd9/seed/seed.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2019-2021 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 seed implements loading and validating of seed data. 21 package seed 22 23 import ( 24 "errors" 25 "fmt" 26 "path/filepath" 27 "time" 28 29 "github.com/snapcore/snapd/asserts" 30 "github.com/snapcore/snapd/seed/internal" 31 "github.com/snapcore/snapd/snap" 32 "github.com/snapcore/snapd/timings" 33 ) 34 35 var ( 36 ErrNoAssertions = errors.New("no seed assertions") 37 ErrNoMeta = errors.New("no seed metadata") 38 ) 39 40 // Snap holds the details of a snap in a seed. 41 type Snap struct { 42 Path string 43 44 SideInfo *snap.SideInfo 45 46 // EssentialType is the type of the snap as specified by the model. 47 // Provided only for essential snaps (Essential = true). 48 EssentialType snap.Type 49 50 Essential bool 51 Required bool 52 53 // options 54 Channel string 55 DevMode bool 56 Classic bool 57 } 58 59 func (s *Snap) SnapName() string { 60 return s.SideInfo.RealName 61 } 62 63 func (s *Snap) ID() string { 64 return s.SideInfo.SnapID 65 } 66 67 // PlaceInfo returns a PlaceInfo for the seed snap. 68 func (s *Snap) PlaceInfo() snap.PlaceInfo { 69 return &snap.Info{SideInfo: *s.SideInfo} 70 } 71 72 // Seed supports loading assertions and seed snaps' metadata. 73 type Seed interface { 74 // LoadAssertions loads all assertions from the seed with 75 // cross-checks. A read-only view on an assertions database 76 // can be passed in together with a commitTo function which 77 // will be used to commit the assertions to the underlying 78 // database. If db is nil an internal temporary database will 79 // be setup instead. ErrNoAssertions will be returned if there 80 // is no assertions directory in the seed, this is legitimate 81 // only on classic. 82 LoadAssertions(db asserts.RODatabase, commitTo func(*asserts.Batch) error) error 83 84 // Model returns the seed provided model assertion. 85 // It will panic if called before LoadAssertions. 86 Model() *asserts.Model 87 88 // Brand returns the brand information of the seed. 89 // It will panic if called before LoadAssertions. 90 Brand() (*asserts.Account, error) 91 92 // LoadEssentialMeta loads the seed's snaps metadata for the 93 // essential snaps with types in the essentialTypes set while 94 // verifying them against assertions. It can return ErrNoMeta 95 // if there is no metadata nor snaps in the seed, this is 96 // legitimate only on classic. It can be called multiple times 97 // if needed before invoking LoadMeta. 98 // It will panic if called before LoadAssertions. 99 LoadEssentialMeta(essentialTypes []snap.Type, tm timings.Measurer) error 100 101 // LoadMeta loads the seed and seed's snaps metadata while 102 // verifying the underlying snaps against assertions. It can 103 // return ErrNoMeta if there is no metadata nor snaps in the 104 // seed, this is legitimate only on classic. 105 // It will panic if called before LoadAssertions. 106 LoadMeta(tm timings.Measurer) error 107 108 // UsesSnapdSnap returns whether the system as defined by the 109 // seed will use the snapd snap, after LoadMeta. 110 UsesSnapdSnap() bool 111 112 // EssentialSnaps returns the essential snaps as defined by 113 // the seed, after LoadMeta. 114 EssentialSnaps() []*Snap 115 116 // ModeSnaps returns the snaps that should be available 117 // in the given mode as defined by the seed, after LoadMeta. 118 ModeSnaps(mode string) ([]*Snap, error) 119 } 120 121 // Open returns a Seed implementation for the seed at seedDir. 122 // label if not empty is used to identify a Core 20 recovery system seed. 123 func Open(seedDir, label string) (Seed, error) { 124 if label != "" { 125 if err := internal.ValidateUC20SeedSystemLabel(label); err != nil { 126 return nil, err 127 } 128 return &seed20{systemDir: filepath.Join(seedDir, "systems", label)}, nil 129 } 130 return &seed16{seedDir: seedDir}, nil 131 } 132 133 // ReadSystemEssential retrieves in one go information about the model 134 // and essential snaps of the given types for the Core 20 recovery 135 // system seed specified by seedDir and label (which cannot be empty). 136 func ReadSystemEssential(seedDir, label string, essentialTypes []snap.Type, tm timings.Measurer) (*asserts.Model, []*Snap, error) { 137 if label == "" { 138 return nil, nil, fmt.Errorf("system label cannot be empty") 139 } 140 seed20, err := Open(seedDir, label) 141 if err != nil { 142 return nil, nil, err 143 } 144 145 // load assertions into a temporary database 146 if err := seed20.LoadAssertions(nil, nil); err != nil { 147 return nil, nil, err 148 } 149 150 // load and verify info about essential snaps 151 if err := seed20.LoadEssentialMeta(essentialTypes, tm); err != nil { 152 return nil, nil, err 153 } 154 155 return seed20.Model(), seed20.EssentialSnaps(), nil 156 } 157 158 // ReadSystemEssentialAndBetterEarliestTime retrieves in one go 159 // information about the model and essential snaps of the given types 160 // for the Core 20 recovery system seed specified by seedDir and label 161 // (which cannot be empty). 162 // It can operate even if current system time is unreliable by taking 163 // a earliestTime lower bound for current time. 164 // It returns as well an improved lower bound by considering 165 // appropriate assertions in the seed. 166 func ReadSystemEssentialAndBetterEarliestTime(seedDir, label string, essentialTypes []snap.Type, earliestTime time.Time, tm timings.Measurer) (*asserts.Model, []*Snap, time.Time, error) { 167 if label == "" { 168 return nil, nil, time.Time{}, fmt.Errorf("system label cannot be empty") 169 } 170 seed20, err := Open(seedDir, label) 171 if err != nil { 172 return nil, nil, time.Time{}, err 173 174 } 175 176 improve := func(a asserts.Assertion) { 177 // we consider only snap-revision and snap-declaration 178 // assertions here as they must be store-signed, see 179 // checkConsistency for each type 180 // other assertions might be signed not by the store 181 // nor the brand so they might be provided by an 182 // attacker, signed using a registered key but 183 // containing unreliable time 184 var tstamp time.Time 185 switch a.Type() { 186 default: 187 // not one of the store-signed assertion types 188 return 189 case asserts.SnapRevisionType: 190 sr := a.(*asserts.SnapRevision) 191 tstamp = sr.Timestamp() 192 case asserts.SnapDeclarationType: 193 sd := a.(*asserts.SnapDeclaration) 194 tstamp = sd.Timestamp() 195 } 196 if tstamp.After(earliestTime) { 197 earliestTime = tstamp 198 } 199 } 200 201 // create a temporary database, commitTo will invoke improve 202 db, commitTo, err := newMemAssertionsDB(improve) 203 if err != nil { 204 return nil, nil, time.Time{}, err 205 } 206 // set up the database to check for key expiry only assuming 207 // earliestTime (if not zero) 208 db.SetEarliestTime(earliestTime) 209 210 // load assertions into the temporary database 211 if err := seed20.LoadAssertions(db, commitTo); err != nil { 212 return nil, nil, time.Time{}, err 213 } 214 215 // load and verify info about essential snaps 216 if err := seed20.LoadEssentialMeta(essentialTypes, tm); err != nil { 217 return nil, nil, time.Time{}, err 218 } 219 220 // consider the model's timestamp as well - it must be signed 221 // by the brand so is safe from the attack detailed above 222 mod := seed20.Model() 223 if mod.Timestamp().After(earliestTime) { 224 earliestTime = mod.Timestamp() 225 } 226 227 return mod, seed20.EssentialSnaps(), earliestTime, nil 228 }