// Takes the "context" policies to extract the revocation and apply it to timeStamp.
CFArrayRef
SecPolicyCreateAppleTimeStampingAndRevocationPolicies(CFTypeRef policyOrArray)
{
    /* can't use SECAPI macros, since this function does not return OSStatus */
    CFArrayRef resultPolicyArray=NULL;
    try {
        // Set default policy
        CFRef<CFArrayRef> policyArray = cfArrayize(policyOrArray);
        CFRef<SecPolicyRef> defaultPolicy = SecPolicyCreateWithOID(kSecPolicyAppleTimeStamping);
        CFRef<CFMutableArrayRef> appleTimeStampingPolicies = makeCFMutableArray(1,defaultPolicy.get());

        // Parse the policy and add revocation related ones
        CFIndex numPolicies = CFArrayGetCount(policyArray);
        for(CFIndex dex=0; dex<numPolicies; dex++) {
            SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policyArray, dex);
            SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol));
            const CssmOid &oid = pol->oid();
            if ((oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION))
                || (oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL))
                || (oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)))
            {
                CFArrayAppendValue(appleTimeStampingPolicies, secPol);
            }
        }
        // Transfer of ownership
        resultPolicyArray=appleTimeStampingPolicies.yield();
    }
    catch (...) {
        CFReleaseNull(resultPolicyArray);
    };
    return resultPolicyArray;
}
//
// Identify a guest by returning its StaticCode and running CodeDirectory hash.
// This uses cshosting RPCs to ask the host (or its proxy).
//
SecStaticCode *GenericCode::identifyGuest(SecCode *guest, CFDataRef *cdhashOut)
{
	if (GenericCode *iguest = dynamic_cast<GenericCode *>(guest)) {
		FilePathOut path;
		CFRef<CFDataRef> cdhash;
		CFDictionary attributes(errSecCSHostProtocolInvalidAttribute);
		identifyGuest(iguest->guestRef(), path, cdhash.aref(), attributes.aref());
		DiskRep::Context ctx;
		if (CFNumberRef architecture = attributes.get<CFNumberRef>(kSecGuestAttributeArchitecture)) {
			cpu_type_t cpu = cfNumber<cpu_type_t>(architecture);
			if (CFNumberRef subarchitecture = attributes.get<CFNumberRef>(kSecGuestAttributeSubarchitecture))
				ctx.arch = Architecture(cpu, cfNumber<cpu_subtype_t>(subarchitecture));
			else
				ctx.arch = Architecture(cpu);
		}
		SecPointer<GenericStaticCode> code = new GenericStaticCode(DiskRep::bestGuess(path, &ctx));
		CODESIGN_GUEST_IDENTIFY_GENERIC(iguest, iguest->guestRef(), code);
		if (cdhash) {
			CODESIGN_GUEST_CDHASH_GENERIC(iguest, (void *)CFDataGetBytePtr(cdhash), (unsigned)CFDataGetLength(cdhash));
			*cdhashOut = cdhash.yield();
		}
		return code.yield();
	} else
		MacOSError::throwMe(errSecCSNotAHost);
}
//
// Given a bag of attribute values, automagically come up with a SecCode
// without any other information.
// This is meant to be the "just do what makes sense" generic call, for callers
// who don't want to engage in the fascinating dance of manual guest enumeration.
//
// Note that we expect the logic embedded here to change over time (in backward
// compatible fashion, one hopes), and that it's all right to use heuristics here
// as long as it's done sensibly.
//
// Be warned that the present logic is quite a bit ad-hoc, and will likely not
// handle arbitrary combinations of proxy hosting, dynamic hosting, and dedicated
// hosting all that well.
//
SecCode *SecCode::autoLocateGuest(CFDictionaryRef attributes, SecCSFlags flags)
{
	// special case: with no attributes at all, return the root of trust
	if (CFDictionaryGetCount(attributes) == 0)
		return KernelCode::active()->retain();
	
	// main logic: we need a pid, and we'll take a canonical guest id as an option
	int pid = 0;
	if (!cfscan(attributes, "{%O=%d}", kSecGuestAttributePid, &pid))
		CSError::throwMe(errSecCSUnsupportedGuestAttributes, kSecCFErrorGuestAttributes, attributes);
	if (SecCode *process =
			KernelCode::active()->locateGuest(attributes)) {
		SecPointer<SecCode> code;
		code.take(process);		// locateGuest gave us a retained object
		if (code->staticCode()->flag(kSecCodeSignatureHost)) {
			// might be a code host. Let's find out
			CFRef<CFMutableDictionaryRef> rest = makeCFMutableDictionary(attributes);
			CFDictionaryRemoveValue(rest, kSecGuestAttributePid);
			if (SecCode *guest = code->locateGuest(rest))
				return guest;
		}
		if (!CFDictionaryGetValue(attributes, kSecGuestAttributeCanonical)) {
			// only "soft" attributes, and no hosting is happening. Return the (non-)host itself
			return code.yield();
		}
	}
	MacOSError::throwMe(errSecCSNoSuchCode);
}
/*!
 */
OSStatus SecAccessCreateFromOwnerAndACL(const CSSM_ACL_OWNER_PROTOTYPE *owner,
	uint32 aclCount, const CSSM_ACL_ENTRY_INFO *acls,
	SecAccessRef *accessRef)
{
	BEGIN_SECAPI
	Required(accessRef);	// preflight
	SecPointer<Access> access = new Access(Required(owner), aclCount, &Required(acls));
	*accessRef = access->handle();
	END_SECAPI
}
示例#5
0
//
// Get the host for an Code
//
OSStatus SecCodeCopyHost(SecCodeRef guestRef, SecCSFlags flags, SecCodeRef *hostRef)
{
	BEGIN_CSAPI

	checkFlags(flags);
	SecPointer<SecCode> host = SecCode::required(guestRef)->host();
	CodeSigning::Required(hostRef) = host ? host->handle() : NULL;

	END_CSAPI
}
示例#6
0
/*!
 */
