diff options
Diffstat (limited to 'lib/cert-cred.c')
-rw-r--r-- | lib/cert-cred.c | 197 |
1 files changed, 196 insertions, 1 deletions
diff --git a/lib/cert-cred.c b/lib/cert-cred.c index 2d7009b2e5..f04ded4c04 100644 --- a/lib/cert-cred.c +++ b/lib/cert-cred.c @@ -40,7 +40,202 @@ #include <str_array.h> #include <x509/verify-high.h> #include "x509/x509_int.h" +#include "x509/common.h" #include "dh.h" +#include "cert-cred.h" + + +/* + * Adds a public/private key pair to a certificate credential + */ +int +_gnutls_certificate_credential_append_keypair(gnutls_certificate_credentials_t res, + gnutls_privkey_t key, + gnutls_str_array_t names, + gnutls_pcert_st * crt, + int nr) +{ + res->sorted_cert_idx = gnutls_realloc_fast(res->sorted_cert_idx, + (1 + res->ncerts) * + sizeof(unsigned int)); + if (res->sorted_cert_idx == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + res->certs = gnutls_realloc_fast(res->certs, + (1 + res->ncerts) * + sizeof(certs_st)); + if (res->certs == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + memset(&res->certs[res->ncerts], 0, sizeof(res->certs[0])); + + res->certs[res->ncerts].cert_list = crt; + res->certs[res->ncerts].cert_list_length = nr; + res->certs[res->ncerts].names = names; + res->certs[res->ncerts].pkey = key; + + /* move RSA-PSS certificates before any RSA key. + * Note that we cannot assume that any previous pointers + * to sorted list are ok, due to the realloc in res->certs. */ + if (crt->pubkey->params.algo == GNUTLS_PK_RSA_PSS) { + unsigned i,ridx; + unsigned tmp; + + for (i=0;i<res->ncerts;i++) { + ridx = res->sorted_cert_idx[i]; + + if (res->certs[ridx].cert_list->pubkey->params.algo == GNUTLS_PK_RSA) { + tmp = ridx; + res->sorted_cert_idx[i] = res->ncerts; + res->sorted_cert_idx[res->ncerts] = tmp; + goto finish; + } + } + } + + /* otherwise append it normally on the end */ + res->sorted_cert_idx[res->ncerts] = res->ncerts; + + finish: + return 0; + +} + + +/** + * gnutls_certificate_set_key: + * @res: is a #gnutls_certificate_credentials_t type. + * @names: is an array of DNS names belonging to the public-key (NULL if none) + * @names_size: holds the size of the names list + * @pcert_list: contains a certificate list (chain) or raw public-key + * @pcert_list_size: holds the size of the certificate list + * @key: is a #gnutls_privkey_t key corresponding to the first public-key in pcert_list + * + * This function sets a public/private key pair in the + * gnutls_certificate_credentials_t type. The given public key may be encapsulated + * in a certificate or can be given as a raw key. This function may be + * called more than once, in case multiple key pairs exist for + * the server. For clients that want to send more than their own end- + * entity certificate (e.g., also an intermediate CA cert), the full + * certificate chain must be provided in @pcert_list. + * + * Note that the @key will become part of the credentials structure and must + * not be deallocated. It will be automatically deallocated when the @res structure + * is deinitialized. + * + * If this function fails, the @res structure is at an undefined state and it must + * not be reused to load other keys or certificates. + * + * Note that, this function by default returns zero on success and a negative value on error. + * Since 3.5.6, when the flag %GNUTLS_CERTIFICATE_API_V2 is set using gnutls_certificate_set_flags() + * it returns an index (greater or equal to zero). That index can be used for other functions to refer to the added key-pair. + * + * Since GnuTLS 3.6.6 this function also handles raw public keys. + * + * Returns: On success this functions returns zero, and otherwise a negative value on error (see above for modifying that behavior). + * + * Since: 3.0 + **/ +int +gnutls_certificate_set_key(gnutls_certificate_credentials_t res, + const char **names, + int names_size, + gnutls_pcert_st * pcert_list, + int pcert_list_size, + gnutls_privkey_t key) +{ + int ret, i; + gnutls_str_array_t str_names; + gnutls_pcert_st *new_pcert_list; + + /* Sanity checks */ + // Check for a valid credential struct + if (res == NULL) { + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + } + + // A complete key pair must be given + if (pcert_list == NULL || key == NULL) { + return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS); + } + + /* Process the names, if any */ + _gnutls_str_array_init(&str_names); + + if (names != NULL && names_size > 0) { + for (i = 0; i < names_size; i++) { + ret = + _gnutls_str_array_append_idna(&str_names, names[i], + strlen(names[i])); + if (ret < 0) { + ret = gnutls_assert_val(ret); + goto cleanup; + } + } + } else if (names == NULL && pcert_list[0].type == GNUTLS_CRT_X509) { + gnutls_x509_crt_t crt; + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = gnutls_x509_crt_import(crt, &pcert_list[0].cert, GNUTLS_X509_FMT_DER); + if (ret < 0) { + gnutls_assert(); + gnutls_x509_crt_deinit(crt); + goto cleanup; + } + + ret = _gnutls_get_x509_name(crt, &str_names); + gnutls_x509_crt_deinit(crt); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + if (res->pin.cb) + gnutls_privkey_set_pin_function(key, res->pin.cb, + res->pin.data); + + new_pcert_list = gnutls_malloc(sizeof(gnutls_pcert_st) * pcert_list_size); + if (new_pcert_list == NULL) { + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + } + memcpy(new_pcert_list, pcert_list, sizeof(gnutls_pcert_st) * pcert_list_size); + + ret = + _gnutls_certificate_credential_append_keypair(res, key, str_names, + new_pcert_list, + pcert_list_size); + if (ret < 0) { + gnutls_assert(); + gnutls_free(new_pcert_list); + goto cleanup; + } + + res->ncerts++; + + /* Unlike gnutls_certificate_set_x509_key, we deinitialize everything + * local after a failure. That is because the caller is responsible for + * freeing these values after a failure, and if we keep references we + * lead to double freeing */ + if ((ret = _gnutls_check_key_cert_match(res)) < 0) { + gnutls_assert(); + gnutls_free(new_pcert_list); + res->ncerts--; + goto cleanup; + } + + CRED_RET_SUCCESS(res); + + cleanup: + _gnutls_str_array_clear(&str_names); + return ret; +} /** * gnutls_certificate_free_keys: @@ -199,7 +394,7 @@ gnutls_certificate_free_credentials(gnutls_certificate_credentials_t sc) // Check for valid pointer and otherwise do nothing if (sc == NULL) return; - + gnutls_x509_trust_list_deinit(sc->tlist, 1); gnutls_certificate_free_keys(sc); memset(sc->pin_tmp, 0, sizeof(sc->pin_tmp)); |