github.com/xyproto/u-root@v6.0.1-0.20200302025726-5528e0c77a3c+incompatible/pkg/securelaunch/measurement/dmi.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 measurement
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"log"
    14  
    15  	"github.com/digitalocean/go-smbios/smbios"
    16  	slaunch "github.com/u-root/u-root/pkg/securelaunch"
    17  	"github.com/u-root/u-root/pkg/securelaunch/tpm"
    18  )
    19  
    20  // DMI Events are expected to be a COMBINED_EVENT extend, as such the json
    21  // definition is designed to allow clusters of DMI fields/strings.
    22  //
    23  // Example json:
    24  //	{
    25  //		"type": "dmi",
    26  //		[
    27  //			{
    28  //				"label": "BIOS",
    29  //				"fields": [
    30  //					"bios-vendor",
    31  //					"bios-version",
    32  //					"bios-release-date"
    33  //				]
    34  //			}
    35  //			{
    36  //				"label": "System",
    37  //				"fields": [
    38  //					"system-manufacturer",
    39  //					"system-product-name",
    40  //					"system-version"
    41  //				]
    42  //			}
    43  //		]
    44  //	}
    45  type fieldCluster struct {
    46  	Label  string   `json:"label"`
    47  	Fields []string `json:"fields"`
    48  }
    49  
    50  /* describes the "dmi" portion of policy file */
    51  type DmiCollector struct {
    52  	Type     string         `json:"type"`
    53  	Clusters []fieldCluster `json:"events"`
    54  }
    55  
    56  /*
    57   * NewDmiCollector extracts the "dmi" portion from the policy file.
    58   * initializes a new DmiCollector structure.
    59   * returns error if unmarshalling of DmiCollector fails
    60   */
    61  func NewDmiCollector(config []byte) (Collector, error) {
    62  	slaunch.Debug("New DMI Collector initialized")
    63  	var dc = new(DmiCollector)
    64  	err := json.Unmarshal(config, &dc)
    65  	if err != nil {
    66  		return nil, err
    67  	}
    68  	return dc, nil
    69  }
    70  
    71  /*
    72   * below look up table is from man dmidecode.
    73   * used to lookup the dmi type parsed from policy file.
    74   * e.g if policy file contains BIOS, this table would return 0.
    75   */
    76  var type_table = map[string]uint8{
    77  	"BIOS":                             0,
    78  	"System":                           1,
    79  	"Base Board":                       2,
    80  	"Chassis":                          3,
    81  	"Processor":                        4,
    82  	"Memory Controller":                5,
    83  	"Memory Module":                    6,
    84  	"Cache":                            7,
    85  	"Port Connector":                   8,
    86  	"System Slots":                     9,
    87  	"On Board Devices":                 10,
    88  	"OEM Strings":                      11,
    89  	"System Configuration Options":     12,
    90  	"BIOS Language":                    13,
    91  	"Group Associations":               14,
    92  	"System Event Log":                 15,
    93  	"Physical Memory Array":            16,
    94  	"Memory Device":                    17,
    95  	"32-bit Memory Error":              18,
    96  	"Memory Array Mapped Address":      19,
    97  	"Memory Device Mapped Address":     20,
    98  	"Built-in Pointing Device":         21,
    99  	"Portable Battery":                 22,
   100  	"System Reset":                     23,
   101  	"Hardware Security":                24,
   102  	"System Power Controls":            25,
   103  	"Voltage Probe":                    26,
   104  	"Cooling Device":                   27,
   105  	"Temperature Probe":                28,
   106  	"Electrical Current Probe":         29,
   107  	"Out-of-band Remote Access":        30,
   108  	"Boot Integrity Services":          31,
   109  	"System Boot":                      32,
   110  	"64-bit Memory Error":              33,
   111  	"Management Device":                34,
   112  	"Management Device Component":      35,
   113  	"Management Device Threshold Data": 36,
   114  	"Memory Channel":                   37,
   115  	"IPMI Device":                      38,
   116  	"Power Supply":                     39,
   117  	"Additional Information":           40,
   118  	"Onboard Device":                   41,
   119  }
   120  
   121  /*
   122   * Collect satisfies collector interface. It calls
   123   * 1. smbios package to get all smbios data,
   124   * 2. then, filters smbios data based on type provided in policy file, and
   125   * 3. the filtered data is then measured into the tpmHandle (tpm device).
   126   */
   127  func (s *DmiCollector) Collect(tpmHandle io.ReadWriteCloser) error {
   128  	slaunch.Debug("DMI Collector: Entering ")
   129  	if s.Type != "dmi" {
   130  		return errors.New("Invalid type passed to a DmiCollector method")
   131  	}
   132  
   133  	// Find SMBIOS data in operating system-specific location.
   134  	rc, _, err := smbios.Stream()
   135  	if err != nil {
   136  		return fmt.Errorf("failed to open stream: %v", err)
   137  	}
   138  
   139  	// Be sure to close the stream!
   140  	defer rc.Close()
   141  
   142  	// Decode SMBIOS structures from the stream.
   143  	d := smbios.NewDecoder(rc)
   144  	data, err := d.Decode()
   145  	if err != nil {
   146  		return fmt.Errorf("failed to decode structures: %v", err)
   147  	}
   148  
   149  	var labels []string // collect all types entered by user in one slice
   150  	for _, fieldCluster := range s.Clusters {
   151  		labels = append(labels, fieldCluster.Label)
   152  	}
   153  
   154  	for _, k := range data { // k ==> data for each dmi type
   155  		// Only look at types mentioned in policy file.
   156  		for _, label := range labels {
   157  			if k.Header.Type != type_table[label] {
   158  				continue
   159  			}
   160  
   161  			slaunch.Debug("DMI Collector: Hashing %s information", label)
   162  			b := new(bytes.Buffer)
   163  			for _, str := range k.Strings {
   164  				b.WriteString(str)
   165  			}
   166  
   167  			// TODO: Extract and Measure specific "Fields" of a FieldCluster on user's request.
   168  			// For example: for BIOS type(type=0), currently we measure entire output
   169  			// but in future we could measure individual fields like bios-vendor, bios-version etc.
   170  
   171  			slaunch.Debug("DMI Collector: Measured dmi label=%s", label)
   172  			if e := tpm.ExtendPCRDebug(tpmHandle, pcr, bytes.NewReader(b.Bytes())); e != nil {
   173  				log.Printf("DMI Collector: err =%v", e)
   174  				return e
   175  			}
   176  		}
   177  	}
   178  
   179  	return nil
   180  }