]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
Added gnutls_verify_stored_pubkey() and gnutls_store_pubkey().
authorNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 28 Jan 2012 11:47:49 +0000 (12:47 +0100)
committerNikos Mavrogiannopoulos <nmav@gnutls.org>
Sat, 28 Jan 2012 12:32:16 +0000 (13:32 +0100)
This enables using ssh-like authentication for TLS sessions.

30 files changed:
NEWS
configure.ac
doc/cha-cert-auth.texi
doc/cha-gtls-examples.texi
doc/examples/Makefile.am
doc/examples/ex-cert-select-pkcs11.c
doc/examples/ex-cert-select.c
doc/examples/ex-client-dtls.c
doc/examples/ex-client-resume.c
doc/examples/ex-client-srp.c
doc/examples/ex-client-x509.c
doc/examples/ex-serv-dtls.c
doc/examples/ex-serv-psk.c
doc/examples/ex-serv-srp.c
doc/examples/ex-serv-x509.c
doc/examples/ex-verify-ssh.c [new file with mode: 0644]
doc/examples/examples.h
lib/Makefile.am
lib/includes/gnutls/gnutls.h.in
lib/libgnutls.map
lib/openpgp/output.c
lib/system.c
lib/system.h
lib/verify-ssh.c [new file with mode: 0644]
lib/x509/output.c
src/cli-args.def.in
src/cli.c
src/common.c
src/common.h
src/tests.c

diff --git a/NEWS b/NEWS
index c6aed0ed148c0a52d38e9fe1dfd8ba7c31885151..ccce513becf091b152202453b724fa3e85a00c44 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -4,12 +4,18 @@ See the end for copying conditions.
 
 * Version 3.0.13 (unreleased)
 
+** gnutls-cli: If no --x509cafile is provided a default is
+assumed (/etc/ssl/certs/ca-certificates.crt).
+
 ** ocsptool: Added --ask parameter, to verify a certificate's
 status from an ocsp server.
 
 ** command line apps: Use gnu autogen (libopts) to parse command
 line arguments and template files.
 
+** libgnutls: Added new functions to easily allow the usage of
+an SSH-style authentication.
+
 ** libgnutls: SUITEB128 and SUITEB192 priority strings account
 for the RFC6460 requirements.
 
@@ -28,6 +34,8 @@ of PKCS #11 modules. This is required on the child process after
 a fork.
 
 ** API and ABI modifications:
+gnutls_verify_stored_pubkey: Added
+gnutls_store_pubkey: Added
 gnutls_x509_crt_get_authority_key_gn_serial: Added
 gnutls_x509_crl_get_authority_key_gn_serial: Added
 gnutls_pkcs11_reinit: Added
index 7531ae524aa7a707344061965e7db2d0866c37f2..42a84d498ff4e66acbdeb77805d9f84933cae0e4 100644 (file)
@@ -117,9 +117,8 @@ AC_C_BIGENDIAN
 
 
 dnl No fork on MinGW, disable some self-tests until we fix them.
-AC_CHECK_FUNCS(fork,,)
+AC_CHECK_FUNCS([fork getrusage getpwuid_r],,)
 AM_CONDITIONAL(HAVE_FORK, test "$ac_cv_func_fork" != "no")
