Imported Upstream version 3.3.5
[platform/upstream/gnutls.git] / lib / gnutls_cert.c
index ac25051..9ed0609 100644 (file)
@@ -7,7 +7,7 @@
  *
  * The GnuTLS is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 3 of
+ * as published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This library is distributed in the hope that it will be useful, but
 #include <gnutls_auth.h>
 #include <gnutls_x509.h>
 #include <gnutls_str_array.h>
+#include <x509/verify-high.h>
 #include "x509/x509_int.h"
 #ifdef ENABLE_OPENPGP
 #include "openpgp/gnutls_openpgp.h"
 #endif
+#include "gettext.h"
+#define _(String) dgettext (PACKAGE, String)
 
 /**
  * gnutls_certificate_free_keys:
  * TLS negotiation that uses the credentials is in progress.
  *
  **/
-void
-gnutls_certificate_free_keys (gnutls_certificate_credentials_t sc)
+void gnutls_certificate_free_keys(gnutls_certificate_credentials_t sc)
 {
-  unsigned i, j;
-
-  for (i = 0; i < sc->ncerts; i++)
-    {
-      for (j = 0; j < sc->certs[i].cert_list_length; j++)
-        {
-          gnutls_pcert_deinit (&sc->certs[i].cert_list[j]);
-        }
-      gnutls_free (sc->certs[i].cert_list);
-      _gnutls_str_array_clear (&sc->certs[i].names);
-    }
-
-  gnutls_free (sc->certs);
-  sc->certs = NULL;
-
-  for (i = 0; i < sc->ncerts; i++)
-    {
-      gnutls_privkey_deinit (sc->pkey[i]);
-    }
-
-  gnutls_free (sc->pkey);
-  sc->pkey = NULL;
-
-  sc->ncerts = 0;
+       unsigned i, j;
+
+       for (i = 0; i < sc->ncerts; i++) {
+               for (j = 0; j < sc->certs[i].cert_list_length; j++) {
+                       gnutls_pcert_deinit(&sc->certs[i].cert_list[j]);
+               }
+               gnutls_free(sc->certs[i].cert_list);
+               _gnutls_str_array_clear(&sc->certs[i].names);
+       }
+
+       gnutls_free(sc->certs);
+       sc->certs = NULL;
+
+       for (i = 0; i < sc->ncerts; i++) {
+               gnutls_privkey_deinit(sc->pkey[i]);
+       }
+
+       gnutls_free(sc->pkey);
+       sc->pkey = NULL;
+
+       sc->ncerts = 0;
 }
 
 /**
@@ -89,11 +88,10 @@ gnutls_certificate_free_keys (gnutls_certificate_credentials_t sc)
  * gnutls_certificate_verify_peers2() may call this to save some
  * memory.
  **/
-void
-gnutls_certificate_free_cas (gnutls_certificate_credentials_t sc)
+void gnutls_certificate_free_cas(gnutls_certificate_credentials_t sc)
 {
-  /* FIXME: do nothing for now */
-  return;
+       /* FIXME: do nothing for now */
+       return;
 }
 
 /**
@@ -111,10 +109,52 @@ gnutls_certificate_free_cas (gnutls_certificate_credentials_t sc)
  * Since: 3.0
  **/
 int