OSStatus SecACLCopySimpleContents(SecACLRef aclRef,
	CFArrayRef *applicationList,
	CFStringRef *promptDescription, CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR *promptSelector)
{
	BEGIN_SECAPI
	SecPointer<ACL> acl = ACL::required(aclRef);
	switch (acl->form()) {
	case ACL::allowAllForm:
		Required(applicationList) = NULL;
		Required(promptDescription) =
			acl->promptDescription().empty() ? NULL
				: makeCFString(acl->promptDescription());
		Required(promptSelector) = acl->promptSelector();
		break;
	case ACL::appListForm:
		Required(applicationList) =
			makeCFArrayFrom(convert, acl->applications());
		Required(promptDescription) = makeCFString(acl->promptDescription());
		Required(promptSelector) = acl->promptSelector();
		break;
    case ACL::integrityForm:
        Required(applicationList) = NULL;
        Required(promptDescription) = makeCFString(acl->integrity().toHex());

        // We don't have a prompt selector. Nullify.
        Required(promptSelector).version = CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION;
        Required(promptSelector).flags = 0;
        break;
	default:
		return errSecACLNotSimple;		// custom or unknown
	}
	END_SECAPI
}
示例#7
0
OSStatus SecACLSetSimpleContents(SecACLRef aclRef,
	CFArrayRef applicationList,
	CFStringRef description, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR *promptSelector)
{
	BEGIN_SECAPI
	SecPointer<ACL> acl = ACL::required(aclRef);
    if(acl->form() == ACL::integrityForm) {
        // If this is an integrity ACL, route the (unhexified) promptDescription into the right place
        string hex = cfString(description);
        if(hex.length() %2 == 0) {
            // might be a valid hex string, try to set
            CssmAutoData data(Allocator::standard());
            data.malloc(hex.length() / 2);
            data.get().fromHex(hex.c_str());
            acl->setIntegrity(data);
        }
    } else {
        // Otherwise, put it in the promptDescription where it belongs
        acl->promptDescription() = description ? cfString(description) : "";
    }
	acl->promptSelector() = promptSelector ? *promptSelector : ACL::defaultSelector;
    if(acl->form() !=  ACL::integrityForm) {
        if (applicationList) {
            // application-list + prompt
            acl->form(ACL::appListForm);
            setApplications(acl, applicationList);
        } else {
            // allow-any
            acl->form(ACL::allowAllForm);
        }
	}
	acl->modify();
	END_SECAPI
}
OSStatus
SecKeychainItemCopyAccess(SecKeychainItemRef itemRef, SecAccessRef* accessRef)
{
	BEGIN_SECKCITEMAPI

	Required(accessRef);	// preflight
	SecPointer<Access> access = new Access(*aclBearer(reinterpret_cast<CFTypeRef>(__itemImplRef)));
	*accessRef = access->handle();

	END_SECKCITEMAPI
}
void MachOEditor::parentAction()
{
	if (mHelperOverridden) {
		CODESIGN_ALLOCATE_VALIDATE((char*)mHelperPath, this->pid());
		// check code identity of an overridden allocation helper
		SecPointer<SecStaticCode> code = new SecStaticCode(DiskRep::bestGuess(mHelperPath));
		code->validateDirectory();
		code->validateExecutable();
		code->validateResources();
		code->validateRequirement((const Requirement *)appleReq, errSecCSReqFailed);
	}
}
static
OSStatus SecAccessCreateFromObject(CFTypeRef sourceRef,
	SecAccessRef *accessRef)
{
	BEGIN_SECAPI

	Required(accessRef);	// preflight
	SecPointer<Access> access = new Access(*aclBearer(sourceRef));
	*accessRef = access->handle();

	END_SECAPI
}
示例#11
0
OSStatus SecACLSetAuthorizations(SecACLRef aclRef,
	CSSM_ACL_AUTHORIZATION_TAG *tags, uint32 tagCount)
{
	BEGIN_SECAPI
	SecPointer<ACL> acl = ACL::required(aclRef);
	if (acl->isOwner())		// can't change rights of the owner ACL
		MacOSError::throwMe(errSecInvalidOwnerEdit);
	AclAuthorizationSet &auths = acl->authorizations();
	auths.clear();
	copy(tags, tags + tagCount, insert_iterator<AclAuthorizationSet>(auths, auths.begin()));
	acl->modify();
	END_SECAPI
}
SecPolicyRef
SecPolicyCreateWithSecAsn1Oid(SecAsn1Oid *oidPtr)
{
	SecPolicyRef policy = NULL;
	try {
		SecPointer<Policy> policyObj;
		PolicyCursor::policy(oidPtr, policyObj);
		policy = policyObj->handle();
	}
	catch (...) {}

	return policy;
}
//
// Add a code object to the whitelist
//
void OpaqueWhitelist::add(SecStaticCodeRef codeRef)
{
	// make our own copy of the code object
	SecPointer<SecStaticCode> code = new SecStaticCode(SecStaticCode::requiredStatic(codeRef)->diskRep());

	CFCopyRef<CFDataRef> current = code->cdHash();
	attachOpaque(code->handle(false), NULL);	// compute and attach an opaque signature
	CFDataRef opaque = code->cdHash();

	SQLite::Statement insert(*this, "INSERT OR REPLACE INTO whitelist (current,opaque) VALUES (:current, :opaque)");
	insert.bind(":current") = current.get();
	insert.bind(":opaque") = opaque;
	insert.execute();
}
/* 
 * Given an app-specified array of Policies, determine if at least one of them
 * matches the given policy OID.
 */
bool Trust::policySpecified(CFArrayRef policies, const CSSM_OID &inOid)
{
	if(policies == NULL) {
		return false;
	}
	CFIndex numPolicies = CFArrayGetCount(policies);
	for(CFIndex dex=0; dex<numPolicies; dex++) {
		SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
		SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol));
		const CssmOid &oid = pol->oid();
		if(oid == CssmOid::overlay(inOid)) {
			return true;
		}
	}
	return false;
}
OSStatus
SecIdentitySearchCopyNext(
	SecIdentitySearchRef searchRef,
	SecIdentityRef *identityRef)
{
    BEGIN_SECAPI

	RequiredParam(identityRef);
	SecPointer<Identity> identityPtr;
	if (!IdentityCursor::required(searchRef)->next(identityPtr))
		return errSecItemNotFound;

	*identityRef = identityPtr->handle();

    END_SECAPI
}
示例#16
0
OSStatus SecCodeCheckValidityWithErrors(SecCodeRef codeRef, SecCSFlags flags,
	SecRequirementRef requirementRef, CFErrorRef *errors)
{
	BEGIN_CSAPI

	checkFlags(flags,
		  kSecCSConsiderExpiration
		| kSecCSStrictValidate
		| kSecCSRestrictSidebandData
		| kSecCSEnforceRevocationChecks);
	SecPointer<SecCode> code = SecCode::required(codeRef);
	code->checkValidity(flags);
	if (const SecRequirement *req = SecRequirement::optional(requirementRef))
		code->staticCode()->validateRequirement(req->requirement(), errSecCSReqFailed);

	END_CSAPI_ERRORS
}
示例#17
0
//
// Get the StaticCode for an Code
//
OSStatus SecCodeCopyStaticCode(SecCodeRef codeRef, SecCSFlags flags, SecStaticCodeRef *staticCodeRef)
{
	BEGIN_CSAPI

	checkFlags(flags, kSecCSUseAllArchitectures);
	SecPointer<SecStaticCode> staticCode = SecCode::required(codeRef)->staticCode();
	if (flags & kSecCSUseAllArchitectures)
		if (Universal* macho = staticCode->diskRep()->mainExecutableImage())	// Mach-O main executable
			if (macho->narrowed()) {
				// create a new StaticCode comprising the whole fat file
				RefPointer<DiskRep> rep = DiskRep::bestGuess(staticCode->diskRep()->mainExecutablePath());
				staticCode = new SecStaticCode(rep);
			}
	CodeSigning::Required(staticCodeRef) = staticCode ? staticCode->handle() : NULL;

	END_CSAPI
}
/*!
 *	Create a new SecAccessRef that is set to the default configuration
 *	of a (newly created) security object.
 */
