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