diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2011-08-17 16:45:22 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2011-08-17 16:47:39 +0200 |
commit | a8ef3bd75912e374cff57a3db0019514f63f9d17 (patch) | |
tree | 87f682b8c2015c2d7cd99b2edab1890df899ed86 /lib | |
parent | abc8e1d0874e3a2046a5c69193946220edc4d051 (diff) | |
download | gnutls-a8ef3bd75912e374cff57a3db0019514f63f9d17.tar.gz |
gnutls_certificate_set_x509_key_file() and friends support server name indication.
If multiple certificates are set using this function the proper one will be
selected during a handshake, with the limitation of a single name per certificate.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/auth/cert.c | 88 | ||||
-rw-r--r-- | lib/auth/cert.h | 23 | ||||
-rw-r--r-- | lib/gnutls_cert.c | 15 | ||||
-rw-r--r-- | lib/gnutls_state.c | 2 | ||||
-rw-r--r-- | lib/gnutls_ui.c | 2 | ||||
-rw-r--r-- | lib/gnutls_x509.c | 99 | ||||
-rw-r--r-- | lib/openpgp/gnutls_openpgp.c | 7 |
7 files changed, 170 insertions, 66 deletions
diff --git a/lib/auth/cert.c b/lib/auth/cert.c index 46ceba70b1..6aa37b91eb 100644 --- a/lib/auth/cert.c +++ b/lib/auth/cert.c @@ -238,10 +238,10 @@ _find_x509_cert (const gnutls_certificate_credentials_t cred, for (i = 0; i < cred->ncerts; i++) { - for (j = 0; j < cred->cert_list_length[i]; j++) + for (j = 0; j < cred->certs[i].cert_list_length; j++) { if ((result = - _gnutls_cert_get_issuer_dn (&cred->cert_list[i][j], + _gnutls_cert_get_issuer_dn (&cred->certs[i].cert_list[j], &odn)) < 0) { gnutls_assert (); @@ -256,7 +256,7 @@ _find_x509_cert (const gnutls_certificate_credentials_t cred, * the cert is our cert! */ cert_pk = - gnutls_pubkey_get_pk_algorithm (cred->cert_list[i][0].pubkey, + gnutls_pubkey_get_pk_algorithm (cred->certs[i].cert_list[0].pubkey, NULL); if ((memcmp (odn.data, data, size) == 0) && @@ -298,7 +298,7 @@ _find_openpgp_cert (const gnutls_certificate_credentials_t cred, for (i = 0; i < cred->ncerts; i++) { - for (j = 0; j < cred->cert_list_length[i]; j++) + for (j = 0; j < cred->certs[i].cert_list_length; j++) { /* If the *_SIGN algorithm matches @@ -306,9 +306,9 @@ _find_openpgp_cert (const gnutls_certificate_credentials_t cred, */ if ((_gnutls_check_pk_algo_in_list (pk_algos, pk_algos_length, - gnutls_pubkey_get_pk_algorithm (cred->cert_list[i][0].pubkey, + gnutls_pubkey_get_pk_algorithm (cred->certs[i].cert_list[0].pubkey, NULL)) == 0) - && (cred->cert_list[i][0].type == GNUTLS_CRT_OPENPGP)) + && (cred->certs[i].cert_list[0].type == GNUTLS_CRT_OPENPGP)) { *indx = i; break; @@ -757,8 +757,8 @@ _select_client_cert (gnutls_session_t session, if (indx >= 0) { _gnutls_selected_certs_set (session, - &cred->cert_list[indx][0], - cred->cert_list_length[indx], + &cred->certs[indx].cert_list[0], + cred->certs[indx].cert_list_length, cred->pkey[indx], 0); } else @@ -2073,6 +2073,25 @@ _gnutls_selected_certs_set (gnutls_session_t session, } +static void get_server_name(gnutls_session_t session, uint8_t* name, size_t max_name_size) +{ +int ret, i; +size_t max_name; +unsigned int type; + + ret = 0; + for (i=0; !(ret<0);i++) + { + max_name = max_name_size; + ret = gnutls_server_name_get (session, name, &max_name, &type, i); + if (ret >= 0 && type == GNUTLS_NAME_DNS) + return; + } + + name[0] = 0; + + return; +} /* finds the most appropriate certificate in the cert list. * The 'appropriate' is defined by the user. @@ -2092,6 +2111,7 @@ _gnutls_server_select_cert (gnutls_session_t session, unsigned i, j; int idx, ret; gnutls_certificate_credentials_t cred; + char server_name[MAX_CN]; cred = (gnutls_certificate_credentials_t) _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); @@ -2114,9 +2134,43 @@ _gnutls_server_select_cert (gnutls_session_t session, } /* Otherwise... */ + + get_server_name(session, server_name, sizeof(server_name)); idx = -1; /* default is use no certificate */ + /* find certificates that match the requested server_name + */ + + if (server_name[0] != 0) + { + for (i = 0; i < cred->ncerts; i++) + { +fprintf(stderr, "\n*** name[i]: %s, req: %s\n\n", cred->certs[i].name, server_name); + if (cred->certs[i].name != NULL && strcasecmp(cred->certs[i].name, server_name) == 0) + { + /* if requested algorithms are also compatible select it */ + gnutls_pk_algorithm pk = + gnutls_pubkey_get_pk_algorithm (cred->certs[i].cert_list[0].pubkey, + NULL); + + _gnutls_handshake_log("HSK[%p]: Requested server name: '%s', ctype: %s (%d)", session, server_name, + gnutls_certificate_type_get_name (session->security_parameters.cert_type), + session->security_parameters.cert_type); + + if (session->security_parameters.cert_type == cred->certs[i].cert_list[0].type) + { + for (j = 0; j < pk_algos_size; j++) + if (pk_algos[j] == pk) + { + idx = i; + goto finished; + } + } + } + } + } + for (j = 0; j < pk_algos_size; j++) { _gnutls_handshake_log @@ -2129,42 +2183,40 @@ _gnutls_server_select_cert (gnutls_session_t session, for (i = 0; i < cred->ncerts; i++) { gnutls_pk_algorithm pk = - gnutls_pubkey_get_pk_algorithm (cred->cert_list[i][0].pubkey, + gnutls_pubkey_get_pk_algorithm (cred->certs[i].cert_list[0].pubkey, NULL); /* find one compatible certificate */ _gnutls_handshake_log ("HSK[%p]: certificate[%d] PK algorithm: %s (%d) - ctype: %s (%d)\n", session, i, gnutls_pk_get_name (pk), pk, - gnutls_certificate_type_get_name (cred->cert_list[i][0].type), - cred->cert_list[i][0].type); + gnutls_certificate_type_get_name (cred->certs[i].cert_list[0].type), + cred->certs[i].cert_list[0].type); if (pk_algos[j] == pk) { /* if cert type matches */ /* *INDENT-OFF* */ - if (session->security_parameters.cert_type == cred->cert_list[i][0].type) + if (session->security_parameters.cert_type == cred->certs[i].cert_list[0].type) { idx = i; - break; + goto finished; } /* *INDENT-ON* */ } } - - if (idx != -1) - break; } /* store the certificate pointer for future use, in the handshake. * (This will allow not calling this callback again.) */ +finished: if (idx >= 0) { _gnutls_selected_certs_set (session, - &cred->cert_list[idx][0], - cred->cert_list_length[idx], + &cred->certs[idx].cert_list[0], + cred->certs[idx].cert_list_length, cred->pkey[idx], 0); } else diff --git a/lib/auth/cert.h b/lib/auth/cert.h index bb18915848..5b0d15f487 100644 --- a/lib/auth/cert.h +++ b/lib/auth/cert.h @@ -29,6 +29,12 @@ #include <gnutls/abstract.h> #include <gnutls/compat.h> +typedef struct { + gnutls_pcert_st * cert_list; /* a certificate chain */ + unsigned int cert_list_length; /* its length */ + char* name; /* the name of the first certificate - only 1 allowed*/ +} certs_st; + /* This structure may be complex, but it's the only way to * support a server that has multiple certificates */ @@ -41,19 +47,8 @@ typedef struct gnutls_certificate_credentials_st */ gnutls_params_function *params_func; - gnutls_pcert_st **cert_list; - /* contains a list of a list of certificates. - * eg (X509): [0] certificate1, certificate11, certificate111 - * (if more than one, one certificate certifies the one before) - * [1] certificate2, certificate22, ... - */ - unsigned *cert_list_length; - /* contains the number of the certificates in a - * row (should be 1 for OpenPGP keys). - */ - unsigned ncerts; /* contains the number of columns in cert_list. - * This is the same with the number of pkeys. - */ + certs_st *certs; + unsigned ncerts; /* the number of certs */ gnutls_privkey_t *pkey; /* private keys. It contains ncerts private @@ -158,7 +153,7 @@ int _gnutls_get_auth_info_pcert (gnutls_pcert_st* gcert, cert_auth_info_t info); int certificate_credential_append_crt_list (gnutls_certificate_credentials_t - res, gnutls_pcert_st* crt, int nr); + res, const char* name, gnutls_pcert_st* crt, int nr); int certificate_credentials_append_pkey (gnutls_certificate_credentials_t res, gnutls_privkey_t pkey); diff --git a/lib/gnutls_cert.c b/lib/gnutls_cert.c index 8fba58d3ad..c5a872ffe3 100644 --- a/lib/gnutls_cert.c +++ b/lib/gnutls_cert.c @@ -58,18 +58,16 @@ gnutls_certificate_free_keys (gnutls_certificate_credentials_t sc) for (i = 0; i < sc->ncerts; i++) { - for (j = 0; j < sc->cert_list_length[i]; j++) + for (j = 0; j < sc->certs[i].cert_list_length; j++) { - gnutls_pcert_deinit (&sc->cert_list[i][j]); + gnutls_pcert_deinit (&sc->certs[i].cert_list[j]); } - gnutls_free (sc->cert_list[i]); + gnutls_free (sc->certs[i].cert_list); + gnutls_free (sc->certs[i].name); } - gnutls_free (sc->cert_list_length); - sc->cert_list_length = NULL; - - gnutls_free (sc->cert_list); - sc->cert_list = NULL; + gnutls_free (sc->certs); + sc->certs = NULL; for (i = 0; i < sc->ncerts; i++) { @@ -80,7 +78,6 @@ gnutls_certificate_free_keys (gnutls_certificate_credentials_t sc) sc->pkey = NULL; sc->ncerts = 0; - } /** diff --git a/lib/gnutls_state.c b/lib/gnutls_state.c index d4106dfcea..e1b3ac58ac 100644 --- a/lib/gnutls_state.c +++ b/lib/gnutls_state.c @@ -193,7 +193,7 @@ _gnutls_session_cert_type_supported (gnutls_session_t session, { for (i = 0; i < cred->ncerts; i++) { - if (cred->cert_list[i][0].type == cert_type) + if (cred->certs[i].cert_list[0].type == cert_type) { cert_found = 1; break; diff --git a/lib/gnutls_ui.c b/lib/gnutls_ui.c index a7dfb32652..726846736b 100644 --- a/lib/gnutls_ui.c +++ b/lib/gnutls_ui.c @@ -468,7 +468,7 @@ gnutls_certificate_get_ours (gnutls_session_t session) cred = (gnutls_certificate_credentials_t) _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL); - if (cred == NULL || cred->cert_list == NULL) + if (cred == NULL || cred->certs == NULL) { gnutls_assert (); return NULL; diff --git a/lib/gnutls_x509.c b/lib/gnutls_x509.c index a80ec0cdf6..774454a446 100644 --- a/lib/gnutls_x509.c +++ b/lib/gnutls_x509.c @@ -196,7 +196,7 @@ _gnutls_x509_cert_verify_peers (gnutls_session_t session, static int _gnutls_check_key_cert_match (gnutls_certificate_credentials_t res) { - unsigned int pk = gnutls_pubkey_get_pk_algorithm(res->cert_list[res->ncerts - 1][0].pubkey, NULL); + unsigned int pk = gnutls_pubkey_get_pk_algorithm(res->certs[res->ncerts-1].cert_list[0].pubkey, NULL); if (gnutls_privkey_get_pk_algorithm (res->pkey[res->ncerts - 1], NULL) != pk) @@ -208,6 +208,55 @@ _gnutls_check_key_cert_match (gnutls_certificate_credentials_t res) return 0; } +/* Returns the name of the certificate of a null name + */ +static void get_x509_name(gnutls_x509_crt_t crt, char* name, size_t max_name_size) +{ +size_t max_size; +int i, ret = 0; + + name[0] = 0; + + for (i = 0; !(ret < 0); i++) + { + max_size = max_name_size; + + ret = gnutls_x509_crt_get_subject_alt_name(crt, 0, name, &max_size, NULL); + if (ret == GNUTLS_SAN_DNSNAME) + return; + } + + max_size = max_name_size; + gnutls_x509_crt_get_dn_by_oid (crt, OID_X520_COMMON_NAME, 0, 0, name, &max_size); +} + +static void get_x509_name_raw(gnutls_datum_t *raw, gnutls_x509_crt_fmt_t type, char* name, size_t max_name_size) +{ +int ret; +gnutls_x509_crt_t crt; + + name[0] = 0; + + ret = gnutls_x509_crt_init (&crt); + if (ret < 0) + { + gnutls_assert (); + return; + } + + ret = gnutls_x509_crt_import (crt, raw, type); + if (ret < 0) + { + gnutls_assert (); + gnutls_x509_crt_deinit (crt); + return; + } + + get_x509_name(crt, name, max_name_size); + gnutls_x509_crt_deinit (crt); + return; +} + /* Reads a DER encoded certificate list from memory and stores it to a * gnutls_cert structure. Returns the number of certificates parsed. */ @@ -219,6 +268,7 @@ parse_der_cert_mem (gnutls_certificate_credentials_t res, gnutls_x509_crt_t crt; gnutls_pcert_st *ccert; int ret; + char name[MAX_CN]; ccert = gnutls_malloc (sizeof (*ccert)); if (ccert == NULL) @@ -245,6 +295,8 @@ parse_der_cert_mem (gnutls_certificate_credentials_t res, goto cleanup; } + get_x509_name(crt, name, sizeof(name)); + ret = gnutls_pcert_import_x509 (ccert, crt, 0); gnutls_x509_crt_deinit (crt); @@ -254,7 +306,7 @@ parse_der_cert_mem (gnutls_certificate_credentials_t res, goto cleanup; } - ret = certificate_credential_append_crt_list (res, ccert, 1); + ret = certificate_credential_append_crt_list (res, name, ccert, 1); if (ret < 0) { gnutls_assert (); @@ -280,6 +332,7 @@ parse_pem_cert_mem (gnutls_certificate_credentials_t res, gnutls_datum_t tmp; int ret, count, i; gnutls_pcert_st *certs = NULL; + char name[MAX_CN]; /* move to the certificate */ @@ -312,6 +365,8 @@ parse_pem_cert_mem (gnutls_certificate_credentials_t res, tmp.data = (void*)ptr; tmp.size = size; + if (count == 0) get_x509_name_raw(&tmp, GNUTLS_X509_FMT_PEM, name, sizeof(name)); + ret = gnutls_pcert_import_x509_raw (&certs[count], &tmp, GNUTLS_X509_FMT_PEM, 0); if (ret < 0) { @@ -345,7 +400,7 @@ parse_pem_cert_mem (gnutls_certificate_credentials_t res, } while (ptr != NULL); - ret = certificate_credential_append_crt_list (res, certs, count); + ret = certificate_credential_append_crt_list (res, name, certs, count); if (ret < 0) { gnutls_assert (); @@ -620,6 +675,7 @@ read_cert_url (gnutls_certificate_credentials_t res, const char *url) int ret; gnutls_x509_crt_t crt; gnutls_pcert_st *ccert; + char name[MAX_CN]; ccert = gnutls_malloc (sizeof (*ccert)); if (ccert == NULL) @@ -650,6 +706,8 @@ read_cert_url (gnutls_certificate_credentials_t res, const char *url) return ret; } + get_x509_name(crt, name, sizeof(name)); + ret = gnutls_pcert_import_x509 (ccert, crt, 0); gnutls_x509_crt_deinit (crt); @@ -660,7 +718,7 @@ read_cert_url (gnutls_certificate_credentials_t res, const char *url) return ret; } - ret = certificate_credential_append_crt_list (res, ccert, 1); + ret = certificate_credential_append_crt_list (res, name, ccert, 1); if (ret < 0) { gnutls_assert (); @@ -855,7 +913,7 @@ cleanup: int certificate_credential_append_crt_list (gnutls_certificate_credentials_t res, - gnutls_pcert_st * crt, int nr) + const char* name, gnutls_pcert_st * crt, int nr) { int ret; @@ -863,27 +921,21 @@ int ret; if (ret < 0) return gnutls_assert_val(ret); - res->cert_list = gnutls_realloc_fast (res->cert_list, - (1 + - res->ncerts) * - sizeof (gnutls_pcert_st *)); - if (res->cert_list == NULL) + res->certs = gnutls_realloc_fast (res->certs, + (1 + res->ncerts) * + sizeof (certs_st)); + if (res->certs == NULL) { gnutls_assert (); return GNUTLS_E_MEMORY_ERROR; } - res->cert_list_length = gnutls_realloc_fast (res->cert_list_length, - (1 + - res->ncerts) * sizeof (int)); - if (res->cert_list_length == NULL) - { - gnutls_assert (); - return GNUTLS_E_MEMORY_ERROR; - } - - res->cert_list[res->ncerts] = crt; - res->cert_list_length[res->ncerts] = nr; + res->certs[res->ncerts].cert_list = crt; + res->certs[res->ncerts].cert_list_length = nr; + if (name[0] != 0) + res->certs[res->ncerts].name = gnutls_strdup(name); + else + res->certs[res->ncerts].name = NULL; return 0; @@ -935,6 +987,7 @@ gnutls_certificate_set_x509_key (gnutls_certificate_credentials_t res, int ret, i; gnutls_privkey_t pkey; gnutls_pcert_st *pcerts = NULL; + char name[MAX_CN]; /* this should be first */ @@ -967,6 +1020,8 @@ gnutls_certificate_set_x509_key (gnutls_certificate_credentials_t res, return GNUTLS_E_MEMORY_ERROR; } + get_x509_name(cert_list[0], name, sizeof(name)); + for (i = 0; i < cert_list_size; i++) { ret = gnutls_pcert_import_x509 (&pcerts[i], cert_list[i], 0); @@ -977,7 +1032,7 @@ gnutls_certificate_set_x509_key (gnutls_certificate_credentials_t res, } } - ret = certificate_credential_append_crt_list (res, pcerts, cert_list_size); + ret = certificate_credential_append_crt_list (res, name, pcerts, cert_list_size); if (ret < 0) { gnutls_assert (); diff --git a/lib/openpgp/gnutls_openpgp.c b/lib/openpgp/gnutls_openpgp.c index 44ba47b40c..9696e67d6d 100644 --- a/lib/openpgp/gnutls_openpgp.c +++ b/lib/openpgp/gnutls_openpgp.c @@ -90,6 +90,8 @@ gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t res, int ret; gnutls_privkey_t privkey; gnutls_pcert_st *ccert; + char name[MAX_CN]; + size_t name_size = sizeof(name); /* this should be first */ @@ -110,6 +112,9 @@ gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t res, return ret; } + ret = gnutls_openpgp_crt_get_name(crt, 0, name, &name_size); + if (ret < 0) + name[0] = 0; ccert = gnutls_calloc (1, sizeof (gnutls_pcert_st)); if (ccert == NULL) @@ -130,7 +135,7 @@ gnutls_certificate_set_openpgp_key (gnutls_certificate_credentials_t res, ret = certificate_credentials_append_pkey (res, privkey); if (ret >= 0) - ret = certificate_credential_append_crt_list (res, ccert, 1); + ret = certificate_credential_append_crt_list (res, name, ccert, 1); if (ret < 0) { |