From 47df352e2d1782e33b09d0eacccbc6144a71fcbc Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Sun, 3 Jun 2007 20:26:33 +0200 Subject: [PATCH] TLS handshaking work. --- submit/Makefile | 9 ++- submit/connect.c | 160 +++++++++++++++++++++++++++++++++++++++ submit/submitd.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 356 insertions(+), 6 deletions(-) create mode 100644 submit/connect.c diff --git a/submit/Makefile b/submit/Makefile index d5fe54a..ccac81a 100644 --- a/submit/Makefile +++ b/submit/Makefile @@ -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 index 0000000..7f5709e --- /dev/null +++ b/submit/connect.c @@ -0,0 +1,160 @@ +/* + * A Simple Testing Client + * + * (c) 2007 Martin Mares + */ + +#include "lib/lib.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +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; +} diff --git a/submit/submitd.c b/submit/submitd.c index 1bc63d1..09bf581 100644 --- a/submit/submitd.c +++ b/submit/submitd.c @@ -1,17 +1,38 @@ +/* + * The Submit Daemon + * + * (c) 2007 Martin Mares + */ + +#define LOCAL_DEBUG + #include "lib/lib.h" +#include +#include +#include +#include +#include #include +#include + +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; } -- 2.39.2