//
// Add item to keychain
//
PrimaryKey UnlockReferralItem::add(Keychain &keychain)
{
	StLock<Mutex>_(mMutex);
	// If we already have a Keychain we can't be added.
	if (mKeychain)
		MacOSError::throwMe(errSecDuplicateItem);

	populateAttributes();

	CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();

	Db db(keychain->database());
	// add the item to the (regular) db
	try
	{
		mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
		secdebug("usertrust", "%p inserted", this);
	}
	catch (const CssmError &e)
	{
		if (e.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE)
			throw;

		// Create the referral relation and try again.
		secdebug("usertrust", "adding schema relation for user trusts");
#if 0
		db->createRelation(CSSM_DL_DB_RECORD_UNLOCK_REFERRAL,
			"CSSM_DL_DB_RECORD_UNLOCK_REFERRAL",
			Schema::UnlockReferralSchemaAttributeCount,
			Schema::UnlockReferralSchemaAttributeList,
			Schema::UnlockReferralSchemaIndexCount,
			Schema::UnlockReferralSchemaIndexList);
		keychain->keychainSchema()->didCreateRelation(
			CSSM_DL_DB_RECORD_UNLOCK_REFERRAL,
			"CSSM_DL_DB_RECORD_UNLOCK_REFERRAL",
			Schema::UnlockReferralSchemaAttributeCount,
			Schema::UnlockReferralSchemaAttributeList,
			Schema::UnlockReferralSchemaIndexCount,
			Schema::UnlockReferralSchemaIndexList);
#endif
		//keychain->resetSchema();

		mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
		secdebug("usertrust", "%p inserted now", this);
	}

	mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
    mKeychain = keychain;
	return mPrimaryKey;
}
Esempio n. 2
0
PrimaryKey
Certificate::add(Keychain &keychain)
{
	StLock<Mutex>_(mMutex);
	// If we already have a Keychain we can't be added.
	if (mKeychain)
		MacOSError::throwMe(errSecDuplicateItem);

	populateAttributes();

	CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();

	Db db(keychain->database());
	// add the item to the (regular) db
	try
	{
		mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
	}
	catch (const CssmError &e)
	{
		if (e.osStatus() != CSSMERR_DL_INVALID_RECORDTYPE)
			throw;

		// Create the cert relation and try again.
		db->createRelation(CSSM_DL_DB_RECORD_X509_CERTIFICATE,
			"CSSM_DL_DB_RECORD_X509_CERTIFICATE",
			Schema::X509CertificateSchemaAttributeCount,
			Schema::X509CertificateSchemaAttributeList,
			Schema::X509CertificateSchemaIndexCount,
			Schema::X509CertificateSchemaIndexList);
		keychain->keychainSchema()->didCreateRelation(
			CSSM_DL_DB_RECORD_X509_CERTIFICATE,
			"CSSM_DL_DB_RECORD_X509_CERTIFICATE",
			Schema::X509CertificateSchemaAttributeCount,
			Schema::X509CertificateSchemaAttributeList,
			Schema::X509CertificateSchemaIndexCount,
			Schema::X509CertificateSchemaIndexList);

		mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
	}

	mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
    mKeychain = keychain;

	return mPrimaryKey;
}
PrimaryKey ItemImpl::addWithCopyInfo (Keychain &keychain, bool isCopy)
{
	StLock<Mutex>_(mMutex);
	// If we already have a Keychain we can't be added.
	if (mKeychain)
		MacOSError::throwMe(errSecDuplicateItem);

    // If we don't have any attributes we can't be added.
    // (this might occur if attempting to add the item twice, since our attributes
    // and data are set to NULL at the end of this function.)
    if (!mDbAttributes.get())
		MacOSError::throwMe(errSecDuplicateItem);

	CSSM_DB_RECORDTYPE recordType = mDbAttributes->recordType();

	// update the creation and update dates on the new item
	if (!isCopy)
	{
		KeychainSchema schema = keychain->keychainSchema();
		SInt64 date;
		GetCurrentMacLongDateTime(date);
		if (schema->hasAttribute(recordType, kSecCreationDateItemAttr))
		{
			setAttribute(schema->attributeInfoFor(recordType, kSecCreationDateItemAttr), date);
		}

		if (schema->hasAttribute(recordType, kSecModDateItemAttr))
		{
			setAttribute(schema->attributeInfoFor(recordType, kSecModDateItemAttr), date);
		}
	}

    // If the label (PrintName) attribute isn't specified, set a default label.
    if (!mDoNotEncrypt && !mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr)))
    {
		// if doNotEncrypt was set all of the attributes are wrapped in the data blob.  Don't calculate here.
        CssmDbAttributeData *label = NULL;
        switch (recordType)
        {
            case CSSM_DL_DB_RECORD_GENERIC_PASSWORD:
                label = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr));
                break;

            case CSSM_DL_DB_RECORD_APPLESHARE_PASSWORD:
            case CSSM_DL_DB_RECORD_INTERNET_PASSWORD:
                label = mDbAttributes->find(Schema::attributeInfo(kSecServerItemAttr));
                // if AppleShare server name wasn't specified, try the server address
                if (!label) label = mDbAttributes->find(Schema::attributeInfo(kSecAddressItemAttr));
                break;

            default:
                break;
        }
        // if all else fails, use the account name.
        if (!label)
			label = mDbAttributes->find(Schema::attributeInfo(kSecAccountItemAttr));

        if (label && label->size())
            setAttribute (Schema::attributeInfo(kSecLabelItemAttr), label->at<CssmData>(0));
    }

	// get the attributes that are part of the primary key
	const CssmAutoDbRecordAttributeInfo &primaryKeyInfos =
		keychain->primaryKeyInfosFor(recordType);

	// make sure each primary key element has a value in the item, otherwise
	// the database will complain. we make a set of the provided attribute infos
	// to avoid O(N^2) behavior.

	DbAttributes *attributes = mDbAttributes.get();
	typedef set<CssmDbAttributeInfo> InfoSet;
	InfoSet infoSet;

	if (!mDoNotEncrypt)
	{
		// make a set of all the attributes in the key
		for (uint32 i = 0; i < attributes->size(); i++)
			infoSet.insert(attributes->at(i).Info);

		for (uint32 i = 0; i < primaryKeyInfos.size(); i++) { // check to make sure all required attributes are in the key
			InfoSet::const_iterator it = infoSet.find(primaryKeyInfos.at(i));

			if (it == infoSet.end()) { // not in the key?  add the default
				// we need to add a default value to the item attributes
				attributes->add(primaryKeyInfos.at(i), defaultAttributeValue(primaryKeyInfos.at(i)));
			}
		}
	}

	Db db(keychain->database());
	if (mDoNotEncrypt)
	{
		mUniqueId = db->insertWithoutEncryption (recordType, NULL, mData.get());
	}
	else if (useSecureStorage(db))
	{
		// Add the item to the secure storage db
		SSDbImpl* impl = dynamic_cast<SSDbImpl *>(&(*db));
		if (impl == NULL)
		{
			CssmError::throwMe(CSSMERR_CSSM_INVALID_POINTER);
		}

		SSDb ssDb(impl);

		TrackingAllocator allocator(Allocator::standard());

		// hhs replaced with the new aclFactory class
		AclFactory aclFactory;
		const AccessCredentials *nullCred = aclFactory.nullCred();

		SecPointer<Access> access = mAccess;
		if (!access) {
			// create default access controls for the new item
			CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecLabelItemAttr));
			string printName = data ? CssmData::overlay(data->Value[0]).toString() : "keychain item";
			access = new Access(printName);

			// special case for "iTools" password - allow anyone to decrypt the item
			if (recordType == CSSM_DL_DB_RECORD_GENERIC_PASSWORD)
			{
				CssmDbAttributeData *data = mDbAttributes->find(Schema::attributeInfo(kSecServiceItemAttr));
				if (data && data->Value[0].Length == 6 && !memcmp("iTools", data->Value[0].Data, 6))
				{
					typedef vector<SecPointer<ACL> > AclSet;
					AclSet acls;
					access->findAclsForRight(CSSM_ACL_AUTHORIZATION_DECRYPT, acls);
					for (AclSet::const_iterator it = acls.begin(); it != acls.end(); it++)
						(*it)->form(ACL::allowAllForm);
				}
			}
		}

		// Get the handle of the DL underlying this CSPDL.
		CSSM_DL_DB_HANDLE dldbh;
		db->passThrough(CSSM_APPLECSPDL_DB_GET_HANDLE, NULL,
			reinterpret_cast<void **>(&dldbh));

		// Turn off autocommit on the underlying DL and remember the old state.
		CSSM_BOOL autoCommit = CSSM_TRUE;
		ObjectImpl::check(CSSM_DL_PassThrough(dldbh,
			CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
			0, reinterpret_cast<void **>(&autoCommit)));

		try
		{
			// Create a new SSGroup with temporary access controls
			Access::Maker maker;
			ResourceControlContext prototype;
			maker.initialOwner(prototype, nullCred);
			SSGroup ssGroup(ssDb, &prototype);

			try
			{
				// Insert the record using the newly created group.
				mUniqueId = ssDb->insert(recordType, mDbAttributes.get(),
										 mData.get(), ssGroup, maker.cred());
			}
			catch(...)
			{
				ssGroup->deleteKey(nullCred);
				throw;
			}

			// now finalize the access controls on the group
			access->setAccess(*ssGroup, maker);
			mAccess = NULL;	// use them and lose them
			if (autoCommit)
			{
				// autoCommit was on so commit now that we are done and turn
				// it back on.
				ObjectImpl::check(CSSM_DL_PassThrough(dldbh,
					CSSM_APPLEFILEDL_COMMIT, NULL, NULL));
				CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
					reinterpret_cast<const void *>(autoCommit), NULL);
			}
		}
		catch (...)
		{
			if (autoCommit)
			{
				// autoCommit was off so rollback since we failed and turn
				// autoCommit back on.
				CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_ROLLBACK, NULL, NULL);
				CSSM_DL_PassThrough(dldbh, CSSM_APPLEFILEDL_TOGGLE_AUTOCOMMIT,
					reinterpret_cast<const void *>(autoCommit), NULL);
			}
			throw;
		}
	}
	else
	{
		// add the item to the (regular) db
		mUniqueId = db->insert(recordType, mDbAttributes.get(), mData.get());
	}

	mPrimaryKey = keychain->makePrimaryKey(recordType, mUniqueId);
    mKeychain = keychain;

	// Forget our data and attributes.
	mData = NULL;
	mDbAttributes.reset(NULL);

	return mPrimaryKey;
}