OSStatus SecAccessCreate(CFStringRef descriptor, CFArrayRef trustedList, SecAccessRef *accessRef)
{
	BEGIN_SECAPI
	Required(descriptor);
	SecPointer<Access> access;
	if (trustedList) {
		CFIndex length = CFArrayGetCount(trustedList);
		ACL::ApplicationList trusted;
		for (CFIndex n = 0; n < length; n++)
			trusted.push_back(TrustedApplication::required(
				SecTrustedApplicationRef(CFArrayGetValueAtIndex(trustedList, n))));
		access = new Access(cfString(descriptor), trusted);
	} else {
		access = new Access(cfString(descriptor));
	}
	Required(accessRef) = access->handle();
	END_SECAPI
}
/* 
 * Obtain non-normalized issuer and serial number for specified cert, both 
 * returned as CFDataRefs owned by caller. 
 */
void TrustSettings::copyIssuerAndSerial(
	SecCertificateRef	certRef,
	CFDataRef			*issuer,		/* optional, RETURNED */
	CFDataRef			*serial)		/* RETURNED */
{
	SecPointer<Certificate> cert = Certificate::required(certRef);
	CSSM_DATA_PTR fieldVal;
	
	if(issuer != NULL) { 
		fieldVal = cert->copyFirstFieldValue(CSSMOID_X509V1IssuerNameStd);
		*issuer = CFDataCreate(NULL, fieldVal->Data, fieldVal->Length);
		cert->releaseFieldValue(CSSMOID_X509V1IssuerNameStd, fieldVal);
	}
	
	fieldVal = cert->copyFirstFieldValue(CSSMOID_X509V1SerialNumber);
	*serial = CFDataCreate(NULL, fieldVal->Data, fieldVal->Length);
	cert->releaseFieldValue(CSSMOID_X509V1SerialNumber, fieldVal);
}
/* 
 * Given an app-specified array of Policies, determine if at least one of them
 * is an explicit revocation policy.
 */
bool Trust::revocationPolicySpecified(CFArrayRef policies)
{
	if(policies == NULL) {
		return false;
	}
	CFIndex numPolicies = CFArrayGetCount(policies);
	for(CFIndex dex=0; dex<numPolicies; dex++) {
		SecPolicyRef secPol = (SecPolicyRef)CFArrayGetValueAtIndex(policies, dex);
		SecPointer<Policy> pol = Policy::required(SecPolicyRef(secPol));
		const CssmOid &oid = pol->oid();
		if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_CRL)) {
			return true;
		}
		if(oid == CssmOid::overlay(CSSMOID_APPLE_TP_REVOCATION_OCSP)) {
			return true;
		}
	}
	return false;
}
示例#21
0
OSStatus
SecPolicyCopyAll(CSSM_CERT_TYPE certificateType, CFArrayRef* policies)
{
    BEGIN_SECAPI
	Required(policies);
	CFMutableArrayRef currPolicies = NULL;
	currPolicies = CFArrayCreateMutable(NULL, 0, NULL);
	if ( currPolicies )
	{
		SecPointer<PolicyCursor> cursor(new PolicyCursor(NULL, NULL));
		SecPointer<Policy> policy;
		while ( cursor->next(policy) ) /* copies the next policy */
		{
			CFArrayAppendValue(currPolicies, policy->handle());  /* 'SecPolicyRef' appended */
			CFRelease(policy->handle()); /* refcount bumped up when appended to array */
		}
		*policies = CFArrayCreateCopy(NULL, currPolicies);
		CFRelease(currPolicies);
		CFRelease(cursor->handle());
	}
	END_SECAPI
}
示例#22
0
OSStatus SecCodeCopySigningInformation(SecStaticCodeRef codeRef, SecCSFlags flags,
	CFDictionaryRef *infoRef)
{
	BEGIN_CSAPI

	checkFlags(flags,
		  kSecCSInternalInformation
		| kSecCSSigningInformation
		| kSecCSRequirementInformation
		| kSecCSDynamicInformation
		| kSecCSContentInformation);

	SecPointer<SecStaticCode> code = SecStaticCode::requiredStatic(codeRef);
	CFRef<CFDictionaryRef> info = code->signingInformation(flags);

	if (flags & kSecCSDynamicInformation)
		if (SecPointer<SecCode> dcode = SecStaticCode::optionalDynamic(codeRef))
			info.take(cfmake<CFDictionaryRef>("{+%O,%O=%u}", info.get(), kSecCodeInfoStatus, dcode->status()));

	CodeSigning::Required(infoRef) = info.yield();

	END_CSAPI
}
//
// Identify a guest by attribute set, and return a new GenericCode representing it.
// This uses cshosting RPCs to ask the host (or its proxy).
//
SecCode *GenericCode::locateGuest(CFDictionaryRef attributes)
{
	if (Port host = hostingPort()) {
		CFRef<CFDataRef> attrData;
		void *attrPtr = NULL; size_t attrLength = 0;
		if (attributes) {
			attrData.take(CFPropertyListCreateXMLData(NULL, attributes));
			attrPtr = (void *)CFDataGetBytePtr(attrData);
			attrLength = CFDataGetLength(attrData);
		}
		GuestChain guestPath;
		mach_msg_type_number_t guestPathLength;
		mach_port_t subport;
		CALL(host, findGuest, guestRef(), attrPtr, (mach_msg_type_number_t)attrLength,
			&guestPath, &guestPathLength, &subport);
		CODESIGN_GUEST_LOCATE_GENERIC(this, guestPath, guestPathLength, subport);
		SecPointer<SecCode> code = this;
		for (unsigned n = 0; n < guestPathLength; n++)
			code = new GenericCode(code, guestPath[n]);
		return code.yield();
	} else
		return NULL;		// not found, no error
}
OSStatus
SecKeyGenerateWithAttributes(
	SecKeychainAttributeList* attrList,
	SecKeychainRef keychainRef,
	CSSM_ALGORITHMS algorithm,
	uint32 keySizeInBits,
	CSSM_CC_HANDLE contextHandle,
	CSSM_KEYUSE keyUsage,
	uint32 keyAttr,
	SecAccessRef initialAccess,
	SecKeyRef* keyRef)
{
	BEGIN_SECAPI

	Keychain keychain;
	SecPointer<Access> theAccess;

	if (keychainRef)
		keychain = KeychainImpl::required(keychainRef);
	if (initialAccess)
		theAccess = Access::required(initialAccess);

	SecPointer<KeyItem> item = KeyItem::generateWithAttributes(attrList,
        keychain,
        algorithm,
        keySizeInBits,
        contextHandle,
        keyUsage,
        keyAttr,
        theAccess);

	// Return the generated key.
	if (keyRef)
		*keyRef = item->handle();

	END_SECAPI
}
示例#25
0
/*!
 */
