--- /dev/null
+/*
+ * extendAttrTest.cpp
+ */
+
+#include <stdlib.h>
+#include <strings.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <Security/SecKeychainItemExtendedAttributes.h>
+#include <Security/Security.h>
+#include <security_cdsa_utils/cuFileIo.h>
+#include <utilLib/common.h>
+
+#define DEFAULT_KC_NAME "extendAttr.keychain"
+
+static void usage(char **argv)
+{
+ printf("usage: %s [options]\n", argv[0]);
+ printf("Options:\n");
+ printf(" -k keychain -- default is %s\n", DEFAULT_KC_NAME);
+ printf(" -n -- don't delete attributes or keychain\n");
+ printf(" -q -- quiet\n");
+ exit(1);
+}
+
+/* RSA keys, both in OpenSSL format */
+#define PUB_KEY "rsakey_pub.der"
+#define PRIV_KEY "rsakey_priv.der"
+#define CERT_FILE "amazon_v3.100.cer"
+#define PWD_SERVICE "some service"
+#define PWD_ACCOUNT "some account"
+#define PWD_PWD "some password"
+
+/* set up unique extended attributes for each tested item */
+typedef struct {
+ CFStringRef attr1Name;
+ const char *attr1Value;
+ CFStringRef attr2Name;
+ const char *attr2Value;
+} ItemAttrs;
+
+static const ItemAttrs pubKeyAttrs = {
+ CFSTR("one pub key Attribute"),
+ "some pub key value",
+ CFSTR("another pub key Attribute"),
+ "another pub key value"
+};
+
+static const ItemAttrs privKeyAttrs = {
+ CFSTR("one priv key Attribute"),
+ "some priv key value",
+ CFSTR("another priv key Attribute"),
+ "another priv key value"
+};
+
+static const ItemAttrs certAttrs = {
+ CFSTR("one cert Attribute"),
+ "some cert value",
+ CFSTR("another cert Attribute"),
+ "another cert value"
+};
+
+static const ItemAttrs pwdAttrs = {
+ CFSTR("one pwd Attribute"),
+ "some pwd value",
+ CFSTR("another pwd Attribute"),
+ "another pwd value"
+};
+
+#define CFRELEASE(cf) if(cf) { CFRelease(cf); }
+
+/* import file as key into specified keychain */
+static int doImportKey(
+ const char *fileName,
+ SecExternalFormat format,
+ SecExternalItemType itemType,
+ SecKeychainRef kcRef,
+ SecKeyRef *keyRef) // RETURNED
+{
+ unsigned char *item = NULL;
+ unsigned itemLen = 0;
+
+ if(readFile(fileName, &item, &itemLen)) {
+ printf("***Error reading %s. \n", fileName);
+ }
+ CFDataRef cfd = CFDataCreate(NULL, (const UInt8 *)item, itemLen);
+ free(item);
+ SecKeyImportExportParameters params;
+ memset(¶ms, 0, sizeof(params));
+ params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+ params.keyUsage = CSSM_KEYUSE_ANY;
+ params.keyAttributes = CSSM_KEYATTR_PERMANENT;
+ if(itemType == kSecItemTypePrivateKey) {
+ params.keyAttributes |= CSSM_KEYATTR_SENSITIVE;
+ }
+ CFArrayRef outArray = NULL;
+ OSStatus ortn;
+ ortn = SecKeychainItemImport(cfd, NULL, &format, &itemType, 0, ¶ms, kcRef, &outArray);
+ if(ortn) {
+ cssmPerror("SecKeychainItemImport", ortn);
+ }
+ CFRelease(cfd);
+ if(ortn) {
+ return -1;
+ }
+ if((outArray == NULL) || (CFArrayGetCount(outArray) == 0)) {
+ printf("SecKeychainItemImport succeeded, but no returned items\n");
+ return -1;
+ }
+ *keyRef = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
+ if(CFGetTypeID(*keyRef) != SecKeyGetTypeID()) {
+ printf("***Unknown type returned after import\n");
+ return -1;
+ }
+ CFRetain(*keyRef);
+ CFRelease(outArray);
+ return 0;
+}
+
+/* import file as cert into specified keychain */
+static int doImportCert(
+ const char *fileName,
+ SecKeychainRef kcRef,
+ SecCertificateRef *certRef) // RETURNED
+{
+ unsigned char *item = NULL;
+ unsigned itemLen = 0;
+
+ if(readFile(fileName, &item, &itemLen)) {
+ printf("***Error reading %s. \n", fileName);
+ return -1;
+ }
+ CSSM_DATA certData = {itemLen, (uint8 *)item};
+ OSStatus ortn = SecCertificateCreateFromData(&certData,
+ CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, certRef);
+ if(ortn) {
+ cssmPerror("SecCertificateCreateFromData", ortn);
+ return -1;
+ }
+ ortn = SecCertificateAddToKeychain(*certRef, kcRef);
+ if(ortn) {
+ cssmPerror("SecCertificateAddToKeychain", ortn);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Verify specified attr does not exist
+ * set it
+ * make sure we get it back
+ */
+int testOneAttr(
+ SecKeychainItemRef itemRef,
+ CFStringRef attrName,
+ CFDataRef attrVal,
+ bool quiet)
+{
+ OSStatus ortn;
+ CFDataRef fetchedVal = NULL;
+ int ourRtn = 0;
+
+ if(!quiet) {
+ printf(" ...verifying attribute doesn't exist\n");
+ }
+ ortn = SecKeychainItemCopyExtendedAttribute(itemRef, attrName, &fetchedVal);
+ if(ortn != errSecNoSuchAttr) {
+ printf("***First SecKeychainItemCopyExtendedAttribute returned %d, expected %d\n",
+ (int)ortn, (int)errSecNoSuchAttr);
+ ourRtn = -1;
+ goto errOut;
+ }
+ if(!quiet) {
+ printf(" ...setting attribute\n");
+ }
+ ortn = SecKeychainItemSetExtendedAttribute(itemRef, attrName, attrVal);
+ if(ortn) {
+ cssmPerror("SecKeychainItemSetExtendedAttribute", ortn);
+ ourRtn = -1;
+ goto errOut;
+ }
+ if(!quiet) {
+ printf(" ...verify attribute\n");
+ }
+ ortn = SecKeychainItemCopyExtendedAttribute(itemRef, attrName, &fetchedVal);
+ if(ortn) {
+ cssmPerror("SecKeychainItemCopyExtendedAttribute", ortn);
+ ourRtn = -1;
+ goto errOut;
+ }
+ if(!CFEqual(fetchedVal, attrVal)) {
+ printf("***Mismatch in set and fetched attribute\n");
+ ourRtn = -1;
+ }
+errOut:
+ CFRELEASE(fetchedVal);
+ return ourRtn;
+}
+
+/*
+ * Set two distinct extended attributes;
+ * Ensure that each comes back via SecKeychainItemCopyExtendedAttribute();
+ * Ensure that both come back via SecKeychainItemCopyAllExtendedAttributes();
+ */
+int doTest(SecKeychainItemRef itemRef,
+ const ItemAttrs &itemAttrs,
+ bool quiet)
+{
+ CFDataRef attrVal1 = CFDataCreate(NULL,
+ (const UInt8 *)itemAttrs.attr1Value, strlen(itemAttrs.attr1Value));
+ if(testOneAttr(itemRef, itemAttrs.attr1Name, attrVal1, quiet)) {
+ return -1;
+ }
+ CFDataRef attrVal2 = CFDataCreate(NULL,
+ (const UInt8 *)itemAttrs.attr2Value, strlen(itemAttrs.attr2Value));
+ if(testOneAttr(itemRef, itemAttrs.attr2Name, attrVal2, quiet)) {
+ return -1;
+ }
+
+ if(!quiet) {
+ printf(" ...verify both attributes via CopyAllExtendedAttributes()\n");
+ }
+ /* make sure they both come back in SecKeychainItemCopyAllExtendedAttributes */
+ CFArrayRef attrNames = NULL;
+ CFArrayRef attrValues = NULL;
+ OSStatus ortn = SecKeychainItemCopyAllExtendedAttributes(itemRef, &attrNames, &attrValues);
+ if(ortn) {
+ cssmPerror("SecKeychainItemCopyAllExtendedAttributes", ortn);
+ return -1;
+ }
+ CFIndex numNames = CFArrayGetCount(attrNames);
+ CFIndex numValues = CFArrayGetCount(attrValues);
+ if((numNames != 2) || (numValues != 2)) {
+ printf("***Bad array count after SecKeychainItemCopyAllExtendedAttributes\n");
+ printf(" numNames %ld numValues %ld; expected 2 for both\n",
+ (long)numNames, (long)numValues);
+ return -1;
+ }
+ bool found1 = false;
+ bool found2 = false;
+ for(CFIndex dex=0; dex<numNames; dex++) {
+ CFStringRef attrName = (CFStringRef)CFArrayGetValueAtIndex(attrNames, dex);
+ CFDataRef valToCompare = NULL;
+ if(CFEqual(attrName, itemAttrs.attr1Name)) {
+ found1 = true;
+ valToCompare = attrVal1;
+ }
+ else if(CFEqual(attrName, itemAttrs.attr2Name)) {
+ found2 = true;
+ valToCompare = attrVal2;
+ }
+ else {
+ printf("***Found unknown attribute name\n");
+ return -1;
+ }
+ CFDataRef foundVal = (CFDataRef)CFArrayGetValueAtIndex(attrValues, dex);
+ if(!CFEqual(foundVal, valToCompare)) {
+ printf("***Attribute Value miscompare\n");
+ return -1;
+ }
+ }
+ CFRelease(attrNames);
+ CFRelease(attrValues);
+ CFRelease(attrVal1);
+ CFRelease(attrVal2);
+
+ if(!found1 || !found2) {
+ printf("***wrote two attribute; found1 %s, found2 %s\n",
+ found1 ? "true" : "false", found2 ? "true" : "false");
+ return 1;
+ }
+
+ return 0;
+}
+
+/* delete two attrs, verify that none are left */
+static int doDeleteTest(
+ SecKeychainItemRef itemRef,
+ const ItemAttrs &itemAttrs,
+ bool quiet)
+{
+ if(!quiet) {
+ printf(" ...deleting both attributes, verifying none are left\n");
+ }
+
+ OSStatus ortn = SecKeychainItemSetExtendedAttribute(itemRef, itemAttrs.attr1Name, NULL);
+ if(ortn) {
+ cssmPerror("SecKeychainItemSetExtendedAttribute (NULL)", ortn);
+ return -1;
+ }
+ ortn = SecKeychainItemSetExtendedAttribute(itemRef, itemAttrs.attr2Name, NULL);
+ if(ortn) {
+ cssmPerror("SecKeychainItemSetExtendedAttribute (NULL)", ortn);
+ return -1;
+ }
+ CFArrayRef attrNames = NULL;
+ CFArrayRef attrValues = NULL;
+ ortn = SecKeychainItemCopyAllExtendedAttributes(itemRef, &attrNames, &attrValues);
+ if(ortn != errSecNoSuchAttr) {
+ printf("***Last SecKeychainItemCopyExtendedAttribute returned %d, expected %d\n",
+ (int)ortn, (int)errSecNoSuchAttr);
+ return -1;
+ }
+ return 0;
+}
+
+/*
+ * Verify that SecKeychainItemDelete() also deletes extended attributes.
+ *
+ * Assuming empty keychain:
+ * Import a cert;
+ * Set two extended attributes, make sure they're there;
+ * Delete the cert;
+ * Import the cert again;
+ * Verify that the new item has *no* extended attributes;
+ */
+static int doDeleteItemTest(
+ SecKeychainRef kcRef,
+ bool quiet)
+{
+ SecCertificateRef certRef = NULL;
+
+ if(doImportCert(CERT_FILE, kcRef, &certRef)) {
+ return 1;
+ }
+ if(!quiet) {
+ printf("...testing cert\n");
+ }
+ if(doTest((SecKeychainItemRef)certRef, certAttrs, quiet)) {
+ return -1;
+ }
+
+ /* doTest() verified that there are two extended attrs */
+ if(!quiet) {
+ printf("...deleting cert\n");
+ }
+ OSStatus ortn = SecKeychainItemDelete((SecKeychainItemRef)certRef);
+ if(ortn) {
+ cssmPerror("SecKeychainItemDelete", ortn);
+ return -1;
+ }
+ CFRelease(certRef);
+
+ if(!quiet) {
+ printf("...reimporting cert, verifying it has no extended attributes\n");
+ }
+ if(doImportCert(CERT_FILE, kcRef, &certRef)) {
+ return 1;
+ }
+ CFArrayRef attrNames = NULL;
+ ortn = SecKeychainItemCopyAllExtendedAttributes((SecKeychainItemRef)certRef, &attrNames,
+ NULL);
+ if(ortn != errSecNoSuchAttr) {
+ printf("***Deleted cert, re-imported it, and the new cert has extended attributes!\n");
+ return -1;
+ }
+ CFRelease(certRef);
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ const char *kcName = DEFAULT_KC_NAME;
+ extern char *optarg;
+ int arg;
+ bool quiet = false;
+ bool noDelete = false;
+
+ while ((arg = getopt(argc, argv, "k:qnh")) != -1) {
+ switch (arg) {
+ case 'k':
+ kcName = optarg;
+ break;
+ case 'n':
+ noDelete = true;
+ break;
+ case 'q':
+ quiet = true;
+ break;
+ case 'h':
+ usage(argv);
+ }
+ }
+ if(optind != argc) {
+ usage(argv);
+ }
+
+ testStartBanner("extendAttrTest", argc, argv);
+
+ SecKeychainRef kcRef = NULL;
+ OSStatus ortn;
+
+ if(!quiet) {
+ printf("Deleting possible existing keychain and creating %s...\n", kcName);
+
+ }
+ /* delete possible existing keychain, then create it */
+ if (SecKeychainOpen(kcName, &kcRef) == noErr)
+ {
+ SecKeychainDelete(kcRef);
+ CFRelease(kcRef);
+ }
+
+ kcRef = NULL;
+ ortn = SecKeychainCreate(kcName,
+ strlen(DEFAULT_KC_NAME), DEFAULT_KC_NAME,
+ false, NULL, &kcRef);
+ if(ortn) {
+ cssmPerror("SecKeychainCreate", ortn);
+ exit(1);
+ }
+
+ /* import keys */
+ SecKeyRef pubKey = NULL;
+ SecKeyRef privKey = NULL;
+ if(!quiet) {
+ printf("Importing %s to keychain...\n", PUB_KEY);
+ }
+ if(doImportKey(PUB_KEY, kSecFormatOpenSSL, kSecItemTypePublicKey, kcRef, &pubKey)) {
+ exit(1);
+ }
+ if(!quiet) {
+ printf("Importing %s to keychain...\n", PRIV_KEY);
+ }
+ if(doImportKey(PRIV_KEY, kSecFormatOpenSSL, kSecItemTypePrivateKey, kcRef, &privKey)) {
+ exit(1);
+ }
+
+ if(!quiet) {
+ printf("...testing public key\n");
+ }
+ if(doTest((SecKeychainItemRef)pubKey, pubKeyAttrs, quiet)) {
+ return -1;
+ }
+ if(!quiet) {
+ printf("...testing private key\n");
+ }
+ if(doTest((SecKeychainItemRef)privKey, privKeyAttrs, quiet)) {
+ return -1;
+ }
+
+ /*
+ * Those keys and their extended attrs are still in the keychain. Test a cert.
+ */
+ SecCertificateRef certRef = NULL;
+ if(doImportCert(CERT_FILE, kcRef, &certRef)) {
+ exit(1);
+ }
+ if(!quiet) {
+ printf("...testing cert\n");
+ }
+ if(doTest((SecKeychainItemRef)certRef, certAttrs, quiet)) {
+ return -1;
+ }
+
+ /* leaving everything in place, test a generic password. */
+ SecKeychainItemRef pwdRef = NULL;
+ ortn = SecKeychainAddGenericPassword(kcRef,
+ strlen(PWD_SERVICE), PWD_SERVICE,
+ strlen(PWD_ACCOUNT), PWD_ACCOUNT,
+ strlen(PWD_PWD), PWD_PWD,
+ &pwdRef);
+ if(ortn) {
+ cssmPerror("SecKeychainAddGenericPassword", ortn);
+ exit(1);
+ }
+ if(!quiet) {
+ printf("...testing generic password\n");
+ }
+ if(doTest(pwdRef, pwdAttrs, quiet)) {
+ return -1;
+ }
+
+ if(noDelete) {
+ goto done;
+ }
+
+ /* delete extended attrs; make sure they really get deleted */
+ if(!quiet) {
+ printf("...removing extended attributes from public key\n");
+ }
+ if(doDeleteTest((SecKeychainItemRef)pubKey, pubKeyAttrs, quiet)) {
+ exit(1);
+ }
+ if(!quiet) {
+ printf("...removing extended attributes from private key\n");
+ }
+ if(doDeleteTest((SecKeychainItemRef)privKey, privKeyAttrs, quiet)) {
+ exit(1);
+ }
+ if(!quiet) {
+ printf("...removing extended attributes from certificate\n");
+ }
+ if(doDeleteTest((SecKeychainItemRef)certRef, certAttrs, quiet)) {
+ exit(1);
+ }
+ if(!quiet) {
+ printf("...removing extended attributes from generic password\n");
+ }
+ if(doDeleteTest(pwdRef, pwdAttrs, quiet)) {
+ exit(1);
+ }
+
+ CFRelease(pubKey);
+ CFRelease(privKey);
+ CFRelease(pwdRef);
+
+ /* Verify that SecKeychainItemDelete() also deletes extended attributes */
+ ortn = SecKeychainItemDelete((SecKeychainItemRef)certRef);
+ if(ortn) {
+ cssmPerror("SecKeychainItemDelete", ortn);
+ exit(1);
+ }
+ CFRelease(certRef);
+ if(doDeleteItemTest(kcRef, quiet)) {
+ exit(1);
+ }
+
+ SecKeychainDelete(kcRef);
+ CFRelease(kcRef);
+done:
+ if(!quiet) {
+ printf("...Success\n");
+ }
+ return 0;
+}