+#define SetKeyToString(dict, key, arg) \
+{ \
+ CFStringRef str = CFStringCreateWithCStringNoCopy(NULL, arg, kCFStringEncodingUTF8, kCFAllocatorNull); \
+ CFDictionarySetValue(dict, key, str); \
+ CFReleaseNull(str); \
+}
+
+int
+keychain_find_key(int argc, char * const *argv) {
+ /*
+ * " -a Match \"application label\" string\n"
+ * " -c Match \"creator\" (four-character code)\n"
+ * " -d Match keys that can decrypt\n"
+ * " -D Match \"description\" string\n"
+ * " -e Match keys that can encrypt\n"
+ * " -j Match \"comment\" string\n"
+ * " -l Match \"label\" string\n"
+ * " -r Match keys that can derive\n"
+ * " -s Match keys that can sign\n"
+ * " -t Type of key to find: one of \"symmetric\", \"public\", or \"private\"\n"
+ * " -u Match keys that can unwrap\n"
+ * " -v Match keys that can verify\n"
+ * " -w Match keys that can wrap\n"
+ */
+
+ CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+ CFDictionarySetValue(query, kSecClass, kSecClassKey);
+ CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
+
+ CFTypeRef results = NULL;
+
+ int ch, result = 0;
+ while ((ch = getopt(argc, argv, "a:c:dD:ej:l:rst:uvw")) != -1)
+ {
+ switch (ch)
+ {
+ case 'a':
+ SetKeyToString(query, kSecAttrApplicationLabel, optarg);
+ break;
+ case 'c':
+ SetKeyToString(query, kSecAttrCreator, optarg);
+ break;
+ case 'd':
+ CFDictionarySetValue(query, kSecAttrCanDecrypt, kCFBooleanTrue);
+ break;
+ case 'D':
+ SetKeyToString(query, kSecAttrDescription, optarg);
+ break;
+ case 'e':
+ CFDictionarySetValue(query, kSecAttrCanEncrypt, kCFBooleanTrue);
+ break;
+ case 'j':
+ SetKeyToString(query, kSecAttrComment, optarg);
+ break;
+ case 'l':
+ SetKeyToString(query, kSecAttrLabel, optarg);
+ break;
+ case 'r':
+ CFDictionarySetValue(query, kSecAttrCanDerive, kCFBooleanTrue);
+ break;
+ case 's':
+ CFDictionarySetValue(query, kSecAttrCanSign, kCFBooleanTrue);
+ break;
+ case 't':
+ if(strcmp(optarg, "symmetric") == 0) {
+ CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassSymmetric);
+ } else if(strcmp(optarg, "public") == 0) {
+ CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPublic);
+ } else if(strcmp(optarg, "private") == 0) {
+ CFDictionarySetValue(query, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
+ } else {
+ result = 2;
+ goto cleanup;
+ }
+ break;
+ case 'u':
+ CFDictionarySetValue(query, kSecAttrCanUnwrap, kCFBooleanTrue);
+ break;
+ case 'v':
+ CFDictionarySetValue(query, kSecAttrCanVerify, kCFBooleanTrue);
+ break;
+ case 'w':
+ CFDictionarySetValue(query, kSecAttrCanWrap, kCFBooleanTrue);
+ break;
+ case '?':
+ default:
+ result = 2;
+ goto cleanup;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ CFTypeRef keychainOrArray = keychain_create_array(argc, argv);
+
+ if(keychainOrArray && CFGetTypeID(keychainOrArray) == CFArrayGetTypeID()) {
+ CFDictionarySetValue(query, kSecMatchSearchList, keychainOrArray);
+ } else if(keychainOrArray) {
+ // if it's not an array (but is something), it's a keychain. Put it in an array.
+ CFMutableArrayRef searchList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
+ CFArrayAppendValue((CFMutableArrayRef)searchList, keychainOrArray);
+ CFDictionarySetValue(query, kSecMatchSearchList, searchList);
+ CFRelease(searchList);
+ }
+ CFReleaseNull(keychainOrArray);
+
+ OSStatus status = SecItemCopyMatching(query, &results);
+ if(status) {
+ sec_perror("SecItemCopyMatching", status);
+ result = 1;
+ goto cleanup;
+ }
+
+ if (CFGetTypeID(results) == CFArrayGetTypeID()) {
+ for(int i = 0; i < CFArrayGetCount(results); i++) {
+ SecKeychainItemRef item = (SecKeychainItemRef) CFArrayGetValueAtIndex(results, i);
+
+ print_keychain_item_attributes(stdout, item, FALSE, FALSE, FALSE, FALSE);
+ }
+ }
+
+cleanup:
+ safe_CFRelease(&results);
+ safe_CFRelease(&query);
+ return result;
+}
+