]> git.saurik.com Git - apple/security.git/blob - SecurityTool/macOS/identity_prefs.c
Security-59306.101.1.tar.gz
[apple/security.git] / SecurityTool / macOS / identity_prefs.c
1 /*
2 * Copyright (c) 2003-2010,2012,2014-2019 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * identity_prefs.c
24 */
25
26 #include "identity_prefs.h"
27 #include "identity_find.h"
28 #include "keychain_utilities.h"
29 #include "security_tool.h"
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <Security/cssmtype.h>
36 #include <Security/SecCertificate.h>
37 #include <Security/SecIdentity.h>
38 #include <Security/SecIdentitySearch.h>
39 #include <Security/SecItem.h>
40 #include <CommonCrypto/CommonDigest.h>
41
42 // SecCertificateInferLabel, SecDigestGetData
43 #include <Security/SecCertificatePriv.h>
44
45 static int
46 do_set_identity_preference(CFTypeRef keychainOrArray,
47 const char *identity,
48 const char *service,
49 CSSM_KEYUSE keyUsage,
50 const char *hash)
51 {
52 int result = 0;
53 CFStringRef serviceRef = NULL;
54 SecIdentityRef identityRef = NULL;
55
56 // must have a service name
57 if (!service) {
58 return SHOW_USAGE_MESSAGE;
59 }
60
61 // find identity (if specified by name or hash)
62 if (identity || hash) {
63 identityRef = CopyMatchingIdentity(keychainOrArray, identity, hash, keyUsage);
64 if (!identityRef) {
65 sec_error("No matching identity found for \"%s\"", (hash) ? hash : identity);
66 result = 1;
67 goto cleanup;
68 }
69 }
70
71 // set the identity preference
72 serviceRef = CFStringCreateWithCString(NULL, service, kCFStringEncodingUTF8);
73 result = SecIdentitySetPreference(identityRef, serviceRef, keyUsage);
74
75 cleanup:
76 if (identityRef)
77 CFRelease(identityRef);
78 if (serviceRef)
79 CFRelease(serviceRef);
80
81 return result;
82 }
83
84 typedef struct {
85 int i;
86 const char *name;
87 } ctk_print_context;
88
89 OSStatus ctk_dump_item(CFTypeRef item, ctk_print_context *ctx);
90
91 static int
92 do_get_identity_preference(const char *service,
93 CSSM_KEYUSE keyUsage,
94 Boolean printName,
95 Boolean printHash,
96 Boolean pemFormat)
97 {
98 int result = 0;
99 if (!service) {
100 return SHOW_USAGE_MESSAGE;
101 }
102 CFStringRef serviceRef = CFStringCreateWithCString(NULL, service, kCFStringEncodingUTF8);
103 SecCertificateRef certRef = NULL;
104 SecIdentityRef identityRef = NULL;
105 CSSM_DATA certData = { 0, NULL };
106
107 result = SecIdentityCopyPreference(serviceRef, keyUsage, NULL, &identityRef);
108 if (result) {
109 sec_perror("SecIdentityCopyPreference", result);
110 goto cleanup;
111 }
112 result = SecIdentityCopyCertificate(identityRef, &certRef);
113 if (result) {
114 sec_perror("SecIdentityCopyCertificate", result);
115 goto cleanup;
116 }
117 result = SecCertificateGetData(certRef, &certData);
118 if (result) {
119 sec_perror("SecCertificateGetData", result);
120 goto cleanup;
121 }
122
123 if (printName) {
124 char *nameBuf = NULL;
125 CFStringRef nameRef = NULL;
126 (void)SecCertificateCopyCommonName(certRef, &nameRef);
127 CFIndex nameLen = (nameRef) ? CFStringGetLength(nameRef) : 0;
128 if (nameLen > 0) {
129 CFIndex bufLen = 1 + CFStringGetMaximumSizeForEncoding(nameLen, kCFStringEncodingUTF8);
130 nameBuf = (char *)malloc(bufLen);
131 if (!CFStringGetCString(nameRef, nameBuf, bufLen-1, kCFStringEncodingUTF8))
132 nameBuf[0]=0;
133 }
134 fprintf(stdout, "common name: \"%s\"\n", (nameBuf && nameBuf[0] != 0) ? nameBuf : "<NULL>");
135 if (nameBuf)
136 free(nameBuf);
137 safe_CFRelease(&nameRef);
138 }
139
140 if (printHash) {
141 uint8_t digest[CC_SHA256_DIGEST_LENGTH];
142 unsigned int i;
143 if (certData.Data != NULL && certData.Length > 0) {
144 // print SHA-256 hash value
145 CC_SHA256(certData.Data, (CC_LONG)certData.Length, digest);
146 fprintf(stdout, "SHA-256 hash: ");
147 for (i=0; i<CC_SHA256_DIGEST_LENGTH; i++) {
148 fprintf(stdout, "%02X", ((unsigned char *)digest)[i]);
149 }
150 fprintf(stdout, "\n");
151 // print SHA-1 hash value for compatibility
152 CC_SHA1(certData.Data, (CC_LONG)certData.Length, digest);
153 fprintf(stdout, "SHA-1 hash: ");
154 for (i=0; i<CC_SHA1_DIGEST_LENGTH; i++) {
155 fprintf(stdout, "%02X", ((unsigned char *)digest)[i]);
156 }
157 fprintf(stdout, "\n");
158 }
159 }
160
161 if (pemFormat)
162 {
163 CSSM_DATA certData = { 0, NULL };
164 result = SecCertificateGetData(certRef, &certData);
165 if (result) {
166 sec_perror("SecCertificateGetData", result);
167 goto cleanup;
168 }
169
170 print_buffer_pem(stdout, "CERTIFICATE", certData.Length, certData.Data);
171 }
172 else
173 {
174 CFTypeRef keys[] = { kSecValueRef, kSecReturnAttributes };
175 CFTypeRef values[] = { certRef, kCFBooleanTrue };
176 CFDictionaryRef query = CFDictionaryCreate(kCFAllocatorDefault, keys, values, sizeof(keys) / sizeof(CFTypeRef), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
177 CFDictionaryRef attributes = NULL;
178 if (SecItemCopyMatching(query, (void *)&attributes) == errSecSuccess && CFDictionaryContainsKey(attributes, kSecAttrTokenID)) {
179 ctk_print_context ctx = { 1, "certificate" };
180 ctk_dump_item(attributes, &ctx);
181 } else
182 print_keychain_item_attributes(stdout, (SecKeychainItemRef)certRef, FALSE, FALSE, FALSE, FALSE);
183
184 CFRelease(query);
185 if(attributes)
186 CFRelease(attributes);
187 }
188
189 cleanup:
190 safe_CFRelease(&serviceRef);
191 safe_CFRelease(&certRef);
192 safe_CFRelease(&identityRef);
193
194 return result;
195 }
196
197 int
198 set_identity_preference(int argc, char * const *argv)
199 {
200 int ch, result = 0;
201 char *identity = NULL, *service = NULL, *hash = NULL;
202 CSSM_KEYUSE keyUsage = 0;
203 CFTypeRef keychainOrArray = NULL;
204
205 /*
206 * " -n Specify no identity (clears existing preference for service)\n"
207 * " -c Specify identity by common name of the certificate\n"
208 * " -s Specify service (URI, email address, DNS host, or other name)\n"
209 * " for which this identity is to be preferred\n"
210 * " -u Specify key usage (optional)\n"
211 * " -Z Specify identity by SHA-256 (or SHA-1) hash of certificate (optional)\n"
212 */
213
214 while ((ch = getopt(argc, argv, "hnc:s:u:Z:")) != -1)
215 {
216 switch (ch)
217 {
218 case 'n':
219 identity = NULL;
220 break;
221 case 'c':
222 identity = optarg;
223 break;
224 case 's':
225 service = optarg;
226 break;
227 case 'u':
228 keyUsage = atoi(optarg);
229 break;
230 case 'Z':
231 hash = optarg;
232 break;
233 case '?':
234 default:
235 result = SHOW_USAGE_MESSAGE;
236 goto cleanup;
237 }
238 }
239
240 argc -= optind;
241 argv += optind;
242
243 keychainOrArray = keychain_create_array(argc, argv);
244
245 result = do_set_identity_preference(keychainOrArray, identity, service, keyUsage, hash);
246
247 cleanup:
248 safe_CFRelease(&keychainOrArray);
249
250 return result;
251 }
252
253 int
254 get_identity_preference(int argc, char * const *argv)
255 {
256 int ch, result = 0;
257 char *service = NULL;
258 Boolean printName = FALSE, printHash = FALSE, pemFormat = FALSE;
259 CSSM_KEYUSE keyUsage = 0;
260
261 /*
262 * " -s Specify service (URI, email address, DNS host, or other name)\n"
263 * " -u Specify key usage (optional)\n"
264 * " -p Output identity certificate in pem format\n"
265 * " -c Print common name of the preferred identity certificate (optional)\n"
266 * " -Z Print SHA-256 (or SHA-1) hash of the preferred identity certificate (optional)\n"
267 */
268
269 while ((ch = getopt(argc, argv, "hs:u:pcZ")) != -1)
270 {
271 switch (ch)
272 {
273 case 'c':
274 printName = TRUE;
275 break;
276 case 'p':
277 pemFormat = TRUE;
278 break;
279 case 's':
280 service = optarg;
281 break;
282 case 'u':
283 keyUsage = atoi(optarg);
284 break;
285 case 'Z':
286 printHash = TRUE;
287 break;
288 case '?':
289 default:
290 result = 2; /* @@@ Return 2 triggers usage message. */
291 goto cleanup;
292 }
293 }
294
295 result = do_get_identity_preference(service, keyUsage, printName, printHash, pemFormat);
296
297 cleanup:
298
299 return result;
300 }
301