mirror of
https://git.openldap.org/openldap/openldap.git
synced 2025-03-07 14:18:15 +08:00
ITS#9318 add TLS_REQSAN option
Add an option to specify how subjectAlternativeNames should be handled when validating the names in a server certificate.
This commit is contained in:
parent
2386a11649
commit
608a822349
@ -815,6 +815,15 @@ one of
|
||||
.BR LDAP_OPT_X_TLS_ALLOW ,
|
||||
.BR LDAP_OPT_X_TLS_TRY .
|
||||
.TP
|
||||
.B LDAP_OPT_X_TLS_REQUIRE_SAN
|
||||
Sets/gets the peer certificate subjectAlternativeName checking strategy,
|
||||
one of
|
||||
.BR LDAP_OPT_X_TLS_NEVER ,
|
||||
.BR LDAP_OPT_X_TLS_HARD ,
|
||||
.BR LDAP_OPT_X_TLS_DEMAND ,
|
||||
.BR LDAP_OPT_X_TLS_ALLOW ,
|
||||
.BR LDAP_OPT_X_TLS_TRY .
|
||||
.TP
|
||||
.B LDAP_OPT_X_TLS_SSL_CTX
|
||||
Gets the TLS session context associated with this handle.
|
||||
.BR outvalue
|
||||
|
@ -425,6 +425,37 @@ certificate is provided, or a bad certificate is provided, the session
|
||||
is immediately terminated. This is the default setting.
|
||||
.RE
|
||||
.TP
|
||||
.B TLS_REQSAN <level>
|
||||
Specifies what checks to perform on the subjectAlternativeName
|
||||
(SAN) extensions in a server certificate when validating the certificate
|
||||
name against the specified hostname of the server. The
|
||||
.B <level>
|
||||
can be specified as one of the following keywords:
|
||||
.RS
|
||||
.TP
|
||||
.B never
|
||||
The client will not check any SAN in the certificate.
|
||||
.TP
|
||||
.B allow
|
||||
The SAN is checked against the specified hostname. If a SAN is
|
||||
present but none match the specified hostname, the SANs are ignored
|
||||
and the usual check against the certificate DN is used.
|
||||
This is the default setting.
|
||||
.TP
|
||||
.B try
|
||||
The SAN is checked against the specified hostname. If no SAN is present
|
||||
in the server certificate, the usual check against the certificate DN
|
||||
is used. If a SAN is present but doesn't match the specified hostname,
|
||||
the session is immediately terminated. This setting may be preferred
|
||||
when a mix of certs with and without SANs are in use.
|
||||
.TP
|
||||
.B demand | hard
|
||||
These keywords are equivalent. The SAN is checked against the specified
|
||||
hostname. If no SAN is present in the server certificate, or no SANs
|
||||
match, the session is immediately terminated. This setting should be
|
||||
used when only certificates with SANs are in use.
|
||||
.RE
|
||||
.TP
|
||||
.B TLS_CRLCHECK <level>
|
||||
Specifies if the Certificate Revocation List (CRL) of the CA should be
|
||||
used to verify if the server certificates have not been revoked. This
|
||||
|
@ -160,6 +160,7 @@ LDAP_BEGIN_DECL
|
||||
#define LDAP_OPT_X_TLS_CERT 0x6017
|
||||
#define LDAP_OPT_X_TLS_KEY 0x6018
|
||||
#define LDAP_OPT_X_TLS_PEERKEY_HASH 0x6019
|
||||
#define LDAP_OPT_X_TLS_REQUIRE_SAN 0x601a
|
||||
|
||||
#define LDAP_OPT_X_TLS_NEVER 0
|
||||
#define LDAP_OPT_X_TLS_HARD 1
|
||||
|
@ -119,6 +119,7 @@ static const struct ol_attribute {
|
||||
{0, ATTR_TLS, "TLS_CACERT", NULL, LDAP_OPT_X_TLS_CACERTFILE},
|
||||
{0, ATTR_TLS, "TLS_CACERTDIR", NULL, LDAP_OPT_X_TLS_CACERTDIR},
|
||||
{0, ATTR_TLS, "TLS_REQCERT", NULL, LDAP_OPT_X_TLS_REQUIRE_CERT},
|
||||
{0, ATTR_TLS, "TLS_REQSAN", NULL, LDAP_OPT_X_TLS_REQUIRE_SAN},
|
||||
{0, ATTR_TLS, "TLS_RANDFILE", NULL, LDAP_OPT_X_TLS_RANDOM_FILE},
|
||||
{0, ATTR_TLS, "TLS_CIPHER_SUITE", NULL, LDAP_OPT_X_TLS_CIPHER_SUITE},
|
||||
{0, ATTR_TLS, "TLS_PROTOCOL_MIN", NULL, LDAP_OPT_X_TLS_PROTOCOL_MIN},
|
||||
@ -597,6 +598,7 @@ void ldap_int_initialize_global_options( struct ldapoptions *gopts, int *dbglvl
|
||||
gopts->ldo_tls_connect_cb = NULL;
|
||||
gopts->ldo_tls_connect_arg = NULL;
|
||||
gopts->ldo_tls_require_cert = LDAP_OPT_X_TLS_DEMAND;
|
||||
gopts->ldo_tls_require_san = LDAP_OPT_X_TLS_ALLOW;
|
||||
#endif
|
||||
gopts->ldo_keepalive_probes = 0;
|
||||
gopts->ldo_keepalive_interval = 0;
|
||||
|
@ -285,6 +285,7 @@ struct ldapoptions {
|
||||
int ldo_tls_require_cert;
|
||||
int ldo_tls_impl;
|
||||
int ldo_tls_crlcheck;
|
||||
int ldo_tls_require_san;
|
||||
char *ldo_tls_pin_hashalg;
|
||||
struct berval ldo_tls_pin;
|
||||
#define LDAP_LDO_TLS_NULLARG ,0,0,0,{0,0,0,0,0,0,0,0,0},0,0,0,0,0,{0,0}
|
||||
|
@ -585,6 +585,7 @@ ldap_pvt_tls_config( LDAP *ld, int option, const char *arg )
|
||||
return ldap_pvt_tls_set_option( ld, option, (void *) arg );
|
||||
|
||||
case LDAP_OPT_X_TLS_REQUIRE_CERT:
|
||||
case LDAP_OPT_X_TLS_REQUIRE_SAN:
|
||||
case LDAP_OPT_X_TLS:
|
||||
i = -1;
|
||||
if ( strcasecmp( arg, "never" ) == 0 ) {
|
||||
@ -715,6 +716,9 @@ ldap_pvt_tls_get_option( LDAP *ld, int option, void *arg )
|
||||
case LDAP_OPT_X_TLS_REQUIRE_CERT:
|
||||
*(int *)arg = lo->ldo_tls_require_cert;
|
||||
break;
|
||||
case LDAP_OPT_X_TLS_REQUIRE_SAN:
|
||||
*(int *)arg = lo->ldo_tls_require_san;
|
||||
break;
|
||||
#ifdef HAVE_OPENSSL_CRL
|
||||
case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */
|
||||
*(int *)arg = lo->ldo_tls_crlcheck;
|
||||
@ -921,6 +925,18 @@ ldap_pvt_tls_set_option( LDAP *ld, int option, void *arg )
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
case LDAP_OPT_X_TLS_REQUIRE_SAN:
|
||||
if ( !arg ) return -1;
|
||||
switch( *(int *) arg ) {
|
||||
case LDAP_OPT_X_TLS_NEVER:
|
||||
case LDAP_OPT_X_TLS_DEMAND:
|
||||
case LDAP_OPT_X_TLS_ALLOW:
|
||||
case LDAP_OPT_X_TLS_TRY:
|
||||
case LDAP_OPT_X_TLS_HARD:
|
||||
lo->ldo_tls_require_san = * (int *) arg;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
#ifdef HAVE_OPENSSL_CRL
|
||||
case LDAP_OPT_X_TLS_CRLCHECK: /* OpenSSL only */
|
||||
if ( !arg ) return -1;
|
||||
|
@ -559,6 +559,7 @@ tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
|
||||
{
|
||||
tlsg_session *s = (tlsg_session *)session;
|
||||
int i, ret;
|
||||
int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0;
|
||||
const gnutls_datum_t *peer_cert_list;
|
||||
unsigned int list_size;
|
||||
char altname[NI_MAXHOST];
|
||||
@ -620,12 +621,14 @@ tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
|
||||
}
|
||||
}
|
||||
|
||||
if (chkSAN) {
|
||||
for ( i=0, ret=0; ret >= 0; i++ ) {
|
||||
altnamesize = sizeof(altname);
|
||||
ret = gnutls_x509_crt_get_subject_alt_name( cert, i,
|
||||
altname, &altnamesize, NULL );
|
||||
if ( ret < 0 ) break;
|
||||
|
||||
gotSAN = 1;
|
||||
/* ignore empty */
|
||||
if ( altnamesize == 0 ) continue;
|
||||
|
||||
@ -661,7 +664,44 @@ tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
|
||||
}
|
||||
if ( ret >= 0 ) {
|
||||
ret = LDAP_SUCCESS;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
if (ret != LDAP_SUCCESS && chkSAN) {
|
||||
switch(chkSAN) {
|
||||
case LDAP_OPT_X_TLS_DEMAND:
|
||||
case LDAP_OPT_X_TLS_HARD:
|
||||
if (!gotSAN) {
|
||||
Debug0( LDAP_DEBUG_ANY,
|
||||
"TLS: unable to get subjectAltName from peer certificate.\n" );
|
||||
ret = LDAP_CONNECT_ERROR;
|
||||
if ( ld->ld_error ) {
|
||||
LDAP_FREE( ld->ld_error );
|
||||
}
|
||||
ld->ld_error = LDAP_STRDUP(
|
||||
_("TLS: unable to get subjectAltName from peer certificate"));
|
||||
goto done;
|
||||
}
|
||||
/* FALLTHRU */
|
||||
case LDAP_OPT_X_TLS_TRY:
|
||||
if (gotSAN) {
|
||||
Debug1( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
|
||||
"subjectAltName in certificate.\n",
|
||||
name );
|
||||
ret = LDAP_CONNECT_ERROR;
|
||||
if ( ld->ld_error ) {
|
||||
LDAP_FREE( ld->ld_error );
|
||||
}
|
||||
ld->ld_error = LDAP_STRDUP(
|
||||
_("TLS: hostname does not match subjectAltName in peer certificate"));
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case LDAP_OPT_X_TLS_ALLOW:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( ret != LDAP_SUCCESS ){
|
||||
/* find the last CN */
|
||||
i=0;
|
||||
do {
|
||||
@ -715,9 +755,10 @@ tlsg_session_chkhost( LDAP *ld, tls_session *session, const char *name_in )
|
||||
LDAP_FREE( ld->ld_error );
|
||||
}
|
||||
ld->ld_error = LDAP_STRDUP(
|
||||
_("TLS: hostname does not match CN in peer certificate"));
|
||||
_("TLS: hostname does not match name in peer certificate"));
|
||||
}
|
||||
}
|
||||
done:
|
||||
gnutls_x509_crt_deinit( cert );
|
||||
return ret;
|
||||
}
|
||||
|
@ -662,6 +662,7 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
|
||||
{
|
||||
tlso_session *s = (tlso_session *)sess;
|
||||
int i, ret = LDAP_LOCAL_ERROR;
|
||||
int chkSAN = ld->ld_options.ldo_tls_require_san, gotSAN = 0;
|
||||
X509 *x;
|
||||
const char *name;
|
||||
char *ptr;
|
||||
@ -699,7 +700,8 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
|
||||
if ((ptr = strrchr(name, '.')) && isdigit((unsigned char)ptr[1])) {
|
||||
if (inet_aton(name, (struct in_addr *)&addr)) ntype = IS_IP4;
|
||||
}
|
||||
|
||||
|
||||
if (chkSAN) {
|
||||
i = X509_get_ext_by_NID(x, NID_subject_alt_name, -1);
|
||||
if (i >= 0) {
|
||||
X509_EXTENSION *ex;
|
||||
@ -712,6 +714,7 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
|
||||
char *domain = NULL;
|
||||
GENERAL_NAME *gn;
|
||||
|
||||
gotSAN = 1;
|
||||
if (ntype == IS_DNS) {
|
||||
domain = strchr(name, '.');
|
||||
if (domain) {
|
||||
@ -770,6 +773,41 @@ tlso_session_chkhost( LDAP *ld, tls_session *sess, const char *name_in )
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ret != LDAP_SUCCESS && chkSAN) {
|
||||
switch(chkSAN) {
|
||||
case LDAP_OPT_X_TLS_DEMAND:
|
||||
case LDAP_OPT_X_TLS_HARD:
|
||||
if (!gotSAN) {
|
||||
Debug0( LDAP_DEBUG_ANY,
|
||||
"TLS: unable to get subjectAltName from peer certificate.\n" );
|
||||
ret = LDAP_CONNECT_ERROR;
|
||||
if ( ld->ld_error ) {
|
||||
LDAP_FREE( ld->ld_error );
|
||||
}
|
||||
ld->ld_error = LDAP_STRDUP(
|
||||
_("TLS: unable to get subjectAltName from peer certificate"));
|
||||
goto done;
|
||||
}
|
||||
/* FALLTHRU */
|
||||
case LDAP_OPT_X_TLS_TRY:
|
||||
if (gotSAN) {
|
||||
Debug1( LDAP_DEBUG_ANY, "TLS: hostname (%s) does not match "
|
||||
"subjectAltName in certificate.\n",
|
||||
name );
|
||||
ret = LDAP_CONNECT_ERROR;
|
||||
if ( ld->ld_error ) {
|
||||
LDAP_FREE( ld->ld_error );
|
||||
}
|
||||
ld->ld_error = LDAP_STRDUP(
|
||||
_("TLS: hostname does not match subjectAltName in peer certificate"));
|
||||
goto done;
|
||||
}
|
||||
break;
|
||||
case LDAP_OPT_X_TLS_ALLOW:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != LDAP_SUCCESS) {
|
||||
X509_NAME *xn;
|
||||
@ -832,9 +870,10 @@ no_cn:
|
||||
LDAP_FREE( ld->ld_error );
|
||||
}
|
||||
ld->ld_error = LDAP_STRDUP(
|
||||
_("TLS: hostname does not match CN in peer certificate"));
|
||||
_("TLS: hostname does not match name in peer certificate"));
|
||||
}
|
||||
}
|
||||
done:
|
||||
X509_free(x);
|
||||
return ret;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user