diff options
author | Sumit Bose <sbose@redhat.com> | 2015-07-15 09:40:00 +0200 |
---|---|---|
committer | Jakub Hrozek <jhrozek@redhat.com> | 2015-07-31 09:52:06 +0200 |
commit | 4de84af23db74e13e867985c9093f394c9fa8d51 (patch) | |
tree | 62ed6590bbad5200b47b5fa8662d96639c2a8e4d /src/util/cert | |
parent | 5242964d275d0b2e96c9b0d1f8a9958c85d566fc (diff) | |
download | sssd-4de84af23db74e13e867985c9093f394c9fa8d51.tar.gz sssd-4de84af23db74e13e867985c9093f394c9fa8d51.tar.xz sssd-4de84af23db74e13e867985c9093f394c9fa8d51.zip |
ssh: generate public keys from certificate
Resolves: https://fedorahosted.org/sssd/ticket/2711
Reviewed-by: Jakub Hrozek <jhrozek@redhat.com>
Diffstat (limited to 'src/util/cert')
-rw-r--r-- | src/util/cert/libcrypto/cert.c | 93 | ||||
-rw-r--r-- | src/util/cert/nss/cert.c | 130 |
2 files changed, 223 insertions, 0 deletions
diff --git a/src/util/cert/libcrypto/cert.c b/src/util/cert/libcrypto/cert.c index 1a250f60d..01f9554b9 100644 --- a/src/util/cert/libcrypto/cert.c +++ b/src/util/cert/libcrypto/cert.c @@ -166,3 +166,96 @@ done: return ret; } + +#define SSH_RSA_HEADER "ssh-rsa" +#define SSH_RSA_HEADER_LEN (sizeof(SSH_RSA_HEADER) - 1) + +errno_t cert_to_ssh_key(TALLOC_CTX *mem_ctx, const char *ca_db, + const uint8_t *der_blob, size_t der_size, + uint8_t **key, size_t *key_size) +{ + int ret; + size_t size; + const unsigned char *d; + uint8_t *buf = NULL; + size_t c; + X509 *cert = NULL; + EVP_PKEY *cert_pub_key = NULL; + int modulus_len; + unsigned char modulus[OPENSSL_RSA_MAX_MODULUS_BITS/8]; + int exponent_len; + unsigned char exponent[OPENSSL_RSA_MAX_PUBEXP_BITS/8]; + + if (der_blob == NULL || der_size == 0) { + return EINVAL; + } + + d = (const unsigned char *) der_blob; + + cert = d2i_X509(NULL, &d, (int) der_size); + if (cert == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "d2i_X509 failed.\n"); + return EINVAL; + } + + /* TODO: verify certificate !!!!! */ + + cert_pub_key = X509_get_pubkey(cert); + if (cert_pub_key == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "X509_get_pubkey failed.\n"); + ret = EIO; + goto done; + } + + if (cert_pub_key->type != EVP_PKEY_RSA) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Expected RSA public key, found unsupported [%d].\n", + cert_pub_key->type); + ret = EINVAL; + goto done; + } + + modulus_len = BN_bn2bin(cert_pub_key->pkey.rsa->n, modulus); + exponent_len = BN_bn2bin(cert_pub_key->pkey.rsa->e, exponent); + + size = SSH_RSA_HEADER_LEN + 3 * sizeof(uint32_t) + + modulus_len + + exponent_len + + 1; /* see comment about missing 00 below */ + + buf = talloc_size(mem_ctx, size); + if (buf == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n"); + ret = ENOMEM; + goto done; + } + + c = 0; + + SAFEALIGN_SET_UINT32(buf, htobe32(SSH_RSA_HEADER_LEN), &c); + safealign_memcpy(&buf[c], SSH_RSA_HEADER, SSH_RSA_HEADER_LEN, &c); + SAFEALIGN_SET_UINT32(&buf[c], htobe32(exponent_len), &c); + safealign_memcpy(&buf[c], exponent, exponent_len, &c); + + /* Adding missing 00 which afaik is added to make sure + * the bigint is handled as positive number */ + /* TODO: make a better check if 00 must be added or not, e.g. ... & 0x80) + */ + SAFEALIGN_SET_UINT32(&buf[c], htobe32(modulus_len + 1), &c); + SAFEALIGN_SETMEM_VALUE(&buf[c], '\0', unsigned char, &c); + safealign_memcpy(&buf[c], modulus, modulus_len, &c); + + *key = buf; + *key_size = size; + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(buf); + } + EVP_PKEY_free(cert_pub_key); + X509_free(cert); + + return ret; +} diff --git a/src/util/cert/nss/cert.c b/src/util/cert/nss/cert.c index a20abf63a..1ada35b63 100644 --- a/src/util/cert/nss/cert.c +++ b/src/util/cert/nss/cert.c @@ -20,9 +20,13 @@ #include "util/util.h" +#include <nss.h> #include <cert.h> #include <base64.h> +#include <key.h> +#include <prerror.h> +#include "util/crypto/sss_crypto.h" #include "util/crypto/nss/nss_util.h" #define NS_CERT_HEADER "-----BEGIN CERTIFICATE-----" @@ -210,3 +214,129 @@ done: return ret; } + +#define SSH_RSA_HEADER "ssh-rsa" +#define SSH_RSA_HEADER_LEN (sizeof(SSH_RSA_HEADER) - 1) + +errno_t cert_to_ssh_key(TALLOC_CTX *mem_ctx, const char *ca_db, + const uint8_t *der_blob, size_t der_size, + uint8_t **key, size_t *key_size) +{ + CERTCertDBHandle *handle; + CERTCertificate *cert = NULL; + SECItem der_item; + SECKEYPublicKey *cert_pub_key = NULL; + int ret; + size_t size; + uint8_t *buf = NULL; + size_t c; + NSSInitContext *nss_ctx; + NSSInitParameters parameters = { 0 }; + parameters.length = sizeof (parameters); + SECStatus rv; + + if (der_blob == NULL || der_size == 0) { + return EINVAL; + } + + /* initialize NSS with context, we might have already called + * NSS_NoDB_Init() but for validation we need to have access to a DB with + * the trusted issuer cert. Only NSS_InitContext will really open the DB + * in this case. I'm not sure about how long validation might need e.g. if + * CRLs or OSCP is enabled, maybe it would be better to run validation in + * p11_child ? */ + nss_ctx = NSS_InitContext(ca_db, "", "", SECMOD_DB, ¶meters, + NSS_INIT_READONLY); + if (nss_ctx == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "NSS_InitContext failed [%d].\n", + PR_GetError()); + return EIO; + } + + handle = CERT_GetDefaultCertDB(); + + der_item.len = der_size; + der_item.data = discard_const(der_blob); + + cert = CERT_NewTempCertificate(handle, &der_item, NULL, PR_FALSE, PR_TRUE); + if (cert == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "CERT_NewTempCertificate failed.\n"); + ret = EINVAL; + goto done; + } + + rv = CERT_VerifyCertificateNow(handle, cert, PR_TRUE, + certificateUsageSSLClient, NULL, NULL); + if (rv != SECSuccess) { + DEBUG(SSSDBG_CRIT_FAILURE, "CERT_VerifyCertificateNow failed [%d].\n", + PR_GetError()); + ret = EACCES; + goto done; + } + + cert_pub_key = CERT_ExtractPublicKey(cert); + if (cert_pub_key == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "CERT_ExtractPublicKey failed.\n"); + ret = EIO; + goto done; + } + + if (cert_pub_key->keyType != rsaKey) { + DEBUG(SSSDBG_CRIT_FAILURE, + "Expected RSA public key, found unsupported [%d].\n", + cert_pub_key->keyType); + ret = EINVAL; + goto done; + } + + size = SSH_RSA_HEADER_LEN + 3 * sizeof(uint32_t) + + cert_pub_key->u.rsa.modulus.len + + cert_pub_key->u.rsa.publicExponent.len + + 1; /* see comment about missing 00 below */ + + buf = talloc_size(mem_ctx, size); + if (buf == NULL) { + DEBUG(SSSDBG_OP_FAILURE, "talloc_size failed.\n"); + ret = ENOMEM; + goto done; + } + + c = 0; + + SAFEALIGN_SET_UINT32(buf, htobe32(SSH_RSA_HEADER_LEN), &c); + safealign_memcpy(&buf[c], SSH_RSA_HEADER, SSH_RSA_HEADER_LEN, &c); + SAFEALIGN_SET_UINT32(&buf[c], + htobe32(cert_pub_key->u.rsa.publicExponent.len), &c); + safealign_memcpy(&buf[c], cert_pub_key->u.rsa.publicExponent.data, + cert_pub_key->u.rsa.publicExponent.len, &c); + + /* Looks like nss drops the leading 00 which afaik is added to make sure + * the bigint is handled as positive number */ + /* TODO: make a better check if 00 must be added or not, e.g. ... & 0x80) + */ + SAFEALIGN_SET_UINT32(&buf[c], + htobe32(cert_pub_key->u.rsa.modulus.len + 1 ), &c); + SAFEALIGN_SETMEM_VALUE(&buf[c], '\0', unsigned char, &c); + safealign_memcpy(&buf[c], cert_pub_key->u.rsa.modulus.data, + cert_pub_key->u.rsa.modulus.len, &c); + + *key = buf; + *key_size = size; + + ret = EOK; + +done: + if (ret != EOK) { + talloc_free(buf); + } + SECKEY_DestroyPublicKey(cert_pub_key); + CERT_DestroyCertificate(cert); + + rv = NSS_ShutdownContext(nss_ctx); + if (rv != SECSuccess) { + DEBUG(SSSDBG_OP_FAILURE, "NSS_ShutdownContext failed [%d].\n", + PR_GetError()); + } + + return ret; +} |