github.com/mvdan/u-root-coreutils@v0.0.0-20230122170626-c2eef2898555/pkg/securelaunch/policy/policy.go (about) 1 // Copyright 2019 the u-root Authors. All rights reserved 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // Package policy locates and parses a JSON policy file. 6 package policy 7 8 import ( 9 "encoding/json" 10 "errors" 11 "fmt" 12 "log" 13 "os" 14 "path/filepath" 15 16 "github.com/mvdan/u-root-coreutils/pkg/cmdline" 17 "github.com/mvdan/u-root-coreutils/pkg/mount" 18 slaunch "github.com/mvdan/u-root-coreutils/pkg/securelaunch" 19 "github.com/mvdan/u-root-coreutils/pkg/securelaunch/eventlog" 20 "github.com/mvdan/u-root-coreutils/pkg/securelaunch/launcher" 21 "github.com/mvdan/u-root-coreutils/pkg/securelaunch/measurement" 22 ) 23 24 // Policy describes the policy used to drive the security engine. 25 // 26 // The policy is stored as a JSON file. 27 type Policy struct { 28 Collectors []measurement.Collector 29 Launcher launcher.Launcher 30 EventLog eventlog.EventLog 31 } 32 33 // policyBytes is a byte slice to hold a copy of the policy file in memory. 34 var policyBytes []byte 35 36 // scanKernelCmdLine scans the kernel cmdline for the 'sl_policy' flag. 37 // When set it provides location of the policy file on disk. It reads it and 38 // returns the policy file as a byte slice. 39 // 40 // The format of sl_policy flag is as follows: 41 // sl_policy=<block device identifier>:<path> 42 // e.g., sda:/boot/securelaunch.policy 43 // e.g., 4qccd342-12zr-4e99-9ze7-1234cb1234c4:/foo/securelaunch.policy 44 func scanKernelCmdLine() []byte { 45 slaunch.Debug("scanKernelCmdLine: scanning kernel cmd line for *sl_policy* flag") 46 val, ok := cmdline.Flag("sl_policy") 47 if !ok { 48 log.Printf("scanKernelCmdLine: sl_policy cmdline flag is not set") 49 return nil 50 } 51 52 // val is of type sda:path/to/file or UUID:path/to/file 53 mntFilePath, e := slaunch.GetMountedFilePath(val, mount.MS_RDONLY) // false means readonly mount 54 if e != nil { 55 log.Printf("scanKernelCmdLine: GetMountedFilePath err=%v", e) 56 return nil 57 } 58 slaunch.Debug("scanKernelCmdLine: Reading file=%s", mntFilePath) 59 60 d, err := os.ReadFile(mntFilePath) 61 if err != nil { 62 log.Printf("Error reading policy file:mountPath=%s, passed=%s", mntFilePath, val) 63 return nil 64 } 65 return d 66 } 67 68 // scanBlockDevice scans for the policy file on already mounted block devices. 69 // It looks in the "/", "/efi", and "/boot" directories. If found, it returns 70 // the policy file as a byte slice. 71 // 72 // e.g., if /dev/sda1 is mounted on /tmp/sda1, then mountPath would be 73 // 74 // /tmp/sda1 and searchPath would be /tmp/sda1/securelaunch.policy, 75 // /tmp/sda1/efi/securelaunch.policy, and /tmp/sda1/boot/securelaunch.policy 76 // respectively for each iteration of loop over SearchRoots slice. 77 func scanBlockDevice(mountPath string) []byte { 78 log.Printf("scanBlockDevice") 79 // scan for securelaunch.policy under /, /efi, or /boot 80 SearchRoots := []string{"/", "/efi", "/boot"} 81 for _, c := range SearchRoots { 82 83 searchPath := filepath.Join(mountPath, c, "securelaunch.policy") 84 if _, err := os.Stat(searchPath); os.IsNotExist(err) { 85 continue 86 } 87 88 d, err := os.ReadFile(searchPath) 89 if err != nil { 90 // Policy File not found. Moving on to next search root... 91 log.Printf("Error reading policy file %s, continuing", searchPath) 92 continue 93 } 94 log.Printf("policy file found on mountPath=%s, directory =%s", mountPath, c) 95 return d // return when first policy file found 96 } 97 98 return nil 99 } 100 101 // locate searches for the policy file on the kernel cmdline. 102 // If it's not found on cmdline, it looks for the policy file on each block 103 // device in the "/", "efi", and "/boot" directories. 104 func locate() ([]byte, error) { 105 d := scanKernelCmdLine() 106 if d != nil { 107 return d, nil 108 } 109 110 slaunch.Debug("Searching for block devices") 111 if err := slaunch.GetBlkInfo(); err != nil { 112 return nil, err 113 } 114 115 // devName = sda, mountPath = /tmp/sluinit-FOO/ 116 for _, device := range slaunch.StorageBlkDevices { 117 118 devName := device.Name 119 mountPath, err := slaunch.MountDevice(device, mount.MS_RDONLY) 120 if err != nil { 121 log.Printf("failed to mount %s, continuing to next block device", devName) 122 continue 123 } 124 125 slaunch.Debug("scanning for policy file under devName=%s, mountPath=%s", devName, mountPath) 126 raw := scanBlockDevice(mountPath) 127 if raw == nil { 128 log.Printf("no policy file found under this device") 129 continue 130 } 131 132 slaunch.Debug("policy file found at devName=%s", devName) 133 return raw, nil 134 } 135 136 return nil, errors.New("policy file not found anywhere") 137 } 138 139 // parse accepts a JSON file as input, parses it into a well defined Policy 140 // structure and returns a pointer to the Policy structure. 141 func parse(pf []byte) (*Policy, error) { 142 p := &Policy{} 143 var parse struct { 144 Collectors []json.RawMessage `json:"collectors"` 145 Attestor json.RawMessage `json:"attestor"` 146 Launcher json.RawMessage `json:"launcher"` 147 EventLog json.RawMessage `json:"eventlog"` 148 } 149 150 if err := json.Unmarshal(pf, &parse); err != nil { 151 log.Printf("parse SL Policy: Unmarshall error for entire policy file!! err=%v", err) 152 return nil, err 153 } 154 155 for _, c := range parse.Collectors { 156 collector, err := measurement.GetCollector(c) 157 if err != nil { 158 log.Printf("GetCollector err:c=%s, collector=%v", c, collector) 159 return nil, err 160 } 161 p.Collectors = append(p.Collectors, collector) 162 } 163 164 if len(parse.Launcher) > 0 { 165 if err := json.Unmarshal(parse.Launcher, &p.Launcher); err != nil { 166 log.Printf("parse policy: Launcher Unmarshall error=%v!!", err) 167 return nil, err 168 } 169 } 170 171 if len(parse.EventLog) > 0 { 172 if err := json.Unmarshal(parse.EventLog, &p.EventLog); err != nil { 173 log.Printf("parse policy: EventLog Unmarshall error=%v!!", err) 174 return nil, err 175 } 176 } 177 return p, nil 178 } 179 180 // Measure measures the policy file. 181 func Measure() error { 182 if len(policyBytes) == 0 { 183 return fmt.Errorf("policy file not yet loaded or empty") 184 } 185 186 eventDesc := "File Collector: measured securelaunch policy file" 187 if err := measurement.HashBytes(policyBytes, eventDesc); err != nil { 188 log.Printf("policy: ERR: could not measure policy file: %v", err) 189 return err 190 } 191 192 return nil 193 } 194 195 // Get locates and parses the policy file. 196 // 197 // The file is located by the following priority: 198 // 1. the kernel cmdline `sl_policy` argument. 199 // 2. a file on any partition on any disk called "securelaunch.policy". 200 func Get() (*Policy, error) { 201 policyBytes, err := locate() 202 if err != nil { 203 return nil, err 204 } 205 206 policy, err := parse(policyBytes) 207 if err != nil { 208 return nil, err 209 } 210 if policy == nil { 211 return nil, fmt.Errorf("no policy found") 212 } 213 214 return policy, nil 215 }