//
// Database timeouts
// @@@ Incomplete and not satisfactory
//
void timeouts()
{
	printf("* Database timeout locks test\n");
    CssmAllocator &alloc = CssmAllocator::standard();
	ClientSession ss(alloc, alloc);
    
    DLDbIdentifier dbId1(ssuid, "/tmp/one", NULL);
    DLDbIdentifier dbId2(ssuid, "/tmp/two", NULL);
	DBParameters initialParams1 = { 4, false };	// 4 seconds timeout
	DBParameters initialParams2 = { 8, false };	// 8 seconds timeout
    
	// credential to set keychain passphrase
    AutoCredentials pwCred(alloc);
    StringData password("mumbojumbo");
    pwCred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
        new(alloc) ListElement(password));
    
	DbHandle db1 = ss.createDb(dbId1, &pwCred, NULL, initialParams1);
	DbHandle db2 = ss.createDb(dbId2, &pwCred, NULL, initialParams2);
	detail("Databases created");
    
    // generate a key
	const CssmCryptoData seed(StringData("rain tonight"));
	FakeContext genContext(CSSM_ALGCLASS_KEYGEN, CSSM_ALGID_DES,
		&::Context::Attr(CSSM_ATTRIBUTE_KEY_LENGTH, 64),
		&::Context::Attr(CSSM_ATTRIBUTE_SEED, seed),
		NULL);
    KeyHandle key;
    CssmKey::Header header;
    ss.generateKey(db1, genContext, CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
        CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
        /*cred*/NULL, NULL, key, header);
    ss.generateKey(db2, genContext, CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
        CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
        /*cred*/NULL, NULL, key, header);
	detail("Keys generated and stored");
	
	// credential to provide keychain passphrase
    AutoCredentials pwCred2(alloc);
    pwCred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
        new(alloc) ListElement(password));

	//@@@ incomplete
	ss.releaseDb(db1);
	ss.releaseDb(db2);
}
Beispiel #2
0
//
// Given a (truncated) Database credentials TypedList specifying a master key,
// locate the key and return a reference to it.
//
CssmClient::Key KeychainDatabase::keyFromCreds(const TypedList &sample, unsigned int requiredLength)
{
	// decode TypedList structure (sample type; Data:CSPHandle; Data:CSSM_KEY)
	assert(sample.type() == CSSM_SAMPLE_TYPE_SYMMETRIC_KEY || sample.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY);
	if (sample.length() != requiredLength
		|| sample[1].type() != CSSM_LIST_ELEMENT_DATUM
		|| sample[2].type() != CSSM_LIST_ELEMENT_DATUM
		|| (requiredLength == 4 && sample[3].type() != CSSM_LIST_ELEMENT_DATUM))
			CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
	KeyHandle &handle = *sample[1].data().interpretedAs<KeyHandle>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
    // We used to be able to check the length but supporting multiple client
    // architectures dishes that (sizeof(CSSM_KEY) varies due to alignment and
    // field-size differences).  The decoding in the transition layer should 
    // serve as a sufficient garbling check anyway.  
    if (sample[2].data().data() == NULL)
        CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
    CssmKey &key = *sample[2].data().interpretedAs<CssmKey>();

	if (key.header().cspGuid() == gGuidAppleCSPDL) {
		// handleOrKey is a SecurityServer KeyHandle; ignore key argument
		return safer_cast<LocalKey &>(*Server::key(handle));
	} else 
	if (sample.type() == CSSM_SAMPLE_TYPE_ASYMMETRIC_KEY) {
		/*
			Contents (see DefaultCredentials::unlockKey in libsecurity_keychain/defaultcreds.cpp)
			
			sample[0]	sample type
			sample[1]	csp handle for master or wrapping key; is really a keyhandle
			sample[2]	masterKey [not used since securityd cannot interpret; use sample[1] handle instead]
			sample[3]	UnlockReferralRecord data, in this case the flattened symmetric key
		*/

		// RefPointer<Key> Server::key(KeyHandle key)
		KeyHandle keyhandle = *sample[1].data().interpretedAs<KeyHandle>(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
		CssmData &flattenedKey = sample[3].data();
		RefPointer<Key> unwrappingKey = Server::key(keyhandle);
		Database &db=unwrappingKey->database();
		
		CssmKey rawWrappedKey;
		unflattenKey(flattenedKey, rawWrappedKey);

		RefPointer<Key> masterKey;
		CssmData emptyDescriptiveData;
		const AccessCredentials *cred = NULL;
		const AclEntryPrototype *owner = NULL;
		CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
		CSSM_KEYATTR_FLAGS attrs = CSSM_KEYATTR_EXTRACTABLE;	//CSSM_KEYATTR_RETURN_REF | 

		// Get default credentials for unwrappingKey (the one on the token)
		// Copied from Statics::Statics() in libsecurity_keychain/aclclient.cpp
		// Following KeyItem::getCredentials, one sees that the "operation" parameter
		// e.g. "CSSM_ACL_AUTHORIZATION_DECRYPT" is ignored
		Allocator &alloc = Allocator::standard();
		AutoCredentials promptCred(alloc, 3);// enable interactive prompting
	
		// promptCred: a credential permitting user prompt confirmations
		// contains:
		//  a KEYCHAIN_PROMPT sample, both by itself and in a THRESHOLD
		//  a PROMPTED_PASSWORD sample
		promptCred.sample(0) = TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT);
		promptCred.sample(1) = TypedList(alloc, CSSM_SAMPLE_TYPE_THRESHOLD,
			new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT)));
		promptCred.sample(2) = TypedList(alloc, CSSM_SAMPLE_TYPE_PROMPTED_PASSWORD,
			new(alloc) ListElement(alloc, CssmData()));

		// This unwrap object is here just to provide a context
		CssmClient::UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);	//ok to lie about csp here
		unwrap.mode(CSSM_ALGMODE_NONE);
		unwrap.padding(CSSM_PADDING_PKCS1);
		unwrap.cred(promptCred);
		unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7));
		Security::Context *tmpContext;
		CSSM_CC_HANDLE CCHandle = unwrap.handle();
		/*CSSM_RETURN rx = */ CSSM_GetContext (CCHandle, (CSSM_CONTEXT_PTR *)&tmpContext);
		
		// OK, this is skanky but necessary. We overwrite fields in the context struct

		tmpContext->ContextType = CSSM_ALGCLASS_ASYMMETRIC;
		tmpContext->AlgorithmType = CSSM_ALGID_RSA;
		
		db.unwrapKey(*tmpContext, cred, owner, unwrappingKey, NULL, usage, attrs,
			rawWrappedKey, masterKey, emptyDescriptiveData);

	    Allocator::standard().free(rawWrappedKey.KeyData.Data);

		return safer_cast<LocalKey &>(*masterKey).key();
	}
	else
	{
		// not a KeyHandle reference; use key as a raw key
		if (key.header().blobType() != CSSM_KEYBLOB_RAW)
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
		if (key.header().keyClass() != CSSM_KEYCLASS_SESSION_KEY)
			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
		return CssmClient::Key(Server::csp(), key, true);
	}
}
CssmList OriginAclSubject::toList(Allocator &alloc) const
{
	return TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PREAUTH,
		new(alloc) ListElement(mAuthTag));
}
CssmList SourceAclSubject::toList(Allocator &alloc) const
{
	return TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PREAUTH_SOURCE,
		new(alloc) ListElement(mSourceSubject->toList(alloc)));
}
//
// Database tests.
// Database locks/unlocks etc.
//
void databases()
{
	printf("* Database manipulation test\n");
    CssmAllocator &alloc = CssmAllocator::standard();
	ClientSession ss(alloc, alloc);
    
    AutoCredentials pwCred(alloc);
    StringData passphrase("two");
    StringData badPassphrase("three");
    pwCred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
        new(alloc) ListElement(passphrase));
    pwCred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
        new(alloc) ListElement(badPassphrase));
	// pwCred = (NEW: two, OLD: three)
	
	DbTester db1(ss, "/tmp/one", NULL, 30, true);
	DbTester db2(ss, "/tmp/two", &pwCred, 60, false);
	// db2.passphrase = two
	
	// encode db1 and re-open it
	CssmData dbBlob;
	ss.encodeDb(db1, dbBlob);
	DbHandle db1b = ss.decodeDb(db1.dbId, &nullCred, dbBlob);
	if (db1b == db1.dbRef)
		detail("REUSED DB HANDLE ON DECODEDB (probably wrong)");
	
    // open db1 a third time (so now there's three db handles for db1)
    DbHandle db1c = ss.decodeDb(db1.dbId, &nullCred, dbBlob);
    
    // lock them to get started
    ss.lock(db1);
    ss.lock(db2);
    
    // unlock it through user
    prompt("unlock");
    ss.unlock(db1);
	prompt();
    ss.unlock(db1b);		// 2nd unlock should not prompt
    ss.lock(db1c);			// lock it again
    prompt("unlock");
    ss.unlock(db1);		// and that should prompt again
	prompt();
    
    // db2 has a passphrase lock credentials - it'll work without U/I
	db2.unlock("wrong passphrase");		// pw=two, cred=three
    AutoCredentials pwCred2(alloc);
    pwCred2 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
        new(alloc) ListElement(passphrase));
	// pwCred2 = (OLD: two)
    ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, &pwCred2); // set it
	db2.unlock();
    ss.lock(db2);
    
    // now change db2's passphrase
    ss.lock(db2);
    pwCred2 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
        new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
        new(alloc) ListElement(badPassphrase));
	// pwCred2 = (OLD: two, NEW: three)
	db2.changePassphrase(&pwCred2);
	// passphrase = three, cred = (OLD: two)
	
    // encode and re-decode to make sure new data is there
    CssmData blob2;
    ss.encodeDb(db2, blob2);
    DbHandle db2a = ss.decodeDb(db2.dbId, &pwCred, blob2);
	// db2a cred = (OLD: two, NEW: three)
    
    // now, the *old* cred won't work anymore
	db2.unlock("old passphrase accepted");

    // back to the old credentials, which *do* have the (old bad, now good) passphrase
    ss.lock(db2a);
    ss.unlock(db2a);
    detail("New passphrase accepted");
	
	// clear the credentials (this will prompt; cancel it)
	ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, NULL);
	prompt("cancel");
	db2.unlock("null credential accepted");
	prompt();
	
	// fell-swoop from-to change password operation
	StringData newPassphrase("hollerith");
	AutoCredentials pwCred3(alloc);
	pwCred3 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
		new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
		new(alloc) ListElement(newPassphrase));
	pwCred3 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
		new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
		new(alloc) ListElement(passphrase));
	db2.changePassphrase(&pwCred3, "accepting original (unchanged) passphrase");
	
	AutoCredentials pwCred4(alloc);
	pwCred4 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK,
		new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
		new(alloc) ListElement(newPassphrase));
	pwCred4 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
		new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
		new(alloc) ListElement(badPassphrase));
	db2.changePassphrase(&pwCred4);
	
	// final status check
	AutoCredentials pwCred5(alloc);
	pwCred5 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK,
		new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD),
		new(alloc) ListElement(newPassphrase));	
	ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, &pwCred5);
	db2.unlock();
	detail("Final passphrase change verified");
}
//
// Key encryption tests.
//
void keyBlobs()
{
	printf("* Keyblob encryption test\n");
    CssmAllocator &alloc = CssmAllocator::standard();
	ClientSession ss(alloc, alloc);
    
    DLDbIdentifier dbId1(ssuid, "/tmp/one", NULL);
	DBParameters initialParams1 = { 3600, false };
    
    // create a new database
	DbHandle db = ss.createDb(dbId1, NULL, NULL, initialParams1);
	detail("Database created");
	
	// establish an ACL for the key
	StringData theAclPassword("Strenge Geheimsache");
	AclEntryPrototype initialAcl;
	initialAcl.TypedSubject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD,
		new(alloc) ListElement(theAclPassword));
	AclEntryInput initialAclInput(initialAcl);
    
    AutoCredentials cred(alloc);
    cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD,
        new(alloc) ListElement(theAclPassword));
    
    // generate a key
	const CssmCryptoData seed(StringData("Farmers' day"));
	FakeContext genContext(CSSM_ALGCLASS_KEYGEN, CSSM_ALGID_DES,
		&::Context::Attr(CSSM_ATTRIBUTE_KEY_LENGTH, 64),
		&::Context::Attr(CSSM_ATTRIBUTE_SEED, seed),
		NULL);
    KeyHandle key;
    CssmKey::Header header;
    ss.generateKey(db, genContext, CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
        CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT,
        /*cred*/NULL, &initialAclInput, key, header);
	detail("Key generated");
    
    // encrypt with the key
    StringData clearText("Yet another boring cleartext sample string text sequence.");
    StringData iv("Aardvark");
    CssmKey nullKey; memset(&nullKey, 0, sizeof(nullKey));
	FakeContext cryptoContext(CSSM_ALGCLASS_SYMMETRIC, CSSM_ALGID_DES,
		&::Context::Attr(CSSM_ATTRIBUTE_KEY, nullKey),
		&::Context::Attr(CSSM_ATTRIBUTE_INIT_VECTOR, iv),
		&::Context::Attr(CSSM_ATTRIBUTE_MODE, CSSM_ALGMODE_CBC_IV8),
		&::Context::Attr(CSSM_ATTRIBUTE_PADDING, CSSM_PADDING_PKCS1),
        &::Context::Attr(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS, cred),
		NULL);
	CssmData cipherText;
	ss.encrypt(cryptoContext, key, clearText, cipherText);
	detail("Plaintext encrypted with original key");
    
    // encode the key and release it
    CssmData blob;
    ss.encodeKey(key, blob);
    ss.releaseKey(key);
    detail("Key encoded and released");
    
    // decode it again, re-introducing it
    CssmKey::Header decodedHeader;
    KeyHandle key2 = ss.decodeKey(db, blob, decodedHeader);
    detail("Key decoded");
    
    // decrypt with decoded key
    CssmData recovered;
    ss.decrypt(cryptoContext, key2, cipherText, recovered);
    assert(recovered == clearText);
    detail("Decoded key correctly decrypts ciphertext");
    
    // check a few header fields
    if (!memcmp(&header, &decodedHeader, sizeof(header))) {
        detail("All header fields match");
    } else {
        assert(header.algorithm() == decodedHeader.algorithm());
        assert(header.blobType() == decodedHeader.blobType());
        assert(header.blobFormat() == decodedHeader.blobFormat());
        assert(header.keyClass() == decodedHeader.keyClass());
        assert(header.attributes() == decodedHeader.attributes());
        assert(header.usage() == decodedHeader.usage());
        printf("Some header fields differ (probably okay)\n");
    }
    
    // make sure we need the credentials (destructive)
    memset(&cred, 0, sizeof(cred));
    try {
        ss.decrypt(cryptoContext, key2, cipherText, recovered);
        error("RESTORED ACL FAILS TO RESTRICT");
    } catch (CssmError &err) {
        detail(err, "Restored key restricts access properly");
    }
}
Beispiel #7
0
//
// Make a copy of this subject in CSSM_LIST form
//
CssmList PasswordAclSubject::toList(Allocator &alloc) const
{
    // the password itself is private and not exported to CSSM
	return TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD);
}