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"
35RCSID("$Id: ks_keychain.c 22084 2007-11-16 20:12:30Z lha $");
36
37#ifdef HAVE_FRAMEWORK_SECURITY
38
39#include <Security/Security.h>
40
41/* Missing function decls in pre Leopard */
42#ifdef NEED_SECKEYGETCSPHANDLE_PROTO
43OSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *);
44OSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG,
45			      int, const CSSM_ACCESS_CREDENTIALS **);
46#define kSecCredentialTypeDefault 0
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
123    cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
124    if(cret) abort();
125
126    cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
127    if(cret) abort();
128
129    ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN,
130			       kSecCredentialTypeDefault, &creds);
131    if(ret) abort();
132
133    ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA,
134					  creds, cssmKey, &sigHandle);
135    if(ret) abort();
136
137    in.Data = (uint8 *)from;
138    in.Length = flen;
139
140    sig.Data = (uint8 *)to;
141    sig.Length = kc->keysize;
142
143    cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig);
144    if(cret) {
145	/* cssmErrorString(cret); */
146	fret = -1;
147    } else
148	fret = sig.Length;
149
150    if(sigHandle)
151	CSSM_DeleteContext(sigHandle);
152
153    return fret;
154}
155
156static int
157kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
158		       RSA * rsa, int padding)
159{
160    return -1;
161}
162
163static int
164kc_rsa_init(RSA *rsa)
165{
166    return 1;
167}
168
169static int
170kc_rsa_finish(RSA *rsa)
171{
172    struct kc_rsa *kc_rsa = RSA_get_app_data(rsa);
173    CFRelease(kc_rsa->item);
174    memset(kc_rsa, 0, sizeof(*kc_rsa));
175    free(kc_rsa);
176    return 1;
177}
178
179static const RSA_METHOD kc_rsa_pkcs1_method = {
180    "hx509 Keychain PKCS#1 RSA",
181    kc_rsa_public_encrypt,
182    kc_rsa_public_decrypt,
183    kc_rsa_private_encrypt,
184    kc_rsa_private_decrypt,
185    NULL,
186    NULL,
187    kc_rsa_init,
188    kc_rsa_finish,
189    0,
190    NULL,
191    NULL,
192    NULL
193};
194
195static int
196set_private_key(hx509_context context,
197		SecKeychainItemRef itemRef,
198		hx509_cert cert)
199{
200    struct kc_rsa *kc;
201    hx509_private_key key;
202    RSA *rsa;
203    int ret;
204
205    ret = _hx509_private_key_init(&key, NULL, NULL);
206    if (ret)
207	return ret;
208
209    kc = calloc(1, sizeof(*kc));
210    if (kc == NULL)
211	_hx509_abort("out of memory");
212
213    kc->item = itemRef;
214
215    rsa = RSA_new();
216    if (rsa == NULL)
217	_hx509_abort("out of memory");
218
219    /* Argh, fake modulus since OpenSSL API is on crack */
220    {
221	SecKeychainAttributeList *attrs = NULL;
222	uint32_t size;
223	void *data;
224
225	rsa->n = BN_new();
226	if (rsa->n == NULL) abort();
227
228	ret = getAttribute(itemRef, kSecKeyKeySizeInBits, &attrs);
229	if (ret) abort();
230
231	size = *(uint32_t *)attrs->attr[0].data;
232	SecKeychainItemFreeAttributesAndData(attrs, NULL);
233
234	kc->keysize = (size + 7) / 8;
235
236	data = malloc(kc->keysize);
237	memset(data, 0xe0, kc->keysize);
238	BN_bin2bn(data, kc->keysize, rsa->n);
239	free(data);
240    }
241    rsa->e = NULL;
242
243    RSA_set_method(rsa, &kc_rsa_pkcs1_method);
244    ret = RSA_set_app_data(rsa, kc);
245    if (ret != 1)
246	_hx509_abort("RSA_set_app_data");
247
248    _hx509_private_key_assign_rsa(key, rsa);
249    _hx509_cert_assign_key(cert, key);
250
251    return 0;
252}
253
254/*
255 *
256 */
257
258struct ks_keychain {
259    int anchors;
260    SecKeychainRef keychain;
261};
262
263static int
264keychain_init(hx509_context context,
265	      hx509_certs certs, void **data, int flags,
266	      const char *residue, hx509_lock lock)
267{
268    struct ks_keychain *ctx;
269
270    ctx = calloc(1, sizeof(*ctx));
271    if (ctx == NULL) {
272	hx509_clear_error_string(context);
273	return ENOMEM;
274    }
275
276    if (residue) {
277	if (strcasecmp(residue, "system-anchors") == 0) {
278	    ctx->anchors = 1;
279	} else if (strncasecmp(residue, "FILE:", 5) == 0) {
280	    OSStatus ret;
281
282	    ret = SecKeychainOpen(residue + 5, &ctx->keychain);
283	    if (ret != noErr) {
284		hx509_set_error_string(context, 0, ENOENT,
285				       "Failed to open %s", residue);
286		return ENOENT;
287	    }
288	} else {
289	    hx509_set_error_string(context, 0, ENOENT,
290				   "Unknown subtype %s", residue);
291	    return ENOENT;
292	}
293    }
294
295    *data = ctx;
296    return 0;
297}
298
299/*
300 *
301 */
302
303static int
304keychain_free(hx509_certs certs, void *data)
305{
306    struct ks_keychain *ctx = data;
307    if (ctx->keychain)
308	CFRelease(ctx->keychain);
309    memset(ctx, 0, sizeof(*ctx));
310    free(ctx);
311    return 0;
312}
313
314/*
315 *
316 */
317
318struct iter {
319    hx509_certs certs;
320    void *cursor;
321    SecKeychainSearchRef searchRef;
322};
323
324static int
325keychain_iter_start(hx509_context context,
326		    hx509_certs certs, void *data, void **cursor)
327{
328    struct ks_keychain *ctx = data;
329    struct iter *iter;
330
331    iter = calloc(1, sizeof(*iter));
332    if (iter == NULL) {
333	hx509_set_error_string(context, 0, ENOMEM, "out of memory");
334	return ENOMEM;
335    }
336
337    if (ctx->anchors) {
338        CFArrayRef anchors;
339	int ret;
340	int i;
341
342	ret = hx509_certs_init(context, "MEMORY:ks-file-create",
343			       0, NULL, &iter->certs);
344	if (ret) {
345	    free(iter);
346	    return ret;
347	}
348
349	ret = SecTrustCopyAnchorCertificates(&anchors);
350	if (ret != 0) {
351	    hx509_certs_free(&iter->certs);
352	    free(iter);
353	    hx509_set_error_string(context, 0, ENOMEM,
354				   "Can't get trust anchors from Keychain");
355	    return ENOMEM;
356	}
357	for (i = 0; i < CFArrayGetCount(anchors); i++) {
358	    SecCertificateRef cr;
359	    hx509_cert cert;
360	    CSSM_DATA cssm;
361
362	    cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i);
363
364	    SecCertificateGetData(cr, &cssm);
365
366	    ret = hx509_cert_init_data(context, cssm.Data, cssm.Length, &cert);
367	    if (ret)
368		continue;
369
370	    ret = hx509_certs_add(context, iter->certs, cert);
371	    hx509_cert_free(cert);
372	}
373	CFRelease(anchors);
374    }
375
376    if (iter->certs) {
377	int ret;
378	ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor);
379	if (ret) {
380	    hx509_certs_free(&iter->certs);
381	    free(iter);
382	    return ret;
383	}
384    } else {
385	OSStatus ret;
386
387	ret = SecKeychainSearchCreateFromAttributes(ctx->keychain,
388						    kSecCertificateItemClass,
389						    NULL,
390						    &iter->searchRef);
391	if (ret) {
392	    free(iter);
393	    hx509_set_error_string(context, 0, ret,
394				   "Failed to start search for attributes");
395	    return ENOMEM;
396	}
397    }
398
399    *cursor = iter;
400    return 0;
401}
402
403/*
404 *
405 */
406
407static int
408keychain_iter(hx509_context context,
409	      hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
410{
411    SecKeychainAttributeList *attrs = NULL;
412    SecKeychainAttributeInfo attrInfo;
413    UInt32 attrFormat[1] = { 0 };
414    SecKeychainItemRef itemRef;
415    SecItemAttr item[1];
416    struct iter *iter = cursor;
417    OSStatus ret;
418    UInt32 len;
419    void *ptr = NULL;
420
421    if (iter->certs)
422	return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert);
423
424    *cert = NULL;
425
426    ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef);
427    if (ret == errSecItemNotFound)
428	return 0;
429    else if (ret != 0)
430	return EINVAL;
431
432    /*
433     * Pick out certificate and matching "keyid"
434     */
435
436    item[0] = kSecPublicKeyHashItemAttr;
437
438    attrInfo.count = 1;
439    attrInfo.tag = item;
440    attrInfo.format = attrFormat;
441
442    ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
443					       &attrs, &len, &ptr);
444    if (ret)
445	return EINVAL;
446
447    ret = hx509_cert_init_data(context, ptr, len, cert);
448    if (ret)
449	goto out;
450
451    /*
452     * Find related private key if there is one by looking at
453     * kSecPublicKeyHashItemAttr == kSecKeyLabel
454     */
455    {
456	SecKeychainSearchRef search;
457	SecKeychainAttribute attrKeyid;
458	SecKeychainAttributeList attrList;
459
460	attrKeyid.tag = kSecKeyLabel;
461	attrKeyid.length = attrs->attr[0].length;
462	attrKeyid.data = attrs->attr[0].data;
463
464	attrList.count = 1;
465	attrList.attr = &attrKeyid;
466
467	ret = SecKeychainSearchCreateFromAttributes(NULL,
468						    CSSM_DL_DB_RECORD_PRIVATE_KEY,
469						    &attrList,
470						    &search);
471	if (ret) {
472	    ret = 0;
473	    goto out;
474	}
475
476	ret = SecKeychainSearchCopyNext(search, &itemRef);
477	CFRelease(search);
478	if (ret == errSecItemNotFound) {
479	    ret = 0;
480	    goto out;
481	} else if (ret) {
482	    ret = EINVAL;
483	    goto out;
484	}
485	set_private_key(context, itemRef, *cert);
486    }
487
488out:
489    SecKeychainItemFreeAttributesAndData(attrs, ptr);
490
491    return ret;
492}
493
494/*
495 *
496 */
497
498static int
499keychain_iter_end(hx509_context context,
500		  hx509_certs certs,
501		  void *data,
502		  void *cursor)
503{
504    struct iter *iter = cursor;
505
506    if (iter->certs) {
507	int ret;
508	ret = hx509_certs_end_seq(context, iter->certs, iter->cursor);
509	hx509_certs_free(&iter->certs);
510    } else {
511	CFRelease(iter->searchRef);
512    }
513
514    memset(iter, 0, sizeof(*iter));
515    free(iter);
516    return 0;
517}
518
519/*
520 *
521 */
522
523struct hx509_keyset_ops keyset_keychain = {
524    "KEYCHAIN",
525    0,
526    keychain_init,
527    NULL,
528    keychain_free,
529    NULL,
530    NULL,
531    keychain_iter_start,
532    keychain_iter,
533    keychain_iter_end
534};
535
536#endif /* HAVE_FRAMEWORK_SECURITY */
537
538/*
539 *
540 */
541
542void
543_hx509_ks_keychain_register(hx509_context context)
544{
545#ifdef HAVE_FRAMEWORK_SECURITY
546    _hx509_ks_register(context, &keyset_keychain);
547#endif
548}
549