summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNikos Mavrogiannopoulos <nmav@gnutls.org>2012-01-28 12:47:49 +0100
committerNikos Mavrogiannopoulos <nmav@gnutls.org>2012-01-28 13:32:16 +0100
commitdc42971afc5051136ebc8d4b21cb49a2055d4a7b (patch)
tree0c5ed89d85e634ee087b09b911cfd3ac04563ad5
parenteb3ba487cd5881107f8c63dd3ae4356ccb847dff (diff)
downloadgnutls-dc42971afc5051136ebc8d4b21cb49a2055d4a7b.tar.gz
Added gnutls_verify_stored_pubkey() and gnutls_store_pubkey().
This enables using ssh-like authentication for TLS sessions.
-rw-r--r--NEWS8
-rw-r--r--configure.ac3
-rw-r--r--doc/cha-cert-auth.texi14
-rw-r--r--doc/cha-gtls-examples.texi13
-rw-r--r--doc/examples/Makefile.am2
-rw-r--r--doc/examples/ex-cert-select-pkcs11.c2
-rw-r--r--doc/examples/ex-cert-select.c2
-rw-r--r--doc/examples/ex-client-dtls.c2
-rw-r--r--doc/examples/ex-client-resume.c2
-rw-r--r--doc/examples/ex-client-srp.c2
-rw-r--r--doc/examples/ex-client-x509.c2
-rw-r--r--doc/examples/ex-serv-dtls.c2
-rw-r--r--doc/examples/ex-serv-psk.c2
-rw-r--r--doc/examples/ex-serv-srp.c2
-rw-r--r--doc/examples/ex-serv-x509.c2
-rw-r--r--doc/examples/ex-verify-ssh.c138
-rw-r--r--doc/examples/examples.h3
-rw-r--r--lib/Makefile.am2
-rw-r--r--lib/includes/gnutls/gnutls.h.in24
-rw-r--r--lib/libgnutls.map2
-rw-r--r--lib/openpgp/output.c7
-rw-r--r--lib/system.c76
-rw-r--r--lib/system.h2
-rw-r--r--lib/verify-ssh.c489
-rw-r--r--lib/x509/output.c16
-rw-r--r--src/cli-args.def.in8
-rw-r--r--src/cli.c88
-rw-r--r--src/common.c330
-rw-r--r--src/common.h4
-rw-r--r--src/tests.c2
30 files changed, 1124 insertions, 127 deletions
diff --git a/NEWS b/NEWS
index c6aed0ed14..ccce513bec 100644
--- 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
diff --git a/configure.ac b/configure.ac
index 7531ae524a..42a84d498f 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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
diff --git a/doc/cha-cert-auth.texi b/doc/cha-cert-auth.texi
index 5253f5b368..7fb41a60dd 100644
--- a/doc/cha-cert-auth.texi
+++ b/doc/cha-cert-auth.texi
@@ -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
diff --git a/doc/cha-gtls-examples.texi b/doc/cha-gtls-examples.texi
index 8fbf6b8b9f..9d253a0045 100644
--- a/doc/cha-gtls-examples.texi
+++ b/doc/cha-gtls-examples.texi
@@ -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
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
index ba6d047591..ed1592cb06 100644
--- a/doc/examples/Makefile.am
+++ b/doc/examples/Makefile.am
@@ -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
diff --git a/doc/examples/ex-cert-select-pkcs11.c b/doc/examples/ex-cert-select-pkcs11.c
index bd7851c0be..e8cb21e367 100644
--- a/doc/examples/ex-cert-select-pkcs11.c
+++ b/doc/examples/ex-cert-select-pkcs11.c
@@ -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
diff --git a/doc/examples/ex-cert-select.c b/doc/examples/ex-cert-select.c
index 43f666fda8..d45e8e583a 100644
--- a/doc/examples/ex-cert-select.c
+++ b/doc/examples/ex-cert-select.c
@@ -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);
diff --git a/doc/examples/ex-client-dtls.c b/doc/examples/ex-client-dtls.c
index 222762a0b5..3c9d1ea679 100644
--- a/doc/examples/ex-client-dtls.c
+++ b/doc/examples/ex-client-dtls.c
@@ -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);
diff --git a/doc/examples/ex-client-resume.c b/doc/examples/ex-client-resume.c
index 5aeae58c92..1a041d196d 100644
--- a/doc/examples/ex-client-resume.c
+++ b/doc/examples/ex-client-resume.c
@@ -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
diff --git a/doc/examples/ex-client-srp.c b/doc/examples/ex-client-srp.c
index 5a753abff3..89d5165777 100644
--- a/doc/examples/ex-client-srp.c
+++ b/doc/examples/ex-client-srp.c
@@ -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
diff --git a/doc/examples/ex-client-x509.c b/doc/examples/ex-client-x509.c
index 0ea151dd72..c5ed190787 100644
--- a/doc/examples/ex-client-x509.c
+++ b/doc/examples/ex-client-x509.c
@@ -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);
diff --git a/doc/examples/ex-serv-dtls.c b/doc/examples/ex-serv-dtls.c
index 355f7b9839..a32a984051 100644
--- a/doc/examples/ex-serv-dtls.c
+++ b/doc/examples/ex-serv-dtls.c
@@ -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.
diff --git a/doc/examples/ex-serv-psk.c b/doc/examples/ex-serv-psk.c
index 70732f60e3..1f2af6c472 100644
--- a/doc/examples/ex-serv-psk.c
+++ b/doc/examples/ex-serv-psk.c
@@ -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
diff --git a/doc/examples/ex-serv-srp.c b/doc/examples/ex-serv-srp.c
index 70ae1660bb..5873b39920 100644
--- a/doc/examples/ex-serv-srp.c
+++ b/doc/examples/ex-serv-srp.c
@@ -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.
*/
diff --git a/doc/examples/ex-serv-x509.c b/doc/examples/ex-serv-x509.c
index 44c671a3f4..c06f13ec09 100644
--- a/doc/examples/ex-serv-x509.c
+++ b/doc/examples/ex-serv-x509.c
@@ -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
index 0000000000..c9fab66e28
--- /dev/null
+++ b/doc/examples/ex-verify-ssh.c
@@ -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;
+}
+
diff --git a/doc/examples/examples.h b/doc/examples/examples.h
index e96cb26080..0c2dbb3372 100644
--- a/doc/examples/examples.h
+++ b/doc/examples/examples.h
@@ -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,
diff --git a/lib/Makefile.am b/lib/Makefile.am
index d692a5864f..820f09062d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -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
diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
index 45008ae8d5..415e282719 100644
--- a/lib/includes/gnutls/gnutls.h.in
+++ b/lib/includes/gnutls/gnutls.h.in
@@ -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.
*/
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index e169a06170..40765f8d54 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -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 {
diff --git a/lib/openpgp/output.c b/lib/openpgp/output.c
index fdd176ac3a..88f522db70 100644
--- a/lib/openpgp/output.c
+++ b/lib/openpgp/output.c
@@ -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,
diff --git a/lib/system.c b/lib/system.c
index eddb1053a4..476b313787 100644
--- a/lib/system.c
+++ b/lib/system.c
@@ -25,14 +25,20 @@
#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;
+}
diff --git a/lib/system.h b/lib/system.h
index df4ee5a36d..0d2f5a501f 100644
--- a/lib/system.h
+++ b/lib/system.h
@@ -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
index 0000000000..8d6562f705
--- /dev/null
+++ b/lib/verify-ssh.c
@@ -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;
+}
diff --git a/lib/x509/output.c b/lib/x509/output.c
index 564004fb35..827845f427 100644
--- a/lib/x509/output.c
+++ b/lib/x509/output.c
@@ -1700,6 +1700,22 @@ gnutls_x509_crt_print (gnutls_x509_crt_t cert,
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)
{
_gnutls_buffer_init (&str);
diff --git a/src/cli-args.def.in b/src/cli-args.def.in
index a691c8623f..92c3bd63f8 100644
--- a/src/cli-args.def.in
+++ b/src/cli-args.def.in
@@ -37,6 +37,12 @@ flag = {
};
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;
descrip = "Connect, establish a session. Connect again and resume this session";
@@ -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 = "";
};
diff --git a/src/cli.c b/src/cli.c
index 27885c5f72..4226e773d4 100644
--- 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
diff --git a/src/common.c b/src/common.c
index 40aa5b05d5..38ea2cffb6 100644
--- a/src/common.c
+++ b/src/common.c
@@ -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;
}
}
diff --git a/src/common.h b/src/common.h
index dd6d569881..aeeb395e9f 100644
--- a/src/common.h
+++ b/src/common.h
@@ -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);
diff --git a/src/tests.c b/src/tests.c
index ede92c3eb4..cbed4680fa 100644
--- a/src/tests.c
+++ b/src/tests.c
@@ -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;
}