/* * Copyright (c) 2000-2001,2004 Apple Computer, 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@ */ // // testacls - ACL-related test cases. // #include "testclient.h" #include "testutils.h" // // Blob tests. // Encodes and decodes Db and Key blobs and all that jazz. // void blobs() { printf("* Database blob encryption test\n"); ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard()); DbTester db1(ss, "/tmp/one", NULL, 60, true); DbTester db2(ss, "/tmp/two", NULL, 30, false); // encode db1, purge it, decode it again CssmData dbBlob; ss.encodeDb(db1, dbBlob); DbHandle db1a = ss.decodeDb(db1.dbId, &nullCred, dbBlob); ss.releaseDb(db1); if (db1 == db1a) detail("REUSED DB HANDLE ON DECODEDB (probably wrong)"); DBParameters savedParams; ss.getDbParameters(db1a, savedParams); assert(savedParams.idleTimeout == db1.params.idleTimeout); assert(savedParams.lockOnSleep == db1.params.lockOnSleep); detail("Database encode/decode passed"); // make sure the old handle isn't valid anymore try { ss.getDbParameters(db1, savedParams); printf("OLD DATABASE HANDLE NOT PURGED (possibly wrong)\n"); } catch (const CssmCommonError &err) { detail(err, "old DB handle rejected"); } // open db1 a second time (so now there's two db handles for db1) DbHandle db1b = ss.decodeDb(db1.dbId, &nullCred, dbBlob); // release both db1 handles and db2 ss.releaseDb(db1a); ss.releaseDb(db1b); ss.releaseDb(db2); } // // 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"); } }