/* * Copyright (c) 2003-2010 Apple 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@ * * keychain_find.c */ #include "keychain_find.h" #include "keychain_utilities.h" #include "readline.h" #include "security.h" #include #include #include #include #include #include #include #include #include #include // SecDigestGetData, SecKeychainSearchCreateForCertificateByEmail, SecCertificateFindByEmail #include Boolean gDeleteIt = 0; // find_first_generic_password // // Returns a SecKeychainItemRef for the first item // which matches the specified attributes. Caller is // responsible for releasing the item (with CFRelease). // SecKeychainItemRef find_first_generic_password(CFTypeRef keychainOrArray, FourCharCode itemCreator, FourCharCode itemType, const char *kind, const char *value, const char *comment, const char *label, const char *serviceName, const char *accountName) { OSStatus status = noErr; SecKeychainSearchRef searchRef = NULL; SecKeychainItemRef itemRef = NULL; SecKeychainAttribute attrs[8]; // maximum number of searchable attributes SecKeychainAttributeList attrList = { 0, attrs }; // the primary key for a generic password item (i.e. the combination of // attributes which determine whether the item is unique) consists of: // { kSecAccountItemAttr, kSecServiceItemAttr } // // if we have a primary key, we don't need to search on other attributes // (and we don't want to, if non-primary attributes are being updated) Boolean primaryKey = (accountName && serviceName); // build the attribute list for searching if ((UInt32)itemCreator != 0 && !primaryKey) { attrs[attrList.count].tag = kSecCreatorItemAttr; attrs[attrList.count].length = sizeof(FourCharCode); attrs[attrList.count].data = (FourCharCode *)&itemCreator; attrList.count++; } if ((UInt32)itemType != 0 && !primaryKey) { attrs[attrList.count].tag = kSecTypeItemAttr; attrs[attrList.count].length = sizeof(FourCharCode); attrs[attrList.count].data = (FourCharCode *)&itemType; attrList.count++; } if (kind != NULL && !primaryKey) { attrs[attrList.count].tag = kSecDescriptionItemAttr; attrs[attrList.count].length = strlen(kind); attrs[attrList.count].data = (void*)kind; attrList.count++; } if (value != NULL && !primaryKey) { attrs[attrList.count].tag = kSecGenericItemAttr; attrs[attrList.count].length = strlen(value); attrs[attrList.count].data = (void*)value; attrList.count++; } if (comment != NULL && !primaryKey) { attrs[attrList.count].tag = kSecCommentItemAttr; attrs[attrList.count].length = strlen(comment); attrs[attrList.count].data = (void*)comment; attrList.count++; } if (label != NULL && !primaryKey) { attrs[attrList.count].tag = kSecLabelItemAttr; attrs[attrList.count].length = strlen(label); attrs[attrList.count].data = (void*)label; attrList.count++; } if (serviceName != NULL) { attrs[attrList.count].tag = kSecServiceItemAttr; attrs[attrList.count].length = strlen(serviceName); attrs[attrList.count].data = (void*)serviceName; attrList.count++; } if (accountName != NULL) { attrs[attrList.count].tag = kSecAccountItemAttr; attrs[attrList.count].length = strlen(accountName); attrs[attrList.count].data = (void*)accountName; attrList.count++; } status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecGenericPasswordItemClass, &attrList, &searchRef); if (status) { sec_perror("SecKeychainSearchCreateFromAttributes", status); goto cleanup; } // we're only interested in the first match, if there is a match at all status = SecKeychainSearchCopyNext(searchRef, &itemRef); if (status) { itemRef = NULL; } cleanup: if (searchRef) CFRelease(searchRef); return itemRef; } // find_first_internet_password // // Returns a SecKeychainItemRef for the first item // which matches the specified attributes. Caller is // responsible for releasing the item (with CFRelease). // SecKeychainItemRef find_first_internet_password(CFTypeRef keychainOrArray, FourCharCode itemCreator, FourCharCode itemType, const char *kind, const char *comment, const char *label, const char *serverName, const char *securityDomain, const char *accountName, const char *path, UInt16 port, SecProtocolType protocol, SecAuthenticationType authenticationType) { OSStatus status = noErr; SecKeychainSearchRef searchRef = NULL; SecKeychainItemRef itemRef = NULL; SecKeychainAttribute attrs[12]; // maximum number of searchable attributes SecKeychainAttributeList attrList = { 0, attrs }; // the primary key for an internet password item (i.e. the combination of // attributes which determine whether the item is unique) consists of: // { kSecAccountItemAttr, kSecSecurityDomainItemAttr, kSecServerItemAttr, // kSecProtocolItemAttr, kSecAuthenticationTypeItemAttr, // kSecPortItemAttr, kSecPathItemAttr } // // if we have a primary key, we don't need to search on other attributes. // (and we don't want to, if non-primary attributes are being updated) Boolean primaryKey = (accountName && securityDomain && serverName && protocol && authenticationType && port && path); // build the attribute list for searching if ((UInt32)itemCreator != 0 && !primaryKey) { attrs[attrList.count].tag = kSecCreatorItemAttr; attrs[attrList.count].length = sizeof(FourCharCode); attrs[attrList.count].data = (FourCharCode *)&itemCreator; attrList.count++; } if ((UInt32)itemType != 0 && !primaryKey) { attrs[attrList.count].tag = kSecTypeItemAttr; attrs[attrList.count].length = sizeof(FourCharCode); attrs[attrList.count].data = (FourCharCode *)&itemType; attrList.count++; } if (kind != NULL && !primaryKey) { attrs[attrList.count].tag = kSecDescriptionItemAttr; attrs[attrList.count].length = strlen(kind); attrs[attrList.count].data = (void*)kind; attrList.count++; } if (comment != NULL && !primaryKey) { attrs[attrList.count].tag = kSecCommentItemAttr; attrs[attrList.count].length = strlen(comment); attrs[attrList.count].data = (void*)comment; attrList.count++; } if (label != NULL && !primaryKey) { attrs[attrList.count].tag = kSecLabelItemAttr; attrs[attrList.count].length = strlen(label); attrs[attrList.count].data = (void*)label; attrList.count++; } if (serverName != NULL) { attrs[attrList.count].tag = kSecServerItemAttr; attrs[attrList.count].length = strlen(serverName); attrs[attrList.count].data = (void*)serverName; attrList.count++; } if (securityDomain != NULL) { attrs[attrList.count].tag = kSecSecurityDomainItemAttr; attrs[attrList.count].length = strlen(securityDomain); attrs[attrList.count].data = (void *)securityDomain; attrList.count++; } if (accountName != NULL) { attrs[attrList.count].tag = kSecAccountItemAttr; attrs[attrList.count].length = strlen(accountName); attrs[attrList.count].data = (void *)accountName; attrList.count++; } if (path != NULL) { attrs[attrList.count].tag = kSecPathItemAttr; attrs[attrList.count].length = strlen(path); attrs[attrList.count].data = (void *)path; attrList.count++; } if (port != 0) { attrs[attrList.count].tag = kSecPortItemAttr; attrs[attrList.count].length = sizeof(UInt16); attrs[attrList.count].data = (UInt16 *)&port; attrList.count++; } if ((UInt32)protocol != 0) { attrs[attrList.count].tag = kSecProtocolItemAttr; attrs[attrList.count].length = sizeof(SecProtocolType); attrs[attrList.count].data = (SecProtocolType *)&protocol; attrList.count++; } if ((UInt32)authenticationType != 0) { attrs[attrList.count].tag = kSecAuthenticationTypeItemAttr; attrs[attrList.count].length = sizeof(SecAuthenticationType); attrs[attrList.count].data = (SecAuthenticationType *)&authenticationType; attrList.count++; } status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecInternetPasswordItemClass, &attrList, &searchRef); if (status) { sec_perror("SecKeychainSearchCreateFromAttributes", status); goto cleanup; } // we're only interested in the first match, if there is a match at all status = SecKeychainSearchCopyNext(searchRef, &itemRef); if (status) { itemRef = NULL; } cleanup: if (searchRef) CFRelease(searchRef); return itemRef; } // find_unique_certificate // // Returns a SecKeychainItemRef for the certificate // in the specified keychain (or keychain list) // which is a unique match for either the specified name // or SHA-1 hash. If more than one match exists, the // certificate is not unique and none are returned. Caller is // responsible for releasing the item (with CFRelease). // SecKeychainItemRef find_unique_certificate(CFTypeRef keychainOrArray, const char *name, const char *hash) { OSStatus status = noErr; SecKeychainSearchRef searchRef = NULL; SecKeychainItemRef uniqueItemRef = NULL; status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef); if (status) { return uniqueItemRef; } // check input hash string and convert to data CSSM_DATA hashData = { 0, NULL }; if (hash) { CSSM_SIZE len = strlen(hash)/2; hashData.Length = len; hashData.Data = (uint8 *)malloc(hashData.Length); fromHex(hash, &hashData); } // filter candidates against the hash (or the name, if no hash provided) CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL; Boolean exactMatch = FALSE; CSSM_DATA certData = { 0, NULL }; SecKeychainItemRef candidate = NULL; while (SecKeychainSearchCopyNext(searchRef, &candidate) == noErr) { SecCertificateRef cert = (SecCertificateRef)candidate; if (SecCertificateGetData(cert, &certData) != noErr) { safe_CFRelease(&candidate); continue; } if (hash) { uint8 candidate_sha1_hash[20]; CSSM_DATA digest; digest.Length = sizeof(candidate_sha1_hash); digest.Data = candidate_sha1_hash; if ((SecDigestGetData(CSSM_ALGID_SHA1, &digest, &certData) == CSSM_OK) && (hashData.Length == digest.Length) && (!memcmp(hashData.Data, digest.Data, digest.Length))) { exactMatch = TRUE; uniqueItemRef = candidate; // currently retained break; // we're done - can't get more exact than this } } else { // copy certificate name CFStringRef nameRef = NULL; if ((SecCertificateCopyCommonName(cert, &nameRef) != noErr) || nameRef == NULL) { safe_CFRelease(&candidate); continue; // no name, so no match is possible } CFIndex nameLen = CFStringGetLength(nameRef); CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8); char *nameBuf = (char *)malloc(bufLen); if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8)) nameBuf[0]=0; CFRange find = { kCFNotFound, 0 }; if (nameRef && matchRef) find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral); Boolean isExact = (find.location == 0 && find.length == nameLen); if (find.location == kCFNotFound) { free(nameBuf); safe_CFRelease(&nameRef); safe_CFRelease(&candidate); continue; // no match } if (uniqueItemRef) { // got two matches if (exactMatch && !isExact) { // prior is better; ignore this one free(nameBuf); safe_CFRelease(&nameRef); safe_CFRelease(&candidate); continue; } if (exactMatch == isExact) { // same class of match if (CFEqual(uniqueItemRef, candidate)) { // same certificate free(nameBuf); safe_CFRelease(&nameRef); safe_CFRelease(&candidate); continue; } // ambiguity - must fail sec_error("\"%s\" is ambiguous, matches more than one certificate", name); free(nameBuf); safe_CFRelease(&nameRef); safe_CFRelease(&candidate); safe_CFRelease(&uniqueItemRef); break; } safe_CFRelease(&uniqueItemRef); // about to replace with this one } uniqueItemRef = candidate; // currently retained exactMatch = isExact; free(nameBuf); safe_CFRelease(&nameRef); } } safe_CFRelease(&searchRef); safe_CFRelease(&matchRef); if (hashData.Data) { free(hashData.Data); } return uniqueItemRef; } static OSStatus do_password_item_printing( SecKeychainItemRef itemRef, Boolean get_password, Boolean password_stdout) { OSStatus result = noErr; void *passwordData = NULL; UInt32 passwordLength = 0; if(get_password) { result = SecKeychainItemCopyContent(itemRef, NULL, NULL, &passwordLength, &passwordData); if(result != noErr) return result; } if(!password_stdout) { print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE); if(get_password) { fputs("password: ", stderr); print_buffer(stderr, passwordLength, passwordData); fputc('\n', stderr); } } else { char *password = (char *) passwordData; int doHex = 0; for(int i=0; i