--- /dev/null
+/*
+ * Copyright (c) 2003-2019 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_delete.c
+ */
+
+#include "keychain_delete.h"
+#include "keychain_find.h"
+
+#include "keychain_utilities.h"
+#include "security_tool.h"
+#include <unistd.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecKeychain.h>
+#include <Security/SecKeychainItem.h>
+#include <Security/SecTrustSettings.h>
+
+static int
+do_delete(CFTypeRef keychainOrArray)
+{
+ /* @@@ SecKeychainDelete should really take a CFTypeRef argument. */
+ OSStatus result = SecKeychainDelete((SecKeychainRef)keychainOrArray);
+ if (result)
+ {
+ /* @@@ Add printing of keychainOrArray. */
+ sec_perror("SecKeychainDelete", result);
+ }
+
+ return result;
+}
+
+static int
+do_delete_certificate(CFTypeRef keychainOrArray, const char *name, const char *hash,
+ Boolean deleteTrust, Boolean deleteIdentity)
+{
+ OSStatus result = noErr;
+ SecKeychainItemRef itemToDelete = NULL;
+ if (!name && !hash) {
+ return SHOW_USAGE_MESSAGE;
+ }
+
+ itemToDelete = find_unique_certificate(keychainOrArray, name, hash);
+ if (itemToDelete) {
+ OSStatus status = noErr;
+ if (deleteTrust) {
+ status = SecTrustSettingsRemoveTrustSettings((SecCertificateRef)itemToDelete,
+ kSecTrustSettingsDomainUser);
+ if (status) {
+ // if trust settings do not exist, it's not an error.
+ if (status != errSecItemNotFound) {
+ result = status;
+ sec_perror("SecTrustSettingsRemoveTrustSettings (user)", result);
+ }
+ }
+ if (geteuid() == 0) {
+ status = SecTrustSettingsRemoveTrustSettings((SecCertificateRef)itemToDelete,
+ kSecTrustSettingsDomainAdmin);
+ if (status) {
+ if (status != errSecItemNotFound) {
+ result = status;
+ sec_perror("SecTrustSettingsRemoveTrustSettings (admin)", result);
+ }
+ }
+ }
+ }
+ if (!result && deleteIdentity) {
+ SecIdentityRef identity = NULL;
+ status = SecIdentityCreateWithCertificate(keychainOrArray,
+ (SecCertificateRef)itemToDelete,
+ &identity);
+ if (status) {
+ // if the private key doesn't exist, and we succeed in deleting
+ // the certificate, overall result will still be good.
+ if (status == errSecItemNotFound) {
+ status = noErr;
+ } else {
+ result = status;
+ }
+ } else {
+ SecKeyRef keyToDelete = NULL;
+ status = SecIdentityCopyPrivateKey(identity, &keyToDelete);
+ if (status) {
+ result = status;
+ } else {
+ result = SecKeychainItemDelete((SecKeychainItemRef)keyToDelete);
+ if (result) {
+ sec_perror("SecKeychainItemDelete", result);
+ }
+ }
+ safe_CFRelease(&keyToDelete);
+ }
+ safe_CFRelease(&identity);
+
+ if (status) {
+ fprintf(stderr, "Unable to obtain private key reference for \"%s\" (error %d)",
+ (name) ? name : (hash) ? hash : "", (int) status);
+ }
+ }
+ if (!result) {
+ result = SecKeychainItemDelete(itemToDelete);
+ if (result) {
+ sec_perror("SecKeychainItemDelete", result);
+ goto cleanup;
+ }
+ }
+ } else {
+ result = 1;
+ fprintf(stderr, "Unable to delete certificate matching \"%s\"",
+ (name) ? name : (hash) ? hash : "");
+ }
+
+cleanup:
+ safe_CFRelease(&itemToDelete);
+
+ return result;
+}
+
+static int
+keychain_delete_cert_common(int argc, char * const *argv, Boolean delete_identity)
+{
+ CFTypeRef keychainOrArray = NULL;
+ char *name = NULL;
+ char *hash = NULL;
+ Boolean delete_trust = FALSE;
+ int ch, result = 0;
+
+ while ((ch = getopt(argc, argv, "hc:Z:t")) != -1)
+ {
+ switch (ch)
+ {
+ case 'c':
+ name = optarg;
+ break;
+ case 'Z':
+ hash = optarg;
+ break;
+ case 't':
+ delete_trust = TRUE;
+ break;
+ case '?':
+ default:
+ result = 2; /* @@@ Return 2 triggers usage message. */
+ goto cleanup;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ keychainOrArray = keychain_create_array(argc, argv);
+
+ result = do_delete_certificate(keychainOrArray, name, hash, delete_trust, delete_identity);
+
+cleanup:
+ safe_CFRelease(&keychainOrArray);
+
+ return result;
+}
+
+int
+keychain_delete_certificate(int argc, char * const *argv)
+{
+ return keychain_delete_cert_common(argc, argv, FALSE);
+}
+
+int
+keychain_delete_identity(int argc, char * const *argv)
+{
+ return keychain_delete_cert_common(argc, argv, TRUE);
+}
+
+int
+keychain_delete(int argc, char * const *argv)
+{
+ CFTypeRef keychainOrArray = NULL;
+ int ch, result = 0;
+
+ while ((ch = getopt(argc, argv, "h")) != -1)
+ {
+ switch (ch)
+ {
+ case '?':
+ default:
+ return SHOW_USAGE_MESSAGE;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ keychainOrArray = keychain_create_array(argc, argv);
+
+ result = do_delete(keychainOrArray);
+ if (keychainOrArray)
+ CFRelease(keychainOrArray);
+
+ return result;
+}