2 * Copyright (c) 2000-2001,2003-2004 Apple Computer, Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // testacls - ACL-related test cases.
28 #include "testclient.h"
29 #include "testutils.h"
30 #include <Security/osxsigner.h>
32 using namespace CodeSigning
;
40 printf("* Basic ACL tests\n");
41 CssmAllocator
&alloc
= CssmAllocator::standard();
42 ClientSession
ss(alloc
, alloc
);
44 // create key with initial ACL
45 StringData
initialAclPassphrase("very secret");
46 AclEntryPrototype initialAcl
;
47 initialAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
48 new(alloc
) ListElement(initialAclPassphrase
));
49 AclEntryInput
initialAclInput(initialAcl
);
50 AclTester
tester(ss
, &initialAclInput
);
52 // get the owner and verify
53 AclOwnerPrototype owner
;
54 ss
.getKeyOwner(tester
.keyRef
, owner
);
55 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
56 assert(owner
.subject().length() == 1);
58 // get the acl entry and verify
62 ss
.getKeyAcl(tester
.keyRef
, NULL
/*tag*/, count
, acls
);
64 const AclEntryInfo
&acl1
= acls
[0];
65 const TypedList
&subject1
= acl1
.proto().subject();
66 assert(subject1
.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
67 assert(subject1
.length() == 1);
70 // try to use the key and see...
71 tester
.testWrap(&nullCred
, "ACCEPTING NULL CREDENTIAL");
72 AutoCredentials
cred(alloc
);
73 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
74 new(alloc
) ListElement(StringData("wrongo")));
75 tester
.testWrap(&cred
, "ACCEPTING WRONG PASSWORD CREDENTIAL");
76 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
77 new(alloc
) ListElement(StringData("very secret")));
78 tester
.testWrap(&cred
);
80 // now *replace* the ACL entry with a new one...
82 detail("Changing ACL");
85 ss
.getKeyAcl(tester
.keyRef
, NULL
, count
, infos
);
86 assert(count
== 1); // one entry
88 AclEntryPrototype newAcl
;
89 TypedList subject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
90 new(alloc
) ListElement(2), new(alloc
) ListElement(3));
91 subject
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
92 new(alloc
) ListElement(alloc
, "check me!")));
93 subject
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
94 new(alloc
) ListElement(alloc
, "once again!")));
95 subject
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
96 new(alloc
) ListElement(alloc
, "hug me!")));
97 newAcl
.TypedSubject
= subject
;
98 AclEntryInput
input(newAcl
);
99 AclEdit
edit(infos
[0].handle(), input
);
102 AutoCredentials
nullCred(alloc
);
103 ss
.changeKeyAcl(tester
.keyRef
, nullCred
, edit
);
104 error("ALLOWED ACL EDIT WITHOUT CREDENTIALS");
105 } catch (CssmCommonError
&err
) {
106 detail(err
, "Acl Edit rejected properly");
108 ss
.changeKeyAcl(tester
.keyRef
, cred
, edit
);
109 detail("ACL changed OK");
112 // ... and see how the new one reacts
113 tester
.testWrap(&nullCred
, "ACCEPTING NULL CREDENTIALS NOW");
114 tester
.testWrap(&cred
, "ACCEPTING OLD CREDENTIALS FOR NEW ACL");
116 AutoCredentials
cred(alloc
);
117 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
118 new(alloc
) ListElement(alloc
, "check me!"));
119 tester
.testWrap(&cred
, "ACCEPTING LEAF SAMPLE WITHOUT THRESHOLD FRAMEWORK");
122 // Threshold subjects
124 detail("Testing threshold ACLs");
125 AutoCredentials
cred(alloc
);
126 TypedList
&threshold
= cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_THRESHOLD
,
127 new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
128 new(alloc
) ListElement(alloc
, "wrongo!")))
130 tester
.testWrap(&cred
, "ACCEPTING ALL WRONG SAMPLES IN THRESHOLD");
131 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
132 new(alloc
) ListElement(alloc
, "hug me!")));
133 tester
.testWrap(&cred
, "ACCEPTING TOO FEW THRESHOLD SAMPLES");
134 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
135 new(alloc
) ListElement(alloc
, "check me!")));
136 tester
.testWrap(&cred
);
137 // stuff the ballot box
138 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
139 new(alloc
) ListElement(alloc
, "and this!")));
140 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
141 new(alloc
) ListElement(alloc
, "and that!")));
142 threshold
+= new(alloc
) ListElement(TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
143 new(alloc
) ListElement(alloc
, "and more!")));
144 #ifdef STRICT_THRESHOLD_SUBJECTS
145 tester
.testWrap(&cred
, "ACCEPTING OVER-STUFFED THRESHOLD");
147 tester
.testWrap(&cred
);
148 #endif //STRICT_THRESHOLD_SUBJECTS
151 // comment ACLs and tags
153 detail("Adding Comment entry");
155 AclEntryPrototype newAcl
;
156 TypedList subject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_COMMENT
,
157 new(alloc
) ListElement(TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_THRESHOLD
,
158 new(alloc
) ListElement(alloc
, "Robby Ray!"))),
159 new(alloc
) ListElement(666));
160 newAcl
.TypedSubject
= subject
;
161 strcpy(newAcl
.EntryTag
, "vamos");
162 AclEntryInput
input(newAcl
);
164 ss
.changeKeyAcl(tester
.keyRef
, cred
, edit
);
165 detail("Entry added");
169 ss
.getKeyAcl(tester
.keyRef
, "vamos", count
, infos
);
170 assert(count
== 1); // one entry (with this tag)
171 const AclEntryInfo
&acl
= infos
[0];
172 const TypedList
&read
= acl
.proto().subject();
173 assert(read
.type() == CSSM_ACL_SUBJECT_TYPE_COMMENT
);
174 assert(read
.length() == 3);
175 assert(read
[2] == 666);
176 CssmList
&sublist
= read
[1];
177 assert(sublist
[0] == CSSM_ACL_SUBJECT_TYPE_THRESHOLD
);
178 assert(string(sublist
[1]) == "Robby Ray!");
180 detail("Comment entry retrieved okay");
186 // ACL authorization tests
190 printf("* ACL authorizations test\n");
191 CssmAllocator
&alloc
= CssmAllocator::standard();
192 ClientSession
ss(alloc
, alloc
);
194 // create key with initial ACL
195 CSSM_ACL_AUTHORIZATION_TAG wrapTag
= CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
;
196 CSSM_ACL_AUTHORIZATION_TAG encryptTag
= CSSM_ACL_AUTHORIZATION_ENCRYPT
;
197 StringData
initialAclPassphrase("very secret");
198 StringData
the2ndAclPassword("most secret");
199 AclEntryPrototype initialAcl
;
200 initialAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
201 new(alloc
) ListElement(initialAclPassphrase
));
202 initialAcl
.authorization().NumberOfAuthTags
= 1;
203 initialAcl
.authorization().AuthTags
= &wrapTag
;
204 AclEntryInput
initialAclInput(initialAcl
);
205 AclTester
tester(ss
, &initialAclInput
);
207 // get the owner and verify
208 AclOwnerPrototype owner
;
209 ss
.getKeyOwner(tester
.keyRef
, owner
);
210 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
211 assert(owner
.subject().length() == 1);
213 // get the acl entry and verify
217 ss
.getKeyAcl(tester
.keyRef
, NULL
/*tag*/, count
, acls
);
219 const AclEntryInfo
&acl1
= acls
[0];
220 const TypedList
&subject1
= acl1
.proto().subject();
221 assert(subject1
.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
222 assert(subject1
.length() == 1);
223 const AuthorizationGroup
&auths
= acl1
.proto().authorization();
224 assert(auths
.count() == 1);
225 assert(auths
[0] == CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
);
228 // try to use the key and see...
229 tester
.testWrap(&nullCred
, "ACCEPTING NULL CREDENTIAL");
230 AutoCredentials
cred(alloc
);
231 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
232 new(alloc
) ListElement(StringData("wrongo")));
233 tester
.testWrap(&cred
, "ACCEPTING WRONG PASSWORD CREDENTIAL");
234 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
235 new(alloc
) ListElement(initialAclPassphrase
));
236 tester
.testWrap(&cred
);
238 tester
.testEncrypt(&nullCred
, "ACCEPTING NULL CREDENTIAL FOR UNAUTHORIZED OPERATION");
239 tester
.testEncrypt(&cred
, "ACCEPTING GOOD CREDENTIAL FOR UNAUTHORIZED OPERATION");
241 // now *add* a new ACL entry for encryption
243 detail("Adding new ACL entry");
245 AclEntryPrototype newAcl
;
246 newAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_PASSWORD
,
247 new(alloc
) ListElement(the2ndAclPassword
));
248 newAcl
.authorization().NumberOfAuthTags
= 1;
249 newAcl
.authorization().AuthTags
= &encryptTag
;
250 AclEntryInput
newInput(newAcl
);
251 AclEdit
edit(newInput
);
254 AutoCredentials
nullCred(alloc
);
255 ss
.changeKeyAcl(tester
.keyRef
, nullCred
, edit
);
256 error("ALLOWED ACL EDIT WITHOUT CREDENTIALS");
257 } catch (CssmCommonError
&err
) {
258 detail(err
, "Acl Edit rejected properly");
260 ss
.changeKeyAcl(tester
.keyRef
, cred
, edit
);
261 detail("ACL changed OK");
263 // read it back and check
267 ss
.getKeyAcl(tester
.keyRef
, NULL
/*tag*/, count
, acls
);
269 const AclEntryInfo
&acl1
= acls
[0];
270 const TypedList
&subject1
= acl1
.proto().subject();
271 assert(subject1
.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
272 assert(subject1
.length() == 1);
273 const AuthorizationGroup
&auths1
= acl1
.proto().authorization();
274 assert(auths1
.count() == 1);
275 assert(auths1
[0] == CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR
);
276 const AclEntryInfo
&acl2
= acls
[1];
277 const TypedList
&subject2
= acl2
.proto().subject();
278 assert(subject2
.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD
);
279 assert(subject2
.length() == 1);
280 const AuthorizationGroup
&auths2
= acl2
.proto().authorization();
281 assert(auths2
.count() == 1);
282 assert(auths2
[0] == CSSM_ACL_AUTHORIZATION_ENCRYPT
);
286 // ... and see how the new composite ACL behaves
287 AutoCredentials
cred2(alloc
);
288 cred2
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
289 new(alloc
) ListElement(the2ndAclPassword
));
290 tester
.testWrap(&nullCred
, "ACCEPTING NULL CREDENTIALS FOR WRAPPING");
291 tester
.testEncrypt(&nullCred
, "ACCEPTING NULL CREDENTIALS FOR ENCRYPTION");
292 tester
.testWrap(&cred
); // "very secret" allows wrapping
293 tester
.testEncrypt(&cred2
); // "most secret" allows encrypting
294 tester
.testWrap(&cred2
, "ACCEPTING ENCRYPT CRED FOR WRAPPING");
295 tester
.testEncrypt(&cred
, "ACCEPTING WRAP CRED FOR ENCRYPTING");
300 // Keychain ACL subjects
304 printf("* Keychain (interactive) ACL test\n");
305 CssmAllocator
&alloc
= CssmAllocator::standard();
306 ClientSession
ss(alloc
, alloc
);
308 // create key with initial ACL
309 AclEntryPrototype initialAcl
;
310 initialAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
,
311 new(alloc
) ListElement(alloc
, "Test Key"));
312 AclEntryInput
initialAclInput(initialAcl
);
313 AclTester
tester(ss
, &initialAclInput
);
315 // get the owner and verify
316 AclOwnerPrototype owner
;
317 ss
.getKeyOwner(tester
.keyRef
, owner
);
318 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
);
319 assert(owner
.subject().length() == 2);
321 // get the acl entry and verify
325 ss
.getKeyAcl(tester
.keyRef
, NULL
/*tag*/, count
, acls
);
327 const AclEntryInfo
&acl1
= acls
[0];
328 const TypedList
&subject1
= acl1
.proto().subject();
329 assert(subject1
.type() == CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT
);
330 assert(subject1
.length() == 2);
331 assert(static_cast<string
>(subject1
[1]) == "Test Key");
334 // try to use the key and see...
335 tester
.testWrap(NULL
, "ACCEPTING NULL CREDENTIAL");
336 AutoCredentials
cred(alloc
);
337 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_PASSWORD
,
338 new(alloc
) ListElement(StringData("Test Key")));
339 tester
.testWrap(&cred
, "ACCEPTING PASSWORD CREDENTIAL");
340 cred
+= TypedList(alloc
, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT
);
341 tester
.testWrap(&cred
);
342 // once again, for allow-this-pid feature testing
343 tester
.testWrap(&cred
);
348 // Code-signing ACL subjects
352 printf("* Code Signing ACL test\n");
353 CssmAllocator
&alloc
= CssmAllocator::standard();
354 ClientSession
ss(alloc
, alloc
);
358 OSXCode
*main
= OSXCode::main();
359 Signature
*mySignature
= signer
.sign(*main
);
360 detail("Code signature for testclient obtained");
362 // make a variant signature that isn't right
363 Signature
*badSignature
;
366 assert(mySignature
->length() <= sizeof(buffer
));
367 memcpy(buffer
, mySignature
->data(), mySignature
->length());
368 memcpy(buffer
, "xyz!", 4); // 1 in 2^32 this is right...
369 badSignature
= signer
.restore(mySignature
->type(), buffer
, mySignature
->length());
372 // create key with good code signature ACL
373 AclEntryPrototype initialAcl
;
374 initialAcl
.subject() = TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE
,
375 new(alloc
) ListElement(mySignature
->type()),
376 new(alloc
) ListElement(alloc
.alloc(*mySignature
)));
377 AclEntryInput
initialAclInput(initialAcl
);
378 AclTester
tester(ss
, &initialAclInput
);
380 // get the owner and verify
381 AclOwnerPrototype owner
;
382 ss
.getKeyOwner(tester
.keyRef
, owner
);
383 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE
);
384 assert(owner
.subject().length() == 3);
386 // we are us, so the SecurityServer should accept us
387 tester
.testWrap(&nullCred
);
389 // now try this again with a *bad* signature...
390 AclEntryPrototype badAcl
;
391 badAcl
.TypedSubject
= TypedList(alloc
, CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE
,
392 new(alloc
) ListElement(badSignature
->type()),
393 new(alloc
) ListElement(alloc
.alloc(*badSignature
)));
394 AclEntryInput
badAclInput(badAcl
);
395 AclTester
badTester(ss
, &badAclInput
);
396 badTester
.testWrap(&nullCred
, "BAD CODE SIGNATURE ACCEPTED");
398 // make sure the optional comment field makes it back out intact
399 // (reusing original initialAcl structures)
400 StringData
comment("Walla Walla Washington!\nAbra cadabra.\n\n");
401 initialAcl
.subject() += new(alloc
) ListElement(alloc
, comment
);
402 AclEntryInput
initialAclInputWithComment(initialAcl
);
403 AclTester
commentTester(ss
, &initialAclInputWithComment
);
404 ss
.getKeyOwner(commentTester
.keyRef
, owner
);
405 assert(owner
.subject().type() == CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE
);
406 assert(owner
.subject().length() == 4);
407 assert(owner
.subject()[3] == comment
);
408 detail("Verified comment field intact");