Blob Blame History Raw
From b15e6ab7242b25311a9e0dcf14187d21a80a44a6 Mon Sep 17 00:00:00 2001
From: Jiri Vymazal <jvymazal@redhat.com>
Date: Fri, 16 Aug 2019 15:01:11 +0200
Subject: [PATCH] Stricter GnuTLS operation

This commit adds to new flags which can be set to allow
1) checking of extendedKeyUsage certificate field
2) stricter checking of certificate name/adresses
---
 plugins/imtcp/imtcp.c | 12 +++++++
 runtime/netstrm.c     | 22 +++++++++++++
 runtime/netstrm.h     |  5 ++-
 runtime/netstrms.c    | 47 +++++++++++++++++++++++++++
 runtime/netstrms.h    |  6 ++++
 runtime/nsd.h         |  7 ++--
 runtime/nsd_gtls.c    | 74 ++++++++++++++++++++++++++++++++++++++++---
 runtime/nsd_gtls.h    | 12 +++++++
 runtime/nsd_ossl.c    | 36 +++++++++++++++++++++
 runtime/nsd_ptcp.c    | 35 ++++++++++++++++++++
 runtime/tcpsrv.c      | 24 ++++++++++++++
 runtime/tcpsrv.h      |  7 +++-
 tools/omfwd.c         | 12 +++++++
 13 files changed, 291 insertions(+), 8 deletions(-)