-gnutls_certificate_get_issuer (gnutls_certificate_credentials_t sc,
-  gnutls_x509_crt_t cert, gnutls_x509_crt_t* issuer, unsigned int flags)
+gnutls_certificate_get_issuer(gnutls_certificate_credentials_t sc,
+                             gnutls_x509_crt_t cert,
+                             gnutls_x509_crt_t * issuer,
+                             unsigned int flags)
 {
-  return gnutls_x509_trust_list_get_issuer(sc->tlist, cert, issuer, flags);
+       return gnutls_x509_trust_list_get_issuer(sc->tlist, cert, issuer,
+                                                flags);
+}
+
+/**
+ * gnutls_certificate_get_crt_raw:
+ * @sc: is a #gnutls_certificate_credentials_t structure.
+ * @idx1: the index of the certificate if multiple are present
+ * @idx2: the index in the certificate list. Zero gives the server's certificate.
+ * @cert: Will hold the DER encoded certificate.
+ *
+ * This function will return the DER encoded certificate of the
+ * server or any other certificate on its certificate chain (based on @idx2).
+ * The returned data should be treated as constant and only accessible during the lifetime
+ * of @sc.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value. In case the indexes are out of bounds %GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE
+ *   is returned.
+ *
+ * Since: 3.2.5
+ **/
+int
+gnutls_certificate_get_crt_raw(gnutls_certificate_credentials_t sc,
+                              unsigned idx1,
+                              unsigned idx2, gnutls_datum_t * cert)
+{
+       if (idx1 >= sc->ncerts)
+               return
+                   gnutls_assert_val
+                   (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+       if (idx2 >= sc->certs[idx1].cert_list_length)
+               return
+                   gnutls_assert_val
+                   (GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
+
+       cert->data = sc->certs[idx1].cert_list[idx2].cert.data;
+       cert->size = sc->certs[idx1].cert_list[idx2].cert.size;
+
+       return 0;
 }
 
 /**
@@ -131,48 +171,9 @@ gnutls_certificate_get_issuer (gnutls_certificate_credentials_t sc,
  * CA names are used by servers to advertise the CAs they support to
  * clients.
  **/
-void
-gnutls_certificate_free_ca_names (gnutls_certificate_credentials_t sc)
-{
-  _gnutls_free_datum (&sc->x509_rdn_sequence);
-}
-
-/*-
- * _gnutls_certificate_get_rsa_params - Returns the RSA parameters pointer
- * @rsa_params: holds the RSA parameters or NULL.
- * @func: function to retrieve the parameters or NULL.
- * @session: The session.
- *
- * This function will return the rsa parameters pointer.
- -*/
-gnutls_rsa_params_t
-_gnutls_certificate_get_rsa_params (gnutls_rsa_params_t rsa_params,
-                                    gnutls_params_function * func,
-                                    gnutls_session_t session)
+void gnutls_certificate_free_ca_names(gnutls_certificate_credentials_t sc)
 {
-  gnutls_params_st params;
-  int ret;
-
-  if (session->internals.params.rsa_params)
-    {
-      return session->internals.params.rsa_params;
-    }
-
-  if (rsa_params)
-    {
-      session->internals.params.rsa_params = rsa_params;
-    }
-  else if (func)
-    {
-      ret = func (session, GNUTLS_PARAMS_RSA_EXPORT, &params);
-      if (ret == 0 && params.type == GNUTLS_PARAMS_RSA_EXPORT)
-        {
-          session->internals.params.rsa_params = params.params.rsa_export;
-          session->internals.params.free_rsa_params = params.deinit;
-        }
-    }
-
-  return session->internals.params.rsa_params;
+       _gnutls_free_datum(&sc->tlist->x509_rdn_sequence);
 }
 
 
@@ -188,17 +189,17 @@ _gnutls_certificate_get_rsa_params (gnutls_rsa_params_t rsa_params,
  * function).
  **/
 void
-gnutls_certificate_free_credentials (gnutls_certificate_credentials_t sc)
+gnutls_certificate_free_credentials(gnutls_certificate_credentials_t sc)
 {
-  gnutls_x509_trust_list_deinit(sc->tlist, 1);
-  gnutls_certificate_free_keys (sc);
-  gnutls_certificate_free_ca_names (sc);
-
+       gnutls_x509_trust_list_deinit(sc->tlist, 1);
+       gnutls_certificate_free_keys(sc);
+       gnutls_free(sc->ocsp_response_file);
+       memset(sc->pin_tmp, 0, sizeof(sc->pin_tmp));
 #ifdef ENABLE_OPENPGP
-  gnutls_openpgp_keyring_deinit (sc->keyring);
+       gnutls_openpgp_keyring_deinit(sc->keyring);
 #endif
 
-  gnutls_free (sc);
+       gnutls_free(sc);
 }
 
 
@@ -212,27 +213,26 @@ gnutls_certificate_free_credentials (gnutls_certificate_credentials_t sc)
  * Returns: %GNUTLS_E_SUCCESS on success, or an error code.
  **/
 int
-gnutls_certificate_allocate_credentials (gnutls_certificate_credentials_t *
-                                         res)
+gnutls_certificate_allocate_credentials(gnutls_certificate_credentials_t *
+                                       res)
 {
-int ret;
+       int ret;
 
-  *res = gnutls_calloc (1, sizeof (certificate_credentials_st));
+       *res = gnutls_calloc(1, sizeof(certificate_credentials_st));
 
-  if (*res == NULL)
-    return GNUTLS_E_MEMORY_ERROR;
+       if (*res == NULL)
+               return GNUTLS_E_MEMORY_ERROR;
 
-  ret = gnutls_x509_trust_list_init( &(*res)->tlist, 0);
-  if (ret < 0)
-    {
-      gnutls_assert();
-      gnutls_free(*res);
-      return GNUTLS_E_MEMORY_ERROR;
-    }
-  (*res)->verify_bits = DEFAULT_VERIFY_BITS;
-  (*res)->verify_depth = DEFAULT_VERIFY_DEPTH;
+       ret = gnutls_x509_trust_list_init(&(*res)->tlist, 0);
+       if (ret < 0) {
+               gnutls_assert();
+               gnutls_free(*res);
+               return GNUTLS_E_MEMORY_ERROR;
+       }
+       (*res)->verify_bits = DEFAULT_MAX_VERIFY_BITS;
+       (*res)->verify_depth = DEFAULT_MAX_VERIFY_DEPTH;
 
-  return 0;
+       return 0;
 }
 
 
@@ -243,51 +243,48 @@ int ret;
  * extensions in order to disable unneded algorithms.
  */
 int
-_gnutls_selected_cert_supported_kx (gnutls_session_t session,
-                                    gnutls_kx_algorithm_t * alg,
-                                    int *alg_size)
+_gnutls_selected_cert_supported_kx(gnutls_session_t session,
+                                  gnutls_kx_algorithm_t * alg,
+                                  int *alg_size)
 {
-  gnutls_kx_algorithm_t kx;
-  gnutls_pk_algorithm_t pk, cert_pk;
-  gnutls_pcert_st *cert;
-  int i;
-
-  if (session->internals.selected_cert_list_length == 0)
-    {
-      *alg_size = 0;
-      return 0;
-    }
-
-  cert = &session->internals.selected_cert_list[0];
-  cert_pk = gnutls_pubkey_get_pk_algorithm(cert->pubkey, NULL);
-  i = 0;
-
-  for (kx = 0; kx < MAX_ALGOS; kx++)
-    {
-      pk = _gnutls_map_pk_get_pk (kx);
-      if (pk == cert_pk)
-        {
-          /* then check key usage */
-          if (_gnutls_check_key_usage (cert, kx) == 0)
-            {
-              alg[i] = kx;
-              i++;
-              
-              if (i > *alg_size)
-                return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
-            }
-        }
-    }
-
-  if (i == 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INVALID_REQUEST;
-    }
-
-  *alg_size = i;
-
-  return 0;
+       unsigned kx;
+       gnutls_pk_algorithm_t pk, cert_pk;
+       gnutls_pcert_st *cert;
+       int i;
+
+       if (session->internals.selected_cert_list_length == 0) {
+               *alg_size = 0;
+               return 0;
+       }
+
+       cert = &session->internals.selected_cert_list[0];
+       cert_pk = gnutls_pubkey_get_pk_algorithm(cert->pubkey, NULL);
+       i = 0;
+
+       for (kx = 0; kx < MAX_ALGOS; kx++) {
+               pk = _gnutls_map_pk_get_pk(kx);
+               if (pk == cert_pk) {
+                       /* then check key usage */
+                       if (_gnutls_check_key_usage(cert, kx) == 0) {
+                               alg[i] = kx;
+                               i++;
+
+                               if (i > *alg_size)
+                                       return
+                                           gnutls_assert_val
+                                           (GNUTLS_E_INTERNAL_ERROR);
+                       }
+               }
+       }
+
+       if (i == 0) {
+               gnutls_assert();
+               return GNUTLS_E_INVALID_REQUEST;
+       }
+
+       *alg_size = i;
+
+       return 0;
 }
 
 
@@ -303,10 +300,10 @@ _gnutls_selected_cert_supported_kx (gnutls_session_t session,
  * function then the client will not be asked to send a certificate.
  **/
 void
-gnutls_certificate_server_set_request (gnutls_session_t session,
-                                       gnutls_certificate_request_t req)
+gnutls_certificate_server_set_request(gnutls_session_t session,
+                                     gnutls_certificate_request_t req)
 {
-  session->internals.send_cert_req = req;
+       session->internals.send_cert_req = req;
 }
 
 /**
@@ -344,10 +341,9 @@ gnutls_certificate_server_set_request (gnutls_session_t session,
  * indicates error and the handshake will be terminated.
  **/
 void gnutls_certificate_client_set_retrieve_function
-  (gnutls_certificate_credentials_t cred,
-   gnutls_certificate_client_retrieve_function * func)
-{
-  cred->client_get_cert_callback = func;
+    (gnutls_certificate_credentials_t cred,
+     gnutls_certificate_client_retrieve_function * func) {
+       cred->client_get_cert_callback = func;
 }
 
 /**
@@ -373,10 +369,9 @@ void gnutls_certificate_client_set_retrieve_function
  * will be terminated.
  **/
 void gnutls_certificate_server_set_retrieve_function
-  (gnutls_certificate_credentials_t cred,
-   gnutls_certificate_server_retrieve_function * func)
-{
-  cred->server_get_cert_callback = func;
+    (gnutls_certificate_credentials_t cred,
+     gnutls_certificate_server_retrieve_function * func) {
+       cred->server_get_cert_callback = func;
 }
 
 /**
@@ -417,10 +412,9 @@ void gnutls_certificate_server_set_retrieve_function
  * Since: 3.0
  **/
 void gnutls_certificate_set_retrieve_function
-  (gnutls_certificate_credentials_t cred,
-   gnutls_certificate_retrieve_function * func)
-{
-  cred->get_cert_callback = func;
+    (gnutls_certificate_credentials_t cred,
+     gnutls_certificate_retrieve_function * func) {
+       cred->get_cert_callback = func;
 }
 
 /**
@@ -464,10 +458,9 @@ void gnutls_certificate_set_retrieve_function
  * Since: 3.0
  **/
 void gnutls_certificate_set_retrieve_function2
-  (gnutls_certificate_credentials_t cred,
-   gnutls_certificate_retrieve_function2 * func)
-{
-  cred->get_cert_callback2 = func;
+    (gnutls_certificate_credentials_t cred,
+     gnutls_certificate_retrieve_function2 * func) {
+       cred->get_cert_callback2 = func;
 }
 
 /**
@@ -494,11 +487,10 @@ void gnutls_certificate_set_retrieve_function2
  * Since: 2.10.0
  **/
 void
-  gnutls_certificate_set_verify_function
-  (gnutls_certificate_credentials_t cred,
-   gnutls_certificate_verify_function * func)
-{
-  cred->verify_callback = func;
+ gnutls_certificate_set_verify_function
+    (gnutls_certificate_credentials_t cred,
+     gnutls_certificate_verify_function * func) {
+       cred->verify_callback = func;
 }
 
 /*-
@@ -512,27 +504,26 @@ void
  *
  -*/
 static time_t
-_gnutls_x509_get_raw_crt_activation_time (const gnutls_datum_t * cert)
+_gnutls_x509_get_raw_crt_activation_time(const gnutls_datum_t * cert)
 {
-  gnutls_x509_crt_t xcert;
-  time_t result;
+       gnutls_x509_crt_t xcert;
+       time_t result;
 
-  result = gnutls_x509_crt_init (&xcert);
-  if (result < 0)
-    return (time_t) - 1;
+       result = gnutls_x509_crt_init(&xcert);
+       if (result < 0)
+               return (time_t) - 1;
 
-  result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER);
-  if (result < 0)
-    {
-      gnutls_x509_crt_deinit (xcert);
-      return (time_t) - 1;
-    }
+       result = gnutls_x509_crt_import(xcert, cert, GNUTLS_X509_FMT_DER);
+       if (result < 0) {
+               gnutls_x509_crt_deinit(xcert);
+               return (time_t) - 1;
+       }
 
-  result = gnutls_x509_crt_get_activation_time (xcert);
+       result = gnutls_x509_crt_get_activation_time(xcert);
 
-  gnutls_x509_crt_deinit (xcert);
+       gnutls_x509_crt_deinit(xcert);
 
-  return result;
+       return result;
 }
 
 /*-
@@ -546,27 +537,26 @@ _gnutls_x509_get_raw_crt_activation_time (const gnutls_datum_t * cert)
  *
  -*/
 static time_t
-_gnutls_x509_get_raw_crt_expiration_time (const gnutls_datum_t * cert)
+_gnutls_x509_get_raw_crt_expiration_time(const gnutls_datum_t * cert)
 {
-  gnutls_x509_crt_t xcert;
-  time_t result;
+       gnutls_x509_crt_t xcert;
+       time_t result;
 
-  result = gnutls_x509_crt_init (&xcert);
-  if (result < 0)
-    return (time_t) - 1;
+       result = gnutls_x509_crt_init(&xcert);
+       if (result < 0)
+               return (time_t) - 1;
 
-  result = gnutls_x509_crt_import (xcert, cert, GNUTLS_X509_FMT_DER);
-  if (result < 0)
-    {
-      gnutls_x509_crt_deinit (xcert);
-      return (time_t) - 1;
-    }
+       result = gnutls_x509_crt_import(xcert, cert, GNUTLS_X509_FMT_DER);
+       if (result < 0) {
+               gnutls_x509_crt_deinit(xcert);
+               return (time_t) - 1;
+       }
 
-  result = gnutls_x509_crt_get_expiration_time (xcert);
+       result = gnutls_x509_crt_get_expiration_time(xcert);
 
-  gnutls_x509_crt_deinit (xcert);
+       gnutls_x509_crt_deinit(xcert);
 
-  return result;
+       return result;
 }
 
 #ifdef ENABLE_OPENPGP
@@ -578,57 +568,60 @@ _gnutls_x509_get_raw_crt_expiration_time (const gnutls_datum_t * cert)
  * Returns a negative error code in case of an error, or GNUTLS_E_NO_CERTIFICATE_FOUND if no certificate was sent.
  -*/
 static int
-_gnutls_openpgp_crt_verify_peers (gnutls_session_t session,
-                                  unsigned int *status)
+_gnutls_openpgp_crt_verify_peers(gnutls_session_t session,
+                                const char *hostname,
+                                unsigned int *status)
 {
-  cert_auth_info_t info;
-  gnutls_certificate_credentials_t cred;
-  int peer_certificate_list_size, ret;
-
-  CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);
-
-  info = _gnutls_get_auth_info (session);
-  if (info == NULL)
-    return GNUTLS_E_INVALID_REQUEST;
-
-  cred = (gnutls_certificate_credentials_t)
-    _gnutls_get_cred (session->key, GNUTLS_CRD_CERTIFICATE, NULL);
-  if (cred == NULL)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
-    }
-
-  if (info->raw_certificate_list == NULL || info->ncerts == 0)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_NO_CERTIFICATE_FOUND;
-    }
-
-  /* generate a list of gnutls_certs based on the auth info
-   * raw certs.
-   */
-  peer_certificate_list_size = info->ncerts;
-
-  if (peer_certificate_list_size != 1)
-    {
-      gnutls_assert ();
-      return GNUTLS_E_INTERNAL_ERROR;
-    }
-
-  /* Verify certificate 
-   */
-  ret =
-    _gnutls_openpgp_verify_key (cred, &info->raw_certificate_list[0],
-                                peer_certificate_list_size, status);
-
-  if (ret < 0)
-    {
-      gnutls_assert ();
-      return ret;
-    }
-
-  return 0;
+       cert_auth_info_t info;
+       gnutls_certificate_credentials_t cred;
+       int peer_certificate_list_size, ret;
+       unsigned int verify_flags;
+
+       CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);
+
+       info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+       if (info == NULL)
+               return GNUTLS_E_INVALID_REQUEST;
+
+       cred = (gnutls_certificate_credentials_t)
+           _gnutls_get_cred(session, GNUTLS_CRD_CERTIFICATE);
+       if (cred == NULL) {
+               gnutls_assert();
+               return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+       }
+
+       if (info->raw_certificate_list == NULL || info->ncerts == 0) {
+               gnutls_assert();
+               return GNUTLS_E_NO_CERTIFICATE_FOUND;
+       }
+
+       verify_flags = cred->verify_flags | session->internals.priorities.additional_verify_flags;
+
+       /* generate a list of gnutls_certs based on the auth info
+        * raw certs.
+        */
+       peer_certificate_list_size = info->ncerts;
+
+       if (peer_certificate_list_size != 1) {
+               gnutls_assert();
+               return GNUTLS_E_INTERNAL_ERROR;
+       }
+
+       /* Verify certificate 
+        */
+       ret =
+           _gnutls_openpgp_verify_key(cred, hostname,
+                                      &info->raw_certificate_list[0],
+                                      peer_certificate_list_size,
+                                      verify_flags,
+                                      status);
+
+       if (ret < 0) {
+               gnutls_assert();
+               return ret;
+       }
+
+       return 0;
 }
 #endif
 
@@ -637,50 +630,151 @@ _gnutls_openpgp_crt_verify_peers (gnutls_session_t session,
  * @session: is a gnutls session
  * @status: is the output of the verification
  *
- * This function will try to verify the peer's certificate and return
- * its status (trusted, invalid etc.).  The value of @status should
- * be one or more of the gnutls_certificate_status_t enumerated
- * elements bitwise or'd. To avoid denial of service attacks some
+ * This function will verify the peer's certificate and store
+ * the status in the @status variable as a bitwise or'd gnutls_certificate_status_t
+ * values or zero if the certificate is trusted. Note that value in @status
+ * is set only when the return value of this function is success (i.e, failure 
+ * to trust a certificate does not imply a negative return value).
+ * The default verification flags used by this function can be overridden
+ * using gnutls_certificate_set_verify_flags().
+ *
+ * This function will take into account the OCSP Certificate Status TLS extension,
+ * as well as the following X.509 certificate extensions: Name Constraints,
+ * Key Usage, and Basic Constraints (pathlen).
+ * 
+ * To avoid denial of service attacks some
  * default upper limits regarding the certificate key size and chain
- * size are set. To override them use
- * gnutls_certificate_set_verify_limits().
+ * size are set. To override them use gnutls_certificate_set_verify_limits().
  *
  * Note that you must also check the peer's name in order to check if
- * the verified certificate belongs to the actual peer.
- *
- * This function uses gnutls_x509_crt_list_verify() with the CAs in
- * the credentials as trusted CAs.
+ * the verified certificate belongs to the actual peer, see gnutls_x509_crt_check_hostname(),
+ * or use gnutls_certificate_verify_peers3().
  *
  * Returns: a negative error code on error and %GNUTLS_E_SUCCESS (0) on success.
  **/
 int
-gnutls_certificate_verify_peers2 (gnutls_session_t session,
-                                  unsigned int *status)
+gnutls_certificate_verify_peers2(gnutls_session_t session,
+                                unsigned int *status)
 {
-  cert_auth_info_t info;
+       return gnutls_certificate_verify_peers(session, NULL, 0, status);
+}
 
-  CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);
+/**
+ * gnutls_certificate_verify_peers3:
+ * @session: is a gnutls session
+ * @hostname: is the expected name of the peer; may be %NULL
+ * @status: is the output of the verification
+ *
+ * This function will verify the peer's certificate and store the
+ * status in the @status variable as a bitwise or'd gnutls_certificate_status_t
+ * values or zero if the certificate is trusted. Note that value in @status
+ * is set only when the return value of this function is success (i.e, failure 
+ * to trust a certificate does not imply a negative return value).
+ * The default verification flags used by this function can be overridden
+ * using gnutls_certificate_set_verify_flags(). See the documentation
+ * of gnutls_certificate_verify_peers2() for details in the verification process.
+ *
+ * If the @hostname provided is non-NULL then this function will compare
+ * the hostname in the certificate against the given. The comparison will
+ * be accurate for ascii names; non-ascii names are compared byte-by-byte. 
+ * If names do not match the %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set.
+ *
+ * In order to verify the purpose of the end-certificate (by checking the extended
+ * key usage), use gnutls_certificate_verify_peers().
+ *
+ * Returns: a negative error code on error and %GNUTLS_E_SUCCESS (0) on success.
+ *
+ * Since: 3.1.4
+ **/
+int
+gnutls_certificate_verify_peers3(gnutls_session_t session,
+                                const char *hostname,
+                                unsigned int *status)
+{
+gnutls_typed_vdata_st data;
 
-  info = _gnutls_get_auth_info (session);
-  if (info == NULL)
-    {
-      return GNUTLS_E_NO_CERTIFICATE_FOUND;
-    }
+       data.type = GNUTLS_DT_DNS_HOSTNAME;
+       data.size = 0;
+       data.data = (void*)hostname;
 
-  if (info->raw_certificate_list == NULL || info->ncerts == 0)
-    return GNUTLS_E_NO_CERTIFICATE_FOUND;
+       return gnutls_certificate_verify_peers(session, &data, 1, status);
+}
 
-  switch (gnutls_certificate_type_get (session))
-    {
-    case GNUTLS_CRT_X509:
-      return _gnutls_x509_cert_verify_peers (session, status);
+/**
+ * gnutls_certificate_verify_peers:
+ * @session: is a gnutls session
+ * @data: an array of typed data
+ * @elements: the number of data elements
+ * @status: is the output of the verification
+ *
+ * This function will verify the peer's certificate and store the
+ * status in the @status variable as a bitwise or'd gnutls_certificate_status_t
+ * values or zero if the certificate is trusted. Note that value in @status
+ * is set only when the return value of this function is success (i.e, failure 
+ * to trust a certificate does not imply a negative return value).
+ * The default verification flags used by this function can be overridden
+ * using gnutls_certificate_set_verify_flags(). See the documentation
+ * of gnutls_certificate_verify_peers2() for details in the verification process.
+ *
+ * The acceptable data types are %GNUTLS_DT_DNS_HOSTNAME and %GNUTLS_DT_KEY_PURPOSE_OID.
+ * If a DNS hostname is provided then this function will compare
+ * the hostname in the certificate against the given. The comparison will
+ * be accurate for ascii names; non-ascii names are compared byte-by-byte. 
+ * If names do not match the %GNUTLS_CERT_UNEXPECTED_OWNER status flag will be set.
+ *
+ * If a key purpose OID is provided and the end-certificate contains the extended key
+ * usage PKIX extension, it will be required to be have the provided key purpose 
+ * (e.g., %GNUTLS_KP_TLS_WWW_SERVER), or be marked for any purpose, otherwise 
+ * verification will fail with %GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE status.
+ *
+ * Returns: a negative error code on error and %GNUTLS_E_SUCCESS (0) on success.
+ *
+ * Since: 3.3.0
+ **/
+int
+gnutls_certificate_verify_peers(gnutls_session_t session,
+                               gnutls_typed_vdata_st * data,
+                               unsigned int elements,
+                               unsigned int *status)
+{
+       cert_auth_info_t info;
+       const char *hostname = NULL;
+       const char *purpose = NULL;
+       unsigned i;
+
+       CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);
+
+       info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+       if (info == NULL) {
+               return GNUTLS_E_NO_CERTIFICATE_FOUND;
+       }
+
+       if (info->raw_certificate_list == NULL || info->ncerts == 0)
+               return GNUTLS_E_NO_CERTIFICATE_FOUND;
+
+       for (i=0;i<elements;i++) {
+               if (data[i].type == GNUTLS_DT_DNS_HOSTNAME) {
+                       hostname = (void*)data[i].data;
+               } else if (data[i].type == GNUTLS_DT_KEY_PURPOSE_OID) {
+                       purpose = (void*)data[i].data;
+               } else {
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               }
+       }
+
+       switch (gnutls_certificate_type_get(session)) {
+       case GNUTLS_CRT_X509:
+               return _gnutls_x509_cert_verify_peers(session, hostname,
+                                                     purpose,
+                                                     status);
 #ifdef ENABLE_OPENPGP
-    case GNUTLS_CRT_OPENPGP:
-      return _gnutls_openpgp_crt_verify_peers (session, status);
+       case GNUTLS_CRT_OPENPGP:
+               return _gnutls_openpgp_crt_verify_peers(session, hostname,
+                                                       status);
 #endif
-    default:
-      return GNUTLS_E_INVALID_REQUEST;
-    }
+       default:
+               return GNUTLS_E_INVALID_REQUEST;
+       }
 }
 
 /**
@@ -693,40 +787,37 @@ gnutls_certificate_verify_peers2 (gnutls_session_t session,
  *
  * Deprecated: gnutls_certificate_verify_peers2() now verifies expiration times.
  **/
-time_t
-gnutls_certificate_expiration_time_peers (gnutls_session_t session)
+time_t gnutls_certificate_expiration_time_peers(gnutls_session_t session)
 {
-  cert_auth_info_t info;
-
-  CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);
-
-  info = _gnutls_get_auth_info (session);
-  if (info == NULL)
-    {
-      return (time_t) - 1;
-    }
-
-  if (info->raw_certificate_list == NULL || info->ncerts == 0)
-    {
-      gnutls_assert ();
-      return (time_t) - 1;
-    }
-
-  switch (gnutls_certificate_type_get (session))
-    {
-    case GNUTLS_CRT_X509:
-      return
-        _gnutls_x509_get_raw_crt_expiration_time (&info->raw_certificate_list
-                                                  [0]);
+       cert_auth_info_t info;
+
+       CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);
+
+       info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+       if (info == NULL) {
+               return (time_t) - 1;
+       }
+
+       if (info->raw_certificate_list == NULL || info->ncerts == 0) {
+               gnutls_assert();
+               return (time_t) - 1;
+       }
+
+       switch (gnutls_certificate_type_get(session)) {
+       case GNUTLS_CRT_X509:
+               return
+                   _gnutls_x509_get_raw_crt_expiration_time(&info->
+                                                            raw_certificate_list
+                                                            [0]);
 #ifdef ENABLE_OPENPGP
-    case GNUTLS_CRT_OPENPGP:
-      return
-        _gnutls_openpgp_get_raw_key_expiration_time
-        (&info->raw_certificate_list[0]);
+       case GNUTLS_CRT_OPENPGP:
+               return
+                   _gnutls_openpgp_get_raw_key_expiration_time
+                   (&info->raw_certificate_list[0]);
 #endif
-    default:
-      return (time_t) - 1;
-    }
+       default:
+               return (time_t) - 1;
+       }
 }
 
 /**
@@ -740,40 +831,38 @@ gnutls_certificate_expiration_time_peers (gnutls_session_t session)
  *
  * Deprecated: gnutls_certificate_verify_peers2() now verifies activation times.
  **/
