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