--- /dev/null
+/*
+ * Copyright (c) 2003-2009,2011-2014 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_add.c
+ */
+
+#include "keychain_add.h"
+#include "keychain_find.h"
+#include "readline.h"
+#include "security.h"
+#include "access_utils.h"
+#include "keychain_utilities.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.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>
+
+// SecTrustedApplicationCreateApplicationGroup
+#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,
+ 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;
+ }
+
+ // build list of attributes
+ SecKeychainAttribute attrs[8]; // maximum number of attributes
+ 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 = strlen(kind);
+ attrs[attrList.count].data = (void *)kind;
+ attrList.count++;
+ }
+ if (value != NULL) {
+ attrs[attrList.count].tag = kSecGenericItemAttr;
+ attrs[attrList.count].length = strlen(value);
+ attrs[attrList.count].data = (void *)value;
+ attrList.count++;
+ }
+ if (comment != NULL) {
+ attrs[attrList.count].tag = kSecCommentItemAttr;
+ attrs[attrList.count].length = strlen(comment);
+ attrs[attrList.count].data = (void *)comment;
+ attrList.count++;
+ }
+ if (label != NULL) {
+ 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++;
+ }
+
+ // modify attributes and password data, if provided
+ status = SecKeychainItemModifyContent(itemRef, &attrList, (passwordData) ? strlen(passwordData) : 0, passwordData);
+ if (status) {
+ sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status));
+ }
+
+ // modify access, if provided
+ if (!status && access) {
+ SecAccessRef curAccess = NULL;
+ // for historical reasons, we have to modify the item's existing access reference
+ // (setting the item's access to a freshly created SecAccessRef instance doesn't behave as expected)
+ status = SecKeychainItemCopyAccess(itemRef, &curAccess);
+ if (status) {
+ sec_error("SecKeychainItemCopyAccess: %s", sec_errstr(status));
+ } else {
+ int result = merge_access(curAccess, access); // make changes to the existing access reference
+ if (result == noErr) {
+ status = SecKeychainItemSetAccess(itemRef, curAccess); // will prompt
+ 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,
+ SecAccessRef access,
+ Boolean update)
+{
+ SecKeychainRef keychain = NULL;
+ OSStatus result = 0;
+ SecKeychainItemRef itemRef = NULL;
+
+ // if update flag is specified, try to find and update an existing item
+ if (update) {
+ result = do_update_generic_password(keychainName,itemCreator,itemType,kind,value,comment,label,serviceName,accountName,passwordData,access);
+ if (result == noErr)
+ return result;
+ }
+
+ if (keychainName)
+ {
+ keychain = keychain_open(keychainName);
+ if (!keychain)
+ {
+ result = 1;
+ goto cleanup;
+ }
+ }
+
+ // set up attribute vector for new item (each attribute consists of {tag, length, pointer})
+ SecKeychainAttribute attrs[] = {
+ { kSecLabelItemAttr, label ? strlen(label) : 0, (char *)label },
+ { kSecDescriptionItemAttr, kind ? strlen(kind) : 0, (char *)kind },
+ { kSecGenericItemAttr, value ? strlen(value) : 0, (char *)value },
+ { kSecCommentItemAttr, comment ? strlen(comment) : 0, (char *)comment },
+ { kSecServiceItemAttr, serviceName ? strlen(serviceName) : 0, (char *)serviceName },
+ { kSecAccountItemAttr, accountName ? strlen(accountName) : 0, (char *)accountName },
+ { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL }, /* placeholder */
+ { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL } /* placeholder */
+ };
+ 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,
+ passwordData ? strlen(passwordData) : 0,
+ 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,
+ 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;
+ }
+
+ // build list of attributes
+ SecKeychainAttribute attrs[12]; // maximum number of attributes
+ 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 = strlen(kind);
+ attrs[attrList.count].data = (void *)kind;
+ attrList.count++;
+ }
+ if (comment != NULL) {
+ attrs[attrList.count].tag = kSecCommentItemAttr;
+ attrs[attrList.count].length = strlen(comment);
+ attrs[attrList.count].data = (void *)comment;
+ attrList.count++;
+ }
+ if (label != NULL) {
+ 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++;
+ }
+
+ // modify attributes and password data, if provided
+ status = SecKeychainItemModifyContent(itemRef, &attrList, (passwordData) ? strlen(passwordData) : 0, passwordData);
+ if (status) {
+ sec_error("SecKeychainItemModifyContent: %s", sec_errstr(status));
+ }
+
+ // modify access, if provided
+ 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,
+ SecAccessRef access,
+ Boolean update)
+{
+ SecKeychainRef keychain = NULL;
+ SecKeychainItemRef itemRef = NULL;
+ OSStatus result = 0;
+
+ // if update flag is specified, try to find and update an existing item
+ if (update) {
+ result = do_update_internet_password(keychainName,itemCreator,itemType,kind,comment,label,serverName,
+ securityDomain,accountName,path,port,protocol,authenticationType,
+ passwordData,access);
+ if (result == noErr)
+ return result;
+ }
+
+ if (keychainName)
+ {
+ keychain = keychain_open(keychainName);
+ if (!keychain)
+ {
+ result = 1;
+ goto cleanup;
+ }
+ }
+
+ // set up attribute vector for new item (each attribute consists of {tag, length, pointer})
+ SecKeychainAttribute attrs[] = {
+ { kSecLabelItemAttr, label ? strlen(label) : 0, (char *)label },
+ { kSecDescriptionItemAttr, kind ? strlen(kind) : 0, (char *)kind },
+ { kSecCommentItemAttr, comment ? strlen(comment) : 0, (char *)comment },
+ { kSecServerItemAttr, serverName ? strlen(serverName) : 0, (char *)serverName },
+ { kSecSecurityDomainItemAttr, securityDomain ? strlen(securityDomain) : 0, (char *)securityDomain },
+ { kSecAccountItemAttr, accountName ? strlen(accountName) : 0, (char *)accountName },
+ { kSecPathItemAttr, path ? 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 }, /* placeholder */
+ { (SecKeychainAttrType)0, sizeof(FourCharCode), NULL } /* placeholder */
+ };
+ 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,
+ passwordData ? strlen(passwordData) : 0,
+ 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;
+}
+
+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;
+ 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;
+
+ /*
+ * " -a Specify account name (required)\n"
+ * " -c Specify item creator (optional four-character code)\n"
+ * " -C Specify item type (optional four-character code)\n"
+ * " -D Specify kind (default is \"application password\")\n"
+ * " -G Specify generic attribute (optional)\n"
+ * " -j Specify comment string (optional)\n"
+ * " -l Specify label (if omitted, service name is used as default label)\n"
+ * " -s Specify service name (required)\n"
+ * " -p Specify password to be added (legacy option, equivalent to -w)\n"
+ * " -w Specify password to be added\n"
+ * " -A Allow any application to access this item without warning (insecure, not recommended!)\n"
+ * " -T Specify an application which may access this item (multiple -T options are allowed)\n"
+ * " -U Update item if it already exists (if omitted, the item cannot already exist)\n"
+ */
+
+ while ((ch = getopt(argc, argv, "a:c:C:D:G:j:l:s:p:w:UAT:")) != -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;
+ break;
+ case 'U':
+ update_item = TRUE;
+ break;
+ case 'A':
+ always_allow = TRUE;
+ access_specified = TRUE;
+ break;
+ case 'T':
+ {
+ if (optarg[0])
+ {
+ SecTrustedApplicationRef app = NULL;
+ /* check whether the argument specifies an application group */
+ 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 '?':
+ default:
+ result = 2;
+ goto cleanup; /* @@@ Return 2 triggers usage message. */
+ }
+ }
+
+ 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,
+ access,
+ update_item);
+
+cleanup:
+ 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;
+ 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;
+
+ /*
+ * " -a Specify account name (required)\n"
+ * " -c Specify item creator (optional four-character code)\n"
+ * " -C Specify item type (optional four-character code)\n"
+ * " -d Specify security domain string (optional)\n"
+ * " -D Specify kind (default is \"Internet password\")\n"
+ * " -j Specify comment string (optional)\n"
+ * " -l Specify label (if omitted, server name is used as default label)\n"
+ * " -p Specify path string (optional)\n"
+ * " -P Specify port number (optional)\n"
+ * " -r Specify protocol (optional four-character SecProtocolType, e.g. \"http\", \"ftp \")\n"
+ * " -s Specify server name (required)\n"
+ * " -t Specify authentication type (as a four-character SecAuthenticationType, default is \"dflt\")\n"
+ * " -w Specify password to be added\n"
+ * " -A Allow any application to access this item without warning (insecure, not recommended!)\n"
+ * " -T Specify an application which may access this item (multiple -T options are allowed)\n"
+ * " -U Update item if it already exists (if omitted, the item cannot already exist)\n"
+ */
+
+ while ((ch = getopt(argc, argv, "a:c:C:d:D:j:l:p:P:r:s:t:w: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;
+ /* auth type attribute is special */
+ authenticationType = OSSwapHostToBigInt32(authenticationType);
+ break;
+ case 'w':
+ passwordData = optarg;
+ break;
+ case 'U':
+ update_item = TRUE;
+ break;
+ case 'A':
+ always_allow = TRUE;
+ access_specified = TRUE;
+ break;
+ case 'T':
+ {
+ if (optarg[0])
+ {
+ SecTrustedApplicationRef app = NULL;
+ /* check whether the argument specifies an application group */
+ 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 '?':
+ default:
+ result = 2;
+ goto cleanup; /* @@@ Return 2 triggers usage message. */
+ }
+ }
+
+ 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,
+ access,
+ update_item);
+
+cleanup:
+ 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 2;
+ break;
+ case '?':
+ default:
+ return 2; /* @@@ Return 2 triggers usage message. */
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ return 2;
+
+ result = do_add_certificates(keychainName, argc, argv);
+
+ return result;
+}