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  }