gnutls: Fix up GTlsCertificateFlags handling
authorDan Winship <danw@gnome.org>
Tue, 7 Dec 2010 22:10:23 +0000 (23:10 +0100)
committerDan Winship <danw@gnome.org>
Tue, 7 Dec 2010 22:10:23 +0000 (23:10 +0100)
If a certificate isn't signed by a known CA, gnutls won't bother
checking some of the other things (eg, expiration). But callers
might be planning to do the CA check themselves, in which case we
need to do the other checks anyway. So do that.

tls/gnutls/gtlscertificate-gnutls.c
tls/gnutls/gtlsclientconnection-gnutls.c
tls/gnutls/gtlsconnection-gnutls.c
tls/gnutls/gtlsconnection-gnutls.h
tls/gnutls/gtlsserverconnection-gnutls.c

index 29e992f..68b6e2d 100644 (file)
@@ -285,10 +285,10 @@ g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
                                 GTlsCertificate     *trusted_ca)
 {
   GTlsCertificateGnutls *cert_gnutls;
-  int status;
-  guint gnutls_flags, num_certs, i, num_cas;
-  gnutls_x509_crt_t *chain, ca;
+  guint num_certs, i;
+  gnutls_x509_crt_t *chain;
   GTlsCertificateFlags gtls_flags;
+  time_t t, now;
   
   cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (cert);
   for (num_certs = 0; cert_gnutls; cert_gnutls = cert_gnutls->priv->issuer)
@@ -300,28 +300,42 @@ g_tls_certificate_gnutls_verify (GTlsCertificate     *cert,
 
   if (trusted_ca)
     {
-      cert_gnutls = G_TLS_CERTIFICATE_GNUTLS (trusted_ca);
-      ca = cert_gnutls->priv->cert;
-      num_cas = 1;
+      gnutls_x509_crt_t ca;
+      guint gnutls_flags;
+      int status;
+
+      ca = G_TLS_CERTIFICATE_GNUTLS (trusted_ca)->priv->cert;
+      status = gnutls_x509_crt_list_verify (chain, num_certs,
+                                           &ca, 1,
+                                           NULL, 0,
+                                           GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
+                                           &gnutls_flags);
+      if (status != 0)
+       {
+         g_free (chain);
+         return G_TLS_CERTIFICATE_GENERIC_ERROR;
+       }
+
+      gtls_flags = g_tls_certificate_gnutls_convert_flags (gnutls_flags);
     }
-  else
+
+  /* We have to check these ourselves since gnutls_x509_crt_list_verify
+   * won't bother if it gets an UNKNOWN_CA.
+   */
+  now = time (NULL);
+  for (i = 0; i < num_certs; i++)
     {
-      ca = NULL;
-      num_cas = 0;
+      t = gnutls_x509_crt_get_activation_time (chain[i]);
+      if (t == (time_t) -1 || t > now)
+       gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+
+      t = gnutls_x509_crt_get_expiration_time (chain[i]);
+      if (t == (time_t) -1 || t < now)
+       gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
     }
 
-  status = gnutls_x509_crt_list_verify (chain, num_certs,
-                                       &ca, num_cas,
-                                       NULL, 0,
-                                       GNUTLS_VERIFY_ALLOW_X509_V1_CA_CRT,
-                                       &gnutls_flags);
   g_free (chain);
 
-  if (status != 0)
-    return G_TLS_CERTIFICATE_GENERIC_ERROR;
-
-  gtls_flags = g_tls_certificate_gnutls_convert_flags (gnutls_flags);
-
   if (identity)
     gtls_flags |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (cert), identity);
 
index 476a60b..5b2fb95 100644 (file)
@@ -256,8 +256,6 @@ g_tls_client_connection_gnutls_verify_peer (GTlsConnectionGnutls  *conn_gnutls,
   GTlsClientConnectionGnutls *gnutls = G_TLS_CLIENT_CONNECTION_GNUTLS (conn_gnutls);
   gboolean accepted;
 
-  *errors = g_tls_connection_gnutls_validate_peer (conn_gnutls);
-
   if (gnutls->priv->server_identity)
     {
       *errors |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (peer_certificate),
index de25e4d..6239890 100644 (file)
@@ -442,15 +442,6 @@ g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
     st->ncerts = 0;
 }
 
-GTlsCertificateFlags
-g_tls_connection_gnutls_validate_peer (GTlsConnectionGnutls *gnutls)
-{
-  int status;
-
-  status = gnutls_certificate_verify_peers (gnutls->priv->session);
-  return g_tls_certificate_gnutls_convert_flags (status);
-}
-
 static void
 begin_gnutls_io (GTlsConnectionGnutls  *gnutls,
                 gboolean               blocking,
@@ -865,6 +856,20 @@ handshake_internal (GTlsConnectionGnutls  *gnutls,
 
   if (peer_certificate)
     {
+      int status;
+
+      status = gnutls_certificate_verify_peers (gnutls->priv->session);
+      peer_certificate_errors = g_tls_certificate_gnutls_convert_flags (status);
+      if (peer_certificate_errors)
+       {
+         /* gnutls_certificate_verify_peers() bails out on the first
+          * error, which may be G_TLS_CERTIFICATE_UNKNOWN_CA, but the
+          * caller may be planning to check that part themselves. So
+          * call g_tls_certificate_verify() to get any other errors.
+          */
+         peer_certificate_errors |= g_tls_certificate_verify (peer_certificate, NULL, NULL);
+       }
+
       if (!G_TLS_CONNECTION_GNUTLS_GET_CLASS (gnutls)->verify_peer (gnutls, peer_certificate, &peer_certificate_errors))
        {
          g_object_unref (peer_certificate);
index 0e54df8..fff49af 100644 (file)
@@ -53,7 +53,6 @@ gnutls_certificate_credentials g_tls_connection_gnutls_get_credentials (GTlsConn
 gnutls_session                 g_tls_connection_gnutls_get_session     (GTlsConnectionGnutls *connection);
 void                           g_tls_connection_gnutls_get_certificate (GTlsConnectionGnutls *gnutls,
                                                                         gnutls_retr_st       *st);
-GTlsCertificateFlags           g_tls_connection_gnutls_validate_peer   (GTlsConnectionGnutls *gnutls);
 
 gssize   g_tls_connection_gnutls_read          (GTlsConnectionGnutls  *gnutls,
                                                void                  *buffer,
index 6e9a8bd..e374b63 100644 (file)
@@ -174,8 +174,6 @@ g_tls_server_connection_gnutls_verify_peer (GTlsConnectionGnutls  *gnutls,
                                            GTlsCertificate       *peer_certificate,
                                            GTlsCertificateFlags  *errors)
 {
-  *errors = g_tls_connection_gnutls_validate_peer (G_TLS_CONNECTION_GNUTLS (gnutls));
-
   return g_tls_connection_emit_accept_certificate (G_TLS_CONNECTION (gnutls),
                                                   peer_certificate, *errors);
 }