From a099270d452af92c5640d5d6712aa0987adb7c44 Mon Sep 17 00:00:00 2001 From: Jilles Tjoelker Date: Sun, 31 Jan 2010 19:04:20 +0100 Subject: [PATCH] Add certfp support to libratbox and ssld. This lets a user connect with a client certificate, and passes the certificate's fingerprint to ircd, which currently just notices it to the user. A new ssld->ircd message 'F' is used to pass on the fingerprint. This is only for OpenSSL for now, not GNUTLS. --- libratbox/include/rb_commio.h | 3 +++ libratbox/src/gnutls.c | 7 +++++++ libratbox/src/nossl.c | 6 ++++++ libratbox/src/openssl.c | 33 +++++++++++++++++++++++++++++++++ src/sslproc.c | 26 ++++++++++++++++++++++++++ ssld/ssld.c | 8 ++++++++ 6 files changed, 83 insertions(+) diff --git a/libratbox/include/rb_commio.h b/libratbox/include/rb_commio.h index 64aa889..1eef573 100644 --- a/libratbox/include/rb_commio.h +++ b/libratbox/include/rb_commio.h @@ -100,6 +100,8 @@ void rb_note(rb_fde_t *, const char *); #define RB_SELECT_ACCEPT RB_SELECT_READ #define RB_SELECT_CONNECT RB_SELECT_WRITE +#define RB_SSL_CERTFP_LEN 20 + int rb_set_nb(rb_fde_t *); int rb_set_buffers(rb_fde_t *, int); @@ -141,6 +143,7 @@ int rb_select(unsigned long); int rb_fd_ssl(rb_fde_t *F); int rb_get_fd(rb_fde_t *F); const char *rb_get_ssl_strerror(rb_fde_t *F); +int rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN]); rb_fde_t *rb_get_fde(int fd); diff --git a/libratbox/src/gnutls.c b/libratbox/src/gnutls.c index 0da7748..1b7f4d7 100644 --- a/libratbox/src/gnutls.c +++ b/libratbox/src/gnutls.c @@ -496,6 +496,13 @@ rb_get_ssl_strerror(rb_fde_t *F) return gnutls_strerror(F->ssl_errno); } +int +rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN]) +{ + /* XXX implement this for gnutls */ + return 0; +} + int rb_supports_ssl(void) { diff --git a/libratbox/src/nossl.c b/libratbox/src/nossl.c index 1d5c15f..ee85189 100644 --- a/libratbox/src/nossl.c +++ b/libratbox/src/nossl.c @@ -100,6 +100,12 @@ rb_get_ssl_strerror(rb_fde_t *F) return nosupport; } +int +rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN]) +{ + return 0; +} + void rb_ssl_start_accepted(rb_fde_t *new_F, ACCB * cb, void *data, int timeout) { diff --git a/libratbox/src/openssl.c b/libratbox/src/openssl.c index ffe8330..9ddc356 100644 --- a/libratbox/src/openssl.c +++ b/libratbox/src/openssl.c @@ -281,6 +281,12 @@ rb_ssl_write(rb_fde_t *F, const void *buf, size_t count) return rb_ssl_read_or_write(1, F, NULL, buf, count); } +static int +verify_accept_all_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + return 1; +} + int rb_init_ssl(void) { @@ -298,6 +304,7 @@ rb_init_ssl(void) } /* Disable SSLv2, make the client use our settings */ SSL_CTX_set_options(ssl_server_ctx, SSL_OP_NO_SSLv2 | SSL_OP_CIPHER_SERVER_PREFERENCE); + SSL_CTX_set_verify(ssl_server_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb); ssl_client_ctx = SSL_CTX_new(TLSv1_client_method()); @@ -605,6 +612,32 @@ rb_get_ssl_strerror(rb_fde_t *F) return ERR_error_string(F->ssl_errno, NULL); } +int +rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN]) +{ + X509 *cert; + int res; + + if (F->ssl == NULL) + return 0; + + cert = SSL_get_peer_certificate((SSL *) F->ssl); + if(cert != NULL) + { + res = SSL_get_verify_result((SSL *) F->ssl); + if(res == X509_V_OK || + res == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN || + res == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE || + res == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) + { + memcpy(certfp, cert->sha1_hash, RB_SSL_CERTFP_LEN); + return 1; + } + } + + return 0; +} + int rb_supports_ssl(void) { diff --git a/src/sslproc.c b/src/sslproc.c index 573482a..b02a045 100644 --- a/src/sslproc.c +++ b/src/sslproc.c @@ -402,6 +402,29 @@ ssl_process_dead_fd(ssl_ctl_t * ctl, ssl_ctl_buf_t * ctl_buf) exit_client(client_p, client_p, &me, reason); } +static void +ssl_process_certfp(ssl_ctl_t * ctl, ssl_ctl_buf_t * ctl_buf) +{ + struct Client *client_p; + int32_t fd; + uint8_t *certfp; + char certfp_string[RB_SSL_CERTFP_LEN * 2 + 1]; + int i; + + if(ctl_buf->buflen != 5 + RB_SSL_CERTFP_LEN) + return; /* bogus message..drop it.. XXX should warn here */ + + fd = buf_to_int32(&ctl_buf->buf[1]); + certfp = (uint8_t *)&ctl_buf->buf[5]; + client_p = find_cli_fd_hash(fd); + if(client_p == NULL) + return; + for(i = 0; i < RB_SSL_CERTFP_LEN; i++) + rb_snprintf(certfp_string + 2 * i, 3, "%02x", + certfp[i]); + sendto_one_notice(client_p, ":*** CertFP is %s", certfp_string); +} + static void ssl_process_cmd_recv(ssl_ctl_t * ctl) { @@ -422,6 +445,9 @@ ssl_process_cmd_recv(ssl_ctl_t * ctl) case 'D': ssl_process_dead_fd(ctl, ctl_buf); break; + case 'F': + ssl_process_certfp(ctl, ctl_buf); + break; case 'S': ssl_process_zipstats(ctl, ctl_buf); break; diff --git a/ssld/ssld.c b/ssld/ssld.c index b28520d..a6c6b5f 100644 --- a/ssld/ssld.c +++ b/ssld/ssld.c @@ -679,8 +679,16 @@ static void ssl_process_accept_cb(rb_fde_t *F, int status, struct sockaddr *addr, rb_socklen_t len, void *data) { conn_t *conn = data; + char buf[5 + RB_SSL_CERTFP_LEN]; + if(status == RB_OK) { + if(rb_get_ssl_certfp(F, &buf[5])) + { + buf[0] = 'F'; + int32_to_buf(&buf[1], conn->id); + mod_cmd_write_queue(conn->ctl, buf, sizeof buf); + } conn_mod_read_cb(conn->mod_fd, conn); conn_plain_read_cb(conn->plain_fd, conn); return;