From b674dd69bd35ee4da511ae73e70946ce4040b439 Mon Sep 17 00:00:00 2001 From: Rainer Gerhards Date: Mon, 26 May 2008 12:53:49 +0200 Subject: added gtls name authentication based on common name (inside DN) also changed fingerprint gtls auth mode to new format fingerprint --- ChangeLog | 1 + runtime/nsd_gtls.c | 106 +++++++++++++++++++++++++++++++++++++++++++++++++---- runtime/rsyslog.h | 1 + 3 files changed, 101 insertions(+), 7 deletions(-) diff --git a/ChangeLog b/ChangeLog index 032d7b29..4a65e0c5 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,6 @@ - implemented x509/certvalid gtls auth mode - implemented x509/name gtls auth mode +- changed fingerprint gtls auth mode to new format fingerprint - changed config directive name to reflect different use $ActionSendStreamDriverCertFingerprint is now $ActionSendStreamDriverPermittedPeer and can be used both for diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c index fab400fc..f3c0d983 100644 --- a/runtime/nsd_gtls.c +++ b/runtime/nsd_gtls.c @@ -282,17 +282,13 @@ GenFingerprintStr(uchar *pFingerprint, size_t sizeFingerprint, cstr_t **ppStr) cstr_t *pStr = NULL; uchar buf[4]; size_t i; - int bAddColon = 0; /* do we need to add a colon to the fingerprint string? */ DEFiRet; CHKiRet(rsCStrConstruct(&pStr)); + CHKiRet(rsCStrAppendStrWithLen(pStr, (uchar*)"SHA1", 4)); for(i = 0 ; i < sizeFingerprint ; ++i) { - if(bAddColon) { - CHKiRet(rsCStrAppendChar(pStr, ':')); - } else { - bAddColon = 1; /* all but the first need a colon added */ - } - snprintf((char*)buf, sizeof(buf), "%2.2X", pFingerprint[i]); + CHKiRet(rsCStrAppendChar(pStr, ':')); + snprintf((char*)buf, sizeof(buf), ":%2.2X", pFingerprint[i]); CHKiRet(rsCStrAppendStrWithLen(pStr, buf, 2)); } CHKiRet(rsCStrFinish(pStr)); @@ -453,6 +449,88 @@ finalize_it: } +/* Obtain the CN from the DN field and hand it back to the caller + * (which is responsible for destructing it). We try to follow + * RFC2253 as far as it makes sense for our use-case. This function + * is considered a compromise providing good-enough correctness while + * limiting code size and complexity. If a problem occurs, we may enhance + * this function. A (pointer to a) certificate must be caller-provided. + * If no CN is contained in the cert, no string is returned + * (*ppstrCN remains NULL). *ppstrCN MUST be NULL on entry! + * rgerhards, 2008-05-22 + */ +static rsRetVal +gtlsGetCN(nsd_gtls_t *pThis, gnutls_x509_crt *pCert, cstr_t **ppstrCN) +{ + DEFiRet; + int gnuRet; + int i; + int bFound; + cstr_t *pstrCN = NULL; + size_t size; + /* big var the last, so we hope to have all we usually neeed within one mem cache line */ + uchar szDN[1024]; /* this should really be large enough for any non-malicious case... */ + + ISOBJ_TYPE_assert(pThis, nsd_gtls); + assert(pCert != NULL); + assert(ppstrCN != NULL); + assert(*ppstrCN == NULL); + + size = sizeof(szDN); + CHKgnutls(gnutls_x509_crt_get_dn(*pCert, (char*)szDN, &size)); + + /* now search for the CN part */ + i = 0; + bFound = 0; + while(!bFound && szDN[i] != '\0') { + /* note that we do not overrun our string due to boolean shortcut + * operations. If we have '\0', the if does not match and evaluation + * stops. Order of checks is obviously important! + */ + if(szDN[i] == 'C' && szDN[i+1] == 'N' && szDN[i+2] == '=') { + bFound = 1; + i += 2; + } + i++; + + } + + if(!bFound) { + FINALIZE; /* we are done */ + } + + /* we found a common name, now extract it */ + CHKiRet(rsCStrConstruct(&pstrCN)); + while(szDN[i] != '\0' && szDN[i] != ',') { + if(szDN[i] == '\\') { + /* hex escapes are not implemented */ + ++i; /* escape char processed */ + if(szDN[i] == '\0') + ABORT_FINALIZE(RS_RET_CERT_INVALID_DN); + CHKiRet(rsCStrAppendChar(pstrCN, szDN[i])); + } else { + CHKiRet(rsCStrAppendChar(pstrCN, szDN[i])); + } + ++i; /* char processed */ + } + CHKiRet(rsCStrFinish(pstrCN)); + + /* we got it - we ignore the rest of the DN string (if any). So we may + * not detect if it contains more than one CN + */ + + *ppstrCN = pstrCN; + +finalize_it: + if(iRet != RS_RET_OK) { + if(pstrCN != NULL) + rsCStrDestruct(&pstrCN); + } + + RETiRet; +} + + /* Check the peer's ID in fingerprint auth mode. * rgerhards, 2008-05-22 */ @@ -555,6 +633,7 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert) size_t szAltNameLen; int bFoundPositiveMatch; cstr_t *pStr = NULL; + cstr_t *pstrCN = NULL; int gnuRet; DEFiRet; @@ -581,6 +660,17 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert) ++iAltName; } + if(!bFoundPositiveMatch) { + /* if we did not succeed so far, we try the CN part of the DN... */ + CHKiRet(gtlsGetCN(pThis, pCert, &pstrCN)); + if(pstrCN != NULL) { /* NULL if there was no CN present */ + dbgprintf("gtls noch checking auth for CN '%s'\n", rsCStrGetSzStr(pstrCN)); + snprintf((char*)lnBuf, sizeof(lnBuf), "CN: %s; ", rsCStrGetSzStr(pstrCN)); + CHKiRet(rsCStrAppendStr(pStr, lnBuf)); + CHKiRet(gtlsChkOnePeerName(pThis, rsCStrGetSzStr(pstrCN), &bFoundPositiveMatch)); + } + } + if(!bFoundPositiveMatch) { dbgprintf("invalid peer name, not permitted to talk to it\n"); if(pThis->bReportAuthErr == 1) { @@ -597,6 +687,8 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt *pCert) finalize_it: if(pStr != NULL) rsCStrDestruct(&pStr); + if(pstrCN != NULL) + rsCStrDestruct(&pstrCN); RETiRet; } diff --git a/runtime/rsyslog.h b/runtime/rsyslog.h index dfa14f35..4f858928 100644 --- a/runtime/rsyslog.h +++ b/runtime/rsyslog.h @@ -230,6 +230,7 @@ enum rsRetVal_ /** return value. All methods return this if not specified oth 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 */ + RS_RET_CERT_INVALID_DN = -2091, /**< distinguised name in x509 certificate is invalid (e.g. wrong escaping) */ /* RainerScript error messages (range 1000.. 1999) */ RS_RET_SYSVAR_NOT_FOUND = 1001, /**< system variable could not be found (maybe misspelled) */ -- cgit