OSStatus SecACLCreateFromSimpleContents(SecAccessRef accessRef,
	CFArrayRef applicationList,
	CFStringRef description, const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR *promptSelector,
	SecACLRef *newAcl)
{
	BEGIN_SECAPI
	SecPointer<Access> access = Access::required(accessRef);
	SecPointer<ACL> acl = new ACL(cfString(description), *promptSelector);
	if (applicationList) {
		// application-list + prompt
		acl->form(ACL::appListForm);
		setApplications(acl, applicationList);
	} else {
		// allow-any
		acl->form(ACL::allowAllForm);
	}
	access->add(acl.get());
	Required(newAcl) = acl->handle();
	END_SECAPI
}
示例#26
0
Item
KeyItem::importTo(const Keychain &keychain, Access *newAccess, SecKeychainAttributeList *attrList)
{
	if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
		MacOSError::throwMe(errSecInvalidKeychain);

	/* Get the destination keychain's db. */
	SSDbImpl* dbImpl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
	if (dbImpl == NULL)
		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);

	SSDb ssDb(dbImpl);

	/* Make sure mKey is valid. */
	/* We can't call key() here, since we won't have a unique record id yet */
	if (!mKey)
		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);

	// Generate a random label to use initially
	CssmClient::CSP appleCsp(gGuidAppleCSP);
	CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
	uint8 labelBytes[20];
	CssmData label(labelBytes, sizeof(labelBytes));
	random.generate(label, label.Length);

	/* Set up the ACL for the new key. */
	SecPointer<Access> access;
	if (newAccess)
		access = newAccess;
	else
		access = new Access(*mKey);

	/* Generate a random 3DES wrapping Key. */
	CssmClient::GenerateKey genKey(csp(), CSSM_ALGID_3DES_3KEY, 192);
	CssmClient::Key wrappingKey(genKey(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
		CSSM_KEYATTR_EXTRACTABLE /* | CSSM_KEYATTR_RETURN_DATA */)));

	/* make a random IV */
	uint8 ivBytes[8];
	CssmData iv(ivBytes, sizeof(ivBytes));
	random.generate(iv, iv.length());

	/* Extract the key by wrapping it with the wrapping key. */
	CssmClient::WrapKey wrap(csp(), CSSM_ALGID_3DES_3KEY_EDE);
	wrap.key(wrappingKey);
	wrap.cred(getCredentials(CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, kSecCredentialTypeDefault));
	wrap.mode(CSSM_ALGMODE_ECBPad);
	wrap.padding(CSSM_PADDING_PKCS7);
	wrap.initVector(iv);
	CssmClient::Key wrappedKey(wrap(mKey));

	/* Unwrap the new key into the new Keychain. */
	CssmClient::UnwrapKey unwrap(keychain->csp(), CSSM_ALGID_3DES_3KEY_EDE);
	unwrap.key(wrappingKey);
	unwrap.mode(CSSM_ALGMODE_ECBPad);
	unwrap.padding(CSSM_PADDING_PKCS7);
	unwrap.initVector(iv);

	/* Setup the dldbHandle in the context. */
	unwrap.add(CSSM_ATTRIBUTE_DL_DB_HANDLE, ssDb->handle());

	/* Set up an initial aclEntry so we can change it after the unwrap. */
	Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
	ResourceControlContext rcc;
	maker.initialOwner(rcc, NULL);
	unwrap.owner(rcc.input());

	/* Unwrap the key. */
	uint32 usage = mKey->usage();
	/* Work around csp brokeness where it sets all usage bits in the Keyheader when CSSM_KEYUSE_ANY is set. */
	if (usage & CSSM_KEYUSE_ANY)
		usage = CSSM_KEYUSE_ANY;

	CssmClient::Key unwrappedKey(unwrap(wrappedKey, KeySpec(usage,
		(mKey->attributes() | CSSM_KEYATTR_PERMANENT) & ~(CSSM_KEYATTR_ALWAYS_SENSITIVE | CSSM_KEYATTR_NEVER_EXTRACTABLE),
		label)));

	/* Look up unwrapped key in the DLDB. */
	DbUniqueRecord uniqueId;
	SSDbCursor dbCursor(ssDb, 1);
	dbCursor->recordType(recordType());
	dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
	CssmClient::Key copiedKey;
	if (!dbCursor->nextKey(NULL, copiedKey, uniqueId))
		MacOSError::throwMe(errSecItemNotFound);

	// Set the initial label, application label, and application tag (if provided)
	if (attrList) {
		DbAttributes newDbAttributes;
		SSDbCursor otherDbCursor(ssDb, 1);
		otherDbCursor->recordType(recordType());
		bool checkForDuplicates = false;

		for (UInt32 index=0; index < attrList->count; index++) {
			SecKeychainAttribute attr = attrList->attr[index];
			CssmData attrData(attr.data, attr.length);
			if (attr.tag == kSecKeyPrintName) {
				newDbAttributes.add(kInfoKeyPrintName, attrData);
			}
			if (attr.tag == kSecKeyLabel) {
				newDbAttributes.add(kInfoKeyLabel, attrData);
				otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
				checkForDuplicates = true;
			}
			if (attr.tag == kSecKeyApplicationTag) {
				newDbAttributes.add(kInfoKeyApplicationTag, attrData);
				otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
				checkForDuplicates = true;
			}
		}

		DbAttributes otherDbAttributes;
		DbUniqueRecord otherUniqueId;
		CssmClient::Key otherKey;
		try
		{
			if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
				MacOSError::throwMe(errSecDuplicateItem);

			uniqueId->modify(recordType(), &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
		}
		catch (CssmError e)
		{
			// clean up after trying to insert a duplicate key
			uniqueId->deleteRecord ();
			throw;
		}
	}

	/* Set the acl and owner on the unwrapped key. */
	access->setAccess(*unwrappedKey, maker);

	/* Return a keychain item which represents the new key.  */
	Item item(keychain->item(recordType(), uniqueId));

    KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, item);

	return item;
}
示例#27
0
SecPointer<KeyItem>
KeyItem::generateWithAttributes(const SecKeychainAttributeList *attrList,
	Keychain keychain,
	CSSM_ALGORITHMS algorithm,
	uint32 keySizeInBits,
	CSSM_CC_HANDLE contextHandle,
	CSSM_KEYUSE keyUsage,
	uint32 keyAttr,
	SecPointer<Access> initialAccess)
{
	CssmClient::CSP appleCsp(gGuidAppleCSP);
	CssmClient::CSP csp(NULL);
	SSDb ssDb(NULL);
	uint8 labelBytes[20];
	CssmData label(labelBytes, sizeof(labelBytes));
	bool freeKey = false;
	bool deleteContext = false;
	const CSSM_DATA *plabel = NULL;

	if (keychain)
	{
		if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
			MacOSError::throwMe(errSecInvalidKeychain);

		SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
		if (impl == NULL)
		{
			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
		}

		ssDb = SSDb(impl);
		csp = keychain->csp();

		// Generate a random label to use initially
		CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
		random.generate(label, label.Length);
		plabel = &label;
	}
	else
	{
		// Not a persistent key so create it in the regular csp
		csp = appleCsp;
	}

	// Create a Access::Maker for the initial owner of the private key.
	ResourceControlContext *prcc = NULL, rcc;
	const AccessCredentials *cred = NULL;
	Access::Maker maker;
	if (keychain && initialAccess)
	{
		memset(&rcc, 0, sizeof(rcc));
		// @@@ Potentially provide a credential argument which allows us to generate keys in the csp.
		// Currently the CSP lets anyone do this, but we might restrict this in the future, e.g. a smartcard
		// could require out-of-band pin entry before a key can be generated.
		maker.initialOwner(rcc);
		// Create the cred we need to manipulate the keys until we actually set a new access control for them.
		cred = maker.cred();
		prcc = &rcc;
	}

	CSSM_KEY cssmKey;

	CSSM_CC_HANDLE ccHandle = 0;

	Item keyItem;
	try
	{
		CSSM_RETURN status;
		if (contextHandle)
			ccHandle = contextHandle;
		else
		{
			status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
			if (status)
				CssmError::throwMe(status);
			deleteContext = true;
		}

		if (ssDb)
		{
			CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
			CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
			CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
			status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
			if (status)
				CssmError::throwMe(status);

			keyAttr |= CSSM_KEYATTR_PERMANENT;
		}

		// Generate the key
		status = CSSM_GenerateKey(ccHandle, keyUsage, keyAttr, plabel, prcc, &cssmKey);
		if (status)
			CssmError::throwMe(status);

		if (ssDb)
		{
			freeKey = true;
			// Find the key we just generated in the DL and get a SecKeyRef
			// so we can specify the label attribute(s) and initial ACL.

			// Look up key in the DLDB.
			DbAttributes dbAttributes;
			DbUniqueRecord uniqueId;
			SSDbCursor dbCursor(ssDb, 1);
			dbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
			dbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
			CssmClient::Key key;
			if (!dbCursor->nextKey(&dbAttributes, key, uniqueId))
				MacOSError::throwMe(errSecItemNotFound);

			// Set the initial label, application label, and application tag (if provided)
			if (attrList) {
				DbAttributes newDbAttributes;
				SSDbCursor otherDbCursor(ssDb, 1);
				otherDbCursor->recordType(CSSM_DL_DB_RECORD_SYMMETRIC_KEY);
				bool checkForDuplicates = false;

				for (UInt32 index=0; index < attrList->count; index++) {
					SecKeychainAttribute attr = attrList->attr[index];
					CssmData attrData(attr.data, attr.length);
					if (attr.tag == kSecKeyPrintName) {
						newDbAttributes.add(kInfoKeyPrintName, attrData);
					}
					if (attr.tag == kSecKeyLabel) {
						newDbAttributes.add(kInfoKeyLabel, attrData);
						otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, attrData);
						checkForDuplicates = true;
					}
					if (attr.tag == kSecKeyApplicationTag) {
						newDbAttributes.add(kInfoKeyApplicationTag, attrData);
						otherDbCursor->add(CSSM_DB_EQUAL, kInfoKeyApplicationTag, attrData);
						checkForDuplicates = true;
					}
				}

				DbAttributes otherDbAttributes;
				DbUniqueRecord otherUniqueId;
				CssmClient::Key otherKey;
				if (checkForDuplicates && otherDbCursor->nextKey(&otherDbAttributes, otherKey, otherUniqueId))
					MacOSError::throwMe(errSecDuplicateItem);

				uniqueId->modify(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, &newDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
			}

			// Finally, fix the acl and owner of the key to the specified access control settings.
			if (initialAccess)
				initialAccess->setAccess(*key, maker);

			// Create keychain item which will represent the key.
			keyItem = keychain->item(CSSM_DL_DB_RECORD_SYMMETRIC_KEY, uniqueId);
		}
		else
		{
			CssmClient::Key tempKey(csp, cssmKey);
			keyItem = new KeyItem(tempKey);
		}
	}
	catch (...)
	{
		if (freeKey)
		{
			// Delete the key if something goes wrong so we don't end up with inaccessible keys in the database.
			CSSM_FreeKey(csp->handle(), cred, &cssmKey, TRUE);
		}

		if (deleteContext)
			CSSM_DeleteContext(ccHandle);

		throw;
	}

	if (freeKey)
	{
		CSSM_FreeKey(csp->handle(), NULL, &cssmKey, FALSE);
	}

	if (deleteContext)
		CSSM_DeleteContext(ccHandle);

	if (keychain && keyItem)
		keychain->postEvent(kSecAddEvent, keyItem);

	KeyItem* item = dynamic_cast<KeyItem*>(&*keyItem);
	if (item == NULL)
	{
		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
	}

	return item;
}
示例#28
0
void
KeyItem::importPair(
	Keychain keychain,
	const CSSM_KEY &publicWrappedKey,
	const CSSM_KEY &privateWrappedKey,
	SecPointer<Access> initialAccess,
	SecPointer<KeyItem> &outPublicKey,
	SecPointer<KeyItem> &outPrivateKey)
{
	bool freePublicKey = false;
	bool freePrivateKey = false;
	bool deleteContext = false;

	if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
		MacOSError::throwMe(errSecInvalidKeychain);

	SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*keychain->database()));
	if (impl == NULL)
	{
		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
	}

	SSDb ssDb(impl);
	CssmClient::CSP csp(keychain->csp());
	CssmClient::CSP appleCsp(gGuidAppleCSP);

	// Create a Access::Maker for the initial owner of the private key.
	ResourceControlContext rcc;
	memset(&rcc, 0, sizeof(rcc));
	Access::Maker maker(Allocator::standard(), Access::Maker::kAnyMakerType);
	// @@@ Potentially provide a credential argument which allows us to unwrap keys in the csp.
	// Currently the CSP lets anyone do this, but we might restrict this in the future, e.g.
	// a smartcard could require out of band pin entry before a key can be generated.
	maker.initialOwner(rcc);
	// Create the cred we need to manipulate the keys until we actually set a new access control for them.
	const AccessCredentials *cred = maker.cred();

	CSSM_KEY publicCssmKey, privateCssmKey;
	memset(&publicCssmKey, 0, sizeof(publicCssmKey));
	memset(&privateCssmKey, 0, sizeof(privateCssmKey));

	CSSM_CC_HANDLE ccHandle = 0;

	Item publicKeyItem, privateKeyItem;
	try
	{
		CSSM_RETURN status;

		// Calculate the hash of the public key using the appleCSP.
		CssmClient::PassThrough passThrough(appleCsp);
		void *outData;
		CssmData *cssmData;

		/* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
		* associated key blob.
		* Key is specified in CSSM_CSP_CreatePassThroughContext.
		* Hash is allocated bythe CSP, in the App's memory, and returned
		* in *outData. */
		passThrough.key(&publicWrappedKey);
		passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
		cssmData = reinterpret_cast<CssmData *>(outData);
		CssmData &pubKeyHash = *cssmData;

		status = CSSM_CSP_CreateSymmetricContext(csp->handle(), publicWrappedKey.KeyHeader.WrapAlgorithmId, CSSM_ALGMODE_NONE, NULL, NULL, NULL, CSSM_PADDING_NONE, NULL, &ccHandle);
		if (status)
			CssmError::throwMe(status);
		deleteContext = true;

		CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
		CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
		CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
		status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
		if (status)
			CssmError::throwMe(status);

		// Unwrap the the keys
		CSSM_DATA descriptiveData = {0, NULL};

		status = CSSM_UnwrapKey(
			ccHandle,
			NULL,
			&publicWrappedKey,
			publicWrappedKey.KeyHeader.KeyUsage,
			publicWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
			&pubKeyHash,
			&rcc,
			&publicCssmKey,
			&descriptiveData);

		if (status)
			CssmError::throwMe(status);
		freePublicKey = true;

		if (descriptiveData.Data != NULL)
			free (descriptiveData.Data);

		status = CSSM_UnwrapKey(
			ccHandle,
			NULL,
			&privateWrappedKey,
			privateWrappedKey.KeyHeader.KeyUsage,
			privateWrappedKey.KeyHeader.KeyAttr | CSSM_KEYATTR_PERMANENT,
			&pubKeyHash,
			&rcc,
			&privateCssmKey,
			&descriptiveData);

		if (status)
			CssmError::throwMe(status);

		if (descriptiveData.Data != NULL)
			free (descriptiveData.Data);

		freePrivateKey = true;

		// Find the keys we just generated in the DL to get SecKeyRefs to them
		// so we can change the label to be the hash of the public key, and
		// fix up other attributes.

		// Look up public key in the DLDB.
		DbAttributes pubDbAttributes;
		DbUniqueRecord pubUniqueId;
		SSDbCursor dbPubCursor(ssDb, 1);
		dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
		dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
		CssmClient::Key publicKey;
		if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
			MacOSError::throwMe(errSecItemNotFound);

		// Look up private key in the DLDB.
		DbAttributes privDbAttributes;
		DbUniqueRecord privUniqueId;
		SSDbCursor dbPrivCursor(ssDb, 1);
		dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
		dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, pubKeyHash);
		CssmClient::Key privateKey;
		if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
			MacOSError::throwMe(errSecItemNotFound);

		// @@@ Not exception safe!
		csp.allocator().free(cssmData->Data);
		csp.allocator().free(cssmData);

		auto_ptr<string>privDescription;
		auto_ptr<string>pubDescription;
		try {
			privDescription.reset(new string(initialAccess->promptDescription()));
			pubDescription.reset(new string(initialAccess->promptDescription()));
		}
		catch(...) {
			/* this path taken if no promptDescription available, e.g., for complex ACLs */
			privDescription.reset(new string("Private key"));
			pubDescription.reset(new string("Public key"));
		}

		// Set the label of the public key to the public key hash.
		// Set the PrintName of the public key to the description in the acl.
		pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
		pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);

		// Set the label of the private key to the public key hash.
		// Set the PrintName of the private key to the description in the acl.
		privDbAttributes.add(kInfoKeyPrintName, *privDescription);
		privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);

		// Finally fix the acl and owner of the private key to the specified access control settings.
		initialAccess->setAccess(*privateKey, maker);

		// Make the public key acl completely open
		SecPointer<Access> pubKeyAccess(new Access());
		pubKeyAccess->setAccess(*publicKey, maker);

		// Create keychain items which will represent the keys.
		publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
		privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);

		KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
		if (impl == NULL)
		{
			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
		}

		outPublicKey = impl;

		impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
		if (impl == NULL)
		{
			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
		}
		outPrivateKey = impl;
	}
	catch (...)
	{
		if (freePublicKey)
			CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
		if (freePrivateKey)
			CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);

		if (deleteContext)
			CSSM_DeleteContext(ccHandle);

		throw;
	}

	if (freePublicKey)
		CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, FALSE);
	if (freePrivateKey)
		CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, FALSE);

	if (deleteContext)
		CSSM_DeleteContext(ccHandle);

	if (keychain && publicKeyItem && privateKeyItem)
	{
		KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, publicKeyItem);
		KCEventNotifier::PostKeychainEvent(kSecAddEvent, keychain, privateKeyItem);
	}
}
bool
IdentityCursorPolicyAndID::next(SecPointer<Identity> &identity)
{
	SecPointer<Identity> currIdentity;
	Boolean identityOK = true;

	if (!mPreferredIdentityChecked)
	{
        try
        {
            findPreferredIdentity();
        }
        catch(...) {}
		mPreferredIdentityChecked = true;
		if (mPreferredIdentity)
		{
			identity = mPreferredIdentity;
			return true;
		}
	}

	for (;;)
	{
		bool result = IdentityCursor::next(currIdentity);   // base class finds the next identity by keyUsage
		if ( result )
		{
			if (mPreferredIdentity && (currIdentity == mPreferredIdentity))
			{
				identityOK = false;	// we already returned this one, move on to the next
				continue;
			}

			// If there was no policy specified, we're done.
			if ( !mPolicy )
			{
				identityOK = true; // return this identity
				break;
			}

			// To reduce the number of (potentially expensive) trust evaluations performed, we need
			// to do some pre-processing to filter out certs that don't match the search criteria.
			// Rather than try to duplicate the TP's policy logic here, we'll just call the TP with
			// a single-element certificate array, no anchors, and no keychains to search.

			SecPointer<Certificate> certificate = currIdentity->certificate();
			CFRef<SecCertificateRef> certRef(certificate->handle());
			CFRef<CFMutableArrayRef> anchorsArray(CFArrayCreateMutable(NULL, 1, NULL));
			CFRef<CFMutableArrayRef> certArray(CFArrayCreateMutable(NULL, 1, NULL));
			if ( !certArray || !anchorsArray )
			{
				identityOK = false; // skip this and move on to the next one
				continue;
			}
			CFArrayAppendValue(certArray, certRef);

			SecPointer<Trust> trustLite = new Trust(certArray, mPolicy);
			StorageManager::KeychainList emptyList;
			// Set the anchors and keychain search list to be empty
			trustLite->anchors(anchorsArray);
			trustLite->searchLibs(emptyList);
			trustLite->evaluate();
			SecTrustResultType trustResult = trustLite->result();

			if (trustResult == kSecTrustResultRecoverableTrustFailure ||
				trustResult == kSecTrustResultFatalTrustFailure)
			{
				CFArrayRef certChain = NULL;
				CSSM_TP_APPLE_EVIDENCE_INFO *statusChain = NULL, *evInfo = NULL;
				trustLite->buildEvidence(certChain, TPEvidenceInfo::overlayVar(statusChain));
				if (statusChain)
					evInfo = &statusChain[0];
				if (!evInfo || evInfo->NumStatusCodes > 0) // per-cert codes means we can't use this cert for this policy
					trustResult = kSecTrustResultInvalid; // handled below
				if (certChain)
					CFRelease(certChain);
			}
			if (trustResult == kSecTrustResultInvalid)
			{
				identityOK = false; // move on to the next one
				continue;
			}

			// If trust evaluation isn't requested, we're done.
			if ( !mReturnOnlyValidIdentities )
			{
				identityOK = true; // return this identity
				break;
			}

			// Perform a full trust evaluation on the certificate with the specified policy.
			SecPointer<Trust> trust = new Trust(certArray, mPolicy);
			trust->evaluate();
			trustResult = trust->result();

			if (trustResult == kSecTrustResultInvalid ||
				trustResult == kSecTrustResultRecoverableTrustFailure ||
				trustResult == kSecTrustResultFatalTrustFailure)
			{
				identityOK = false; // move on to the next one
				continue;
			}

			identityOK = true; // this one was OK; return it.
			break;
		}
		else
		{
			identityOK = false; // no more left.
			break;
		}
	}   // for(;;)

	if ( identityOK )
	{
		identity = currIdentity; // caller will release the identity
		return true;
	}
	else
	{
		return false;
	}
}
示例#30
0
void
KeyItem::createPair(
	Keychain keychain,
	CSSM_ALGORITHMS algorithm,
	uint32 keySizeInBits,
	CSSM_CC_HANDLE contextHandle,
	CSSM_KEYUSE publicKeyUsage,
	uint32 publicKeyAttr,
	CSSM_KEYUSE privateKeyUsage,
	uint32 privateKeyAttr,
	SecPointer<Access> initialAccess,
	SecPointer<KeyItem> &outPublicKey,
	SecPointer<KeyItem> &outPrivateKey)
{
	bool freeKeys = false;
	bool deleteContext = false;

	if (!(keychain->database()->dl()->subserviceMask() & CSSM_SERVICE_CSP))
		MacOSError::throwMe(errSecInvalidKeychain);

	SSDbImpl* impl = dynamic_cast<SSDbImpl*>(&(*keychain->database()));
	if (impl == NULL)
	{
		CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
	}

	SSDb ssDb(impl);
	CssmClient::CSP csp(keychain->csp());
	CssmClient::CSP appleCsp(gGuidAppleCSP);

	// Generate a random label to use initially
	CssmClient::Random random(appleCsp, CSSM_ALGID_APPLE_YARROW);
	uint8 labelBytes[20];
	CssmData label(labelBytes, sizeof(labelBytes));
	random.generate(label, label.Length);

	// Create a Access::Maker for the initial owner of the private key.
	ResourceControlContext rcc;
	memset(&rcc, 0, sizeof(rcc));
	Access::Maker maker;
	// @@@ Potentially provide a credential argument which allows us to generate keys in the csp.  Currently the CSP let's anyone do this, but we might restrict this in the future, f.e. a smartcard could require out of band pin entry before a key can be generated.
	maker.initialOwner(rcc);
	// Create the cred we need to manipulate the keys until we actually set a new access control for them.
	const AccessCredentials *cred = maker.cred();

	CSSM_KEY publicCssmKey, privateCssmKey;
	memset(&publicCssmKey, 0, sizeof(publicCssmKey));
	memset(&privateCssmKey, 0, sizeof(privateCssmKey));

	CSSM_CC_HANDLE ccHandle = 0;

	Item publicKeyItem, privateKeyItem;
	try
	{
		CSSM_RETURN status;
		if (contextHandle)
				ccHandle = contextHandle;
		else
		{
			status = CSSM_CSP_CreateKeyGenContext(csp->handle(), algorithm, keySizeInBits, NULL, NULL, NULL, NULL, NULL, &ccHandle);
			if (status)
				CssmError::throwMe(status);
			deleteContext = true;
		}

		CSSM_DL_DB_HANDLE dldbHandle = ssDb->handle();
		CSSM_DL_DB_HANDLE_PTR dldbHandlePtr = &dldbHandle;
		CSSM_CONTEXT_ATTRIBUTE contextAttributes = { CSSM_ATTRIBUTE_DL_DB_HANDLE, sizeof(dldbHandle), { (char *)dldbHandlePtr } };
		status = CSSM_UpdateContextAttributes(ccHandle, 1, &contextAttributes);
		if (status)
			CssmError::throwMe(status);

		// Generate the keypair
		status = CSSM_GenerateKeyPair(ccHandle, publicKeyUsage, publicKeyAttr, &label, &publicCssmKey, privateKeyUsage, privateKeyAttr, &label, &rcc, &privateCssmKey);
		if (status)
			CssmError::throwMe(status);
		freeKeys = true;

		// Find the keys we just generated in the DL to get SecKeyRef's to them
		// so we can change the label to be the hash of the public key, and
		// fix up other attributes.

		// Look up public key in the DLDB.
		DbAttributes pubDbAttributes;
		DbUniqueRecord pubUniqueId;
		SSDbCursor dbPubCursor(ssDb, 1);
		dbPubCursor->recordType(CSSM_DL_DB_RECORD_PUBLIC_KEY);
		dbPubCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
		CssmClient::Key publicKey;
		if (!dbPubCursor->nextKey(&pubDbAttributes, publicKey, pubUniqueId))
			MacOSError::throwMe(errSecItemNotFound);

		// Look up private key in the DLDB.
		DbAttributes privDbAttributes;
		DbUniqueRecord privUniqueId;
		SSDbCursor dbPrivCursor(ssDb, 1);
		dbPrivCursor->recordType(CSSM_DL_DB_RECORD_PRIVATE_KEY);
		dbPrivCursor->add(CSSM_DB_EQUAL, kInfoKeyLabel, label);
		CssmClient::Key privateKey;
		if (!dbPrivCursor->nextKey(&privDbAttributes, privateKey, privUniqueId))
			MacOSError::throwMe(errSecItemNotFound);

		// Convert reference public key to a raw key so we can use it
		// in the appleCsp.
		CssmClient::WrapKey wrap(csp, CSSM_ALGID_NONE);
		wrap.cred(cred);
		CssmClient::Key rawPubKey = wrap(publicKey);

		// Calculate the hash of the public key using the appleCSP.
		CssmClient::PassThrough passThrough(appleCsp);
		void *outData;
		CssmData *cssmData;

		/* Given a CSSM_KEY_PTR in any format, obtain the SHA-1 hash of the
		* associated key blob.
		* Key is specified in CSSM_CSP_CreatePassThroughContext.
		* Hash is allocated bythe CSP, in the App's memory, and returned
		* in *outData. */
		passThrough.key(rawPubKey);
		passThrough(CSSM_APPLECSP_KEYDIGEST, NULL, &outData);
		cssmData = reinterpret_cast<CssmData *>(outData);
		CssmData &pubKeyHash = *cssmData;

		auto_ptr<string>privDescription;
		auto_ptr<string>pubDescription;
		try {
			privDescription.reset(new string(initialAccess->promptDescription()));
			pubDescription.reset(new string(initialAccess->promptDescription()));
		}
		catch(...) {
			/* this path taken if no promptDescription available, e.g., for complex ACLs */
			privDescription.reset(new string("Private key"));
			pubDescription.reset(new string("Public key"));
		}

		// Set the label of the public key to the public key hash.
		// Set the PrintName of the public key to the description in the acl.
		pubDbAttributes.add(kInfoKeyLabel, pubKeyHash);
		pubDbAttributes.add(kInfoKeyPrintName, *pubDescription);
		pubUniqueId->modify(CSSM_DL_DB_RECORD_PUBLIC_KEY, &pubDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);

		// Set the label of the private key to the public key hash.
		// Set the PrintName of the private key to the description in the acl.
		privDbAttributes.add(kInfoKeyLabel, pubKeyHash);
		privDbAttributes.add(kInfoKeyPrintName, *privDescription);
		privUniqueId->modify(CSSM_DL_DB_RECORD_PRIVATE_KEY, &privDbAttributes, NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);

		// @@@ Not exception safe!
		csp.allocator().free(cssmData->Data);
		csp.allocator().free(cssmData);

		// Finally fix the acl and owner of the private key to the specified access control settings.
		initialAccess->setAccess(*privateKey, maker);

		if(publicKeyAttr & CSSM_KEYATTR_PUBLIC_KEY_ENCRYPT) {
			/*
			 * Make the public key acl completely open.
			 * If the key was not encrypted, it already has a wide-open
			 * ACL (though that is a feature of securityd; it's not
			 * CDSA-specified behavior).
			 */
			SecPointer<Access> pubKeyAccess(new Access());
			pubKeyAccess->setAccess(*publicKey, maker);
		}

		// Create keychain items which will represent the keys.
		publicKeyItem = keychain->item(CSSM_DL_DB_RECORD_PUBLIC_KEY, pubUniqueId);
		privateKeyItem = keychain->item(CSSM_DL_DB_RECORD_PRIVATE_KEY, privUniqueId);

		KeyItem* impl = dynamic_cast<KeyItem*>(&(*publicKeyItem));
		if (impl == NULL)
		{
			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
		}

		outPublicKey = impl;

		impl = dynamic_cast<KeyItem*>(&(*privateKeyItem));
		if (impl == NULL)
		{
			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
		}

		outPrivateKey = impl;
	}
	catch (...)
	{
		if (freeKeys)
		{
			// Delete the keys if something goes wrong so we don't end up with inaccessible keys in the database.
			CSSM_FreeKey(csp->handle(), cred, &publicCssmKey, TRUE);
			CSSM_FreeKey(csp->handle(), cred, &privateCssmKey, TRUE);
		}

		if (deleteContext)
			CSSM_DeleteContext(ccHandle);

		throw;
	}

	if (freeKeys)
	{
		CSSM_FreeKey(csp->handle(), NULL, &publicCssmKey, FALSE);
		CSSM_FreeKey(csp->handle(), NULL, &privateCssmKey, FALSE);
	}

	if (deleteContext)
		CSSM_DeleteContext(ccHandle);

	if (keychain && publicKeyItem && privateKeyItem)
	{
		keychain->postEvent(kSecAddEvent, publicKeyItem);
		keychain->postEvent(kSecAddEvent, privateKeyItem);
	}
}