#include "keychain_find.h"
#include "keychain_utilities.h"
#include "readline_cssm.h"
#include "security_tool.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <libkern/OSByteOrder.h>
#include <Security/SecACL.h>
#include <Security/SecItem.h>
#include <Security/SecItemPriv.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecKeychainItemPriv.h>
#include <Security/SecKeychainSearch.h>
#include <Security/SecCertificate.h>
#include <CommonCrypto/CommonDigest.h>
#include <CoreFoundation/CFString.h>
#include <ctype.h>
#include <utilities/SecCFRelease.h>
#include <Security/SecCertificatePriv.h>
Boolean gDeleteIt = 0;
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]; SecKeychainAttributeList attrList = { 0, attrs };
Boolean primaryKey = (accountName && serviceName);
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 = (UInt32) strlen(kind);
attrs[attrList.count].data = (void*)kind;
attrList.count++;
}
if (value != NULL && !primaryKey) {
attrs[attrList.count].tag = kSecGenericItemAttr;
attrs[attrList.count].length = (UInt32) strlen(value);
attrs[attrList.count].data = (void*)value;
attrList.count++;
}
if (comment != NULL && !primaryKey) {
attrs[attrList.count].tag = kSecCommentItemAttr;
attrs[attrList.count].length = (UInt32) strlen(comment);
attrs[attrList.count].data = (void*)comment;
attrList.count++;
}
if (label != NULL && !primaryKey) {
attrs[attrList.count].tag = kSecLabelItemAttr;
attrs[attrList.count].length = (UInt32) strlen(label);
attrs[attrList.count].data = (void*)label;
attrList.count++;
}
if (serviceName != NULL) {
attrs[attrList.count].tag = kSecServiceItemAttr;
attrs[attrList.count].length = (UInt32) strlen(serviceName);
attrs[attrList.count].data = (void*)serviceName;
attrList.count++;
}
if (accountName != NULL) {
attrs[attrList.count].tag = kSecAccountItemAttr;
attrs[attrList.count].length = (UInt32) strlen(accountName);
attrs[attrList.count].data = (void*)accountName;
attrList.count++;
}
status = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecGenericPasswordItemClass, &attrList, &searchRef);
if (status) {
sec_perror("SecKeychainSearchCreateFromAttributes", status);
goto cleanup;
}
status = SecKeychainSearchCopyNext(searchRef, &itemRef);
if (status) {
itemRef = NULL;
}
cleanup:
if (searchRef)
CFRelease(searchRef);
return itemRef;
}
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]; SecKeychainAttributeList attrList = { 0, attrs };
Boolean primaryKey = (accountName && securityDomain && serverName &&
protocol && authenticationType && port && path);
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 = (UInt32) strlen(kind);
attrs[attrList.count].data = (void*)kind;
attrList.count++;
}
if (comment != NULL && !primaryKey) {
attrs[attrList.count].tag = kSecCommentItemAttr;
attrs[attrList.count].length = (UInt32) strlen(comment);
attrs[attrList.count].data = (void*)comment;
attrList.count++;
}
if (label != NULL && !primaryKey) {
attrs[attrList.count].tag = kSecLabelItemAttr;
attrs[attrList.count].length = (UInt32) strlen(label);
attrs[attrList.count].data = (void*)label;
attrList.count++;
}
if (serverName != NULL) {
attrs[attrList.count].tag = kSecServerItemAttr;
attrs[attrList.count].length = (UInt32) strlen(serverName);
attrs[attrList.count].data = (void*)serverName;
attrList.count++;
}
if (securityDomain != NULL) {
attrs[attrList.count].tag = kSecSecurityDomainItemAttr;
attrs[attrList.count].length = (UInt32) strlen(securityDomain);
attrs[attrList.count].data = (void *)securityDomain;
attrList.count++;
}
if (accountName != NULL) {
attrs[attrList.count].tag = kSecAccountItemAttr;
attrs[attrList.count].length = (UInt32) strlen(accountName);
attrs[attrList.count].data = (void *)accountName;
attrList.count++;
}
if (path != NULL) {
attrs[attrList.count].tag = kSecPathItemAttr;
attrs[attrList.count].length = (UInt32) 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;
}
status = SecKeychainSearchCopyNext(searchRef, &itemRef);
if (status) {
itemRef = NULL;
}
cleanup:
if (searchRef)
CFRelease(searchRef);
return itemRef;
}
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;
}
CFDataRef hashData = NULL;
CFIndex hashLength = 0;
if (hash) {
CSSM_DATA hashCssmData = { 0, NULL };
hashCssmData.Length = (CSSM_SIZE)strlen(hash)/2;
hashCssmData.Data = (uint8 *)malloc(hashCssmData.Length);
fromHex(hash, &hashCssmData);
hashLength = (CFIndex)hashCssmData.Length;
hashData = CFDataCreate(kCFAllocatorDefault, (const UInt8 *)hashCssmData.Data, hashLength);
free(hashCssmData.Data);
}
CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL;
Boolean exactMatch = FALSE;
SecKeychainItemRef candidate = NULL;
while (SecKeychainSearchCopyNext(searchRef, &candidate) == noErr) {
SecCertificateRef cert = (SecCertificateRef)candidate;
if (hashData) {
CFDataRef certHash = NULL;
if (hashLength == CC_SHA1_DIGEST_LENGTH) {
certHash = SecCertificateGetSHA1Digest(cert);
if (certHash) { CFRetain(certHash); }
} else if (hashLength == CC_SHA256_DIGEST_LENGTH) {
certHash = SecCertificateCopySHA256Digest(cert);
}
if (!certHash) {
safe_CFRelease(&candidate);
continue; } else if (CFEqual(certHash, hashData)) {
exactMatch = TRUE;
}
safe_CFRelease(&certHash);
if (exactMatch) {
uniqueItemRef = candidate; break; }
} else {
CFStringRef nameRef = NULL;
if ((SecCertificateCopyCommonName(cert, &nameRef) != noErr) || nameRef == NULL) {
safe_CFRelease(&candidate);
continue; }
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; }
if (uniqueItemRef) { if (exactMatch && !isExact) { free(nameBuf);
safe_CFRelease(&nameRef);
safe_CFRelease(&candidate);
continue;
}
if (exactMatch == isExact) { if (CFEqual(uniqueItemRef, candidate)) { free(nameBuf);
safe_CFRelease(&nameRef);
safe_CFRelease(&candidate);
continue;
}
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); }
uniqueItemRef = candidate; exactMatch = isExact;
free(nameBuf);
safe_CFRelease(&nameRef);
}
}
safe_CFRelease(&searchRef);
safe_CFRelease(&matchRef);
safe_CFRelease(&hashData);
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 {
uint8_t *password = (uint8_t *) passwordData;
int doHex = 0;
for(uint32_t i=0; i<passwordLength; i++) if(!isprint(password[i])) doHex = 1;
if(doHex) {
for(uint32_t i=0; i<passwordLength; i++) printf("%02x", password[i]);
} else {
for(uint32_t i=0; i<passwordLength; i++) putchar(password[i]);
}
putchar('\n');
}
if (passwordData) SecKeychainItemFreeContent(NULL, passwordData);
return noErr;
}
static int
do_keychain_find_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,
Boolean get_password,
Boolean password_stdout)
{
OSStatus result = noErr;
SecKeychainItemRef itemRef = NULL;
itemRef = find_first_generic_password(keychainOrArray,
itemCreator,
itemType,
kind,
value,
comment,
label,
serviceName,
accountName);
if(itemRef) {
result = do_password_item_printing(itemRef, get_password, password_stdout);
} else {
result = errSecItemNotFound;
sec_perror("SecKeychainSearchCopyNext", result);
}
if (itemRef) CFRelease(itemRef);
return result;
}
static int
do_keychain_delete_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 result = noErr;
SecKeychainItemRef itemRef = NULL;
void *passwordData = NULL;
if (!itemCreator && !itemType && !kind && !value && !comment && !label && !serviceName && !accountName) {
return SHOW_USAGE_MESSAGE;
}
itemRef = find_first_generic_password(keychainOrArray,
itemCreator,
itemType,
kind,
value,
comment,
label,
serviceName,
accountName);
if (!itemRef) {
result = errSecItemNotFound;
sec_perror("SecKeychainSearchCopyNext", result);
goto cleanup;
}
print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE);
result = SecKeychainItemDelete(itemRef);
fputs("password has been deleted.\n", stderr);
cleanup:
if (passwordData)
SecKeychainItemFreeContent(NULL, passwordData);
if (itemRef)
CFRelease(itemRef);
return result;
}
static int
do_keychain_find_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,
Boolean get_password,
Boolean password_stdout)
{
OSStatus result = noErr;
SecKeychainItemRef itemRef = NULL;
itemRef = find_first_internet_password(keychainOrArray,
itemCreator,
itemType,
kind,
comment,
label,
serverName,
securityDomain,
accountName,
path,
port,
protocol,
authenticationType);
if(itemRef) {
result = do_password_item_printing(itemRef, get_password, password_stdout);
} else {
result = errSecItemNotFound;
sec_perror("SecKeychainSearchCopyNext", result);
}
return result;
}
static int
do_keychain_delete_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 result = noErr;
SecKeychainItemRef itemRef = NULL;
void *passwordData = NULL;
if (!itemCreator && !itemType && !kind && !comment && !label && !serverName && !securityDomain && !accountName && !path && !port && !protocol && !authenticationType) {
return SHOW_USAGE_MESSAGE;
}
itemRef = find_first_internet_password(keychainOrArray,
itemCreator,
itemType,
kind,
comment,
label,
serverName,
securityDomain,
accountName,
path,
port,
protocol,
authenticationType);
if (!itemRef) {
result = errSecItemNotFound;
sec_perror("SecKeychainSearchCopyNext", result);
goto cleanup;
}
print_keychain_item_attributes(stdout, itemRef, FALSE, FALSE, FALSE, FALSE);
result = SecKeychainItemDelete(itemRef);
fputs("password has been deleted.\n", stderr);
cleanup:
if (passwordData)
SecKeychainItemFreeContent(NULL, passwordData);
if (itemRef)
CFRelease(itemRef);
return result;
}
static int
do_keychain_find_certificate(CFTypeRef keychainOrArray,
const char *name,
const char *emailAddress,
Boolean print_hash,
Boolean output_pem,
Boolean find_all,
Boolean print_email)
{
OSStatus result = noErr;
SecCertificateRef certificateRef = NULL;
SecKeychainSearchRef searchRef = NULL;
CFStringRef matchRef = (name) ? CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8) : NULL;
if (find_all && emailAddress) {
result = SecKeychainSearchCreateForCertificateByEmail(keychainOrArray, emailAddress, &searchRef);
if (result) {
sec_perror("SecKeychainSearchCreateForCertificateByEmail", result);
goto cleanup;
}
} else {
result = SecKeychainSearchCreateFromAttributes(keychainOrArray, kSecCertificateItemClass, NULL, &searchRef);
if (result) {
sec_perror("SecKeychainSearchCreateFromAttributes", result);
goto cleanup;
}
}
do
{
if (find_all) {
SecKeychainItemRef itemRef = NULL;
result = SecKeychainSearchCopyNext(searchRef, &itemRef);
if (result == errSecItemNotFound) {
result = 0;
break;
}
else if (result) {
sec_perror("SecKeychainSearchCopyNext", result);
goto cleanup;
}
if (!emailAddress && name) {
CFStringRef nameRef = NULL;
if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) {
safe_CFRelease(&itemRef);
continue; }
CFRange find = { kCFNotFound, 0 };
if (nameRef && matchRef)
find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
if (find.location == kCFNotFound) {
safe_CFRelease(&nameRef);
safe_CFRelease(&itemRef);
continue; }
safe_CFRelease(&nameRef);
}
safe_CFRelease(&certificateRef);
certificateRef = (SecCertificateRef) itemRef;
}
else { if (emailAddress) {
result = SecCertificateFindByEmail(keychainOrArray, emailAddress, &certificateRef);
if (result) {
sec_perror("SecCertificateFindByEmail", result);
goto cleanup;
}
} else {
SecKeychainItemRef itemRef = NULL;
while ((result = SecKeychainSearchCopyNext(searchRef, &itemRef)) != errSecItemNotFound) {
if (name) {
CFStringRef nameRef = NULL;
if (SecCertificateCopyCommonName((SecCertificateRef)itemRef, &nameRef) != noErr) {
safe_CFRelease(&itemRef);
continue; }
CFRange find = { kCFNotFound, 0 };
if (nameRef && matchRef)
find = CFStringFind(nameRef, matchRef, kCFCompareCaseInsensitive | kCFCompareNonliteral);
if (find.location == kCFNotFound) {
safe_CFRelease(&nameRef);
safe_CFRelease(&itemRef);
continue; }
safe_CFRelease(&nameRef);
}
break; }
if (result == errSecItemNotFound) {
sec_perror("SecKeychainSearchCopyNext", result);
goto cleanup;
}
certificateRef = (SecCertificateRef) itemRef;
}
}
if (print_hash) {
unsigned char *p;
CFIndex i, count;
CFDataRef sha256Digest = SecCertificateCopySHA256Digest(certificateRef);
if (sha256Digest != NULL &&
(count = (CFIndex)CFDataGetLength(sha256Digest)) == CC_SHA256_DIGEST_LENGTH &&
(p = (unsigned char *)CFDataGetBytePtr(sha256Digest)) != NULL) {
fprintf(stdout, "SHA-256 hash: ");
for (i=0; i<count; i++) { fprintf(stdout, "%02X", p[i]); }
fprintf(stdout, "\n");
}
safe_CFRelease(&sha256Digest);
CFDataRef sha1Digest = SecCertificateGetSHA1Digest(certificateRef);
if (sha1Digest != NULL &&
(count = (CFIndex)CFDataGetLength(sha1Digest)) == CC_SHA1_DIGEST_LENGTH &&
(p = (unsigned char *)CFDataGetBytePtr(sha1Digest)) != NULL) {
fprintf(stdout, "SHA-1 hash: ");
for (i=0; i<count; i++) { fprintf(stdout, "%02X", p[i]); }
fprintf(stdout, "\n");
}
}
if (print_email)
{
CFArrayRef emailAddresses = NULL;
CFIndex ix, count;
result = SecCertificateCopyEmailAddresses(certificateRef, &emailAddresses);
if (result)
{
sec_perror("SecCertificateCopyEmailAddresses", result);
goto cleanup;
}
count = CFArrayGetCount(emailAddresses);
fputs("email addresses: ", stdout);
for (ix = 0; ix < count; ++ix)
{
CFStringRef emailAddress = (CFStringRef)CFArrayGetValueAtIndex(emailAddresses, ix);
const char *addr;
char buffer[256];
if (ix)
fputs(", ", stdout);
addr = CFStringGetCStringPtr(emailAddress, kCFStringEncodingUTF8);
if (!addr)
{
if (CFStringGetCString(emailAddress, buffer, sizeof(buffer), kCFStringEncodingUTF8))
addr = buffer;
}
fprintf(stdout, "%s", addr);
}
fputc('\n', stdout);
CFRelease(emailAddresses);
}
if (output_pem)
{
CSSM_DATA certData = {};
result = SecCertificateGetData(certificateRef, &certData);
if (result)
{
sec_perror("SecCertificateGetData", result);
goto cleanup;
}
print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data);
}
else
{
print_keychain_item_attributes(stdout, (SecKeychainItemRef)certificateRef, FALSE, FALSE, FALSE, FALSE);
}
} while (find_all);
cleanup:
safe_CFRelease(&searchRef);
safe_CFRelease(&certificateRef);
safe_CFRelease(&matchRef);
return result;
}
int
keychain_delete_internet_password(int argc, char * const *argv)
{
char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
char *kind = NULL, *label = NULL, *comment = NULL;
FourCharCode itemCreator = 0, itemType = 0;
UInt16 port = 0;
SecProtocolType protocol = 0;
SecAuthenticationType authenticationType = 0;
CFTypeRef keychainOrArray = NULL;
int ch, result = 0;
while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:t:")) != -1)
{
switch (ch)
{
case 'a':
accountName = optarg;
break;
case 'c':
result = parse_fourcharcode(optarg, &itemCreator);
if (result) goto cleanup;
break;
case 'C':
result = parse_fourcharcode(optarg, &itemType);
if (result) goto cleanup;
break;
case 'd':
securityDomain = optarg;
break;
case 'D':
kind = optarg;
break;
case 'j':
comment = optarg;
break;
case 'l':
label = optarg;
break;
case 'p':
path = optarg;
break;
case 'P':
port = atoi(optarg);
break;
case 'r':
result = parse_fourcharcode(optarg, &protocol);
if (result) goto cleanup;
break;
case 's':
serverName = optarg;
break;
case 't':
result = parse_fourcharcode(optarg, &authenticationType);
if (result) goto cleanup;
break;
case '?':
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
keychainOrArray = keychain_create_array(argc, argv);
result = do_keychain_delete_internet_password(keychainOrArray,
itemCreator,
itemType,
kind,
comment,
label,
serverName,
securityDomain,
accountName,
path,
port,
protocol,
authenticationType);
cleanup:
if (keychainOrArray)
CFRelease(keychainOrArray);
return result;
}
int
keychain_find_internet_password(int argc, char * const *argv)
{
char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL;
char *kind = NULL, *label = NULL, *comment = NULL;
FourCharCode itemCreator = 0, itemType = 0;
UInt16 port = 0;
SecProtocolType protocol = 0;
SecAuthenticationType authenticationType = 0;
CFTypeRef keychainOrArray = NULL;
int ch, result = 0;
Boolean get_password = FALSE;
Boolean password_stdout = FALSE;
while ((ch = getopt(argc, argv, "ha:c:C:d:D:hgj:l:p:P:r:s:wt:")) != -1)
{
switch (ch)
{
case 'a':
accountName = optarg;
break;
case 'c':
result = parse_fourcharcode(optarg, &itemCreator);
if (result) goto cleanup;
break;
case 'C':
result = parse_fourcharcode(optarg, &itemType);
if (result) goto cleanup;
break;
case 'd':
securityDomain = optarg;
break;
case 'D':
kind = optarg;
break;
case 'j':
comment = optarg;
break;
case 'l':
label = optarg;
break;
case 'g':
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 cleanup;
break;
case 's':
serverName = optarg;
break;
case 'w':
get_password = TRUE;
password_stdout = TRUE;
break;
case 't':
result = parse_fourcharcode(optarg, &authenticationType);
if (result) goto cleanup;
authenticationType = OSSwapHostToBigInt32(authenticationType);
break;
case '?':
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
keychainOrArray = keychain_create_array(argc, argv);
result = do_keychain_find_internet_password(keychainOrArray,
itemCreator,
itemType,
kind,
comment,
label,
serverName,
securityDomain,
accountName,
path,
port,
protocol,
authenticationType,
get_password,
password_stdout);
cleanup:
if (keychainOrArray)
CFRelease(keychainOrArray);
return result;
}
int
keychain_delete_generic_password(int argc, char * const *argv)
{
char *serviceName = NULL, *accountName = NULL;
char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL;
FourCharCode itemCreator = 0, itemType = 0;
CFTypeRef keychainOrArray = nil;
int ch, result = 0;
while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:g")) != -1)
{
switch (ch)
{
case 'a':
accountName = optarg;
break;
case 'c':
result = parse_fourcharcode(optarg, &itemCreator);
if (result) goto cleanup;
break;
case 'C':
result = parse_fourcharcode(optarg, &itemType);
if (result) goto cleanup;
break;
case 'D':
kind = optarg;
break;
case 'G':
value = optarg;
break;
case 'j':
comment = optarg;
break;
case 'l':
label = optarg;
break;
case 's':
serviceName = optarg;
break;
case '?':
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
keychainOrArray = keychain_create_array(argc, argv);
result = do_keychain_delete_generic_password(keychainOrArray,
itemCreator,
itemType,
kind,
value,
comment,
label,
serviceName,
accountName);
cleanup:
if (keychainOrArray)
CFRelease(keychainOrArray);
return result;
}
int
keychain_find_generic_password(int argc, char * const *argv)
{
char *serviceName = NULL, *accountName = NULL;
char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL;
FourCharCode itemCreator = 0, itemType = 0;
CFTypeRef keychainOrArray = nil;
int ch, result = 0;
Boolean get_password = FALSE;
Boolean password_stdout = FALSE;
while ((ch = getopt(argc, argv, "ha:c:C:D:G:j:l:s:wg")) != -1)
{
switch (ch)
{
case 'a':
accountName = optarg;
break;
case 'c':
result = parse_fourcharcode(optarg, &itemCreator);
if (result) goto cleanup;
break;
case 'C':
result = parse_fourcharcode(optarg, &itemType);
if (result) goto cleanup;
break;
case 'D':
kind = optarg;
break;
case 'G':
value = optarg;
break;
case 'j':
comment = optarg;
break;
case 'l':
label = optarg;
break;
case 's':
serviceName = optarg;
break;
case 'w':
password_stdout = TRUE;
get_password = TRUE;
break;
case 'g':
get_password = TRUE;
break;
case '?':
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
keychainOrArray = keychain_create_array(argc, argv);
result = do_keychain_find_generic_password(keychainOrArray,
itemCreator,
itemType,
kind,
value,
comment,
label,
serviceName,
accountName,
get_password,
password_stdout);
cleanup:
if (keychainOrArray)
CFRelease(keychainOrArray);
return result;
}
#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) {
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) {
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;
}
int keychain_set_partition_list(SecKeychainRef kc, CFDictionaryRef query, CFStringRef password, CFStringRef partitionlist);
int keychain_parse_args_and_set_partition_list(int argc, char * const *argv, CFMutableDictionaryRef query, CFStringRef partitionidsinput, CFStringRef password);
int keychain_set_internet_password_partition_list(int argc, char * const *argv) {
CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(query, kSecClass, kSecClassInternetPassword);
CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
CFStringRef partitionidsinput = NULL;
CFStringRef password = NULL;
int ch, result = 0;
while ((ch = getopt(argc, argv, "a:c:C:d:D:j:l:p:P:r:s:S:t:k:")) != -1)
{
switch (ch)
{
case 'a':
SetKeyToString(query, kSecAttrAccount, optarg);
break;
case 'c':
SetKeyToString(query, kSecAttrCreator, optarg);
break;
case 'C':
SetKeyToString(query, kSecAttrType, optarg);
break;
case 'd':
SetKeyToString(query, kSecAttrSecurityDomain, optarg);
break;
case 'D':
SetKeyToString(query, kSecAttrDescription, optarg);
break;
case 'j':
SetKeyToString(query, kSecAttrComment, optarg);
break;
case 'l':
SetKeyToString(query, kSecAttrLabel, optarg);
break;
case 'p':
SetKeyToString(query, kSecAttrPath, optarg);
break;
case 'P':
SetKeyToString(query, kSecAttrPort, optarg);
break;
case 'r':
SetKeyToString(query, kSecAttrProtocol, optarg);
break;
case 's':
SetKeyToString(query, kSecAttrServer, optarg);
break;
case 't':
SetKeyToString(query, kSecAttrAuthenticationType, optarg);
break;
case 'S':
CFReleaseNull(partitionidsinput);
partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
break;
case 'k':
CFReleaseNull(password);
password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
break;
case '?':
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password);
cleanup:
safe_CFRelease(&password);
safe_CFRelease(&partitionidsinput);
safe_CFRelease(&query);
return result;
}
int
keychain_set_generic_password_partition_list(int argc, char * const *argv) {
CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(query, kSecClass, kSecClassGenericPassword);
CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
CFStringRef partitionidsinput = NULL;
CFStringRef password = NULL;
int ch, result = 0;
while ((ch = getopt(argc, argv, ":a:c:C:D:G:j:l:s:S:k:")) != -1)
{
switch (ch)
{
case 'a':
SetKeyToString(query, kSecAttrAccount, optarg);
break;
case 'c':
SetKeyToString(query, kSecAttrCreator, optarg);
break;
case 'C':
SetKeyToString(query, kSecAttrType, optarg);
break;
case 'D':
SetKeyToString(query, kSecAttrDescription, optarg);
break;
case 'G':
SetKeyToString(query, kSecAttrGeneric, optarg);
break;
case 'j':
SetKeyToString(query, kSecAttrComment, optarg);
break;
case 'l':
SetKeyToString(query, kSecAttrLabel, optarg);
break;
case 's':
SetKeyToString(query, kSecAttrService, optarg);
break;
case 'S':
CFReleaseNull(partitionidsinput);
partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
break;
case 'k':
CFReleaseNull(password);
password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
break;
case '?':
case ':':
if (optopt == 'k') {
break;
}
result = 2;
goto cleanup;
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password);
cleanup:
safe_CFRelease(&password);
safe_CFRelease(&partitionidsinput);
safe_CFRelease(&query);
return result;
}
int
keychain_set_key_partition_list(int argc, char * const *argv) {
CFMutableDictionaryRef query = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(query, kSecClass, kSecClassKey);
CFDictionarySetValue(query, kSecMatchLimit, kSecMatchLimitAll);
CFStringRef partitionidsinput = NULL;
CFStringRef password = NULL;
int ch, result = 0;
while ((ch = getopt(argc, argv, ":a:c:dD:ej:k:l:rsS:t:uvw")) != -1)
{
switch (ch)
{
case 'a':
SetKeyToString(query, kSecAttrApplicationLabel, optarg);
break;
case 'c':
SetKeyToString(query, kSecAttrCreator, optarg);
break;
case 'd':
SetKeyToString(query, kSecAttrCanDecrypt, optarg);
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);
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 'S':
CFReleaseNull(partitionidsinput);
partitionidsinput = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
break;
case 'k':
CFReleaseNull(password);
password = CFStringCreateWithCStringNoCopy(NULL, optarg, kCFStringEncodingUTF8, kCFAllocatorNull);
break;
case '?':
case ':':
if (optopt == 'k') {
break;
}
result = 2;
goto cleanup;
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
result = keychain_parse_args_and_set_partition_list(argc, argv, query, partitionidsinput, password);
cleanup:
CFReleaseNull(partitionidsinput);
CFReleaseNull(password);
safe_CFRelease(&query);
return result;
}
int keychain_parse_args_and_set_partition_list(int argc, char * const *argv, CFMutableDictionaryRef query, CFStringRef partitionidsinput, CFStringRef password) {
int result = 0;
const char *keychainName = NULL;
SecKeychainRef kc = NULL;
CFStringRef localPassword = NULL;
if (argc == 1)
{
keychainName = argv[0];
if (*keychainName == '\0')
{
result = 2;
goto cleanup;
}
kc = keychain_open(keychainName);
if(!kc) {
sec_error("couldn't open \"%s\"", keychainName);
result = 1;
goto cleanup;
}
CFMutableArrayRef searchList = (CFMutableArrayRef) CFArrayCreateMutable(kCFAllocatorDefault, 1, &kCFTypeArrayCallBacks);
CFArrayAppendValue((CFMutableArrayRef)searchList, kc);
CFDictionarySetValue(query, kSecMatchSearchList, searchList);
} else if (argc != 0) {
result = 2;
goto cleanup;
}
if(!partitionidsinput) {
result = 2;
goto cleanup;
}
if(!password) {
char* cpassword = prompt_password(keychainName);
if (!cpassword) {
result = -1;
goto cleanup;
}
localPassword = CFStringCreateWithCString(NULL, cpassword, kCFStringEncodingUTF8);
password = localPassword;
free(cpassword);
}
result = keychain_set_partition_list(kc, query, password, partitionidsinput);
cleanup:
CFReleaseNull(localPassword);
return result;
}
int keychain_set_partition_list(SecKeychainRef kc, CFDictionaryRef query, CFStringRef password, CFStringRef partitionlist) {
int result = 0;
char *passwordBuf = NULL;
size_t passwordLen;
GetCStringFromCFString(password, &passwordBuf, &passwordLen);
OSStatus status;
CFTypeRef results = NULL;
status = SecKeychainUnlock(kc, (UInt32) passwordLen, passwordBuf, true);
if(status) {
sec_perror("SecKeychainUnlock", status);
result = 1;
goto cleanup;
}
status = SecItemCopyMatching(query, &results);
if(status) {
sec_perror("SecItemCopyMatching", status);
result = 1;
goto cleanup;
}
if(!results) {
result = 0;
goto cleanup;
}
if (CFGetTypeID(results) == CFArrayGetTypeID()) {
for(int i = 0; i < CFArrayGetCount(results); i++) {
SecKeychainItemRef item = (SecKeychainItemRef) CFArrayGetValueAtIndex(results, i);
SecAccessRef access = NULL;
do_password_item_printing(item, false, false);
status = SecKeychainItemCopyAccess(item, &access);
if (status == errSecNoAccessForItem) {
continue;
}
if(status) {
sec_perror("SecKeychainItemCopyAccess", status);
result = 1;
goto cleanup;
}
CFArrayRef aclList = NULL;
status = SecAccessCopyACLList(access, &aclList);
if (status)
{
sec_perror("SecAccessCopyACLList", status);
result = 1;
goto cleanup;
}
CFIndex size = CFArrayGetCount(aclList);
for(CFIndex i = 0; i < size; i++) {
SecACLRef acl = (SecACLRef) CFArrayGetValueAtIndex(aclList, i);
CSSM_ACL_AUTHORIZATION_TAG tags[64]; uint32 tagix, tagCount = sizeof(tags) / sizeof(*tags);
status = SecACLGetAuthorizations(acl, tags, &tagCount);
if (status)
{
sec_perror("SecACLGetAuthorizations", status);
result = 1;
goto cleanup;
}
CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR promptSelector = {};
for (tagix = 0; tagix < tagCount; ++tagix)
{
CSSM_ACL_AUTHORIZATION_TAG tag = tags[tagix];
if(tag == CSSM_ACL_AUTHORIZATION_PARTITION_ID) {
CFArrayRef applicationList;
CFStringRef promptDescription;
status = SecACLCopySimpleContents(acl, &applicationList, &promptDescription, &promptSelector);
if(status) {
sec_perror("SecACLCopySimpleContents", status);
result = 1;
goto cleanup;
}
CFArrayRef partitionIDs = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, partitionlist, CFSTR(","));
CFMutableDictionaryRef dict = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(dict, CFSTR("Partitions"), partitionIDs);
CFDataRef xml = CFPropertyListCreateXMLData(NULL, dict);
CFStringRef xmlstr = cfToHex(xml);
SecACLSetSimpleContents(acl, applicationList, xmlstr, &promptSelector);
safe_CFRelease(&xmlstr);
safe_CFRelease(&xml);
safe_CFRelease(&dict);
safe_CFRelease(&partitionIDs);
}
}
}
status = SecKeychainItemSetAccessWithPassword(item, access, (UInt32) passwordLen, passwordBuf);
if(status) {
sec_perror("SecKeychainItemSetAccessWithPassword", status);
result = 1;
goto cleanup;
}
}
}
result = 0;
cleanup:
if(passwordBuf) {
free(passwordBuf);
}
safe_CFRelease(&results);
return result;
}
int
keychain_find_certificate(int argc, char * const *argv)
{
char *emailAddress = NULL;
char *name = NULL;
int ch, result = 0;
CFTypeRef keychainOrArray = nil;
Boolean output_pem = FALSE;
Boolean find_all = FALSE;
Boolean print_hash = FALSE;
Boolean print_email = FALSE;
while ((ch = getopt(argc, argv, "hac:e:mpZ")) != -1)
{
switch (ch)
{
case 'a':
find_all = TRUE;
break;
case 'c':
name = optarg;
break;
case 'e':
emailAddress = optarg;
break;
case 'm':
print_email = TRUE;
break;
case 'p':
output_pem = TRUE;
break;
case 'Z':
print_hash = TRUE;
break;
case '?':
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
keychainOrArray = keychain_create_array(argc, argv);
result = do_keychain_find_certificate(keychainOrArray, name, emailAddress, print_hash, output_pem, find_all, print_email);
cleanup:
if (keychainOrArray)
CFRelease(keychainOrArray);
return result;
}
static int
do_keychain_dump_class(FILE *stream, CFTypeRef keychainOrArray, SecItemClass itemClass, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
{
SecKeychainItemRef item;
SecKeychainSearchRef search = NULL;
int result = 0;
OSStatus status;
status = SecKeychainSearchCreateFromAttributes(keychainOrArray, itemClass, NULL, &search);
if (status)
{
sec_perror("SecKeychainSearchCreateFromAttributes", status);
result = 1;
goto cleanup;
}
while ((status = SecKeychainSearchCopyNext(search, &item)) == 0)
{
print_keychain_item_attributes(stream, item, show_data, show_raw_data, show_acl, interactive);
CFRelease(item);
}
if (status != errSecItemNotFound)
{
sec_perror("SecKeychainSearchCopyNext", status);
result = 1;
goto cleanup;
}
cleanup:
if (search)
CFRelease(search);
return result;
}
static int
do_keychain_dump(FILE *stream, CFTypeRef keychainOrArray, Boolean show_data, Boolean show_raw_data, Boolean show_acl, Boolean interactive)
{
return do_keychain_dump_class(stream, keychainOrArray, CSSM_DL_DB_RECORD_ANY, show_data, show_raw_data, show_acl, interactive);
}
int
keychain_dump(int argc, char * const *argv)
{
int ch, result = 0;
Boolean show_data = FALSE, show_raw_data = FALSE, show_acl = FALSE, interactive = FALSE;
CFTypeRef keychainOrArray = NULL;
const char *outputFilename = NULL;
FILE *output;
while ((ch = getopt(argc, argv, "adhiro:")) != -1)
{
switch (ch)
{
case 'a':
show_acl = TRUE;
break;
case 'd':
show_data = TRUE;
break;
case 'i':
show_acl = TRUE;
interactive = TRUE;
break;
case 'r':
show_raw_data = TRUE;
break;
case 'o':
outputFilename = optarg;
break;
case '?':
default:
return SHOW_USAGE_MESSAGE;
}
}
argc -= optind;
argv += optind;
keychainOrArray = keychain_create_array(argc, argv);
if (outputFilename)
output = fopen(outputFilename, "w");
else
output = stdout;
result = do_keychain_dump(output, keychainOrArray, show_data, show_raw_data, show_acl, interactive);
if (outputFilename)
fclose(output);
if (keychainOrArray)
CFRelease(keychainOrArray);
return result;
}