#include "keychain_add.h"
#include "keychain_find.h"
#include "readline_cssm.h"
#include "security_tool.h"
#include "access_utils.h"
#include "keychain_utilities.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <AssertMacros.h>
#include <libkern/OSByteOrder.h>
#include <Security/SecAccess.h>
#include <Security/SecCertificate.h>
#include <Security/SecKeychain.h>
#include <Security/SecKeychainItem.h>
#include <Security/SecTrustedApplication.h>
#include <Security/SecTrustedApplicationPriv.h>
static int
do_update_generic_password(const char *keychainName,
FourCharCode itemCreator,
FourCharCode itemType,
const char *kind,
const char *value,
const char *comment,
const char *label,
const char *serviceName,
const char *accountName,
const void *passwordData,
uint32_t passwordLength,
SecAccessRef access)
{
OSStatus status;
SecKeychainRef keychainRef = NULL;
SecKeychainItemRef itemRef = NULL;
if (keychainName) {
keychainRef = keychain_open(keychainName);
}
itemRef = find_first_generic_password(keychainRef,itemCreator,itemType,kind,value,comment,label,serviceName,accountName);
if (keychainRef) {
CFRelease(keychainRef);
}
if (!itemRef) {
return errSecItemNotFound;
}
SecKeychainAttribute attrs[8]; SecKeychainAttributeList attrList = { 0, attrs };
if ((UInt32)itemCreator != 0) {
attrs[attrList.count].tag = kSecCreatorItemAttr;
attrs[attrList.count].length = sizeof(FourCharCode);
attrs[attrList.count].data = (FourCharCode *)&itemCreator;
attrList.count++;
}
if ((UInt32)itemType != 0) {
attrs[attrList.count].tag = kSecTypeItemAttr;
attrs[attrList.count].length = sizeof(FourCharCode);
attrs[attrList.count].data = (FourCharCode *)&itemType;
attrList.count++;
}
if (kind != NULL) {
attrs[attrList.count].tag = kSecDescriptionItemAttr;
attrs[attrList.count].length = (UInt32) strlen(kind);
attrs[attrList.count].data = (void *)kind;
attrList.count++;
}
if (value != NULL) {
attrs[attrList.count].tag = kSecGenericItemAttr;
attrs[attrList.count].length = (UInt32) strlen(value);
attrs[attrList.count].data = (void *)value;
attrList.count++;
}
if (comment != NULL) {
attrs[attrList.count].tag = kSecCommentItemAttr;
attrs[attrList.count].length = (UInt32) strlen(comment);
attrs[attrList.count].data = (void *)comment;
attrList.count++;
}
if (label != NULL) {
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 = SecKeychainItemModifyContent(itemRef, &attrList, passwordLength, passwordData);
if (status) {
sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status));
}
if (!status && access) {
SecAccessRef curAccess = NULL;
status = SecKeychainItemCopyAccess(itemRef, &curAccess);
if (status) {
sec_error("SecKeychainItemCopyAccess: %s", sec_errstr(status));
} else {
int result = merge_access(curAccess, access); if (result == noErr) {
status = SecKeychainItemSetAccess(itemRef, curAccess); if (status) {
sec_error("SecKeychainItemSetAccess: %s", sec_errstr(status));
}
}
}
if (curAccess) {
CFRelease(curAccess);
}
}
CFRelease(itemRef);
return status;
}
static int
do_add_generic_password(const char *keychainName,
FourCharCode itemCreator,
FourCharCode itemType,
const char *kind,
const char *value,
const char *comment,
const char *label,
const char *serviceName,
const char *accountName,
const void *passwordData,
uint32_t passwordLength,
SecAccessRef access,
Boolean update)
{
SecKeychainRef keychain = NULL;
OSStatus result = 0;
SecKeychainItemRef itemRef = NULL;
if (update) {
result = do_update_generic_password(keychainName,itemCreator,itemType,kind,value,
comment,label,serviceName,accountName,passwordData,passwordLength,access);
if (result == noErr)
return result;
}
if (keychainName)
{
keychain = keychain_open(keychainName);
if (!keychain)
{
result = 1;
goto cleanup;
}
}
SecKeychainAttribute attrs[] = {
{ kSecLabelItemAttr, label ? (UInt32) strlen(label) : 0, (char *)label },
{ kSecDescriptionItemAttr, kind ? (UInt32) strlen(kind) : 0, (char *)kind },
{ kSecGenericItemAttr, value ? (UInt32) strlen(value) : 0, (char *)value },
{ kSecCommentItemAttr, comment ? (UInt32) strlen(comment) : 0, (char *)comment },
{ kSecServiceItemAttr, serviceName ? (UInt32) strlen(serviceName) : 0, (char *)serviceName },
{ kSecAccountItemAttr, accountName ? (UInt32) strlen(accountName) : 0, (char *)accountName },
{ (SecKeychainAttrType)0, sizeof(FourCharCode), NULL },
{ (SecKeychainAttrType)0, sizeof(FourCharCode), NULL }
};
SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
attributes.count -= 2;
if (itemCreator != 0)
{
attrs[attributes.count].tag = kSecCreatorItemAttr;
attrs[attributes.count].data = (FourCharCode *)&itemCreator;
attributes.count++;
}
if (itemType != 0)
{
attrs[attributes.count].tag = kSecTypeItemAttr;
attrs[attributes.count].data = (FourCharCode *)&itemType;
attributes.count++;
}
result = SecKeychainItemCreateFromContent(kSecGenericPasswordItemClass,
&attributes,
passwordLength,
passwordData,
keychain,
access,
&itemRef);
if (result)
{
sec_error("SecKeychainItemCreateFromContent (%s): %s", keychainName ? keychainName : "<default>", sec_errstr(result));
}
cleanup:
if (itemRef)
CFRelease(itemRef);
if (keychain)
CFRelease(keychain);
return result;
}
static int
do_update_internet_password(const char *keychainName,
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,
const void *passwordData,
uint32_t passwordLength,
SecAccessRef access)
{
OSStatus status;
SecKeychainRef keychainRef = NULL;
SecKeychainItemRef itemRef = NULL;
if (keychainName) {
keychainRef = keychain_open(keychainName);
}
itemRef = find_first_internet_password(keychainRef,itemCreator,itemType,kind,comment,label,serverName,
securityDomain,accountName,path,port,protocol,authenticationType);
if (keychainRef) {
CFRelease(keychainRef);
}
if (!itemRef) {
return errSecItemNotFound;
}
SecKeychainAttribute attrs[12]; SecKeychainAttributeList attrList = { 0, attrs };
if ((UInt32)itemCreator != 0) {
attrs[attrList.count].tag = kSecCreatorItemAttr;
attrs[attrList.count].length = sizeof(FourCharCode);
attrs[attrList.count].data = (FourCharCode *)&itemCreator;
attrList.count++;
}
if ((UInt32)itemType != 0) {
attrs[attrList.count].tag = kSecTypeItemAttr;
attrs[attrList.count].length = sizeof(FourCharCode);
attrs[attrList.count].data = (FourCharCode *)&itemType;
attrList.count++;
}
if (kind != NULL) {
attrs[attrList.count].tag = kSecDescriptionItemAttr;
attrs[attrList.count].length = (UInt32) strlen(kind);
attrs[attrList.count].data = (void *)kind;
attrList.count++;
}
if (comment != NULL) {
attrs[attrList.count].tag = kSecCommentItemAttr;
attrs[attrList.count].length = (UInt32) strlen(comment);
attrs[attrList.count].data = (void *)comment;
attrList.count++;
}
if (label != NULL) {
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 = SecKeychainItemModifyContent(itemRef, &attrList, passwordLength, passwordData);
if (status) {
sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status));
}
if (!status && access) {
status = modify_access(itemRef, access);
}
CFRelease(itemRef);
return status;
}
static int
do_add_internet_password(const char *keychainName,
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,
const void *passwordData,
uint32_t passwordLength,
SecAccessRef access,
Boolean update)
{
SecKeychainRef keychain = NULL;
SecKeychainItemRef itemRef = NULL;
OSStatus result = 0;
if (update) {
result = do_update_internet_password(keychainName,itemCreator,itemType,kind,comment,label,serverName,
securityDomain,accountName,path,port,protocol,authenticationType,
passwordData,passwordLength,access);
if (result == noErr)
return result;
}
if (keychainName)
{
keychain = keychain_open(keychainName);
if (!keychain)
{
result = 1;
goto cleanup;
}
}
SecKeychainAttribute attrs[] = {
{ kSecLabelItemAttr, label ? (UInt32) strlen(label) : 0, (char *)label },
{ kSecDescriptionItemAttr, kind ? (UInt32) strlen(kind) : 0, (char *)kind },
{ kSecCommentItemAttr, comment ? (UInt32) strlen(comment) : 0, (char *)comment },
{ kSecServerItemAttr, serverName ? (UInt32) strlen(serverName) : 0, (char *)serverName },
{ kSecSecurityDomainItemAttr, securityDomain ? (UInt32) strlen(securityDomain) : 0, (char *)securityDomain },
{ kSecAccountItemAttr, accountName ? (UInt32) strlen(accountName) : 0, (char *)accountName },
{ kSecPathItemAttr, path ? (UInt32) strlen(path) : 0, (char *)path },
{ kSecPortItemAttr, sizeof(UInt16), (UInt16 *)&port },
{ kSecProtocolItemAttr, sizeof(SecProtocolType), (SecProtocolType *)&protocol },
{ kSecAuthenticationTypeItemAttr, sizeof(SecAuthenticationType), (SecAuthenticationType *)&authenticationType },
{ (SecKeychainAttrType)0, sizeof(FourCharCode), NULL },
{ (SecKeychainAttrType)0, sizeof(FourCharCode), NULL }
};
SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
attributes.count -= 2;
if (itemCreator != 0)
{
attrs[attributes.count].tag = kSecCreatorItemAttr;
attrs[attributes.count].data = (FourCharCode *)&itemCreator;
attributes.count++;
}
if (itemType != 0)
{
attrs[attributes.count].tag = kSecTypeItemAttr;
attrs[attributes.count].data = (FourCharCode *)&itemType;
attributes.count++;
}
result = SecKeychainItemCreateFromContent(kSecInternetPasswordItemClass,
&attributes,
passwordLength,
passwordData,
keychain,
access,
&itemRef);
if (result)
{
sec_error("SecKeychainAddInternetPassword %s: %s", keychainName ? keychainName : "<NULL>", sec_errstr(result));
}
cleanup:
if (itemRef)
CFRelease(itemRef);
if (keychain)
CFRelease(keychain);
return result;
}
static int
do_add_certificates(const char *keychainName, int argc, char * const *argv)
{
SecKeychainRef keychain = NULL;
int ix, result = 0;
if (keychainName)
{
keychain = keychain_open(keychainName);
if (!keychain)
{
result = 1;
goto cleanup;
}
}
for (ix = 0; ix < argc; ++ix)
{
CSSM_DATA certData = {};
OSStatus status;
SecCertificateRef certificate = NULL;
if (read_file(argv[ix], &certData))
{
result = 1;
continue;
}
status = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_UNKNOWN, &certificate);
if (status)
{
sec_perror("SecCertificateCreateFromData", status);
result = 1;
}
else
{
status = SecCertificateAddToKeychain(certificate, keychain);
if (status)
{
if (status == errSecDuplicateItem)
{
if (keychainName)
sec_error("%s: already in %s", argv[ix], keychainName);
else
sec_error("%s: already in default keychain", argv[ix]);
}
else
{
sec_perror("SecCertificateAddToKeychain", status);
}
result = 1;
}
}
if (certData.Data)
free(certData.Data);
if (certificate)
CFRelease(certificate);
}
cleanup:
if (keychain)
CFRelease(keychain);
return result;
}
static bool convertPasswordData(const char *hexString, char **outData, uint32_t *outLength) {
size_t length = 0;
bool result = convertHex(hexString, (uint8_t **)outData, &length);
if (result && outLength) {
*outLength = (uint32_t)length & 0xFFFFFFFF;
}
return result;
}
static bool promptForPasswordData(char **returnedPasswordData) {
if (!returnedPasswordData)
return false;
bool result = false;
char *password = NULL;
int tries;
for (tries = 3; tries-- > 0;) {
bool passwordsMatch = false;
char *firstpass = NULL;
password = getpass("password data for new item: ");
if (!password)
break;
firstpass = malloc(strlen(password) + 1);
strcpy(firstpass, password);
password = getpass("retype password for new item: ");
passwordsMatch = password && (strcmp(password, firstpass) == 0);
memset(firstpass, 0, strlen(firstpass));
free(firstpass);
if (!password)
break;
if (passwordsMatch) {
result = true;
break;
}
fprintf(stderr, "passwords don't match\n");
memset(password, 0, strlen(password));
}
if (result) {
*returnedPasswordData = password;
} else {
free(password);
}
return result;
}
int
keychain_add_generic_password(int argc, char * const *argv)
{
char *serviceName = NULL, *passwordData = NULL, *accountName = NULL;
char *kind = NULL, *label = NULL, *value = NULL, *comment = NULL;
FourCharCode itemCreator = 0, itemType = 0;
int ch, result = 0;
uint32_t passwordLength = 0;
const char *keychainName = NULL;
Boolean access_specified = FALSE;
Boolean always_allow = FALSE;
Boolean update_item = FALSE;
SecAccessRef access = NULL;
CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
OSStatus status;
bool mustFreePasswordData = false;
while ((ch = getopt(argc, argv, ":a:c:C:D:G:j:l:s:p:w:X:UAT:h")) != -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 'p':
case 'w':
passwordData = optarg;
passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0;
break;
case 'U':
update_item = TRUE;
break;
case 'A':
always_allow = TRUE;
access_specified = TRUE;
break;
case 'T':
{
if (optarg[0])
{
SecTrustedApplicationRef app = NULL;
const char *groupPrefix = "group://";
size_t prefixLen = strlen(groupPrefix);
if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) {
const char *groupName = &optarg[prefixLen];
if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) {
sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status));
}
} else {
if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) {
sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
}
}
if (status) {
result = 1;
goto cleanup;
}
CFArrayAppendValue(trusted_list, app);
CFRelease(app);
}
access_specified = TRUE;
break;
}
case 'X':
if (convertPasswordData(optarg, &passwordData, &passwordLength)) {
mustFreePasswordData = true;
break;
}
sec_error("Unable to convert password data (-X must specify valid hex digits)");
result = 2;
goto cleanup;
case '?':
case ':':
if (optopt == 'p' || optopt == 'w') {
if (promptForPasswordData(&passwordData)) {
passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0;
mustFreePasswordData = true;
break;
} else {
result = 1;
goto cleanup;
}
}
result = 2;
goto cleanup;
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
if (!accountName || !serviceName)
{
result = 2;
goto cleanup;
}
else if (argc > 0)
{
keychainName = argv[0];
if (argc > 1 || *keychainName == '\0')
{
result = 2;
goto cleanup;
}
}
if (access_specified)
{
const char *accessName = (label) ? label : (serviceName) ? serviceName : (accountName) ? accountName : "";
if ((result = create_access(accessName, always_allow, trusted_list, &access)) != 0)
goto cleanup;
}
result = do_add_generic_password(keychainName,
itemCreator,
itemType,
kind,
value,
comment,
(label) ? label : serviceName,
serviceName,
accountName,
passwordData,
passwordLength,
access,
update_item);
cleanup:
if (mustFreePasswordData)
free(passwordData);
if (trusted_list)
CFRelease(trusted_list);
if (access)
CFRelease(access);
return result;
}
int
keychain_add_internet_password(int argc, char * const *argv)
{
char *serverName = NULL, *securityDomain = NULL, *accountName = NULL, *path = NULL, *passwordData = NULL;
char *kind = NULL, *comment = NULL, *label = NULL;
FourCharCode itemCreator = 0, itemType = 0;
UInt16 port = 0;
SecProtocolType protocol = 0;
SecAuthenticationType authenticationType = OSSwapHostToBigInt32('dflt');
int ch, result = 0;
uint32_t passwordLength = 0;
const char *keychainName = NULL;
Boolean access_specified = FALSE;
Boolean always_allow = FALSE;
Boolean update_item = FALSE;
SecAccessRef access = NULL;
CFMutableArrayRef trusted_list = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
OSStatus status;
bool mustFreePasswordData = false;
while ((ch = getopt(argc, argv, ":a:c:C:d:D:j:l:p:P:r:s:t:w:X:UAT:h")) != -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;
authenticationType = OSSwapHostToBigInt32(authenticationType);
break;
case 'w':
passwordData = optarg;
passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0;
break;
case 'U':
update_item = TRUE;
break;
case 'A':
always_allow = TRUE;
access_specified = TRUE;
break;
case 'T':
{
if (optarg[0])
{
SecTrustedApplicationRef app = NULL;
const char *groupPrefix = "group://";
size_t prefixLen = strlen(groupPrefix);
if (strlen(optarg) > prefixLen && !memcmp(optarg, groupPrefix, prefixLen)) {
const char *groupName = &optarg[prefixLen];
if ((status = SecTrustedApplicationCreateApplicationGroup(groupName, NULL, &app)) != noErr) {
sec_error("SecTrustedApplicationCreateApplicationGroup %s: %s", optarg, sec_errstr(status));
}
} else {
if ((status = SecTrustedApplicationCreateFromPath(optarg, &app)) != noErr) {
sec_error("SecTrustedApplicationCreateFromPath %s: %s", optarg, sec_errstr(status));
}
}
if (status) {
result = 1;
goto cleanup;
}
CFArrayAppendValue(trusted_list, app);
CFRelease(app);
}
access_specified = TRUE;
break;
}
case 'X':
if (convertPasswordData(optarg, &passwordData, &passwordLength)) {
mustFreePasswordData = true;
break;
}
sec_error("Unable to convert password data (-X must specify valid hex digits)");
result = 2;
goto cleanup;
case '?':
case ':':
if (optopt == 'p' || optopt == 'w') {
if (promptForPasswordData(&passwordData)) {
passwordLength = (passwordData) ? (uint32_t)strlen(passwordData) : 0;
mustFreePasswordData = true;
break;
} else {
result = 1;
goto cleanup;
}
}
result = 2;
goto cleanup;
default:
result = 2;
goto cleanup;
}
}
argc -= optind;
argv += optind;
if (!accountName || !serverName)
{
result = 2;
goto cleanup;
}
else if (argc > 0)
{
keychainName = argv[0];
if (argc > 1 || *keychainName == '\0')
{
result = 2;
goto cleanup;
}
}
if (access_specified)
{
const char *accessName = (label) ? label : (serverName) ? serverName : (accountName) ? accountName : "";
if ((result = create_access(accessName, always_allow, trusted_list, &access)) != 0)
goto cleanup;
}
result = do_add_internet_password(keychainName,
itemCreator,
itemType,
kind,
comment,
(label) ? label : serverName,
serverName,
securityDomain,
accountName,
path,
port,
protocol,
authenticationType,
passwordData,
passwordLength,
access,
update_item);
cleanup:
if (mustFreePasswordData)
free(passwordData);
if (trusted_list)
CFRelease(trusted_list);
if (access)
CFRelease(access);
return result;
}
int
keychain_add_certificates(int argc, char * const *argv)
{
int ch, result = 0;
const char *keychainName = NULL;
while ((ch = getopt(argc, argv, "hk:")) != -1)
{
switch (ch)
{
case 'k':
keychainName = optarg;
if (*keychainName == '\0')
return SHOW_USAGE_MESSAGE;
break;
case '?':
default:
return SHOW_USAGE_MESSAGE;
}
}
argc -= optind;
argv += optind;
if (argc == 0)
return SHOW_USAGE_MESSAGE;
result = do_add_certificates(keychainName, argc, argv);
return result;
}