lib/gssapi/krb5: implement GSS_C_CHANNEL_BOUND_FLAG for gss_init_sec_context()
[heimdal.git] / lib / hx509 / ks_keychain.c
blob3243ee8b26c3ddb9d685d430602280b40f587efa
1 /*
2 * Copyright (c) 2007 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
34 #include "hx_locl.h"
36 #ifdef HAVE_FRAMEWORK_SECURITY
38 #pragma clang diagnostic push
39 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
41 #include <Security/Security.h>
43 /* Missing function decls in pre Leopard */
44 #ifdef NEED_SECKEYGETCSPHANDLE_PROTO
45 OSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *);
46 OSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG,
47 int, const CSSM_ACCESS_CREDENTIALS **);
48 #define kSecCredentialTypeDefault 0
49 #define CSSM_SIZE uint32_t
50 #endif
53 static int
54 getAttribute(SecKeychainItemRef itemRef, SecItemAttr item,
55 SecKeychainAttributeList **attrs)
57 SecKeychainAttributeInfo attrInfo;
58 UInt32 attrFormat = 0;
59 OSStatus ret;
61 *attrs = NULL;
63 attrInfo.count = 1;
64 attrInfo.tag = &item;
65 attrInfo.format = &attrFormat;
67 ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
68 attrs, NULL, NULL);
69 if (ret)
70 return EINVAL;
71 return 0;
79 struct kc_rsa {
80 SecKeychainItemRef item;
81 size_t keysize;
85 static int
86 kc_rsa_public_encrypt(int flen,
87 const unsigned char *from,
88 unsigned char *to,
89 RSA *rsa,
90 int padding)
92 return -1;
95 static int
96 kc_rsa_public_decrypt(int flen,
97 const unsigned char *from,
98 unsigned char *to,
99 RSA *rsa,
100 int padding)
102 return -1;
106 static int
107 kc_rsa_private_encrypt(int flen,
108 const unsigned char *from,
109 unsigned char *to,
110 RSA *rsa,
111 int padding)
113 struct kc_rsa *kc = RSA_get_app_data(rsa);
115 CSSM_RETURN cret;
116 OSStatus ret;
117 const CSSM_ACCESS_CREDENTIALS *creds;
118 SecKeyRef privKeyRef = (SecKeyRef)kc->item;
119 CSSM_CSP_HANDLE cspHandle;
120 const CSSM_KEY *cssmKey;
121 CSSM_CC_HANDLE sigHandle = 0;
122 CSSM_DATA sig, in;
123 int fret = 0;
125 if (padding != RSA_PKCS1_PADDING)
126 return -1;
128 cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
129 if(cret) abort();
131 cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
132 if(cret) abort();
134 ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN,
135 kSecCredentialTypeDefault, &creds);
136 if(ret) abort();
138 ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA,
139 creds, cssmKey, &sigHandle);
140 if(ret) abort();
142 in.Data = (uint8 *)from;
143 in.Length = flen;
145 sig.Data = (uint8 *)to;
146 sig.Length = kc->keysize;
148 cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig);
149 if(cret) {
150 /* cssmErrorString(cret); */
151 fret = -1;
152 } else
153 fret = sig.Length;
155 if(sigHandle)
156 CSSM_DeleteContext(sigHandle);
158 return fret;
161 static int
162 kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to,
163 RSA * rsa, int padding)
165 struct kc_rsa *kc = RSA_get_app_data(rsa);
167 CSSM_RETURN cret;
168 OSStatus ret;
169 const CSSM_ACCESS_CREDENTIALS *creds;
170 SecKeyRef privKeyRef = (SecKeyRef)kc->item;
171 CSSM_CSP_HANDLE cspHandle;
172 const CSSM_KEY *cssmKey;
173 CSSM_CC_HANDLE handle = 0;
174 CSSM_DATA out, in, rem;
175 int fret = 0;
176 CSSM_SIZE outlen = 0;
177 char remdata[1024];
179 if (padding != RSA_PKCS1_PADDING)
180 return -1;
182 cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey);
183 if(cret) abort();
185 cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle);
186 if(cret) abort();
188 ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_DECRYPT,
189 kSecCredentialTypeDefault, &creds);
190 if(ret) abort();
193 ret = CSSM_CSP_CreateAsymmetricContext (cspHandle,
194 CSSM_ALGID_RSA,
195 creds,
196 cssmKey,
197 CSSM_PADDING_PKCS1,
198 &handle);
199 if(ret) abort();
201 in.Data = (uint8 *)from;
202 in.Length = flen;
204 out.Data = (uint8 *)to;
205 out.Length = kc->keysize;
207 rem.Data = (uint8 *)remdata;
208 rem.Length = sizeof(remdata);
210 cret = CSSM_DecryptData(handle, &in, 1, &out, 1, &outlen, &rem);
211 if(cret) {
212 /* cssmErrorString(cret); */
213 fret = -1;
214 } else
215 fret = out.Length;
217 if(handle)
218 CSSM_DeleteContext(handle);
220 return fret;
223 static int
224 kc_rsa_init(RSA *rsa)
226 return 1;
229 static int
230 kc_rsa_finish(RSA *rsa)
232 struct kc_rsa *kc_rsa = RSA_get_app_data(rsa);
233 CFRelease(kc_rsa->item);
234 memset(kc_rsa, 0, sizeof(*kc_rsa));
235 free(kc_rsa);
236 return 1;
239 static const RSA_METHOD kc_rsa_pkcs1_method = {
240 "hx509 Keychain PKCS#1 RSA",
241 kc_rsa_public_encrypt,
242 kc_rsa_public_decrypt,
243 kc_rsa_private_encrypt,
244 kc_rsa_private_decrypt,
245 NULL,
246 NULL,
247 kc_rsa_init,
248 kc_rsa_finish,
250 NULL,
251 NULL,
252 NULL,
253 NULL
256 static int
257 set_private_key(hx509_context context,
258 SecKeychainItemRef itemRef,
259 hx509_cert cert)
261 struct kc_rsa *kc;
262 hx509_private_key key;
263 RSA *rsa;
264 int ret;
266 ret = hx509_private_key_init(&key, NULL, NULL);
267 if (ret)
268 return ret;
270 kc = calloc(1, sizeof(*kc));
271 if (kc == NULL)
272 _hx509_abort("out of memory");
274 kc->item = itemRef;
276 rsa = RSA_new();
277 if (rsa == NULL)
278 _hx509_abort("out of memory");
280 /* Argh, fake modulus since OpenSSL API is on crack */
282 SecKeychainAttributeList *attrs = NULL;
283 uint32_t size;
284 void *data;
286 rsa->n = BN_new();
287 if (rsa->n == NULL) abort();
289 ret = getAttribute(itemRef, kSecKeyKeySizeInBits, &attrs);
290 if (ret) abort();
292 size = *(uint32_t *)attrs->attr[0].data;
293 SecKeychainItemFreeAttributesAndData(attrs, NULL);
295 kc->keysize = (size + 7) / 8;
297 data = malloc(kc->keysize);
298 memset(data, 0xe0, kc->keysize);
299 BN_bin2bn(data, kc->keysize, rsa->n);
300 free(data);
302 rsa->e = NULL;
304 RSA_set_method(rsa, &kc_rsa_pkcs1_method);
305 ret = RSA_set_app_data(rsa, kc);
306 if (ret != 1)
307 _hx509_abort("RSA_set_app_data");
309 hx509_private_key_assign_rsa(key, rsa);
310 _hx509_cert_assign_key(cert, key);
312 return 0;
319 struct ks_keychain {
320 int anchors;
321 SecKeychainRef keychain;
324 static int
325 keychain_init(hx509_context context,
326 hx509_certs certs, void **data, int flags,
327 const char *residue, hx509_lock lock)
329 struct ks_keychain *ctx;
331 if (flags & HX509_CERTS_NO_PRIVATE_KEYS) {
332 hx509_set_error_string(context, 0, ENOTSUP,
333 "KEYCHAIN store does not support not reading "
334 "private keys");
335 return ENOTSUP;
338 ctx = calloc(1, sizeof(*ctx));
339 if (ctx == NULL) {
340 hx509_clear_error_string(context);
341 return ENOMEM;
344 if (residue) {
345 if (strcasecmp(residue, "system-anchors") == 0) {
346 ctx->anchors = 1;
347 } else if (strncasecmp(residue, "FILE:", 5) == 0) {
348 OSStatus ret;
350 ret = SecKeychainOpen(residue + 5, &ctx->keychain);
351 if (ret != noErr) {
352 hx509_set_error_string(context, 0, ENOENT,
353 "Failed to open %s", residue);
354 free(ctx);
355 return ENOENT;
357 } else {
358 hx509_set_error_string(context, 0, ENOENT,
359 "Unknown subtype %s", residue);
360 free(ctx);
361 return ENOENT;
365 *data = ctx;
366 return 0;
373 static int
374 keychain_free(hx509_certs certs, void *data)
376 struct ks_keychain *ctx = data;
377 if (ctx->keychain)
378 CFRelease(ctx->keychain);
379 memset(ctx, 0, sizeof(*ctx));
380 free(ctx);
381 return 0;
388 struct iter {
389 hx509_certs certs;
390 void *cursor;
391 SecKeychainSearchRef searchRef;
394 static int
395 keychain_iter_start(hx509_context context,
396 hx509_certs certs, void *data, void **cursor)
398 struct ks_keychain *ctx = data;
399 struct iter *iter;
401 iter = calloc(1, sizeof(*iter));
402 if (iter == NULL) {
403 hx509_set_error_string(context, 0, ENOMEM, "out of memory");
404 return ENOMEM;
407 if (ctx->anchors) {
408 CFArrayRef anchors;
409 int ret;
410 int i;
412 ret = hx509_certs_init(context, "MEMORY:ks-file-create",
413 0, NULL, &iter->certs);
414 if (ret) {
415 free(iter);
416 return ret;
419 ret = SecTrustCopyAnchorCertificates(&anchors);
420 if (ret != 0) {
421 hx509_certs_free(&iter->certs);
422 free(iter);
423 hx509_set_error_string(context, 0, ENOMEM,
424 "Can't get trust anchors from Keychain");
425 return ENOMEM;
427 for (i = 0; i < CFArrayGetCount(anchors); i++) {
428 SecCertificateRef cr;
429 hx509_cert cert;
430 CSSM_DATA cssm;
432 cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i);
434 SecCertificateGetData(cr, &cssm);
436 cert = hx509_cert_init_data(context, cssm.Data, cssm.Length, NULL);
437 if (cert == NULL)
438 continue;
440 ret = hx509_certs_add(context, iter->certs, cert);
441 hx509_cert_free(cert);
443 CFRelease(anchors);
446 if (iter->certs) {
447 int ret;
448 ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor);
449 if (ret) {
450 hx509_certs_free(&iter->certs);
451 free(iter);
452 return ret;
454 } else {
455 OSStatus ret;
457 ret = SecKeychainSearchCreateFromAttributes(ctx->keychain,
458 kSecCertificateItemClass,
459 NULL,
460 &iter->searchRef);
461 if (ret) {
462 free(iter);
463 hx509_set_error_string(context, 0, ret,
464 "Failed to start search for attributes");
465 return ENOMEM;
469 *cursor = iter;
470 return 0;
477 static int
478 keychain_iter(hx509_context context,
479 hx509_certs certs, void *data, void *cursor, hx509_cert *cert)
481 SecKeychainAttributeList *attrs = NULL;
482 SecKeychainAttributeInfo attrInfo;
483 UInt32 attrFormat[1] = { 0 };
484 SecKeychainItemRef itemRef;
485 SecItemAttr item[1];
486 heim_error_t error = NULL;
487 struct iter *iter = cursor;
488 OSStatus ret;
489 UInt32 len;
490 void *ptr = NULL;
492 if (iter->certs)
493 return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert);
495 *cert = NULL;
497 ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef);
498 if (ret == errSecItemNotFound)
499 return 0;
500 else if (ret != 0)
501 return EINVAL;
504 * Pick out certificate and matching "keyid"
507 item[0] = kSecPublicKeyHashItemAttr;
509 attrInfo.count = 1;
510 attrInfo.tag = item;
511 attrInfo.format = attrFormat;
513 ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL,
514 &attrs, &len, &ptr);
515 if (ret)
516 return EINVAL;
518 *cert = hx509_cert_init_data(context, ptr, len, &error);
519 if (*cert == NULL) {
520 ret = heim_error_get_code(error);
521 heim_release(error);
522 goto out;
526 * Find related private key if there is one by looking at
527 * kSecPublicKeyHashItemAttr == kSecKeyLabel
530 SecKeychainSearchRef search;
531 SecKeychainAttribute attrKeyid;
532 SecKeychainAttributeList attrList;
534 attrKeyid.tag = kSecKeyLabel;
535 attrKeyid.length = attrs->attr[0].length;
536 attrKeyid.data = attrs->attr[0].data;
538 attrList.count = 1;
539 attrList.attr = &attrKeyid;
541 ret = SecKeychainSearchCreateFromAttributes(NULL,
542 CSSM_DL_DB_RECORD_PRIVATE_KEY,
543 &attrList,
544 &search);
545 if (ret) {
546 ret = 0;
547 goto out;
550 ret = SecKeychainSearchCopyNext(search, &itemRef);
551 CFRelease(search);
552 if (ret == errSecItemNotFound) {
553 ret = 0;
554 goto out;
555 } else if (ret) {
556 ret = EINVAL;
557 goto out;
559 set_private_key(context, itemRef, *cert);
562 out:
563 SecKeychainItemFreeAttributesAndData(attrs, ptr);
565 return ret;
572 static int
573 keychain_iter_end(hx509_context context,
574 hx509_certs certs,
575 void *data,
576 void *cursor)
578 struct iter *iter = cursor;
580 if (iter->certs) {
581 hx509_certs_end_seq(context, iter->certs, iter->cursor);
582 hx509_certs_free(&iter->certs);
583 } else {
584 CFRelease(iter->searchRef);
587 memset(iter, 0, sizeof(*iter));
588 free(iter);
589 return 0;
596 struct hx509_keyset_ops keyset_keychain = {
597 "KEYCHAIN",
599 keychain_init,
600 NULL,
601 keychain_free,
602 NULL,
603 NULL,
604 keychain_iter_start,
605 keychain_iter,
606 keychain_iter_end,
607 NULL,
608 NULL,
609 NULL,
610 NULL
613 #pragma clang diagnostic pop
615 #endif /* HAVE_FRAMEWORK_SECURITY */
621 HX509_LIB_FUNCTION void HX509_LIB_CALL
622 _hx509_ks_keychain_register(hx509_context context)
624 #ifdef HAVE_FRAMEWORK_SECURITY
625 _hx509_ks_register(context, &keyset_keychain);
626 #endif