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