Skip to content

Commit 29be795

Browse files
committed
Add SSLVerifyClientEKU directive to control Extended Key Usage checks for client certificates.
1 parent 6310dd7 commit 29be795

5 files changed

Lines changed: 109 additions & 0 deletions

File tree

docs/manual/mod/mod_ssl.xml

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1460,6 +1460,48 @@ SSLVerifyClient require
14601460
</usage>
14611461
</directivesynopsis>
14621462

1463+
<directivesynopsis>
1464+
<name>SSLVerifyClientEKU</name>
1465+
<description>Whether to enforce Extended Key Usage checks for Client Certificates</description>
1466+
<syntax>SSLVerifyClientEKU on|off</syntax>
1467+
<default>SSLVerifyClientEKU on</default>
1468+
<contextlist><context>server config</context>
1469+
<context>virtual host</context>
1470+
<context>directory</context>
1471+
<context>.htaccess</context></contextlist>
1472+
<override>AuthConfig</override>
1473+
1474+
<usage>
1475+
<p>
1476+
This directive controls whether mod_ssl enforces X.509 Extended Key Usage
1477+
(EKU) <code>invalid purpose</code> checks during client certificate
1478+
verification. The default value <code>on</code> preserves the standard
1479+
behavior and rejects client certificates whose EKU does not allow client
1480+
authentication.
1481+
</p>
1482+
<p>
1483+
Setting this directive explicitly to <code>on</code> is identical to omitting
1484+
the directive.
1485+
</p>
1486+
<p>
1487+
When set to <code>off</code>, mod_ssl will ignore only the
1488+
<code>invalid purpose</code> verification error for client certificates while
1489+
leaving other verification checks (e.g. chain validation, signature, validity
1490+
period, revocation checks) unchanged.
1491+
</p>
1492+
<p>
1493+
This setting only affects client certificate verification performed by
1494+
<directive module="mod_ssl">SSLVerifyClient</directive>.
1495+
</p>
1496+
<example><title>Example</title>
1497+
<highlight language="config">
1498+
SSLVerifyClient require
1499+
SSLVerifyClientEKU off
1500+
</highlight>
1501+
</example>
1502+
</usage>
1503+
</directivesynopsis>
1504+
14631505
<directivesynopsis>
14641506
<name>SSLVerifyDepth</name>
14651507
<description>Maximum depth of CA Certificates in Client

