]> git.saurik.com Git - apple/security.git/blob - OSX/sec/Security/Tool/scep.c
Security-58286.31.2.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 if (CFStringGetLength(query_string) > 1)
167 CFStringAppend(query_string, CFSTR("&"));
168
169 CFStringAppendFormat(query_string, NULL, CFSTR("%@=%@"), escaped_key, escaped_value);
170 CFRelease(escaped_key);
171 CFRelease(escaped_value);
172 }
173
174 static CF_RETURNS_RETAINED 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 CF_RETURNS_RETAINED 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 CFReleaseSafe(pki_op);
219 return scep_reply;
220 }
221
222
223 static inline CF_RETURNS_RETAINED 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 CFReleaseNull(scep_subject_name);
298 scep_subject_name = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
299 break;
300 case 'h':
301 scep_subject_alt_name = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
302 break;
303 case 'x':
304 validate_cert = false;
305 break;
306 case 'o':
307 scep_capabilities = CFStringCreateWithCString(kCFAllocatorDefault, optarg, kCFStringEncodingUTF8);
308 break;
309 default:
310 return 2; /* Trigger usage message. */
311 }
312 }
313
314 argc -= optind;
315 argv += optind;
316
317 if (argc != 1)
318 return 2; /* Trigger usage message. */
319
320 CFDataRef scep_request = NULL;
321 CFArrayRef issued_certs = NULL;
322 SecCertificateRef leaf = NULL;
323 SecIdentityRef candidate_identity = NULL;
324 CFMutableDictionaryRef csr_parameters = NULL;
325 CFDataRef scep_reply = NULL;
326 SecKeyRef phone_publicKey = NULL, phone_privateKey = NULL;
327 CFStringRef scep_base_url = NULL;
328 CFDictionaryRef identity_add = NULL;
329
330 CFNumberRef scep_key_usage = NULL;
331 CFNumberRef scep_key_bitsize = NULL;
332
333 CFURLRef url = NULL;
334
335 CFDataRef data = NULL;
336 CFStringRef ctype = NULL;
337
338 scep_base_url = CFStringCreateWithCString(kCFAllocatorDefault, argv[0], kCFStringEncodingASCII);
339
340 #if 0
341 CFStringRef uuid_cfstr = uuid_cfstring();
342 require(uuid_cfstr, out);
343 const void * ca_cn[] = { kSecOidCommonName, uuid_cfstr };
344 CFArrayRef ca_cn_dn = CFArrayCreate(kCFAllocatorDefault, ca_cn, 2, NULL);
345 const void *ca_dn_array[1];
346 ca_dn_array[0] = CFArrayCreate(kCFAllocatorDefault, (const void **)&ca_cn_dn, 1, NULL);
347 CFArrayRef scep_subject = CFArrayCreate(kCFAllocatorDefault, ca_dn_array, array_size(ca_dn_array), NULL);
348 #else
349 CFArrayRef scep_subject = make_scep_subject(scep_subject_name);
350 require(scep_subject, out);
351 CFShow(scep_subject);
352 #endif
353
354 scep_key_usage = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_usage);
355 scep_key_bitsize = CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &key_bitsize);
356
357 const void *keygen_keys[] = { kSecAttrKeyType, kSecAttrKeySizeInBits };
358 const void *keygen_vals[] = { kSecAttrKeyTypeRSA, scep_key_bitsize };
359 CFDictionaryRef keygen_parameters = CFDictionaryCreate(kCFAllocatorDefault,
360 keygen_keys, keygen_vals, array_size(keygen_vals),
361 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
362 CFRelease(scep_key_bitsize);
363 require_noerr(SecKeyGeneratePair(keygen_parameters, &phone_publicKey, &phone_privateKey), out);
364 CFRelease(keygen_parameters);
365
366 /* GetCACert
367
368 A binary X.509 CA certificate is sent back as a MIME object with a
369 Content-Type of application/x-x509-ca-cert.
370
371 When an RA exists, both CA and RA certificates must be sent back in
372 the response to the GetCACert request. The RA certificate(s) must be
373 signed by the CA. A certificates-only PKCS#7 [RFC2315] SignedData is
374 used to carry the certificates to the requester, with a Content-Type
375 of application/x-x509-ca-ra-cert.
376 */
377 SecCertificateRef ca_certificate = NULL, ra_certificate = NULL;
378 SecCertificateRef ra_encryption_certificate = NULL;
379 CFArrayRef ra_certificates = NULL;
380 CFTypeRef scep_certificates = NULL;
381 SecCertificateRef scep_signing_certificate = NULL;
382
383 url = scep_url_operation(scep_base_url, CFSTR("GetCACert"), scep_instance_name);
384 data = MCNetworkLoadRequest(url, NULL, NULL, &ctype, validate_cert);
385
386 if (data && ctype) {
387 if (CFEqual(CFSTR("application/x-x509-ca-cert"), ctype)) {
388 ca_certificate = SecCertificateCreateWithData(kCFAllocatorDefault, (CFDataRef)data);
389 fprintf(stderr, "GetCACert returned a single CA certificate.\n");
390 } else if (CFEqual(ctype, CFSTR("application/x-x509-ca-ra-cert"))) {
391 CFArrayRef cert_array = SecCMSCertificatesOnlyMessageCopyCertificates(data);
392
393 require_noerr(SecSCEPValidateCACertMessage(cert_array,
394 NULL,
395 &ca_certificate,
396 &ra_certificate,
397 &ra_encryption_certificate), out);
398
399 if (ra_certificate && ra_encryption_certificate) {
400 const void *ra_certs[] = { ra_encryption_certificate, ra_certificate };
401 ra_certificates = CFArrayCreate(kCFAllocatorDefault, ra_certs, array_size(ra_certs), &kCFTypeArrayCallBacks);
402 fprintf(stderr, "GetCACert returned a separate signing and encryption certificates for RA.\n");
403 }
404 CFRelease(cert_array);
405 }
406 }
407
408 fprintf(stderr, "CA certificate to issue cert:\n");
409 CFShow(ca_certificate);
410
411 if (ra_certificates) {
412 scep_certificates = ra_certificates;
413 scep_signing_certificate = ra_certificate;
414 } else if (ra_certificate) {
415 scep_certificates = ra_certificate;
416 scep_signing_certificate = ra_certificate;
417 } else if (ca_certificate) {
418 scep_certificates = ca_certificate;
419 scep_signing_certificate = ca_certificate;
420 } else {
421 fprintf(stderr, "Unsupported GetCACert configuration: please file a bug.\n");
422 goto out;
423 }
424
425 (void) scep_signing_certificate; // Silence analyzer
426
427 #if 0
428 GetCACaps capabilities advertised by SCEP server:
429
430 +--------------------+----------------------------------------------+
431 | Keyword | Description |
432 +--------------------+----------------------------------------------+
433 | "GetNextCACert" | CA Supports the GetNextCACert message. |
434 | "POSTPKIOperation" | PKIOPeration messages may be sent via HTTP |
435 | | POST. |
436 | "Renewal" | Clients may use current certificate and key |
437 | | to authenticate an enrollment request for a |
438 | | new certificate. |
439 | "SHA-512" | CA Supports the SHA-512 hashing algorithm in |
440 | | signatures and fingerprints. |
441 | "SHA-256" | CA Supports the SHA-256 hashing algorithm in |
442 | | signatures and fingerprints. |
443 | "SHA-1" | CA Supports the SHA-1 hashing algorithm in |
444 | | signatures and fingerprints. |
445 | "DES3" | CA Supports triple-DES for encryption. |
446 +--------------------+----------------------------------------------+
447 #endif
448
449 bool scep_can_use_post = false;
450 bool scep_use_3des = false;
451 bool scep_can_use_sha1 = false;
452 bool scep_can_use_sha512 = false;
453 bool scep_can_use_sha256 = false;
454
455 CFArrayRef caps = NULL;
456 if (!scep_capabilities) {
457 CFURLRef ca_caps_url = scep_url_operation(scep_base_url, CFSTR("GetCACaps"), scep_instance_name);
458 require(ca_caps_url, out);
459 CFDataRef caps_data = MCNetworkLoadRequest(ca_caps_url, NULL, NULL, NULL, validate_cert);
460 CFRelease(ca_caps_url);
461 if (caps_data) {
462 CFStringRef caps_data_string = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, caps_data, kCFStringEncodingASCII);
463 require(caps_data_string, out);
464 caps = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, caps_data_string, CFSTR("\n"));
465 if (!caps) {
466 fprintf(stderr, "GetCACaps couldn't be parsed:\n");
467 CFShow(caps_data);
468 }
469 CFRelease(caps_data);
470 CFRelease(caps_data_string);
471 }
472 } else {
473 caps = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, scep_capabilities, CFSTR(","));
474 }
475
476 if (caps) {
477 fprintf(stderr, "GetCACaps advertised following capabilities:\n");
478 CFShow(caps);
479
480 CFRange caps_length = CFRangeMake(0, CFArrayGetCount(caps));
481 scep_can_use_post = CFArrayContainsValue(caps, caps_length, CFSTR("POSTPKIOperation"));
482 scep_use_3des = CFArrayContainsValue(caps, caps_length, CFSTR("DES3"));
483 scep_can_use_sha1 = CFArrayContainsValue(caps, caps_length, CFSTR("SHA-1"));
484 scep_can_use_sha256 = CFArrayContainsValue(caps, caps_length, CFSTR("SHA-256"));
485 scep_can_use_sha512 = CFArrayContainsValue(caps, caps_length, CFSTR("SHA-512"));
486
487 // We probably inteded these to be the values and not override them below..
488 // but for now to quiet the analyzer we reference them here. see <rdar://problem/15010402> scep.c, command_scep assumes 3des and sha1
489 (void) scep_use_3des;
490 (void) scep_can_use_sha1;
491 CFRelease(caps);
492 }
493
494 scep_use_3des = true;
495 scep_can_use_sha1 = true;
496
497 csr_parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
498 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
499 if (scep_key_usage)
500 CFDictionarySetValue(csr_parameters, kSecCertificateKeyUsage, scep_key_usage);
501 if (scep_challenge)
502 CFDictionarySetValue(csr_parameters, kSecCSRChallengePassword, scep_challenge);
503 else
504 fprintf(stderr, "No SCEP challenge provided, hope that's ok.\n");
505
506 if (!scep_use_3des) {
507 CFDictionarySetValue(csr_parameters, kSecCMSBulkEncryptionAlgorithm, kSecCMSEncryptionAlgorithmDESCBC);
508 fprintf(stderr, "SCEP server does not support 3DES, falling back to DES. You should reconfigure your server.\n");
509 }
510
511 if (scep_can_use_sha512) {
512 CFDictionarySetValue(csr_parameters, kSecCMSSignHashAlgorithm, kSecCMSHashingAlgorithmSHA512);
513 } else if (scep_can_use_sha256) {
514 CFDictionarySetValue(csr_parameters, kSecCMSSignHashAlgorithm, kSecCMSHashingAlgorithmSHA256);
515 } else if (scep_can_use_sha1) {
516 CFDictionarySetValue(csr_parameters, kSecCMSSignHashAlgorithm, kSecCMSHashingAlgorithmSHA1);
517 } else {
518 fprintf(stderr, "SCEP server does not support SHA-1. You must reconfigure your server.\n");
519 }
520
521 if (scep_subject_alt_name) {
522 fprintf(stderr, "Adding subjectAltName to request\n");
523 CFStringRef name = CFSTR("dnsName");
524 CFDictionaryRef subject_alt_name = CFDictionaryCreate(kCFAllocatorDefault,
525 (const void **)&name, (const void **)&scep_subject_alt_name,
526 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
527 CFDictionarySetValue(csr_parameters, kSecSubjectAltName, subject_alt_name);
528 }
529
530 SecIdentityRef self_signed_identity = SecSCEPCreateTemporaryIdentity(phone_publicKey, phone_privateKey);
531
532 // store encryption identity in the keychain because the decrypt function looks in there only
533 identity_add = CFDictionaryCreate(NULL,
534 (const void **)&kSecValueRef, (const void **)&self_signed_identity, 1, NULL, NULL);
535 require_noerr(SecItemAdd(identity_add, NULL), out);
536
537 require(scep_request = SecSCEPGenerateCertificateRequest((CFArrayRef)scep_subject,
538 csr_parameters, phone_publicKey, phone_privateKey, self_signed_identity,
539 scep_certificates), out);
540
541 fprintf(stderr, "storing scep_request.der\n");
542 write_data("scep_request.der", scep_request);
543
544 scep_reply = perform_pki_op(scep_base_url, scep_request, scep_can_use_post, validate_cert);
545 require(scep_reply, out);
546
547 require_action(CFDataGetLength(scep_reply), out, fprintf(stderr, "Empty scep_reply, exiting.\n"));
548 fprintf(stderr, "Storing scep_reply.der\n");
549 write_data("scep_reply.der", scep_reply);
550
551 CFErrorRef server_error = NULL;
552 int retry_count = 3;
553 while ( !(issued_certs = SecSCEPVerifyReply(scep_request, scep_reply, scep_certificates, &server_error)) &&
554 server_error &&
555 retry_count--)
556 {
557 CFDataRef retry_get_cert_initial = NULL;
558 CFDictionaryRef error_dict = CFErrorCopyUserInfo(server_error);
559 retry_get_cert_initial = SecSCEPGetCertInitial(ra_certificate ? ra_certificate : ca_certificate, scep_subject, NULL, error_dict, self_signed_identity, scep_certificates);
560 CFReleaseNull(scep_reply);
561 CFReleaseSafe(error_dict);
562 fprintf(stderr, "Waiting 10 seconds before trying a GetCertInitial\n");
563 sleep(10);
564 scep_reply = perform_pki_op(scep_base_url, retry_get_cert_initial, scep_can_use_post, validate_cert);
565 CFReleaseSafe(retry_get_cert_initial);
566 }
567
568 require(issued_certs, out);
569 require_string(CFArrayGetCount(issued_certs) > 0, out, "No certificates issued.");
570
571 leaf = (SecCertificateRef)CFArrayGetValueAtIndex(issued_certs, 0);
572 require(leaf, out);
573 CFDataRef leaf_data = SecCertificateCopyData(leaf);
574 if (leaf_data) {
575 fprintf(stderr, "Storing issued_cert.der\n");
576 write_data("issued_cert.der", leaf_data);
577 CFRelease(leaf_data);
578 }
579 CFShow(leaf);
580
581 candidate_identity = SecIdentityCreate(kCFAllocatorDefault, leaf, phone_privateKey);
582
583 const void *keys_ref_to_persist[] = {
584 /*kSecReturnPersistentRef, */kSecValueRef, kSecAttrLabel };
585 const void *values_ref_to_persist[] = {
586 /*kCFBooleanTrue, */candidate_identity, scep_subject_name };
587 CFDictionaryRef dict = CFDictionaryCreate(NULL,
588 (const void **)keys_ref_to_persist,
589 (const void **)values_ref_to_persist,
590 array_size(keys_ref_to_persist),
591 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
592 OSStatus status = SecItemAdd(dict, NULL);
593 require_noerr_action(status, out, fprintf(stderr, "failed to store new identity, SecItemAdd: %" PRIdOSStatus, status));
594 result = 0;
595
596 out:
597 if (identity_add)
598 SecItemDelete(identity_add);
599 CFReleaseSafe(identity_add);
600 //if (uuid_cfstr) CFRelease(uuid_cfstr);
601 CFReleaseSafe(candidate_identity);
602 CFReleaseSafe(scep_request);
603 CFReleaseSafe(scep_reply);
604 CFReleaseSafe(scep_key_usage);
605 CFReleaseSafe(scep_key_bitsize);
606 CFReleaseSafe(csr_parameters);
607 CFReleaseSafe(scep_subject_name);
608 CFReleaseSafe(scep_base_url);
609 CFReleaseSafe(url);
610 CFReleaseSafe(issued_certs);
611 CFReleaseSafe(data);
612 CFReleaseSafe(ctype);
613
614 return result;
615 }
616
617
618 #endif // TARGET_OS_MAC