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