]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
gnutls_certificate_set_x509_key_file() and friends support server name indication.
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 17 Aug 2011 14:45:22 +0000 (16:45 +0200)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Wed, 17 Aug 2011 14:47:39 +0000 (16:47 +0200)
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.

lib/auth/cert.c
lib/auth/cert.h
lib/gnutls_cert.c
lib/gnutls_state.c
lib/gnutls_ui.c
lib/gnutls_x509.c
lib/openpgp/gnutls_openpgp.c

index 46ceba70b1ca32848b81284a95c30007a8f6b917..6aa37b91eb2234e6fc3036ae9e497fbfb3f586ae 100644 (file)
@@ -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
index bb18915848b494e208812941be02c2fc5aaa1312..5b0d15f487313b6fd549ec34351929a60c1f1bf6 100644 (file)
 #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);
 
index 8fba58d3ad2138a5ff536b8b07925146ef3f9d52..c5a872ffe3c17f32d80a6de65c89d5249f3f27af 100644 (file)
@@ -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;
-
 }
 
 /**
index d4106dfceaf2a2555a2a3649858b3d8dc2b72a20..e1b3ac58ac3883ba8172b063905d210d57c8bd1a 100644 (file)
@@ -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;
index a7dfb326525973027921e5679c8b193b53786287..726846736bb8202ca2fb7aa934fa30eface2e143 100644 (file)
@@ -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;
index a80ec0cdf6580d95ab716003139ff1c813e681b0..774454a4461c184e19b64b623ba014c8eaf8f968 100644 (file)
@@ -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 ();
index 44ba47b40ceb49ebaf9a6bb4a162fb9c5f02fe3f..9696e67d6d86461c0c2cea256c53ebc8b49b3f0f 100644 (file)
@@ -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)
     {