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  }