/* * Copyright (c) 2000-2001,2004 Apple Computer, Inc. All Rights Reserved. * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved. * * 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" #include using namespace CodeSigning; // // ACL get/set tests // void acls() { printf("* Basic ACL tests\n"); CssmAllocator &alloc = CssmAllocator::standard(); ClientSession ss(alloc, alloc); // create key with initial ACL StringData initialAclPassphrase("very secret"); AclEntryPrototype initialAcl; initialAcl.TypedSubject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD, new(alloc) ListElement(initialAclPassphrase)); AclEntryInput initialAclInput(initialAcl); AclTester tester(ss, &initialAclInput); // get the owner and verify AclOwnerPrototype owner; ss.getKeyOwner(tester.keyRef, owner); assert(owner.subject().type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD); assert(owner.subject().length() == 1); // get the acl entry and verify { uint32 count; AclEntryInfo *acls; ss.getKeyAcl(tester.keyRef, NULL/*tag*/, count, acls); assert(count == 1); const AclEntryInfo &acl1 = acls[0]; const TypedList &subject1 = acl1.proto().subject(); assert(subject1.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD); assert(subject1.length() == 1); } // try to use the key and see... tester.testWrap(&nullCred, "ACCEPTING NULL CREDENTIAL"); AutoCredentials cred(alloc); cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(StringData("wrongo"))); tester.testWrap(&cred, "ACCEPTING WRONG PASSWORD CREDENTIAL"); cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(StringData("very secret"))); tester.testWrap(&cred); // now *replace* the ACL entry with a new one... { detail("Changing ACL"); uint32 count; AclEntryInfo *infos; ss.getKeyAcl(tester.keyRef, NULL, count, infos); assert(count == 1); // one entry AclEntryPrototype newAcl; TypedList subject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_THRESHOLD, new(alloc) ListElement(2), new(alloc) ListElement(3)); subject += new(alloc) ListElement(TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD, new(alloc) ListElement(alloc, "check me!"))); subject += new(alloc) ListElement(TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD, new(alloc) ListElement(alloc, "once again!"))); subject += new(alloc) ListElement(TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD, new(alloc) ListElement(alloc, "hug me!"))); newAcl.TypedSubject = subject; AclEntryInput input(newAcl); AclEdit edit(infos[0].handle(), input); try { AutoCredentials nullCred(alloc); ss.changeKeyAcl(tester.keyRef, nullCred, edit); error("ALLOWED ACL EDIT WITHOUT CREDENTIALS"); } catch (CssmCommonError &err) { detail(err, "Acl Edit rejected properly"); } ss.changeKeyAcl(tester.keyRef, cred, edit); detail("ACL changed OK"); } // ... and see how the new one reacts tester.testWrap(&nullCred, "ACCEPTING NULL CREDENTIALS NOW"); tester.testWrap(&cred, "ACCEPTING OLD CREDENTIALS FOR NEW ACL"); { AutoCredentials cred(alloc); cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(alloc, "check me!")); tester.testWrap(&cred, "ACCEPTING LEAF SAMPLE WITHOUT THRESHOLD FRAMEWORK"); } // Threshold subjects { detail("Testing threshold ACLs"); AutoCredentials cred(alloc); TypedList &threshold = cred += TypedList(alloc, CSSM_SAMPLE_TYPE_THRESHOLD, new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(alloc, "wrongo!"))) ); tester.testWrap(&cred, "ACCEPTING ALL WRONG SAMPLES IN THRESHOLD"); threshold += new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(alloc, "hug me!"))); tester.testWrap(&cred, "ACCEPTING TOO FEW THRESHOLD SAMPLES"); threshold += new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(alloc, "check me!"))); tester.testWrap(&cred); // stuff the ballot box threshold += new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(alloc, "and this!"))); threshold += new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(alloc, "and that!"))); threshold += new(alloc) ListElement(TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(alloc, "and more!"))); #ifdef STRICT_THRESHOLD_SUBJECTS tester.testWrap(&cred, "ACCEPTING OVER-STUFFED THRESHOLD"); #else tester.testWrap(&cred); #endif //STRICT_THRESHOLD_SUBJECTS } // comment ACLs and tags { detail("Adding Comment entry"); AclEntryPrototype newAcl; TypedList subject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_COMMENT, new(alloc) ListElement(TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_THRESHOLD, new(alloc) ListElement(alloc, "Robby Ray!"))), new(alloc) ListElement(666)); newAcl.TypedSubject = subject; strcpy(newAcl.EntryTag, "vamos"); AclEntryInput input(newAcl); AclEdit edit(input); ss.changeKeyAcl(tester.keyRef, cred, edit); detail("Entry added"); uint32 count; AclEntryInfo *infos; ss.getKeyAcl(tester.keyRef, "vamos", count, infos); assert(count == 1); // one entry (with this tag) const AclEntryInfo &acl = infos[0]; const TypedList &read = acl.proto().subject(); assert(read.type() == CSSM_ACL_SUBJECT_TYPE_COMMENT); assert(read.length() == 3); assert(read[2] == 666); CssmList &sublist = read[1]; assert(sublist[0] == CSSM_ACL_SUBJECT_TYPE_THRESHOLD); assert(string(sublist[1]) == "Robby Ray!"); detail("Comment entry retrieved okay"); } } // // ACL authorization tests // void authAcls() { printf("* ACL authorizations test\n"); CssmAllocator &alloc = CssmAllocator::standard(); ClientSession ss(alloc, alloc); // create key with initial ACL CSSM_ACL_AUTHORIZATION_TAG wrapTag = CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR; CSSM_ACL_AUTHORIZATION_TAG encryptTag = CSSM_ACL_AUTHORIZATION_ENCRYPT; StringData initialAclPassphrase("very secret"); StringData the2ndAclPassword("most secret"); AclEntryPrototype initialAcl; initialAcl.TypedSubject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD, new(alloc) ListElement(initialAclPassphrase)); initialAcl.authorization().NumberOfAuthTags = 1; initialAcl.authorization().AuthTags = &wrapTag; AclEntryInput initialAclInput(initialAcl); AclTester tester(ss, &initialAclInput); // get the owner and verify AclOwnerPrototype owner; ss.getKeyOwner(tester.keyRef, owner); assert(owner.subject().type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD); assert(owner.subject().length() == 1); // get the acl entry and verify { uint32 count; AclEntryInfo *acls; ss.getKeyAcl(tester.keyRef, NULL/*tag*/, count, acls); assert(count == 1); const AclEntryInfo &acl1 = acls[0]; const TypedList &subject1 = acl1.proto().subject(); assert(subject1.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD); assert(subject1.length() == 1); const AuthorizationGroup &auths = acl1.proto().authorization(); assert(auths.count() == 1); assert(auths[0] == CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR); } // try to use the key and see... tester.testWrap(&nullCred, "ACCEPTING NULL CREDENTIAL"); AutoCredentials cred(alloc); cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(StringData("wrongo"))); tester.testWrap(&cred, "ACCEPTING WRONG PASSWORD CREDENTIAL"); cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(initialAclPassphrase)); tester.testWrap(&cred); tester.testEncrypt(&nullCred, "ACCEPTING NULL CREDENTIAL FOR UNAUTHORIZED OPERATION"); tester.testEncrypt(&cred, "ACCEPTING GOOD CREDENTIAL FOR UNAUTHORIZED OPERATION"); // now *add* a new ACL entry for encryption { detail("Adding new ACL entry"); AclEntryPrototype newAcl; newAcl.TypedSubject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PASSWORD, new(alloc) ListElement(the2ndAclPassword)); newAcl.authorization().NumberOfAuthTags = 1; newAcl.authorization().AuthTags = &encryptTag; AclEntryInput newInput(newAcl); AclEdit edit(newInput); try { AutoCredentials nullCred(alloc); ss.changeKeyAcl(tester.keyRef, nullCred, edit); error("ALLOWED ACL EDIT WITHOUT CREDENTIALS"); } catch (CssmCommonError &err) { detail(err, "Acl Edit rejected properly"); } ss.changeKeyAcl(tester.keyRef, cred, edit); detail("ACL changed OK"); // read it back and check { uint32 count; AclEntryInfo *acls; ss.getKeyAcl(tester.keyRef, NULL/*tag*/, count, acls); assert(count == 2); const AclEntryInfo &acl1 = acls[0]; const TypedList &subject1 = acl1.proto().subject(); assert(subject1.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD); assert(subject1.length() == 1); const AuthorizationGroup &auths1 = acl1.proto().authorization(); assert(auths1.count() == 1); assert(auths1[0] == CSSM_ACL_AUTHORIZATION_EXPORT_CLEAR); const AclEntryInfo &acl2 = acls[1]; const TypedList &subject2 = acl2.proto().subject(); assert(subject2.type() == CSSM_ACL_SUBJECT_TYPE_PASSWORD); assert(subject2.length() == 1); const AuthorizationGroup &auths2 = acl2.proto().authorization(); assert(auths2.count() == 1); assert(auths2[0] == CSSM_ACL_AUTHORIZATION_ENCRYPT); } } // ... and see how the new composite ACL behaves AutoCredentials cred2(alloc); cred2 += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(the2ndAclPassword)); tester.testWrap(&nullCred, "ACCEPTING NULL CREDENTIALS FOR WRAPPING"); tester.testEncrypt(&nullCred, "ACCEPTING NULL CREDENTIALS FOR ENCRYPTION"); tester.testWrap(&cred); // "very secret" allows wrapping tester.testEncrypt(&cred2); // "most secret" allows encrypting tester.testWrap(&cred2, "ACCEPTING ENCRYPT CRED FOR WRAPPING"); tester.testEncrypt(&cred, "ACCEPTING WRAP CRED FOR ENCRYPTING"); } // // Keychain ACL subjects // void keychainAcls() { printf("* Keychain (interactive) ACL test\n"); CssmAllocator &alloc = CssmAllocator::standard(); ClientSession ss(alloc, alloc); // create key with initial ACL AclEntryPrototype initialAcl; initialAcl.TypedSubject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT, new(alloc) ListElement(alloc, "Test Key")); AclEntryInput initialAclInput(initialAcl); AclTester tester(ss, &initialAclInput); // get the owner and verify AclOwnerPrototype owner; ss.getKeyOwner(tester.keyRef, owner); assert(owner.subject().type() == CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT); assert(owner.subject().length() == 2); // get the acl entry and verify { uint32 count; AclEntryInfo *acls; ss.getKeyAcl(tester.keyRef, NULL/*tag*/, count, acls); assert(count == 1); const AclEntryInfo &acl1 = acls[0]; const TypedList &subject1 = acl1.proto().subject(); assert(subject1.type() == CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT); assert(subject1.length() == 2); assert(static_cast(subject1[1]) == "Test Key"); } // try to use the key and see... tester.testWrap(NULL, "ACCEPTING NULL CREDENTIAL"); AutoCredentials cred(alloc); cred += TypedList(alloc, CSSM_SAMPLE_TYPE_PASSWORD, new(alloc) ListElement(StringData("Test Key"))); tester.testWrap(&cred, "ACCEPTING PASSWORD CREDENTIAL"); cred += TypedList(alloc, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT); tester.testWrap(&cred); // once again, for allow-this-pid feature testing tester.testWrap(&cred); } // // Code-signing ACL subjects // void codeSigning() { printf("* Code Signing ACL test\n"); CssmAllocator &alloc = CssmAllocator::standard(); ClientSession ss(alloc, alloc); // sign ourselves OSXSigner signer; OSXCode *main = OSXCode::main(); Signature *mySignature = signer.sign(*main); detail("Code signature for testclient obtained"); // make a variant signature that isn't right Signature *badSignature; { char buffer[512]; assert(mySignature->length() <= sizeof(buffer)); memcpy(buffer, mySignature->data(), mySignature->length()); memcpy(buffer, "xyz!", 4); // 1 in 2^32 this is right... badSignature = signer.restore(mySignature->type(), buffer, mySignature->length()); } // create key with good code signature ACL AclEntryPrototype initialAcl; initialAcl.subject() = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE, new(alloc) ListElement(mySignature->type()), new(alloc) ListElement(alloc.alloc(*mySignature))); AclEntryInput initialAclInput(initialAcl); AclTester tester(ss, &initialAclInput); // get the owner and verify AclOwnerPrototype owner; ss.getKeyOwner(tester.keyRef, owner); assert(owner.subject().type() == CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE); assert(owner.subject().length() == 3); // we are us, so the SecurityServer should accept us tester.testWrap(&nullCred); // now try this again with a *bad* signature... AclEntryPrototype badAcl; badAcl.TypedSubject = TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE, new(alloc) ListElement(badSignature->type()), new(alloc) ListElement(alloc.alloc(*badSignature))); AclEntryInput badAclInput(badAcl); AclTester badTester(ss, &badAclInput); badTester.testWrap(&nullCred, "BAD CODE SIGNATURE ACCEPTED"); // make sure the optional comment field makes it back out intact // (reusing original initialAcl structures) StringData comment("Walla Walla Washington!\nAbra cadabra.\n\n"); initialAcl.subject() += new(alloc) ListElement(alloc, comment); AclEntryInput initialAclInputWithComment(initialAcl); AclTester commentTester(ss, &initialAclInputWithComment); ss.getKeyOwner(commentTester.keyRef, owner); assert(owner.subject().type() == CSSM_ACL_SUBJECT_TYPE_CODE_SIGNATURE); assert(owner.subject().length() == 4); assert(owner.subject()[3] == comment); detail("Verified comment field intact"); }