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

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

View as plain text