Skip to content

Commit 7634be3

Browse files
committed
Add SSLVerifyClientEKU directive to control Extended Key Usage checks for client certificates.
1 parent f85258e commit 7634be3

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
@@ -1458,6 +1458,48 @@ SSLVerifyClient require
14581458
</usage>
14591459
</directivesynopsis>
14601460

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

472+
typedef enum {
473+
SSL_VERIFY_EKU_UNSET = UNSET,
474+
SSL_VERIFY_EKU_OFF = 0
475+
} ssl_verify_eku_t;
476+
472477
#define SSL_VERIFY_PEER_STRICT \
473478
(SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT)
474479

@@ -781,6 +786,7 @@ typedef struct {
781786
/** for client or downstream server authentication */
782787
int verify_depth;
783788
ssl_verify_t verify_mode;
789+
ssl_verify_eku_t verify_client_eku;
784790

785791
/** TLSv1.3 has its separate cipher list, separate from the
786792
settings for older TLS protocol versions. Since which one takes
@@ -916,6 +922,7 @@ struct SSLDirConfigRec {
916922
ssl_opt_t nOptionsDel;
917923
const char *szCipherSuite;
918924
ssl_verify_t nVerifyClient;
925+
ssl_verify_eku_t nVerifyClientEKU;
919926
int nVerifyDepth;
920927
const char *szUserName;
921928
apr_size_t nRenegBufferSize;
@@ -967,6 +974,7 @@ const char *ssl_cmd_SSLClientHelloVars(cmd_parms *, void *, int flag);
967974
const char *ssl_cmd_SSLCompression(cmd_parms *, void *, int flag);
968975
const char *ssl_cmd_SSLSessionTickets(cmd_parms *, void *, int flag);
969976
const char *ssl_cmd_SSLVerifyClient(cmd_parms *, void *, const char *);
977+
const char *ssl_cmd_SSLVerifyClientEKU(cmd_parms *, void *, const char *);
970978
const char *ssl_cmd_SSLVerifyDepth(cmd_parms *, void *, const char *);
971979
const char *ssl_cmd_SSLSessionCache(cmd_parms *, void *, const char *);
972980
const char *ssl_cmd_SSLSessionCacheTimeout(cmd_parms *, void *, const char *);

0 commit comments

Comments
 (0)