modules/ssl/mod_ssl.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,9 @@ static const command_rec ssl_config_cmds[] = {
152152
SSL_CMD_ALL(VerifyClient, TAKE1,
153153
"SSL Client verify type "
154154
"('none', 'optional', 'require', 'optional_no_ca')")
155+
SSL_CMD_ALL(VerifyClientEKU, TAKE1,
156+
"Whether to enforce client certificate Extended Key Usage "
157+
"during SSL client verification ('on' or 'off')")
155158
SSL_CMD_ALL(VerifyDepth, TAKE1,
156159
"SSL Client verify depth "
157160
"('N' - number of intermediate certificates)")

modules/ssl/ssl_engine_config.c

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,7 @@ static void modssl_ctx_init(modssl_ctx_t *mctx, apr_pool_t *p)
138138
mctx->auth.cipher_suite = NULL;
139139
mctx->auth.verify_depth = UNSET;
140140
mctx->auth.verify_mode = SSL_CVERIFY_UNSET;
141+
mctx->auth.verify_client_eku = SSL_VERIFY_EKU_UNSET;
141142
mctx->auth.tls13_ciphers = NULL;
142143

143144
mctx->ocsp_mask = UNSET;
@@ -284,6 +285,7 @@ static void modssl_ctx_cfg_merge(apr_pool_t *p,
284285
cfgMergeString(auth.cipher_suite);
285286
cfgMergeInt(auth.verify_depth);
286287
cfgMerge(auth.verify_mode, SSL_CVERIFY_UNSET);
288+
cfgMerge(auth.verify_client_eku, SSL_VERIFY_EKU_UNSET);
287289
cfgMergeString(auth.tls13_ciphers);
288290

289291
cfgMergeInt(ocsp_mask);
@@ -405,6 +407,7 @@ void *ssl_config_perdir_create(apr_pool_t *p, char *dir)
405407

406408
dc->szCipherSuite = NULL;
407409
dc->nVerifyClient = SSL_CVERIFY_UNSET;
410+
dc->nVerifyClientEKU = SSL_VERIFY_EKU_UNSET;
408411
dc->nVerifyDepth = UNSET;
409412

410413
dc->szUserName = NULL;
@@ -461,6 +464,7 @@ void *ssl_config_perdir_merge(apr_pool_t *p, void *basev, void *addv)
461464

462465
cfgMergeString(szCipherSuite);
463466
cfgMerge(nVerifyClient, SSL_CVERIFY_UNSET);
467+
cfgMerge(nVerifyClientEKU, SSL_VERIFY_EKU_UNSET);
464468
cfgMergeInt(nVerifyDepth);
465469

466470
cfgMergeString(szUserName);
@@ -1321,6 +1325,36 @@ const char *ssl_cmd_SSLVerifyClient(cmd_parms *cmd,
13211325
return NULL;
13221326
}
13231327

1328+
const char *ssl_cmd_SSLVerifyClientEKU(cmd_parms *cmd,
1329+
void *dcfg,
1330+
const char *arg)
1331+
{
1332+
SSLDirConfigRec *dc = (SSLDirConfigRec *)dcfg;
1333+
SSLSrvConfigRec *sc = mySrvConfig(cmd->server);
1334+
ssl_verify_eku_t mode;
1335+
1336+
if (strcEQ(arg, "on")) {
1337+
mode = SSL_VERIFY_EKU_UNSET;
1338+
}
1339+
else if (strcEQ(arg, "off")) {
1340+
mode = SSL_VERIFY_EKU_OFF;
1341+
}
1342+
else {
1343+
return apr_pstrcat(cmd->temp_pool, cmd->cmd->name,
1344+
": Invalid argument '", arg,
1345+
"' (expected 'on' or 'off')", NULL);
1346+
}
1347+
1348+
if (cmd->path) {
1349+
dc->nVerifyClientEKU = mode;
1350+
}
1351+
else {
1352+
sc->server->auth.verify_client_eku = mode;
1353+
}
1354+
1355+
return NULL;
1356+
}
1357+
13241358
static const char *ssl_cmd_verify_depth_parse(cmd_parms *parms,
13251359
const char *arg,
13261360
int *depth)
@@ -2622,6 +2656,9 @@ static void modssl_auth_ctx_dump(modssl_auth_ctx_t *auth, apr_pool_t *p, int pro
26222656
}
26232657
#endif
26242658
DMP_VERIFY(proxy? "SSLProxyVerify" : "SSLVerifyClient", auth->verify_mode);
2659+
if (!proxy) {
2660+
DMP_ON_OFF("SSLVerifyClientEKU", auth->verify_client_eku);
2661+
}
26252662
DMP_LONG( proxy? "SSLProxyVerify" : "SSLVerifyDepth", auth->verify_depth);
26262663
DMP_STRING(proxy? "SSLProxyCACertificateFile" : "SSLCACertificateFile", auth->ca_cert_file);
26272664
DMP_STRING(proxy? "SSLProxyCACertificatePath" : "SSLCACertificatePath", auth->ca_cert_path);

modules/ssl/ssl_engine_kernel.c

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1630,6 +1630,7 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
16301630
int errdepth = X509_STORE_CTX_get_error_depth(ctx);
16311631
int depth = UNSET;
16321632
int verify = SSL_CVERIFY_UNSET;
1633+
ssl_verify_eku_t verify_eku = SSL_VERIFY_EKU_UNSET;
16331634

16341635
/*
16351636
* Log verification information
@@ -1657,6 +1658,13 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
16571658
verify = mctx->auth.verify_mode;
16581659
}
16591660

1661+
if (dc && !conn->outgoing) {
1662+
verify_eku = dc->nVerifyClientEKU;
1663+
}
1664+
if (verify_eku == SSL_VERIFY_EKU_UNSET) {
1665+
verify_eku = mctx->auth.verify_client_eku;
1666+
}
1667+
16601668
if (verify == SSL_CVERIFY_NONE) {
16611669
/*
16621670
* SSLProxyVerify is either not configured or set to "none".
@@ -1666,6 +1674,17 @@ int ssl_callback_SSLVerify(int ok, X509_STORE_CTX *ctx)
16661674
return TRUE;
16671675
}
16681676

1677+
if (!ok && !conn->outgoing
1678+
&& errnum == X509_V_ERR_INVALID_PURPOSE
1679+
&& verify_eku == SSL_VERIFY_EKU_OFF) {
1680+
ap_log_cerror(APLOG_MARK, APLOG_DEBUG, 0, conn,
1681+
"Certificate Verification: EKU check disabled by "
1682+
"SSLVerifyClientEKU, accepting invalid purpose");
1683+
X509_STORE_CTX_set_error(ctx, X509_V_OK);
1684+
errnum = X509_V_OK;
1685+
ok = TRUE;
1686+
}
1687+
16691688
if (ssl_verify_error_is_optional(errnum) &&
16701689
(verify == SSL_CVERIFY_OPTIONAL_NO_CA))
16711690
{

modules/ssl/ssl_private.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,11 @@ typedef enum {
479479
SSL_CVERIFY_OPTIONAL_NO_CA = 3
480480
} ssl_verify_t;
481481

482+
typedef enum {
483+
SSL_VERIFY_EKU_UNSET = UNSET,
484+
SSL_VERIFY_EKU_OFF = 0
485+
} ssl_verify_eku_t;
486+
482487
#define SSL_VERIFY_PEER_STRICT \
483488
(SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
484489

@@ -791,6 +796,7 @@ typedef struct {
791796
/** for client or downstream server authentication */
792797
int verify_depth;
793798
ssl_verify_t verify_mode;
799+
ssl_verify_eku_t verify_client_eku;
794800

795801
/** TLSv1.3 has its separate cipher list, separate from the
796802
settings for older TLS protocol versions. Since which one takes
@@ -926,6 +932,7 @@ struct SSLDirConfigRec {
926932
ssl_opt_t nOptionsDel;
927933
const char *szCipherSuite;
928934
ssl_verify_t nVerifyClient;
935+
ssl_verify_eku_t nVerifyClientEKU;
929936
int nVerifyDepth;
930937
const char *szUserName;
931938
apr_size_t nRenegBufferSize;
@@ -977,6 +984,7 @@ const char *ssl_cmd_SSLClientHelloVars(cmd_parms *, void *, int flag);
977984
const char *ssl_cmd_SSLCompression(cmd_parms *, void *, int flag);
978985
const char *ssl_cmd_SSLSessionTickets(cmd_parms *, void *, int flag);
979986
const char *ssl_cmd_SSLVerifyClient(cmd_parms *, void *, const char *);
987+
const char *ssl_cmd_SSLVerifyClientEKU(cmd_parms *, void *, const char *);
980988
const char *ssl_cmd_SSLVerifyDepth(cmd_parms *, void *, const char *);
981989
const char *ssl_cmd_SSLSessionCache(cmd_parms *, void *, const char *);
982990
const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *, void *, const char *);

0 commit comments

Comments
 (0)