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