1/*
2 * Copyright (c) 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
5 *
6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 *
12 * 1. Redistributions of source code must retain the above copyright
13 *    notice, this list of conditions and the following disclaimer.
14 *
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * 3. Neither the name of the Institute nor the names of its contributors
20 *    may be used to endorse or promote products derived from this software
21 *    without specific prior written permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 */
35
36#include "hx_locl.h"
37
38#if defined(HAVE_FRAMEWORK_SECURITY)
39#include <Security/Security.h>
40
41#if defined(HAVE_CDSA)
42
43/* Missing function decls in pre Leopard */
44#ifdef NEED_SECKEYGETCSPHANDLE_PROTO
45OSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *);
46OSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG,
47			      int, const CSSM_ACCESS_CREDENTIALS **);
48#define kSecCredentialTypeDefault 0
49#define CSSM_SIZE uint32_t
50#endif
51
52#endif /* HAVE_CDSA */
53
54struct kc_rsa {
55    SecKeyRef pkey;
56    size_t keysize;
57};
58
59
60static int
61kc_rsa_public_encrypt(int flen,
62		      const unsigned char *from,
63		      unsigned char *to,
64		      RSA *rsa,
65		      int padding)
66{
67    return -1;
68}
69
70static int
71kc_rsa_public_decrypt(int flen,
72		      const unsigned char *from,
73		      unsigned char *to,
74		      RSA *rsa,
75		      int padding)
76{
77    return -1;
78}
79
80#if defined(HAVE_CDSA)
81
82/*
83 *
84 */
85
86static int
87kc_rsa_sign(int type, const unsigned char *from, unsigned int flen,
88	    unsigned char *to, unsigned int *tlen, const RSA *rsa)
89{
90    struct kc_rsa *kc = RSA_get_app_data(rk_UNCONST(rsa));
91
92    CSSM_RETURN cret;
93    OSStatus ret;
94    const CSSM_ACCESS_CREDENTIALS *creds;
95    SecKeyRef privKeyRef = kc->pkey;
96    CSSM_CSP_HANDLE cspHandle;
97    const CSSM_KEY *cssmKey;
98    CSSM_CC_HANDLE sigHandle = 0;
99    CSSM_DATA sig, in;
100    int fret = 0;
101    CSSM_ALGORITHMS stype;
102
103    if (type == NID_md5) {
104	stype = CSSM_ALGID_MD5;
105    } else if (type == NID_sha1) {
106	stype = CSSM_ALGID_SHA1;
107    } else if (type == NID_sha256) {
108	stype = CSSM_ALGID_SHA256;
109    } else if (type == NID_sha384) {
110	stype = CSSM_ALGID_SHA384;
111    } else if (type == NID_sha512) {
112	stype = CSSM_ALGID_SHA512;
113    } else
114	return -1;
115
116    cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
117    if(cret) heim_abort("SecKeyGetCSSMKey failed: %d", cret);
118
119    cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
120    if(cret) heim_abort("SecKeyGetCSPHandle failed: %d", cret);
121
122    ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN,
123			       kSecCredentialTypeNoUI, &creds);
124    if(ret) heim_abort("SecKeyGetCredentials failed: %d", (int)ret);
125
126    ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA,
127					  creds, cssmKey, &sigHandle);
128    if(ret) heim_abort("CSSM_CSP_CreateSignatureContext failed: %d", (int)ret);
129
130    in.Data = (uint8 *)from;
131    in.Length = flen;
132
133    sig.Data = (uint8 *)to;
134    sig.Length = kc->keysize;
135
136    cret = CSSM_SignData(sigHandle, &in, 1, stype, &sig);
137    if(cret) {
138	/* cssmErrorString(cret); */
139	fret = -1;
140    } else {
141	fret = 1;
142	*tlen = (unsigned int)sig.Length;
143    }
144
145    if(sigHandle)
146	CSSM_DeleteContext(sigHandle);
147
148    return fret;
149}
150
151static int
152kc_rsa_private_encrypt(int flen,
153		       const unsigned char *from,
154		       unsigned char *to,
155		       RSA *rsa,
156		       int padding)
157{
158    struct kc_rsa *kc = RSA_get_app_data(rsa);
159
160    CSSM_RETURN cret;
161    OSStatus ret;
162    const CSSM_ACCESS_CREDENTIALS *creds;
163    SecKeyRef privKeyRef = kc->pkey;
164    CSSM_CSP_HANDLE cspHandle;
165    const CSSM_KEY *cssmKey;
166    CSSM_CC_HANDLE sigHandle = 0;
167    CSSM_DATA sig, in;
168    int fret = 0;
169
170    if (padding != RSA_PKCS1_PADDING)
171	return -1;
172
173    cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
174    if(cret) heim_abort("SecKeyGetCSSMKey failed: %d", cret);
175
176    cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
177    if(cret) heim_abort("SecKeyGetCSPHandle failed: %d", cret);
178
179    ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN,
180			       kSecCredentialTypeNoUI, &creds);
181    if(ret) heim_abort("SecKeyGetCredentials failed: %d", (int)ret);
182
183    ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA,
184					  creds, cssmKey, &sigHandle);
185    if(ret) heim_abort("CSSM_CSP_CreateSignatureContext failed: %d", (int)ret);
186
187    in.Data = (uint8 *)from;
188    in.Length = flen;
189
190    sig.Data = (uint8 *)to;
191    sig.Length = kc->keysize;
192
193    cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig);
194    if(cret) {
195	/* cssmErrorString(cret); */
196	fret = -1;
197    } else
198	fret = (int)sig.Length;
199
200    if(sigHandle)
201	CSSM_DeleteContext(sigHandle);
202
203    return fret;
204}
205
206static int
207kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
208		       RSA * rsa, int padding)
209{
210    struct kc_rsa *kc = RSA_get_app_data(rsa);
211
212    CSSM_RETURN cret;
213    OSStatus ret;
214    const CSSM_ACCESS_CREDENTIALS *creds;
215    SecKeyRef privKeyRef = kc->pkey;
216    CSSM_CSP_HANDLE cspHandle;
217    const CSSM_KEY *cssmKey;
218    CSSM_CC_HANDLE handle = 0;
219    CSSM_DATA out, in, rem;
220    int fret = 0;
221    CSSM_SIZE outlen = 0;
222    char remdata[1024];
223
224    if (padding != RSA_PKCS1_PADDING)
225	return -1;
226
227    cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
228    if(cret) heim_abort("SecKeyGetCSSMKey failed: %d", (int)cret);
229
230    cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
231    if(cret) heim_abort("SecKeyGetCSPHandle failed: %d", (int)cret);
232
233    ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_DECRYPT,
234			       kSecCredentialTypeNoUI, &creds);
235    if(ret) heim_abort("SecKeyGetCredentials failed: %d", (int)ret);
236
237    ret = CSSM_CSP_CreateAsymmetricContext (cspHandle,
238					    CSSM_ALGID_RSA,
239					    creds,
240					    cssmKey,
241					    CSSM_PADDING_PKCS1,
242					    &handle);
243    if(ret) heim_abort("CSSM_CSP_CreateAsymmetricContext failed: %d", (int)ret);
244
245    in.Data = (uint8 *)from;
246    in.Length = flen;
247
248    out.Data = (uint8 *)to;
249    out.Length = kc->keysize;
250
251    rem.Data = (uint8 *)remdata;
252    rem.Length = sizeof(remdata);
253
254    cret = CSSM_DecryptData(handle, &in, 1, &out, 1, &outlen, &rem);
255    if(cret) {
256	/* cssmErrorString(cret); */
257	fret = -1;
258    } else
259	fret = (int)out.Length;
260
261    if(handle)
262	CSSM_DeleteContext(handle);
263
264    return fret;
265}
266
267#else
268
269static int
270kc_rsa_sign(int type, const unsigned char *from, unsigned int flen,
271	    unsigned char *to, unsigned int *tlen, const RSA *rsa)
272{
273    struct kc_rsa *kc = RSA_get_app_data(rk_UNCONST(rsa));
274    size_t sigLen = kc->keysize;
275    SecPadding padding;
276    OSStatus status;
277
278    if (type == NID_md5) {
279	padding = kSecPaddingPKCS1MD5;
280    } else if (type == NID_sha1) {
281	padding = kSecPaddingPKCS1SHA1;
282    } else
283	return -1;
284
285    status =  SecKeyRawSign(kc->pkey,
286			    padding,
287			    from,
288			    flen,
289			    to,
290			    &sigLen);
291    if (status)
292	return -2;
293
294    *tlen = (unsigned int)sigLen;
295
296    return 1;
297}
298
299
300static int
301kc_rsa_private_encrypt(int flen,
302		       const unsigned char *from,
303		       unsigned char *to,
304		       RSA *rsa,
305		       int padding)
306{
307    struct kc_rsa *kc = RSA_get_app_data(rsa);
308    size_t sigLen = kc->keysize;
309    OSStatus status;
310
311    if (padding != RSA_PKCS1_PADDING)
312	return -1;
313
314    status =  SecKeyRawSign(kc->pkey,
315			    kSecPaddingPKCS1,
316			    from,
317			    flen,
318			    to,
319			    &sigLen);
320    if (status)
321	return -2;
322
323    return (int)sigLen;
324}
325
326static int
327kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
328		       RSA * rsa, int padding)
329{
330    struct kc_rsa *kc = RSA_get_app_data(rsa);
331    OSStatus status;
332
333    if (padding != RSA_PKCS1_PADDING)
334	return -1;
335
336    status =  SecKeyRawVerify(kc->pkey,
337			      kSecPaddingPKCS1,
338			      from,
339			      flen,
340			      to,
341			      kc->keysize);
342    if (status)
343	return -2;
344
345    return (int)kc->keysize;
346}
347
348#endif /* HAVE_CDSA */
349
350
351static int
352kc_rsa_init(RSA *rsa)
353{
354    return 1;
355}
356
357static int
358kc_rsa_finish(RSA *rsa)
359{
360    struct kc_rsa *kc_rsa = RSA_get_app_data(rsa);
361    if (kc_rsa) {
362	CFRelease(kc_rsa->pkey);
363	free(kc_rsa);
364    }
365    return 1;
366}
367
368static const RSA_METHOD kc_rsa_pkcs1_method = {
369    "hx509 Keychain PKCS#1 RSA",
370    kc_rsa_public_encrypt,
371    kc_rsa_public_decrypt,
372    kc_rsa_private_encrypt,
373    kc_rsa_private_decrypt,
374    NULL,
375    NULL,
376    kc_rsa_init,
377    kc_rsa_finish,
378    0,
379    NULL,
380    kc_rsa_sign,
381    NULL
382};
383
384
385
386static int
387set_private_key(hx509_context context, hx509_cert cert, SecKeyRef pkey)
388{
389    const SubjectPublicKeyInfo *spi;
390    const Certificate *c;
391    struct kc_rsa *kc;
392    RSAPublicKey pk;
393    hx509_private_key key;
394    size_t size;
395    RSA *rsa;
396    int ret;
397
398    ret = hx509_private_key_init(&key, NULL, NULL);
399    if (ret)
400	return ret;
401
402    kc = calloc(1, sizeof(*kc));
403    if (kc == NULL)
404	_hx509_abort("out of memory");
405
406    CFRetain(pkey);
407    kc->pkey = pkey;
408
409    rsa = RSA_new();
410    if (rsa == NULL)
411	_hx509_abort("out of memory");
412
413    RSA_set_method(rsa, &kc_rsa_pkcs1_method);
414    ret = RSA_set_app_data(rsa, kc);
415    if (ret != 1)
416	_hx509_abort("RSA_set_app_data");
417
418    /*
419     * Set up n and e to please RSA_size()
420     */
421
422    c = _hx509_get_cert(cert);
423    spi = &c->tbsCertificate.subjectPublicKeyInfo;
424
425    ret = decode_RSAPublicKey(spi->subjectPublicKey.data,
426			      spi->subjectPublicKey.length / 8,
427			      &pk, &size);
428    if (ret) {
429	RSA_free(rsa);
430	return 0;
431    }
432    rsa->n = _hx509_int2BN(&pk.modulus);
433    rsa->e = _hx509_int2BN(&pk.publicExponent);
434    free_RSAPublicKey(&pk);
435
436    kc->keysize = BN_num_bytes(rsa->n);
437
438    /*
439     *
440     */
441
442    hx509_private_key_assign_rsa(key, rsa);
443    _hx509_cert_set_key(cert, key);
444
445    hx509_private_key_free(&key);
446
447    return 0;
448}
449
450/*
451 *
452 */
453
454struct ks_keychain {
455    int anchors;
456#ifndef __APPLE_TARGET_EMBEDDED__
457    SecKeychainRef keychain;
458#endif
459};
460
461static int
462keychain_init(hx509_context context,
463	      hx509_certs certs, void **data, int flags,
464	      const char *residue, hx509_lock lock)
465{
466    struct ks_keychain *ctx;
467
468    ctx = calloc(1, sizeof(*ctx));
469    if (ctx == NULL) {
470	hx509_clear_error_string(context);
471	return ENOMEM;
472    }
473
474    if (residue && residue[0]) {
475#ifndef __APPLE_TARGET_EMBEDDED__
476	if (strcasecmp(residue, "system-anchors") == 0) {
477	    ctx->anchors = 1;
478	} else if (strncasecmp(residue, "FILE:", 5) == 0) {
479	    OSStatus ret;
480
481	    ret = SecKeychainOpen(residue + 5, &ctx->keychain);
482	    if (ret != noErr) {
483		free(ctx);
484		hx509_set_error_string(context, 0, ENOENT,
485				       "Failed to open %s", residue);
486		return ENOENT;
487	    }
488	} else
489#endif
490	{
491	    free(ctx);
492	    hx509_set_error_string(context, 0, ENOENT,
493				   "Unknown subtype %s", residue);
494	    return ENOENT;
495	}
496    }
497
498    *data = ctx;
499    return 0;
500}
501
502/*
503 *
504 */
505
506static int
507keychain_free(hx509_certs certs, void *data)
508{
509    struct ks_keychain *ctx = data;
510    if (ctx) {
511#ifndef __APPLE_TARGET_EMBEDDED__
512        if (ctx->keychain)
513            CFRelease(ctx->keychain);
514#endif
515        memset(ctx, 0, sizeof(*ctx));
516        free(ctx);
517    }
518    return 0;
519}
520
521/*
522 *
523 */
524
525static int
526keychain_query(hx509_context context,
527	       hx509_certs certs,
528	       void *data,
529	       const hx509_query *query,
530	       hx509_cert *retcert)
531{
532    CFArrayRef identities = NULL;
533    hx509_cert cert = NULL;
534    CFIndex n, count;
535    int ret;
536    int kdcLookupHack = 0;
537
538    /*
539     * First to course filtering using security framework ....
540     */
541
542#define FASTER_FLAGS (HX509_QUERY_MATCH_PERSISTENT|HX509_QUERY_PRIVATE_KEY)
543
544    if ((query->match & FASTER_FLAGS) == 0)
545	return HX509_UNIMPLEMENTED_OPERATION;
546
547    CFMutableDictionaryRef secQuery = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks,  &kCFTypeDictionaryValueCallBacks);
548
549    /*
550     * XXX this is so broken, SecItem doesn't find the kdc certificte,
551     * and kdc certificates happend to be searched by friendly name,
552     * so find that and mundge on the structure.
553     */
554
555    if ((query->match & HX509_QUERY_MATCH_FRIENDLY_NAME) &&
556	(query->match & HX509_QUERY_PRIVATE_KEY) &&
557	strcmp(query->friendlyname, "O=System Identity,CN=com.apple.kerberos.kdc") == 0)
558    {
559	((hx509_query *)query)->match &= ~HX509_QUERY_PRIVATE_KEY;
560	kdcLookupHack = 1;
561    }
562
563    if (kdcLookupHack || (query->match & HX509_QUERY_MATCH_PERSISTENT)) {
564	CFDictionaryAddValue(secQuery, kSecClass, kSecClassCertificate);
565    } else
566	CFDictionaryAddValue(secQuery, kSecClass, kSecClassIdentity);
567
568    CFDictionaryAddValue(secQuery, kSecReturnRef, kCFBooleanTrue);
569    CFDictionaryAddValue(secQuery, kSecMatchLimit, kSecMatchLimitAll);
570
571    if (query->match & HX509_QUERY_MATCH_PERSISTENT) {
572	CFDataRef refdata = CFDataCreateWithBytesNoCopy(NULL, query->persistent->data, query->persistent->length, kCFAllocatorNull);
573	CFDictionaryAddValue(secQuery, kSecValuePersistentRef, refdata);
574	CFRelease(refdata);
575    }
576
577
578    OSStatus status = SecItemCopyMatching(secQuery, (CFTypeRef *)&identities);
579    CFRelease(secQuery);
580    if (status || identities == NULL) {
581	hx509_clear_error_string(context);
582	return HX509_CERT_NOT_FOUND;
583    }
584
585    heim_assert(CFArrayGetTypeID() == CFGetTypeID(identities), "return value not an array");
586
587    /*
588     * ... now do hx509 filtering
589     */
590
591    count = CFArrayGetCount(identities);
592    for (n = 0; n < count; n++) {
593	CFTypeRef secitem = (CFTypeRef)CFArrayGetValueAtIndex(identities, n);
594
595#ifndef __APPLE_TARGET_EMBEDDED__
596
597	if (query->match & HX509_QUERY_MATCH_PERSISTENT) {
598	    SecIdentityRef other = NULL;
599	    OSStatus osret;
600
601	    osret = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)secitem, &other);
602	    if (osret == noErr) {
603		ret = hx509_cert_init_SecFramework(context, (void *)other, &cert);
604		CFRelease(other);
605		if (ret)
606		    continue;
607	    } else {
608		ret = hx509_cert_init_SecFramework(context, (void *)secitem, &cert);
609		if (ret)
610		    continue;
611	    }
612	} else
613#endif
614        {
615
616	    ret = hx509_cert_init_SecFramework(context, (void *)secitem, &cert);
617	    if (ret)
618		continue;
619	}
620
621	if (_hx509_query_match_cert(context, query, cert)) {
622
623#ifndef __APPLE_TARGET_EMBEDDED__
624	    /* certtool/keychain doesn't glue togheter the cert with keys for system keys */
625	    if (kdcLookupHack) {
626		SecIdentityRef other = NULL;
627		OSStatus osret;
628
629		osret = SecIdentityCreateWithCertificate(NULL, (SecCertificateRef)secitem, &other);
630		if (osret == noErr) {
631		    hx509_cert_free(cert);
632		    ret = hx509_cert_init_SecFramework(context, other, &cert);
633		    CFRelease(other);
634		    if (ret)
635			continue;
636		}
637	    }
638#endif
639	    *retcert = cert;
640	    break;
641	}
642	hx509_cert_free(cert);
643    }
644
645    if (kdcLookupHack)
646	((hx509_query *)query)->match |= HX509_QUERY_PRIVATE_KEY;
647
648    CFRelease(identities);
649
650    if (*retcert == NULL) {
651	hx509_clear_error_string(context);
652	return HX509_CERT_NOT_FOUND;
653    }
654
655    return 0;
656}
657
658/*
659 *
660 */
661
662struct iter {
663    hx509_certs certs;
664    void *cursor;
665    CFArrayRef search;
666    CFIndex index;
667};
668
669static int
670keychain_iter_start(hx509_context context,
671		    hx509_certs certs, void *data, void **cursor)
672{
673#ifndef __APPLE_TARGET_EMBEDDED__
674    struct ks_keychain *ctx = data;
675#endif
676    struct iter *iter;
677
678    iter = calloc(1, sizeof(*iter));
679    if (iter == NULL) {
680	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
681	return ENOMEM;
682    }
683
684#ifndef __APPLE_TARGET_EMBEDDED__
685    if (ctx->anchors) {
686        CFArrayRef anchors;
687	int ret;
688	int i;
689
690	ret = hx509_certs_init(context, "MEMORY:ks-file-create",
691			       0, NULL, &iter->certs);
692	if (ret) {
693	    free(iter);
694	    return ret;
695	}
696
697	ret = SecTrustCopyAnchorCertificates(&anchors);
698	if (ret != 0) {
699	    hx509_certs_free(&iter->certs);
700	    free(iter);
701	    hx509_set_error_string(context, 0, ENOMEM,
702				   "Can't get trust anchors from Keychain");
703	    return ENOMEM;
704	}
705	for (i = 0; i < CFArrayGetCount(anchors); i++) {
706	    SecCertificateRef cr;
707	    hx509_cert cert;
708	    CFDataRef dataref;
709
710	    cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i);
711
712	    dataref = SecCertificateCopyData(cr);
713	    if (dataref == NULL)
714		continue;
715
716	    ret = hx509_cert_init_data(context, CFDataGetBytePtr(dataref), CFDataGetLength(dataref), &cert);
717	    CFRelease(dataref);
718	    if (ret)
719		continue;
720
721	    ret = hx509_certs_add(context, iter->certs, cert);
722	    hx509_cert_free(cert);
723	}
724	CFRelease(anchors);
725	if (ret != 0) {
726	    hx509_certs_free(&iter->certs);
727	    free(iter);
728	    hx509_set_error_string(context, 0, ret,
729				   "Failed to add cert");
730	    return ret;
731	}
732    }
733
734    if (iter->certs) {
735	int ret;
736	ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor);
737	if (ret) {
738	    hx509_certs_free(&iter->certs);
739	    free(iter);
740	    return ret;
741	}
742    } else
743#endif
744    {
745	OSStatus ret;
746	const void *keys[] = {
747	    kSecClass,
748	    kSecReturnRef,
749	    kSecMatchLimit
750	};
751	const void *values[] = {
752	    kSecClassCertificate,
753	    kCFBooleanTrue,
754	    kSecMatchLimitAll
755	};
756
757	CFDictionaryRef secQuery;
758
759	secQuery = CFDictionaryCreate(NULL, keys, values,
760				      sizeof(keys) / sizeof(*keys),
761				      &kCFTypeDictionaryKeyCallBacks,
762				      &kCFTypeDictionaryValueCallBacks);
763
764	ret = SecItemCopyMatching(secQuery, (CFTypeRef *)&iter->search);
765	CFRelease(secQuery);
766	if (ret) {
767	    free(iter);
768	    return ENOMEM;
769	}
770    }
771
772    *cursor = iter;
773    return 0;
774}
775
776/*
777 *
778 */
779
780static int
781keychain_iter(hx509_context context,
782	      hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
783{
784    struct iter *iter = cursor;
785    OSStatus ret = 0;
786
787    if (iter->certs)
788	return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert);
789
790    *cert = NULL;
791
792next:
793    if (iter->index < CFArrayGetCount(iter->search)) {
794
795	CFTypeRef secCert = CFArrayGetValueAtIndex(iter->search, iter->index);
796
797	ret = hx509_cert_init_SecFramework(context, (void *)secCert, cert);
798	iter->index++;
799	if (ret)
800	    goto next;
801    }
802    if (iter->index == CFArrayGetCount(iter->search))
803	return 0;
804
805    return ret;
806}
807
808/*
809 *
810 */
811
812static int
813keychain_iter_end(hx509_context context,
814		  hx509_certs certs,
815		  void *data,
816		  void *cursor)
817{
818    struct iter *iter = cursor;
819
820    if (iter->certs) {
821	hx509_certs_end_seq(context, iter->certs, iter->cursor);
822	hx509_certs_free(&iter->certs);
823    } else {
824	CFRelease(iter->search);
825    }
826
827    memset(iter, 0, sizeof(*iter));
828    free(iter);
829    return 0;
830}
831
832/*
833 *
834 */
835
836struct hx509_keyset_ops keyset_keychain = {
837    "KEYCHAIN",
838    0,
839    keychain_init,
840    NULL,
841    keychain_free,
842    NULL,
843    keychain_query,
844    keychain_iter_start,
845    keychain_iter,
846    keychain_iter_end
847};
848
849#endif /* HAVE_FRAMEWORK_SECURITY */
850
851/*
852 *
853 */
854
855void
856_hx509_ks_keychain_register(hx509_context context)
857{
858#if defined(HAVE_FRAMEWORK_SECURITY)
859    _hx509_ks_register(context, &keyset_keychain);
860#endif
861}
862
863static void
864kc_cert_release(hx509_cert cert, void *ctx)
865{
866    SecCertificateRef seccert = ctx;
867    CFRelease(seccert);
868}
869
870/*
871 *
872 */
873
874
875static void
876setPersistentRef(hx509_cert cert, SecCertificateRef itemRef)
877{
878#if !__APPLE_TARGET_EMBEDDED__
879    CFDataRef persistent;
880    OSStatus ret;
881
882    ret = SecKeychainItemCreatePersistentReference((SecKeychainItemRef)itemRef, &persistent);
883    if (ret == noErr) {
884	heim_octet_string os;
885
886	os.data = rk_UNCONST(CFDataGetBytePtr(persistent));
887	os.length = CFDataGetLength(persistent);
888
889	hx509_cert_set_persistent(cert, &os);
890	CFRelease(persistent);
891    }
892#endif
893}
894
895
896int
897hx509_cert_init_SecFramework(hx509_context context, void * identity,  hx509_cert *cert)
898{
899    CFTypeID typeid = CFGetTypeID(identity);
900    SecCertificateRef seccert;
901    CFTypeRef secdata = NULL;
902    SecKeyRef pkey = NULL;
903    CFDataRef data;
904    OSStatus osret;
905    hx509_cert c;
906    int ret;
907
908    *cert = NULL;
909
910    if (CFDataGetTypeID() == typeid) {
911	void const * keys[4] =  {
912	    kSecClass,
913	    kSecReturnRef,
914	    kSecMatchLimit,
915	    kSecValuePersistentRef
916	};
917	void const * values[4] = {
918	    kSecClassIdentity,
919	    kCFBooleanTrue,
920	    kSecMatchLimitOne,
921	    identity
922	};
923	CFDictionaryRef query;
924
925	assert(sizeof(keys) == sizeof(values));
926
927	query = CFDictionaryCreate(NULL, keys, values,
928				   sizeof(keys) / sizeof(*keys),
929				   &kCFTypeDictionaryKeyCallBacks,
930				   &kCFTypeDictionaryValueCallBacks);
931
932	osret = SecItemCopyMatching(query, &secdata);
933	CFRelease(query);
934	if (osret || secdata == NULL) {
935	    hx509_set_error_string(context, 0, HX509_CERTIFICATE_UNKNOWN_TYPE,
936				   "Failed to turn persistent reference into a certifiate: %d", (int)osret);
937	    return HX509_CERTIFICATE_UNKNOWN_TYPE;
938	}
939
940	typeid = CFGetTypeID(secdata);
941	identity = (void *)secdata;
942    }
943
944    if (SecIdentityGetTypeID() == typeid) {
945	osret = SecIdentityCopyCertificate(identity, &seccert);
946	if (osret) {
947	    if (secdata)
948		CFRelease(secdata);
949	    hx509_set_error_string(context, 0, HX509_CERTIFICATE_UNKNOWN_TYPE,
950				   "Failed to convert the identity to a certificate: %d", (int)osret);
951	    return HX509_CERTIFICATE_UNKNOWN_TYPE;
952	}
953    } else if (SecCertificateGetTypeID() == typeid) {
954	seccert = (SecCertificateRef)identity;
955	CFRetain(seccert);
956    } else {
957	if (secdata)
958	    CFRelease(secdata);
959	hx509_set_error_string(context, 0, HX509_CERTIFICATE_UNKNOWN_TYPE,
960			       "Data from persistent ref not a identity or certificate");
961	return HX509_CERTIFICATE_UNKNOWN_TYPE;
962    }
963
964    data = SecCertificateCopyData(seccert);
965    if (data == NULL) {
966	if (secdata)
967	    CFRelease(secdata);
968	CFRelease(seccert);
969	return ENOMEM;
970    }
971
972    ret = hx509_cert_init_data(context, CFDataGetBytePtr(data),
973			       CFDataGetLength(data), &c);
974    CFRelease(data);
975    if (ret) {
976	if (secdata)
977	    CFRelease(secdata);
978	CFRelease(seccert);
979	return ret;
980    }
981
982    /*
983     * Set Persistent identity
984     */
985
986    setPersistentRef(c, seccert);
987
988    /* if identity assign private key too */
989    if (SecIdentityGetTypeID() == typeid) {
990	(void)SecIdentityCopyPrivateKey(identity, &pkey);
991    }
992
993    if (pkey) {
994	set_private_key(context, c, pkey);
995	CFRelease(pkey);
996    }
997
998    _hx509_cert_set_release(c, kc_cert_release, seccert);
999
1000    if (secdata)
1001	CFRelease(secdata);
1002
1003    *cert = c;
1004
1005    return 0;
1006}
1007