-AC_CHECK_FUNCS(getrusage,,)
 AC_LIB_HAVE_LINKFLAGS(pthread,, [#include <pthread.h>], [pthread_mutex_lock (0);])
 
 dnl Check for p11-kit
index 5253f5b3687b26e246c3220372ea51f3ac961e5c..7fb41a60dd195e0ff064a8f6a01ffe10100ab591 100644 (file)
@@ -87,6 +87,7 @@ acceptable.  The framework is illustrated on @ref{fig:x509}.
 * X.509 distinguished names::
 * Verifying X.509 certificate paths::
 * Verifying a certificate in the context of TLS session::
+* Verifying a certificate using SSH-style authentication::
 @end menu
 
 @node X.509 certificate structure
@@ -276,7 +277,20 @@ about the peer's identity. It is required to verify if the
 certificate's owner is the one you expect. For more information
 consult @xcite{RFC2818} and section @ref{ex:verify} for an example.
 
+@node Verifying a certificate using SSH-style authentication
+@subsection Verifying a certificate using SSH-style authentication
+@cindex verifying certificate paths
+@cindex SSH-style authentication
+@tindex gnutls_certificate_verify_flags
+
+It is possible to use an SSH-style authentication method in GnuTLS.
+That means that having seen and associated a public key with a host
+is enough to trust it on the subsequent connections.
+A hybrid system with X.509 and SSH authentication is 
+shown in @ref{Simple client example with SSH-style certificate verification}.
 
+@showfuncdesc{gnutls_verify_stored_pubkey}
+@showfuncdesc{gnutls_store_pubkey}
 
 @node OpenPGP certificates
 @section @acronym{OpenPGP} certificates
index 8fbf6b8b9f28bca010a69df67a88a13b83cc75be..9d253a0045db6337513aadb18d54da540b7394c7 100644 (file)
@@ -25,6 +25,7 @@ implemented by another example.
 @menu
 * Simple client example with anonymous authentication::
 * Simple client example with X.509 certificate support::
+* Simple client example with SSH-style certificate verification::
 * Simple Datagram TLS client example::
 * Obtaining session information::
 * Using a callback to select the certificate to use::
@@ -47,6 +48,8 @@ is vulnerable to man-in-the-middle (active or redirection) attacks.
 However, the data are integrity protected and encrypted from
 passive eavesdroppers.
 
+Note that the server must support anonymous authentication as well.
+
 @verbatiminclude examples/ex-client-anon.c
 
 @node Simple client example with X.509 certificate support
@@ -62,6 +65,16 @@ resumption.
 
 @verbatiminclude examples/ex-client-x509.c
 
+@node Simple client example with SSH-style certificate verification
+@subsection Simple client example with SSH-style certificate verification
+
+This is an alternative verification function that will use the
+X.509 certificate authorities for verification, but also assume an
+SSH-like authentication system. That is the user is prompted on unknown 
+public keys and known public keys are considered trusted.
+
+@verbatiminclude examples/ex-verify-ssh.c
+
 @node Simple Datagram TLS client example
 @subsection Simple datagram @acronym{TLS} client example
 
index ba6d04759140488d508a201c1a3f18011e3ecd4e..ed1592cb067f16cee4e2caf6782424914a62d350 100644 (file)
@@ -84,4 +84,4 @@ endif
 
 libexamples_la_SOURCES = examples.h ex-alert.c ex-pkcs12.c \
        ex-session-info.c ex-x509-info.c ex-verify.c    \
-       tcp.c udp.c ex-pkcs11-list.c verify.c
+       tcp.c udp.c ex-pkcs11-list.c verify.c ex-verify-ssh.c
index bd7851c0beeb1c34df0938e660c76b820df4f305..e8cb21e367ae40ce36a2c13009b39f9892295221 100644 (file)
@@ -26,7 +26,7 @@
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 #define MIN(x,y) (((x)<(y))?(x):(y))
 
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 
 /* The URLs of the objects can be obtained
  * using p11tool --list-all --login
index 43f666fda87ba41deec6039dfe9f3e98b6cb649b..d45e8e583a5b2a5c244af4a28123ecb7c35127e8 100644 (file)
@@ -26,7 +26,7 @@
 
 #define CERT_FILE "cert.pem"
 #define KEY_FILE "key.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 
 extern int tcp_connect (void);
 extern void tcp_close (int sd);
index 222762a0b582e7cc1220502e80d3d9c97c572152..3c9d1ea679b889ae45fade7d83d8d2b40e8c477e 100644 (file)
@@ -18,7 +18,7 @@
  */
 
 #define MAX_BUF 1024
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 
 extern int udp_connect (void);
index 5aeae58c925816ecd8d3f1507b65507780266bd6..1a041d196d836d652f1c483a06224a513465e470 100644 (file)
@@ -16,7 +16,7 @@ extern int tcp_connect (void);
 extern void tcp_close (int sd);
 
 #define MAX_BUF 1024
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 
 int
index 5a753abff32273e7470bb8e05ea0a7cfca9be360..89d516577719c126e12af868c6621b1c17932618 100644 (file)
@@ -18,7 +18,7 @@ extern void tcp_close (int sd);
 #define MAX_BUF 1024
 #define USERNAME "user"
 #define PASSWORD "pass"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 
 int
index 0ea151dd728eee63df5f72a05f5198ddb12f888d..c5ed190787f83a13de31cc201abd5a845f7be380 100644 (file)
@@ -16,7 +16,7 @@
  */
 
 #define MAX_BUF 1024
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define MSG "GET / HTTP/1.0\r\n\r\n"
 
 extern int tcp_connect (void);
index 355f7b9839908c7fbe420699677a881992af66fb..a32a9840519293af2f14bc85280f45848375dabd 100644 (file)
@@ -20,7 +20,7 @@
 
 #define KEYFILE "key.pem"
 #define CERTFILE "cert.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define CRLFILE "crl.pem"
 
 /* This is a sample DTLS echo server, using X.509 authentication.
index 70732f60e37f3b283dd91f9d8ea366c521957ef1..1f2af6c4727ca155fbe41f97956d3e01891e95c6 100644 (file)
@@ -17,7 +17,7 @@
 
 #define KEYFILE "key.pem"
 #define CERTFILE "cert.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define CRLFILE "crl.pem"
 
 /* This is a sample TLS echo server, supporting X.509 and PSK
index 70ae1660bba8ae0c32f720340f7a98ab0556c9f2..5873b39920660d197eafceed6dd7a1a9617d8917 100644 (file)
@@ -20,7 +20,7 @@
 
 #define KEYFILE "key.pem"
 #define CERTFILE "cert.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 
 /* This is a sample TLS-SRP echo server.
  */
index 44c671a3f4ce93de451c8303397aacd1e505693b..c06f13ec093a60c8d3af2bbb0c4d640977a9a913 100644 (file)
@@ -17,7 +17,7 @@
 
 #define KEYFILE "key.pem"
 #define CERTFILE "cert.pem"
-#define CAFILE "ca.pem"
+#define CAFILE "/etc/ssl/certs/ca-certificates.crt"
 #define CRLFILE "crl.pem"
 
 /* This is a sample TLS 1.0 echo server, using X.509 authentication.
diff --git a/doc/examples/ex-verify-ssh.c b/doc/examples/ex-verify-ssh.c
new file mode 100644 (file)
index 0000000..c9fab66
--- /dev/null
@@ -0,0 +1,138 @@
+/* This example code is placed in the public domain. */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+#include "examples.h"
+
+/* This function will verify the peer's certificate, check
+ * if the hostname matches. In addition it will perform an
+ * SSH-style authentication, where ultimately trusted keys
+ * are only the keys that have been seen before.
+ */
+int
+_ssh_verify_certificate_callback (gnutls_session_t session)
+{
+  unsigned int status;
+  const gnutls_datum_t *cert_list;
+  unsigned int cert_list_size;
+  int ret;
+  gnutls_x509_crt_t cert;
+  const char *hostname;
+
+  /* read hostname */
+  hostname = gnutls_session_get_ptr (session);
+
+  /* This verification function uses the trusted CAs in the credentials
+   * structure. So you must have installed one or more CA certificates.
+   */
+  ret = gnutls_certificate_verify_peers2 (session, &status);
+  if (ret < 0)
+    {
+      printf ("Error\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  if (status & GNUTLS_CERT_INVALID)
+    printf ("The certificate is not trusted.\n");
+
+  if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+    printf ("The certificate hasn't got a known issuer.\n");
+
+  if (status & GNUTLS_CERT_REVOKED)
+    printf ("The certificate has been revoked.\n");
+
+  if (status & GNUTLS_CERT_EXPIRED)
+    printf ("The certificate has expired\n");
+
+  if (status & GNUTLS_CERT_NOT_ACTIVATED)
+    printf ("The certificate is not yet activated\n");
+
+  /* Up to here the process is the same for X.509 certificates and
+   * OpenPGP keys. From now on X.509 certificates are assumed. This can
+   * be easily extended to work with openpgp keys as well.
+   */
+  if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509)
+    return GNUTLS_E_CERTIFICATE_ERROR;
+
+  if (gnutls_x509_crt_init (&cert) < 0)
+    {
+      printf ("error in initialization\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+  if (cert_list == NULL)
+    {
+      printf ("No certificate was found!\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  /* This is not a real world example, since we only check the first 
+   * certificate in the given chain.
+   */
+  if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
+    {
+      printf ("error parsing certificate\n");
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+
+  if (!gnutls_x509_crt_check_hostname (cert, hostname))
+    {
+      printf ("The certificate's owner does not match hostname '%s'\n",
+              hostname);
+      status |= GNUTLS_CERT_INVALID;
+    }
+
+  gnutls_x509_crt_deinit (cert);
+  
+  ret = gnutls_verify_stored_pubkey(NULL, NULL, hostname, "443", 
+                                    GNUTLS_CRT_X509, &cert_list[0], 0);
+  if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND)
+    {
+      fprintf(stderr, "Host %s is not known.", hostname);
+      if (status == 0)
+        fprintf(stderr, "Its certificate is valid for %s.\n", hostname);
+      
+      /* the certificate must be printed and user must be asked on
+       * whether it is trustworthy. --see gnutls_x509_crt_print() */
+      
+      /* if not trusted */
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+  else if (ret == GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
+    {
+      fprintf(stderr, "Warning: host %s is known but has another key associated.", hostname);
+      fprintf(stderr, "It might be that the server has multiple keys, or you are under attack\n");
+      if (status == 0)
+        fprintf(stderr, "Its certificate is valid for %s.\n", hostname);
+      
+      /* the certificate must be printed and user must be asked on
+       * whether it is trustworthy. --see gnutls_x509_crt_print() */
+      
+      /* if not trusted */
+      return GNUTLS_E_CERTIFICATE_ERROR;
+    }
+  else if (ret < 0)
+    {
+      fprintf(stderr, "gnutls_verify_stored_pubkey: %s\n", gnutls_strerror(ret));
+      return ret;
+    }
+  
+  /* user trusts the key -> store it */
+  ret = gnutls_store_pubkey(NULL, NULL, hostname, "443", GNUTLS_CRT_X509, &cert_list[0], 0);
+  if (ret < 0)
+    {
+      fprintf(stderr, "gnutls_store_pubkey: %s\n", gnutls_strerror(ret));
+    }
+
+  /* notify gnutls to continue handshake normally */
+  return 0;
+}
+
index e96cb26080ab5f5416457424387f2f5c1f978dd1..0c2dbb33727acb204dac86b70a28dc6ad279f66f 100644 (file)
@@ -12,6 +12,9 @@ int print_info (gnutls_session_t session);
 
 void print_x509_certificate_info (gnutls_session_t session);
 
+int
+_ssh_verify_certificate_callback (gnutls_session_t session);
+
 void
 verify_certificate_chain (const char *hostname,
                           const gnutls_datum_t * cert_chain,
index d692a5864f5ff5aaa874e5fa7ab2f8cdd4d1446f..820f09062da8247155d6eb574967ee3bd39076e2 100644 (file)
@@ -74,7 +74,7 @@ COBJECTS = gnutls_record.c gnutls_compress.c debug.c gnutls_cipher.c  \
        gnutls_rsa_export.c gnutls_helper.c gnutls_supplemental.c       \
        random.c crypto-api.c gnutls_privkey.c gnutls_pcert.c           \
        gnutls_pubkey.c locks.c hash.c gnutls_dtls.c system_override.c  \
-       crypto-backend.c
+       crypto-backend.c verify-ssh.c
 
 if ENABLE_PKCS11
 COBJECTS += pkcs11.c pkcs11_privkey.c pkcs11_write.c pkcs11_secret.c
index 45008ae8d5610f7b7d732d65fced09299e93619e..415e282719a0d059755efc9e8d0c67acf1a8ceb6 100644 (file)
@@ -526,6 +526,7 @@ extern "C"
  * @GNUTLS_CRT_UNKNOWN: Unknown certificate type.
  * @GNUTLS_CRT_X509: X.509 Certificate.
  * @GNUTLS_CRT_OPENPGP: OpenPGP certificate.
+ * @GNUTLS_CRT_RAW: Raw public key (SubjectPublicKey)
  *
  * Enumeration of different certificate types.
  */
@@ -533,7 +534,8 @@ extern "C"
   {
     GNUTLS_CRT_UNKNOWN = 0,
     GNUTLS_CRT_X509 = 1,
-    GNUTLS_CRT_OPENPGP = 2
+    GNUTLS_CRT_OPENPGP = 2,
+    GNUTLS_CRT_RAW = 3
   } gnutls_certificate_type_t;
 
 /**
@@ -552,6 +554,7 @@ extern "C"
 /**
  * gnutls_certificate_print_formats_t:
  * @GNUTLS_CRT_PRINT_FULL: Full information about certificate.
+ * @GNUTLS_CRT_PRINT_COMPACT: Information about certificate name in one line, plus identification of the public key.
  * @GNUTLS_CRT_PRINT_ONELINE: Information about certificate in one line.
  * @GNUTLS_CRT_PRINT_UNSIGNED_FULL: All info for an unsigned certificate.
  *
@@ -561,7 +564,8 @@ extern "C"
   {
     GNUTLS_CRT_PRINT_FULL = 0,
     GNUTLS_CRT_PRINT_ONELINE = 1,
-    GNUTLS_CRT_PRINT_UNSIGNED_FULL = 2
+    GNUTLS_CRT_PRINT_UNSIGNED_FULL = 2,
+    GNUTLS_CRT_PRINT_COMPACT = 3
   } gnutls_certificate_print_formats_t;
 
 #define GNUTLS_PK_ECC GNUTLS_PK_EC
@@ -1652,6 +1656,22 @@ gnutls_ecc_curve_t gnutls_ecc_curve_get(gnutls_session_t session);
   int gnutls_hex2bin (const char *hex_data, size_t hex_size,
                       void *bin_data, size_t * bin_size);
 
+  /* ssh style functions */
+  int gnutls_verify_stored_pubkey(const char* file, 
+                            const char* application,
+                            const char* host,
+                            const char* service,
+                            gnutls_certificate_type_t cert_type,
+                            const gnutls_datum_t * cert, unsigned int flags);
+
+  int gnutls_store_pubkey(const char* file, 
+                    const char* application,
+                    const char* host,
+                    const char* service,
+                    gnutls_certificate_type_t cert_type,
+                    const gnutls_datum_t * cert, unsigned int flags);
+
+
   /* Gnutls error codes. The mapping to a TLS alert is also shown in
    * comments.
    */
index e169a06170f90322b86f44bcee5e41906790adb9..40765f8d54d64dda061200eaf298b7e550526b1c 100644 (file)
@@ -771,6 +771,8 @@ GNUTLS_3_0_0 {
        gnutls_priority_protocol_list;
        gnutls_priority_compression_list;
        gnutls_priority_ecc_curve_list;
+       gnutls_verify_stored_pubkey;
+       gnutls_store_pubkey;
 } GNUTLS_2_12;
 
 GNUTLS_PRIVATE {
index fdd176ac3a9d8bb41f2abecf769838e72e2088e6..88f522db70782613b4d3d796f044c418dddfb0d7 100644 (file)
@@ -506,6 +506,13 @@ gnutls_openpgp_crt_print (gnutls_openpgp_crt_t cert,
 
   if (format == GNUTLS_CRT_PRINT_ONELINE)
     print_oneline (&str, cert);
+  else if (format == GNUTLS_CRT_PRINT_COMPACT)
+    {
+      print_oneline (&str, cert);
+
+      _gnutls_buffer_append_data (&str, "\n", 1);
+      print_key_fingerprint (&str, cert);
+    }
   else
     {
       _gnutls_buffer_append_str (&str,
index eddb1053a4002dee29a883ec433b593241291bcd..476b313787a6b137b9930684bba4b39f263ebe5b 100644 (file)
 #include <gnutls_errors.h>
 
 #include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 
 #ifdef _WIN32
-#include <windows.h>
+# include <windows.h>
 
 #else
-#ifdef HAVE_PTHREAD_LOCKS
-#include <pthread.h>
-#endif
+# ifdef HAVE_PTHREAD_LOCKS
+#  include <pthread.h>
+# endif
+
+# if defined(HAVE_GETPWUID_R)
+#  include <pwd.h>
+# endif
 #endif
 
 /* We need to disable gnulib's replacement wrappers to get native
@@ -278,3 +284,65 @@ mutex_init_func gnutls_mutex_init = gnutls_system_mutex_init;
 mutex_deinit_func gnutls_mutex_deinit = gnutls_system_mutex_deinit;
 mutex_lock_func gnutls_mutex_lock = gnutls_system_mutex_lock;
 mutex_unlock_func gnutls_mutex_unlock = gnutls_system_mutex_unlock;
+
+#define CONFIG_PATH ".gnutls"
+
+/* Returns a path to store user-specific configuration
+ * data.
+ */
+int _gnutls_find_config_path(char* path, size_t max_size)
+{
+char tmp_home_dir[1024];
+const char *home_dir = getenv ("HOME");
+
+#ifdef _WIN32
+  if (home_dir == NULL || home_dir[0] == '\0')
+    {
+      const char *home_drive = getenv ("HOMEDRIVE");
+      const char *home_path = getenv ("HOMEPATH");
+
+      if (home_drive != NULL && home_path != NULL)
+        {
+          snprintf(tmp_home_dir, sizeof(tmp_home_dir), "%s%s", home_drive, home_path);
+        }
+      else
+        {
+          tmp_home_dir[0] = 0;
+        }
+      
+      home_dir = tmp_home_dir;
+    }
+#elsif defined(HAVE_GETPWUID_R)
+  if (home_dir == NULL || home_dir[0] == '\0')
+    {
+      struct passwd *pwd;
+      struct passwd _pwd;
+      char buf[1024];
+
+      getpwuid_r(getuid(), &_pwd, buf, sizeof(buf), &pwd);
+      if (pwd != NULL)
+        {
+          snprintf(tmp_home_dir, sizeof(tmp_home_dir), "%s", pwd->pw_dir);
+        }
+      else
+        {
+          tmp_home_dir[0] = 0;
+        }
+
+      home_dir = tmp_home_dir;
+    }
+#else
+  if (home_dir == NULL || home_dir[0] == '\0')
+    {
+      tmp_home_dir[0] = 0;
+      home_dir = tmp_home_dir;
+    }
+#endif
+
+  if (home_dir == NULL || home_dir[0] == 0)
+    path[0] = 0;
+  else
+    snprintf(path, max_size, "%s/"CONFIG_PATH, home_dir);
+      
+  return 0;
+}
index df4ee5a36d57b2f100a3de9b25b252a6153cbfc9..0d2f5a501fc293c8d48d23692aaf6812cbfaa178 100644 (file)
@@ -71,4 +71,6 @@ struct timespec ts;
 #endif
 }
 
+int _gnutls_find_config_path(char* path, size_t max_size);
+
 #endif /* SYSTEM_H */
diff --git a/lib/verify-ssh.c b/lib/verify-ssh.c
new file mode 100644 (file)
index 0000000..8d6562f
--- /dev/null
@@ -0,0 +1,489 @@
+/*
+ * Copyright (C) 2012 Free Software Foundation, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * 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
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+#include <gnutls_int.h>
+#include <gnutls_errors.h>
+#include <libtasn1.h>
+#include <gnutls_global.h>
+#include <gnutls_num.h>         /* MAX */
+#include <gnutls_sig.h>
+#include <gnutls_str.h>
+#include <gnutls_datum.h>
+#include <hash.h>
+#include "x509_int.h"
+#include <common.h>
+#include <base64.h>
+#include <gnutls/abstract.h>
+#include <system.h>
+
+static int raw_pubkey_to_base64(gnutls_datum_t* pubkey);
+static int x509_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rpubkey);
+static int pgp_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rpubkey);
+static int find_stored_pubkey(const char* file, const char* application,
+                              const char* host, const char* service, 
+                              const gnutls_datum_t* skey);
+static int find_config_file(char* file, size_t max_size);
+#define MAX_FILENAME 512
+
+/**
+ * gnutls_verify_stored_pubkey:
+ * @file: A file specifying the stored keys (use NULL for the default)
+ * @application: non-NULL with an application name if this key is application-specific
+ * @host: The peer's name
+ * @service: non-NULL if this key is specific to a service (e.g. http)
+ * @cert_type: The type of the certificate
+ * @cert: The raw (der) data of the certificate
+ * @flags: should be 0.
+ *
+ * This function will try to verify the provided certificate using
+ * a list of stored public keys.  The @service field if non-NULL should
+ * be a port number.
+ *
+ * Returns: If no associated public key is found
+ * then %GNUTLS_E_NO_CERTIFICATE_FOUND will be returned. If a key
+ * is found but does not match %GNUTLS_E_CERTIFICATE_KEY_MISMATCH
+ * is returned. On success, %GNUTLS_E_SUCCESS (0) is returned, 
+ * or a negative error value on other errors.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_verify_stored_pubkey(const char* file, 
+                            const char* application,
+                            const char* host,
+                            const char* service,
+                            gnutls_certificate_type_t cert_type,
+                            const gnutls_datum_t * cert, unsigned int flags)
+{
+gnutls_datum_t pubkey = { NULL, 0 };
+int ret;
+char local_file[MAX_FILENAME];
+
+  if (cert_type != GNUTLS_CRT_X509 && cert_type != GNUTLS_CRT_OPENPGP)
+    return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+
+  if (file == NULL)
+    {
+      ret = find_config_file(local_file, sizeof(local_file));
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+      file = local_file;
+    }
+
+  if (cert_type == GNUTLS_CRT_X509)
+    ret = x509_crt_to_raw_pubkey(cert, &pubkey);
+  else
+    ret = pgp_crt_to_raw_pubkey(cert, &pubkey);
+
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = raw_pubkey_to_base64(&pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  ret = find_stored_pubkey(file, application, host, service, &pubkey);
+  if (ret < 0)
+    return gnutls_assert_val(GNUTLS_E_NO_CERTIFICATE_FOUND);
+  
+
+cleanup:
+  gnutls_free(pubkey.data);
+  return ret;
+}
+
+static int parse_line(char* line, const char* application,
+                      size_t application_len,
+                      const char* host, size_t host_len,
+                      const char* service, size_t service_len,
+                      const gnutls_datum_t *skey)
+{
+char* p, *kp;
+char* savep = NULL;
+size_t kp_len;
+
+  /* read version */
+  p = strtok_r(line, "|", &savep);
+  if (p == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  if (strncmp(p, "g0", 2) != 0)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  /* read application */
+  p = strtok_r(NULL, "|", &savep);
+  if (p == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+    
+  if (p[0] != '*' && strcmp(p, application)!=0)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  /* read host */
+  p = strtok_r(NULL, "|", &savep);
+  if (p == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+    
+  if (p[0] != '*' && strcmp(p, host) != 0)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  /* read service */
+  p = strtok_r(NULL, "|", &savep);
+  if (p == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+    
+  if (p[0] != '*' && strcmp(p, service) != 0)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+
+  /* read service */
+  kp = strtok_r(NULL, "|", &savep);
+  if (kp == NULL)
+    return gnutls_assert_val(GNUTLS_E_PARSING_ERROR);
+  
+  p = strpbrk(kp, "\n \r\t|");
+  if (p != NULL) *p = 0;
+
+  kp_len = strlen(kp);
+  if (kp_len != skey->size)
+    return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+    
+  if (memcmp(kp, skey->data, skey->size) != 0)
+    return gnutls_assert_val(GNUTLS_E_CERTIFICATE_KEY_MISMATCH);
+  
+  /* key found and matches */
+  return 0;
+}
+
+/* Returns the base64 key if found 
+ */
+static int find_stored_pubkey(const char* file, const char* application,
+                             const char* host, const char* service, 
+                             const gnutls_datum_t* skey)
+{
+FILE* fd;
+char* line = NULL;
+size_t line_size = 0;
+int ret, l2, mismatch = 0;
+size_t application_len = 0, host_len = 0, service_len = 0;
+
+  if (host != NULL) host_len = strlen(host);
+  if (service != NULL) service_len = strlen(service);
+  if (application != NULL) application_len = strlen(application);
+
+  fd = fopen(file, "rb");
+  if (fd == NULL)
+    return gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+  
+  do 
+    {
+      l2 = getline(&line, &line_size, fd);
+      if (l2 > 0)
+        {
+          ret = parse_line(line, application, application_len,
+                          host, host_len, service, service_len, skey);
+          if (ret == 0) /* found */
+            {
+              goto cleanup;
+            }
+          else if (ret == GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
+            mismatch = 1;
+        }
+    }
+  while(l2 >= 0);
+
+  if (mismatch)
+    ret = GNUTLS_E_CERTIFICATE_KEY_MISMATCH;
+  else
+    ret = GNUTLS_E_NO_CERTIFICATE_FOUND;
+  
+cleanup:
+  free(line);
+  fclose(fd);
+  
+  return ret;
+}
+
+static int raw_pubkey_to_base64(gnutls_datum_t* pubkey)
+{
+  int ret;
+  char* out;
+  
+  ret = base64_encode_alloc((void*)pubkey->data, pubkey->size, &out);
+  if (ret == 0)
+    return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+  
+  gnutls_free(pubkey->data);
+  pubkey->data = (void*)out;
+  pubkey->size = ret;
+  
+  return 0;
+}
+
+static int x509_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rpubkey)
+{
+gnutls_x509_crt_t crt = NULL;
+gnutls_pubkey_t pubkey = NULL;
+size_t size;
+int ret;
+
+  ret = gnutls_x509_crt_init(&crt);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  ret = gnutls_pubkey_init(&pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = gnutls_x509_crt_import(crt, cert, GNUTLS_X509_FMT_DER);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  ret = gnutls_pubkey_import_x509 (pubkey, crt, 0);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  size = 0;
+  ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, NULL, &size);
+  if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  rpubkey->data = gnutls_malloc(size);
+  if (rpubkey->data == NULL)
+  if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      ret = GNUTLS_E_MEMORY_ERROR;
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, rpubkey->data, &size);
+  if (ret < 0)
+    {
+      gnutls_free(rpubkey->data);
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  rpubkey->size = size;
+  ret = 0;
+
+cleanup:
+  gnutls_x509_crt_deinit(crt);
+  gnutls_pubkey_deinit(pubkey);
+
+  return ret;
+}
+
+static int pgp_crt_to_raw_pubkey(const gnutls_datum_t * cert, gnutls_datum_t *rpubkey)
+{
+gnutls_openpgp_crt_t crt = NULL;
+gnutls_pubkey_t pubkey = NULL;
+size_t size;
+int ret;
+
+  ret = gnutls_openpgp_crt_init(&crt);
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  ret = gnutls_pubkey_init(&pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = gnutls_openpgp_crt_import(crt, cert, GNUTLS_OPENPGP_FMT_RAW);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  ret = gnutls_pubkey_import_openpgp (pubkey, crt, 0);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  size = 0;
+  ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, NULL, &size);
+  if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  rpubkey->data = gnutls_malloc(size);
+  if (rpubkey->data == NULL)
+  if (ret < 0 && ret != GNUTLS_E_SHORT_MEMORY_BUFFER)
+    {
+      ret = GNUTLS_E_MEMORY_ERROR;
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = gnutls_pubkey_export(pubkey, GNUTLS_X509_FMT_DER, rpubkey->data, &size);
+  if (ret < 0)
+    {
+      gnutls_free(rpubkey->data);
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  rpubkey->size = size;
+  ret = 0;
+
+cleanup:
+  gnutls_openpgp_crt_deinit(crt);
+  gnutls_pubkey_deinit(pubkey);
+
+  return ret;
+}
+
+/**
+ * gnutls_store_pubkey:
+ * @file: A file specifying the stored keys (use NULL for the default)
+ * @application: non-NULL with an application name if this key is application-specific
+ * @host: The peer's name
+ * @service: non-NULL if this key is specific to a service (e.g. http)
+ * @cert_type: The type of the certificate
+ * @cert: The data of the certificate
+ * @flags: should be 0.
+ *
+ * This function will store to verify the provided certificate to 
+ * the list of stored public keys. 
+ *
+ * Note that this function is not thread safe.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a
+ *   negative error value.
+ *
+ * Since: 3.0.0
+ **/
+int
+gnutls_store_pubkey(const char* file, 
+                    const char* application,
+                    const char* host,
+                    const char* service,
+                    gnutls_certificate_type_t cert_type,
+                    const gnutls_datum_t * cert, unsigned int flags)
+{
+FILE* fd = NULL;
+gnutls_datum_t pubkey = { NULL, 0 };
+int ret;
+char local_file[MAX_FILENAME];
+
+  if (cert_type != GNUTLS_CRT_X509 && cert_type != GNUTLS_CRT_OPENPGP)
+    return gnutls_assert_val(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE);
+  
+  if (file == NULL)
+    {
+      ret = _gnutls_find_config_path(local_file, sizeof(local_file));
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+      
+      _gnutls_debug_log("Configuration path: %s\n", local_file);
+      mkdir(local_file, 0700);
+      
+      ret = find_config_file(local_file, sizeof(local_file));
+      if (ret < 0)
+        return gnutls_assert_val(ret);
+      file = local_file;
+    }
+    
+  if (cert_type == GNUTLS_CRT_X509)
+    ret = x509_crt_to_raw_pubkey(cert, &pubkey);
+  else
+    ret = pgp_crt_to_raw_pubkey(cert, &pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+  
+  ret = raw_pubkey_to_base64(&pubkey);
+  if (ret < 0)
+    {
+      gnutls_assert();
+      goto cleanup;
+    }
+
+  _gnutls_debug_log("Configuration file: %s\n", file);
+
+  fd = fopen(file, "ab+");
+  if (fd == NULL)
+    {
+      ret = gnutls_assert_val(GNUTLS_E_FILE_ERROR);
+      goto cleanup;
+    }
+
+  if (application == NULL) application = "*";
+  if (service == NULL) service = "*";
+  if (host == NULL) host = "*";
+
+  fprintf(fd, "|g0|%s|%s|%s|%.*s\n", application, host, service, pubkey.size, pubkey.data);
+
+  ret = 0;
+
+cleanup:
+  gnutls_free(pubkey.data);
+  if (fd != NULL) fclose(fd);
+  
+  return ret;
+}
+
+#define CONFIG_FILE "known_hosts"
+
+static int find_config_file(char* file, size_t max_size)
+{
+char path[MAX_FILENAME];
+int ret;
+
+  ret = _gnutls_find_config_path(path, sizeof(path));
+  if (ret < 0)
+    return gnutls_assert_val(ret);
+
+  if (path[0] == 0)
+    snprintf(file, max_size, "%s", CONFIG_FILE);
+  else
+    snprintf(file, max_size, "%s/%s", path, CONFIG_FILE);
+      
+  return 0;
+}
index 564004fb35db996b68a36c3628471609e8fbaaf0..827845f427b62122868f991a92c49c08628f8fd3 100644 (file)
@@ -1698,6 +1698,22 @@ gnutls_x509_crt_print (gnutls_x509_crt_t cert,
       ret = _gnutls_buffer_to_datum( &str, out);
       if (out->size > 0) out->size--;
       
+      return ret;
+    }
+  else if (format == GNUTLS_CRT_PRINT_COMPACT)
+    {
+      _gnutls_buffer_init (&str);
+
+      print_oneline (&str, cert);
+
+      _gnutls_buffer_append_data (&str, "\n", 1);
+      print_keyid (&str, cert);
+
+      _gnutls_buffer_append_data (&str, "\0", 1);
+
+      ret = _gnutls_buffer_to_datum( &str, out);
+      if (out->size > 0) out->size--;
+      
       return ret;
     }
   else if (format == GNUTLS_CRT_PRINT_ONELINE)
index a691c8623fd9544774b9911a409d431fcee5ff2f..92c3bd63f882e53528a9d73194433453dc923806 100644 (file)
@@ -36,6 +36,12 @@ flag = {
     doc       = "";
 };
 
+flag = {
+    name      = ssh;
+    descrip   = "Enable SSH-style authentication";
+    doc       = "This option will, in addition to certificate authentication, perform authentication based on stored public keys.";
+};
+
 flag = {
     name      = resume;
     value     = r;
@@ -229,7 +235,7 @@ flag = {
     name      = port;
     value     = p;
     arg-type  = string;
-    descrip   = "The port to connect to";
+    descrip   = "The port or service to connect to";
     doc      = "";
 };
 
index 27885c5f72e8783d52fd9240cb39e81c82dbdb9a..4226e773d4361f3d9f7bc4d1729c87b2ccb72fc5 100644 (file)
--- a/src/cli.c
+++ b/src/cli.c
@@ -70,6 +70,8 @@ int crlf;
 unsigned int verbose = 0;
 extern int print_cert;
 
+#define DEFAULT_CA_FILE "/etc/ssl/certs/ca-certificates.crt"
+
 const char *srp_passwd = NULL;
 const char *srp_username = NULL;
 const char *pgp_keyfile = NULL;
@@ -423,23 +425,93 @@ load_keys (void)
 
 }
 
+#define IS_NEWLINE(x) ((x[0] == '\n') || (x[0] == '\r'))
+static int
+read_yesno (const char *input_str)
+{
+  char input[128];
+
+  fputs (input_str, stderr);
+  if (fgets (input, sizeof (input), stdin) == NULL)
+    return 0;
+
+  if (IS_NEWLINE(input))
+    return 0;
+
+  if (input[0] == 'y' || input[0] == 'Y')
+    return 1;
+
+  return 0;
+}
+
 static int
 cert_verify_callback (gnutls_session_t session)
 {
   int rc;
-  unsigned int status;
+  unsigned int status = 0;
+  int ssh = HAVE_OPT(SSH);
 
   if (!x509_cafile && !pgp_keyring)
     return 0;
 
-  rc = gnutls_certificate_verify_peers2 (session, &status);
-  if (rc != 0 || status != 0)
+  rc = cert_verify(session, hostname);
+  if (rc == 0)
     {
       printf ("*** Verifying server certificate failed...\n");
-      if (!insecure)
+      if (!insecure && !ssh)
         return -1;
     }
 
+  if (ssh) /* try ssh auth */
+    {
+      unsigned int list_size;
+      const gnutls_datum_t * cert;
+      
+      cert = gnutls_certificate_get_peers(session, &list_size);
+      if (cert == NULL)
+        {
+          fprintf(stderr, "Cannot obtain peer's certificate!\n");
+          return -1;
+        }
+      
+      rc = gnutls_verify_stored_pubkey(NULL, NULL, hostname, service, GNUTLS_CRT_X509,
+                                       cert, 0);
+      if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
+        {
+          print_cert_info(session, GNUTLS_CRT_PRINT_COMPACT);
+          fprintf(stderr, "Host %s has never been contacted before and is not in the trusted list.\n", hostname);
+          if (status == 0)
+            fprintf(stderr, "Its certificate is valid for %s.\n", hostname);
+
+          rc = read_yesno("Are you sure you want to trust it? (y/N): ");
+          if (rc == 0)
+            return -1;
+        }
+      else if (rc == GNUTLS_E_CERTIFICATE_KEY_MISMATCH)
+        {
+          print_cert_info(session, GNUTLS_CRT_PRINT_COMPACT);
+          fprintf(stderr, "Warning: host %s is known and it is associated with a different key.\n", hostname);
+          fprintf(stderr, "It might be that the server has multiple keys, or an attacker replaced the key to eavesdrop this connection .\n");
+          if (status == 0)
+            fprintf(stderr, "Its certificate is valid for %s.\n", hostname);
+
+          rc = read_yesno("Do you trust the received key? (y/N): ");
+          if (rc == 0)
+            return -1;
+        }
+      else if (rc < 0)
+        {
+          fprintf(stderr, "gnutls_verify_stored_pubkey: %s\n", gnutls_strerror(rc));
+          return -1;
+        }
+      
+      rc = gnutls_store_pubkey(NULL, NULL, hostname, service, GNUTLS_CRT_X509, cert, 0);
+      if (rc < 0)
+        {
+          fprintf(stderr, "Could not store key: %s\n", gnutls_strerror(rc));
+        }
+    }
+
   return 0;
 }
 
@@ -1018,6 +1090,7 @@ const char* rest = NULL;
   resume = HAVE_OPT(RESUME);
   rehandshake = HAVE_OPT(REHANDSHAKE);
   insecure = HAVE_OPT(INSECURE);
+
   udp = HAVE_OPT(UDP);
   mtu = OPT_VALUE_MTU;
   
@@ -1046,6 +1119,11 @@ const char* rest = NULL;
   
   if (HAVE_OPT(X509CAFILE))
     x509_cafile = OPT_ARG(X509CAFILE);
+  else
+    {
+      if (access(DEFAULT_CA_FILE, R_OK) == 0)
+        x509_cafile = DEFAULT_CA_FILE;
+    }
   
   if (HAVE_OPT(X509CRLFILE))
     x509_crlfile = OPT_ARG(X509CRLFILE);
@@ -1151,8 +1229,6 @@ do_handshake (socket_st * socket)
     {
       /* print some information */
       print_info (socket->session, socket->hostname, HAVE_OPT(INSECURE));
-
-
       socket->secure = 1;
     }
   else
index 40aa5b05d5e24bec0100699aa862f6cef06e0e28..38ea2cffb6ae6891fc5cd771c14e8b604ebb4617 100644 (file)
@@ -68,14 +68,53 @@ raw_to_string (const unsigned char *raw, size_t raw_size)
 }
 
 static void
-print_x509_info (gnutls_session_t session, const char *hostname,
-                 int insecure)
+print_x509_info_compact (gnutls_session_t session, int flag)
+{
+    gnutls_x509_crt_t crt;
+    const gnutls_datum_t *cert_list;
+    unsigned int cert_list_size = 0;
+    int ret;
+    gnutls_datum_t cinfo;
+
+    cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+    if (cert_list_size == 0)
+      {
+          fprintf (stderr, "No certificates found!\n");
+          return;
+      }
+
+    gnutls_x509_crt_init (&crt);
+    ret =
+          gnutls_x509_crt_import (crt, &cert_list[0],
+                                  GNUTLS_X509_FMT_DER);
+    if (ret < 0)
+      {
+        fprintf (stderr, "Decoding error: %s\n",
+                 gnutls_strerror (ret));
+        return;
+      }
+
+    ret =
+      gnutls_x509_crt_print (crt, flag, &cinfo);
+    if (ret == 0)
+      {
+        printf ("- X.509 cert: %s\n", cinfo.data);
+        gnutls_free (cinfo.data);
+      }
+
+    gnutls_x509_crt_deinit (crt);
+}
+
+static void
+print_x509_info (gnutls_session_t session, int flag)
 {
     gnutls_x509_crt_t crt;
     const gnutls_datum_t *cert_list;
     unsigned int cert_list_size = 0, j;
-    int hostname_ok = 0;
     int ret;
+    
+    if (flag == GNUTLS_CRT_PRINT_COMPACT)
+      return print_x509_info_compact(session, flag);
 
     cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
     if (cert_list_size == 0)
@@ -84,6 +123,7 @@ print_x509_info (gnutls_session_t session, const char *hostname,
           return;
       }
 
+    printf (" - Certificate type: X.509\n");
     printf (" - Got a certificate list of %d certificates.\n",
             cert_list_size);
 
@@ -104,14 +144,8 @@ print_x509_info (gnutls_session_t session, const char *hostname,
 
           printf (" - Certificate[%d] info:\n  - ", j);
 
-          if (verbose)
-              ret =
-                  gnutls_x509_crt_print (crt, GNUTLS_CRT_PRINT_FULL,
-                                         &cinfo);
-          else
-              ret =
-                  gnutls_x509_crt_print (crt, GNUTLS_CRT_PRINT_ONELINE,
-                                         &cinfo);
+          ret =
+            gnutls_x509_crt_print (crt, flag, &cinfo);
           if (ret == 0)
             {
                 printf ("%s\n", cinfo.data);
@@ -153,46 +187,118 @@ print_x509_info (gnutls_session_t session, const char *hostname,
                 gnutls_free (p);
             }
 
-          if (j == 0 && hostname != NULL)
-            {
-                /* Check the hostname of the first certificate if it matches
-                 * the name of the host we connected to.
-                 */
-                if (gnutls_x509_crt_check_hostname (crt, hostname) == 0)
-                    hostname_ok = 1;
-                else
-                    hostname_ok = 2;
-            }
-
           gnutls_x509_crt_deinit (crt);
       }
+}
 
-    if (hostname_ok == 1)
-      {
-          printf
-              ("- The hostname in the certificate does NOT match '%s'\n",
-               hostname);
-          if (!insecure)
-              exit (1);
-      }
-    else if (hostname_ok == 2)
-      {
-          printf ("- The hostname in the certificate matches '%s'.\n",
-                  hostname);
-      }
+/* returns true or false, depending on whether the hostname
+ * matches to certificate */
+static int
+verify_x509_hostname (gnutls_session_t session, const char *hostname)
+{
+  gnutls_x509_crt_t crt;
+  const gnutls_datum_t *cert_list;
+  unsigned int cert_list_size = 0;
+  int ret;
+
+  cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+  if (cert_list_size == 0)
+    {
+      fprintf (stderr, "No certificates found!\n");
+      return 0;
+    }
+
+  gnutls_x509_crt_init (&crt);
+  ret =
+      gnutls_x509_crt_import (crt, &cert_list[0],
+                              GNUTLS_X509_FMT_DER);
+  if (ret < 0)
+    {
+      fprintf (stderr, "Decoding error: %s\n",
+               gnutls_strerror (ret));
+      return 0;
+    }
+
+  /* Check the hostname of the first certificate if it matches
+   * the name of the host we connected to.
+   */
+  if (gnutls_x509_crt_check_hostname (crt, hostname) == 0)
+    {
+      printf
+             ("- The hostname in the certificate does NOT match '%s'\n",
+              hostname);
+      ret = 0;
+    }
+  else
+    {
+      printf ("- The hostname in the certificate matches '%s'.\n",
+              hostname);
+      ret = 1;
+    }
+
+  gnutls_x509_crt_deinit (crt);
+
+  return ret;
 }
 
 #ifdef ENABLE_OPENPGP
+/* returns true or false, depending on whether the hostname
+ * matches to certificate */
+static int
+verify_openpgp_hostname (gnutls_session_t session, const char *hostname)
+{
+  gnutls_openpgp_crt_t crt;
+  const gnutls_datum_t *cert_list;
+  unsigned int cert_list_size = 0;
+  int ret;
+
+  cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+  if (cert_list_size == 0)
+    {
+      fprintf (stderr, "No certificates found!\n");
+      return 0;
+    }
+
+  gnutls_openpgp_crt_init (&crt);
+  ret =
+      gnutls_openpgp_crt_import (crt, &cert_list[0],
+                              GNUTLS_OPENPGP_FMT_RAW);
+  if (ret < 0)
+    {
+      fprintf (stderr, "Decoding error: %s\n",
+               gnutls_strerror (ret));
+      return 0;
+    }
+
+  /* Check the hostname of the first certificate if it matches
+   * the name of the host we connected to.
+   */
+  if (gnutls_openpgp_crt_check_hostname (crt, hostname) == 0)
+    {
+      printf
+             ("- The hostname in the certificate does NOT match '%s'\n",
+              hostname);
+      ret = 0;
+    }
+  else
+    {
+      printf ("- The hostname in the certificate matches '%s'.\n",
+              hostname);
+      ret = 1;
+    }
+
+  gnutls_openpgp_crt_deinit (crt);
+
+  return ret;
+}
 
 static void
-print_openpgp_info (gnutls_session_t session, const char *hostname,
-                    int insecure)
+print_openpgp_info_compact (gnutls_session_t session, int flag)
 {
 
     gnutls_openpgp_crt_t crt;
     const gnutls_datum_t *cert_list;
     unsigned int cert_list_size = 0;
-    int hostname_ok = 0;
     int ret;
 
     cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
@@ -211,14 +317,50 @@ print_openpgp_info (gnutls_session_t session, const char *hostname,
                 return;
             }
 
-          if (verbose)
-              ret =
-                  gnutls_openpgp_crt_print (crt, GNUTLS_CRT_PRINT_FULL,
-                                            &cinfo);
-          else
-              ret =
-                  gnutls_openpgp_crt_print (crt, GNUTLS_CRT_PRINT_ONELINE,
-                                            &cinfo);
+          ret =
+              gnutls_openpgp_crt_print (crt, flag, &cinfo);
+          if (ret == 0)
+            {
+                printf ("- OpenPGP cert: %s\n", cinfo.data);
+                gnutls_free (cinfo.data);
+            }
+
+          gnutls_openpgp_crt_deinit (crt);
+      }
+}
+
+static void
+print_openpgp_info (gnutls_session_t session, int flag)
+{
+
+    gnutls_openpgp_crt_t crt;
+    const gnutls_datum_t *cert_list;
+    unsigned int cert_list_size = 0;
+    int ret;
+
+    if (flag == GNUTLS_CRT_PRINT_COMPACT)
+      print_openpgp_info_compact(session, flag);
+    
+    printf (" - Certificate type: OpenPGP\n");
+
+    cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+
+    if (cert_list_size > 0)
+      {
+          gnutls_datum_t cinfo;
+
+          gnutls_openpgp_crt_init (&crt);
+          ret = gnutls_openpgp_crt_import (crt, &cert_list[0],
+                                           GNUTLS_OPENPGP_FMT_RAW);
+          if (ret < 0)
+            {
+                fprintf (stderr, "Decoding error: %s\n",
+                         gnutls_strerror (ret));
+                return;
+            }
+
+          ret =
+              gnutls_openpgp_crt_print (crt, flag, &cinfo);
           if (ret == 0)
             {
                 printf (" - %s\n", cinfo.data);
@@ -261,59 +403,38 @@ print_openpgp_info (gnutls_session_t session, const char *hostname,
                 gnutls_free (p);
             }
 
-          if (hostname != NULL)
-            {
-                /* Check the hostname of the first certificate if it matches
-                 * the name of the host we connected to.
-                 */
-                if (gnutls_openpgp_crt_check_hostname (crt, hostname) == 0)
-                    hostname_ok = 1;
-                else
-                    hostname_ok = 2;
-            }
-
           gnutls_openpgp_crt_deinit (crt);
       }
-
-    if (hostname_ok == 1)
-      {
-          printf
-              ("- The hostname in the certificate does NOT match '%s'\n",
-               hostname);
-          if (!insecure)
-              exit (1);
-      }
-    else if (hostname_ok == 2)
-      {
-          printf ("- The hostname in the certificate matches '%s'.\n",
-                  hostname);
-      }
 }
 
 #endif
 
-static void
-print_cert_vrfy (gnutls_session_t session)
+/* returns false (0) if not verified, or true (1) otherwise */
+int
+cert_verify (gnutls_session_t session, const char* hostname)
 {
     int rc;
-    unsigned int status;
+    unsigned int status = 0;
+    int type;
 
     rc = gnutls_certificate_verify_peers2 (session, &status);
+    if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
+      {
+          printf ("- Peer did not send any certificate.\n");
+          return 0;
+      }
+
     if (rc < 0)
       {
           printf ("- Could not verify certificate (err: %s)\n",
                   gnutls_strerror (rc));
-          return;
+          return 0;
       }
 
-    if (rc == GNUTLS_E_NO_CERTIFICATE_FOUND)
+    type = gnutls_certificate_type_get (session);
+    if (type == GNUTLS_CRT_X509)
       {
-          printf ("- Peer did not send any certificate.\n");
-          return;
-      }
 
-    if (gnutls_certificate_type_get (session) == GNUTLS_CRT_X509)
-      {
           if (status & GNUTLS_CERT_REVOKED)
               printf ("- Peer's certificate chain revoked\n");
           if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
@@ -333,8 +454,11 @@ print_cert_vrfy (gnutls_session_t session)
               printf ("- Peer's certificate is NOT trusted\n");
           else
               printf ("- Peer's certificate is trusted\n");
+
+          rc = verify_x509_hostname (session, hostname);
+          if (rc == 0) status |= GNUTLS_CERT_INVALID;
       }
-    else
+    else if (type == GNUTLS_CRT_OPENPGP)
       {
           if (status & GNUTLS_CERT_INVALID)
               printf ("- Peer's key is invalid\n");
@@ -342,7 +466,20 @@ print_cert_vrfy (gnutls_session_t session)
               printf ("- Peer's key is valid\n");
           if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
               printf ("- Could not find a signer of the peer's key\n");
+
+          rc = verify_openpgp_hostname (session, hostname);
+          if (rc == 0) status |= GNUTLS_CERT_INVALID;
+      }
+    else
+      {
+        fprintf(stderr, "Unknown certificate type\n");
+        status |= GNUTLS_CERT_INVALID;
       }
+
+    if (status)
+      return 0;
+
+    return 1;
 }
 
 static void
@@ -450,6 +587,7 @@ print_info (gnutls_session_t session, const char *hostname, int insecure)
     gnutls_credentials_type_t cred;
     gnutls_kx_algorithm_t kx;
     unsigned char session_id[33];
+    int ret;
     size_t session_id_size = sizeof (session_id);
 
     /* print session ID */
@@ -517,9 +655,14 @@ print_info (gnutls_session_t session, const char *hostname, int insecure)
                 }
           }
 
-          print_cert_info (session, hostname, insecure);
+          print_cert_info (session, verbose?GNUTLS_CRT_PRINT_FULL:GNUTLS_CRT_PRINT_COMPACT);
 
-          print_cert_vrfy (session);
+          ret = cert_verify (session, hostname);
+          if (insecure == 0 && ret == 0)
+            {
+              fprintf(stderr, "Exiting because verification failed (use --insecure to force connection)\n");
+              exit(1);
+            }
 
           if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS)
               print_dh_info (session, "Ephemeral ");
@@ -578,32 +721,25 @@ print_info (gnutls_session_t session, const char *hostname, int insecure)
 }
 
 void
-print_cert_info (gnutls_session_t session, const char *hostname,
-                 int insecure)
+print_cert_info (gnutls_session_t session, int flag)
 {
 
     if (gnutls_certificate_client_get_request_status (session) != 0)
         printf ("- Server has requested a certificate.\n");
 
-    printf ("- Certificate type: ");
     switch (gnutls_certificate_type_get (session))
       {
-      case GNUTLS_CRT_UNKNOWN:
-          printf ("Unknown\n");
-
-          if (!insecure)
-              exit (1);
-          break;
       case GNUTLS_CRT_X509:
-          printf ("X.509\n");
-          print_x509_info (session, hostname, insecure);
+          print_x509_info (session, flag);
           break;
 #ifdef ENABLE_OPENPGP
       case GNUTLS_CRT_OPENPGP:
-          printf ("OpenPGP\n");
-          print_openpgp_info (session, hostname, insecure);
+          print_openpgp_info (session, flag);
           break;
 #endif
+      default:
+          printf ("Unknown type\n");
+          break;
       }
 }
 
index dd6d569881df3d3336b9b4e44bdf87cf7dcacae3..aeeb395e9f32b36d2b337456defcb94a7bd86e96 100644 (file)
@@ -50,9 +50,9 @@
 extern const char str_unknown[];
 
 int print_info (gnutls_session_t state, const char *hostname, int insecure);
-void print_cert_info (gnutls_session_t state, const char *hostname,
-                      int insecure);
+void print_cert_info (gnutls_session_t, int flag);
 void print_list (const char* priorities, int verbose);
+int cert_verify (gnutls_session_t session, const char* hostname);
 
 const char *raw_to_string (const unsigned char *raw, size_t raw_size);
 void pkcs11_common (void);
index ede92c3eb4d2b4650091c6adefc2c44289e1d87c..cbed4680fa0eeba0178317bfefd8e14f17adb3e7 100644 (file)
@@ -1056,7 +1056,7 @@ test_certificate (gnutls_session_t session)
     return ret;
 
   printf ("\n");
-  print_cert_info (session, hostname, 1);
+  print_cert_info (session, GNUTLS_CRT_PRINT_FULL);
 
   return TEST_SUCCEED;
 }