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