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