diff --git a/plugins/imtcp/imtcp.c b/plugins/imtcp/imtcp.c
index 55245842d5..6b3401f8fe 100644
--- a/plugins/imtcp/imtcp.c
+++ b/plugins/imtcp/imtcp.c
@@ -131,6 +131,8 @@ struct modConfData_s {
 	int iTCPSessMax; /* max number of sessions */
 	int iTCPLstnMax; /* max number of sessions */
 	int iStrmDrvrMode; /* mode for stream driver, driver-dependent (0 mostly means plain tcp) */
+	int iStrmDrvrExtendedCertCheck; /* verify also purpose OID in certificate extended field */
+	int iStrmDrvrSANPreference; /* ignore CN when any SAN set */
 	int iAddtlFrameDelim; /* addtl frame delimiter, e.g. for netscreen, default none */
 	int maxFrameSize;
 	int bSuppOctetFram;
@@ -170,6 +172,8 @@ static struct cnfparamdescr modpdescr[] = {
 	{ "streamdriver.mode", eCmdHdlrNonNegInt, 0 },
 	{ "streamdriver.authmode", eCmdHdlrString, 0 },
 	{ "streamdriver.name", eCmdHdlrString, 0 },
+	{ "streamdriver.CheckExtendedKeyPurpose", eCmdHdlrBinary, 0 },
+	{ "streamdriver.PrioritizeSAN", eCmdHdlrBinary, 0 },
 	{ "permittedpeer", eCmdHdlrArray, 0 },
 	{ "keepalive", eCmdHdlrBinary, 0 },
 	{ "keepalive.probes", eCmdHdlrPositiveInt, 0 },
@@ -368,6 +372,8 @@ addListner(modConfData_t *modConf, instanceConf_t *inst)
 		CHKiRet(tcpsrv.SetSessMax(pOurTcpsrv, modConf->iTCPSessMax));
 		CHKiRet(tcpsrv.SetLstnMax(pOurTcpsrv, modConf->iTCPLstnMax));
 		CHKiRet(tcpsrv.SetDrvrMode(pOurTcpsrv, modConf->iStrmDrvrMode));
+		CHKiRet(tcpsrv.SetDrvrCheckExtendedKeyUsage(pOurTcpsrv, modConf->iStrmDrvrExtendedCertCheck));
+		CHKiRet(tcpsrv.SetDrvrPrioritizeSAN(pOurTcpsrv, modConf->iStrmDrvrSANPreference));
 		CHKiRet(tcpsrv.SetUseFlowControl(pOurTcpsrv, modConf->bUseFlowControl));
 		CHKiRet(tcpsrv.SetAddtlFrameDelim(pOurTcpsrv, modConf->iAddtlFrameDelim));
 		CHKiRet(tcpsrv.SetMaxFrameSize(pOurTcpsrv, modConf->maxFrameSize));
@@ -479,6 +485,8 @@ CODESTARTbeginCnfLoad
 	loadModConf->iTCPLstnMax = 20;
 	loadModConf->bSuppOctetFram = 1;
 	loadModConf->iStrmDrvrMode = 0;
+	loadModConf->iStrmDrvrExtendedCertCheck = 0;
+	loadModConf->iStrmDrvrSANPreference = 0;
 	loadModConf->bUseFlowControl = 1;
 	loadModConf->bKeepAlive = 0;
 	loadModConf->iKeepAliveIntvl = 0;
@@ -560,6 +568,10 @@ CODESTARTsetModCnf
 			loadModConf->gnutlsPriorityString = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
 		} else if(!strcmp(modpblk.descr[i].name, "streamdriver.mode")) {
 			loadModConf->iStrmDrvrMode = (int) pvals[i].val.d.n;
+		} else if(!strcmp(modpblk.descr[i].name, "streamdriver.CheckExtendedKeyPurpose")) {
+			loadModConf->iStrmDrvrExtendedCertCheck = (int) pvals[i].val.d.n;
+		} else if(!strcmp(modpblk.descr[i].name, "streamdriver.PrioritizeSAN")) {
+			loadModConf->iStrmDrvrSANPreference = (int) pvals[i].val.d.n;
 		} else if(!strcmp(modpblk.descr[i].name, "streamdriver.authmode")) {
 			loadModConf->pszStrmDrvrAuthMode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
 		} else if(!strcmp(modpblk.descr[i].name, "streamdriver.name")) {
diff --git a/runtime/netstrm.c b/runtime/netstrm.c
index e1df83edee..59aa135997 100644
--- a/runtime/netstrm.c
+++ b/runtime/netstrm.c
@@ -221,6 +221,26 @@ SetDrvrPermPeers(netstrm_t *pThis, permittedPeers_t *pPermPeers)
 	RETiRet;
 }
 
+/* Mandate also verification of Extended key usage / purpose field */
+static rsRetVal
+SetDrvrCheckExtendedKeyUsage(netstrm_t *pThis, int ChkExtendedKeyUsage)
+{
+	DEFiRet;
+	ISOBJ_TYPE_assert(pThis, netstrm);
+	iRet = pThis->Drvr.SetCheckExtendedKeyUsage(pThis->pDrvrData, ChkExtendedKeyUsage);
+	RETiRet;
+}
+
+/* Mandate stricter name checking per RFC 6125 - ignoce CN if any SAN present */
+static rsRetVal
+SetDrvrPrioritizeSAN(netstrm_t *pThis, int prioritizeSan)
+{
+	DEFiRet;
+	ISOBJ_TYPE_assert(pThis, netstrm);
+	iRet = pThis->Drvr.SetPrioritizeSAN(pThis->pDrvrData, prioritizeSan);
+	RETiRet;
+}
+
 
 /* End of methods to shuffle autentication settings to the driver.
  * -------------------------------------------------------------------------- */
@@ -405,6 +425,8 @@ CODESTARTobjQueryInterface(netstrm)
 	pIf->SetKeepAliveTime = SetKeepAliveTime;
 	pIf->SetKeepAliveIntvl = SetKeepAliveIntvl;
 	pIf->SetGnutlsPriorityString = SetGnutlsPriorityString;
+	pIf->SetDrvrCheckExtendedKeyUsage = SetDrvrCheckExtendedKeyUsage;
+	pIf->SetDrvrPrioritizeSAN = SetDrvrPrioritizeSAN;
 finalize_it:
 ENDobjQueryInterface(netstrm)
 
diff --git a/runtime/netstrm.h b/runtime/netstrm.h
index 113585d0a5..08b58fd119 100644
--- a/runtime/netstrm.h
+++ b/runtime/netstrm.h
@@ -78,8 +78,11 @@ BEGINinterface(netstrm) /* name must also be changed in ENDinterface macro! */
 	rsRetVal (*SetKeepAliveTime)(netstrm_t *pThis, int keepAliveTime);
 	rsRetVal (*SetKeepAliveIntvl)(netstrm_t *pThis, int keepAliveIntvl);
 	rsRetVal (*SetGnutlsPriorityString)(netstrm_t *pThis, uchar *priorityString);
+	/* v12 -- two new binary flags added to gtls driver enabling stricter operation */
+	rsRetVal (*SetDrvrCheckExtendedKeyUsage)(netstrm_t *pThis, int ChkExtendedKeyUsage);
+	rsRetVal (*SetDrvrPrioritizeSAN)(netstrm_t *pThis, int prioritizeSan);
 ENDinterface(netstrm)
-#define netstrmCURR_IF_VERSION 10 /* increment whenever you change the interface structure! */
+#define netstrmCURR_IF_VERSION 12 /* increment whenever you change the interface structure! */
 /* interface version 3 added GetRemAddr()
  * interface version 4 added EnableKeepAlive() -- rgerhards, 2009-06-02
  * interface version 5 changed return of CheckConnection from void to rsRetVal -- alorbach, 2012-09-06
diff --git a/runtime/netstrms.c b/runtime/netstrms.c
index bd6a06bd7f..a8c342b76b 100644
--- a/runtime/netstrms.c
+++ b/runtime/netstrms.c
@@ -279,6 +279,49 @@ GetDrvrMode(netstrms_t *pThis)
 }
 
 
+/* set the driver cert extended key usage check setting -- jvymazal, 2019-08-16 */
+static rsRetVal
+SetDrvrCheckExtendedKeyUsage(netstrms_t *pThis, int ChkExtendedKeyUsage)
+{
+	DEFiRet;
+	ISOBJ_TYPE_assert(pThis, netstrms);
+	pThis->DrvrChkExtendedKeyUsage = ChkExtendedKeyUsage;
+	RETiRet;
+}
+
+
+/* return the driver cert extended key usage check setting
+ * jvymazal, 2019-08-16
+ */
+static int
+GetDrvrCheckExtendedKeyUsage(netstrms_t *pThis)
+{
+	ISOBJ_TYPE_assert(pThis, netstrms);
+	return pThis->DrvrChkExtendedKeyUsage;
+}
+
+
+/* set the driver name checking policy -- jvymazal, 2019-08-16 */
+static rsRetVal
+SetDrvrPrioritizeSAN(netstrms_t *pThis, int prioritizeSan)
+{
+	DEFiRet;
+	ISOBJ_TYPE_assert(pThis, netstrms);
+	pThis->DrvrPrioritizeSan = prioritizeSan;
+	RETiRet;
+}
+
+
+/* return the driver name checking policy
+ * jvymazal, 2019-08-16
+ */
+static int
+GetDrvrPrioritizeSAN(netstrms_t *pThis)
+{
+	ISOBJ_TYPE_assert(pThis, netstrms);
+	return pThis->DrvrPrioritizeSan;
+}
+
 /* create an instance of a netstrm object. It is initialized with default
  * values. The current driver is used. The caller may set netstrm properties
  * and must call ConstructFinalize().
@@ -337,6 +380,10 @@ CODESTARTobjQueryInterface(netstrms)
 	pIf->GetDrvrGnutlsPriorityString = GetDrvrGnutlsPriorityString;
 	pIf->SetDrvrPermPeers = SetDrvrPermPeers;
 	pIf->GetDrvrPermPeers = GetDrvrPermPeers;
+	pIf->SetDrvrCheckExtendedKeyUsage = SetDrvrCheckExtendedKeyUsage;
+	pIf->GetDrvrCheckExtendedKeyUsage = GetDrvrCheckExtendedKeyUsage;
+	pIf->SetDrvrPrioritizeSAN = SetDrvrPrioritizeSAN;
+	pIf->GetDrvrPrioritizeSAN = GetDrvrPrioritizeSAN;
 finalize_it:
 ENDobjQueryInterface(netstrms)
 
diff --git a/runtime/netstrms.h b/runtime/netstrms.h
index 440beb20c9..f21bd6a8e2 100644
--- a/runtime/netstrms.h
+++ b/runtime/netstrms.h
@@ -33,6 +33,8 @@ struct netstrms_s {
 	uchar *pDrvrName;	/**< full base driver name (set when driver is loaded) */
 	int iDrvrMode;		/**< current default driver mode */
 	uchar *pszDrvrAuthMode;	/**< current driver authentication mode */
+	int DrvrChkExtendedKeyUsage;		/**< if true, verify extended key usage in certs */
+	int DrvrPrioritizeSan;		/**< if true, perform stricter checking of names in certs */
 	uchar *gnutlsPriorityString; /**< priorityString for connection */
 	permittedPeers_t *pPermPeers;/**< current driver's permitted peers */
 
@@ -58,6 +60,10 @@ BEGINinterface(netstrms) /* name must also be changed in ENDinterface macro! */
 	permittedPeers_t* (*GetDrvrPermPeers)(netstrms_t *pThis);
 	rsRetVal (*SetDrvrGnutlsPriorityString)(netstrms_t *pThis, uchar*);
 	uchar*   (*GetDrvrGnutlsPriorityString)(netstrms_t *pThis);
+	rsRetVal (*SetDrvrCheckExtendedKeyUsage)(netstrms_t *pThis, int ChkExtendedKeyUsage);
+	int      (*GetDrvrCheckExtendedKeyUsage)(netstrms_t *pThis);
+	rsRetVal (*SetDrvrPrioritizeSAN)(netstrms_t *pThis, int prioritizeSan);
+	int      (*GetDrvrPrioritizeSAN)(netstrms_t *pThis);
 ENDinterface(netstrms)
 #define netstrmsCURR_IF_VERSION 1 /* increment whenever you change the interface structure! */
 
diff --git a/runtime/nsd.h b/runtime/nsd.h
index eab53ad6ae..f0cb5bd1aa 100644
--- a/runtime/nsd.h
+++ b/runtime/nsd.h
@@ -85,8 +85,11 @@ BEGINinterface(nsd) /* name must also be changed in ENDinterface macro! */
 	rsRetVal (*SetKeepAliveProbes)(nsd_t *pThis, int keepAliveProbes);
 	rsRetVal (*SetKeepAliveTime)(nsd_t *pThis, int keepAliveTime);
 	rsRetVal (*SetGnutlsPriorityString)(nsd_t *pThis, uchar *gnutlsPriorityString);
+	/* v13 -- two new binary flags added to gtls driver enabling stricter operation */
+	rsRetVal (*SetCheckExtendedKeyUsage)(nsd_t *pThis, int ChkExtendedKeyUsage);
+	rsRetVal (*SetPrioritizeSAN)(nsd_t *pThis, int prioritizeSan);
 ENDinterface(nsd)
-#define nsdCURR_IF_VERSION 11 /* increment whenever you change the interface structure! */
+#define nsdCURR_IF_VERSION 13 /* increment whenever you change the interface structure! */
 /* interface version 4 added GetRemAddr()
  * interface version 5 added EnableKeepAlive() -- rgerhards, 2009-06-02
  * interface version 6 changed return of CheckConnection from void to rsRetVal -- alorbach, 2012-09-06
diff --git a/runtime/nsd_gtls.c b/runtime/nsd_gtls.c
index 56238b9cb4..a3662bedf4 100644
--- a/runtime/nsd_gtls.c
+++ b/runtime/nsd_gtls.c
@@ -1004,6 +1004,7 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt_t *pCert)
 	int iAltName;
 	size_t szAltNameLen;
 	int bFoundPositiveMatch;
+	int bHaveSAN = 0;
 	cstr_t *pStr = NULL;
 	cstr_t *pstrCN = NULL;
 	int gnuRet;
@@ -1023,6 +1024,7 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt_t *pCert)
 		if(gnuRet < 0)
 			break;
 		else if(gnuRet == GNUTLS_SAN_DNSNAME) {
+			bHaveSAN = 1;
 			dbgprintf("subject alt dnsName: '%s'\n", szAltName);
 			snprintf((char*)lnBuf, sizeof(lnBuf), "DNSname: %s; ", szAltName);
 			CHKiRet(rsCStrAppendStr(pStr, lnBuf));
@@ -1032,8 +1034,8 @@ gtlsChkPeerName(nsd_gtls_t *pThis, gnutls_x509_crt_t *pCert)
 		++iAltName;
 	}
 
-	if(!bFoundPositiveMatch) {
-		/* if we did not succeed so far, we try the CN part of the DN... */
+	/* Check also CN only if not configured per stricter RFC 6125 or no SAN present*/
+	if(!bFoundPositiveMatch && (!pThis->bSANpriority || !bHaveSAN)) {
 		CHKiRet(gtlsGetCN(pCert, &pstrCN));
 		if(pstrCN != NULL) { /* NULL if there was no CN present */
 			dbgprintf("gtls now checking auth for CN '%s'\n", cstrGetSzStrNoNULL(pstrCN));
@@ -1044,7 +1044,19 @@  gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
 		ABORT_FINALIZE(RS_RET_TLS_NO_CERT);
 	}
 
-	CHKgnutls(gnutls_certificate_verify_peers2(pThis->sess, &stateCert));
+	if (pThis->dataTypeCheck == GTLS_NONE) {
+		CHKgnutls(gnutls_certificate_verify_peers2(pThis->sess, &stateCert));
+	} else { /* we have configured data to check in addition to cert */
+		gnutls_typed_vdata_st data;
+		data.type = GNUTLS_DT_KEY_PURPOSE_OID;
+		if (pThis->bIsInitiator) { /* client mode */
+			data.data = (uchar *)GNUTLS_KP_TLS_WWW_SERVER;
+		} else { /* server mode */
+			data.data = (uchar *)GNUTLS_KP_TLS_WWW_CLIENT;
+		}
+		data.size = ustrlen(data.data);
+		CHKgnutls(gnutls_certificate_verify_peers(pThis->sess, &data, 1, &stateCert));
+	}
 
 	if(stateCert & GNUTLS_CERT_INVALID) {
 		/* provide error details if we have them */
@@ -1188,6 +1205,8 @@ gtlsChkPeerCertValidity(nsd_gtls_t *pThis)
 			pszErrCause = "insecure algorithm";
 		} else if(stateCert & GNUTLS_CERT_REVOKED) {
 			pszErrCause = "certificate revoked";
+		} else if(stateCert & GNUTLS_CERT_PURPOSE_MISMATCH) {
+			pszErrCause = "key purpose OID does not match";
 		} else {
 			pszErrCause = "GnuTLS returned no specific reason";
 			dbgprintf("GnuTLS returned no specific reason for GNUTLS_CERT_INVALID, certificate "
@@ -1499,6 +1516,53 @@ SetGnutlsPriorityString(nsd_t *pNsd, uchar *gnutlsPriorityString)
 	RETiRet;
 }
 
+/* Set the driver cert extended key usage check setting
+ * 0 - ignore contents of extended key usage
+ * 1 - verify that cert contents is compatible with appropriate OID
+ * jvymazal, 2019-08-16
+ */
+static rsRetVal
+SetCheckExtendedKeyUsage(nsd_t *pNsd, int ChkExtendedKeyUsage)
+{
+	DEFiRet;
+	nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+	ISOBJ_TYPE_assert((pThis), nsd_gtls);
+	if(ChkExtendedKeyUsage != 0 && ChkExtendedKeyUsage != 1) {
+		LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: driver ChkExtendedKeyUsage %d "
+				"not supported by gtls netstream driver", ChkExtendedKeyUsage);
+		ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+	}
+
+	pThis->dataTypeCheck = ChkExtendedKeyUsage;
+
+finalize_it:
+	RETiRet;
+}
+
+/* Set the driver name checking strictness
+ * 0 - less strict per RFC 5280, section 4.1.2.6 - either SAN or CN match is good
+ * 1 - more strict per RFC 6125 - if any SAN present it must match (CN is ignored)
+ * jvymazal, 2019-08-16
+ */
+static rsRetVal
+SetPrioritizeSAN(nsd_t *pNsd, int prioritizeSan)
+{
+	DEFiRet;
+	nsd_gtls_t *pThis = (nsd_gtls_t*) pNsd;
+
+	ISOBJ_TYPE_assert((pThis), nsd_gtls);
+	if(prioritizeSan != 0 && prioritizeSan != 1) {
+		LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: driver prioritizeSan %d "
+				"not supported by gtls netstream driver", prioritizeSan);
+		ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+	}
+
+	pThis->bSANpriority = prioritizeSan;
+
+finalize_it:
+	RETiRet;
+}
 
 /* Provide access to the underlying OS socket. This is primarily
  * useful for other drivers (like nsd_gtls) who utilize ourselfs
@@ -2124,6 +2188,8 @@ CODESTARTobjQueryInterface(nsd_gtls)
 	pIf->SetKeepAliveProbes = SetKeepAliveProbes;
 	pIf->SetKeepAliveTime = SetKeepAliveTime;
 	pIf->SetGnutlsPriorityString = SetGnutlsPriorityString;
+	pIf->SetCheckExtendedKeyUsage = SetCheckExtendedKeyUsage;
+	pIf->SetPrioritizeSAN = SetPrioritizeSAN;
 finalize_it:
 ENDobjQueryInterface(nsd_gtls)
 
diff --git a/runtime/nsd_gtls.h b/runtime/nsd_gtls.h
index 80a86f21d7..d73c0e6a4d 100644
--- a/runtime/nsd_gtls.h
+++ b/runtime/nsd_gtls.h
@@ -54,6 +54,11 @@ struct nsd_gtls_s {
 		GTLS_AUTH_CERTVALID = 2,
 		GTLS_AUTH_CERTANON = 3
 	} authMode;
+	enum {
+		GTLS_NONE = 0,
+		GTLS_PURPOSE = 1
+	} dataTypeCheck;
+	int bSANpriority; /* if true, we do stricter checking (if any SAN present we do not cehck CN) */
 	gtlsRtryCall_t rtryCall;/**< what must we retry? */
 	int bIsInitiator;	/**< 0 if socket is the server end (listener), 1 if it is the initiator */
 	gnutls_session_t sess;
diff --git a/runtime/nsd_ossl.c b/runtime/nsd_ossl.c
index 0a474fd744..419e0e97ef 100644
--- a/runtime/nsd_ossl.c
+++ b/runtime/nsd_ossl.c
@@ -1864,6 +1864,40 @@ SetGnutlsPriorityString(__attribute__((unused)) nsd_t *pNsd, __attribute__((unus
 	RETiRet;
 }
 
+/* Set the driver cert extended key usage check setting, for now it is empty wrapper.
+ * TODO: implement openSSL version
+ * jvymazal, 2019-08-16
+ */
+static rsRetVal
+SetCheckExtendedKeyUsage(nsd_t __attribute__((unused)) *pNsd, int ChkExtendedKeyUsage)
+{
+	DEFiRet;
+	if(ChkExtendedKeyUsage != 0) {
+		LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: driver ChkExtendedKeyUsage %d "
+				"not supported by ossl netstream driver", ChkExtendedKeyUsage);
+		ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+	}
+finalize_it:
+	RETiRet;
+}
+
+/* Set the driver name checking strictness, for now it is empty wrapper.
+ * TODO: implement openSSL version
+ * jvymazal, 2019-08-16
+ */
+static rsRetVal
+SetPrioritizeSAN(nsd_t __attribute__((unused)) *pNsd, int prioritizeSan)
+{
+	DEFiRet;
+	if(prioritizeSan != 0) {
+		LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: driver prioritizeSan %d "
+				"not supported by ossl netstream driver", prioritizeSan);
+		ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+	}
+finalize_it:
+	RETiRet;
+}
+
 /* queryInterface function */
 BEGINobjQueryInterface(nsd_ossl)
 CODESTARTobjQueryInterface(nsd_ossl)
@@ -1898,6 +1932,8 @@ CODESTARTobjQueryInterface(nsd_ossl)
 	pIf->SetKeepAliveProbes = SetKeepAliveProbes;
 	pIf->SetKeepAliveTime = SetKeepAliveTime;
 	pIf->SetGnutlsPriorityString = SetGnutlsPriorityString; /* we don't NEED this interface! */
+	pIf->SetCheckExtendedKeyUsage = SetCheckExtendedKeyUsage; /* we don't NEED this interface! */
+	pIf->SetPrioritizeSAN = SetPrioritizeSAN; /* we don't NEED this interface! */
 
 finalize_it:
 ENDobjQueryInterface(nsd_ossl)
diff --git a/runtime/nsd_ptcp.c b/runtime/nsd_ptcp.c
index 68bed5b9ea..60c40ad444 100644
--- a/runtime/nsd_ptcp.c
+++ b/runtime/nsd_ptcp.c
@@ -150,6 +150,37 @@ SetMode(nsd_t __attribute__((unused)) *pNsd, int mode)
 	RETiRet;
 }
 