-time_t
-gnutls_certificate_activation_time_peers (gnutls_session_t session)
+time_t gnutls_certificate_activation_time_peers(gnutls_session_t session)
 {
-  cert_auth_info_t info;
-
-  CHECK_AUTH (GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);
-
-  info = _gnutls_get_auth_info (session);
-  if (info == NULL)
-    {
-      return (time_t) - 1;
-    }
-
-  if (info->raw_certificate_list == NULL || info->ncerts == 0)
-    {
-      gnutls_assert ();
-      return (time_t) - 1;
-    }
-
-  switch (gnutls_certificate_type_get (session))
-    {
-    case GNUTLS_CRT_X509:
-      return
-        _gnutls_x509_get_raw_crt_activation_time (&info->raw_certificate_list
-                                                  [0]);
+       cert_auth_info_t info;
+
+       CHECK_AUTH(GNUTLS_CRD_CERTIFICATE, GNUTLS_E_INVALID_REQUEST);
+
+       info = _gnutls_get_auth_info(session, GNUTLS_CRD_CERTIFICATE);
+       if (info == NULL) {
+               return (time_t) - 1;
+       }
+
+       if (info->raw_certificate_list == NULL || info->ncerts == 0) {
+               gnutls_assert();
+               return (time_t) - 1;
+       }
+
+       switch (gnutls_certificate_type_get(session)) {
+       case GNUTLS_CRT_X509:
+               return
+                   _gnutls_x509_get_raw_crt_activation_time(&info->
+                                                            raw_certificate_list
+                                                            [0]);
 #ifdef ENABLE_OPENPGP
-    case GNUTLS_CRT_OPENPGP:
-      return
-        _gnutls_openpgp_get_raw_key_creation_time (&info->raw_certificate_list
-                                                   [0]);
+       case GNUTLS_CRT_OPENPGP:
+               return
+                   _gnutls_openpgp_get_raw_key_creation_time(&info->
+                                                             raw_certificate_list
+                                                             [0]);
 #endif
-    default:
-      return (time_t) - 1;
-    }
+       default:
+               return (time_t) - 1;
+       }
 }
 
 /**
@@ -796,13 +885,13 @@ gnutls_certificate_activation_time_peers (gnutls_session_t session)
  * callback function.  See also gnutls_sign_callback_get().
  *
  * Deprecated: Use the PKCS 11 or #gnutls_privkey_t interfacess like gnutls_privkey_import_ext() instead.
- */
+ **/
 void
