]> mj.ucw.cz Git - eval.git/commitdiff
TLS handshaking work.
authorMartin Mares <mj@ucw.cz>
Sun, 3 Jun 2007 18:26:33 +0000 (20:26 +0200)
committerMartin Mares <mj@ucw.cz>
Sun, 3 Jun 2007 18:26:33 +0000 (20:26 +0200)
submit/Makefile
submit/connect.c [new file with mode: 0644]
submit/submitd.c

index d5fe54a9018a03bfc9ca547c25aa9f73ebb1a98b..ccac81a243c93f75b0e1d10c0ee2419f7ba88e3e 100644 (file)
@@ -4,10 +4,12 @@ TLSLF:=$(shell libgnutls-config --libs)
 CFLAGS=-O2 -Iinclude -g -Wall -W -Wno-parentheses -Wstrict-prototypes -Wmissing-prototypes -Wundef -Wredundant-decls -std=gnu99 $(TLSCF)
 LDFLAGS=$(TLSLF)
 
-all: submitd
+all: submitd connect
 
 submitd: submitd.o lib/libucw.a lib/libsh.a
 submitd.o: submitd.c
+connect: connect.o lib/libucw.a
+connect.o: connect.c
 
 certs:
        certtool --generate-privkey --outfile ca-key.pem
@@ -15,8 +17,11 @@ certs:
        certtool --generate-privkey --outfile server-key.pem
        certtool --generate-request --load-privkey server-key.pem --outfile server-req.pem --template server-cert.tpl
        certtool --generate-certificate --load-request server-req.pem --outfile server-cert.pem --load-ca-certificate ca-cert.pem --load-ca-privkey ca-key.pem --template server-cert.tpl
+       certtool --generate-privkey --outfile client-key.pem
+       certtool --generate-request --load-privkey client-key.pem --outfile client-req.pem --template client-cert.tpl
+       certtool --generate-certificate --load-request client-req.pem --outfile client-cert.pem --load-ca-certificate ca-cert.pem --load-ca-privkey ca-key.pem --template client-cert.tpl
        # Beware of serial numbers
 
 clean:
        rm -f `find . -name "*~" -or -name "*.[oa]" -or -name "\#*\#" -or -name TAGS -or -name core -or -name .depend -or -name .#*`
