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  }