summaryrefslogtreecommitdiff
path: root/src
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 /src
parenteb3ba487cd5881107f8c63dd3ae4356ccb847dff (diff)
downloadgnutls-dc42971afc5051136ebc8d4b21cb49a2055d4a7b.tar.gz
Added gnutls_verify_stored_pubkey() and gnutls_store_pubkey().
This enables using ssh-like authentication for TLS sessions.
Diffstat (limited to 'src')
-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
5 files changed, 325 insertions, 107 deletions
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;
}