+/* Set the driver cert extended key usage check setting, not supported in ptcp.
+ * jvymazal, 2019-08-16
+ */
+static rsRetVal
+SetCheckExtendedKeyUsage(nsd_t __attribute__((unused)) *pNsd, int ChkExtendedKeyUsage)
+{
+	DEFiRet;
+	if(ChkExtendedKeyUsage != 0) {
+		LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: driver ChkExtendedKeyUsage %d "
+				"not supported by ptcp netstream driver", ChkExtendedKeyUsage);
+		ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+	}
+finalize_it:
+	RETiRet;
+}
+
+/* Set the driver name checking strictness, not supported in ptcp.
+ * jvymazal, 2019-08-16
+ */
+static rsRetVal
+SetPrioritizeSAN(nsd_t __attribute__((unused)) *pNsd, int prioritizeSan)
+{
+	DEFiRet;
+	if(prioritizeSan != 0) {
+		LogError(0, RS_RET_VALUE_NOT_SUPPORTED, "error: driver prioritizeSan %d "
+				"not supported by ptcp netstream driver", prioritizeSan);
+		ABORT_FINALIZE(RS_RET_VALUE_NOT_SUPPORTED);
+	}
+finalize_it:
+	RETiRet;
+}
 
 /* Set the authentication mode. For us, the following is supported:
  * anon - no certificate checks whatsoever (discouraged, but supported)
@@ -613,6 +644,8 @@ LstnInit(netstrms_t *pNS, void *pUsr, rsRetVal(*fAddLstn)(void*,netstrm_t*),
 		CHKiRet(pNS->Drvr.Construct(&pNewNsd));
 		CHKiRet(pNS->Drvr.SetSock(pNewNsd, sock));
 		CHKiRet(pNS->Drvr.SetMode(pNewNsd, netstrms.GetDrvrMode(pNS)));
+		CHKiRet(pNS->Drvr.SetCheckExtendedKeyUsage(pNewNsd, netstrms.GetDrvrCheckExtendedKeyUsage(pNS)));
+		CHKiRet(pNS->Drvr.SetPrioritizeSAN(pNewNsd, netstrms.GetDrvrPrioritizeSAN(pNS)));
 		CHKiRet(pNS->Drvr.SetAuthMode(pNewNsd, netstrms.GetDrvrAuthMode(pNS)));
 		CHKiRet(pNS->Drvr.SetPermPeers(pNewNsd, netstrms.GetDrvrPermPeers(pNS)));
 		CHKiRet(pNS->Drvr.SetGnutlsPriorityString(pNewNsd, netstrms.GetDrvrGnutlsPriorityString(pNS)));
@@ -963,6 +996,8 @@ CODESTARTobjQueryInterface(nsd_ptcp)
 	pIf->SetKeepAliveIntvl = SetKeepAliveIntvl;
 	pIf->SetKeepAliveProbes = SetKeepAliveProbes;
 	pIf->SetKeepAliveTime = SetKeepAliveTime;
+	pIf->SetCheckExtendedKeyUsage = SetCheckExtendedKeyUsage;
+	pIf->SetPrioritizeSAN = SetPrioritizeSAN;
 finalize_it:
 ENDobjQueryInterface(nsd_ptcp)
 
diff --git a/runtime/tcpsrv.c b/runtime/tcpsrv.c
index d7ea2f9f00..43a6687b4a 100644
--- a/runtime/tcpsrv.c
+++ b/runtime/tcpsrv.c
@@ -1013,6 +1013,8 @@ tcpsrvConstructFinalize(tcpsrv_t *pThis)
 	if(pThis->pszDrvrName != NULL)
 		CHKiRet(netstrms.SetDrvrName(pThis->pNS, pThis->pszDrvrName));
 	CHKiRet(netstrms.SetDrvrMode(pThis->pNS, pThis->iDrvrMode));
+	CHKiRet(netstrms.SetDrvrCheckExtendedKeyUsage(pThis->pNS, pThis->DrvrChkExtendedKeyUsage));
+	CHKiRet(netstrms.SetDrvrPrioritizeSAN(pThis->pNS, pThis->DrvrPrioritizeSan));
 	if(pThis->pszDrvrAuthMode != NULL)
 		CHKiRet(netstrms.SetDrvrAuthMode(pThis->pNS, pThis->pszDrvrAuthMode));
 	if(pThis->pPermPeers != NULL)
@@ -1405,6 +1407,26 @@ SetDrvrPermPeers(tcpsrv_t *pThis, permittedPeers_t *pPermPeers)
 	RETiRet;
 }
 
+/* set the driver cert extended key usage check setting -- jvymazal, 2019-08-16 */
+static rsRetVal
+SetDrvrCheckExtendedKeyUsage(tcpsrv_t *pThis, int ChkExtendedKeyUsage)
+{
+	DEFiRet;
+	ISOBJ_TYPE_assert(pThis, tcpsrv);
+	pThis->DrvrChkExtendedKeyUsage = ChkExtendedKeyUsage;
+	RETiRet;
+}
+
+/* set the driver name checking policy -- jvymazal, 2019-08-16 */
+static rsRetVal
+SetDrvrPrioritizeSAN(tcpsrv_t *pThis, int prioritizeSan)
+{
+	DEFiRet;
+	ISOBJ_TYPE_assert(pThis, tcpsrv);
+	pThis->DrvrPrioritizeSan = prioritizeSan;
+	RETiRet;
+}
+
 
 /* End of methods to shuffle autentication settings to the driver.;
 
@@ -1522,6 +1544,8 @@ CODESTARTobjQueryInterface(tcpsrv)
 	pIf->SetLinuxLikeRatelimiters = SetLinuxLikeRatelimiters;
 	pIf->SetNotificationOnRemoteClose = SetNotificationOnRemoteClose;
 	pIf->SetPreserveCase = SetPreserveCase;
+	pIf->SetDrvrCheckExtendedKeyUsage = SetDrvrCheckExtendedKeyUsage;
+	pIf->SetDrvrPrioritizeSAN = SetDrvrPrioritizeSAN;
 
 finalize_it:
 ENDobjQueryInterface(tcpsrv)
diff --git a/runtime/tcpsrv.h b/runtime/tcpsrv.h
index 55aa8e9922..79b659013d 100644
--- a/runtime/tcpsrv.h
+++ b/runtime/tcpsrv.h
@@ -61,6 +61,8 @@ struct tcpsrv_s {
 	int iKeepAliveTime;	/**< socket layer KEEPALIVE timeout */
 	netstrms_t *pNS;	/**< pointer to network stream subsystem */
 	int iDrvrMode;		/**< mode of the stream driver to use */
