>From 5593a5f7da88ae37ae032b95c7a3a369e8d61a1a Mon Sep 17 00:00:00 2001 From: Nathan Kinder Date: Fri, 2 Oct 2009 13:47:38 -0700 Subject: [PATCH] Add ssf bind rule to access control plug-in. This patch adds a new ssf bind rule keyword to the access control plug-in. This allows you to write ACIs that require a specific level of encryption for the rule to apply. The new keyword can be used with '=', '!=', '<', '>', '<=' and '>=' comparators. I added code that stores the SSF in effect for an operation into the operation struct. The value that we store is the higher of the two between the SASL SSF and the SSL/TLS SSF. --- ldap/servers/plugins/acl/acl.h | 12 ++- ldap/servers/plugins/acl/acl_ext.c | 15 +++- ldap/servers/plugins/acl/aclinit.c | 7 ++ ldap/servers/plugins/acl/acllas.c | 176 ++++++++++++++++++++++++++++++++--- ldap/servers/plugins/acl/aclparse.c | 5 +- ldap/servers/plugins/acl/aclutil.c | 4 + ldap/servers/slapd/connection.c | 17 +++- ldap/servers/slapd/pblock.c | 6 + ldap/servers/slapd/slap.h | 1 + ldap/servers/slapd/slapi-plugin.h | 1 + 10 files changed, 225 insertions(+), 19 deletions(-) diff --git a/ldap/servers/plugins/acl/acl.h b/ldap/servers/plugins/acl/acl.h index bccf4c5..d8bc507 100644 --- a/ldap/servers/plugins/acl/acl.h +++ b/ldap/servers/plugins/acl/acl.h @@ -68,6 +68,7 @@ #include #include #include +#include #ifndef _WIN32 #include #include @@ -147,6 +148,7 @@ static char* const access_str_proxy = "proxy"; #define DS_LAS_USERATTR "userattr" #define DS_LAS_ROLEDN "roledn" #define DS_LAS_ROLEDNATTR "rolednattr" +#define DS_LAS_SSF "ssf" /* These define the things that aclutil_evaluate_macro() supports */ @@ -203,6 +205,7 @@ typedef enum #define DS_PROP_ACLPB "aclblock" #define DS_ATTR_AUTHTYPE "authtype" #define DS_ATTR_CERT "clientcert" +#define DS_ATTR_SSF "ssf" #define ACL_ANOM_MAX_ACL 40 struct scoped_entry_anominfo { @@ -294,6 +297,7 @@ typedef struct aci { #define ACI_PARAM_ATTRRULE (short) 0x0800 #define ACI_USERDN_SELFRULE (short) 0x1000 #define ACI_ROLEDN_RULE (short) 0x2000 +#define ACI_SSF_RULE (short) 0x4000 @@ -645,7 +649,7 @@ typedef struct { int anomUser; Acl_PBlock *aclpb; Slapi_Entry *resourceEntry; - + int ssf; }lasInfo; @@ -759,6 +763,12 @@ extern int DS_LASUserAttrEval(NSErr_t *errp, char *attribute, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth); +extern int DS_LASSSFEval(NSErr_t *errp, char *attribute, + CmpOp_t comparator, + char *pattern, int *cachable, void **las_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth); + /* other function declaration */ int aclinit_main(); int acl_match_substring (struct slapi_filter *f, char *str, int match); diff --git a/ldap/servers/plugins/acl/acl_ext.c b/ldap/servers/plugins/acl/acl_ext.c index 1b47377..d400c9c 100644 --- a/ldap/servers/plugins/acl/acl_ext.c +++ b/ldap/servers/plugins/acl/acl_ext.c @@ -580,11 +580,16 @@ acl__malloc_aclpb ( ) "Unable to set the AUTH TYPE in the Plist\n"); return NULL; } - if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_ENTRY, aclpb, 0) < 0) { + if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_ENTRY, aclpb, 0) < 0) { slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Unable to set the ENTRY TYPE in the Plist\n"); return NULL; } + if (PListInitProp(aclpb->aclpb_proplist, 0, DS_ATTR_SSF, aclpb, 0) < 0) { + slapi_log_error(SLAPI_LOG_FATAL, plugin_name, + "Unable to set the SSF in the Plist\n"); + return NULL; + } /* * ACL_ATTR_IP and ACL_ATTR_DNS are initialized lazily in the @@ -648,6 +653,7 @@ acl_init_aclpb ( Slapi_PBlock *pb , Acl_PBlock *aclpb, const char *dn, int copy_ char *authType; void *conn; int op_type; + int ssf = 0; if ( NULL == aclpb ) { @@ -688,6 +694,13 @@ acl_init_aclpb ( Slapi_PBlock *pb , Acl_PBlock *aclpb, const char *dn, int copy_ "Unable to set the AUTH TYPE in the Plist\n"); return; } + slapi_pblock_get ( pb, SLAPI_OPERATION_SSF, &ssf); + if (PListAssignValue(aclpb->aclpb_proplist, DS_ATTR_SSF, ssf, 0) < 0) { + slapi_log_error(SLAPI_LOG_FATAL, plugin_name, + "Unable to set the SSF in the Plist\n"); + return; + } + /* PKBxxx: We should be getting it from the OP struct */ slapi_pblock_get ( pb, SLAPI_CONN_CERT, &aclpb->aclpb_clientcert ); diff --git a/ldap/servers/plugins/acl/aclinit.c b/ldap/servers/plugins/acl/aclinit.c index 4609592..e99e57f 100644 --- a/ldap/servers/plugins/acl/aclinit.c +++ b/ldap/servers/plugins/acl/aclinit.c @@ -567,5 +567,12 @@ __aclinit__RegisterLases(void) "Unable to register USERATTR Las\n"); return ACL_ERR; } + if (ACL_LasRegister(NULL, DS_LAS_SSF, + (LASEvalFunc_t)DS_LASSSFEval, + (LASFlushFunc_t)NULL) < 0) { + slapi_log_error (SLAPI_LOG_FATAL, plugin_name, + "Unable to register SSF Las\n"); + return ACL_ERR; + } return ACL_OK; } diff --git a/ldap/servers/plugins/acl/acllas.c b/ldap/servers/plugins/acl/acllas.c index d220596..53620e4 100644 --- a/ldap/servers/plugins/acl/acllas.c +++ b/ldap/servers/plugins/acl/acllas.c @@ -254,7 +254,7 @@ static int acllas__get_members (Slapi_Entry* e, void *callback_data); static int acllas__client_match_URL (struct acl_pblock *aclpb, char *n_dn, char *url ); static int acllas__handle_client_search (Slapi_Entry *e, void *callback_data); -static int __acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator, +static int __acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator, int allow_range, char *attr_pattern, int *cachable, void **LAS_cookie, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth, char *lasType, char *lasName, lasInfo *linfo); @@ -483,7 +483,7 @@ DS_LASUserDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, lasInfo lasinfo; int got_undefined = 0; - if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 0, /* Don't allow range comparators */ attr_pattern,cachable,LAS_cookie, subject, resource, auth_info,global_auth, DS_LAS_USERDN, "DS_LASUserDnEval", &lasinfo )) ) { @@ -761,7 +761,7 @@ DS_LASGroupDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, int got_undefined = 0; /* the setup should not fail under normal operation */ - if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 0, /* Don't allow range comparators */ attr_pattern,cachable,LAS_cookie, subject, resource, auth_info,global_auth, DS_LAS_GROUPDN, "DS_LASGroupDnEval", &lasinfo )) ) { @@ -979,7 +979,7 @@ DS_LASRoleDnEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, lasInfo lasinfo; int got_undefined = 0; - if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 0, /* Don't allow range comparators */ attr_pattern,cachable,LAS_cookie, subject, resource, auth_info,global_auth, DS_LAS_ROLEDN, "DS_LASRoleDnEval", @@ -1154,7 +1154,7 @@ DS_LASUserDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, lasInfo lasinfo; int got_undefined = 0; - if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 0, /* Don't allow range comparators */ attr_pattern,cachable,LAS_cookie, subject, resource, auth_info,global_auth, DS_LAS_USERDNATTR, "DS_LASUserDnAttrEval", @@ -1629,7 +1629,7 @@ DS_LASAuthMethodEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *s = NULL; lasInfo lasinfo; - if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 0, /* Don't allow range comparators */ attr_pattern,cachable,LAS_cookie, subject, resource, auth_info,global_auth, DS_LAS_AUTHMETHOD, "DS_LASAuthMethodEval", @@ -1679,6 +1679,143 @@ DS_LASAuthMethodEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, return rc; } + +/*************************************************************************** +* +* DS_LASSSFEval +* +* +* Input: +* attr_name The string "ssf" - in lower case. +* comparator CMP_OP_EQ, CMP_OP_NE, CMP_OP_GT, CMP_OP_LT, CMP_OP_GE, CMP_OP_LE +* attr_pattern An integer representing the SSF +* cachable Always set to FALSE. +* subject Subject property list +* resource Resource property list +* auth_info Authentication info, if any +* +* Returns: +* retcode The usual LAS return codes. +* +* Error Handling: +* None. +* +**************************************************************************/ +int +DS_LASSSFEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, + char *attr_pattern, int *cachable, void **LAS_cookie, + PList_t subject, PList_t resource, PList_t auth_info, + PList_t global_auth) +{ + char *attr; + char *ptr; + int len; + int rc; + char *s = NULL; + lasInfo lasinfo; + int aclssf; + + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 1, /* Allow range comparators */ + attr_pattern,cachable,LAS_cookie, + subject, resource, auth_info,global_auth, + DS_LAS_SSF, "DS_LASSSFEval", + &lasinfo )) ) { + return LAS_EVAL_FAIL; + } + + attr = attr_pattern; + + /* ignore leading and trailing whitespace */ + while(ldap_utf8isspace(attr)) LDAP_UTF8INC(attr); + len = strlen(attr); + ptr = attr+len-1; + while(ptr >= attr && ldap_utf8isspace(ptr)) { + *ptr = '\0'; + LDAP_UTF8DEC(ptr); + } + + /* Convert SSF from bind rule to an int. */ + aclssf = (int) strtol(attr, &ptr, 10); + if (*ptr != '\0') { + rc = LAS_EVAL_FAIL; + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "Error parsing numeric SSF from bind rule.\n"); + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "Returning UNDEFINED for ssf evaluation.\n"); + } + + /* Check for negative values or a value overflow. */ + if ((aclssf < 0) || (((aclssf == INT_MAX) || (aclssf == INT_MIN)) && (errno == ERANGE))){ + rc = LAS_EVAL_FAIL; + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "SSF \"%s\" is invalid. Value must range from 0 to %d", + attr, INT_MAX); + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "Returning UNDEFINED for ssf evaluation.\n"); + } + + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "DS_LASSSFEval: aclssf:%d, ssf:%d\n", + aclssf, lasinfo.ssf); + + switch ((int)comparator) { + case CMP_OP_EQ: + if (lasinfo.ssf == aclssf) { + rc = LAS_EVAL_TRUE; + } else { + rc = LAS_EVAL_FALSE; + } + break; + case CMP_OP_NE: + if (lasinfo.ssf != aclssf) { + rc = LAS_EVAL_TRUE; + } else { + rc = LAS_EVAL_FALSE; + } + break; + case CMP_OP_GT: + if (lasinfo.ssf > aclssf) { + rc = LAS_EVAL_TRUE; + } else { + rc = LAS_EVAL_FALSE; + } + break; + case CMP_OP_LT: + if (lasinfo.ssf < aclssf) { + rc = LAS_EVAL_TRUE; + } else { + rc = LAS_EVAL_FALSE; + } + break; + case CMP_OP_GE: + if (lasinfo.ssf >= aclssf) { + rc = LAS_EVAL_TRUE; + } else { + rc = LAS_EVAL_FALSE; + } + break; + case CMP_OP_LE: + if (lasinfo.ssf <= aclssf) { + rc = LAS_EVAL_TRUE; + } else { + rc = LAS_EVAL_FALSE; + } + break; + default: + /* This should never happen since the comparator is + * validated by __acllas_setup(), but better safe + * than sorry. */ + rc = LAS_EVAL_FAIL; + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "Invalid comparator \"%d\" evaluating SSF.\n", + (int)comparator); + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "Returning UNDEFINED for ssf evaluation.\n"); + } + + return rc; +} + /**************************************************************************** * Struct to evaluate and keep the current members being evaluated @@ -2394,7 +2531,7 @@ DS_LASGroupDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, lasInfo lasinfo; int got_undefined = 0; - if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 0, /* Don't allow range comparators */ attr_pattern,cachable,LAS_cookie, subject, resource, auth_info,global_auth, DS_LAS_GROUPDNATTR, "DS_LASGroupDnAttrEval", @@ -3145,7 +3282,7 @@ DS_LASUserAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, lasInfo lasinfo; int got_undefined = 0; - if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 0, /* Don't allow range comparators */ attr_pattern,cachable,LAS_cookie, subject, resource, auth_info,global_auth, DS_LAS_USERATTR, "DS_LASUserAttrEval", @@ -3414,7 +3551,7 @@ acllas__handle_client_search ( Slapi_Entry *e, void *callback_data ) static int __acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator, - char *attr_pattern, int *cachable, void **LAS_cookie, + int allow_range, char *attr_pattern, int *cachable, void **LAS_cookie, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth, char *lasType, char*lasName, lasInfo *linfo) { @@ -3431,9 +3568,16 @@ __acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator, return LAS_EVAL_INVALID; } - if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { + /* Validate the comparator */ + if (allow_range && (comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE) && + (comparator != CMP_OP_GT) && (comparator != CMP_OP_LT) && + (comparator != CMP_OP_GE) && (comparator != CMP_OP_LE)) { + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "%s:Invalid comparator(%d)\n", lasName, (int)comparator); + return LAS_EVAL_INVALID; + } else if (!allow_range && (comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { slapi_log_error( SLAPI_LOG_ACL, plugin_name, - "%s:Invalid comparator(%d)\n", lasName, (int)comparator); + "%s:Invalid comparator(%d)\n", lasName, (int)comparator); return LAS_EVAL_INVALID; } @@ -3491,6 +3635,14 @@ __acllas_setup ( NSErr_t *errp, char *attr_name, CmpOp_t comparator, "%s:Unable to get the auth type(%d)\n", lasName, rc); return LAS_EVAL_FAIL; } + + /* get the SSF */ + if ((rc = PListFindValue(subject, DS_ATTR_SSF, + (void **)&linfo->ssf, NULL)) < 0) { + acl_print_acllib_err(errp, NULL); + slapi_log_error( SLAPI_LOG_ACL, plugin_name, + "%s:Unable to get the ssf(%d)\n", lasName, rc); + } return 0; } @@ -3568,7 +3720,7 @@ DS_LASRoleDnAttrEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, int k=0; int got_undefined = 0; - if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, + if ( 0 != (rc = __acllas_setup (errp, attr_name, comparator, 0, /* Don't allow range comparators */ attr_pattern,cachable,LAS_cookie, subject, resource, auth_info,global_auth, DS_LAS_ROLEDN, "DS_LASRoleDnAttrEval", diff --git a/ldap/servers/plugins/acl/aclparse.c b/ldap/servers/plugins/acl/aclparse.c index 57fd5ae..8d4a21a 100644 --- a/ldap/servers/plugins/acl/aclparse.c +++ b/ldap/servers/plugins/acl/aclparse.c @@ -927,7 +927,10 @@ __aclp__getNextLASRule (aci_t *aci_item, char *original_str , char **endOfCurrRu } else if ((ruleStart = strstr(word, ACL_ATTR_DNS)) != NULL) { ruleType = ACI_DNS_RULE; ruleLen = strlen ( ACL_ATTR_DNS) ; - } + } else if ((ruleStart = strstr(word, DS_LAS_SSF)) != NULL) { + ruleType = ACI_SSF_RULE; + ruleLen = strlen ( DS_LAS_SSF) ; + } /* Here, we've found a space...if we were in in_dn_expr mode * and we'vve found a closure for that ie.a '"' or a ')' * eg. "'ldap:///all"' or 'ldap:///all")' then exit in_dn_expr mode. diff --git a/ldap/servers/plugins/acl/aclutil.c b/ldap/servers/plugins/acl/aclutil.c index a93a53a..4aebd47 100644 --- a/ldap/servers/plugins/acl/aclutil.c +++ b/ldap/servers/plugins/acl/aclutil.c @@ -409,6 +409,10 @@ aclutil__Ruletypestr (int type , char str[]) strcpy (p, "paramAttr "); p = strchr (p, '\0'); } + if ( type & ACI_SSF_RULE) { + strcpy (p, "ssf "); + p = strchr (p, '\0'); + } } /* ** acl_gen_err_msg diff --git a/ldap/servers/slapd/connection.c b/ldap/servers/slapd/connection.c index 70990c6..57028de 100644 --- a/ldap/servers/slapd/connection.c +++ b/ldap/servers/slapd/connection.c @@ -484,12 +484,12 @@ 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 ); + /* Copy the Connection DN and SSF into the operation struct */ + op_copy_identity( conn, op ); + /* 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 @@ -2538,7 +2538,7 @@ connection_operations_pending( Connection *conn, Operation *op2ignore, * that is, after the first few bytes of the request are received. * In particular, we want the first request from an LDAPS client * to have an authorization identity derived from the initial SSL - * handshake. + * handshake. We also copy the SSF at this time. */ static void op_copy_identity(Connection *conn, Operation *op) @@ -2570,6 +2570,15 @@ op_copy_identity(Connection *conn, Operation *op) /* copy isroot flag as well so root DN privileges are preserved */ op->o_isroot = conn->c_isroot; + + /* copy the highest SSF (between SASL and SSL/TLS) into the + * operation for use by access control. */ + if (conn->c_sasl_ssf >= conn->c_ssl_ssf) { + op->o_ssf = conn->c_sasl_ssf; + } else { + op->o_ssf = conn->c_ssl_ssf; + } + PR_Unlock( conn->c_mutex ); } diff --git a/ldap/servers/slapd/pblock.c b/ldap/servers/slapd/pblock.c index d8cd876..21195ea 100644 --- a/ldap/servers/slapd/pblock.c +++ b/ldap/servers/slapd/pblock.c @@ -1544,6 +1544,12 @@ slapi_pblock_get( Slapi_PBlock *pblock, int arg, void *value ) (*( char **)value ) = pblock->pb_op->o_authtype; break; + case SLAPI_OPERATION_SSF: + if (pblock->pb_op!=NULL) { + * ((int *) value) = pblock->pb_op->o_ssf; + } + break; + case SLAPI_CLIENT_DNS: if (pblock->pb_conn == NULL) { LDAPDebug( LDAP_DEBUG_ANY, diff --git a/ldap/servers/slapd/slap.h b/ldap/servers/slapd/slap.h index 0184817..ec030bc 100644 --- a/ldap/servers/slapd/slap.h +++ b/ldap/servers/slapd/slap.h @@ -1197,6 +1197,7 @@ typedef struct op { int o_isroot; /* requestor is manager */ Slapi_DN o_sdn; /* dn bound when op was initiated */ char *o_authtype; /* auth method used to bind dn */ + int o_ssf; /* ssf for this operation (highest between SASL and TLS/SSL) */ int o_opid; /* id of this operation */ PRUint64 o_connid; /* id of conn initiating this op; for logging only */ void *o_handler_data; diff --git a/ldap/servers/slapd/slapi-plugin.h b/ldap/servers/slapd/slapi-plugin.h index 5ed054d..1d545a0 100644 --- a/ldap/servers/slapd/slapi-plugin.h +++ b/ldap/servers/slapd/slapi-plugin.h @@ -3052,6 +3052,7 @@ int slapi_reslimit_get_integer_limit( Slapi_Connection *conn, int handle, #define SLAPI_OPERATION_TYPE 590 #define SLAPI_OPERATION_AUTHTYPE 741 #define SLAPI_OPERATION_ID 744 +#define SLAPI_OPERATION_SSF 750 #define SLAPI_IS_REPLICATED_OPERATION 142 #define SLAPI_IS_MMR_REPLICATED_OPERATION 153 #define SLAPI_IS_LEGACY_REPLICATED_OPERATION 154 -- 1.6.2.5