]> git.saurik.com Git - apple/security.git/blob - sec/Security/Tool/scep.c
Security-55471.14.8.tar.gz
[apple/security.git] / sec / Security / Tool / scep.c
1 /*
2 * Copyright (c) 2003-2004,2006-2007,2009-2010 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 * scep.c
24 */
25
26 #include <TargetConditionals.h>
27 #if TARGET_OS_EMBEDDED
28
29 #include "SecurityCommands.h"
30
31 #include <unistd.h>
32 #include <uuid/uuid.h>
33 #include <AssertMacros.h>
34
35 #include <Security/SecItem.h>
36 #include <Security/SecCertificateRequest.h>
37 #include <Security/SecCertificatePriv.h>
38 #include <Security/SecIdentityPriv.h>
39 #include <Security/SecSCEP.h>
40 #include <Security/SecCMS.h>
41
42 #include <utilities/array_size.h>
43 #include <utilities/SecIOFormat.h>
44
45 #include <CommonCrypto/CommonDigest.h>
46
47 #include <CFNetwork/CFNetwork.h>
48 #include "SecBase64.h"
49
50
51 #include <fcntl.h>
52 static inline void write_data(const char * path, CFDataRef data)
53 {
54 int data_file = open(path, O_CREAT|O_WRONLY|O_TRUNC, 0644);
55 write(data_file, CFDataGetBytePtr(data), CFDataGetLength(data));
56 close(data_file);
57 }
58
59 #define BUFSIZE 1024
60
61 static CFHTTPMessageRef load_request(CFHTTPMessageRef request, CFMutableDataRef data, int retry, bool validate_cert)
62 {
63 CFHTTPMessageRef result = NULL;
64
65 if (retry < 0)
66 return result;
67
68 CFReadStreamRef rs;
69 rs = CFReadStreamCreateForHTTPRequest(NULL, request);
70
71 if (!validate_cert) {
72 const void *keys[] = {
73 kCFStreamSSLValidatesCertificateChain,
74 };
75 const void *values[] = {
76 kCFBooleanFalse,
77 };
78 CFDictionaryRef dict = CFDictionaryCreate(NULL, keys, values,
79 array_size(keys),
80 &kCFTypeDictionaryKeyCallBacks,
81 &kCFTypeDictionaryValueCallBacks);
82 CFReadStreamSetProperty(rs, kCFStreamPropertySSLSettings, dict);
83 CFRelease(dict);
84 CFReadStreamSetProperty(rs, kCFStreamPropertyHTTPAttemptPersistentConnection, kCFBooleanTrue);
85 }
86
87 if (CFReadStreamOpen(rs)) {
88 do {
89 UInt8 buf[BUFSIZE];
90 CFIndex bytesRead = CFReadStreamRead(rs, buf, BUFSIZE);
91 if (bytesRead > 0) {
92 CFDataAppendBytes(data, buf, bytesRead);
93 } else if (bytesRead == 0) {
94 result = (CFHTTPMessageRef)CFReadStreamCopyProperty(rs, kCFStreamPropertyHTTPResponseHeader);
95 break;
96 } else {
97 CFStreamStatus status = CFReadStreamGetStatus(rs);
98 CFStreamError error = CFReadStreamGetError(rs);
99 fprintf(stderr, "CFReadStreamRead status=%ld error(domain=%" PRIdCFIndex " error=%ld)\n",
100 status, error.domain, (long) error.error);
101 break;
102 }
103 } while (true);
104 } else {
105 CFStreamStatus status = CFReadStreamGetStatus(rs);
106 CFStreamError error = CFReadStreamGetError(rs);
107 fprintf(stderr, "CFReadStreamRead status=%ld error(domain=%" PRIdCFIndex " error=%ld)\n",
108 status, error.domain, (long) error.error);
109 }
110
111 CFReadStreamClose(rs);
112 CFRelease(rs);
113 return result;
114 }
115
116 static CFDataRef MCNetworkLoadRequest(CFURLRef url, CFDataRef content, CFStringRef type,
117 CFStringRef *contentType, bool validate_cert)
118 {
119 CFMutableDataRef out_data = CFDataCreateMutable(kCFAllocatorDefault, 0);
120 CFHTTPMessageRef response = NULL;
121 CFStringRef request_type = content ? CFSTR("POST") : CFSTR("GET");
122 CFHTTPMessageRef request = CFHTTPMessageCreateRequest(kCFAllocatorDefault,
123 request_type, url, kCFHTTPVersion1_0);
124 if (content)
125 CFHTTPMessageSetBody(request, content);
126 CFHTTPMessageSetHeaderFieldValue(request, CFSTR("Content-Type"), type);
127
128 int retries = 1;
129 do {
130 response = load_request(request, out_data, 1, validate_cert);
131 if (!response && retries) {
132 sleep(5);
133 CFDataSetLength(out_data, 0);
134 }
135 } while (!response && retries--);
136
137 CFRelease(request);
138
139 CFIndex status_code = response ? CFHTTPMessageGetResponseStatusCode(response) : 0;
140 if (!response || (200 != status_code)) {
141 CFStringRef url_string = CFURLGetString(url);
142 if (url_string && request_type && out_data)
143 fprintf(stderr, "MCNetworkLoadRequest failed to load\n");
144 return NULL;
145 }
146
147 if (contentType)
148 *contentType = CFHTTPMessageCopyHeaderFieldValue(response, CFSTR("Content-Type"));
149
150 CFRelease(response);
151 return out_data;
152 }
153
154 static void _query_string_apply(CFMutableStringRef query_string, const void *key, const void *value)
155 {
156 CFStringRef escaped_key =
157 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
158 (CFStringRef)key, NULL, CFSTR("+/="), kCFStringEncodingUTF8);
159 CFStringRef escaped_value =
160 CFURLCreateStringByAddingPercentEscapes(kCFAllocatorDefault,
161 (CFStringRef)value, NULL, CFSTR("+/="), kCFStringEncodingUTF8);
162
163 CFStringRef format;
164 if (CFStringGetLength(query_string) > 1)
165 format = CFSTR("&%@=%@");
166 else
167 format = CFSTR("%@=%@");
168
169 CFStringAppendFormat(query_string, NULL, format, escaped_key, escaped_value);
170 CFRelease(escaped_key);
171 CFRelease(escaped_value);
172 }
173
174 static CFURLRef scep_url_operation(CFStringRef base, CFStringRef operation, CFStringRef message)
175 {
176 CFURLRef url = NULL, base_url = NULL;
177 CFMutableStringRef query_string =
178 CFStringCreateMutableCopy(kCFAllocatorDefault, 0, CFSTR("?"));
179 require(query_string, out);
180 require(operation, out);
181 _query_string_apply(query_string, CFSTR("operation"), operation);
182 if (message)
183 _query_string_apply(query_string, CFSTR("message"), message);
184 base_url = CFURLCreateWithString(kCFAllocatorDefault, base, NULL);
185 url = CFURLCreateWithString(kCFAllocatorDefault, query_string, base_url);
186 out:
187 if (query_string)
188 CFRelease(query_string);
189 if (base_url)
190 CFRelease(base_url);
191 return url;
192 }
193
194 static CFDataRef perform_pki_op(CFStringRef scep_base_url, CFDataRef scep_request, bool scep_can_use_post, bool validate_cert)
195 {
196 CFDataRef scep_reply = NULL;
197 CFURLRef pki_op = NULL;
198 if (scep_can_use_post) {
199 pki_op = scep_url_operation(scep_base_url, CFSTR("PKIOperation"), NULL);
200 scep_reply = MCNetworkLoadRequest(pki_op, scep_request, CFSTR("application/x-pki-message"), NULL, validate_cert);
201 } else {
202 SecBase64Result base64_result;
203 size_t buffer_length = CFDataGetLength(scep_request)*2+1;
204 char *buffer = malloc(buffer_length);
205 require(buffer, out);
206 size_t output_size = SecBase64Encode2(CFDataGetBytePtr(scep_request), CFDataGetLength(scep_request), buffer, buffer_length,
207 kSecB64_F_LINE_LEN_INFINITE, -1, &base64_result);
208 *(buffer + output_size) = '\0';
209 require(!base64_result, out);
210 CFStringRef message = CFStringCreateWithCString(kCFAllocatorDefault, buffer, kCFStringEncodingASCII);
211 require(message, out);
212 pki_op = scep_url_operation(scep_base_url, CFSTR("PKIOperation"), message);
213 CFRelease(message);
214 fprintf(stderr, "Performing PKIOperation using GET\n");
215 scep_reply = MCNetworkLoadRequest(pki_op, NULL, CFSTR("application/x-pki-message"), NULL, validate_cert);
216 }
217 out:
218 if (pki_op) CFRelease(pki_op);
219 return scep_reply;
220 }
221
222
223 static inline CFStringRef uuid_cfstring()
224 {
225 char uuid_string[40] = "CN=";
226 uuid_t uuid;
227 uuid_generate_random(uuid);
228 uuid_unparse(uuid, uuid_string+3);
229 return CFStringCreateWithCString(kCFAllocatorDefault, uuid_string, kCFStringEncodingASCII);
230 }
231
232 /* /O=foo/CN=blah => [ [ [ O, foo ] ], [ [ CN, blah ] ] ] */
233 static void make_subject_pairs(const void *value, void *context)
234 {
235 CFArrayRef entries = NULL, array = NULL;
236 if (!CFStringGetLength(value))
237 return; /* skip '/'s that aren't separating key/vals */
238 entries = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, value, CFSTR("="));
239 require(entries, out);
240 if (CFArrayGetCount(entries)) {
241 array = CFArrayCreate(kCFAllocatorDefault, (const void **)&entries, 1, &kCFTypeArrayCallBacks);
242 require(array, out);
243 CFArrayAppendValue((CFMutableArrayRef)context, array);
244 }
245 out:
246 if (entries) CFRelease(entries);
247 if (array) CFRelease(array);
248 }
249
250 static CFArrayRef make_scep_subject(CFStringRef scep_subject_name)
251 {
252 CFMutableArrayRef subject = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
253 require(subject, out);
254 CFArrayRef entries = NULL;
255 entries = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, scep_subject_name, CFSTR("/"));
256 require(entries, out);
257 CFArrayApplyFunction(entries, CFRangeMake(0, CFArrayGetCount(entries)), make_subject_pairs, subject);
258 CFRelease(entries);
259 if (CFArrayGetCount(subject))
260 return subject;
261 out:
262 if (subject) CFRelease(subject);
263 return NULL;
264 }
265
266
267 extern int command_scep(int argc, char * const *argv)
268 {
269 int result = 1, verbose = 0;
270 char ch;
271 int key_usage = 1, key_bitsize = 1024;
272 bool validate_cert = true;
273 CFStringRef scep_challenge = NULL, scep_instance_name = NULL,
274 scep_subject_name = uuid_cfstring(), scep_subject_alt_name = NULL,
275 scep_capabilities = NULL;
276
277 while ((ch = getopt(argc, argv, "vu:b:c:n:s:h:xo:")) != -1)
278 {
279 switch (ch)
280 {
281 case 'v':
282 verbose++;
283 break;
284 case 'u':
285 key_usage = atoi(optarg);
286 break;
287 case 'b':
288 key_bitsize = atoi(optarg);
289 break;
290 case 'c':
291 scep_challenge = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
292 break;
293 case 'n':
294 scep_instance_name = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
295 break;
296 case 's':
297 scep_subject_name = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
298 break;
299 case 'h':
300 scep_subject_alt_name = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
301 break;
302 case 'x':
303 validate_cert = false;
304 break;
305 case 'o':
306 scep_capabilities = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
307 break;
308 default:
309 return 2; /* Trigger usage message. */
310 }
311 }
312
313 argc -= optind;
314 argv += optind;
315
316 if (argc != 1)
317 return 2; /* Trigger usage message. */
318
319 CFDataRef scep_request = NULL;
320 CFArrayRef issued_certs = NULL;
321 SecCertificateRef leaf = NULL;
322 SecIdentityRef candidate_identity = NULL;
323 CFMutableDictionaryRef csr_parameters = NULL;
324 CFDataRef scep_reply = NULL;
325 SecKeyRef phone_publicKey = NULL, phone_privateKey = NULL;
326 CFStringRef scep_base_url = NULL;
327 CFDictionaryRef identity_add = NULL;
328
329 scep_base_url = CFStringCreateWithCString(kCFAllocatorDefault, argv[0], kCFStringEncodingASCII);
330
331 #if 0
332 CFStringRef uuid_cfstr = uuid_cfstring();
333 require(uuid_cfstr, out);
334 const void * ca_cn[] = { kSecOidCommonName, uuid_cfstr };
335 CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
336 const void *ca_dn_array[1];
337 ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL);
338 CFArrayRef scep_subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, array_size(ca_dn_array), NULL);
339 #else
340 CFArrayRef scep_subject = make_scep_subject(scep_subject_name);
341 require(scep_subject, out);
342 CFShow(scep_subject);
343 #endif
344
345 CFNumberRef scep_key_usage = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_usage);
346 CFNumberRef scep_key_bitsize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_bitsize);
347
348 const void *keygen_keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits };
349 const void *keygen_vals[] = { kSecAttrKeyTypeRSA, scep_key_bitsize };
350 CFDictionaryRef keygen_parameters = CFDictionaryCreate(kCFAllocatorDefault,
351 keygen_keys, keygen_vals, array_size(keygen_vals),
352 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
353 CFRelease(scep_key_bitsize);
354 require_noerr(SecKeyGeneratePair(keygen_parameters, &phone_publicKey, &phone_privateKey), out);
355 CFRelease(keygen_parameters);
356
357 /* GetCACert
358
359 A binary X.509 CA certificate is sent back as a MIME object with a
360 Content-Type of application/x-x509-ca-cert.
361
362 When an RA exists, both CA and RA certificates must be sent back in
363 the response to the GetCACert request. The RA certificate(s) must be
364 signed by the CA. A certificates-only PKCS#7 [RFC2315] SignedData is
365 used to carry the certificates to the requester, with a Content-Type
366 of application/x-x509-ca-ra-cert.
367 */
368 CFDataRef data = NULL;
369 CFStringRef ctype = NULL;
370 SecCertificateRef ca_certificate = NULL, ra_certificate = NULL;
371 SecCertificateRef ra_encryption_certificate = NULL;
372 CFArrayRef ra_certificates = NULL;
373 CFTypeRef scep_certificates = NULL;
374 SecCertificateRef scep_signing_certificate = NULL;
375
376 CFURLRef url = scep_url_operation(scep_base_url, CFSTR("GetCACert"), scep_instance_name);
377 data = MCNetworkLoadRequest(url, NULL, NULL, &ctype, validate_cert);
378
379 if (data && ctype) {
380 if (CFEqual(CFSTR("application/x-x509-ca-cert"), ctype)) {
381 ca_certificate = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)data);
382 fprintf(stderr, "GetCACert returned a single CA certificate.\n");
383 } else if (CFEqual(ctype, CFSTR("application/x-x509-ca-ra-cert"))) {
384 CFArrayRef cert_array = SecCMSCertificatesOnlyMessageCopyCertificates(data);
385
386 require_noerr(SecSCEPValidateCACertMessage(cert_array,
387 NULL,
388 &ca_certificate,
389 &ra_certificate,
390 &ra_encryption_certificate), out);
391
392 if (ra_certificate && ra_encryption_certificate) {
393 const void *ra_certs[] = { ra_encryption_certificate, ra_certificate };
394 ra_certificates = CFArrayCreate(kCFAllocatorDefault, ra_certs, array_size(ra_certs), &kCFTypeArrayCallBacks);
395 fprintf(stderr, "GetCACert returned a separate signing and encryption certificates for RA.\n");
396 }
397 CFRelease(cert_array);
398 }
399 }
400
401 fprintf(stderr, "CA certificate to issue cert:\n");
402 CFShow(ca_certificate);
403
404 if (ra_certificates) {
405 scep_certificates = ra_certificates;
406 scep_signing_certificate = ra_certificate;
407 } else if (ra_certificate) {
408 scep_certificates = ra_certificate;
409 scep_signing_certificate = ra_certificate;
410 } else if (ca_certificate) {
411 scep_certificates = ca_certificate;
412 scep_signing_certificate = ca_certificate;
413 } else {
414 fprintf(stderr, "Unsupported GetCACert configuration: please file a bug.\n");
415 goto out;
416 }
417
418 #if 0
419 GetCACaps capabilities advertised by SCEP server:
420
421 +--------------------+----------------------------------------------+
422 | Keyword | Description |
423 +--------------------+----------------------------------------------+
424 | "GetNextCACert" | CA Supports the GetNextCACert message. |
425 | "POSTPKIOperation" | PKIOPeration messages may be sent via HTTP |
426 | | POST. |
427 | "Renewal" | Clients may use current certificate and key |
428 | | to authenticate an enrollment request for a |
429 | | new certificate. |
430 | "SHA-512" | CA Supports the SHA-512 hashing algorithm in |
431 | | signatures and fingerprints. |
432 | "SHA-256" | CA Supports the SHA-256 hashing algorithm in |
433 | | signatures and fingerprints. |
434 | "SHA-1" | CA Supports the SHA-1 hashing algorithm in |
435 | | signatures and fingerprints. |
436 | "DES3" | CA Supports triple-DES for encryption. |
437 +--------------------+----------------------------------------------+
438 #endif
439
440 bool scep_can_use_post = false;
441 bool scep_use_3des = false;
442 bool scep_can_use_sha1 = false;
443
444 CFArrayRef caps = NULL;
445 if (!scep_capabilities) {
446 CFURLRef ca_caps_url = scep_url_operation(scep_base_url, CFSTR("GetCACaps"), scep_instance_name);
447 require(ca_caps_url, out);
448 CFDataRef caps_data = MCNetworkLoadRequest(ca_caps_url, NULL, NULL, NULL, validate_cert);
449 CFRelease(ca_caps_url);
450 if (caps_data) {
451 CFStringRef caps_data_string = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, caps_data, kCFStringEncodingASCII);
452 require(caps_data_string, out);
453 caps = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, caps_data_string, CFSTR("\n"));
454 if (!caps) {
455 fprintf(stderr, "GetCACaps couldn't be parsed:\n");
456 CFShow(caps_data);
457 }
458 CFRelease(caps_data);
459 }
460 } else {
461 caps = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, scep_capabilities, CFSTR(","));
462 }
463
464 if (caps) {
465 fprintf(stderr, "GetCACaps advertised following capabilities:\n");
466 CFShow(caps);
467
468 CFRange caps_length = CFRangeMake(0, CFArrayGetCount(caps));
469 scep_can_use_post = CFArrayContainsValue(caps, caps_length, CFSTR("POSTPKIOperation"));
470 scep_use_3des = CFArrayContainsValue(caps, caps_length, CFSTR("DES3"));
471 scep_can_use_sha1 = CFArrayContainsValue(caps, caps_length, CFSTR("SHA-1"));
472 }
473
474 scep_use_3des = true;
475 scep_can_use_sha1 = true;
476
477 csr_parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
478 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
479 if (scep_key_usage)
480 CFDictionarySetValue(csr_parameters, kSecCertificateKeyUsage, scep_key_usage);
481 if (scep_challenge)
482 CFDictionarySetValue(csr_parameters, kSecCSRChallengePassword, scep_challenge);
483 else
484 fprintf(stderr, "No SCEP challenge provided, hope that's ok.\n");
485
486 if (!scep_use_3des) {
487 CFDictionarySetValue(csr_parameters, kSecCMSBulkEncryptionAlgorithm, kSecCMSEncryptionAlgorithmDESCBC);
488 fprintf(stderr, "SCEP server does not support 3DES, falling back to DES. You should reconfigure your server.\n");
489 }
490 if (!scep_can_use_sha1) {
491 CFDictionarySetValue(csr_parameters, kSecCMSSignHashAlgorithm, kSecCMSHashingAlgorithmMD5);
492 fprintf(stderr, "SCEP server does not support SHA-1, falling back to MD5. You should reconfigure your server.\n");
493 }
494
495 if (scep_subject_alt_name) {
496 fprintf(stderr, "Adding subjectAltName to request\n");
497 CFStringRef name = CFSTR("dnsName");
498 CFDictionaryRef subject_alt_name = CFDictionaryCreate(kCFAllocatorDefault,
499 (const void **)&name, (const void **)&scep_subject_alt_name,
500 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
501 CFDictionarySetValue(csr_parameters, kSecSubjectAltName, subject_alt_name);
502 }
503
504 SecIdentityRef self_signed_identity = SecSCEPCreateTemporaryIdentity(phone_publicKey, phone_privateKey);
505
506 // store encryption identity in the keychain because the decrypt function looks in there only
507 identity_add = CFDictionaryCreate(NULL,
508 &kSecValueRef, (const void **)&self_signed_identity, 1, NULL, NULL);
509 require_noerr(SecItemAdd(identity_add, NULL), out);
510
511 require(scep_request = SecSCEPGenerateCertificateRequest((CFArrayRef)scep_subject,
512 csr_parameters, phone_publicKey, phone_privateKey, self_signed_identity,
513 scep_certificates), out);
514
515 fprintf(stderr, "storing scep_request.der\n");
516 write_data("scep_request.der", scep_request);
517
518 scep_reply = perform_pki_op(scep_base_url, scep_request, scep_can_use_post, validate_cert);
519 require(scep_reply, out);
520
521 require_action(CFDataGetLength(scep_reply), out, fprintf(stderr, "Empty scep_reply, exiting.\n"));
522 fprintf(stderr, "Storing scep_reply.der\n");
523 write_data("scep_reply.der", scep_reply);
524
525 CFErrorRef server_error = NULL;
526 int retry_count = 3;
527 while ( !(issued_certs = SecSCEPVerifyReply(scep_request, scep_reply, scep_certificates, &server_error)) &&
528 server_error &&
529 retry_count--)
530 {
531 CFDataRef retry_get_cert_initial = NULL;
532 CFDictionaryRef error_dict = CFErrorCopyUserInfo(server_error);
533 retry_get_cert_initial = SecSCEPGetCertInitial(ra_certificate ? ra_certificate : ca_certificate, scep_subject, NULL, error_dict, self_signed_identity, scep_certificates);
534 if (scep_reply) CFRelease(scep_reply);
535 fprintf(stderr, "Waiting 10 seconds before trying a GetCertInitial\n");
536 sleep(10);
537 scep_reply = perform_pki_op(scep_base_url, retry_get_cert_initial, scep_can_use_post, validate_cert);
538 }
539
540 require(issued_certs, out);
541 require_string(CFArrayGetCount(issued_certs) > 0, out, "No certificates issued.");
542
543 leaf = (SecCertificateRef)CFArrayGetValueAtIndex(issued_certs, 0);
544 require(leaf, out);
545 CFDataRef leaf_data = SecCertificateCopyData(leaf);
546 if (leaf_data) {
547 fprintf(stderr, "Storing issued_cert.der\n");
548 write_data("issued_cert.der", leaf_data);
549 CFRelease(leaf_data);
550 }
551 CFShow(leaf);
552
553 candidate_identity = SecIdentityCreate(kCFAllocatorDefault, leaf, phone_privateKey);
554
555 const void *keys_ref_to_persist[] = {
556 /*kSecReturnPersistentRef, */kSecValueRef, kSecAttrLabel };
557 const void *values_ref_to_persist[] = {
558 /*kCFBooleanTrue, */candidate_identity, scep_subject_name };
559 CFDictionaryRef dict = CFDictionaryCreate(NULL,
560 (const void **)keys_ref_to_persist,
561 (const void **)values_ref_to_persist,
562 array_size(keys_ref_to_persist),
563 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
564 OSStatus status = SecItemAdd(dict, NULL);
565 require_noerr_action(status, out, fprintf(stderr, "failed to store new identity, SecItemAdd: %" PRIdOSStatus, status));
566 result = 0;
567
568 out:
569 SecItemDelete(identity_add);
570 if (identity_add) CFRelease(identity_add);
571 //if (uuid_cfstr) CFRelease(uuid_cfstr);
572 if (candidate_identity) CFRelease(candidate_identity);
573 if (scep_request) CFRelease(scep_request);
574 if (scep_reply) CFRelease(scep_reply);
575 if (csr_parameters) CFRelease(csr_parameters);
576
577 return result;
578 }
579
580
581 #endif // TARGET_OS_MAC