summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2011-08-17 16:45:22 +0200
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2011-08-17 16:47:39 +0200
commita8ef3bd75912e374cff57a3db0019514f63f9d17 (patch)
tree87f682b8c2015c2d7cd99b2edab1890df899ed86 /lib
parentabc8e1d0874e3a2046a5c69193946220edc4d051 (diff)
downloadgnutls-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.c88
-rw-r--r--lib/auth/cert.h23
-rw-r--r--lib/gnutls_cert.c15
-rw-r--r--lib/gnutls_state.c2
-rw-r--r--lib/gnutls_ui.c2
-rw-r--r--lib/gnutls_x509.c99
-rw-r--r--lib/openpgp/gnutls_openpgp.c7
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)
{