+	int DrvrChkExtendedKeyUsage;		/**< if true, verify extended key usage in certs */
+	int DrvrPrioritizeSan;		/**< if true, perform stricter checking of names in certs */
 	uchar *gnutlsPriorityString;	/**< priority string for gnutls */
 	uchar *pszDrvrAuthMode;	/**< auth mode of the stream driver to use */
 	uchar *pszDrvrName;	/**< name of stream driver to use */
@@ -185,8 +187,11 @@ BEGINinterface(tcpsrv) /* name must also be changed in ENDinterface macro! */
 	rsRetVal (*SetGnutlsPriorityString)(tcpsrv_t*, uchar*);
 	/* added v21 -- Preserve case in fromhost, 2018-08-16 */
 	rsRetVal (*SetPreserveCase)(tcpsrv_t *pThis, int bPreserveCase);
+	/* added v23 -- Options for stricter driver behavior, 2019-08-16 */
+	rsRetVal (*SetDrvrCheckExtendedKeyUsage)(tcpsrv_t *pThis, int ChkExtendedKeyUsage);
+	rsRetVal (*SetDrvrPrioritizeSAN)(tcpsrv_t *pThis, int prioritizeSan);
 ENDinterface(tcpsrv)
-#define tcpsrvCURR_IF_VERSION 21 /* increment whenever you change the interface structure! */
+#define tcpsrvCURR_IF_VERSION 23 /* increment whenever you change the interface structure! */
 /* change for v4:
  * - SetAddtlFrameDelim() added -- rgerhards, 2008-12-10
  * - SetInputName() added -- rgerhards, 2008-12-10
diff --git a/tools/omfwd.c b/tools/omfwd.c
index cdb74b8d22..96fa68752c 100644
--- a/tools/omfwd.c
+++ b/tools/omfwd.c
@@ -83,6 +83,8 @@ typedef struct _instanceData {
 	uchar *pszStrmDrvrAuthMode;
 	permittedPeers_t *pPermPeers;
 	int iStrmDrvrMode;
+	int iStrmDrvrExtendedCertCheck; /* verify also purpose OID in certificate extended field */
+	int iStrmDrvrSANPreference; /* ignore CN when any SAN set */
 	char	*target;
 	char	*address;
 	char	*device;
