github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+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/boot/diskboot" 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, mountPath, 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 e := mount.Unmount(mountPath, true, false); e != nil { 68 log.Printf("Unmount failed. PANIC") 69 panic(e) 70 } 71 72 if err != nil { 73 log.Printf("Error reading policy file:mountPath=%s, passed=%s", mntFilePath, val) 74 return nil 75 } 76 return d 77 } 78 79 /* 80 * scanBlockDevice scans an already mounted block device inside directories 81 * "/", "/efi" and "/boot" for policy file and if found, returns the policy byte as a byte slice. 82 * 83 * e.g: if you mount /dev/sda1 on /tmp/sda1, 84 * then mountPath would be /tmp/sda1 85 * and searchPath would be /tmp/sda1/securelaunch.policy, 86 * /tmp/sda1/efi/securelaunch.policy and /tmp/sda1/boot/securelaunch.policy 87 * respectively for each iteration of loop over SearchRoots slice. 88 */ 89 func scanBlockDevice(mountPath string) []byte { 90 91 log.Printf("scanBlockDevice") 92 // scan for securelaunch.policy under /, /efi, or /boot 93 var SearchRoots = []string{"/", "/efi", "/boot"} 94 for _, c := range SearchRoots { 95 96 searchPath := filepath.Join(mountPath, c, "securelaunch.policy") 97 if _, err := os.Stat(searchPath); os.IsNotExist(err) { 98 continue 99 } 100 101 d, err := ioutil.ReadFile(searchPath) 102 if err != nil { 103 // Policy File not found. Moving on to next search root... 104 log.Printf("Error reading policy file %s, continuing", searchPath) 105 continue 106 } 107 log.Printf("policy file found on mountPath=%s, directory =%s", mountPath, c) 108 return d // return when first policy file found 109 } 110 111 return nil 112 } 113 114 /* 115 * locate searches for policy file on the kernel cmdline. 116 * if not found on cmdline, it looks for policy file on each block device 117 * under "/", "efi" and "/boot" directories. 118 * 119 * Steps: 120 * 1. Check if kernel param sl_policy is set, 121 * parse the string 122 * 2. Iterate through each local block device, 123 * - mount the block device 124 * - scan for securelaunch.policy under /, /efi, or /boot 125 * 3 Read in policy file 126 */ 127 func locate() ([]byte, error) { 128 129 d := scanKernelCmdLine() 130 if d != nil { 131 return d, nil 132 } 133 134 slaunch.Debug("Searching and mounting block devices with bootable configs") 135 blkDevices := diskboot.FindDevices("/sys/class/block/*") // FindDevices find and *mounts* the devices. 136 if len(blkDevices) == 0 { 137 return nil, errors.New("no block devices found") 138 } 139 140 for _, device := range blkDevices { 141 devicePath, mountPath := device.MountPoint.Device, device.MountPoint.Path 142 slaunch.Debug("scanning for policy file under devicePath=%s, mountPath=%s", devicePath, mountPath) 143 raw := scanBlockDevice(mountPath) 144 if e := mount.Unmount(mountPath, true, false); e != nil { 145 log.Printf("Unmount failed. PANIC") 146 panic(e) 147 } 148 149 if raw == nil { 150 log.Printf("no policy file found under this device") 151 continue 152 } 153 154 slaunch.Debug("policy file found at devicePath=%s", devicePath) 155 return raw, nil 156 } 157 158 return nil, errors.New("policy file not found anywhere") 159 } 160 161 /* 162 * parse accepts a JSON file as input, parses 163 * it into a well defined Policy structure (parse) and 164 * returns a pointer to Policy structure. 165 */ 166 func parse(pf []byte) (*Policy, error) { 167 p := &Policy{} 168 var parse struct { 169 DefaultAction string `json:"default_action"` 170 Collectors []json.RawMessage `json:"collectors"` 171 Attestor json.RawMessage `json:"attestor"` 172 Launcher json.RawMessage `json:"launcher"` 173 EventLog json.RawMessage `json:"eventlog"` 174 } 175 176 if err := json.Unmarshal(pf, &parse); err != nil { 177 log.Printf("parse SL Policy: Unmarshall error for entire policy file!! err=%v", err) 178 return nil, err 179 } 180 181 p.DefaultAction = parse.DefaultAction 182 183 for _, c := range parse.Collectors { 184 collector, err := measurement.GetCollector(c) 185 if err != nil { 186 log.Printf("GetCollector err:c=%s, collector=%v", c, collector) 187 return nil, err 188 } 189 p.Collectors = append(p.Collectors, collector) 190 } 191 192 if len(parse.Launcher) > 0 { 193 if err := json.Unmarshal(parse.Launcher, &p.Launcher); err != nil { 194 log.Printf("parse policy: Launcher Unmarshall error=%v!!", err) 195 return nil, err 196 } 197 } 198 199 if len(parse.EventLog) > 0 { 200 if err := json.Unmarshal(parse.EventLog, &p.EventLog); err != nil { 201 log.Printf("parse policy: EventLog Unmarshall error=%v!!", err) 202 return nil, err 203 } 204 } 205 return p, nil 206 } 207 208 /* 209 * Get locates and parses the policy file. 210 * 211 * The file is located by the following priority: 212 * 213 * (1) kernel cmdline "sl_policy" argument. 214 * (2) a file on any partition on any disk called "securelaunch.policy" 215 */ 216 func Get() (*Policy, error) { 217 b, err := locate() 218 if err != nil { 219 return nil, err 220 } 221 policy, err := parse(b) 222 if err != nil { 223 return nil, err 224 } 225 if policy == nil { 226 return nil, fmt.Errorf("no policy found") 227 } 228 return policy, nil 229 }