]> git.saurik.com Git - apple/security.git/blobdiff - securityd/tests/testacls.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / securityd / tests / testacls.cpp
diff --git a/securityd/tests/testacls.cpp b/securityd/tests/testacls.cpp
new file mode 100644 (file)
index 0000000..3d2986e
--- /dev/null
@@ -0,0 +1,409 @@
+/*
+ * Copyright (c) 2000-2001,2003-2004 Apple Computer, Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * 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 <Security/osxsigner.h>
+
+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<string>(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");
+}