>From f9bf0b690a212d52347045a1682a08f46b8c8b01 Mon Sep 17 00:00:00 2001 From: Nathan Kinder <nkinder@xxxxxxxxxx> Date: Wed, 30 Sep 2009 09:33:29 -0700 Subject: [PATCH] Add minimum SSF setting This adds a new configuration setting to the cn=config entry named nsslapd-minssf. This can be set to a non-negative integer representing the minimum key strength required to process operations. The default setting will be 0. The SSF for a particular connection will be determined by the key strength cipher used to protect the connection. If the SSF used for a connection does not meet the minimum requirement, the operation will be rejected with an error code of LDAP_UNWILLING_TO_PERFORM (53) along with a message stating that the minimum SSF was not met. Notable exceptions to this are operations that attempt to protect a connection. These operations are: * SASL BIND * startTLS These operations will be allowed to occur on a connection with a SSF less than the minimum. If the results of these operations end up with a SSF smaller than the minimum, they will be rejected. Additionally, we allow UNBIND and ABANDON operations to go through. I also corrected a few issues with the anonymous access switch code that I noticed while testing. We need to allow the startTLS extended operation to go through when sent by an anonymous user since it is common to send startTLS prior to a BIND to protect the credentials. I also noticed that we were using the authtype from the operation struct to determine is a user was anonymous when we really should have been using the DN. This was causing anonymous operations to get through on SSL/TLS connections. --- ldap/admin/src/scripts/DSMigration.pm.in | 1 + ldap/ldif/template-dse.ldif.in | 1 + ldap/servers/slapd/bind.c | 12 ++++++ ldap/servers/slapd/connection.c | 52 +++++++++++++++++++++++++-- ldap/servers/slapd/extendop.c | 20 ++++++++++ ldap/servers/slapd/libglobs.c | 59 +++++++++++++++++++++++++++++- ldap/servers/slapd/pblock.c | 10 +++++ ldap/servers/slapd/proto-slap.h | 2 + ldap/servers/slapd/saslbind.c | 1 + ldap/servers/slapd/slap.h | 4 ++ ldap/servers/slapd/slapi-plugin.h | 1 + ldap/servers/slapd/start_tls_extop.c | 3 ++ 12 files changed, 162 insertions(+), 4 deletions(-) diff --git a/ldap/admin/src/scripts/DSMigration.pm.in b/ldap/admin/src/scripts/DSMigration.pm.in index 64e066b..2f5641c 100644 --- a/ldap/admin/src/scripts/DSMigration.pm.in +++ b/ldap/admin/src/scripts/DSMigration.pm.in @@ -102,6 +102,7 @@ my %ignoreOld = # these are new attrs that we should just pass through 'nsslapd-allow-unauthenticated-binds' => 'nsslapd-allow-unauthenticated-binds', 'nsslapd-allow-anonymous-access' => 'nsslapd-allow-anonymous-access', + 'nsslapd-minssf' => 'nsslapd-minssf', 'nsslapd-saslpath' => 'nsslapd-saslpath', 'nsslapd-rundir' => 'nsslapd-rundir', 'nsslapd-schemadir' => 'nsslapd-schemadir', diff --git a/ldap/ldif/template-dse.ldif.in b/ldap/ldif/template-dse.ldif.in index a047538..72708a0 100644 --- a/ldap/ldif/template-dse.ldif.in +++ b/ldap/ldif/template-dse.ldif.in @@ -32,6 +32,7 @@ nsslapd-ssl-check-hostname: on nsslapd-allow-unauthenticated-binds: off nsslapd-require-secure-binds: off nsslapd-allow-anonymous-access: on +nsslapd-minssf: 0 nsslapd-port: %ds_port% nsslapd-localuser: %ds_user% nsslapd-errorlog-logging-enabled: on diff --git a/ldap/servers/slapd/bind.c b/ldap/servers/slapd/bind.c index bf54d3c..c6b0092 100644 --- a/ldap/servers/slapd/bind.c +++ b/ldap/servers/slapd/bind.c @@ -127,6 +127,7 @@ do_bind( Slapi_PBlock *pb ) char authtypebuf[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */ Slapi_Entry *bind_target_entry = NULL; int auto_bind = 0; + int minssf = 0; LDAPDebug( LDAP_DEBUG_TRACE, "do_bind\n", 0, 0, 0 ); @@ -421,6 +422,17 @@ do_bind( Slapi_PBlock *pb ) break; case LDAP_AUTH_SIMPLE: slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsSimpleAuthBinds); + + /* Check if the minimum SSF requirement has been met. */ + minssf = config_get_minssf(); + if ((pb->pb_conn->c_sasl_ssf < minssf) && (pb->pb_conn->c_ssl_ssf < minssf)) { + send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL, + "Minimum SSF not met.", 0, NULL); + /* increment BindSecurityErrorcount */ + slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsBindSecurityErrors); + goto free_and_return; + } + /* accept null binds */ if (dn == NULL || *dn == '\0') { slapi_counter_increment(g_get_global_snmp_vars()->ops_tbl.dsAnonymousBinds); diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c index 4dd81f9..70990c6 100644 --- a/ldap/servers/slapd/connection.c +++ b/ldap/servers/slapd/connection.c @@ -65,6 +65,7 @@ static Slapi_PBlock *get_pb( void ); static void connection_add_operation(Connection* conn, Operation *op); static void connection_free_private_buffer(Connection *conn); static void op_copy_identity(Connection *conn, Operation *op); +static void connection_set_ssl_ssf(Connection *conn); static int is_ber_too_big(const Connection *conn, ber_len_t ber_len); static void log_ber_too_big_error(const Connection *conn, ber_len_t ber_len, ber_len_t maxbersize); @@ -194,6 +195,7 @@ connection_cleanup(Connection *conn) conn->c_next= NULL; conn->c_prev= NULL; conn->c_extension= NULL; + conn->c_ssl_ssf = 0; /* remove any SASL I/O from the connection */ sasl_io_cleanup(conn); sasl_dispose((sasl_conn_t**)&conn->c_sasl_conn); @@ -372,6 +374,9 @@ connection_reset(Connection* conn, int ns, PRNetAddr * from, int fromLen, int is conn->c_idlesince = conn->c_starttime; conn->c_flags = is_SSL ? CONN_FLAG_SSL : 0; conn->c_authtype = slapi_ch_strdup(SLAPD_AUTH_NONE); + /* Just initialize the SSL SSF to 0 now since the handshake isn't complete + * yet, which prevents us from getting the effective key length. */ + conn->c_ssl_ssf = 0; } /* Create a pool of threads for handling the operations */ @@ -477,15 +482,42 @@ connection_need_new_password(const Connection *conn, const Operation *op, Slapi_ static void connection_dispatch_operation(Connection *conn, Operation *op, Slapi_PBlock *pb) { + int minssf = config_get_minssf(); + /* Copy the Connection DN into the operation struct */ op_copy_identity( conn, op ); + /* Get the effective key length now since the first SSL handshake should be complete */ + connection_set_ssl_ssf( conn ); + + /* If the minimum SSF requirements are not met, only allow + * bind and extended operations through. The bind and extop + * code will ensure that only SASL binds and startTLS are + * allowed, which gives the connection a chance to meet the + * SSF requirements. We also allow UNBIND and ABANDON.*/ + if ((conn->c_sasl_ssf < minssf) && (conn->c_ssl_ssf < minssf) && + (op->o_tag != LDAP_REQ_BIND) && (op->o_tag != LDAP_REQ_EXTENDED) && + (op->o_tag != LDAP_REQ_UNBIND) && (op->o_tag != LDAP_REQ_ABANDON)) { + slapi_log_access( LDAP_DEBUG_STATS, + "conn=%" NSPRIu64 " op=%d UNPROCESSED OPERATION" + " - Insufficient SSF (sasl_ssf=%d ssl_ssf=%d)\n", + conn->c_connid, op->o_opid, conn->c_sasl_ssf, conn->c_ssl_ssf ); + send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, + "Minimum SSF not met.", 0, NULL ); + return; + } + /* If anonymous access is disabled and the connection is - * not authenticated, only allow the BIND operation. */ + * not authenticated, only allow bind and extended operations. + * We allow extended operations so one can do a startTLS prior + * to binding to protect their credentials in transit. + * We also allow UNBIND and ABANDON. */ if (!config_get_anon_access_switch() && (op->o_tag != LDAP_REQ_BIND) && - ((op->o_authtype == NULL) || (strcasecmp(op->o_authtype, SLAPD_AUTH_NONE) == 0))) { + (op->o_tag != LDAP_REQ_EXTENDED) && (op->o_tag != LDAP_REQ_UNBIND) && + (op->o_tag != LDAP_REQ_ABANDON) && (slapi_sdn_get_dn(&(op->o_sdn)) == NULL )) { slapi_log_access( LDAP_DEBUG_STATS, - "conn=%" NSPRIu64 " op=%d UNPROCESSED OPERATION\n", + "conn=%" NSPRIu64 " op=%d UNPROCESSED OPERATION" + " - Anonymous access not allowed\n", conn->c_connid, op->o_opid ); send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL, @@ -2541,6 +2573,20 @@ op_copy_identity(Connection *conn, Operation *op) PR_Unlock( conn->c_mutex ); } +/* Sets the SSL SSF in the connection struct. */ +static void +connection_set_ssl_ssf(Connection *conn) +{ + PR_Lock( conn->c_mutex ); + + if (conn->c_flags & CONN_FLAG_SSL) { + SSL_SecurityStatus(conn->c_prfd, NULL, NULL, NULL, &(conn->c_ssl_ssf), NULL, NULL); + } else { + conn->c_ssl_ssf = 0; + } + + PR_Unlock( conn->c_mutex ); +} static int is_ber_too_big(const Connection *conn, ber_len_t ber_len) diff --git a/ldap/servers/slapd/extendop.c b/ldap/servers/slapd/extendop.c index d3e6d5a..003c2ab 100644 --- a/ldap/servers/slapd/extendop.c +++ b/ldap/servers/slapd/extendop.c @@ -295,6 +295,26 @@ do_extended( Slapi_PBlock *pb ) goto free_and_return; } + if (strcmp(extoid, START_TLS_OID) != 0) { + int minssf = config_get_minssf(); + + /* If anonymous access is disabled and we haven't + * authenticated yet, only allow startTLS. */ + if (!config_get_anon_access_switch() && ((pb->pb_op->o_authtype == NULL) || + (strcasecmp(pb->pb_op->o_authtype, SLAPD_AUTH_NONE) == 0))) { + send_ldap_result( pb, LDAP_INAPPROPRIATE_AUTH, NULL, + "Anonymous access is not allowed.", 0, NULL ); + goto free_and_return; + } + + /* If the minssf is not met, only allow startTLS. */ + if ((pb->pb_conn->c_sasl_ssf < minssf) && (pb->pb_conn->c_ssl_ssf < minssf)) { + send_ldap_result( pb, LDAP_UNWILLING_TO_PERFORM, NULL, + "Minimum SSF not met.", 0, NULL ); + goto free_and_return; + } + } + /* If a password change is required, only allow the password * modify extended operation */ if (!pb->pb_conn->c_isreplication_session && diff --git a/ldap/servers/slapd/libglobs.c b/ldap/servers/slapd/libglobs.c index 5eb1afd..cd7bb5d 100644 --- a/ldap/servers/slapd/libglobs.c +++ b/ldap/servers/slapd/libglobs.c @@ -613,7 +613,10 @@ static struct config_get_and_set { {CONFIG_ANON_ACCESS_ATTRIBUTE, config_set_anon_access_switch, NULL, 0, (void**)&global_slapdFrontendConfig.allow_anon_access, CONFIG_ON_OFF, - (ConfigGetFunc)config_get_anon_access_switch} + (ConfigGetFunc)config_get_anon_access_switch}, + {CONFIG_MINSSF_ATTRIBUTE, config_set_minssf, + NULL, 0, + (void**)&global_slapdFrontendConfig.minssf, CONFIG_INT, NULL} #ifdef MEMPOOL_EXPERIMENTAL ,{CONFIG_MEMPOOL_SWITCH_ATTRIBUTE, config_set_mempool_switch, NULL, 0, @@ -875,6 +878,7 @@ FrontendConfig_init () { cfg->outbound_ldap_io_timeout = SLAPD_DEFAULT_OUTBOUND_LDAP_IO_TIMEOUT; cfg->max_filter_nest_level = SLAPD_DEFAULT_MAX_FILTER_NEST_LEVEL; cfg->maxsasliosize = SLAPD_DEFAULT_MAX_SASLIO_SIZE; + cfg->minssf = SLAPD_DEFAULT_MIN_SSF; #ifdef _WIN32 cfg->conntablesize = SLAPD_DEFAULT_CONNTABLESIZE; @@ -4666,6 +4670,59 @@ config_get_maxsasliosize() } int +config_set_minssf( const char *attrname, char *value, char *errorbuf, int apply ) +{ + int retVal = LDAP_SUCCESS; + int minssf; + char *endptr; + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + + if ( config_value_is_null( attrname, value, errorbuf, 0 )) { + return LDAP_OPERATIONS_ERROR; + } + + minssf = (int) strtol(value, &endptr, 10); + + /* Check for non-numeric garbage in the value */ + if (*endptr != '\0') { + retVal = LDAP_OPERATIONS_ERROR; + } + + /* Check for a value overflow */ + if (((minssf == INT_MAX) || (minssf == INT_MIN)) && (errno == ERANGE)){ + retVal = LDAP_OPERATIONS_ERROR; + } + + /* Don't allow negative values. */ + if (minssf < 0) { + retVal = LDAP_OPERATIONS_ERROR; + } + + if (retVal != LDAP_SUCCESS) { + PR_snprintf(errorbuf, SLAPI_DSE_RETURNTEXT_SIZE, + "%s: \"%s\" is invalid. Value must range from 0 to %d", + attrname, value, INT_MAX ); + } else if (apply) { + CFG_LOCK_WRITE(slapdFrontendConfig); + slapdFrontendConfig->minssf = minssf; + CFG_UNLOCK_WRITE(slapdFrontendConfig); + } + + return retVal; +} + +int +config_get_minssf() +{ + int minssf; + slapdFrontendConfig_t *slapdFrontendConfig = getFrontendConfig(); + + minssf = slapdFrontendConfig->minssf; + + return minssf; +} + +int config_set_max_filter_nest_level( const char *attrname, char *value, char *errorbuf, int apply ) { diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c index 072b618..d8cd876 100644 --- a/ldap/servers/slapd/pblock.c +++ b/ldap/servers/slapd/pblock.c @@ -351,6 +351,16 @@ slapi_pblock_get( Slapi_PBlock *pblock, int arg, void *value ) (*(int *)value) = pblock->pb_conn->c_sasl_ssf; PR_Unlock( pblock->pb_conn->c_mutex ); break; + case SLAPI_CONN_SSL_SSF: + if (pblock->pb_conn == NULL) { + LDAPDebug( LDAP_DEBUG_ANY, + "Connection is NULL and hence cannot access SLAPI_CONN_SSL_SSF \n", 0, 0, 0 ); + return (-1); + } + PR_Lock( pblock->pb_conn->c_mutex ); + (*(int *)value) = pblock->pb_conn->c_ssl_ssf; + PR_Unlock( pblock->pb_conn->c_mutex ); + break; case SLAPI_CONN_CERT: if (pblock->pb_conn == NULL) { LDAPDebug( LDAP_DEBUG_ANY, diff --git a/ldap/servers/slapd/proto-slap.h b/ldap/servers/slapd/proto-slap.h index c408f69..35e5697 100644 --- a/ldap/servers/slapd/proto-slap.h +++ b/ldap/servers/slapd/proto-slap.h @@ -345,6 +345,7 @@ int config_set_outbound_ldap_io_timeout( const char *attrname, char *value, int config_set_unauth_binds_switch(const char *attrname, char *value, char *errorbuf, int apply ); int config_set_require_secure_binds(const char *attrname, char *value, char *errorbuf, int apply ); int config_set_anon_access_switch(const char *attrname, char *value, char *errorbuf, int apply ); +int config_set_minssf(const char *attrname, char *value, char *errorbuf, int apply ); int config_set_accesslogbuffering(const char *attrname, char *value, char *errorbuf, int apply); int config_set_csnlogging(const char *attrname, char *value, char *errorbuf, int apply); @@ -475,6 +476,7 @@ int config_get_outbound_ldap_io_timeout(void); int config_get_unauth_binds_switch(void); int config_get_require_secure_binds(void); int config_get_anon_access_switch(void); +int config_get_minssf(void); int config_get_csnlogging(); #ifdef MEMPOOL_EXPERIMENTAL int config_get_mempool_switch(); diff --git a/ldap/servers/slapd/saslbind.c b/ldap/servers/slapd/saslbind.c index ce08c5a..376bec1 100644 --- a/ldap/servers/slapd/saslbind.c +++ b/ldap/servers/slapd/saslbind.c @@ -636,6 +636,7 @@ void ids_sasl_server_new(Connection *conn) /* Enable security for this connection */ secprops.maxbufsize = 2048; /* DBDB: hack */ secprops.max_ssf = 0xffffffff; + secprops.min_ssf = config_get_minssf(); rc = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops); if (rc != SASL_OK) { LDAPDebug(LDAP_DEBUG_ANY, "sasl_setprop: %s\n", diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h index ba65781..0184817 100644 --- a/ldap/servers/slapd/slap.h +++ b/ldap/servers/slapd/slap.h @@ -279,6 +279,7 @@ typedef void (*VFP0)(void); #define SLAPD_DEFAULT_MAX_THREADS 30 /* connection pool threads */ #define SLAPD_DEFAULT_MAX_THREADS_PER_CONN 5 /* allowed per connection */ #define SLAPD_DEFAULT_SCHEMA_IGNORE_TRAILING_SPACES LDAP_OFF +#define SLAPD_DEFAULT_MIN_SSF 0 /* allow unsecured connections (no privacy or integrity) */ /* We'd like this number to be prime for the hash into the Connection table */ @@ -1277,6 +1278,7 @@ typedef struct conn { void *c_extension; /* plugins are able to extend the Connection object */ void *c_sasl_conn; /* sasl library connection sasl_conn_t */ int c_sasl_ssf; /* flag to tell us the SASL SSF */ + int c_ssl_ssf; /* flag to tell us the SSL/TLS SSF */ int c_unix_local; /* flag true for LDAPI */ int c_local_valid; /* flag true if the uid/gid are valid */ uid_t c_local_uid; /* uid of connecting process */ @@ -1723,6 +1725,7 @@ typedef struct _slapdEntryPoints { #define CONFIG_UNAUTH_BINDS_ATTRIBUTE "nsslapd-allow-unauthenticated-binds" #define CONFIG_REQUIRE_SECURE_BINDS_ATTRIBUTE "nsslapd-require-secure-binds" #define CONFIG_ANON_ACCESS_ATTRIBUTE "nsslapd-allow-anonymous-access" +#define CONFIG_MINSSF_ATTRIBUTE "nsslapd-minssf" #ifndef _WIN32 #define CONFIG_LOCALUSER_ATTRIBUTE "nsslapd-localuser" #endif /* !_WIN32 */ @@ -2018,6 +2021,7 @@ typedef struct _slapdFrontendConfig { int allow_unauth_binds; /* switch to enable/disable unauthenticated binds */ int require_secure_binds; /* switch to require simple binds to use a secure channel */ int allow_anon_access; /* switch to enable/disable anonymous access */ + int minssf; /* minimum security strength factor (for SASL and SSL/TLS) */ size_t maxsasliosize; /* limit incoming SASL IO packet size */ #ifndef _WIN32 struct passwd *localuserinfo; /* userinfo of localuser */ diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 75a2ddc..5ed054d 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -3066,6 +3066,7 @@ int slapi_reslimit_get_integer_limit( Slapi_Connection *conn, int handle, #define SLAPI_CONN_CERT 743 #define SLAPI_CONN_AUTHMETHOD 746 #define SLAPI_CONN_SASL_SSF 748 +#define SLAPI_CONN_SSL_SSF 749 /* * Types of authentication for SLAPI_CONN_AUTHMETHOD diff --git a/ldap/servers/slapd/start_tls_extop.c b/ldap/servers/slapd/start_tls_extop.c index bb65e5f..def71db 100644 --- a/ldap/servers/slapd/start_tls_extop.c +++ b/ldap/servers/slapd/start_tls_extop.c @@ -282,6 +282,8 @@ start_tls( Slapi_PBlock *pb ) conn->c_sd = ns; conn->c_prfd = newsocket; + /* Get the effective key length */ + SSL_SecurityStatus(conn->c_prfd, NULL, NULL, NULL, &(conn->c_ssl_ssf), NULL, NULL); rv = slapd_ssl_handshakeCallback (conn->c_prfd, (void *)handle_handshake_done, conn); @@ -411,6 +413,7 @@ start_tls_graceful_closure( Connection *c, Slapi_PBlock * pb, int is_initiator ) c->c_sd = ns; c->c_flags &= ~CONN_FLAG_SSL; c->c_flags &= ~CONN_FLAG_START_TLS; + c->c_ssl_ssf = 0; /* authentication & authorization credentials must be set to "anonymous". */ -- 1.6.2.5
-- 389-devel mailing list 389-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/fedora-directory-devel