summaryrefslogtreecommitdiffstats
path: root/ldap
diff options
context:
space:
mode:
authorNathan Kinder <nkinder@redhat.com>2009-09-30 09:33:29 -0700
committerNathan Kinder <nkinder@redhat.com>2009-09-30 11:55:25 -0700
commitab6e5a77de769f55d55e70d7754ec732385e7067 (patch)
tree1dc7e8455aea314347bdb5fa85d353014019a0b3 /ldap
parent7cd8196f272d1cfacb767e2d4e6b04db325cae5c (diff)
downloadds-ab6e5a77de769f55d55e70d7754ec732385e7067.tar.gz
ds-ab6e5a77de769f55d55e70d7754ec732385e7067.tar.xz
ds-ab6e5a77de769f55d55e70d7754ec732385e7067.zip
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.
Diffstat (limited to 'ldap')
-rw-r--r--ldap/admin/src/scripts/DSMigration.pm.in1
-rw-r--r--ldap/ldif/template-dse.ldif.in1
-rw-r--r--ldap/servers/slapd/bind.c12
-rw-r--r--ldap/servers/slapd/connection.c52
-rw-r--r--ldap/servers/slapd/extendop.c20
-rw-r--r--ldap/servers/slapd/libglobs.c59
-rw-r--r--ldap/servers/slapd/pblock.c10
-rw-r--r--ldap/servers/slapd/proto-slap.h2
-rw-r--r--ldap/servers/slapd/saslbind.c1
-rw-r--r--ldap/servers/slapd/slap.h4
-rw-r--r--ldap/servers/slapd/slapi-plugin.h1
-rw-r--r--ldap/servers/slapd/start_tls_extop.c3
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 64e066b7..2f5641c6 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 a0475386..72708a0c 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 bf54d3ca..c6b00923 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 4dd81f97..70990c68 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 d3e6d5a2..003c2ab8 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 5eb1afd5..cd7bb5dc 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 072b6185..d8cd876a 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 c408f690..35e5697c 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 ce08c5a6..376bec13 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 ba65781c..01848172 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 75a2ddc2..5ed054d9 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 bb65e5f0..def71dbf 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". */