]> git.saurik.com Git - apple/security.git/blobdiff - sec/Security/Tool/keychain_find.c
Security-55471.tar.gz
[apple/security.git] / sec / Security / Tool / keychain_find.c
diff --git a/sec/Security/Tool/keychain_find.c b/sec/Security/Tool/keychain_find.c
new file mode 100644 (file)
index 0000000..2b248cc
--- /dev/null
@@ -0,0 +1,572 @@
+//
+//
+//
+//
+
+
+
+#include <CoreFoundation/CoreFoundation.h>
+
+#include <Security/SecItem.h>
+
+#include <SecurityTool/tool_errors.h>
+#include <SecurityTool/readline.h>
+
+#include <utilities/SecCFWrappers.h>
+
+#include "SecurityCommands.h"
+
+//
+// Craptastic hacks.
+
+typedef uint32_t SecProtocolType;
+typedef uint32_t SecAuthenticationType;
+
+/* Parse a string of the form attr=value,attr=value,attr=value */
+static void
+keychain_query_parse_string(CFMutableDictionaryRef q, CFStringRef s) {
+    bool inkey = true;
+    bool escaped = false;
+    CFStringRef key = NULL;
+    CFMutableStringRef str = CFStringCreateMutable(0, 0);
+    CFRange rng = { .location = 0, .length = CFStringGetLength(s) };
+    CFCharacterSetRef cs_key = CFCharacterSetCreateWithCharactersInString(0, CFSTR("=\\"));
+    CFCharacterSetRef cs_value = CFCharacterSetCreateWithCharactersInString(0, CFSTR(",\\"));
+    while (rng.length) {
+        CFRange r;
+        CFStringRef sub;
+        bool complete = false;
+        if (escaped) {
+            r.location = rng.location;
+            r.length = 1;
+            sub = CFStringCreateWithSubstring(0, s, r);
+            escaped = false;
+        } else if (CFStringFindCharacterFromSet(s, inkey ? cs_key : cs_value, rng, 0, &r)) {
+            if (CFStringGetCharacterAtIndex(s, r.location) == '\\') {
+                escaped = true;
+            } else {
+                complete = true;
+            }
+            CFIndex next = r.location + 1;
+            r.length = r.location - rng.location;
+            r.location = rng.location;
+            sub = CFStringCreateWithSubstring(0, s, r);
+            rng.length -= next - rng.location;
+            rng.location = next;
+        } else {
+            sub = CFStringCreateWithSubstring(0, s, rng);
+            rng.location += rng.length;
+            rng.length = 0;
+            complete = true;
+        }
+        CFStringAppend(str, sub);
+        CFRelease(sub);
+        if (complete) {
+            CFStringRef value = CFStringCreateCopy(0, str);
+            CFStringReplaceAll(str, CFSTR(""));
+            if (inkey) {
+                key = value;
+            } else {
+                CFDictionarySetValue(q, key, value);
+                CFReleaseNull(value);
+                CFReleaseNull(key);
+            }
+            inkey = !inkey;
+        }
+    }
+    if (key) {
+        /* Dangeling key value is true?. */
+        CFDictionarySetValue(q, key, kCFBooleanTrue);
+        CFRelease(key);
+    }
+    
+    CFRelease(str);
+    CFReleaseSafe(cs_key);
+    CFReleaseSafe(cs_value);
+}
+
+static void
+keychain_query_parse_cstring(CFMutableDictionaryRef q, const char *query) {
+    CFStringRef s;
+    s = CFStringCreateWithCStringNoCopy(0, query, kCFStringEncodingUTF8, kCFAllocatorNull);
+    keychain_query_parse_string(q, s);
+    CFRelease(s);
+}
+
+static CFMutableDictionaryRef
+keychain_create_query_from_string(const char *query) {
+    CFMutableDictionaryRef q;
+
+    q = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    keychain_query_parse_cstring(q, query);
+    return q;
+}
+
+static void add_key(const void *key, const void *value, void *context) {
+    CFArrayAppendValue(context, key);
+}
+
+static void display_item(const void *v_item, void *context) {
+    CFDictionaryRef item = (CFDictionaryRef)v_item;
+    CFIndex dict_count, key_ix, key_count;
+    CFMutableArrayRef keys = NULL;
+    CFIndex maxWidth = 10; /* Maybe precompute this or grab from context? */
+
+    dict_count = CFDictionaryGetCount(item);
+    keys = CFArrayCreateMutable(kCFAllocatorDefault, dict_count,
+        &kCFTypeArrayCallBacks);
+    CFDictionaryApplyFunction(item, add_key, keys);
+    key_count = CFArrayGetCount(keys);
+    CFArraySortValues(keys, CFRangeMake(0, key_count),
+        (CFComparatorFunction)CFStringCompare, 0);
+
+    for (key_ix = 0; key_ix < key_count; ++key_ix) {
+        CFStringRef key = (CFStringRef)CFArrayGetValueAtIndex(keys, key_ix);
+        CFTypeRef value = CFDictionaryGetValue(item, key);
+        CFMutableStringRef line = CFStringCreateMutable(NULL, 0);
+
+        CFStringAppend(line, key);
+        CFIndex jx;
+        for (jx = CFStringGetLength(key);
+            jx < maxWidth; ++jx) {
+            CFStringAppend(line, CFSTR(" "));
+        }
+        CFStringAppend(line, CFSTR(" : "));
+        if (CFStringGetTypeID() == CFGetTypeID(value)) {
+            CFStringAppend(line, (CFStringRef)value);
+        } else if (CFNumberGetTypeID() == CFGetTypeID(value)) {
+            CFNumberRef v_n = (CFNumberRef)value;
+            CFStringAppendFormat(line, NULL, CFSTR("%@"), v_n);
+        } else if (CFDateGetTypeID() == CFGetTypeID(value)) {
+            CFDateRef v_d = (CFDateRef)value;
+            CFStringAppendFormat(line, NULL, CFSTR("%@"), v_d);
+        } else if (CFDataGetTypeID() == CFGetTypeID(value)) {
+            CFDataRef v_d = (CFDataRef)value;
+            CFStringRef v_s = CFStringCreateFromExternalRepresentation(
+                kCFAllocatorDefault, v_d, kCFStringEncodingUTF8);
+            if (v_s) {
+                CFStringAppend(line, CFSTR("/"));
+                CFStringAppend(line, v_s);
+                CFStringAppend(line, CFSTR("/ "));
+                CFRelease(v_s);
+            }
+            const uint8_t *bytes = CFDataGetBytePtr(v_d);
+            CFIndex len = CFDataGetLength(v_d);
+            for (jx = 0; jx < len; ++jx) {
+                CFStringAppendFormat(line, NULL, CFSTR("%.02X"), bytes[jx]);
+            }
+        } else {
+            CFStringAppendFormat(line, NULL, CFSTR("%@"), value);
+        }
+
+        CFStringWriteToFileWithNewline(line, stdout);
+
+               CFRelease(line);
+    }
+    CFRelease(keys);
+    
+    CFStringWriteToFileWithNewline(CFSTR("===="), stdout);
+
+    //CFShow(item);
+}
+
+
+static void display_results(CFTypeRef results) {
+    if (CFGetTypeID(results) == CFArrayGetTypeID()) {
+        CFArrayRef r_a = (CFArrayRef)results;
+        CFArrayApplyFunction(r_a, CFRangeMake(0, CFArrayGetCount(r_a)),
+            display_item, NULL);
+    } else if (CFGetTypeID(results) == CFArrayGetTypeID()) {
+        display_item(results, NULL);
+    } else {
+        fprintf(stderr, "SecItemCopyMatching returned unexpected results:");
+        CFShow(results);
+    }
+}
+
+static OSStatus do_find_or_delete(CFDictionaryRef query, bool do_delete) {
+    OSStatus result;
+    if (do_delete) {
+        result = SecItemDelete(query);
+        if (result) {
+            sec_perror("SecItemDelete", result);
+        }
+    } else {
+        CFTypeRef results = NULL;
+        result = SecItemCopyMatching(query, &results);
+        if (result) {
+            sec_perror("SecItemCopyMatching", result);
+        } else {
+            display_results(results);
+        }
+        CFReleaseSafe(results);
+    }
+    return result;
+}
+
+static int
+do_keychain_find_or_delete_internet_password(Boolean do_delete,
+       const char *serverName, const char *securityDomain,
+       const char *accountName, const char *path, UInt16 port,
+       SecProtocolType protocol, SecAuthenticationType authenticationType,
+       Boolean get_password)
+ {
+       OSStatus result;
+    CFDictionaryRef query = NULL;
+    const void *keys[11], *values[11];
+    CFIndex ix = 0;
+
+    keys[ix] = kSecClass;
+    values[ix++] = kSecClassInternetPassword;
+       if (serverName) {
+               keys[ix] = kSecAttrServer;
+               values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serverName,
+                       kCFStringEncodingUTF8, kCFAllocatorNull);
+       }
+       if (securityDomain) {
+               keys[ix] = kSecAttrSecurityDomain;
+               values[ix++] = CFStringCreateWithCStringNoCopy(NULL, securityDomain,
+                       kCFStringEncodingUTF8, kCFAllocatorNull);
+       }
+       if (accountName) {
+               keys[ix] = kSecAttrAccount;
+               values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
+                       kCFStringEncodingUTF8, kCFAllocatorNull);
+       }
+       if (path) {
+               keys[ix] = kSecAttrPath;
+               values[ix++] = CFStringCreateWithCStringNoCopy(NULL, path,
+                       kCFStringEncodingUTF8, kCFAllocatorNull);
+       }
+       if (port != 0) {
+               keys[ix] = kSecAttrPort;
+               values[ix++] = CFNumberCreate(NULL, kCFNumberSInt16Type, &port);
+       }
+       if (protocol != 0) {
+               /* Protocol is a 4 char code, perhaps we should use a string rep
+                  instead. */
+               keys[ix] = kSecAttrProtocol;
+               values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type, &protocol);
+       }
+       if (authenticationType != 0) {
+               keys[ix] = kSecAttrAuthenticationType;
+               values[ix++] = CFNumberCreate(NULL, kCFNumberSInt32Type,
+                       &authenticationType);
+       }
+    if (get_password) {
+        /* Only ask for the data if so required. */
+               keys[ix] = kSecReturnData;
+               values[ix++] = kCFBooleanTrue;
+    }
+    keys[ix] = kSecReturnAttributes;
+    values[ix++] = kCFBooleanTrue;
+       if (!do_delete) {
+               /* If we aren't deleting ask for all items. */
+               keys[ix] = kSecMatchLimit;
+               values[ix++] = kSecMatchLimitAll;
+       }
+
+    query = CFDictionaryCreate(NULL, keys, values, ix,
+        &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+    result = do_find_or_delete(query, do_delete);
+    CFReleaseSafe(query);
+
+       return result;
+}
+
+static int
+parse_fourcharcode(const char *name, uint32_t *code)
+{
+       /* @@@ Check for errors. */
+       char *p = (char *)code;
+       strncpy(p, name, 4);
+       return 0;
+}
+
+static int
+keychain_find_or_delete_internet_password(Boolean do_delete, int argc, char * const *argv)
+{
+       char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
+    UInt16 port = 0;
+    SecProtocolType protocol = 0;
+    SecAuthenticationType authenticationType = 0;
+       int ch, result = 0;
+       Boolean get_password = FALSE;
+
+       while ((ch = getopt(argc, argv, "a:d:hgp:P:r:s:t:")) != -1)
+       {
+               switch  (ch)
+               {
+        case 'a':
+            accountName = optarg;
+            break;
+        case 'd':
+            securityDomain = optarg;
+                       break;
+               case 'g':
+            if (do_delete)
+                return 2;
+                       get_password = TRUE;
+                       break;
+        case 'p':
+            path = optarg;
+            break;
+        case 'P':
+            port = atoi(optarg);
+            break;
+        case 'r':
+                       result = parse_fourcharcode(optarg, &protocol);
+                       if (result)
+                               goto loser;
+                       break;
+               case 's':
+                       serverName = optarg;
+                       break;
+        case 't':
+                       result = parse_fourcharcode(optarg, &authenticationType);
+                       if (result)
+                               goto loser;
+                       break;
+        case '?':
+               default:
+                       return 2; /* @@@ Return 2 triggers usage message. */
+               }
+       }
+  
+       argc -= optind;
+       argv += optind;
+
+       result = do_keychain_find_or_delete_internet_password(do_delete, serverName, securityDomain,
+               accountName, path, port, protocol,authenticationType, get_password);
+
+
+loser:
+
+       return result;
+}
+
+int
+keychain_find_internet_password(int argc, char * const *argv) {
+    return keychain_find_or_delete_internet_password(0, argc, argv);
+}
+
+int
+keychain_delete_internet_password(int argc, char * const *argv) {
+    return keychain_find_or_delete_internet_password(1, argc, argv);
+}
+
+static int
+do_keychain_find_or_delete_generic_password(Boolean do_delete,
+       const char *serviceName, const char *accountName,
+       Boolean get_password)
+ {
+       OSStatus result;
+    CFDictionaryRef query = NULL;
+    const void *keys[6], *values[6];
+    CFIndex ix = 0;
+
+    keys[ix] = kSecClass;
+    values[ix++] = kSecClassGenericPassword;
+       if (serviceName) {
+               keys[ix] = kSecAttrService;
+               values[ix++] = CFStringCreateWithCStringNoCopy(NULL, serviceName,
+                       kCFStringEncodingUTF8, kCFAllocatorNull);
+       }
+       if (accountName) {
+               keys[ix] = kSecAttrAccount;
+               values[ix++] = CFStringCreateWithCStringNoCopy(NULL, accountName,
+                       kCFStringEncodingUTF8, kCFAllocatorNull);
+       }
+    if (get_password) {
+        /* Only ask for the data if so required. */
+               keys[ix] = kSecReturnData;
+               values[ix++] = kCFBooleanTrue;
+    }
+    keys[ix] = kSecReturnAttributes;
+    values[ix++] = kCFBooleanTrue;
+       if (!do_delete) {
+               /* If we aren't deleting ask for all items. */
+               keys[ix] = kSecMatchLimit;
+               values[ix++] = kSecMatchLimitAll;
+       }
+
+    query = CFDictionaryCreate(NULL, keys, values, ix,
+        &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+
+    result = do_find_or_delete(query, do_delete);
+
+       CFReleaseSafe(query);
+
+       return result;
+}
+
+int keychain_item(int argc, char * const *argv) {
+       int ch, result = 0;
+    CFMutableDictionaryRef query, update = NULL;
+       bool get_password = false;
+    bool do_delete = false;
+    bool do_add = false;
+    bool verbose = false;
+    int limit = 0;
+
+    query = CFDictionaryCreateMutable(0, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+       while ((ch = getopt(argc, argv, "ad:Df:gq:u:vl:")) != -1)
+       {
+               switch  (ch)
+               {
+            case 'a':
+                do_add = true;
+                break;
+            case 'D':
+                do_delete = true;
+                break;
+            case 'd':
+            {
+                CFStringRef data = CFStringCreateWithCString(0, optarg, kCFStringEncodingUTF8);
+                if (data) {
+                    CFDictionarySetValue(update ? update : query, kSecValueData, data);
+                    CFRelease(data);
+                } else {
+                    result = 1;
+                    goto out;
+                }
+                break;
+            }
+            case 'f':
+            {
+                CFDataRef data = copyFileContents(optarg);
+                CFDictionarySetValue(update ? update : query, kSecValueData, data);
+                CFRelease(data);
+                break;
+            }
+            case 'g':
+                get_password = true;
+                break;
+            case 'q':
+                keychain_query_parse_cstring(query, optarg);
+                break;
+            case 'u':
+                if (!update)
+                    update = keychain_create_query_from_string(optarg);
+                else
+                    keychain_query_parse_cstring(update, optarg);
+                break;
+            case 'v':
+                verbose = true;
+                break;
+            case 'l':
+                limit = atoi(optarg);
+                break;
+            case '?':
+            default:
+                /* Return 2 triggers usage message. */
+                result = 2;
+                goto out;
+               }
+       }
+
+    if (((do_add || do_delete) && (get_password || update)) || !query) {
+        result = 2;
+        goto out;
+    }
+
+       argc -= optind;
+       argv += optind;
+
+    int ix;
+    for (ix = 0; ix < argc; ++ix) {
+        keychain_query_parse_cstring(query, argv[ix]);
+    }
+
+    if (!update && !do_add && !do_delete) {
+        CFDictionarySetValue(query, kSecReturnAttributes, kCFBooleanTrue);
+        if(limit) {
+            CFNumberRef cfLimit = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &limit);
+            CFDictionarySetValue(query, kSecMatchLimit, cfLimit);
+            CFReleaseSafe(cfLimit);
+        } else {
+            CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
+        }
+        if (get_password)
+            CFDictionarySetValue(query, kSecReturnData, kCFBooleanTrue);
+    }
+
+    if (verbose)
+        CFShow(query);
+
+    OSStatus error;
+    if (do_add) {
+        error = SecItemAdd(query, NULL);
+        if (error) {
+            sec_perror("SecItemAdd", error);
+            result = 1;
+        }
+    } else if (update) {
+        error = SecItemUpdate(query, update);
+        if (error) {
+            sec_perror("SecItemUpdate", error);
+            result = 1;
+        }
+    } else if (do_delete) {
+        error = SecItemDelete(query);
+        if (error) {
+            sec_perror("SecItemDelete", error);
+            result = 1;
+        }
+    } else {
+        do_find_or_delete(query, do_delete);
+    }
+
+out:
+    CFReleaseSafe(query);
+    CFReleaseSafe(update);
+       return result;
+}
+
+static int
+keychain_find_or_delete_generic_password(Boolean do_delete,
+       int argc, char * const *argv)
+{
+       char *serviceName = NULL, *accountName = NULL;
+       int ch, result = 0;
+       Boolean get_password = FALSE;
+
+       while ((ch = getopt(argc, argv, "a:s:g")) != -1)
+       {
+               switch  (ch)
+               {
+        case 'a':
+            accountName = optarg;
+            break;
+        case 'g':
+            if (do_delete)
+                return 2;
+                       get_password = TRUE;
+                       break;
+               case 's':
+                       serviceName = optarg;
+                       break;
+        case '?':
+               default:
+                       return 2; /* @@@ Return 2 triggers usage message. */
+               }
+       }
+  
+       argc -= optind;
+       argv += optind;
+
+       result = do_keychain_find_or_delete_generic_password(do_delete,
+               serviceName, accountName, get_password);
+
+       return result;
+}
+
+int
+keychain_find_generic_password(int argc, char * const *argv) {
+    return keychain_find_or_delete_generic_password(0, argc, argv);
+}
+
+int
+keychain_delete_generic_password(int argc, char * const *argv) {
+    return keychain_find_or_delete_generic_password(1, argc, argv);
+}