From 3d74096021d95a5073c4d208cc2e31375bd237a1 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Thu, 8 May 2008 13:24:20 +0200 Subject: added a bit of doc (at least something...) --- runtime/nsd_gtls.c | 234 ++++++++++++++++++++++++++++++++++++- runtime/nsdsel_gtls.c | 4 + runtime/rsyslog.h | 1 + tools/gnutls/cert-gen-selfsigned | 3 + tools/gnutls/cert-show-fingerprint | 4 + 5 files changed, 245 insertions(+), 1 deletion(-) diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c index bd97254d..799992fe 100644 --- a/runtime/nsd_gtls.c +++ b/runtime/nsd_gtls.c @@ -23,10 +23,12 @@ * A copy of the LGPL can be found in the file "COPYING.LESSER" in this distribution. */ #include "config.h" +#include #include #include #include #include +#include #include #include @@ -67,9 +69,192 @@ static int bGlblSrvrInitDone = 0; /**< 0 - server global init not yet done, 1 - /* ------------------------------ GnuTLS specifics ------------------------------ */ +////////////////// experimental /////////////////// +uchar *gtlsStrerror(int error); +static const char* bin2hex(void* bin, size_t bin_size) +{ + static char printable[170]; + uchar *_bin = bin; + char* print; + size_t i; + + if (bin_size > 50) bin_size = 50; + + print = printable; + for(i = 0; i < bin_size; i++) { + sprintf(print, "%2.2X:", _bin[i]); + print += 3; + } + + return printable; +} + +#if 0 +/* This function will print information about this session's peer + * certificate. + */ +static rsRetVal +print_x509_certificate_info(gnutls_session session) +{ + char fingerprint[20]; + char serial[40]; + char dn[128]; + size_t size; + unsigned int algo, bits; + time_t expiration_time, activation_time; + const gnutls_datum *cert_list; + int cert_list_size = 0; + gnutls_x509_crt cert; + int gnuRet; + DEFiRet; + + /* This function only works for X.509 certificates. + */ + if(gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) + return RS_RET_TLS_CERT_ERR; + + cert_list = gnutls_certificate_get_peers(session, &cert_list_size); + + dbgprintf("Peer provided %d certificates.\n", cert_list_size); + + if(cert_list_size > 0) { + /* we only print information about the first certificate */ + gnutls_x509_crt_init( &cert); + + CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)); + + dbgprintf("Certificate info:\n"); + + expiration_time = gnutls_x509_crt_get_expiration_time(cert); + activation_time = gnutls_x509_crt_get_activation_time(cert); + + dbgprintf("\tCertificate is valid since: %s", ctime(&activation_time)); + dbgprintf("\tCertificate expires: %s", ctime(&expiration_time)); + + /* Print the serial number of the certificate */ + size = sizeof(serial); + CHKgnutls(gnutls_x509_crt_get_serial(cert, serial, &size)); + dbgprintf("\tCertificate serial number: %s\n", bin2hex( serial, size)); + + /* print the SHA1 fingerprint */ + size = sizeof(fingerprint); + CHKgnutls(gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, fingerprint, &size)); + dbgprintf("\tCertificate SHA1 fingerprint: %s\n", bin2hex(fingerprint, size)); + + /* Extract some of the public key algorithm's parameters + */ + algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); + + dbgprintf("Certificate public key: %s", gnutls_pk_algorithm_get_name(algo)); + + /* Print the version of the X.509 + * certificate. + */ + dbgprintf("\tCertificate version: #%d\n", gnutls_x509_crt_get_version(cert)); + + size = sizeof(dn); + gnutls_x509_crt_get_dn( cert, dn, &size); + dbgprintf("\tDN: %s\n", dn); + + size = sizeof(dn); + gnutls_x509_crt_get_issuer_dn( cert, dn, &size); + dbgprintf("\tIssuer's DN: %s\n", dn); + + gnutls_x509_crt_deinit( cert); + } + +finalize_it: + RETiRet; +} + + + +/* This function will print some details of the + * given session. + */ +int print_info(gnutls_session session) +{ + const char *tmp; + gnutls_credentials_type cred; + gnutls_kx_algorithm kx; + + /* print the key exchange's algorithm name + */ + kx = gnutls_kx_get(session); + tmp = gnutls_kx_get_name(kx); + dbgprintf("- Key Exchange: %s\n", tmp); + + /* Check the authentication type used and switch + * to the appropriate. + */ + cred = gnutls_auth_get_type(session); + switch (cred) { + case GNUTLS_CRD_ANON: /* anonymous authentication */ + dbgprintf("- Anonymous DH using prime of %d bits\n", + gnutls_dh_get_prime_bits(session)); + break; + case GNUTLS_CRD_CERTIFICATE: /* certificate authentication */ + /* Check if we have been using ephemeral Diffie Hellman. + */ + if (kx == GNUTLS_KX_DHE_RSA || kx == GNUTLS_KX_DHE_DSS) { + dbgprintf("\n- Ephemeral DH using prime of %d bits\n", + gnutls_dh_get_prime_bits(session)); + } + + /* if the certificate list is available, then + * print some information about it. + */ + print_x509_certificate_info(session); + break; + case GNUTLS_CRD_SRP: /* certificate authentication */ + dbgprintf("GNUTLS_CRD_SRP/IA"); + break; + case GNUTLS_CRD_PSK: /* certificate authentication */ + dbgprintf("GNUTLS_CRD_PSK"); + break; + case GNUTLS_CRD_IA: /* certificate authentication */ + dbgprintf("GNUTLS_CRD_IA"); + break; + } /* switch */ + + /* print the protocol's name (ie TLS 1.0) */ + tmp = gnutls_protocol_get_name(gnutls_protocol_get_version(session)); + dbgprintf("- Protocol: %s\n", tmp); + + /* print the certificate type of the peer. + * ie X.509 + */ + tmp = gnutls_certificate_type_get_name( + gnutls_certificate_type_get(session)); + + dbgprintf("- Certificate Type: %s\n", tmp); + + /* print the compression algorithm (if any) + */ + tmp = gnutls_compression_get_name( gnutls_compression_get(session)); + dbgprintf("- Compression: %s\n", tmp); + + /* print the name of the cipher used. + * ie 3DES. + */ + tmp = gnutls_cipher_get_name(gnutls_cipher_get(session)); + dbgprintf("- Cipher: %s\n", tmp); + + /* Print the MAC algorithms name. + * ie SHA1 + */ + tmp = gnutls_mac_get_name(gnutls_mac_get(session)); + dbgprintf("- MAC: %s\n", tmp); + + return 0; +} +#endif + +////////////////////////////////////////////////////////////////////// static gnutls_certificate_credentials xcred; static gnutls_dh_params dh_params; + /* a thread-safe variant of gnutls_strerror - TODO: implement it! * The caller must free the returned string. * rgerhards, 2008-04-30 @@ -142,7 +327,6 @@ finalize_it: } - static rsRetVal generate_dh_params(void) { @@ -191,6 +375,50 @@ finalize_it: } +/* check the fingerprint of the remote peer's certificate. + * rgerhards, 2008-05-08 + */ +static rsRetVal +gtlsChkFingerprint(nsd_gtls_t *pThis) +{ + char fingerprint[20]; + size_t size; + const gnutls_datum *cert_list; + int cert_list_size = 0; + gnutls_x509_crt cert; + int gnuRet; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + /* This function only works for X.509 certificates. */ + if(gnutls_certificate_type_get(pThis->sess) != GNUTLS_CRT_X509) + return RS_RET_TLS_CERT_ERR; + + cert_list = gnutls_certificate_get_peers(pThis->sess, &cert_list_size); + + /* we always use only the first certificate. As of GnuTLS documentation, the + * first certificate always contains the remote peers own certificate. All other + * certificates are issuer's certificates (up the chain). However, we do not match + * against some issuer fingerprint but only ourselfs. -- rgerhards, 2008-05-08 + */ + if(cert_list_size > 0) { + CHKgnutls(gnutls_x509_crt_init(&cert)); + CHKgnutls(gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER)); + + /* obtain the SHA1 fingerprint */ + size = sizeof(fingerprint); + CHKgnutls(gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA1, fingerprint, &size)); + dbgprintf("\tCertificate SHA1 fingerprint: %s\n", bin2hex(fingerprint, size)); + + gnutls_x509_crt_deinit(cert); + } + +finalize_it: + RETiRet; +} + + /* globally de-initialize GnuTLS */ static rsRetVal gtlsGlblExit(void) @@ -408,6 +636,7 @@ AcceptConnReq(nsd_t *pNsd, nsd_t **ppNew) } else if(gnuRet != 0) { ABORT_FINALIZE(RS_RET_TLS_HANDSHAKE_ERR); } + pNew->iMode = 1; /* this session is now in TLS mode! */ *ppNew = (nsd_t*) pNew; @@ -532,6 +761,9 @@ Connect(nsd_t *pNsd, int family, uchar *port, uchar *host) CHKgnutls(gnutls_handshake(pThis->sess)); dbgprintf("GnuTLS handshake succeeded\n"); + /* now check if the remote peer is permitted to talk to us */ + CHKiRet(gtlsChkFingerprint(pThis)); + finalize_it: if(iRet != RS_RET_OK) { if(pThis->bHaveSess) { diff --git a/runtime/nsdsel_gtls.c b/runtime/nsdsel_gtls.c index 8c1e705b..bdd73419 100644 --- a/runtime/nsdsel_gtls.c +++ b/runtime/nsdsel_gtls.c @@ -128,6 +128,10 @@ doRetry(nsd_gtls_t *pNsd) switch(pNsd->rtryCall) { case gtlsRtry_handshake: gnuRet = gnutls_handshake(pNsd->sess); + if(gnuRet == 0) { + /* we got a handshake, now print session info */ + print_info(pNsd->sess); + } break; default: assert(0); /* this shall not happen! */ diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index c32e190c..0be5d49a 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -222,6 +222,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_INVAID_DRVR_MODE = -2081, /**< tried to set mode not supported by driver */ RS_RET_DRVRNAME_TOO_LONG = -2082, /**< driver name too long - should never happen */ RS_RET_TLS_HANDSHAKE_ERR = -2083, /**< TLS handshake failed */ + RS_RET_TLS_CERT_ERR = -2084, /**< generic TLS certificate error */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ diff --git a/tools/gnutls/cert-gen-selfsigned b/tools/gnutls/cert-gen-selfsigned index 84e6e6d6..e1c25386 100755 --- a/tools/gnutls/cert-gen-selfsigned +++ b/tools/gnutls/cert-gen-selfsigned @@ -1,3 +1,6 @@ #/bin/sh +# generates a self-signed certificate and key suitable for use with rsyslog +# 2008-05-08, rgerhards +# TODO: make this a robust shell script certtool --generate-privkey --outfile $1-key.pem certtool --generate-self-signed --load-privkey $1-key.pem --outfile $1-cert.pem diff --git a/tools/gnutls/cert-show-fingerprint b/tools/gnutls/cert-show-fingerprint index 1324ef1c..f61c6840 100755 --- a/tools/gnutls/cert-show-fingerprint +++ b/tools/gnutls/cert-show-fingerprint @@ -1,2 +1,6 @@ #/bin/sh +# must be called with the certificate file as first parameter. Displays all +# fingerprints for the first certificate. +# 2008-05-08, rgerhards +# TODO: make this a robust shell script certtool -i < $1|grep Fingerprint -- cgit