github.com/4ad/go@v0.0.0-20161219182952-69a12818b605/src/crypto/x509/root_darwin.go (about)

     1  // Copyright 2013 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  //go:generate go run root_darwin_arm_gen.go -output root_darwin_armx.go
     6  
     7  package x509
     8  
     9  import (
    10  	"bytes"
    11  	"encoding/pem"
    12  	"fmt"
    13  	"io/ioutil"
    14  	"os"
    15  	"os/exec"
    16  	"strconv"
    17  	"sync"
    18  	"syscall"
    19  )
    20  
    21  func (c *Certificate) systemVerify(opts *VerifyOptions) (chains [][]*Certificate, err error) {
    22  	return nil, nil
    23  }
    24  
    25  // This code is only used when compiling without cgo.
    26  // It is here, instead of root_nocgo_darwin.go, so that tests can check it
    27  // even if the tests are run with cgo enabled.
    28  // The linker will not include these unused functions in binaries built with cgo enabled.
    29  
    30  func execSecurityRoots() (*CertPool, error) {
    31  	cmd := exec.Command("/usr/bin/security", "find-certificate", "-a", "-p", "/System/Library/Keychains/SystemRootCertificates.keychain")
    32  	data, err := cmd.Output()
    33  	if err != nil {
    34  		return nil, err
    35  	}
    36  
    37  	var (
    38  		mu    sync.Mutex
    39  		roots = NewCertPool()
    40  	)
    41  	add := func(cert *Certificate) {
    42  		mu.Lock()
    43  		defer mu.Unlock()
    44  		roots.AddCert(cert)
    45  	}
    46  	blockCh := make(chan *pem.Block)
    47  	var wg sync.WaitGroup
    48  	for i := 0; i < 4; i++ {
    49  		wg.Add(1)
    50  		go func() {
    51  			defer wg.Done()
    52  			for block := range blockCh {
    53  				verifyCertWithSystem(block, add)
    54  			}
    55  		}()
    56  	}
    57  	for len(data) > 0 {
    58  		var block *pem.Block
    59  		block, data = pem.Decode(data)
    60  		if block == nil {
    61  			break
    62  		}
    63  		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
    64  			continue
    65  		}
    66  		blockCh <- block
    67  	}
    68  	close(blockCh)
    69  	wg.Wait()
    70  	return roots, nil
    71  }
    72  
    73  func verifyCertWithSystem(block *pem.Block, add func(*Certificate)) {
    74  	data := pem.EncodeToMemory(block)
    75  	var cmd *exec.Cmd
    76  	if needsTmpFiles() {
    77  		f, err := ioutil.TempFile("", "cert")
    78  		if err != nil {
    79  			fmt.Fprintf(os.Stderr, "can't create temporary file for cert: %v", err)
    80  			return
    81  		}
    82  		defer os.Remove(f.Name())
    83  		if _, err := f.Write(data); err != nil {
    84  			fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
    85  			return
    86  		}
    87  		if err := f.Close(); err != nil {
    88  			fmt.Fprintf(os.Stderr, "can't write temporary file for cert: %v", err)
    89  			return
    90  		}
    91  		cmd = exec.Command("/usr/bin/security", "verify-cert", "-c", f.Name(), "-l")
    92  	} else {
    93  		cmd = exec.Command("/usr/bin/security", "verify-cert", "-c", "/dev/stdin", "-l")
    94  		cmd.Stdin = bytes.NewReader(data)
    95  	}
    96  	if cmd.Run() == nil {
    97  		// Non-zero exit means untrusted
    98  		cert, err := ParseCertificate(block.Bytes)
    99  		if err != nil {
   100  			return
   101  		}
   102  
   103  		add(cert)
   104  	}
   105  }
   106  
   107  var versionCache struct {
   108  	sync.Once
   109  	major int
   110  }
   111  
   112  // needsTmpFiles reports whether the OS is <= 10.11 (which requires real
   113  // files as arguments to the security command).
   114  func needsTmpFiles() bool {
   115  	versionCache.Do(func() {
   116  		release, err := syscall.Sysctl("kern.osrelease")
   117  		if err != nil {
   118  			return
   119  		}
   120  		for i, c := range release {
   121  			if c == '.' {
   122  				release = release[:i]
   123  				break
   124  			}
   125  		}
   126  		major, err := strconv.Atoi(release)
   127  		if err != nil {
   128  			return
   129  		}
   130  		versionCache.major = major
   131  	})
   132  	return versionCache.major <= 15
   133  }