]> git.saurik.com Git - apple/security.git/blobdiff - SecurityTool/macOS/identity_prefs.c
Security-59306.11.20.tar.gz
[apple/security.git] / SecurityTool / macOS / identity_prefs.c
diff --git a/SecurityTool/macOS/identity_prefs.c b/SecurityTool/macOS/identity_prefs.c
new file mode 100644 (file)
index 0000000..153aa48
--- /dev/null
@@ -0,0 +1,301 @@
+/*
+ * Copyright (c) 2003-2010,2012,2014-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@
+ *
+ * identity_prefs.c
+ */
+
+#include "identity_prefs.h"
+#include "identity_find.h"
+#include "keychain_utilities.h"
+#include "security_tool.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <Security/cssmtype.h>
+#include <Security/SecCertificate.h>
+#include <Security/SecIdentity.h>
+#include <Security/SecIdentitySearch.h>
+#include <Security/SecItem.h>
+#include <CommonCrypto/CommonDigest.h>
+
+// SecCertificateInferLabel, SecDigestGetData
+#include <Security/SecCertificatePriv.h>
+
+static int
+do_set_identity_preference(CFTypeRef keychainOrArray,
+       const char *identity,
+       const char *service,
+       CSSM_KEYUSE keyUsage,
+       const char *hash)
+{
+       int result = 0;
+       CFStringRef serviceRef = NULL;
+       SecIdentityRef identityRef = NULL;
+
+       // must have a service name
+       if (!service) {
+               return SHOW_USAGE_MESSAGE;
+       }
+
+       // find identity (if specified by name or hash)
+       if (identity || hash) {
+               identityRef = CopyMatchingIdentity(keychainOrArray, identity, hash, keyUsage);
+               if (!identityRef) {
+                       sec_error("No matching identity found for \"%s\"", (hash) ? hash : identity);
+                       result = 1;
+                       goto cleanup;
+               }
+       }
+
+       // set the identity preference
+       serviceRef = CFStringCreateWithCString(NULL, service, kCFStringEncodingUTF8);
+       result = SecIdentitySetPreference(identityRef, serviceRef, keyUsage);
+
+cleanup:
+       if (identityRef)
+               CFRelease(identityRef);
+       if (serviceRef)
+               CFRelease(serviceRef);
+
+       return result;
+}
+
+typedef struct {
+    int i;
+    const char *name;
+} ctk_print_context;
+
+OSStatus ctk_dump_item(CFTypeRef item, ctk_print_context *ctx);
+
+static int
+do_get_identity_preference(const char *service,
+       CSSM_KEYUSE keyUsage,
+       Boolean printName,
+       Boolean printHash,
+       Boolean pemFormat)
+{
+       int result = 0;
+       if (!service) {
+               return SHOW_USAGE_MESSAGE;
+       }
+       CFStringRef serviceRef = CFStringCreateWithCString(NULL, service, kCFStringEncodingUTF8);
+       SecCertificateRef certRef = NULL;
+       SecIdentityRef identityRef = NULL;
+       CSSM_DATA certData = { 0, NULL };
+
+       result = SecIdentityCopyPreference(serviceRef, keyUsage, NULL, &identityRef);
+       if (result) {
+               sec_perror("SecIdentityCopyPreference", result);
+               goto cleanup;
+       }
+       result = SecIdentityCopyCertificate(identityRef, &certRef);
+       if (result) {
+               sec_perror("SecIdentityCopyCertificate", result);
+               goto cleanup;
+       }
+       result = SecCertificateGetData(certRef, &certData);
+       if (result) {
+               sec_perror("SecCertificateGetData", result);
+               goto cleanup;
+       }
+
+       if (printName) {
+               char *nameBuf = NULL;
+               CFStringRef nameRef = NULL;
+               (void)SecCertificateCopyCommonName(certRef, &nameRef);
+               CFIndex nameLen = (nameRef) ? CFStringGetLength(nameRef) : 0;
+               if (nameLen > 0) {
+                       CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
+                       nameBuf = (char *)malloc(bufLen);
+                       if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8))
+                               nameBuf[0]=0;
+               }
+               fprintf(stdout, "common name: \"%s\"\n", (nameBuf && nameBuf[0] != 0) ? nameBuf : "<NULL>");
+               if (nameBuf)
+                       free(nameBuf);
+               safe_CFRelease(&nameRef);
+       }
+
+       if (printHash) {
+               uint8_t digest[CC_SHA256_DIGEST_LENGTH];
+               unsigned int i;
+               if (certData.Data != NULL && certData.Length > 0)  {
+                       // print SHA-256 hash value
+                       CC_SHA256(certData.Data, (CC_LONG)certData.Length, digest);
+                       fprintf(stdout, "SHA-256 hash: ");
+                       for (i=0; i<CC_SHA256_DIGEST_LENGTH; i++) {
+                               fprintf(stdout, "%02X", ((unsigned char *)digest)[i]);
+                       }
+                       fprintf(stdout, "\n");
+                       // print SHA-1 hash value for compatibility
+                       CC_SHA1(certData.Data, (CC_LONG)certData.Length, digest);
+                       fprintf(stdout, "SHA-1 hash: ");
+                       for (i=0; i<CC_SHA1_DIGEST_LENGTH; i++) {
+                               fprintf(stdout, "%02X", ((unsigned char *)digest)[i]);
+                       }
+                       fprintf(stdout, "\n");
+               }
+       }
+
+       if (pemFormat)
+       {
+               CSSM_DATA certData = { 0, NULL };
+               result = SecCertificateGetData(certRef, &certData);
+               if (result) {
+                       sec_perror("SecCertificateGetData", result);
+                       goto cleanup;
+               }
+
+               print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data);
+       }
+       else
+       {
+        CFTypeRef keys[] = { kSecValueRef, kSecReturnAttributes };
+        CFTypeRef values[] = { certRef, kCFBooleanTrue };
+        CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(CFTypeRef), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+        CFDictionaryRef attributes = NULL;
+        if (SecItemCopyMatching(query, (void *)&attributes) == errSecSuccess && CFDictionaryContainsKey(attributes, kSecAttrTokenID)) {
+            ctk_print_context ctx = { 1, "certificate" };
+            ctk_dump_item(attributes, &ctx);
+        } else
+            print_keychain_item_attributes(stdout, (SecKeychainItemRef)certRef, FALSE, FALSE, FALSE, FALSE);
+
+        CFRelease(query);
+        if(attributes)
+            CFRelease(attributes);
+       }
+
+cleanup:
+       safe_CFRelease(&serviceRef);
+       safe_CFRelease(&certRef);
+       safe_CFRelease(&identityRef);
+
+       return result;
+}
+
+int
+set_identity_preference(int argc, char * const *argv)
+{
+       int ch, result = 0;
+       char *identity = NULL, *service = NULL, *hash = NULL;
+       CSSM_KEYUSE keyUsage = 0;
+       CFTypeRef keychainOrArray = NULL;
+
+       /*
+        *      "    -n  Specify no identity (clears existing preference for service)\n"
+        *      "    -c  Specify identity by common name of the certificate\n"
+        *      "    -s  Specify service (URI, email address, DNS host, or other name)\n"
+        *  "        for which this identity is to be preferred\n"
+        *      "    -u  Specify key usage (optional)\n"
+        *      "    -Z  Specify identity by SHA-256 (or SHA-1) hash of certificate (optional)\n"
+        */
+
+       while ((ch = getopt(argc, argv, "hnc:s:u:Z:")) != -1)
+       {
+               switch  (ch)
+               {
+                       case 'n':
+                               identity = NULL;
+                               break;
+                       case 'c':
+                               identity = optarg;
+                               break;
+                       case 's':
+                               service = optarg;
+                               break;
+                       case 'u':
+                               keyUsage = atoi(optarg);
+                               break;
+                       case 'Z':
+                               hash = optarg;
+                               break;
+                       case '?':
+                       default:
+                               result = SHOW_USAGE_MESSAGE;
+                               goto cleanup;
+               }
+       }
+
+       argc -= optind;
+       argv += optind;
+
+    keychainOrArray = keychain_create_array(argc, argv);
+
+       result = do_set_identity_preference(keychainOrArray, identity, service, keyUsage, hash);
+
+cleanup:
+       safe_CFRelease(&keychainOrArray);
+
+       return result;
+}
+
+int
+get_identity_preference(int argc, char * const *argv)
+{
+       int ch, result = 0;
+       char *service = NULL;
+       Boolean printName = FALSE, printHash = FALSE, pemFormat = FALSE;
+       CSSM_KEYUSE keyUsage = 0;
+
+       /*
+        *      "    -s  Specify service (URI, email address, DNS host, or other name)\n"
+        *      "    -u  Specify key usage (optional)\n"
+        *  "    -p  Output identity certificate in pem format\n"
+        *      "    -c  Print common name of the preferred identity certificate (optional)\n"
+        *      "    -Z  Print SHA-256 (or SHA-1) hash of the preferred identity certificate (optional)\n"
+        */
+
+       while ((ch = getopt(argc, argv, "hs:u:pcZ")) != -1)
+       {
+               switch  (ch)
+               {
+                       case 'c':
+                               printName = TRUE;
+                               break;
+                       case 'p':
+                               pemFormat = TRUE;
+                               break;
+                       case 's':
+                               service = optarg;
+                               break;
+                       case 'u':
+                               keyUsage = atoi(optarg);
+                               break;
+                       case 'Z':
+                               printHash = TRUE;
+                               break;
+                       case '?':
+                       default:
+                               result = 2; /* @@@ Return 2 triggers usage message. */
+                               goto cleanup;
+               }
+       }
+
+       result = do_get_identity_preference(service, keyUsage, printName, printHash, pemFormat);
+
+cleanup:
+
+       return result;
+}
+