Black Lives Matter. Support the Equal Justice Initiative.

Source file src/cmd/internal/codesign/codesign.go

Documentation: cmd/internal/codesign

     1  // Copyright 2020 The Go 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 codesign provides basic functionalities for
     6  // ad-hoc code signing of Mach-O files.
     7  //
     8  // This is not a general tool for code-signing. It is made
     9  // specifically for the Go toolchain. It uses the same
    10  // ad-hoc signing algorithm as the Darwin linker.
    11  package codesign
    12  
    13  import (
    14  	"crypto/sha256"
    15  	"debug/macho"
    16  	"encoding/binary"
    17  	"io"
    18  )
    19  
    20  // Code signature layout.
    21  //
    22  // The code signature is a block of bytes that contains
    23  // a SuperBlob, which contains one or more Blobs. For ad-hoc
    24  // signing, a single CodeDirectory Blob suffices.
    25  //
    26  // A SuperBlob starts with its header (the binary representation
    27  // of the SuperBlob struct), followed by a list of (in our case,
    28  // one) Blobs (offset and size). A CodeDirectory Blob starts
    29  // with its head (the binary representation of CodeDirectory struct),
    30  // followed by the identifier (as a C string) and the hashes, at
    31  // the corresponding offsets.
    32  //
    33  // The signature data must be included in the __LINKEDIT segment.
    34  // In the Mach-O file header, an LC_CODE_SIGNATURE load command
    35  // points to the data.
    36  
    37  const (
    38  	pageSizeBits = 12
    39  	pageSize     = 1 << pageSizeBits
    40  )
    41  
    42  const LC_CODE_SIGNATURE = 0x1d
    43  
    44  // Constants and struct layouts are from
    45  // https://opensource.apple.com/source/xnu/xnu-4903.270.47/osfmk/kern/cs_blobs.h
    46  
    47  const (
    48  	CSMAGIC_REQUIREMENT        = 0xfade0c00 // single Requirement blob
    49  	CSMAGIC_REQUIREMENTS       = 0xfade0c01 // Requirements vector (internal requirements)
    50  	CSMAGIC_CODEDIRECTORY      = 0xfade0c02 // CodeDirectory blob
    51  	CSMAGIC_EMBEDDED_SIGNATURE = 0xfade0cc0 // embedded form of signature data
    52  	CSMAGIC_DETACHED_SIGNATURE = 0xfade0cc1 // multi-arch collection of embedded signatures
    53  
    54  	CSSLOT_CODEDIRECTORY = 0 // slot index for CodeDirectory
    55  )
    56  
    57  const (
    58  	CS_HASHTYPE_SHA1             = 1
    59  	CS_HASHTYPE_SHA256           = 2
    60  	CS_HASHTYPE_SHA256_TRUNCATED = 3
    61  	CS_HASHTYPE_SHA384           = 4
    62  )
    63  
    64  const (
    65  	CS_EXECSEG_MAIN_BINARY     = 0x1   // executable segment denotes main binary
    66  	CS_EXECSEG_ALLOW_UNSIGNED  = 0x10  // allow unsigned pages (for debugging)
    67  	CS_EXECSEG_DEBUGGER        = 0x20  // main binary is debugger
    68  	CS_EXECSEG_JIT             = 0x40  // JIT enabled
    69  	CS_EXECSEG_SKIP_LV         = 0x80  // skip library validation
    70  	CS_EXECSEG_CAN_LOAD_CDHASH = 0x100 // can bless cdhash for execution
    71  	CS_EXECSEG_CAN_EXEC_CDHASH = 0x200 // can execute blessed cdhash
    72  )
    73  
    74  type Blob struct {
    75  	typ    uint32 // type of entry
    76  	offset uint32 // offset of entry
    77  	// data follows
    78  }
    79  
    80  func (b *Blob) put(out []byte) []byte {
    81  	out = put32be(out, b.typ)
    82  	out = put32be(out, b.offset)
    83  	return out
    84  }
    85  
    86  const blobSize = 2 * 4
    87  
    88  type SuperBlob struct {
    89  	magic  uint32 // magic number
    90  	length uint32 // total length of SuperBlob
    91  	count  uint32 // number of index entries following
    92  	// blobs []Blob
    93  }
    94  
    95  func (s *SuperBlob) put(out []byte) []byte {
    96  	out = put32be(out, s.magic)
    97  	out = put32be(out, s.length)
    98  	out = put32be(out, s.count)
    99  	return out
   100  }
   101  
   102  const superBlobSize = 3 * 4
   103  
   104  type CodeDirectory struct {
   105  	magic         uint32 // magic number (CSMAGIC_CODEDIRECTORY)
   106  	length        uint32 // total length of CodeDirectory blob
   107  	version       uint32 // compatibility version
   108  	flags         uint32 // setup and mode flags
   109  	hashOffset    uint32 // offset of hash slot element at index zero
   110  	identOffset   uint32 // offset of identifier string
   111  	nSpecialSlots uint32 // number of special hash slots
   112  	nCodeSlots    uint32 // number of ordinary (code) hash slots
   113  	codeLimit     uint32 // limit to main image signature range
   114  	hashSize      uint8  // size of each hash in bytes
   115  	hashType      uint8  // type of hash (cdHashType* constants)
   116  	_pad1         uint8  // unused (must be zero)
   117  	pageSize      uint8  // log2(page size in bytes); 0 => infinite
   118  	_pad2         uint32 // unused (must be zero)
   119  	scatterOffset uint32
   120  	teamOffset    uint32
   121  	_pad3         uint32
   122  	codeLimit64   uint64
   123  	execSegBase   uint64
   124  	execSegLimit  uint64
   125  	execSegFlags  uint64
   126  	// data follows
   127  }
   128  
   129  func (c *CodeDirectory) put(out []byte) []byte {
   130  	out = put32be(out, c.magic)
   131  	out = put32be(out, c.length)
   132  	out = put32be(out, c.version)
   133  	out = put32be(out, c.flags)
   134  	out = put32be(out, c.hashOffset)
   135  	out = put32be(out, c.identOffset)
   136  	out = put32be(out, c.nSpecialSlots)
   137  	out = put32be(out, c.nCodeSlots)
   138  	out = put32be(out, c.codeLimit)
   139  	out = put8(out, c.hashSize)
   140  	out = put8(out, c.hashType)
   141  	out = put8(out, c._pad1)
   142  	out = put8(out, c.pageSize)
   143  	out = put32be(out, c._pad2)
   144  	out = put32be(out, c.scatterOffset)
   145  	out = put32be(out, c.teamOffset)
   146  	out = put32be(out, c._pad3)
   147  	out = put64be(out, c.codeLimit64)
   148  	out = put64be(out, c.execSegBase)
   149  	out = put64be(out, c.execSegLimit)
   150  	out = put64be(out, c.execSegFlags)
   151  	return out
   152  }
   153  
   154  const codeDirectorySize = 13*4 + 4 + 4*8
   155  
   156  // CodeSigCmd is Mach-O LC_CODE_SIGNATURE load command.
   157  type CodeSigCmd struct {
   158  	Cmd      uint32 // LC_CODE_SIGNATURE
   159  	Cmdsize  uint32 // sizeof this command (16)
   160  	Dataoff  uint32 // file offset of data in __LINKEDIT segment
   161  	Datasize uint32 // file size of data in __LINKEDIT segment
   162  }
   163  
   164  func FindCodeSigCmd(f *macho.File) (CodeSigCmd, bool) {
   165  	get32 := f.ByteOrder.Uint32
   166  	for _, l := range f.Loads {
   167  		data := l.Raw()
   168  		cmd := get32(data)
   169  		if cmd == LC_CODE_SIGNATURE {
   170  			return CodeSigCmd{
   171  				cmd,
   172  				get32(data[4:]),
   173  				get32(data[8:]),
   174  				get32(data[12:]),
   175  			}, true
   176  		}
   177  	}
   178  	return CodeSigCmd{}, false
   179  }
   180  
   181  func put32be(b []byte, x uint32) []byte { binary.BigEndian.PutUint32(b, x); return b[4:] }
   182  func put64be(b []byte, x uint64) []byte { binary.BigEndian.PutUint64(b, x); return b[8:] }
   183  func put8(b []byte, x uint8) []byte     { b[0] = x; return b[1:] }
   184  func puts(b, s []byte) []byte           { n := copy(b, s); return b[n:] }
   185  
   186  // Size computes the size of the code signature.
   187  // id is the identifier used for signing (a field in CodeDirectory blob, which
   188  // has no significance in ad-hoc signing).
   189  func Size(codeSize int64, id string) int64 {
   190  	nhashes := (codeSize + pageSize - 1) / pageSize
   191  	idOff := int64(codeDirectorySize)
   192  	hashOff := idOff + int64(len(id)+1)
   193  	cdirSz := hashOff + nhashes*sha256.Size
   194  	return int64(superBlobSize+blobSize) + cdirSz
   195  }
   196  
   197  // Sign generates an ad-hoc code signature and writes it to out.
   198  // out must have length at least Size(codeSize, id).
   199  // data is the file content without the signature, of size codeSize.
   200  // textOff and textSize is the file offset and size of the text segment.
   201  // isMain is true if this is a main executable.
   202  // id is the identifier used for signing (a field in CodeDirectory blob, which
   203  // has no significance in ad-hoc signing).
   204  func Sign(out []byte, data io.Reader, id string, codeSize, textOff, textSize int64, isMain bool) {
   205  	nhashes := (codeSize + pageSize - 1) / pageSize
   206  	idOff := int64(codeDirectorySize)
   207  	hashOff := idOff + int64(len(id)+1)
   208  	sz := Size(codeSize, id)
   209  
   210  	// emit blob headers
   211  	sb := SuperBlob{
   212  		magic:  CSMAGIC_EMBEDDED_SIGNATURE,
   213  		length: uint32(sz),
   214  		count:  1,
   215  	}
   216  	blob := Blob{
   217  		typ:    CSSLOT_CODEDIRECTORY,
   218  		offset: superBlobSize + blobSize,
   219  	}
   220  	cdir := CodeDirectory{
   221  		magic:        CSMAGIC_CODEDIRECTORY,
   222  		length:       uint32(sz) - (superBlobSize + blobSize),
   223  		version:      0x20400,
   224  		flags:        0x20002, // adhoc | linkerSigned
   225  		hashOffset:   uint32(hashOff),
   226  		identOffset:  uint32(idOff),
   227  		nCodeSlots:   uint32(nhashes),
   228  		codeLimit:    uint32(codeSize),
   229  		hashSize:     sha256.Size,
   230  		hashType:     CS_HASHTYPE_SHA256,
   231  		pageSize:     uint8(pageSizeBits),
   232  		execSegBase:  uint64(textOff),
   233  		execSegLimit: uint64(textSize),
   234  	}
   235  	if isMain {
   236  		cdir.execSegFlags = CS_EXECSEG_MAIN_BINARY
   237  	}
   238  
   239  	outp := out
   240  	outp = sb.put(outp)
   241  	outp = blob.put(outp)
   242  	outp = cdir.put(outp)
   243  
   244  	// emit the identifier
   245  	outp = puts(outp, []byte(id+"\000"))
   246  
   247  	// emit hashes
   248  	var buf [pageSize]byte
   249  	h := sha256.New()
   250  	p := 0
   251  	for p < int(codeSize) {
   252  		n, err := io.ReadFull(data, buf[:])
   253  		if err == io.EOF {
   254  			break
   255  		}
   256  		if err != nil && err != io.ErrUnexpectedEOF {
   257  			panic(err)
   258  		}
   259  		if p+n > int(codeSize) {
   260  			n = int(codeSize) - p
   261  		}
   262  		p += n
   263  		h.Reset()
   264  		h.Write(buf[:n])
   265  		b := h.Sum(nil)
   266  		outp = puts(outp, b[:])
   267  	}
   268  }
   269  

View as plain text