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>
53static 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
62static 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
117static 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"));
152out:
153    CFReleaseSafe(response);
154    return out_data;
155}
156
157static 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
177static 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);
189out:
190    if (query_string)
191        CFRelease(query_string);
192    if (base_url)
193        CFRelease(base_url);
194    return url;
195}
196
197static 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    }
220out:
221	CFReleaseSafe(pki_op);
222	return scep_reply;
223}
224
225
226static inline 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 ] ] ] */
236static 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    }
248out:
249    if (entries) CFRelease(entries);
250    if (array) CFRelease(array);
251}
252
253static 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;
264out:
265    if (subject) CFRelease(subject);
266    return NULL;
267}
268
269
270extern 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
456    CFArrayRef caps = NULL;
457    if (!scep_capabilities) {
458        CFURLRef ca_caps_url = scep_url_operation(scep_base_url, CFSTR("GetCACaps"), scep_instance_name);
459        require(ca_caps_url, out);
460        CFDataRef caps_data = MCNetworkLoadRequest(ca_caps_url, NULL, NULL, NULL, validate_cert);
461        CFRelease(ca_caps_url);
462        if (caps_data) {
463            CFStringRef caps_data_string = CFStringCreateFromExternalRepresentation(kCFAllocatorDefault, caps_data, kCFStringEncodingASCII);
464            require(caps_data_string, out);
465            caps = CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault, caps_data_string, CFSTR("\n"));
466            if (!caps) {
467                fprintf(stderr, "GetCACaps couldn't be parsed:\n");
468                CFShow(caps_data);
469            }
470            CFRelease(caps_data);
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
485        // We probably inteded these to be the values and not override them below..
486        // but for now to quiet the analyzer we reference them here. see <rdar://problem/15010402> scep.c, command_scep assumes 3des and sha1
487        (void) scep_use_3des;
488        (void) scep_can_use_sha1;
489    }
490
491    scep_use_3des = true;
492    scep_can_use_sha1 = true;
493
494    csr_parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
495        &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
496    if (scep_key_usage)
497        CFDictionarySetValue(csr_parameters, kSecCertificateKeyUsage, scep_key_usage);
498    if (scep_challenge)
499        CFDictionarySetValue(csr_parameters, kSecCSRChallengePassword, scep_challenge);
500    else
501        fprintf(stderr, "No SCEP challenge provided, hope that's ok.\n");
502
503    if (!scep_use_3des) {
504        CFDictionarySetValue(csr_parameters, kSecCMSBulkEncryptionAlgorithm, kSecCMSEncryptionAlgorithmDESCBC);
505        fprintf(stderr, "SCEP server does not support 3DES, falling back to DES.  You should reconfigure your server.\n");
506    }
507    if (!scep_can_use_sha1) {
508        CFDictionarySetValue(csr_parameters, kSecCMSSignHashAlgorithm, kSecCMSHashingAlgorithmMD5);
509        fprintf(stderr, "SCEP server does not support SHA-1, falling back to MD5.  You should reconfigure your server.\n");
510    }
511
512    if (scep_subject_alt_name) {
513        fprintf(stderr, "Adding subjectAltName to request\n");
514        CFStringRef name = CFSTR("dnsName");
515        CFDictionaryRef subject_alt_name = CFDictionaryCreate(kCFAllocatorDefault,
516            (const void **)&name, (const void **)&scep_subject_alt_name,
517            1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
518        CFDictionarySetValue(csr_parameters, kSecSubjectAltName, subject_alt_name);
519    }
520
521    SecIdentityRef self_signed_identity = SecSCEPCreateTemporaryIdentity(phone_publicKey, phone_privateKey);
522
523    // store encryption identity in the keychain because the decrypt function looks in there only
524    identity_add = CFDictionaryCreate(NULL,
525        &kSecValueRef, (const void **)&self_signed_identity, 1, NULL, NULL);
526	require_noerr(SecItemAdd(identity_add, NULL), out);
527
528    require(scep_request = SecSCEPGenerateCertificateRequest((CFArrayRef)scep_subject,
529        csr_parameters, phone_publicKey, phone_privateKey, self_signed_identity,
530        scep_certificates), out);
531
532    fprintf(stderr, "storing scep_request.der\n");
533    write_data("scep_request.der", scep_request);
534
535	scep_reply = perform_pki_op(scep_base_url, scep_request, scep_can_use_post, validate_cert);
536    require(scep_reply, out);
537
538    require_action(CFDataGetLength(scep_reply), out, fprintf(stderr, "Empty scep_reply, exiting.\n"));
539    fprintf(stderr, "Storing scep_reply.der\n");
540    write_data("scep_reply.der", scep_reply);
541
542	CFErrorRef server_error = NULL;
543	int retry_count = 3;
544    while ( !(issued_certs = SecSCEPVerifyReply(scep_request, scep_reply, scep_certificates, &server_error)) &&
545			server_error &&
546			retry_count--)
547	{
548		CFDataRef retry_get_cert_initial = NULL;
549		CFDictionaryRef error_dict = CFErrorCopyUserInfo(server_error);
550		retry_get_cert_initial = SecSCEPGetCertInitial(ra_certificate ? ra_certificate : ca_certificate, scep_subject, NULL, error_dict, self_signed_identity, scep_certificates);
551		CFReleaseNull(scep_reply);
552        CFReleaseSafe(error_dict);
553		fprintf(stderr, "Waiting 10 seconds before trying a GetCertInitial\n");
554		sleep(10);
555		scep_reply = perform_pki_op(scep_base_url, retry_get_cert_initial, scep_can_use_post, validate_cert);
556        CFReleaseSafe(retry_get_cert_initial);
557	}
558
559    require(issued_certs, out);
560	require_string(CFArrayGetCount(issued_certs) > 0, out, "No certificates issued.");
561
562    leaf = (SecCertificateRef)CFArrayGetValueAtIndex(issued_certs, 0);
563    require(leaf, out);
564    CFDataRef leaf_data = SecCertificateCopyData(leaf);
565    if (leaf_data) {
566        fprintf(stderr, "Storing issued_cert.der\n");
567        write_data("issued_cert.der", leaf_data);
568        CFRelease(leaf_data);
569    }
570    CFShow(leaf);
571
572    candidate_identity = SecIdentityCreate(kCFAllocatorDefault, leaf, phone_privateKey);
573
574    const void *keys_ref_to_persist[] = {
575        /*kSecReturnPersistentRef, */kSecValueRef, kSecAttrLabel };
576    const void *values_ref_to_persist[] = {
577        /*kCFBooleanTrue, */candidate_identity, scep_subject_name };
578    CFDictionaryRef dict = CFDictionaryCreate(NULL,
579            (const void **)keys_ref_to_persist,
580            (const void **)values_ref_to_persist,
581            array_size(keys_ref_to_persist),
582            &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
583    OSStatus status = SecItemAdd(dict, NULL);
584    require_noerr_action(status, out, fprintf(stderr, "failed to store new identity, SecItemAdd: %" PRIdOSStatus, status));
585    result = 0;
586
587out:
588    SecItemDelete(identity_add);
589    CFReleaseSafe(identity_add);
590    //if (uuid_cfstr) CFRelease(uuid_cfstr);
591    CFReleaseSafe(candidate_identity);
592    CFReleaseSafe(scep_request);
593    CFReleaseSafe(scep_reply);
594    CFReleaseSafe(scep_key_usage);
595    CFReleaseSafe(scep_key_bitsize);
596    CFReleaseSafe(csr_parameters);
597    CFReleaseSafe(scep_subject_name);
598    CFReleaseSafe(scep_base_url);
599    CFReleaseSafe(url);
600    CFReleaseSafe(issued_certs);
601    CFReleaseSafe(data);
602    CFReleaseSafe(ctype);
603
604    return result;
605}
606
607
608#endif // TARGET_OS_MAC
609