-       rm -f submitd
+       rm -f submitd connect
diff --git a/submit/connect.c b/submit/connect.c
new file mode 100644 (file)
index 0000000..7f5709e
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ *  A Simple Testing Client
+ *
+ *  (c) 2007 Martin Mares <mj@ucw.cz>
+ */
+
+#include "lib/lib.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
+#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+static int port = 8888;
+
+static gnutls_certificate_credentials_t cert_cred;
+
+#define TLS_CHECK(name) if (err < 0) die(#name " failed: %s", gnutls_strerror(err))
+
+static void
+tls_init(void)
+{
+  int err;
+
+  log(L_INFO, "Initializing TLS");
+  gnutls_global_init();
+  err = gnutls_certificate_allocate_credentials(&cert_cred);
+  TLS_CHECK(gnutls_certificate_allocate_credentials);
+  err = gnutls_certificate_set_x509_trust_file(cert_cred, "ca-cert.pem", GNUTLS_X509_FMT_PEM);
+  if (!err)
+    die("No CA certificate found");
+  if (err < 0)
+    die("Unable to load X509 trust file: %s", gnutls_strerror(err));
+  err = gnutls_certificate_set_x509_key_file(cert_cred, "client-cert.pem", "client-key.pem", GNUTLS_X509_FMT_PEM);
+  if (err < 0)
+    die("Unable to load X509 key file: %s", gnutls_strerror(err));
+}
+
+static const char *
+tls_verify_cert(gnutls_session_t s)
+{
+  uns status, num_certs;
+  int err;
+  gnutls_x509_crt_t cert;
+  const gnutls_datum_t *certs;
+
+  DBG("Verifying peer certificates");
+  err = gnutls_certificate_verify_peers2(s, &status);
+  if (err < 0)
+    return gnutls_strerror(err);
+  DBG("Verify status: %04x", status);
+  if (status & GNUTLS_CERT_INVALID)
+    return "Certificate is invalid";
+  /* XXX: We do not handle revokation. */
+  if (gnutls_certificate_type_get(s) != GNUTLS_CRT_X509)
+    return "Certificate is not X509";
+
+  err = gnutls_x509_crt_init(&cert);
+  if (err < 0)
+    return "gnutls_x509_crt_init() failed";
+  certs = gnutls_certificate_get_peers(s, &num_certs);
+  if (!certs)
+    return "No peer certificate found";
+  DBG("Got certificate list with %d peers", num_certs);
+
+  err = gnutls_x509_crt_import(cert, &certs[0], GNUTLS_X509_FMT_DER);
+  if (err < 0)
+    return "Cannot import certificate";
+  /* XXX: We do not check expiration and activation since the keys are generated for a single contest only anyway. */
+  /* XXX: Neither we check host name */
+
+  /* Check certificate purpose */
+  byte purp[256];
+  int purpi = 0;
+  do
+    {
+      size_t purp_len = sizeof(purp);
+      uns crit;
+      err = gnutls_x509_crt_get_key_purpose_oid(cert, purpi++, purp, &purp_len, &crit);
+      if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       return "Not a client certificate";
+      TLS_CHECK(gnutls_x509_crt_get_key_purpose_oid);
+    }
+  while (strcmp(purp, GNUTLS_KP_TLS_WWW_SERVER));
+
+  DBG("Verified OK");
+  return NULL;
+}
+
+static void
+tls_log_params(gnutls_session_t s)
+{
+  const char *proto = gnutls_protocol_get_name(gnutls_protocol_get_version(s));
+  const char *kx = gnutls_kx_get_name(gnutls_kx_get(s));
+  const char *cert = gnutls_certificate_type_get_name(gnutls_certificate_type_get(s));
+  const char *comp = gnutls_compression_get_name(gnutls_compression_get(s));
+  const char *cipher = gnutls_cipher_get_name(gnutls_cipher_get(s));
+  const char *mac = gnutls_mac_get_name(gnutls_mac_get(s));
+  log(L_DEBUG, "TLS params: proto=%s kx=%s cert=%s comp=%s cipher=%s mac=%s",
+    proto, kx, cert, comp, cipher, mac);
+}
+
+int main(int argc UNUSED, char **argv UNUSED)
+{
+  tls_init();
+
+  int sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (sk < 0)
+    die("socket: %m");
+
+  log(L_INFO, "Connecting to port %d", port);
+  struct sockaddr_in sa;
+  bzero(&sa, sizeof(sa));
+  sa.sin_family = AF_INET;
+  sa.sin_addr.s_addr = htonl(0x7f000001);
+  sa.sin_port = htons(port);
+  if (connect(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+    die("Cannot connect: %m");
+
+  gnutls_session_t s;
+  gnutls_init(&s, GNUTLS_CLIENT);
+  gnutls_set_default_priority(s);
+  gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, cert_cred);
+  gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t) sk);
+
+  log(L_INFO, "Handshaking");
+  int err = gnutls_handshake(s); TLS_CHECK(gnutls_handshake);
+  tls_log_params(s);
+  const char *cert_err = tls_verify_cert(s);
+  if (cert_err)
+    die("Certificate verification failed: %s", cert_err);
+
+  log(L_INFO, "Session established");
+  for (;;)
+    {
+      byte buf[1024];
+      if (!fgets(buf, sizeof(buf), stdin))
+       break;
+      int len = strlen(buf);
+      err = gnutls_record_send(s, buf, len); TLS_CHECK(gnutls_record_send);
+      err = gnutls_record_recv(s, buf, len); TLS_CHECK(gnutls_record_recv);
+      if (!err)
+       {
+         log(L_INFO, "Connection closed");
+         break;
+       }
+      fwrite(buf, 1, err, stdout);
+      fflush(stdout);
+    }
+
+  gnutls_bye(s, GNUTLS_SHUT_RDWR);
+  close(sk);
+  gnutls_deinit(s);
+
+  return 0;
+}
index 1bc63d11f65d6fbfdbecac6f29b29176ff60399c..09bf58163a812c806c05728b21e2bb3f1422ffd2 100644 (file)
@@ -1,17 +1,38 @@
+/*
+ *  The Submit Daemon
+ *
+ *  (c) 2007 Martin Mares <mj@ucw.cz>
+ */
+
+#define LOCAL_DEBUG
+
 #include "lib/lib.h"
 
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <netinet/in.h>
 #include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