@@ -189,6 +191,8 @@ static struct cnfparamdescr actpdescr[] = {
 	{ "streamdrivermode", eCmdHdlrInt, 0 },
 	{ "streamdriverauthmode", eCmdHdlrGetWord, 0 },
 	{ "streamdriverpermittedpeers", eCmdHdlrGetWord, 0 },
+	{ "streamdriver.CheckExtendedKeyPurpose", eCmdHdlrBinary, 0 },
+	{ "streamdriver.PrioritizeSAN", eCmdHdlrBinary, 0 },
 	{ "resendlastmsgonreconnect", eCmdHdlrBinary, 0 },
 	{ "udp.sendtoall", eCmdHdlrBinary, 0 },
 	{ "udp.senddelay", eCmdHdlrInt, 0 },
@@ -748,6 +752,8 @@ static rsRetVal TCPSendInit(void *pvData)
 		CHKiRet(netstrms.CreateStrm(pWrkrData->pNS, &pWrkrData->pNetstrm));
 		CHKiRet(netstrm.ConstructFinalize(pWrkrData->pNetstrm));
 		CHKiRet(netstrm.SetDrvrMode(pWrkrData->pNetstrm, pData->iStrmDrvrMode));
+		CHKiRet(netstrm.SetDrvrCheckExtendedKeyUsage(pWrkrData->pNetstrm, pData->iStrmDrvrExtendedCertCheck));
+		CHKiRet(netstrm.SetDrvrPrioritizeSAN(pWrkrData->pNetstrm, pData->iStrmDrvrSANPreference));
 		/* now set optional params, but only if they were actually configured */
 		if(pData->pszStrmDrvrAuthMode != NULL) {
 			CHKiRet(netstrm.SetDrvrAuthMode(pWrkrData->pNetstrm, pData->pszStrmDrvrAuthMode));
@@ -1119,6 +1125,8 @@ setInstParamDefaults(instanceData *pData)
 	pData->pszStrmDrvr = NULL;
 	pData->pszStrmDrvrAuthMode = NULL;
 	pData->iStrmDrvrMode = 0;
+	pData->iStrmDrvrExtendedCertCheck = 0;
+	pData->iStrmDrvrSANPreference = 0;
 	pData->iRebindInterval = 0;
 	pData->bKeepAlive = 0;
 	pData->iKeepAliveProbes = 0;
@@ -1220,6 +1228,10 @@ CODESTARTnewActInst
 			pData->pszStrmDrvr = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
 		} else if(!strcmp(actpblk.descr[i].name, "streamdrivermode")) {
 			pData->iStrmDrvrMode = pvals[i].val.d.n;
+		} else if(!strcmp(actpblk.descr[i].name, "streamdriver.CheckExtendedKeyPurpose")) {
+			pData->iStrmDrvrExtendedCertCheck = pvals[i].val.d.n;
+		} else if(!strcmp(actpblk.descr[i].name, "streamdriver.PrioritizeSAN")) {
+			pData->iStrmDrvrSANPreference = pvals[i].val.d.n;
 		} else if(!strcmp(actpblk.descr[i].name, "streamdriverauthmode")) {
 			pData->pszStrmDrvrAuthMode = (uchar*)es_str2cstr(pvals[i].val.d.estr, NULL);
 		} else if(!strcmp(actpblk.descr[i].name, "streamdriverpermittedpeers")) {