diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-05 17:47:29 +0000 |
commit | 4f5791ebd03eaec1c7da0865a383175b05102712 (patch) | |
tree | 8ce7b00f7a76baa386372422adebbe64510812d4 /third_party/heimdal/lib/hx509/ks_keychain.c | |
parent | Initial commit. (diff) | |
download | samba-4f5791ebd03eaec1c7da0865a383175b05102712.tar.xz samba-4f5791ebd03eaec1c7da0865a383175b05102712.zip |
Adding upstream version 2:4.17.12+dfsg.upstream/2%4.17.12+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/heimdal/lib/hx509/ks_keychain.c')
-rw-r--r-- | third_party/heimdal/lib/hx509/ks_keychain.c | 627 |
1 files changed, 627 insertions, 0 deletions
diff --git a/third_party/heimdal/lib/hx509/ks_keychain.c b/third_party/heimdal/lib/hx509/ks_keychain.c new file mode 100644 index 0000000..3243ee8 --- /dev/null +++ b/third_party/heimdal/lib/hx509/ks_keychain.c @@ -0,0 +1,627 @@ +/* + * Copyright (c) 2007 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "hx_locl.h" + +#ifdef HAVE_FRAMEWORK_SECURITY + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + +#include <Security/Security.h> + +/* Missing function decls in pre Leopard */ +#ifdef NEED_SECKEYGETCSPHANDLE_PROTO +OSStatus SecKeyGetCSPHandle(SecKeyRef, CSSM_CSP_HANDLE *); +OSStatus SecKeyGetCredentials(SecKeyRef, CSSM_ACL_AUTHORIZATION_TAG, + int, const CSSM_ACCESS_CREDENTIALS **); +#define kSecCredentialTypeDefault 0 +#define CSSM_SIZE uint32_t +#endif + + +static int +getAttribute(SecKeychainItemRef itemRef, SecItemAttr item, + SecKeychainAttributeList **attrs) +{ + SecKeychainAttributeInfo attrInfo; + UInt32 attrFormat = 0; + OSStatus ret; + + *attrs = NULL; + + attrInfo.count = 1; + attrInfo.tag = &item; + attrInfo.format = &attrFormat; + + ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, + attrs, NULL, NULL); + if (ret) + return EINVAL; + return 0; +} + + +/* + * + */ + +struct kc_rsa { + SecKeychainItemRef item; + size_t keysize; +}; + + +static int +kc_rsa_public_encrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + return -1; +} + +static int +kc_rsa_public_decrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + return -1; +} + + +static int +kc_rsa_private_encrypt(int flen, + const unsigned char *from, + unsigned char *to, + RSA *rsa, + int padding) +{ + struct kc_rsa *kc = RSA_get_app_data(rsa); + + CSSM_RETURN cret; + OSStatus ret; + const CSSM_ACCESS_CREDENTIALS *creds; + SecKeyRef privKeyRef = (SecKeyRef)kc->item; + CSSM_CSP_HANDLE cspHandle; + const CSSM_KEY *cssmKey; + CSSM_CC_HANDLE sigHandle = 0; + CSSM_DATA sig, in; + int fret = 0; + + if (padding != RSA_PKCS1_PADDING) + return -1; + + cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey); + if(cret) abort(); + + cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle); + if(cret) abort(); + + ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_SIGN, + kSecCredentialTypeDefault, &creds); + if(ret) abort(); + + ret = CSSM_CSP_CreateSignatureContext(cspHandle, CSSM_ALGID_RSA, + creds, cssmKey, &sigHandle); + if(ret) abort(); + + in.Data = (uint8 *)from; + in.Length = flen; + + sig.Data = (uint8 *)to; + sig.Length = kc->keysize; + + cret = CSSM_SignData(sigHandle, &in, 1, CSSM_ALGID_NONE, &sig); + if(cret) { + /* cssmErrorString(cret); */ + fret = -1; + } else + fret = sig.Length; + + if(sigHandle) + CSSM_DeleteContext(sigHandle); + + return fret; +} + +static int +kc_rsa_private_decrypt(int flen, const unsigned char *from, unsigned char *to, + RSA * rsa, int padding) +{ + struct kc_rsa *kc = RSA_get_app_data(rsa); + + CSSM_RETURN cret; + OSStatus ret; + const CSSM_ACCESS_CREDENTIALS *creds; + SecKeyRef privKeyRef = (SecKeyRef)kc->item; + CSSM_CSP_HANDLE cspHandle; + const CSSM_KEY *cssmKey; + CSSM_CC_HANDLE handle = 0; + CSSM_DATA out, in, rem; + int fret = 0; + CSSM_SIZE outlen = 0; + char remdata[1024]; + + if (padding != RSA_PKCS1_PADDING) + return -1; + + cret = SecKeyGetCSSMKey(privKeyRef, &cssmKey); + if(cret) abort(); + + cret = SecKeyGetCSPHandle(privKeyRef, &cspHandle); + if(cret) abort(); + + ret = SecKeyGetCredentials(privKeyRef, CSSM_ACL_AUTHORIZATION_DECRYPT, + kSecCredentialTypeDefault, &creds); + if(ret) abort(); + + + ret = CSSM_CSP_CreateAsymmetricContext (cspHandle, + CSSM_ALGID_RSA, + creds, + cssmKey, + CSSM_PADDING_PKCS1, + &handle); + if(ret) abort(); + + in.Data = (uint8 *)from; + in.Length = flen; + + out.Data = (uint8 *)to; + out.Length = kc->keysize; + + rem.Data = (uint8 *)remdata; + rem.Length = sizeof(remdata); + + cret = CSSM_DecryptData(handle, &in, 1, &out, 1, &outlen, &rem); + if(cret) { + /* cssmErrorString(cret); */ + fret = -1; + } else + fret = out.Length; + + if(handle) + CSSM_DeleteContext(handle); + + return fret; +} + +static int +kc_rsa_init(RSA *rsa) +{ + return 1; +} + +static int +kc_rsa_finish(RSA *rsa) +{ + struct kc_rsa *kc_rsa = RSA_get_app_data(rsa); + CFRelease(kc_rsa->item); + memset(kc_rsa, 0, sizeof(*kc_rsa)); + free(kc_rsa); + return 1; +} + +static const RSA_METHOD kc_rsa_pkcs1_method = { + "hx509 Keychain PKCS#1 RSA", + kc_rsa_public_encrypt, + kc_rsa_public_decrypt, + kc_rsa_private_encrypt, + kc_rsa_private_decrypt, + NULL, + NULL, + kc_rsa_init, + kc_rsa_finish, + 0, + NULL, + NULL, + NULL, + NULL +}; + +static int +set_private_key(hx509_context context, + SecKeychainItemRef itemRef, + hx509_cert cert) +{ + struct kc_rsa *kc; + hx509_private_key key; + RSA *rsa; + int ret; + + ret = hx509_private_key_init(&key, NULL, NULL); + if (ret) + return ret; + + kc = calloc(1, sizeof(*kc)); + if (kc == NULL) + _hx509_abort("out of memory"); + + kc->item = itemRef; + + rsa = RSA_new(); + if (rsa == NULL) + _hx509_abort("out of memory"); + + /* Argh, fake modulus since OpenSSL API is on crack */ + { + SecKeychainAttributeList *attrs = NULL; + uint32_t size; + void *data; + + rsa->n = BN_new(); + if (rsa->n == NULL) abort(); + + ret = getAttribute(itemRef, kSecKeyKeySizeInBits, &attrs); + if (ret) abort(); + + size = *(uint32_t *)attrs->attr[0].data; + SecKeychainItemFreeAttributesAndData(attrs, NULL); + + kc->keysize = (size + 7) / 8; + + data = malloc(kc->keysize); + memset(data, 0xe0, kc->keysize); + BN_bin2bn(data, kc->keysize, rsa->n); + free(data); + } + rsa->e = NULL; + + RSA_set_method(rsa, &kc_rsa_pkcs1_method); + ret = RSA_set_app_data(rsa, kc); + if (ret != 1) + _hx509_abort("RSA_set_app_data"); + + hx509_private_key_assign_rsa(key, rsa); + _hx509_cert_assign_key(cert, key); + + return 0; +} + +/* + * + */ + +struct ks_keychain { + int anchors; + SecKeychainRef keychain; +}; + +static int +keychain_init(hx509_context context, + hx509_certs certs, void **data, int flags, + const char *residue, hx509_lock lock) +{ + struct ks_keychain *ctx; + + if (flags & HX509_CERTS_NO_PRIVATE_KEYS) { + hx509_set_error_string(context, 0, ENOTSUP, + "KEYCHAIN store does not support not reading " + "private keys"); + return ENOTSUP; + } + + ctx = calloc(1, sizeof(*ctx)); + if (ctx == NULL) { + hx509_clear_error_string(context); + return ENOMEM; + } + + if (residue) { + if (strcasecmp(residue, "system-anchors") == 0) { + ctx->anchors = 1; + } else if (strncasecmp(residue, "FILE:", 5) == 0) { + OSStatus ret; + + ret = SecKeychainOpen(residue + 5, &ctx->keychain); + if (ret != noErr) { + hx509_set_error_string(context, 0, ENOENT, + "Failed to open %s", residue); + free(ctx); + return ENOENT; + } + } else { + hx509_set_error_string(context, 0, ENOENT, + "Unknown subtype %s", residue); + free(ctx); + return ENOENT; + } + } + + *data = ctx; + return 0; +} + +/* + * + */ + +static int +keychain_free(hx509_certs certs, void *data) +{ + struct ks_keychain *ctx = data; + if (ctx->keychain) + CFRelease(ctx->keychain); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +/* + * + */ + +struct iter { + hx509_certs certs; + void *cursor; + SecKeychainSearchRef searchRef; +}; + +static int +keychain_iter_start(hx509_context context, + hx509_certs certs, void *data, void **cursor) +{ + struct ks_keychain *ctx = data; + struct iter *iter; + + iter = calloc(1, sizeof(*iter)); + if (iter == NULL) { + hx509_set_error_string(context, 0, ENOMEM, "out of memory"); + return ENOMEM; + } + + if (ctx->anchors) { + CFArrayRef anchors; + int ret; + int i; + + ret = hx509_certs_init(context, "MEMORY:ks-file-create", + 0, NULL, &iter->certs); + if (ret) { + free(iter); + return ret; + } + + ret = SecTrustCopyAnchorCertificates(&anchors); + if (ret != 0) { + hx509_certs_free(&iter->certs); + free(iter); + hx509_set_error_string(context, 0, ENOMEM, + "Can't get trust anchors from Keychain"); + return ENOMEM; + } + for (i = 0; i < CFArrayGetCount(anchors); i++) { + SecCertificateRef cr; + hx509_cert cert; + CSSM_DATA cssm; + + cr = (SecCertificateRef)CFArrayGetValueAtIndex(anchors, i); + + SecCertificateGetData(cr, &cssm); + + cert = hx509_cert_init_data(context, cssm.Data, cssm.Length, NULL); + if (cert == NULL) + continue; + + ret = hx509_certs_add(context, iter->certs, cert); + hx509_cert_free(cert); + } + CFRelease(anchors); + } + + if (iter->certs) { + int ret; + ret = hx509_certs_start_seq(context, iter->certs, &iter->cursor); + if (ret) { + hx509_certs_free(&iter->certs); + free(iter); + return ret; + } + } else { + OSStatus ret; + + ret = SecKeychainSearchCreateFromAttributes(ctx->keychain, + kSecCertificateItemClass, + NULL, + &iter->searchRef); + if (ret) { + free(iter); + hx509_set_error_string(context, 0, ret, + "Failed to start search for attributes"); + return ENOMEM; + } + } + + *cursor = iter; + return 0; +} + +/* + * + */ + +static int +keychain_iter(hx509_context context, + hx509_certs certs, void *data, void *cursor, hx509_cert *cert) +{ + SecKeychainAttributeList *attrs = NULL; + SecKeychainAttributeInfo attrInfo; + UInt32 attrFormat[1] = { 0 }; + SecKeychainItemRef itemRef; + SecItemAttr item[1]; + heim_error_t error = NULL; + struct iter *iter = cursor; + OSStatus ret; + UInt32 len; + void *ptr = NULL; + + if (iter->certs) + return hx509_certs_next_cert(context, iter->certs, iter->cursor, cert); + + *cert = NULL; + + ret = SecKeychainSearchCopyNext(iter->searchRef, &itemRef); + if (ret == errSecItemNotFound) + return 0; + else if (ret != 0) + return EINVAL; + + /* + * Pick out certificate and matching "keyid" + */ + + item[0] = kSecPublicKeyHashItemAttr; + + attrInfo.count = 1; + attrInfo.tag = item; + attrInfo.format = attrFormat; + + ret = SecKeychainItemCopyAttributesAndData(itemRef, &attrInfo, NULL, + &attrs, &len, &ptr); + if (ret) + return EINVAL; + + *cert = hx509_cert_init_data(context, ptr, len, &error); + if (*cert == NULL) { + ret = heim_error_get_code(error); + heim_release(error); + goto out; + } + + /* + * Find related private key if there is one by looking at + * kSecPublicKeyHashItemAttr == kSecKeyLabel + */ + { + SecKeychainSearchRef search; + SecKeychainAttribute attrKeyid; + SecKeychainAttributeList attrList; + + attrKeyid.tag = kSecKeyLabel; + attrKeyid.length = attrs->attr[0].length; + attrKeyid.data = attrs->attr[0].data; + + attrList.count = 1; + attrList.attr = &attrKeyid; + + ret = SecKeychainSearchCreateFromAttributes(NULL, + CSSM_DL_DB_RECORD_PRIVATE_KEY, + &attrList, + &search); + if (ret) { + ret = 0; + goto out; + } + + ret = SecKeychainSearchCopyNext(search, &itemRef); + CFRelease(search); + if (ret == errSecItemNotFound) { + ret = 0; + goto out; + } else if (ret) { + ret = EINVAL; + goto out; + } + set_private_key(context, itemRef, *cert); + } + +out: + SecKeychainItemFreeAttributesAndData(attrs, ptr); + + return ret; +} + +/* + * + */ + +static int +keychain_iter_end(hx509_context context, + hx509_certs certs, + void *data, + void *cursor) +{ + struct iter *iter = cursor; + + if (iter->certs) { + hx509_certs_end_seq(context, iter->certs, iter->cursor); + hx509_certs_free(&iter->certs); + } else { + CFRelease(iter->searchRef); + } + + memset(iter, 0, sizeof(*iter)); + free(iter); + return 0; +} + +/* + * + */ + +struct hx509_keyset_ops keyset_keychain = { + "KEYCHAIN", + 0, + keychain_init, + NULL, + keychain_free, + NULL, + NULL, + keychain_iter_start, + keychain_iter, + keychain_iter_end, + NULL, + NULL, + NULL, + NULL +}; + +#pragma clang diagnostic pop + +#endif /* HAVE_FRAMEWORK_SECURITY */ + +/* + * + */ + +HX509_LIB_FUNCTION void HX509_LIB_CALL +_hx509_ks_keychain_register(hx509_context context) +{ +#ifdef HAVE_FRAMEWORK_SECURITY + _hx509_ks_register(context, &keyset_keychain); +#endif +} |