+
+static int port = 8888;
 
 static gnutls_certificate_credentials_t cert_cred;
+static gnutls_dh_params_t dh_params;
 
-int main(void)
+#define DH_BITS 1024
+#define TLS_CHECK(name) if (err < 0) die(#name " failed: %s", gnutls_strerror(err))
+
+static void
+tls_init(void)
 {
   int err;
 
+  log(L_INFO, "Initializing TLS");
   gnutls_global_init();
   err = gnutls_certificate_allocate_credentials(&cert_cred);
-  if (err)
-    die("Unable to allocate credentials: %s", gnutls_strerror(err));
+  TLS_CHECK(gnutls_certificate_allocate_credentials);
   err = gnutls_certificate_set_x509_trust_file(cert_cred, "ca-cert.pem", GNUTLS_X509_FMT_PEM);
   if (!err)
     die("No CA certificate found");
@@ -21,6 +42,170 @@ int main(void)
   if (err < 0)
     die("Unable to load X509 key file: %s", gnutls_strerror(err));
 
-  gnutls_global_deinit();
+  log(L_INFO, "Setting up DH parameters");
+  err = gnutls_dh_params_init(&dh_params); TLS_CHECK(gnutls_dh_params_init);
+  err = gnutls_dh_params_generate2(dh_params, DH_BITS); TLS_CHECK(gnutls_dh_params_generate2);
+  gnutls_certificate_set_dh_params(cert_cred, dh_params);
+}
+
+static gnutls_session_t
+tls_new_session(int sk)
+{
+  gnutls_session_t s;
+  int err;
+
+  err = gnutls_init(&s, GNUTLS_SERVER); TLS_CHECK(gnutls_init);
+  err = gnutls_set_default_priority(s); TLS_CHECK(gnutls_set_default_priority);                        // FIXME
+  gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, cert_cred);
+  gnutls_certificate_server_set_request(s, GNUTLS_CERT_REQUEST);
+  gnutls_dh_set_prime_bits(s, DH_BITS);
+  gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t) sk);
+  return s;
+}
+
+static const char *
+tls_verify_cert(gnutls_session_t s)
+{
+  uns status, num_certs;
+  int err;
+  gnutls_x509_crt_t cert;
+  const gnutls_datum_t *certs;
+
+  DBG("Verifying peer certificates");
+  err = gnutls_certificate_verify_peers2(s, &status);
+  if (err < 0)
+    return gnutls_strerror(err);
+  DBG("Verify status: %04x", status);
+  if (status & GNUTLS_CERT_INVALID)
+    return "Certificate is invalid";
+  /* XXX: We do not handle revokation. */
+  if (gnutls_certificate_type_get(s) != GNUTLS_CRT_X509)
+    return "Certificate is not X509";
+
+  err = gnutls_x509_crt_init(&cert);
+  if (err < 0)
+    return "gnutls_x509_crt_init() failed";
+  certs = gnutls_certificate_get_peers(s, &num_certs);
+  if (!certs)
+    return "No peer certificate found";
+  DBG("Got certificate list with %d peers", num_certs);
+
+  err = gnutls_x509_crt_import(cert, &certs[0], GNUTLS_X509_FMT_DER);
+  if (err < 0)
+    return "Cannot import certificate";
+  /* XXX: We do not check expiration and activation since the keys are generated for a single contest only anyway. */
+
+  byte dn[256];
+  size_t dn_len = sizeof(dn);
+  err = gnutls_x509_crt_get_dn_by_oid(cert, GNUTLS_OID_X520_COMMON_NAME, 0, 0, dn, &dn_len);
+  if (err < 0)
+    return "Cannot retrieve common name";
+  log(L_INFO, "Cert CN: %s", dn);
+
+  /* Check certificate purpose */
+  byte purp[256];
+  int purpi = 0;
+  do
+    {
+      size_t purp_len = sizeof(purp);
+      uns crit;
+      err = gnutls_x509_crt_get_key_purpose_oid(cert, purpi++, purp, &purp_len, &crit);
+      if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
+       return "Not a client certificate";
+      TLS_CHECK(gnutls_x509_crt_get_key_purpose_oid);
+    }
+  while (strcmp(purp, GNUTLS_KP_TLS_WWW_CLIENT));
+
+  DBG("Verified OK");
+  return NULL;
+}
+
+static void
+tls_log_params(gnutls_session_t s)
+{
+  const char *proto = gnutls_protocol_get_name(gnutls_protocol_get_version(s));
+  const char *kx = gnutls_kx_get_name(gnutls_kx_get(s));
+  const char *cert = gnutls_certificate_type_get_name(gnutls_certificate_type_get(s));
+  const char *comp = gnutls_compression_get_name(gnutls_compression_get(s));
+  const char *cipher = gnutls_cipher_get_name(gnutls_cipher_get(s));
+  const char *mac = gnutls_mac_get_name(gnutls_mac_get(s));
+  log(L_DEBUG, "TLS params: proto=%s kx=%s cert=%s comp=%s cipher=%s mac=%s",
+    proto, kx, cert, comp, cipher, mac);
+}
+
+int main(int argc UNUSED, char **argv UNUSED)
+{
+  tls_init();
+
+  int sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (sk < 0)
+    die("socket: %m");
+  int one = 1;
+  if (setsockopt(sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0)
+    die("setsockopt(SO_REUSEADDR): %m");
+
+  struct sockaddr_in sa;
+  bzero(&sa, sizeof(sa));
+  sa.sin_family = AF_INET;
+  sa.sin_addr.s_addr = INADDR_ANY;
+  sa.sin_port = htons(port);
+  if (bind(sk, (struct sockaddr *) &sa, sizeof(sa)) < 0)
+    die("Cannot bind to port %d: %m", port);
+  if (listen(sk, 1024) < 0)
+    die("Cannot listen on port %d: %m", port);
+  log(L_INFO, "Listening on port %d", port);
+
+  for (;;)
+    {
+      struct sockaddr_in sa2;
+      int sa2len = sizeof(sa2);
+      int sk2 = accept(sk, (struct sockaddr *) &sa2, &sa2len);
+      if (sk2 < 0)
+       die("accept: %m");
+
+      byte ipbuf[INET_ADDRSTRLEN];
+      inet_ntop(AF_INET, &sa2.sin_addr, ipbuf, sizeof(ipbuf));
+      log(L_INFO, "Connection from %s port %d", ipbuf, ntohs(sa2.sin_port));
+
+      gnutls_session_t sess = tls_new_session(sk2);
+      int err = gnutls_handshake(sess);
+      if (err < 0)
+       {
+         log(L_ERROR_R, "Handshake failed: %s", gnutls_strerror(err));
+         goto shut;
+       }
+      tls_log_params(sess);
+
+      const char *cert_err = tls_verify_cert(sess);
+      if (cert_err)
+       {
+         log(L_ERROR_R, "Certificate verification failed: %s", cert_err);
+         goto shut;
+       }
+
+      for (;;)
+       {
+         byte buf[1024];
+         int ret = gnutls_record_recv(sess, buf, sizeof(buf));
+         if (ret < 0)
+           {
+             log(L_ERROR_R, "Connection broken: %s", gnutls_strerror(ret));
+             break;
+           }
+         if (!ret)
+           {
+             log(L_INFO, "Client closed connection");
+             break;
+           }
+         log(L_DEBUG, "Received %d bytes", ret);
+         gnutls_record_send(sess, buf, ret);
+       }
+
+      gnutls_bye(sess, GNUTLS_SHUT_WR);
+shut:
+      close(sk2);
+      gnutls_deinit(sess);
+    }
+
   return 0;
 }