>From 5593a5f7da88ae37ae032b95c7a3a369e8d61a1a Mon Sep 17 00:00:00 2001
From: Nathan Kinder <nkinder@redhat.com>
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 	<stdio.h>
 #include 	<string.h>
 #include 	<sys/types.h>
+#include	<limits.h>
 #ifndef _WIN32
 #include 	<sys/socket.h>
 #include 	<netinet/in.h>
@@ -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

