github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/provider/azure/certfile.go (about)

     1  package azure
     2  
     3  import (
     4  	"fmt"
     5  	"io/ioutil"
     6  	"math/rand"
     7  	"os"
     8  	"path"
     9  
    10  	"github.com/juju/errors"
    11  )
    12  
    13  // tempCertFile is a temporary file containing an x509 certificate.
    14  // It's possible to pass a certificate to libcurl in-memory, but much more
    15  // complicated.  We went with this hack for now.  Call newTempCertFile to
    16  // store a certificate in a temporary file, and once you're done with the
    17  // file, invoke its Delete method to clean it up.
    18  type tempCertFile struct {
    19  	tempDir  string
    20  	filename string
    21  }
    22  
    23  // Path returns the full absolute path for the temporary certificate file.
    24  func (certFile *tempCertFile) Path() string {
    25  	return path.Join(certFile.tempDir, certFile.filename)
    26  }
    27  
    28  // Delete cleans up a tempCertFile.  You must call this after use, or you'll
    29  // leave not just garbage but security-sensitive garbage.
    30  // This method is idempotent.  If called after it's already been run, it
    31  // does nothing.
    32  func (certFile *tempCertFile) Delete() {
    33  	if certFile.tempDir == "" {
    34  		// Either it wasn't constructed, or it's been deleted already.
    35  		return
    36  	}
    37  	err := os.RemoveAll(certFile.tempDir)
    38  	if err != nil {
    39  		panic(err)
    40  	}
    41  	// We no longer own a file that needs cleaning up.
    42  	certFile.filename = ""
    43  	certFile.tempDir = ""
    44  }
    45  
    46  // newTempCertFile stores the given x509 certificate in a temporary file,
    47  // which only the current user will be allowed to access.
    48  // You *must* clean up the file after use, by calling its Delete method.
    49  func newTempCertFile(data []byte) (certFile *tempCertFile, err error) {
    50  	// Add context to any error we may return.
    51  	defer errors.Maskf(&err, "failed while writing temporary certificate file")
    52  
    53  	// Access permissions for these temporary files:
    54  	const (
    55  		// Owner can read/write temporary files.  Not backed up.
    56  		fileMode = 0600 | os.ModeTemporary | os.ModeExclusive
    57  		// Temporary dirs are like files, but owner also has "x"
    58  		// permission.
    59  		dirMode = fileMode | 0100
    60  	)
    61  
    62  	certFile = &tempCertFile{}
    63  
    64  	// We'll randomize the file's name, so that even someone with access
    65  	// to the temporary directory (perhaps a group member sneaking in
    66  	// just before we close access to the directory) won't be able to
    67  	// guess its name and inject their own file.
    68  	certFile.filename = fmt.Sprintf("x509-%d.cert", rand.Int31())
    69  
    70  	// To guarantee that nobody else will be able to access the file, even
    71  	// by predicting or guessing its name, we create the file in its own
    72  	// private directory.
    73  	certFile.tempDir, err = ioutil.TempDir("", "juju-azure")
    74  	if err != nil {
    75  		return nil, err
    76  	}
    77  	err = os.Chmod(certFile.tempDir, dirMode)
    78  	if err != nil {
    79  		return nil, err
    80  	}
    81  
    82  	// Now, at last, write the file.  WriteFile could have done most of
    83  	// the work on its own, but it doesn't guarantee that nobody creates
    84  	// a file of the same name first.  When that happens, you get a file
    85  	// but not with the requested permissions.
    86  	err = ioutil.WriteFile(certFile.Path(), data, fileMode)
    87  	if err != nil {
    88  		os.RemoveAll(certFile.tempDir)
    89  		return nil, err
    90  	}
    91  
    92  	return certFile, nil
    93  }