1/* 2 * Copyright (c) 2000-2001,2003-2004 Apple Computer, Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 25// 26// testacls - ACL-related test cases. 27// 28#include "testclient.h" 29#include "testutils.h" 30 31 32// 33// Blob tests. 34// Encodes and decodes Db and Key blobs and all that jazz. 35// 36void blobs() 37{ 38 printf("* Database blob encryption test\n"); 39 ClientSession ss(CssmAllocator::standard(), CssmAllocator::standard()); 40 41 DbTester db1(ss, "/tmp/one", NULL, 60, true); 42 DbTester db2(ss, "/tmp/two", NULL, 30, false); 43 44 // encode db1, purge it, decode it again 45 CssmData dbBlob; 46 ss.encodeDb(db1, dbBlob); 47 DbHandle db1a = ss.decodeDb(db1.dbId, &nullCred, dbBlob); 48 ss.releaseDb(db1); 49 if (db1 == db1a) 50 detail("REUSED DB HANDLE ON DECODEDB (probably wrong)"); 51 DBParameters savedParams; 52 ss.getDbParameters(db1a, savedParams); 53 assert(savedParams.idleTimeout == db1.params.idleTimeout); 54 assert(savedParams.lockOnSleep == db1.params.lockOnSleep); 55 detail("Database encode/decode passed"); 56 57 // make sure the old handle isn't valid anymore 58 try { 59 ss.getDbParameters(db1, savedParams); 60 printf("OLD DATABASE HANDLE NOT PURGED (possibly wrong)\n"); 61 } catch (const CssmCommonError &err) { 62 detail(err, "old DB handle rejected"); 63 } 64 65 // open db1 a second time (so now there's two db handles for db1) 66 DbHandle db1b = ss.decodeDb(db1.dbId, &nullCred, dbBlob); 67 68 // release both db1 handles and db2 69 ss.releaseDb(db1a); 70 ss.releaseDb(db1b); 71 ss.releaseDb(db2); 72} 73 74 75// 76// Database tests. 77// Database locks/unlocks etc. 78// 79void databases() 80{ 81 printf("* Database manipulation test\n"); 82 CssmAllocator &alloc = CssmAllocator::standard(); 83 ClientSession ss(alloc, alloc); 84 85 AutoCredentials pwCred(alloc); 86 StringData passphrase("two"); 87 StringData badPassphrase("three"); 88 pwCred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK, 89 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 90 new(alloc) ListElement(passphrase)); 91 pwCred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, 92 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 93 new(alloc) ListElement(badPassphrase)); 94 // pwCred = (NEW: two, OLD: three) 95 96 DbTester db1(ss, "/tmp/one", NULL, 30, true); 97 DbTester db2(ss, "/tmp/two", &pwCred, 60, false); 98 // db2.passphrase = two 99 100 // encode db1 and re-open it 101 CssmData dbBlob; 102 ss.encodeDb(db1, dbBlob); 103 DbHandle db1b = ss.decodeDb(db1.dbId, &nullCred, dbBlob); 104 if (db1b == db1.dbRef) 105 detail("REUSED DB HANDLE ON DECODEDB (probably wrong)"); 106 107 // open db1 a third time (so now there's three db handles for db1) 108 DbHandle db1c = ss.decodeDb(db1.dbId, &nullCred, dbBlob); 109 110 // lock them to get started 111 ss.lock(db1); 112 ss.lock(db2); 113 114 // unlock it through user 115 prompt("unlock"); 116 ss.unlock(db1); 117 prompt(); 118 ss.unlock(db1b); // 2nd unlock should not prompt 119 ss.lock(db1c); // lock it again 120 prompt("unlock"); 121 ss.unlock(db1); // and that should prompt again 122 prompt(); 123 124 // db2 has a passphrase lock credentials - it'll work without U/I 125 db2.unlock("wrong passphrase"); // pw=two, cred=three 126 AutoCredentials pwCred2(alloc); 127 pwCred2 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, 128 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 129 new(alloc) ListElement(passphrase)); 130 // pwCred2 = (OLD: two) 131 ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, &pwCred2); // set it 132 db2.unlock(); 133 ss.lock(db2); 134 135 // now change db2's passphrase 136 ss.lock(db2); 137 pwCred2 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK, 138 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 139 new(alloc) ListElement(badPassphrase)); 140 // pwCred2 = (OLD: two, NEW: three) 141 db2.changePassphrase(&pwCred2); 142 // passphrase = three, cred = (OLD: two) 143 144 // encode and re-decode to make sure new data is there 145 CssmData blob2; 146 ss.encodeDb(db2, blob2); 147 DbHandle db2a = ss.decodeDb(db2.dbId, &pwCred, blob2); 148 // db2a cred = (OLD: two, NEW: three) 149 150 // now, the *old* cred won't work anymore 151 db2.unlock("old passphrase accepted"); 152 153 // back to the old credentials, which *do* have the (old bad, now good) passphrase 154 ss.lock(db2a); 155 ss.unlock(db2a); 156 detail("New passphrase accepted"); 157 158 // clear the credentials (this will prompt; cancel it) 159 ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, NULL); 160 prompt("cancel"); 161 db2.unlock("null credential accepted"); 162 prompt(); 163 164 // fell-swoop from-to change password operation 165 StringData newPassphrase("hollerith"); 166 AutoCredentials pwCred3(alloc); 167 pwCred3 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK, 168 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 169 new(alloc) ListElement(newPassphrase)); 170 pwCred3 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, 171 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 172 new(alloc) ListElement(passphrase)); 173 db2.changePassphrase(&pwCred3, "accepting original (unchanged) passphrase"); 174 175 AutoCredentials pwCred4(alloc); 176 pwCred4 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_CHANGE_LOCK, 177 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 178 new(alloc) ListElement(newPassphrase)); 179 pwCred4 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, 180 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 181 new(alloc) ListElement(badPassphrase)); 182 db2.changePassphrase(&pwCred4); 183 184 // final status check 185 AutoCredentials pwCred5(alloc); 186 pwCred5 += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_LOCK, 187 new(alloc) ListElement(CSSM_SAMPLE_TYPE_PASSWORD), 188 new(alloc) ListElement(newPassphrase)); 189 ss.authenticateDb(db2, CSSM_DB_ACCESS_WRITE, &pwCred5); 190 db2.unlock(); 191 detail("Final passphrase change verified"); 192} 193 194 195// 196// Key encryption tests. 197// 198void keyBlobs() 199{ 200 printf("* Keyblob encryption test\n"); 201 CssmAllocator &alloc = CssmAllocator::standard(); 202 ClientSession ss(alloc, alloc); 203 204 DLDbIdentifier dbId1(ssuid, "/tmp/one", NULL); 205 DBParameters initialParams1 = { 3600, false }; 206 207 // create a new database 208 DbHandle db = ss.createDb(dbId1, NULL, NULL, initialParams1); 209 detail("Database created"); 210 211 // establish an ACL for the key 212 StringData theAclPassword("Strenge Geheimsache"); 213 AclEntryPrototype initialAcl; 214 initialAcl.TypedSubject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD, 215 new(alloc) ListElement(theAclPassword)); 216 AclEntryInput initialAclInput(initialAcl); 217 218 AutoCredentials cred(alloc); 219 cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, 220 new(alloc) ListElement(theAclPassword)); 221 222 // generate a key 223 const CssmCryptoData seed(StringData("Farmers' day")); 224 FakeContext genContext(CSSM_ALGCLASS_KEYGEN, CSSM_ALGID_DES, 225 &::Context::Attr(CSSM_ATTRIBUTE_KEY_LENGTH, 64), 226 &::Context::Attr(CSSM_ATTRIBUTE_SEED, seed), 227 NULL); 228 KeyHandle key; 229 CssmKey::Header header; 230 ss.generateKey(db, genContext, CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT, 231 CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT, 232 /*cred*/NULL, &initialAclInput, key, header); 233 detail("Key generated"); 234 235 // encrypt with the key 236 StringData clearText("Yet another boring cleartext sample string text sequence."); 237 StringData iv("Aardvark"); 238 CssmKey nullKey; memset(&nullKey, 0, sizeof(nullKey)); 239 FakeContext cryptoContext(CSSM_ALGCLASS_SYMMETRIC, CSSM_ALGID_DES, 240 &::Context::Attr(CSSM_ATTRIBUTE_KEY, nullKey), 241 &::Context::Attr(CSSM_ATTRIBUTE_INIT_VECTOR, iv), 242 &::Context::Attr(CSSM_ATTRIBUTE_MODE, CSSM_ALGMODE_CBC_IV8), 243 &::Context::Attr(CSSM_ATTRIBUTE_PADDING, CSSM_PADDING_PKCS1), 244 &::Context::Attr(CSSM_ATTRIBUTE_ACCESS_CREDENTIALS, cred), 245 NULL); 246 CssmData cipherText; 247 ss.encrypt(cryptoContext, key, clearText, cipherText); 248 detail("Plaintext encrypted with original key"); 249 250 // encode the key and release it 251 CssmData blob; 252 ss.encodeKey(key, blob); 253 ss.releaseKey(key); 254 detail("Key encoded and released"); 255 256 // decode it again, re-introducing it 257 CssmKey::Header decodedHeader; 258 KeyHandle key2 = ss.decodeKey(db, blob, decodedHeader); 259 detail("Key decoded"); 260 261 // decrypt with decoded key 262 CssmData recovered; 263 ss.decrypt(cryptoContext, key2, cipherText, recovered); 264 assert(recovered == clearText); 265 detail("Decoded key correctly decrypts ciphertext"); 266 267 // check a few header fields 268 if (!memcmp(&header, &decodedHeader, sizeof(header))) { 269 detail("All header fields match"); 270 } else { 271 assert(header.algorithm() == decodedHeader.algorithm()); 272 assert(header.blobType() == decodedHeader.blobType()); 273 assert(header.blobFormat() == decodedHeader.blobFormat()); 274 assert(header.keyClass() == decodedHeader.keyClass()); 275 assert(header.attributes() == decodedHeader.attributes()); 276 assert(header.usage() == decodedHeader.usage()); 277 printf("Some header fields differ (probably okay)\n"); 278 } 279 280 // make sure we need the credentials (destructive) 281 memset(&cred, 0, sizeof(cred)); 282 try { 283 ss.decrypt(cryptoContext, key2, cipherText, recovered); 284 error("RESTORED ACL FAILS TO RESTRICT"); 285 } catch (CssmError &err) { 286 detail(err, "Restored key restricts access properly"); 287 } 288} 289