1/*	$NetBSD$	*/
2
3/*
4 * Copyright (c) 2007 Kungliga Tekniska H��gskolan
5 * (Royal Institute of Technology, Stockholm, Sweden).
6 * 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#ifdef HAVE_FRAMEWORK_SECURITY
39
40#include <Security/Security.h>
41
42/* Missing function decls in pre Leopard */
43#ifdef NEED_SECKEYGETCSPHANDLE_PROTO
44OSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *);
45OSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG,
46			      int, const CSSM_ACCESS_CREDENTIALS **);
47#define kSecCredentialTypeDefault 0
48#define CSSM_SIZE uint32_t
49#endif
50
51
52static int
53getAttribute(SecKeychainItemRef itemRef, SecItemAttr item,
54	     SecKeychainAttributeList **attrs)
55{
56    SecKeychainAttributeInfo attrInfo;
57    UInt32 attrFormat = 0;
58    OSStatus ret;
59
60    *attrs = NULL;
61
62    attrInfo.count = 1;
63    attrInfo.tag = &item;
64    attrInfo.format = &attrFormat;
65
66    ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
67					       attrs, NULL, NULL);
68    if (ret)
69	return EINVAL;
70    return 0;
71}
72
73
74/*
75 *
76 */
77
78struct kc_rsa {
79    SecKeychainItemRef item;
80    size_t keysize;
81};
82
83
84static int
85kc_rsa_public_encrypt(int flen,
86		      const unsigned char *from,
87		      unsigned char *to,
88		      RSA *rsa,
89		      int padding)
90{
91    return -1;
92}
93
94static int
95kc_rsa_public_decrypt(int flen,
96		      const unsigned char *from,
97		      unsigned char *to,
98		      RSA *rsa,
99		      int padding)
100{
101    return -1;
102}
103
104
105static int
106kc_rsa_private_encrypt(int flen,
107		       const unsigned char *from,
108		       unsigned char *to,
109		       RSA *rsa,
110		       int padding)
111{
112    struct kc_rsa *kc = RSA_get_app_data(rsa);
113
114    CSSM_RETURN cret;
115    OSStatus ret;
116    const CSSM_ACCESS_CREDENTIALS *creds;
117    SecKeyRef privKeyRef = (SecKeyRef)kc->item;
118    CSSM_CSP_HANDLE cspHandle;
119    const CSSM_KEY *cssmKey;
120    CSSM_CC_HANDLE sigHandle = 0;
121    CSSM_DATA sig, in;
122    int fret = 0;
123
124    if (padding != RSA_PKCS1_PADDING)
125	return -1;
126
127    cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
128    if(cret) abort();
129
130    cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
131    if(cret) abort();
132
133    ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN,
134			       kSecCredentialTypeDefault, &creds);
135    if(ret) abort();
136
137    ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA,
138					  creds, cssmKey, &sigHandle);
139    if(ret) abort();
140
141    in.Data = (uint8 *)from;
142    in.Length = flen;
143
144    sig.Data = (uint8 *)to;
145    sig.Length = kc->keysize;
146
147    cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig);
148    if(cret) {
149	/* cssmErrorString(cret); */
150	fret = -1;
151    } else
152	fret = sig.Length;
153
154    if(sigHandle)
155	CSSM_DeleteContext(sigHandle);
156
157    return fret;
158}
159
160static int
161kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
162		       RSA * rsa, int padding)
163{
164    struct kc_rsa *kc = RSA_get_app_data(rsa);
165
166    CSSM_RETURN cret;
167    OSStatus ret;
168    const CSSM_ACCESS_CREDENTIALS *creds;
169    SecKeyRef privKeyRef = (SecKeyRef)kc->item;
170    CSSM_CSP_HANDLE cspHandle;
171    const CSSM_KEY *cssmKey;
172    CSSM_CC_HANDLE handle = 0;
173    CSSM_DATA out, in, rem;
174    int fret = 0;
175    CSSM_SIZE outlen = 0;
176    char remdata[1024];
177
178    if (padding != RSA_PKCS1_PADDING)
179	return -1;
180
181    cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
182    if(cret) abort();
183
184    cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
185    if(cret) abort();
186
187    ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_DECRYPT,
188			       kSecCredentialTypeDefault, &creds);
189    if(ret) abort();
190
191
192    ret = CSSM_CSP_CreateAsymmetricContext (cspHandle,
193					    CSSM_ALGID_RSA,
194					    creds,
195					    cssmKey,
196					    CSSM_PADDING_PKCS1,
197					    &handle);
198    if(ret) abort();
199
200    in.Data = (uint8 *)from;
201    in.Length = flen;
202
203    out.Data = (uint8 *)to;
204    out.Length = kc->keysize;
205
206    rem.Data = (uint8 *)remdata;
207    rem.Length = sizeof(remdata);
208
209    cret = CSSM_DecryptData(handle, &in, 1, &out, 1, &outlen, &rem);
210    if(cret) {
211	/* cssmErrorString(cret); */
212	fret = -1;
213    } else
214	fret = out.Length;
215
216    if(handle)
217	CSSM_DeleteContext(handle);
218
219    return fret;
220}
221
222static int
223kc_rsa_init(RSA *rsa)
224{
225    return 1;
226}
227
228static int
229kc_rsa_finish(RSA *rsa)
230{
231    struct kc_rsa *kc_rsa = RSA_get_app_data(rsa);
232    CFRelease(kc_rsa->item);
233    memset(kc_rsa, 0, sizeof(*kc_rsa));
234    free(kc_rsa);
235    return 1;
236}
237
238static const RSA_METHOD kc_rsa_pkcs1_method = {
239    "hx509 Keychain PKCS#1 RSA",
240    kc_rsa_public_encrypt,
241    kc_rsa_public_decrypt,
242    kc_rsa_private_encrypt,
243    kc_rsa_private_decrypt,
244    NULL,
245    NULL,
246    kc_rsa_init,
247    kc_rsa_finish,
248    0,
249    NULL,
250    NULL,
251    NULL
252};
253
254static int
255set_private_key(hx509_context context,
256		SecKeychainItemRef itemRef,
257		hx509_cert cert)
258{
259    struct kc_rsa *kc;
260    hx509_private_key key;
261    RSA *rsa;
262    int ret;
263
264    ret = hx509_private_key_init(&key, NULL, NULL);
265    if (ret)
266	return ret;
267
268    kc = calloc(1, sizeof(*kc));
269    if (kc == NULL)
270	_hx509_abort("out of memory");
271
272    kc->item = itemRef;
273
274    rsa = RSA_new();
275    if (rsa == NULL)
276	_hx509_abort("out of memory");
277
278    /* Argh, fake modulus since OpenSSL API is on crack */
279    {
280	SecKeychainAttributeList *attrs = NULL;
281	uint32_t size;
282	void *data;
283
284	rsa->n = BN_new();
285	if (rsa->n == NULL) abort();
286
287	ret = getAttribute(itemRef, kSecKeyKeySizeInBits, &attrs);
288	if (ret) abort();
289
290	size = *(uint32_t *)attrs->attr[0].data;
291	SecKeychainItemFreeAttributesAndData(attrs, NULL);
292
293	kc->keysize = (size + 7) / 8;
294
295	data = malloc(kc->keysize);
296	memset(data, 0xe0, kc->keysize);
297	BN_bin2bn(data, kc->keysize, rsa->n);
298	free(data);
299    }
300    rsa->e = NULL;
301
302    RSA_set_method(rsa, &kc_rsa_pkcs1_method);
303    ret = RSA_set_app_data(rsa, kc);
304    if (ret != 1)
305	_hx509_abort("RSA_set_app_data");
306
307    hx509_private_key_assign_rsa(key, rsa);
308    _hx509_cert_assign_key(cert, key);
309
310    return 0;
311}
312
313/*
314 *
315 */
316
317struct ks_keychain {
318    int anchors;
319    SecKeychainRef keychain;
320};
321
322static int
323keychain_init(hx509_context context,
324	      hx509_certs certs, void **data, int flags,
325	      const char *residue, hx509_lock lock)
326{
327    struct ks_keychain *ctx;
328
329    ctx = calloc(1, sizeof(*ctx));
330    if (ctx == NULL) {
331	hx509_clear_error_string(context);
332	return ENOMEM;
333    }
334
335    if (residue) {
336	if (strcasecmp(residue, "system-anchors") == 0) {
337	    ctx->anchors = 1;
338	} else if (strncasecmp(residue, "FILE:", 5) == 0) {
339	    OSStatus ret;
340
341	    ret = SecKeychainOpen(residue + 5, &ctx->keychain);
342	    if (ret != noErr) {
343		hx509_set_error_string(context, 0, ENOENT,
344				       "Failed to open %s", residue);
345		return ENOENT;
346	    }
347	} else {
348	    hx509_set_error_string(context, 0, ENOENT,
349				   "Unknown subtype %s", residue);
350	    return ENOENT;
351	}
352    }
353
354    *data = ctx;
355    return 0;
356}
357
358/*
359 *
360 */
361
362static int
363keychain_free(hx509_certs certs, void *data)
364{
365    struct ks_keychain *ctx = data;
366    if (ctx->keychain)
367	CFRelease(ctx->keychain);
368    memset(ctx, 0, sizeof(*ctx));
369    free(ctx);
370    return 0;
371}
372
373/*
374 *
375 */
376
377struct iter {
378    hx509_certs certs;
379    void *cursor;
380    SecKeychainSearchRef searchRef;
381};
382
383static int
384keychain_iter_start(hx509_context context,
385		    hx509_certs certs, void *data, void **cursor)
386{
387    struct ks_keychain *ctx = data;
388    struct iter *iter;
389
390    iter = calloc(1, sizeof(*iter));
391    if (iter == NULL) {
392	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
393	return ENOMEM;
394    }
395
396    if (ctx->anchors) {
397        CFArrayRef anchors;
398	int ret;
399	int i;
400
401	ret = hx509_certs_init(context, "MEMORY:ks-file-create",
402			       0, NULL, &iter->certs);
403	if (ret) {
404	    free(iter);
405	    return ret;
406	}
407
408	ret = SecTrustCopyAnchorCertificates(&anchors);
409	if (ret != 0) {
410	    hx509_certs_free(&iter->certs);
411	    free(iter);
412	    hx509_set_error_string(context, 0, ENOMEM,
413				   "Can't get trust anchors from Keychain");
414	    return ENOMEM;
415	}
416	for (i = 0; i < CFArrayGetCount(anchors); i++) {
417	    SecCertificateRef cr;
418	    hx509_cert cert;
419	    CSSM_DATA cssm;
420
421	    cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i);
422
423	    SecCertificateGetData(cr, &cssm);
424
425	    ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert);
426	    if (ret)
427		continue;
428
429	    ret = hx509_certs_add(context, iter->certs, cert);
430	    hx509_cert_free(cert);
431	}
432	CFRelease(anchors);
433    }
434
435    if (iter->certs) {
436	int ret;
437	ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor);
438	if (ret) {
439	    hx509_certs_free(&iter->certs);
440	    free(iter);
441	    return ret;
442	}
443    } else {
444	OSStatus ret;
445
446	ret = SecKeychainSearchCreateFromAttributes(ctx->keychain,
447						    kSecCertificateItemClass,
448						    NULL,
449						    &iter->searchRef);
450	if (ret) {
451	    free(iter);
452	    hx509_set_error_string(context, 0, ret,
453				   "Failed to start search for attributes");
454	    return ENOMEM;
455	}
456    }
457
458    *cursor = iter;
459    return 0;
460}
461
462/*
463 *
464 */
465
466static int
467keychain_iter(hx509_context context,
468	      hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
469{
470    SecKeychainAttributeList *attrs = NULL;
471    SecKeychainAttributeInfo attrInfo;
472    UInt32 attrFormat[1] = { 0 };
473    SecKeychainItemRef itemRef;
474    SecItemAttr item[1];
475    struct iter *iter = cursor;
476    OSStatus ret;
477    UInt32 len;
478    void *ptr = NULL;
479
480    if (iter->certs)
481	return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert);
482
483    *cert = NULL;
484
485    ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef);
486    if (ret == errSecItemNotFound)
487	return 0;
488    else if (ret != 0)
489	return EINVAL;
490
491    /*
492     * Pick out certificate and matching "keyid"
493     */
494
495    item[0] = kSecPublicKeyHashItemAttr;
496
497    attrInfo.count = 1;
498    attrInfo.tag = item;
499    attrInfo.format = attrFormat;
500
501    ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
502					       &attrs, &len, &ptr);
503    if (ret)
504	return EINVAL;
505
506    ret = hx509_cert_init_data(context, ptr, len, cert);
507    if (ret)
508	goto out;
509
510    /*
511     * Find related private key if there is one by looking at
512     * kSecPublicKeyHashItemAttr == kSecKeyLabel
513     */
514    {
515	SecKeychainSearchRef search;
516	SecKeychainAttribute attrKeyid;
517	SecKeychainAttributeList attrList;
518
519	attrKeyid.tag = kSecKeyLabel;
520	attrKeyid.length = attrs->attr[0].length;
521	attrKeyid.data = attrs->attr[0].data;
522
523	attrList.count = 1;
524	attrList.attr = &attrKeyid;
525
526	ret = SecKeychainSearchCreateFromAttributes(NULL,
527						    CSSM_DL_DB_RECORD_PRIVATE_KEY,
528						    &attrList,
529						    &search);
530	if (ret) {
531	    ret = 0;
532	    goto out;
533	}
534
535	ret = SecKeychainSearchCopyNext(search, &itemRef);
536	CFRelease(search);
537	if (ret == errSecItemNotFound) {
538	    ret = 0;
539	    goto out;
540	} else if (ret) {
541	    ret = EINVAL;
542	    goto out;
543	}
544	set_private_key(context, itemRef, *cert);
545    }
546
547out:
548    SecKeychainItemFreeAttributesAndData(attrs, ptr);
549
550    return ret;
551}
552
553/*
554 *
555 */
556
557static int
558keychain_iter_end(hx509_context context,
559		  hx509_certs certs,
560		  void *data,
561		  void *cursor)
562{
563    struct iter *iter = cursor;
564
565    if (iter->certs) {
566	hx509_certs_end_seq(context, iter->certs, iter->cursor);
567	hx509_certs_free(&iter->certs);
568    } else {
569	CFRelease(iter->searchRef);
570    }
571
572    memset(iter, 0, sizeof(*iter));
573    free(iter);
574    return 0;
575}
576
577/*
578 *
579 */
580
581struct hx509_keyset_ops keyset_keychain = {
582    "KEYCHAIN",
583    0,
584    keychain_init,
585    NULL,
586    keychain_free,
587    NULL,
588    NULL,
589    keychain_iter_start,
590    keychain_iter,
591    keychain_iter_end
592};
593
594#endif /* HAVE_FRAMEWORK_SECURITY */
595
596/*
597 *
598 */
599
600void
601_hx509_ks_keychain_register(hx509_context context)
602{
603#ifdef HAVE_FRAMEWORK_SECURITY
604    _hx509_ks_register(context, &keyset_keychain);
605#endif
606}
607