]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_keychain/lib/SecExport.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_keychain / lib / SecExport.cpp
diff --git a/Security/libsecurity_keychain/lib/SecExport.cpp b/Security/libsecurity_keychain/lib/SecExport.cpp
new file mode 100644 (file)
index 0000000..2e83625
--- /dev/null
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 2004,2011-2014 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ *
+ * SecExport.cpp - high-level facility for exporting Sec layer objects. 
+ */
+
+#include "SecImportExport.h"
+#include "SecImportExportAgg.h"
+#include "SecImportExportPem.h"
+#include "SecExternalRep.h"
+#include "SecImportExportUtils.h"
+#include <security_utilities/errors.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecIdentityPriv.h>
+#include <Security/SecItem.h>
+#include <Security/SecBase.h>
+using namespace Security;
+using namespace KeychainCore;
+
+/*
+ * Convert Sec item to one or two SecExportReps, append to exportReps array.
+ * The "one or two" clause exists for SecIdentityRefs, which we split into 
+ * a cert and a key.
+ * Throws a MacOSError if incoming CFTypeRef is of type other than SecKeyRef,
+ * SecCertRef, or SecIdentityRef.
+ */
+static void impExpAddToExportReps(
+       CFTypeRef                       thing,                          // Key, Cert, Identity
+       CFMutableArrayRef   exportReps,
+       unsigned                        &numCerts,                      // IN/OUT - accumulated
+       unsigned                        &numKeys)                       // IN/OUT - accumulated
+{
+       if(CFGetTypeID(thing) == SecIdentityGetTypeID()) {
+               /* special case for SecIdentities, creates two SecExportReps */
+               OSStatus ortn;
+               SecIdentityRef idRef = (SecIdentityRef)thing;
+               SecCertificateRef certRef;
+               SecKeyRef keyRef;
+               SecExportRep *rep;
+               
+               /* cert */
+               SecImpExpDbg("impExpAddToExportReps: adding identity cert and key");
+               ortn = SecIdentityCopyCertificate(idRef, &certRef);
+               if(ortn) {
+                       Security::MacOSError::throwMe(ortn);
+               }
+               rep = SecExportRep::vend(certRef);
+               CFArrayAppendValue(exportReps, rep);
+               CFRelease(certRef);                     // SecExportRep holds a reference
+               numCerts++;
+               
+               /* private key */
+               ortn = SecIdentityCopyPrivateKey(idRef, &keyRef);
+               if(ortn) {
+                       Security::MacOSError::throwMe(ortn);
+               }
+               rep = SecExportRep::vend(keyRef);
+               CFArrayAppendValue(exportReps, rep);
+               CFRelease(keyRef);                      // SecExportRep holds a reference
+               numKeys++;
+       }
+       else {
+               /* this throws if 'thing' is an unacceptable type */
+               SecExportRep *rep = SecExportRep::vend(thing);
+               SecImpExpDbg("impExpAddToExportReps: adding single type %d",
+                       (int)rep->externType());
+               CFArrayAppendValue(exportReps, rep);
+               if(rep->externType() == kSecItemTypeCertificate) {
+                       numCerts++;
+               }
+               else {
+                       numKeys++;
+               }
+       }
+}
+
+#pragma mark --- public export function ---
+
+OSStatus SecKeychainItemExport(
+       CFTypeRef                                                       keychainItemOrArray,
+       SecExternalFormat                                       outputFormat,   // a SecExternalFormat 
+       SecItemImportExportFlags                        flags,                  // kSecItemPemArmour, etc.      
+       const SecKeyImportExportParameters  *keyParams,         // optional 
+       CFDataRef                                                       *exportedData)  // external representation 
+                                                                                                               //    returned here
+{
+       BEGIN_IMP_EXP_SECAPI
+       
+       /* some basic input validation */
+       if(keychainItemOrArray == NULL) {
+               return errSecParam;
+       }
+       if(keyParams != NULL) {
+               /* can't specify explicit passphrase and ask for secure one */
+               if( (keyParams->passphrase != NULL) &&
+                   ((keyParams->flags & kSecKeySecurePassphrase) != 0)) {
+                       return errSecParam;
+               }
+       }
+       
+       unsigned numKeys                        = 0;
+       unsigned numCerts                       = 0;
+       unsigned numTotalExports        = 0;
+       OSStatus ortn                           = errSecSuccess;
+       SecExportRep *rep                       = NULL;                         // common temp variable
+       CFMutableDataRef outputData = NULL;
+       const char *pemHeader           = "UNKNOWN";
+       
+       /* convert keychainItemOrArray to CFArray of SecExportReps */
+       CFMutableArrayRef exportReps = CFArrayCreateMutable(NULL, 0, NULL);
+       /* subsequent errors to errOut: */
+       
+       try {
+               if(CFGetTypeID(keychainItemOrArray) == CFArrayGetTypeID()) {
+                       CFArrayRef arr = (CFArrayRef)keychainItemOrArray;
+                       CFIndex arraySize = CFArrayGetCount(arr);
+                       for(CFIndex dex=0; dex<arraySize; dex++) {
+                               impExpAddToExportReps(CFArrayGetValueAtIndex(arr, dex), 
+                                       exportReps, numCerts, numKeys);
+                       }
+               }
+               else {
+                       impExpAddToExportReps(keychainItemOrArray, exportReps, numCerts, numKeys);
+               }
+       }
+       catch(const Security::MacOSError osErr) {
+               ortn = osErr.error;
+               goto errOut;
+       }
+       catch(...) {
+               ortn = errSecParam;
+               goto errOut;
+       }
+       numTotalExports = (unsigned int)CFArrayGetCount(exportReps);
+       assert((numCerts + numKeys) == numTotalExports);
+       if((numTotalExports > 1) && (outputFormat == kSecFormatUnknown)) {
+               /* default aggregate format is PEM sequence */
+               outputFormat = kSecFormatPEMSequence;
+       }
+       
+       /*
+        * Break out to SecExternalFormat-specific code, appending all data to outputData 
+        */
+       outputData = CFDataCreateMutable(NULL, 0);
+       switch(outputFormat) {
+               case kSecFormatPKCS7:
+                       ortn = impExpPkcs7Export(exportReps, flags, keyParams, outputData);
+                       pemHeader = PEM_STRING_PKCS7;
+                       break;
+               case kSecFormatPKCS12:
+                       ortn = impExpPkcs12Export(exportReps, flags, keyParams, outputData);
+                       pemHeader = PEM_STRING_PKCS12;
+                       break;
+               case kSecFormatPEMSequence:
+                       {
+                               /* 
+                                * A bit of a special case. Create an intermediate DER encoding 
+                                * of each SecExportRef, in the default format for that item;
+                                * PEM encode the result, and append the PEM encoding to 
+                                * outputData.
+                                */
+                               CFIndex numReps = CFArrayGetCount(exportReps);
+                               for(CFIndex dex=0; dex<numReps; dex++) {
+                               
+                                       rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
+                                       
+                                       /* default DER encoding */
+                                       CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0);
+                                       ortn = rep->exportRep(kSecFormatUnknown, flags, keyParams,
+                                               tmpData, &pemHeader);
+                                       if(ortn) {
+                                               SecImpExpDbg("ItemExport: releasing tmpData %p", tmpData);
+                                               CFRelease(tmpData);
+                                               goto errOut;
+                                       }
+                                       
+                                       /* PEM to accumulating output */
+                                       assert(rep->pemParamLines() == NULL);
+                                       ortn = impExpPemEncodeExportRep((CFDataRef)tmpData, 
+                                                       pemHeader, NULL,                /* no pemParamLines, right? */
+                                                       outputData);
+                                       CFRelease(tmpData);
+                                       if(ortn) {
+                                               goto errOut;
+                                       }
+                               }
+                               break;
+                       }
+               
+               /* Enumerate remainder explicitly for clarity; all are single-item forms */
+               case kSecFormatOpenSSL:
+               case kSecFormatSSH:
+               case kSecFormatSSHv2:
+               case kSecFormatBSAFE:
+               case kSecFormatRawKey:
+               case kSecFormatWrappedPKCS8:
+               case kSecFormatWrappedOpenSSL:
+               case kSecFormatWrappedSSH:
+               case kSecFormatWrappedLSH:
+               case kSecFormatX509Cert:
+               case kSecFormatUnknown:         // i.e., default, handled by SecExportRep
+                       {
+                               unsigned foundCount = 0;
+                               
+                               /* verify that we have exactly one of specified item */
+                               if(outputFormat == kSecFormatX509Cert) {
+                                       foundCount = numCerts;
+                               }
+                               else if(outputFormat == kSecFormatUnknown) {
+                                       /* can't go wrong */
+                                       foundCount = numTotalExports;
+                               }
+                               else {
+                                       foundCount = numKeys;
+                               }
+                               if((numTotalExports != 1) || (foundCount != 1)) {
+                                       SecImpExpDbg("Export single item format with other than one item");
+                                       ortn = errSecParam;
+                                       goto errOut;
+                               }
+                               assert(CFArrayGetCount(exportReps) == 1);
+                               rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0);
+                               ortn = rep->exportRep(outputFormat, flags, 
+                                               keyParams, outputData, &pemHeader);
+                               break;
+                       }
+               default:
+                       SecImpExpDbg("SecKeychainItemExport: bad format (%u)", 
+                               (unsigned)outputFormat);
+                       ortn = errSecParam;
+                       goto errOut;
+       }
+       
+       /* 
+        * Final step: possible PEM encode. Skip for kSecFormatPEMSequence (in which
+        * case outputData is all ready to ship out to the caller); mandatory
+        * if exportRep has a non-NULL pemParamLines (which can only happen if we're
+        * exporting a single item). 
+        */
+       if(ortn == errSecSuccess) {
+               if(outputFormat == kSecFormatPEMSequence) {
+                       *exportedData = outputData;
+                       outputData = NULL;              
+               }
+               else {
+                       rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, 0);
+                       if((flags & kSecItemPemArmour) || (rep->pemParamLines() != NULL)) {
+                               /* PEM encode a single item */
+                               CFMutableDataRef tmpData = CFDataCreateMutable(NULL, 0);
+                               ortn = impExpPemEncodeExportRep((CFDataRef)outputData, pemHeader, 
+                                       rep->pemParamLines(), tmpData);
+                               CFRelease(outputData);          // done with this
+                               outputData = NULL;                      
+                               *exportedData = tmpData;        // caller gets PEM
+                       }
+                       else {
+                               *exportedData = outputData;
+                               outputData = NULL;              
+                       }
+               }
+       }
+errOut:
+       if(exportReps != NULL) {
+               /* CFArray of our own classes, no auto release */
+               CFIndex num = CFArrayGetCount(exportReps);
+               for(CFIndex dex=0; dex<num; dex++) {
+                       rep = (SecExportRep *)CFArrayGetValueAtIndex(exportReps, dex);
+                       delete rep;
+               }
+               CFRelease(exportReps);
+       }
+       if(outputData != NULL) {
+               CFRelease(outputData);
+               outputData = NULL;
+       }
+       if(ortn) {
+               return SecKeychainErrFromOSStatus(ortn);
+       }
+       else {
+               return errSecSuccess;
+       }
+       
+       END_IMP_EXP_SECAPI
+}
+
+
+OSStatus SecItemExport(CFTypeRef secItemOrArray, SecExternalFormat outputFormat,       
+       SecItemImportExportFlags                        flags,                          /* kSecItemPemArmor, etc. */
+       const SecItemImportExportKeyParameters  *keyParams,                     /* optional */
+       CFDataRef                                                       *exportedData)  
+{
+       SecKeyImportExportParameters* oldStructPtr = NULL;
+       SecKeyImportExportParameters oldStruct;
+       memset(&oldStruct, 0, sizeof(oldStruct));
+       
+       if (NULL != keyParams)
+       {
+               
+               SecKeyRef tempKey = NULL;
+               
+               if (SecKeyGetTypeID() == CFGetTypeID(secItemOrArray))
+               {
+                       tempKey = (SecKeyRef)secItemOrArray;
+               }
+               
+               if (ConvertSecKeyImportExportParametersToSecImportExportKeyParameters(tempKey,
+                       keyParams, &oldStruct))
+               {
+                       oldStructPtr = &oldStruct;
+               }
+       }
+       
+       return SecKeychainItemExport(secItemOrArray, outputFormat, flags, oldStructPtr, exportedData);
+}
+
+
+
+
+
+
+