implemented x509/certvalid "authentication"

This commit is contained in:
Rainer Gerhards 2008-05-21 14:59:24 +02:00
parent 8cb6ec4cee
commit 68a2c3d512
5 changed files with 286 additions and 7 deletions

View File

@ -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>

View File

@ -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) {

View File

@ -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"

View File

@ -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:

View File

@ -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) */