-gnutls_sign_callback_set (gnutls_session_t session,
-                          gnutls_sign_func sign_func, void *userdata)
+gnutls_sign_callback_set(gnutls_session_t session,
+                        gnutls_sign_func sign_func, void *userdata)
 {
-  session->internals.sign_func = sign_func;
-  session->internals.sign_func_userdata = userdata;
+       session->internals.sign_func = sign_func;
+       session->internals.sign_func_userdata = userdata;
 }
 
 /**
@@ -816,13 +905,175 @@ gnutls_sign_callback_set (gnutls_session_t session,
  *   if not set, %NULL.
  *
  * Deprecated: Use the PKCS 11 interfaces instead.
- */
+ **/
 gnutls_sign_func
-gnutls_sign_callback_get (gnutls_session_t session, void **userdata)
+gnutls_sign_callback_get(gnutls_session_t session, void **userdata)
 {
-  if (userdata)
-    *userdata = session->internals.sign_func_userdata;
-  return session->internals.sign_func;
+       if (userdata)
+               *userdata = session->internals.sign_func_userdata;
+       return session->internals.sign_func;
 }
 
+#define TEST_TEXT "test text"
+/* returns error if the certificate has different algorithm than
+ * the given key parameters.
+ */
+int _gnutls_check_key_cert_match(gnutls_certificate_credentials_t res)
+{
+       gnutls_datum_t test = {(void*)TEST_TEXT, sizeof(TEST_TEXT)-1};
+       gnutls_datum_t sig = {NULL, 0};
+       int pk, pk2, ret;
+
+       pk =
+           gnutls_pubkey_get_pk_algorithm(res->certs[res->ncerts - 1].
+                                          cert_list[0].pubkey, NULL);
+       pk2 =
+           gnutls_privkey_get_pk_algorithm(res->pkey[res->ncerts - 1],
+                                           NULL);
+
+       if (pk2 != pk) {
+               gnutls_assert();
+               return GNUTLS_E_CERTIFICATE_KEY_MISMATCH;
+       }
+
+       /* now check if keys really match. We use the sign/verify approach
+        * because we cannot always obtain the parameters from the abstract
+        * keys (e.g. PKCS #11). */
+       ret = gnutls_privkey_sign_data(res->pkey[res->ncerts - 1],
+               GNUTLS_DIG_SHA256, 0, &test, &sig);
+       if (ret < 0) {
+               /* for some reason we couldn't sign that. That shouldn't have
+                * happened, but since it did, report the issue and do not
+                * try the key matching test */
+               _gnutls_debug_log("%s: failed signing\n", __func__);
+               goto finish;
+       }
+
+       ret = gnutls_pubkey_verify_data2(res->certs[res->ncerts - 1].cert_list[0].pubkey,
+               gnutls_pk_to_sign(pk, GNUTLS_DIG_SHA256),
+               0, &test, &sig);
+
+       gnutls_free(sig.data);
+
+       if (ret < 0)
+               return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+
+ finish:
+       return 0;
+}
 
