diff options
-rw-r--r-- | doc/ns_gtls.html | 13 | ||||
-rw-r--r-- | runtime/nsd_gtls.c | 273 | ||||
-rw-r--r-- | runtime/nsd_gtls.h | 4 | ||||
-rw-r--r-- | runtime/nsdsel_gtls.c | 2 | ||||
-rw-r--r-- | runtime/rsyslog.h | 1 |
5 files changed, 286 insertions, 7 deletions
diff --git a/doc/ns_gtls.html b/doc/ns_gtls.html index 46e2e238..46671f4a 100644 --- a/doc/ns_gtls.html +++ b/doc/ns_gtls.html @@ -24,6 +24,8 @@ described in IETF's draft-ietf-syslog-transport-tls-12 Internet draft</li> <li><span style="font-weight: bold;">x509/fingerprint</span> - certificate fingerprint authentication as described in IETF's draft-ietf-syslog-transport-tls-12 Internet draft</li> +<li><span style="font-weight: bold;">x509/certvalid</span> +- certificate validation only</li> <li><span style="font-weight: bold;">x509/name</span> - certificate validation and subject name authentication as described in IETF's draft-ietf-syslog-transport-tls-12 Internet draft @@ -31,8 +33,13 @@ described in IETF's draft-ietf-syslog-transport-tls-12 Internet draft </ul> Note: "anon" does not permit to authenticate the remote peer. As such, this mode is vulnerable to man in the middle attacks as well as -unauthorized access. It is recommended NOT to use this mode.<br> -<br> +unauthorized access. It is recommended NOT to use this mode.</p> +<p>x509/certvalid is a nonstandard mode. It validates the remote +peers certificate, but does not check the subject name. This is +weak authentication that may be useful in scenarios where multiple +devices are deployed and it is sufficient proof of authenticy when +their certificates are signed by the CA the server trusts. This is +better than anon authentication, but still not recommended. <b>Known Problems</b><br> <p>Even in x509/fingerprint mode, both the client and sever certificate currently must be signed by the same root CA. This is an @@ -48,4 +55,4 @@ Copyright © 2008 by <a href="http://www.gerhards.net/rainer">Rainer Gerhards</a> and <a href="http://www.adiscon.com/">Adiscon</a>. Released under the GNU GPL version 3 or higher.</font></p> -</body></html>
\ No newline at end of file +</body></html> diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c index fd7a502a..b5431a2c 100644 --- a/runtime/nsd_gtls.c +++ b/runtime/nsd_gtls.c @@ -74,6 +74,182 @@ static int bGlblSrvrInitDone = 0; /**< 0 - server global init not yet done, 1 - static gnutls_certificate_credentials xcred; static gnutls_dh_params dh_params; +/* This function extracts some information about this session's peer + * certificate. Works for X.509 certificates only. Adds all + * of the info to a cstr_t, which is handed over to the caller. + * Caller must destruct it when no longer needed. + * rgerhards, 2008-05-21 + */ +static rsRetVal +gtlsGetCertInfo(nsd_gtls_t *pThis, cstr_t **ppStr) +{ + char dn[128]; + uchar lnBuf[256]; + size_t size; + unsigned int algo, bits; + time_t expiration_time, activation_time; + const gnutls_datum *cert_list; + unsigned cert_list_size = 0; + gnutls_x509_crt cert; + cstr_t *pStr = NULL; + int gnuRet; + DEFiRet; + + assert(ppStr != NULL); + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + 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); + + CHKiRet(rsCStrConstruct(&pStr)); + + snprintf((char*)lnBuf, sizeof(lnBuf), "Peer provided %d certificate(s). ", cert_list_size); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + 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)); + + CHKiRet(rsCStrAppendStr(pStr, (uchar*)"Certificate 1 info: ")); + + expiration_time = gnutls_x509_crt_get_expiration_time(cert); + activation_time = gnutls_x509_crt_get_activation_time(cert); + + ctime_r(&activation_time, dn); + dn[strlen(dn) - 1] = '\0'; /* strip linefeed */ + snprintf((char*)lnBuf, sizeof(lnBuf), "certificate valid from %s ", dn); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + ctime_r(&expiration_time, dn); + dn[strlen(dn) - 1] = '\0'; /* strip linefeed */ + snprintf((char*)lnBuf, sizeof(lnBuf), "to %s; ", dn); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + /* Extract some of the public key algorithm's parameters */ + algo = gnutls_x509_crt_get_pk_algorithm(cert, &bits); + + snprintf((char*)lnBuf, sizeof(lnBuf), "Certificate public key: %s; ", + gnutls_pk_algorithm_get_name(algo)); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + /* names */ + size = sizeof(dn); + gnutls_x509_crt_get_dn( cert, dn, &size); + snprintf((char*)lnBuf, sizeof(lnBuf), "DN: %s; ", dn); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + size = sizeof(dn); + gnutls_x509_crt_get_issuer_dn( cert, dn, &size); + snprintf((char*)lnBuf, sizeof(lnBuf), "Issuer DN: %s", dn); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + + gnutls_x509_crt_deinit( cert); + } + + CHKiRet(rsCStrFinish(pStr)); + *ppStr = pStr; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pStr != NULL) + rsCStrDestruct(&pStr); + } + + RETiRet; +} + + + +#if 0 /* we may need this in the future - code needs to be looked at then! */ +/* This function will print some details of the + * given pThis->sess. + */ +static rsRetVal +print_info(nsd_gtls_t *pThis) +{ + const char *tmp; + gnutls_credentials_type cred; + gnutls_kx_algorithm kx; + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + /* print the key exchange's algorithm name + */ + kx = gnutls_kx_get(pThis->sess); + 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(pThis->sess); + switch (cred) { + case GNUTLS_CRD_ANON: /* anonymous authentication */ + dbgprintf("- Anonymous DH using prime of %d bits\n", + gnutls_dh_get_prime_bits(pThis->sess)); + 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(pThis->sess)); + } + + /* if the certificate list is available, then + * print some information about it. + */ + gtlsPrintCert(pThis); + 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(pThis->sess)); + 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(pThis->sess)); + + dbgprintf("- Certificate Type: %s\n", tmp); + + /* print the compression algorithm (if any) + */ + tmp = gnutls_compression_get_name( gnutls_compression_get(pThis->sess)); + dbgprintf("- Compression: %s\n", tmp); + + /* print the name of the cipher used. + * ie 3DES. + */ + tmp = gnutls_cipher_get_name(gnutls_cipher_get(pThis->sess)); + dbgprintf("- Cipher: %s\n", tmp); + + /* Print the MAC algorithms name. + * ie SHA1 + */ + tmp = gnutls_mac_get_name(gnutls_mac_get(pThis->sess)); + dbgprintf("- MAC: %s\n", tmp); + + RETiRet; +} +#endif + + /* Convert a fingerprint to printable data. The conversion is carried out * according IETF I-D syslog-transport-tls-12. The fingerprint string is * returned in a new cstr object. It is the caller's responsibility to @@ -253,7 +429,7 @@ finalize_it: /* check the fingerprint of the remote peer's certificate. * rgerhards, 2008-05-08 */ -rsRetVal +static rsRetVal gtlsChkFingerprint(nsd_gtls_t *pThis) { cstr_t *pstrFingerprint = NULL; @@ -334,6 +510,96 @@ dbgprintf("exit fingerprint check, iRet %d\n", iRet); } +/* Verify the validity of the remote peer's certificate. + * rgerhards, 2008-05-21 + */ +static rsRetVal +gtlsChkPeerCertValidity(nsd_gtls_t *pThis) +{ + DEFiRet; + char *pszErrCause; + int gnuRet; + cstr_t *pStr; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + gnuRet = gnutls_certificate_verify_peers(pThis->sess); + if(gnuRet < 1) + CHKgnutls(gnuRet); + + if(gnuRet & GNUTLS_CERT_INVALID) { + /* provide error details if we have them */ + if(gnuRet & GNUTLS_CERT_SIGNER_NOT_FOUND) { + pszErrCause = "signer not found"; + } else if(gnuRet & GNUTLS_CERT_SIGNER_NOT_FOUND) { + pszErrCause = "signer is not a CA"; + } else if(gnuRet & GNUTLS_CERT_SIGNER_NOT_CA) { + pszErrCause = "insecure algorithm"; + } else if(gnuRet & GNUTLS_CERT_REVOKED) { + pszErrCause = "certificate revoked"; + } else { + pszErrCause = "no specific reason"; + } + errmsg.LogError(NO_ERRCODE, "not permitted to talk to peer, certificate invalid: %s", + pszErrCause); + gtlsGetCertInfo(pThis, &pStr); + errmsg.LogError(NO_ERRCODE, "info on invalid cert: %s", rsCStrGetSzStr(pStr)); + rsCStrDestruct(&pStr); + ABORT_FINALIZE(RS_RET_CERT_INVALID); + } + +finalize_it: + RETiRet; +} + + +/* Perform a name check on the remote peer. This includes certificate + * validity checking. + * rgerhards, 2008-05-21 + */ +static rsRetVal +gtlsChkPeerName(nsd_gtls_t *pThis) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + CHKiRet(gtlsChkPeerCertValidity(pThis)); + +finalize_it: + RETiRet; +} + + +/* check if it is OK to talk to the remote peer + * rgerhards, 2008-05-21 + */ +rsRetVal +gtlsChkPeerAuth(nsd_gtls_t *pThis) +{ + DEFiRet; + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + + /* call the actual function based on current auth mode */ + switch(pThis->authMode) { + case GTLS_AUTH_CERTNAME: + CHKiRet(gtlsChkPeerName(pThis)); + break; + case GTLS_AUTH_CERTFINGERPRINT: + CHKiRet(gtlsChkFingerprint(pThis)); + break; + case GTLS_AUTH_CERTVALID: + CHKiRet(gtlsChkPeerCertValidity(pThis)); + break; + case GTLS_AUTH_CERTANON: + FINALIZE; + break; + } + +finalize_it: + RETiRet; +} + + /* globally de-initialize GnuTLS */ static rsRetVal gtlsGlblExit(void) @@ -434,6 +700,7 @@ finalize_it: /* Set the authentication mode. For us, the following is supported: * anon - no certificate checks whatsoever (discouraged, but supported) + * x509/certvalid - (just) check certificate validity * x509/fingerprint - certificate fingerprint * x509/name - cerfificate name check * mode == NULL is valid and defaults to x509/name @@ -450,6 +717,8 @@ SetAuthMode(nsd_t *pNsd, uchar *mode) pThis->authMode = GTLS_AUTH_CERTNAME; } else if(!strcasecmp((char*) mode, "x509/fingerprint")) { pThis->authMode = GTLS_AUTH_CERTFINGERPRINT; + } else if(!strcasecmp((char*) mode, "x509/certvalid")) { + pThis->authMode = GTLS_AUTH_CERTVALID; } else if(!strcasecmp((char*) mode, "anon")) { pThis->authMode = GTLS_AUTH_CERTANON; } else { @@ -756,7 +1025,7 @@ Connect(nsd_t *pNsd, int family, uchar *port, uchar *host) dbgprintf("GnuTLS handshake succeeded\n"); /* now check if the remote peer is permitted to talk to us */ - CHKiRet(gtlsChkFingerprint(pThis)); + CHKiRet(gtlsChkPeerAuth(pThis)); finalize_it: if(iRet != RS_RET_OK) { diff --git a/runtime/nsd_gtls.h b/runtime/nsd_gtls.h index 1f3eb6b1..59109e68 100644 --- a/runtime/nsd_gtls.h +++ b/runtime/nsd_gtls.h @@ -42,7 +42,8 @@ struct nsd_gtls_s { enum { GTLS_AUTH_CERTNAME = 0, GTLS_AUTH_CERTFINGERPRINT = 1, - GTLS_AUTH_CERTANON = 2 + GTLS_AUTH_CERTVALID = 2, + GTLS_AUTH_CERTANON = 3 } authMode; gtlsRtryCall_t rtryCall;/**< what must we retry? */ int bIsInitiator; /**< 0 if socket is the server end (listener), 1 if it is the initiator */ @@ -62,6 +63,7 @@ struct nsd_gtls_s { PROTOTYPEObj(nsd_gtls); /* some prototypes for things used by our nsdsel_gtls helper class */ uchar *gtlsStrerror(int error); +rsRetVal gtlsChkPeerAuth(nsd_gtls_t *pThis); /* the name of our library binary */ #define LM_NSD_GTLS_FILENAME "lmnsd_gtls" diff --git a/runtime/nsdsel_gtls.c b/runtime/nsdsel_gtls.c index 96456564..7b359950 100644 --- a/runtime/nsdsel_gtls.c +++ b/runtime/nsdsel_gtls.c @@ -131,7 +131,7 @@ doRetry(nsd_gtls_t *pNsd) if(gnuRet == 0) { pNsd->rtryCall = gtlsRtry_None; /* we are done */ /* we got a handshake, now check authorization */ - CHKiRet(gtlsChkFingerprint(pNsd)); + CHKiRet(gtlsChkPeerAuth(pNsd)); } break; default: diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index c06b01c3..dfa14f35 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -229,6 +229,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth RS_RET_VALUE_NOT_IN_THIS_MODE = -2087, /**< a provided value is invalid for the curret mode */ RS_RET_INVALID_FINGERPRINT = -2088, /**< a fingerprint is not valid for this use case */ RS_RET_CONNECTION_ABORTREQ = -2089, /**< connection was abort requested due to previous error */ + RS_RET_CERT_INVALID = -2090, /**< a x509 certificate failed validation */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ |