summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRainer Gerhards <rgerhards@adiscon.com>2008-05-21 14:59:24 +0200
committerRainer Gerhards <rgerhards@adiscon.com>2008-05-21 14:59:24 +0200
commit68a2c3d512615f217d8c6454a679849083c80f00 (patch)
tree599578a58a32ccf7dd986ede80e7cfd715eb87f8
parent8cb6ec4cee79d41c30d7df38b58ab1f198ac8581 (diff)
downloadrsyslog-68a2c3d512615f217d8c6454a679849083c80f00.tar.gz
rsyslog-68a2c3d512615f217d8c6454a679849083c80f00.tar.xz
rsyslog-68a2c3d512615f217d8c6454a679849083c80f00.zip
implemented x509/certvalid "authentication"
-rw-r--r--doc/ns_gtls.html13
-rw-r--r--runtime/nsd_gtls.c273
-rw-r--r--runtime/nsd_gtls.h4
-rw-r--r--runtime/nsdsel_gtls.c2
-rw-r--r--runtime/rsyslog.h1
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) */