+/**
+ * gnutls_certificate_verification_status_print:
+ * @status: The status flags to be printed
+ * @type: The certificate type
+ * @out: Newly allocated datum with (0) terminated string.
+ * @flags: should be zero
+ *
+ * This function will pretty print the status of a verification
+ * process -- eg. the one obtained by gnutls_certificate_verify_peers3().
+ *
+ * The output @out needs to be deallocated using gnutls_free().
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ *
+ * Since: 3.1.4
+ **/
+int
+gnutls_certificate_verification_status_print(unsigned int status,
+                                            gnutls_certificate_type_t
+                                            type, gnutls_datum_t * out,
+                                            unsigned int flags)
+{
+       gnutls_buffer_st str;
+       int ret;
+
+       _gnutls_buffer_init(&str);
+
+       if (status == 0)
+               _gnutls_buffer_append_str(&str,
+                                         _
+                                         ("The certificate is trusted. "));
+       else
+               _gnutls_buffer_append_str(&str,
+                                         _
+                                         ("The certificate is NOT trusted. "));
+
+       if (type == GNUTLS_CRT_X509) {
+               if (status & GNUTLS_CERT_REVOKED)
+                       _gnutls_buffer_append_str(&str,
+                                                 _
+                                                 ("The certificate chain is revoked. "));
+
+               if (status & GNUTLS_CERT_MISMATCH)
+                       _gnutls_buffer_append_str(&str,
+                                                 _
+                                                 ("The certificate doesn't match the local copy (TOFU). "));
+
+               if (status & GNUTLS_CERT_REVOCATION_DATA_SUPERSEDED)
+                       _gnutls_buffer_append_str(&str,
+                                                 _
+                                                 ("The revocation data are old and have been superseded. "));
+
+               if (status & GNUTLS_CERT_REVOCATION_DATA_ISSUED_IN_FUTURE)
+                       _gnutls_buffer_append_str(&str,
+                                                 _
+                                                 ("The revocation data are issued with a future date. "));
+
+               if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+                       _gnutls_buffer_append_str(&str,
+                                                 _
+                                                 ("The certificate issuer is unknown. "));
+
+               if (status & GNUTLS_CERT_SIGNER_NOT_CA)
+                       _gnutls_buffer_append_str(&str,
+                                                 _
+                                                 ("The certificate issuer is not a CA. "));
+       } else if (type == GNUTLS_CRT_OPENPGP) {
+               if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+                       _gnutls_buffer_append_str(&str,
+                                                 _
+                                                 ("Could not find a signer of the certificate. "));
+
+               if (status & GNUTLS_CERT_REVOKED)
+                       _gnutls_buffer_append_str(&str,
+                                                 _
+                                                 ("The certificate is revoked. "));
+       }
+
+       if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
+               _gnutls_buffer_append_str(&str,
+                                         _
+                                         ("The certificate chain uses insecure algorithm. "));
+
+       if (status & GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE)
+               _gnutls_buffer_append_str(&str,
+                                         _
+                                         ("The certificate chain violates the signer's constraints. "));
+
+       if (status & GNUTLS_CERT_NOT_ACTIVATED)
+               _gnutls_buffer_append_str(&str,
+                                         _
+                                         ("The certificate chain uses not yet valid certificate. "));
+
+       if (status & GNUTLS_CERT_EXPIRED)
+               _gnutls_buffer_append_str(&str,
+                                         _
+                                         ("The certificate chain uses expired certificate. "));
+
+       if (status & GNUTLS_CERT_SIGNATURE_FAILURE)
+               _gnutls_buffer_append_str(&str,
+                                         _
+                                         ("The signature in the certificate is invalid. "));
+
+       if (status & GNUTLS_CERT_UNEXPECTED_OWNER)
+               _gnutls_buffer_append_str(&str,
+                                         _
+                                         ("The name in the certificate does not match the expected. "));
+
+       ret = _gnutls_buffer_to_datum(&str, out);
+       if (out->size > 0)
+               out->size--;
+
+       return ret;
+}