2 * Copyright (c) 2000-2001,2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
23 * @APPLE_LICENSE_HEADER_END@
28 // testacls - ACL-related test cases.
30 #include "testclient.h"
31 #include "testutils.h"
32 #include <Security/osxsigner.h>
34 using namespace CodeSigning
;
42 printf("* Basic ACL tests\n");
43 CssmAllocator
&alloc
= CssmAllocator::standard();
44 ClientSession
ss(alloc
, alloc
);
46 // create key with initial ACL
47 StringData
initialAclPassphrase("very secret");
48 AclEntryPrototype initialAcl
;
49 initialAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
50 new(alloc
) ListElement(initialAclPassphrase
));
51 AclEntryInput
initialAclInput(initialAcl
);
52 AclTester
tester(ss
, &initialAclInput
);
54 // get the owner and verify
55 AclOwnerPrototype owner
;
56 ss
.getKeyOwner(tester
.keyRef
, owner
);
57 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
58 assert(owner
.subject().length() == 1);
60 // get the acl entry and verify
64 ss
.getKeyAcl(tester
.keyRef
, NULL
/*tag*/, count
, acls
);
66 const AclEntryInfo
&acl1
= acls
[0];
67 const TypedList
&subject1
= acl1
.proto().subject();
68 assert(subject1
.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
69 assert(subject1
.length() == 1);
72 // try to use the key and see...
73 tester
.testWrap(&nullCred
, "ACCEPTING NULL CREDENTIAL");
74 AutoCredentials
cred(alloc
);
75 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
76 new(alloc
) ListElement(StringData("wrongo")));
77 tester
.testWrap(&cred
, "ACCEPTING WRONG PASSWORD CREDENTIAL");
78 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
79 new(alloc
) ListElement(StringData("very secret")));
80 tester
.testWrap(&cred
);
82 // now *replace* the ACL entry with a new one...
84 detail("Changing ACL");
87 ss
.getKeyAcl(tester
.keyRef
, NULL
, count
, infos
);
88 assert(count
== 1); // one entry
90 AclEntryPrototype newAcl
;
91 TypedList subject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
92 new(alloc
) ListElement(2), new(alloc
) ListElement(3));
93 subject
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
94 new(alloc
) ListElement(alloc
, "check me!")));
95 subject
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
96 new(alloc
) ListElement(alloc
, "once again!")));
97 subject
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
98 new(alloc
) ListElement(alloc
, "hug me!")));
99 newAcl
.TypedSubject
= subject
;
100 AclEntryInput
input(newAcl
);
101 AclEdit
edit(infos
[0].handle(), input
);
104 AutoCredentials
nullCred(alloc
);
105 ss
.changeKeyAcl(tester
.keyRef
, nullCred
, edit
);
106 error("ALLOWED ACL EDIT WITHOUT CREDENTIALS");
107 } catch (CssmCommonError
&err
) {
108 detail(err
, "Acl Edit rejected properly");
110 ss
.changeKeyAcl(tester
.keyRef
, cred
, edit
);
111 detail("ACL changed OK");
114 // ... and see how the new one reacts
115 tester
.testWrap(&nullCred
, "ACCEPTING NULL CREDENTIALS NOW");
116 tester
.testWrap(&cred
, "ACCEPTING OLD CREDENTIALS FOR NEW ACL");
118 AutoCredentials
cred(alloc
);
119 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
120 new(alloc
) ListElement(alloc
, "check me!"));
121 tester
.testWrap(&cred
, "ACCEPTING LEAF SAMPLE WITHOUT THRESHOLD FRAMEWORK");
124 // Threshold subjects
126 detail("Testing threshold ACLs");
127 AutoCredentials
cred(alloc
);
128 TypedList
&threshold
= cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_THRESHOLD
,
129 new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
130 new(alloc
) ListElement(alloc
, "wrongo!")))
132 tester
.testWrap(&cred
, "ACCEPTING ALL WRONG SAMPLES IN THRESHOLD");
133 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
134 new(alloc
) ListElement(alloc
, "hug me!")));
135 tester
.testWrap(&cred
, "ACCEPTING TOO FEW THRESHOLD SAMPLES");
136 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
137 new(alloc
) ListElement(alloc
, "check me!")));
138 tester
.testWrap(&cred
);
139 // stuff the ballot box
140 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
141 new(alloc
) ListElement(alloc
, "and this!")));
142 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
143 new(alloc
) ListElement(alloc
, "and that!")));
144 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
145 new(alloc
) ListElement(alloc
, "and more!")));
146 #ifdef STRICT_THRESHOLD_SUBJECTS
147 tester
.testWrap(&cred
, "ACCEPTING OVER-STUFFED THRESHOLD");
149 tester
.testWrap(&cred
);
150 #endif //STRICT_THRESHOLD_SUBJECTS
153 // comment ACLs and tags
155 detail("Adding Comment entry");
157 AclEntryPrototype newAcl
;
158 TypedList subject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_COMMENT
,
159 new(alloc
) ListElement(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
160 new(alloc
) ListElement(alloc
, "Robby Ray!"))),
161 new(alloc
) ListElement(666));
162 newAcl
.TypedSubject
= subject
;
163 strcpy(newAcl
.EntryTag
, "vamos");
164 AclEntryInput
input(newAcl
);
166 ss
.changeKeyAcl(tester
.keyRef
, cred
, edit
);
167 detail("Entry added");
171 ss
.getKeyAcl(tester
.keyRef
, "vamos", count
, infos
);
172 assert(count
== 1); // one entry (with this tag)
173 const AclEntryInfo
&acl
= infos
[0];
174 const TypedList
&read
= acl
.proto().subject();
175 assert(read
.type() == CSSM_ACL_SUBJECT_TYPE_COMMENT
);
176 assert(read
.length() == 3);
177 assert(read
[2] == 666);
178 CssmList
&sublist
= read
[1];
179 assert(sublist
[0] == CSSM_ACL_SUBJECT_TYPE_THRESHOLD
);
180 assert(string(sublist
[1]) == "Robby Ray!");
182 detail("Comment entry retrieved okay");
188 // ACL authorization tests
192 printf("* ACL authorizations test\n");
193 CssmAllocator
&alloc
= CssmAllocator::standard();
194 ClientSession
ss(alloc
, alloc
);
196 // create key with initial ACL
197 CSSM_ACL_AUTHORIZATION_TAG wrapTag
= CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
;
198 CSSM_ACL_AUTHORIZATION_TAG encryptTag
= CSSM_ACL_AUTHORIZATION_ENCRYPT
;
199 StringData
initialAclPassphrase("very secret");
200 StringData
the2ndAclPassword("most secret");
201 AclEntryPrototype initialAcl
;
202 initialAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
203 new(alloc
) ListElement(initialAclPassphrase
));
204 initialAcl
.authorization().NumberOfAuthTags
= 1;
205 initialAcl
.authorization().AuthTags
= &wrapTag
;
206 AclEntryInput
initialAclInput(initialAcl
);
207 AclTester
tester(ss
, &initialAclInput
);
209 // get the owner and verify
210 AclOwnerPrototype owner
;
211 ss
.getKeyOwner(tester
.keyRef
, owner
);
212 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
213 assert(owner
.subject().length() == 1);
215 // get the acl entry and verify
219 ss
.getKeyAcl(tester
.keyRef
, NULL
/*tag*/, count
, acls
);
221 const AclEntryInfo
&acl1
= acls
[0];
222 const TypedList
&subject1
= acl1
.proto().subject();
223 assert(subject1
.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
224 assert(subject1
.length() == 1);
225 const AuthorizationGroup
&auths
= acl1
.proto().authorization();
226 assert(auths
.count() == 1);
227 assert(auths
[0] == CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
);
230 // try to use the key and see...
231 tester
.testWrap(&nullCred
, "ACCEPTING NULL CREDENTIAL");
232 AutoCredentials
cred(alloc
);
233 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
234 new(alloc
) ListElement(StringData("wrongo")));
235 tester
.testWrap(&cred
, "ACCEPTING WRONG PASSWORD CREDENTIAL");
236 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
237 new(alloc
) ListElement(initialAclPassphrase
));
238 tester
.testWrap(&cred
);
240 tester
.testEncrypt(&nullCred
, "ACCEPTING NULL CREDENTIAL FOR UNAUTHORIZED OPERATION");
241 tester
.testEncrypt(&cred
, "ACCEPTING GOOD CREDENTIAL FOR UNAUTHORIZED OPERATION");
243 // now *add* a new ACL entry for encryption
245 detail("Adding new ACL entry");
247 AclEntryPrototype newAcl
;
248 newAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
249 new(alloc
) ListElement(the2ndAclPassword
));
250 newAcl
.authorization().NumberOfAuthTags
= 1;
251 newAcl
.authorization().AuthTags
= &encryptTag
;
252 AclEntryInput
newInput(newAcl
);
253 AclEdit
edit(newInput
);
256 AutoCredentials
nullCred(alloc
);
257 ss
.changeKeyAcl(tester
.keyRef
, nullCred
, edit
);
258 error("ALLOWED ACL EDIT WITHOUT CREDENTIALS");
259 } catch (CssmCommonError
&err
) {
260 detail(err
, "Acl Edit rejected properly");
262 ss
.changeKeyAcl(tester
.keyRef
, cred
, edit
);
263 detail("ACL changed OK");
265 // read it back and check
269 ss
.getKeyAcl(tester
.keyRef
, NULL
/*tag*/, count
, acls
);
271 const AclEntryInfo
&acl1
= acls
[0];
272 const TypedList
&subject1
= acl1
.proto().subject();
273 assert(subject1
.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
274 assert(subject1
.length() == 1);
275 const AuthorizationGroup
&auths1
= acl1
.proto().authorization();
276 assert(auths1
.count() == 1);
277 assert(auths1
[0] == CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
);
278 const AclEntryInfo
&acl2
= acls
[1];
279 const TypedList
&subject2
= acl2
.proto().subject();
280 assert(subject2
.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
281 assert(subject2
.length() == 1);
282 const AuthorizationGroup
&auths2
= acl2
.proto().authorization();
283 assert(auths2
.count() == 1);
284 assert(auths2
[0] == CSSM_ACL_AUTHORIZATION_ENCRYPT
);
288 // ... and see how the new composite ACL behaves
289 AutoCredentials
cred2(alloc
);
290 cred2
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
291 new(alloc
) ListElement(the2ndAclPassword
));
292 tester
.testWrap(&nullCred
, "ACCEPTING NULL CREDENTIALS FOR WRAPPING");
293 tester
.testEncrypt(&nullCred
, "ACCEPTING NULL CREDENTIALS FOR ENCRYPTION");
294 tester
.testWrap(&cred
); // "very secret" allows wrapping
295 tester
.testEncrypt(&cred2
); // "most secret" allows encrypting
296 tester
.testWrap(&cred2
, "ACCEPTING ENCRYPT CRED FOR WRAPPING");
297 tester
.testEncrypt(&cred
, "ACCEPTING WRAP CRED FOR ENCRYPTING");
302 // Keychain ACL subjects
306 printf("* Keychain (interactive) ACL test\n");
307 CssmAllocator
&alloc
= CssmAllocator::standard();
308 ClientSession
ss(alloc
, alloc
);
310 // create key with initial ACL
311 AclEntryPrototype initialAcl
;
312 initialAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
,
313 new(alloc
) ListElement(alloc
, "Test Key"));
314 AclEntryInput
initialAclInput(initialAcl
);
315 AclTester
tester(ss
, &initialAclInput
);
317 // get the owner and verify
318 AclOwnerPrototype owner
;
319 ss
.getKeyOwner(tester
.keyRef
, owner
);
320 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
);
321 assert(owner
.subject().length() == 2);
323 // get the acl entry and verify
327 ss
.getKeyAcl(tester
.keyRef
, NULL
/*tag*/, count
, acls
);
329 const AclEntryInfo
&acl1
= acls
[0];
330 const TypedList
&subject1
= acl1
.proto().subject();
331 assert(subject1
.type() == CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
);
332 assert(subject1
.length() == 2);
333 assert(static_cast<string
>(subject1
[1]) == "Test Key");
336 // try to use the key and see...
337 tester
.testWrap(NULL
, "ACCEPTING NULL CREDENTIAL");
338 AutoCredentials
cred(alloc
);
339 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
340 new(alloc
) ListElement(StringData("Test Key")));
341 tester
.testWrap(&cred
, "ACCEPTING PASSWORD CREDENTIAL");
342 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
);
343 tester
.testWrap(&cred
);
344 // once again, for allow-this-pid feature testing
345 tester
.testWrap(&cred
);
350 // Code-signing ACL subjects
354 printf("* Code Signing ACL test\n");
355 CssmAllocator
&alloc
= CssmAllocator::standard();
356 ClientSession
ss(alloc
, alloc
);
360 OSXCode
*main
= OSXCode::main();
361 Signature
*mySignature
= signer
.sign(*main
);
362 detail("Code signature for testclient obtained");
364 // make a variant signature that isn't right
365 Signature
*badSignature
;
368 assert(mySignature
->length() <= sizeof(buffer
));
369 memcpy(buffer
, mySignature
->data(), mySignature
->length());
370 memcpy(buffer
, "xyz!", 4); // 1 in 2^32 this is right...
371 badSignature
= signer
.restore(mySignature
->type(), buffer
, mySignature
->length());
374 // create key with good code signature ACL
375 AclEntryPrototype initialAcl
;
376 initialAcl
.subject() = TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE
,
377 new(alloc
) ListElement(mySignature
->type()),
378 new(alloc
) ListElement(alloc
.alloc(*mySignature
)));
379 AclEntryInput
initialAclInput(initialAcl
);
380 AclTester
tester(ss
, &initialAclInput
);
382 // get the owner and verify
383 AclOwnerPrototype owner
;
384 ss
.getKeyOwner(tester
.keyRef
, owner
);
385 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE
);
386 assert(owner
.subject().length() == 3);
388 // we are us, so the SecurityServer should accept us
389 tester
.testWrap(&nullCred
);
391 // now try this again with a *bad* signature...
392 AclEntryPrototype badAcl
;
393 badAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE
,
394 new(alloc
) ListElement(badSignature
->type()),
395 new(alloc
) ListElement(alloc
.alloc(*badSignature
)));
396 AclEntryInput
badAclInput(badAcl
);
397 AclTester
badTester(ss
, &badAclInput
);
398 badTester
.testWrap(&nullCred
, "BAD CODE SIGNATURE ACCEPTED");
400 // make sure the optional comment field makes it back out intact
401 // (reusing original initialAcl structures)
402 StringData
comment("Walla Walla Washington!\nAbra cadabra.\n\n");
403 initialAcl
.subject() += new(alloc
) ListElement(alloc
, comment
);
404 AclEntryInput
initialAclInputWithComment(initialAcl
);
405 AclTester
commentTester(ss
, &initialAclInputWithComment
);
406 ss
.getKeyOwner(commentTester
.keyRef
, owner
);
407 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE
);
408 assert(owner
.subject().length() == 4);
409 assert(owner
.subject()[3] == comment
);
410 detail("Verified comment field intact");