mirror of
https://github.com/rsyslog/rsyslog.git
synced 2025-12-17 07:00:42 +01:00
implemented x509/certvalid "authentication"
This commit is contained in:
parent
8cb6ec4cee
commit
68a2c3d512
@ -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
|
||||
Gerhards</a> and
|
||||
<a href="http://www.adiscon.com/">Adiscon</a>.
|
||||
Released under the GNU GPL version 3 or higher.</font></p>
|
||||
</body></html>
|
||||
</body></html>
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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:
|
||||
|
||||
@ -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) */
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user