//
// Set an individual trust element
//
void TrustStore::assign(Certificate *cert, Policy *policy, SecTrustUserSetting trust)
{
	StLock<Mutex> _(mMutex);

	TrustData trustData = { UserTrustItem::currentVersion, trust };
	Keychain defaultKeychain = Keychain::optional(NULL);
	Keychain trustLocation = defaultKeychain;	// default keychain, unless trust entry found
	StorageManager::KeychainList searchList;
	globals().storageManager.getSearchList(searchList);

	if (Item item = findItem(cert, policy, searchList)) {
		// user has a trust setting in a keychain - modify that
		trustLocation = item->keychain();
		if (trust == kSecTrustResultUnspecified)
			item->keychain()->deleteItem(item);
		else
			item->modifyContent(NULL, sizeof(trustData), &trustData);
	} else {
		// no trust entry: make one
		if (trust != kSecTrustResultUnspecified) {
			Item item = new UserTrustItem(cert, policy, trustData);
			if (Keychain location = cert->keychain()) {
				try {
					location->add(item);				// try the cert's keychain first
					trustLocation = location;
				} catch (...) {
					if (&*location != &*defaultKeychain)
						defaultKeychain->add(item);		// try the default (if it's not the same)
				}
			} else {
				defaultKeychain->add(item);				// raw cert - use default keychain
			}
		}
	}

	// Make sure that the certificate is available in some keychain,
	// to provide a basis for editing the trust setting that we're assigning.
	if (cert->keychain() == NULL) {
		if (cert->findInKeychain(searchList) == NULL) {
			try {
				cert->copyTo(trustLocation);	// add cert to the trust item's keychain
			} catch (...) {
				secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"",
					cert, trustLocation->name());
				try {
					if (&*trustLocation != &*defaultKeychain)
						cert->copyTo(defaultKeychain);	// try the default (if it's not the same)
				} catch (...) {
					// unable to add the certificate
					secdebug("trusteval", "failed to add certificate %p to keychain \"%s\"",
						cert, defaultKeychain->name());
				}
			}
		}
	}
}
OSStatus
SecKeychainItemCreateFromContent(SecItemClass itemClass, SecKeychainAttributeList *attrList,
		UInt32 length, const void *data, SecKeychainRef keychainRef,
		SecAccessRef initialAccess, SecKeychainItemRef *itemRef)
{
	BEGIN_SECAPI

	KCThrowParamErrIf_(length!=0 && data==NULL);
	Item item(itemClass, attrList, length, data);
	if (initialAccess) {
		item->setAccess(Access::required(initialAccess));
	}
	Keychain keychain = nil;
	try
	{
		keychain = Keychain::optional(keychainRef);
		if ( !keychain->exists() )
		{
			MacOSError::throwMe(errSecNoSuchKeychain);	// Might be deleted or not available at this time.
		}
	}
	catch(...)
	{
		keychain = globals().storageManager.defaultKeychainUI(item);
	}

	keychain->add(item);
	if (itemRef) {
		*itemRef = item->handle();
	}

	END_SECAPI
}
Esempio n. 3
0
Item
Certificate::copyTo(const Keychain &keychain, Access *newAccess)
{
	StLock<Mutex>_(mMutex);
	/* Certs can't have access controls. */
	if (newAccess)
		MacOSError::throwMe(errSecNoAccessForItem);

	Item item(new Certificate(data(), type(), encoding()));
	keychain->add(item);
	return item;
}
static
OSStatus _SecIdentityAddPreferenceItemWithName(
	SecKeychainRef keychainRef,
	SecIdentityRef identityRef,
	CFStringRef idString,
	SecKeychainItemRef *itemRef)
{
    // this is NOT exported, and called only from SecIdentityAddPreferenceItem (below), so no BEGIN/END macros here;
    // caller must handle exceptions

	if (!identityRef || !idString)
		return errSecParam;
	SecPointer<Certificate> cert(Identity::required(identityRef)->certificate());
	Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
	sint32 keyUsage = 0;

	// determine the account attribute
	//
	// This attribute must be synthesized from certificate label + pref item type + key usage,
	// as only the account and service attributes can make a generic keychain item unique.
	// For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
	// we can save a certificate preference if an identity preference already exists for the
	// given service name, and vice-versa.
	// If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
	//
    CFStringRef labelStr = nil;
	cert->inferLabel(false, &labelStr);
	if (!labelStr) {
        return errSecDataTooLarge; // data is "in a format which cannot be displayed"
	}
	CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1;
	const char *templateStr = "%s [key usage 0x%X]";
	const int keyUsageMaxStrLen = 8;
	accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen;
	char accountUTF8[accountUTF8Len];
    if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
		accountUTF8[0] = (char)'\0';
	if (keyUsage)
		snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
	snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
    CssmData account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
    CFRelease(labelStr);

	// service attribute (name provided by the caller)
	CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(idString), kCFStringEncodingUTF8) + 1;;
	char serviceUTF8[serviceUTF8Len];
    if (!CFStringGetCString(idString, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8))
        serviceUTF8[0] = (char)'\0';
    CssmData service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8));

	// set item attribute values
	item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
	item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);
	item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), (FourCharCode)'iprf');
	item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
	item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), keyUsage);

	// generic attribute (store persistent certificate reference)
	CFDataRef pItemRef = nil;
	OSStatus status = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)cert->handle(), &pItemRef);
	if (!pItemRef)
		status = errSecInvalidItemRef;
	if (status)
		 return status;
	const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
	CFIndex dataLen = CFDataGetLength(pItemRef);
	CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
	item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
	CFRelease(pItemRef);

	Keychain keychain = nil;
	try {
        keychain = Keychain::optional(keychainRef);
        if (!keychain->exists())
            MacOSError::throwMe(errSecNoSuchKeychain);	// Might be deleted or not available at this time.
    }
    catch(...) {
        keychain = globals().storageManager.defaultKeychainUI(item);
    }

	try {
		keychain->add(item);
	}
	catch (const MacOSError &err) {
		if (err.osStatus() != errSecDuplicateItem)
			throw; // if item already exists, fall through to update
	}

	item->update();

    if (itemRef)
		*itemRef = item->handle();

    return status;
}
OSStatus SecIdentitySetPreference(
    SecIdentityRef identity,
    CFStringRef name,
    CSSM_KEYUSE keyUsage)
{
	if (!name) {
		return errSecParam;
	}
	if (!identity) {
		// treat NULL identity as a request to clear the preference
		// (note: if keyUsage is 0, this clears all key usage prefs for name)
		return SecIdentityDeletePreferenceItemWithNameAndKeyUsage(NULL, name, keyUsage);
	}

    BEGIN_SECAPI

	SecPointer<Certificate> certificate(Identity::required(identity)->certificate());

	// determine the account attribute
	//
	// This attribute must be synthesized from certificate label + pref item type + key usage,
	// as only the account and service attributes can make a generic keychain item unique.
	// For 'iprf' type items (but not 'cprf'), we append a trailing space. This insures that
	// we can save a certificate preference if an identity preference already exists for the
	// given service name, and vice-versa.
	// If the key usage is 0 (i.e. the normal case), we omit the appended key usage string.
	//
    CFStringRef labelStr = nil;
	certificate->inferLabel(false, &labelStr);
	if (!labelStr) {
        MacOSError::throwMe(errSecDataTooLarge); // data is "in a format which cannot be displayed"
	}
	CFIndex accountUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(labelStr), kCFStringEncodingUTF8) + 1;
	const char *templateStr = "%s [key usage 0x%X]";
	const int keyUsageMaxStrLen = 8;
	accountUTF8Len += strlen(templateStr) + keyUsageMaxStrLen;
	char accountUTF8[accountUTF8Len];
    if (!CFStringGetCString(labelStr, accountUTF8, accountUTF8Len-1, kCFStringEncodingUTF8))
		accountUTF8[0] = (char)'\0';
	if (keyUsage)
		snprintf(accountUTF8, accountUTF8Len-1, templateStr, accountUTF8, keyUsage);
	snprintf(accountUTF8, accountUTF8Len-1, "%s ", accountUTF8);
    CssmData account(const_cast<char *>(accountUTF8), strlen(accountUTF8));
    CFRelease(labelStr);

	// service attribute (name provided by the caller)
	CFIndex serviceUTF8Len = CFStringGetMaximumSizeForEncoding(CFStringGetLength(name), kCFStringEncodingUTF8) + 1;;
	char serviceUTF8[serviceUTF8Len];
    if (!CFStringGetCString(name, serviceUTF8, serviceUTF8Len-1, kCFStringEncodingUTF8))
        serviceUTF8[0] = (char)'\0';
    CssmData service(const_cast<char *>(serviceUTF8), strlen(serviceUTF8));

    // look for existing identity preference item, in case this is an update
	StorageManager::KeychainList keychains;
	globals().storageManager.getSearchList(keychains);
	KCCursor cursor(keychains, kSecGenericPasswordItemClass, NULL);
    FourCharCode itemType = 'iprf';
    cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecServiceItemAttr), service);
	cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecTypeItemAttr), itemType);
    if (keyUsage) {
        cursor->add(CSSM_DB_EQUAL, Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
	}

	Item item(kSecGenericPasswordItemClass, 'aapl', 0, NULL, false);
    bool add = (!cursor->next(item));
	// at this point, we either have a new item to add or an existing item to update

    // set item attribute values
    item->setAttribute(Schema::attributeInfo(kSecServiceItemAttr), service);
    item->setAttribute(Schema::attributeInfo(kSecTypeItemAttr), itemType);
    item->setAttribute(Schema::attributeInfo(kSecAccountItemAttr), account);
	item->setAttribute(Schema::attributeInfo(kSecScriptCodeItemAttr), (sint32)keyUsage);
    item->setAttribute(Schema::attributeInfo(kSecLabelItemAttr), service);

	// generic attribute (store persistent certificate reference)
	CFDataRef pItemRef = nil;
    certificate->copyPersistentReference(pItemRef);
	if (!pItemRef) {
		MacOSError::throwMe(errSecInvalidItemRef);
    }
	const UInt8 *dataPtr = CFDataGetBytePtr(pItemRef);
	CFIndex dataLen = CFDataGetLength(pItemRef);
	CssmData pref(const_cast<void *>(reinterpret_cast<const void *>(dataPtr)), dataLen);
	item->setAttribute(Schema::attributeInfo(kSecGenericItemAttr), pref);
	CFRelease(pItemRef);

    if (add) {
        Keychain keychain = nil;
        try {
            keychain = globals().storageManager.defaultKeychain();
            if (!keychain->exists())
                MacOSError::throwMe(errSecNoSuchKeychain);	// Might be deleted or not available at this time.
        }
        catch(...) {
            keychain = globals().storageManager.defaultKeychainUI(item);
        }

		try {
			keychain->add(item);
		}
		catch (const MacOSError &err) {
			if (err.osStatus() != errSecDuplicateItem)
				throw; // if item already exists, fall through to update
		}
    }
	item->update();

    END_SECAPI
}