]> git.ipfire.org Git - thirdparty/openldap.git/commitdiff
ITS#9471 Add RBAC overlay to contrib
authorOndřej Kuzník <ondra@mistotebe.net>
Mon, 15 Mar 2021 15:41:00 +0000 (15:41 +0000)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 18 Mar 2021 19:36:39 +0000 (19:36 +0000)
14 files changed:
contrib/slapd-modules/rbac/Makefile [new file with mode: 0755]
contrib/slapd-modules/rbac/init.c [new file with mode: 0644]
contrib/slapd-modules/rbac/jts.c [new file with mode: 0644]
contrib/slapd-modules/rbac/ldap_rbac.h [new file with mode: 0644]
contrib/slapd-modules/rbac/rbac.c [new file with mode: 0644]
contrib/slapd-modules/rbac/rbac.h [new file with mode: 0644]
contrib/slapd-modules/rbac/rbacacl.c [new file with mode: 0644]
contrib/slapd-modules/rbac/rbacaudit.c [new file with mode: 0644]
contrib/slapd-modules/rbac/rbacperm.c [new file with mode: 0644]
contrib/slapd-modules/rbac/rbacreq.c [new file with mode: 0644]
contrib/slapd-modules/rbac/rbacsess.c [new file with mode: 0644]
contrib/slapd-modules/rbac/rbacuser.c [new file with mode: 0644]
contrib/slapd-modules/rbac/slapo-rbac.5 [new file with mode: 0644]
contrib/slapd-modules/rbac/util.c [new file with mode: 0644]

diff --git a/contrib/slapd-modules/rbac/Makefile b/contrib/slapd-modules/rbac/Makefile
new file mode 100755 (executable)
index 0000000..9a4f4d4
--- /dev/null
@@ -0,0 +1,50 @@
+# $OpenLDAP$
+
+LDAP_SRC = ../../..
+LDAP_BUILD = $(LDAP_SRC)
+LDAP_INC = -I$(LDAP_BUILD)/include -I$(LDAP_SRC)/include -I$(LDAP_SRC)/servers/slapd
+LDAP_LIB = $(LDAP_BUILD)/libraries/libldap/libldap.la \
+       $(LDAP_BUILD)/libraries/liblber/liblber.la
+
+LIBTOOL = $(LDAP_BUILD)/libtool
+CC = gcc
+OPT = -g -O2 -Wall
+DEFS = -DSLAPD_OVER_RBAC=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = rbac.la
+LTVER = 0:0:0
+
+prefix=/usr/local
+exec_prefix=$(prefix)
+ldap_subdir=/openldap
+
+libdir=$(exec_prefix)/lib
+libexecdir=$(exec_prefix)/libexec
+moduledir = $(libexecdir)$(ldap_subdir)
+
+SRCS = rbac.c rbacperm.c rbacsess.c rbacuser.c rbacreq.c rbacaudit.c init.c rbacacl.c util.c jts.c
+OBJS = $(patsubst %.c,%.o,$(SRCS))
+LOBJS = $(patsubst %.c,%.lo,$(SRCS))
+
+.SUFFIXES: .c .lo
+
+%.lo: %.c rbac.h
+       $(LIBTOOL) --mode=compile $(CC) $(OPT) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+rbac.la: $(LOBJS)
+       $(LIBTOOL) --mode=link $(CC) $(OPT) -version-info $(LTVER) \
+               -rpath $(moduledir) -module -o $@ $^ $(LIBS)
+
+clean:
+       rm -rf *.o *.lo *.la .libs
+
+install: $(PROGRAMS)
+       mkdir -p $(DESTDIR)$(moduledir)
+       for p in $(PROGRAMS) ; do \
+               $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+       done
+
diff --git a/contrib/slapd-modules/rbac/init.c b/contrib/slapd-modules/rbac/init.c
new file mode 100644 (file)
index 0000000..1925ae5
--- /dev/null
@@ -0,0 +1,324 @@
+/* init.c - RBAC initialization */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static slap_callback nullsc = { NULL, NULL, NULL, NULL };
+
+struct slap_rbac_internal_schema slap_rbac_schema;
+
+extern rbac_tenant_t rbac_tenants;
+extern int initialize_jts( void );
+
+rbac_ad_t rbac_session_ads[] = {
+       { RBAC_SESSION_ID,
+               BER_BVC("rbacSessid"), &slap_rbac_schema.ad_session_id },
+       { RBAC_USER_DN,
+               BER_BVC("rbacUserDN"), &slap_rbac_schema.ad_session_user_dn },
+       { RBAC_ROLES,
+               BER_BVC("rbacRoles"), &slap_rbac_schema.ad_session_roles },
+       { RBAC_ROLE_CONSTRAINTS,
+               BER_BVC("rbacRoleConstraints"),
+               &slap_rbac_schema.ad_session_role_constraints },
+       { RBAC_UID,
+               BER_BVC("uid"), &slap_rbac_schema.ad_uid},
+       { RBAC_TENANT_ID,
+               BER_BVC("tenantid"), &slap_rbac_schema.ad_tenant_id },
+
+       { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t rbac_session_permission_ads[] = {
+       { RBAC_OP_NAME,
+               BER_BVC("rbacOpName"), &slap_rbac_schema.ad_permission_opname },
+       { RBAC_OBJ_NAME,
+               BER_BVC("rbacObjName"), &slap_rbac_schema.ad_permission_objname },
+       { RBAC_ROLE_NAME,
+               BER_BVC("rbacRoleName"), &slap_rbac_schema.ad_permission_rolename },
+
+       { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t audit_ads[] = {
+       { RBAC_AUDIT_OP,
+               BER_BVC("rbacAuditOp"), &slap_rbac_schema.ad_audit_op },
+       { RBAC_AUDIT_ID,
+               BER_BVC("rbacAuditId"), &slap_rbac_schema.ad_audit_id },
+       { RBAC_AUDIT_ROLES,
+               BER_BVC("rbacAuditRoles"), &slap_rbac_schema.ad_audit_roles },
+       { RBAC_AUDIT_REQUESTED_ROLES,
+               BER_BVC("rbacAuditRequestedRoles"),
+               &slap_rbac_schema.ad_audit_requested_roles
+       },
+       { RBAC_AUDIT_TIMESTAMP,
+               BER_BVC("rbacAuditTimestamp"), &slap_rbac_schema.ad_audit_timestamp },
+       { RBAC_AUDIT_RESOURCES,
+               BER_BVC("rbacAuditResources"), &slap_rbac_schema.ad_audit_resources },
+       { RBAC_AUDIT_OBJS,
+               BER_BVC("rbacAuditObjects"), &slap_rbac_schema.ad_audit_objects },
+       { RBAC_AUDIT_OPS,
+               BER_BVC("rbacAuditOperations"), &slap_rbac_schema.ad_audit_operations },
+       { RBAC_AUDIT_RESULT,
+               BER_BVC("rbacAuditResult"), &slap_rbac_schema.ad_audit_result },
+       { RBAC_AUDIT_PROPERTIES,
+               BER_BVC("rbacAuditProperties"), &slap_rbac_schema.ad_audit_properties },
+       { RBAC_AUDIT_MSGS,
+               BER_BVC("rbacAuditMessages"), &slap_rbac_schema.ad_audit_messages },
+
+       { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+/* initialize repository attribute descriptions */
+
+static int
+initialize_sessions()
+{
+       int i, nattrs, rc = LDAP_SUCCESS;
+       const char *text;
+
+       for ( nattrs = 0; !BER_BVISNULL( &rbac_session_ads[nattrs].attr );
+                       nattrs++ )
+               ; /* count the number of attrs */
+
+       slap_rbac_schema.session_attrs =
+                       slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+       for ( i = 0; !BER_BVISNULL( &rbac_session_ads[i].attr ); i++ ) {
+               rc = slap_bv2ad(
+                               &rbac_session_ads[i].attr, rbac_session_ads[i].ad, &text );
+               if ( rc != LDAP_SUCCESS ) {
+                       goto done;
+               }
+               slap_rbac_schema.session_attrs[i].an_name = rbac_session_ads[i].attr;
+               slap_rbac_schema.session_attrs[i].an_desc = *rbac_session_ads[i].ad;
+       }
+
+       BER_BVZERO( &slap_rbac_schema.session_attrs[nattrs].an_name );
+
+done:;
+       return rc;
+}
+
+static int
+initialize_audit()
+{
+       int i, rc = LDAP_SUCCESS;
+       const char *text;
+
+       /* for audit */
+       for ( i = 0; !BER_BVISNULL( &audit_ads[i].attr ); i++ ) {
+               rc = slap_bv2ad( &audit_ads[i].attr, audit_ads[i].ad, &text );
+               if ( rc != LDAP_SUCCESS ) {
+                       goto done;
+               }
+       }
+
+done:;
+       return rc;
+}
+
+static int
+initialize_tenant(
+               BackendDB *be,
+               ConfigReply *cr,
+               tenant_info_t *tenantp,
+               int init_op )
+{
+       int rc = LDAP_SUCCESS;
+       Entry *e = NULL;
+       OperationBuffer opbuf;
+       Operation *op2;
+       SlapReply rs2 = { REP_RESULT };
+       Connection conn = { 0 };
+       struct berval rbac_container_oc = BER_BVC("rbacContainer");
+       struct berval rbac_audit_container = BER_BVC("audit");
+       struct berval rbac_session_container = BER_BVC("rbac");
+       void *thrctx = ldap_pvt_thread_pool_context();
+
+       e = entry_alloc();
+
+       switch ( init_op ) {
+               case INIT_AUDIT_CONTAINER:
+                       ber_dupbv( &e->e_name, &tenantp->audit_basedn );
+                       ber_dupbv( &e->e_nname, &tenantp->audit_basedn );
+
+                       /* container cn */
+                       attr_merge_one(
+                                       e, slap_schema.si_ad_cn, &rbac_audit_container, NULL );
+                       break;
+               case INIT_SESSION_CONTAINER:
+                       ber_dupbv( &e->e_name, &tenantp->sessions_basedn );
+                       ber_dupbv( &e->e_nname, &tenantp->sessions_basedn );
+
+                       /* rendered dynmaicObject for session */
+                       attr_merge_one( e, slap_schema.si_ad_objectClass,
+                                       &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+                       /* container cn */
+                       attr_merge_one(
+                                       e, slap_schema.si_ad_cn, &rbac_session_container, NULL );
+                       break;
+               default:
+                       break;
+       }
+
+       attr_merge_one(
+                       e, slap_schema.si_ad_objectClass, &rbac_container_oc, NULL );
+       attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+                       &rbac_container_oc, NULL );
+
+       /* store RBAC session */
+       connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+       op2 = &opbuf.ob_op;
+       op2->o_callback = &nullsc;
+       op2->o_tag = LDAP_REQ_ADD;
+       op2->o_protocol = LDAP_VERSION3;
+       op2->o_req_dn = e->e_name;
+       op2->o_req_ndn = e->e_nname;
+       op2->ora_e = e;
+       op2->o_bd = select_backend( &op2->o_req_ndn, 0 );
+       op2->o_dn = op2->o_bd->be_rootdn;
+       op2->o_ndn = op2->o_bd->be_rootndn;
+       rc = op2->o_bd->be_add( op2, &rs2 );
+
+       if ( e ) entry_free( e );
+
+       return rc;
+}
+
+int
+rbac_initialize_tenants( BackendDB *be, ConfigReply *cr )
+{
+       int rc = LDAP_SUCCESS;
+       rbac_tenant_t *tenantp = NULL;
+
+       for ( tenantp = &rbac_tenants; tenantp; tenantp = tenantp->next ) {
+               rc = initialize_tenant(
+                               be, cr, &tenantp->tenant_info, INIT_AUDIT_CONTAINER );
+               if ( rc != LDAP_SUCCESS ) {
+                       if ( rc == LDAP_ALREADY_EXISTS ) {
+                               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                                               "audit container exists, tenant (%s)\n",
+                                               tenantp->tenant_info.tid.bv_val ?
+                                                               tenantp->tenant_info.tid.bv_val :
+                                                               "NULL" );
+                               rc = LDAP_SUCCESS;
+                       } else {
+                               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                                               "failed to initialize (%s): rc (%d)\n",
+                                               tenantp->tenant_info.tid.bv_val ?
+                                                               tenantp->tenant_info.tid.bv_val :
+                                                               "NULL",
+                                               rc );
+                               goto done;
+                       }
+               } else {
+                       Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                                       "created audit container for tenant (%s):\n",
+                                       tenantp->tenant_info.tid.bv_val ?
+                                                       tenantp->tenant_info.tid.bv_val :
+                                                       "NULL" );
+               }
+               rc = initialize_tenant(
+                               be, cr, &tenantp->tenant_info, INIT_SESSION_CONTAINER );
+               if ( rc != LDAP_SUCCESS ) {
+                       if ( rc == LDAP_ALREADY_EXISTS ) {
+                               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                                               "session container exists, tenant (%s)\n",
+                                               tenantp->tenant_info.tid.bv_val ?
+                                                               tenantp->tenant_info.tid.bv_val :
+                                                               "NULL" );
+                               rc = LDAP_SUCCESS;
+                       } else {
+                               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                                               "failed to initialize (%s): rc (%d)\n",
+                                               tenantp->tenant_info.tid.bv_val ?
+                                                               tenantp->tenant_info.tid.bv_val :
+                                                               "NULL",
+                                               rc );
+                               goto done;
+                       }
+               } else {
+                       Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                                       "created session container for tenant (%s):\n",
+                                       tenantp->tenant_info.tid.bv_val ?
+                                                       tenantp->tenant_info.tid.bv_val :
+                                                       "NULL" );
+               }
+       }
+
+done:;
+
+       return rc;
+}
+
+static int
+initialize_rbac_session_permissions()
+{
+       int i, rc = LDAP_SUCCESS;
+       const char *text;
+
+       for ( i = 0; !BER_BVISNULL( &rbac_session_permission_ads[i].attr ); i++ ) {
+               rc = slap_bv2ad( &rbac_session_permission_ads[i].attr,
+                               rbac_session_permission_ads[i].ad, &text );
+               if ( rc != LDAP_SUCCESS ) {
+                       goto done;
+               }
+       }
+
+done:;
+       return rc;
+}
+
+int
+rbac_initialize_repository()
+{
+       int rc = LDAP_SUCCESS;
+
+       rc = initialize_jts();
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       rc = initialize_rbac_session_permissions();
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       rc = initialize_sessions();
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       rc = initialize_audit();
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       return rc;
+}
diff --git a/contrib/slapd-modules/rbac/jts.c b/contrib/slapd-modules/rbac/jts.c
new file mode 100644 (file)
index 0000000..c7c072b
--- /dev/null
@@ -0,0 +1,198 @@
+/* jts.c - RBAC JTS initialization */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+struct slap_rbac_tenant_schema slap_rbac_jts_schema;
+
+/* to replace all JTS schema initialization */
+rbac_ad_t ft_ads[] = {
+       { RBAC_ROLE_ASSIGNMENT,
+               BER_BVC("ftRA"), &slap_rbac_jts_schema.ad_role },
+       { RBAC_ROLE_CONSTRAINTS,
+               BER_BVC("ftRC"), &slap_rbac_jts_schema.ad_role_constraint },
+       { RBAC_USER_CONSTRAINTS,
+               BER_BVC("ftCstr"), &slap_rbac_jts_schema.ad_user_constraint },
+       { RBAC_UID,
+               BER_BVC("uid"), &slap_rbac_jts_schema.ad_uid },
+       { RBAC_USERS,
+               BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+       { RBAC_ROLES,
+               BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+       { RBAC_OBJ_NAME,
+               BER_BVC("ftObjNm"), &slap_rbac_jts_schema.ad_permission_objname },
+       { RBAC_OP_NAME,
+               BER_BVC("ftOpNm"), &slap_rbac_jts_schema.ad_permission_opname },
+
+       { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_user_ads[] = {
+       { RBAC_ROLE_ASSIGNMENT,
+               BER_BVC("ftRA"), &slap_rbac_jts_schema.ad_role },
+       { RBAC_ROLE_CONSTRAINTS,
+               BER_BVC("ftRC"), &slap_rbac_jts_schema.ad_role_constraint },
+       { RBAC_USER_CONSTRAINTS,
+               BER_BVC("ftCstr"), &slap_rbac_jts_schema.ad_user_constraint },
+       { RBAC_UID,
+               BER_BVC("uid"), &slap_rbac_jts_schema.ad_uid },
+
+       { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_perm_ads[] = {
+       { RBAC_USERS,
+               BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+       { RBAC_ROLES,
+               BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+
+       { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+rbac_ad_t ft_session_perm_ads[] = {
+       { RBAC_USERS,
+               BER_BVC("ftUsers"), &slap_rbac_jts_schema.ad_permission_users },
+       { RBAC_ROLES,
+               BER_BVC("ftRoles"), &slap_rbac_jts_schema.ad_permission_roles },
+       { RBAC_OBJ_NAME,
+               BER_BVC("ftObjNm"), &slap_rbac_jts_schema.ad_permission_objname },
+       { RBAC_OP_NAME,
+               BER_BVC("ftOpNm"), &slap_rbac_jts_schema.ad_permission_opname },
+
+       { RBAC_NONE, BER_BVNULL, NULL }
+};
+
+static int
+initialize_jts_session_permission_ads()
+{
+       int i, nattrs, rc = LDAP_SUCCESS;
+
+       for ( nattrs = 0; !BER_BVISNULL( &ft_session_perm_ads[nattrs].attr );
+                       nattrs++ )
+               ; /* count the number of attrs */
+
+       slap_rbac_jts_schema.session_perm_attrs =
+                       slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+       for ( i = 0; !BER_BVISNULL( &ft_session_perm_ads[i].attr ); i++ ) {
+               slap_rbac_jts_schema.session_perm_attrs[i].an_name =
+                               ft_session_perm_ads[i].attr;
+               slap_rbac_jts_schema.session_perm_attrs[i].an_desc =
+                               *ft_session_perm_ads[i].ad;
+       }
+
+       BER_BVZERO( &slap_rbac_jts_schema.session_perm_attrs[nattrs].an_name );
+
+       slap_rbac_jts_schema.session_permissions_ads = ft_session_perm_ads;
+
+       return rc;
+}
+
+static int
+initialize_jts_permission_ads()
+{
+       int i, nattrs, rc = LDAP_SUCCESS;
+
+       /* jts permissions configuration */
+
+       for ( nattrs = 0; !BER_BVISNULL( &ft_perm_ads[nattrs].attr ); nattrs++ )
+               ; /* count the number of attrs */
+
+       slap_rbac_jts_schema.perm_attrs =
+                       slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+       for ( i = 0; !BER_BVISNULL( &ft_perm_ads[i].attr ); i++ ) {
+               slap_rbac_jts_schema.perm_attrs[i].an_name = ft_perm_ads[i].attr;
+               slap_rbac_jts_schema.perm_attrs[i].an_desc = *ft_perm_ads[i].ad;
+       }
+
+       BER_BVZERO( &slap_rbac_jts_schema.perm_attrs[nattrs].an_name );
+
+       slap_rbac_jts_schema.permission_ads = ft_perm_ads;
+
+       return rc;
+}
+
+static int
+initialize_jts_user_ads()
+{
+       int i, nattrs, rc = LDAP_SUCCESS;
+
+       /* jts user attribute descriptions */
+
+       /* jts user attributes */
+       for ( nattrs = 0; !BER_BVISNULL( &ft_user_ads[nattrs].attr ); nattrs++ )
+               ; /* count the number of attrs */
+
+       slap_rbac_jts_schema.user_attrs =
+                       slap_sl_calloc( sizeof(AttributeName), nattrs + 1, NULL );
+
+       for ( i = 0; !BER_BVISNULL( &ft_user_ads[i].attr ); i++ ) {
+               slap_rbac_jts_schema.user_attrs[i].an_name = ft_user_ads[i].attr;
+               slap_rbac_jts_schema.user_attrs[i].an_desc = *ft_user_ads[i].ad;
+       }
+
+       BER_BVZERO( &slap_rbac_jts_schema.user_attrs[nattrs].an_name );
+
+       slap_rbac_jts_schema.user_ads = ft_user_ads;
+
+       return rc;
+}
+
+int
+initialize_jts()
+{
+       int i, rc;
+       const char *text;
+
+       /* jts attributes */
+       for ( i = 0; !BER_BVISNULL( &ft_ads[i].attr ); i++ ) {
+               rc = slap_bv2ad( &ft_ads[i].attr, ft_ads[i].ad, &text );
+               if ( rc != LDAP_SUCCESS ) {
+                       goto done;
+               }
+       }
+
+       rc = initialize_jts_user_ads();
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       rc = initialize_jts_permission_ads();
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       rc = initialize_jts_session_permission_ads();
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+done:;
+       return rc;
+}
diff --git a/contrib/slapd-modules/rbac/ldap_rbac.h b/contrib/slapd-modules/rbac/ldap_rbac.h
new file mode 100644 (file)
index 0000000..d57fe6e
--- /dev/null
@@ -0,0 +1,55 @@
+#ifndef LDAP_RBAC_H
+#define LDAP_RBAC_H
+
+/* extended operations for RBAC */
+#define LDAP_RBAC_EXOP_CREATE_SESSION   "1.3.6.1.4.1.4203.555.1" /* RFC xxxx */
+#define LDAP_RBAC_EXOP_CHECK_ACCESS     "1.3.6.1.4.1.4203.555.2"
+#define LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE  "1.3.6.1.4.1.4203.555.3"
+#define LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE "1.3.6.1.4.1.4203.555.4"
+#define LDAP_RBAC_EXOP_DELETE_SESSION   "1.3.6.1.4.1.4203.555.5"
+#define LDAP_RBAC_EXOP_SESSION_ROLES    "1.3.6.1.4.1.4203.555.6"
+#define LDAP_RBAC_EXOP_SESSION_PERMISSIONS "1.3.6.1.4.1.4203.555.7"
+
+#define LDAP_TAG_EXOP_RBAC_SESSION_ID ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_TENANT_ID ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_USER_ID ((ber_tag_t)0x82U)
+#define LDAP_TAG_EXOP_RBAC_USER ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_AUTHTOK ((ber_tag_t)0x83U)
+#define LDAP_TAG_EXOP_RBAC_ACTIVE_ROLE ((ber_tag_t)0xA4U)
+#define LDAP_TAG_EXOP_RBAC_OPNAME ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_OBJNAME ((ber_tag_t)0x82U)
+#define LDAP_TAG_EXOP_RBAC_OBJID ((ber_tag_t)0x83U)
+#define LDAP_TAG_EXOP_RBAC_PWPOLICY_STATE ((ber_tag_t)0x85U)
+#define LDAP_TAG_EXOP_RBAC_PWPOLICY_VALUE ((ber_tag_t)0x86U)
+#define LDAP_TAG_EXOP_RBAC_ROLES ((ber_tag_t)0x04U)
+
+#define LDAP_TAG_EXOP_RBAC_USER_ID_SESS        ((ber_tag_t)0x80U)
+#define LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS     ((ber_tag_t)0x81U)
+#define LDAP_TAG_EXOP_RBAC_ROLE_NM_SESS        ((ber_tag_t)0x82U)
+
+#define RBAC_REQ_CREATE_SESSION      0
+#define RBAC_REQ_CHECK_ACCESS        1
+#define RBAC_REQ_ADD_ACTIVE_ROLE     2
+#define RBAC_REQ_DROP_ACTIVE_ROLE    3
+#define RBAC_REQ_DELETE_SESSION      4
+#define RBAC_REQ_SESSION_PERMISSIONS 5
+#define RBAC_REQ_SESSION_ROLES       6
+
+/* defines for password policy */
+#define RBAC_BIND_NEW_AUTHTOK_REQD 1
+
+#define RBAC_PASSWORD_GOOD 0
+#define RBAC_PASSWORD_EXPIRATION_WARNING 11
+#define RBAC_PASSWORD_GRACE_WARNING 12
+#define RBAC_PASSWORD_HAS_EXPIRED 100
+#define RBAC_ACCOUNT_LOCKED 101
+#define RBAC_CHANGE_AFTER_RESET 102
+#define RBAC_NO_MODIFICATIONS 103
+#define RBAC_MUST_SUPPLY_OLD 104
+#define RBAC_INSUFFICIENT_QUALITY 105
+#define RBAC_PASSWORD_TOO_SHORT 106
+#define RBAC_PASSWORD_TOO_YOUNG 107
+#define RBAC_HISTORY_VIOLATION 108
+#define RBAC_ACCOUNT_LOCKED_CONSTRAINTS 109
+
+#endif /* LDAP_RBAC_H */
diff --git a/contrib/slapd-modules/rbac/rbac.c b/contrib/slapd-modules/rbac/rbac.c
new file mode 100644 (file)
index 0000000..57ac59e
--- /dev/null
@@ -0,0 +1,2169 @@
+/* rbac.c - RBAC main file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2013-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+#define RBAC_REQ 1
+
+static slap_overinst rbac;
+
+static struct berval slap_EXOP_CREATE_SESSION =
+               BER_BVC(LDAP_RBAC_EXOP_CREATE_SESSION);
+static struct berval slap_EXOP_CHECK_ACCESS =
+               BER_BVC(LDAP_RBAC_EXOP_CHECK_ACCESS);
+static struct berval slap_EXOP_ADD_ACTIVE_ROLE =
+               BER_BVC(LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE);
+static struct berval slap_EXOP_DROP_ACTIVE_ROLE =
+               BER_BVC(LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE);
+static struct berval slap_EXOP_DELETE_SESSION =
+               BER_BVC(LDAP_RBAC_EXOP_DELETE_SESSION);
+static struct berval slap_EXOP_SESSION_ROLES =
+               BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES);
+
+rbac_tenant_t rbac_tenants = {
+       {
+               .schema = &slap_rbac_jts_schema,
+       },
+       NULL
+};
+
+static ConfigDriver rbac_cf_gen;
+
+static ConfigTable rbaccfg[] = {
+       { "rbac-default-users-base-dn", "usersDN", 2, 2, 0,
+               ARG_MAGIC|ARG_DN|RBAC_DEFAULT_USERS_BASE_DN,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.1 NAME 'olcRBACDefaultUsersBaseDn' "
+                       "DESC 'default Base DN for RBAC users ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-default-roles-base-dn", "rolesDN", 2, 2, 0,
+               ARG_MAGIC|ARG_DN|RBAC_DEFAULT_ROLES_BASE_DN,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.2 NAME 'olcRBACDefaultRolesBaseDn' "
+                       "DESC 'default base DN for RBAC roles ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-default-permissions-base-dn", "permissionsDN", 2, 2, 0,
+               ARG_MAGIC|ARG_DN|RBAC_DEFAULT_PERMISSIONS_BASE_DN,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.3 NAME 'olcRBACDefaultPermissionsBaseDn' "
+                       "DESC 'default base DN for RBAC permissions ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-default-sessions-base-dn", "sessionsDN", 2, 2, 0,
+               ARG_MAGIC|ARG_DN|RBAC_DEFAULT_SESSIONS_BASE_DN,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.4 NAME 'olcRBACDefaultSessionsBaseDn' "
+                       "DESC 'default base DN for RBAC permissions ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-admin", "adminDN", 2, 2, 0,
+               ARG_MAGIC|ARG_DN|RBAC_ADMIN_DN,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.5 NAME 'olcRBACAdminDn' "
+                       "DESC 'default admin DN for RBAC repository ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-pwd", "adminPwd", 2, 2, 0,
+               ARG_MAGIC|RBAC_ADMIN_PWD,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.6 NAME 'olcRBACAdminPwd' "
+                       "DESC 'default admin pwd for RBAC repository ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-session-admin", "sessionAdminDN", 2, 2, 0,
+               ARG_MAGIC|ARG_DN|RBAC_SESSION_ADMIN_DN,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.7 NAME 'olcRBACSessionAdminDn' "
+                       "DESC 'admin DN for RBAC session repository ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-session-admin-pwd", "sessionAdminPwd", 2, 2, 0,
+               ARG_MAGIC|RBAC_SESSION_ADMIN_PWD,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.8 NAME 'olcRBACSessionAdminPwd' "
+                       "DESC 'admin pwd for RBAC session repository ' "
+                       "SYNTAX OMsDirectoryString SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "tenant", "tenant", 2, 2, 0,
+               ARG_MAGIC|ARG_DN|RBAC_TENANT,
+               rbac_cf_gen, "(OLcfgCtAt:7.9 NAME 'olcRBACTenant' "
+                       "DESC 'RBAC tenant ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-default-audit-base-dn", "auditDN", 2, 2, 0,
+               ARG_MAGIC|ARG_DN|RBAC_DEFAULT_AUDIT_BASE_DN,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.10 NAME 'olcRBACDefaultAuditBaseDn' "
+                       "DESC 'default base DN for RBAC audit records ' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+       { "rbac-default-tenant-id", "tenantId", 2, 2, 0,
+               ARG_MAGIC|RBAC_DEFAULT_TENANT_ID,
+               rbac_cf_gen,
+               "(OLcfgCtAt:7.11 NAME 'olcRBACDefaultTenantId' "
+                       "DESC 'default tenant id' "
+                       "SYNTAX OMsDirectoryString "
+                       "SINGLE-VALUE )",
+               NULL, NULL
+       },
+
+       { NULL, NULL, 0, 0, 0, ARG_IGNORED }
+};
+
+static ConfigOCs rbac_ocs[] = {
+       { "( OLcfgCtOc:7.1 "
+                       "NAME 'olcRBACConfig' "
+                       "DESC 'RBAC configuration' "
+                       "SUP olcOverlayConfig "
+                       "MAY ( olcRBACDefaultUsersBaseDn $ olcRBACDefaultRolesBaseDn $ "
+                       "olcRBACDefaultPermissionsBaseDn $ olcRBACDefaultSessionsBaseDn $ "
+                       "olcRBACAdminDn $  olcRBACAdminPwd $ olcRBACSessionAdminDn $ "
+                       "olcRBACSessionAdminPwd) )",
+               Cft_Overlay, rbaccfg },
+
+       { NULL, 0, NULL }
+};
+
+static slap_verbmasks rbac_keys[] = {
+       { BER_BVC("default_users_base_dn"), RBAC_DEFAULT_USERS_BASE_DN },
+       { BER_BVC("default_roles_base_dn"), RBAC_DEFAULT_ROLES_BASE_DN },
+       { BER_BVC("default_permissions_base_dn"),
+               RBAC_DEFAULT_PERMISSIONS_BASE_DN },
+       { BER_BVC("tenant"), RBAC_TENANT },
+
+       { BER_BVNULL, 0 }
+};
+
+static slap_verbmasks rbac_tenant_keys[] = {
+       { BER_BVC("id"), RBAC_TENANT_ID },
+       { BER_BVC("users_base_dn"), RBAC_USERS_BASE_DN },
+       { BER_BVC("roles_base_dn"), RBAC_ROLES_BASE_DN },
+       { BER_BVC("permissions_base_dn"), RBAC_PERMISSIONS_BASE_DN },
+
+       { BER_BVNULL, 0 }
+};
+
+static void
+rbac_tenant_parse( char *tenent_info, tenant_info_t *tenants )
+{
+       return;
+}
+
+static int
+rbac_cf_gen( ConfigArgs *c )
+{
+       slap_overinst *on = (slap_overinst *)c->bi;
+       rbac_tenant_t *ri = &rbac_tenants;
+       int rc = 0;
+
+       if ( c->op == SLAP_CONFIG_EMIT ) {
+               switch ( c->type ) {
+                       case RBAC_DEFAULT_USERS_BASE_DN:
+                               value_add_one( &c->rvalue_vals, &ri->tenant_info.users_basedn );
+                               break;
+                       case RBAC_DEFAULT_ROLES_BASE_DN:
+                               value_add_one( &c->rvalue_vals, &ri->tenant_info.roles_basedn );
+                               break;
+                       case RBAC_DEFAULT_PERMISSIONS_BASE_DN:
+                               value_add_one(
+                                               &c->rvalue_vals, &ri->tenant_info.permissions_basedn );
+                               break;
+                       case RBAC_DEFAULT_SESSIONS_BASE_DN:
+                               value_add_one(
+                                               &c->rvalue_vals, &ri->tenant_info.sessions_basedn );
+                               break;
+                       case RBAC_DEFAULT_AUDIT_BASE_DN:
+                               value_add_one( &c->rvalue_vals, &ri->tenant_info.audit_basedn );
+                               break;
+                       case RBAC_ADMIN_DN:
+                               value_add_one( &c->rvalue_vals, &ri->tenant_info.admin );
+                               break;
+                       case RBAC_ADMIN_PWD:
+                               value_add_one( &c->rvalue_vals, &ri->tenant_info.pwd );
+                               break;
+                       case RBAC_SESSION_ADMIN_DN:
+                               value_add_one(
+                                               &c->rvalue_vals, &ri->tenant_info.session_admin );
+                               break;
+                       case RBAC_DEFAULT_TENANT_ID:
+                               value_add_one( &c->rvalue_vals, &ri->tenant_info.tid );
+                               break;
+                       default:
+                               break;
+               }
+               return rc;
+       } else if ( c->op == LDAP_MOD_DELETE ) {
+               /* FIXME */
+               return 1;
+       }
+       switch ( c->type ) {
+               case RBAC_DEFAULT_USERS_BASE_DN: {
+                       struct berval dn = BER_BVNULL;
+                       ber_str2bv( c->argv[1], 0, 0, &dn );
+                       rc = dnNormalize(
+                                       0, NULL, NULL, &dn, &ri->tenant_info.users_basedn, NULL );
+                       break;
+               }
+               case RBAC_DEFAULT_ROLES_BASE_DN: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.roles_basedn );
+                       break;
+               }
+               case RBAC_DEFAULT_PERMISSIONS_BASE_DN: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.permissions_basedn );
+                       break;
+               }
+               case RBAC_DEFAULT_SESSIONS_BASE_DN: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.sessions_basedn );
+                       break;
+               }
+               case RBAC_DEFAULT_AUDIT_BASE_DN: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.audit_basedn );
+                       break;
+               }
+               case RBAC_ADMIN_DN: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.admin );
+                       break;
+               }
+               case RBAC_ADMIN_PWD: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.pwd );
+                       break;
+               }
+               case RBAC_SESSION_ADMIN_DN: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.session_admin );
+                       break;
+               }
+               case RBAC_SESSION_ADMIN_PWD: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.session_admin_pwd );
+                       break;
+               }
+               case RBAC_DEFAULT_TENANT_ID: {
+                       ber_str2bv( c->argv[1], 0, 1, &ri->tenant_info.tid );
+                       break;
+               }
+               case RBAC_TENANT: {
+                       rbac_tenant_parse( c->argv[1], &ri->tenant_info );
+                       break;
+               }
+               default:
+                       break;
+       }
+
+       return rc;
+}
+
+/*
+ * rbac configuration
+ */
+
+tenant_info_t *
+rbac_tid2tenant( struct berval *tid )
+{
+       /* return the only tenant for now */
+       return &rbac_tenants.tenant_info;
+}
+
+//{ BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES), rbac_session_roles },
+
+static int
+slap_parse_rbac_session_roles(
+               struct berval *in,
+               rbac_req_t **reqpp,
+               const char **text,
+               void *ctx )
+{
+       int rc = LDAP_SUCCESS;
+       struct berval reqdata = BER_BVNULL;
+       rbac_req_t *reqp = NULL;
+       BerElementBuffer berbuf;
+       BerElement *ber = (BerElement *)&berbuf;
+       ber_tag_t tag;
+       ber_len_t len = -1;
+
+       *text = NULL;
+
+       if ( in == NULL || in->bv_len == 0 ) {
+               *text = "empty request data field in rbac_session_roles exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       reqp = rbac_alloc_req( RBAC_REQ_SESSION_ROLES );
+
+       if ( !reqp ) {
+               *text = "unable to allocate memory for rbac_session_roles exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       ber_dupbv_x( &reqdata, in, ctx );
+
+       /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+       ber_init2( ber, &reqdata, 0 );
+
+       tag = ber_scanf( ber, "{" /*}*/ );
+
+       if ( tag == LBER_ERROR ) {
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+                               "decoding error.\n" );
+               goto decoding_error;
+       }
+
+       tag = ber_peek_tag( ber, &len );
+       if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+               struct berval uid;
+               tag = ber_scanf( ber, "m", &uid );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+                                       "user id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->uid, &uid, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       //tag = ber_peek_tag( ber, &len );
+       if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+               struct berval sessid;
+               tag = ber_scanf( ber, "m", &sessid );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+                                       "session id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->sessid, &sessid, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_session_roles: "
+                               "decoding error, len=%ld\n",
+                               (long)len );
+               rc = LDAP_PROTOCOL_ERROR;
+               *text = "data decoding error";
+       }
+
+       if ( rc == LDAP_SUCCESS ) {
+               *reqpp = reqp;
+       } else {
+               rbac_free_req( reqp );
+               *reqpp = NULL;
+       }
+
+       if ( !BER_BVISNULL( &reqdata ) ) {
+               ber_memfree_x( reqdata.bv_val, ctx );
+       }
+
+       return rc;
+}
+
+static int
+rbac_session_roles( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       const struct berval rbac_op = BER_BVC("SessionRoles");
+       rbac_req_t *reqp = NULL;
+       rbac_session_t *sessp;
+       int rc;
+
+       rs->sr_err = slap_parse_rbac_session_roles(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       /* get the session using the session id */
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+                               "session not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* checking whether the session is owned by the user */
+       if ( !rbac_is_session_owner( sessp, reqp ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+                               "session not owned by user\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       rc = rbac_int_delete_session( op, sessp );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+                               "unable to delete session\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /*
+        * If we wanted to...
+        * load these roles into a response with a sequence nested within a
+        * sequence: (No, we're not actually doing this here.)
+        *              0x30 LL         ber_printf( ber,  "{" );
+        *                      0x04 L1
+        *                              0x04 L2 a b c d
+        *                              0x04 L3 e f g h
+        *                              0x04 L4 i j k l
+        * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+        * close it      ber_printf( ber, "t{W}", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+        */
+
+       /*
+        * Instead we are...
+        * loading these roles into the response within a sequence:  (Yes, we are doing this here.)
+        *              0x30 LL         ber_printf( ber,  "{" );
+        *                      0x04 L1 a b c d
+        *                      0x04 L2 e f g h
+        *                      0x04 L3 i j k l
+        *      add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+        *      close it      ber_printf( ber, "tW", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+        */
+       BerElementBuffer berbuf;
+       BerElement *ber = (BerElement *)&berbuf;
+       ber_init_w_nullc( ber, LBER_USE_DER );
+       BerVarray roles = NULL;
+       if ( sessp->roles ) {
+               struct berval tmpbv;
+               // open the sequence:
+               ber_printf( ber, "{" /*}*/ );
+               //char *role;
+               int i = 0;
+               //BerVarray roles = NULL;
+               for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+                       tmpbv.bv_val = sessp->roles[i].bv_val;
+                       tmpbv.bv_len = sessp->roles[i].bv_len;
+                       // add role name:
+                       ber_bvarray_add_x( &roles, &tmpbv, NULL );
+
+                       //LBER_F( int )
+                       //ber_bvecadd_x LDAP_P(( struct berval ***bvec,
+                       //      struct berval *bv, void *ctx ));
+
+                       // first attempt at sequence within a sequence...
+                       // open another sequence:
+                       /*
+                       ber_printf( ber, "{" } );
+                       // add role name (again):
+                       ber_bvarray_add_x(&roles, &tmpbv, NULL);
+                       // close the nested sequence:
+                       ber_printf( ber, { "}" );
+*/
+                       // end 2nd sequence
+               }
+               /*
+                * This is how we add several octet strings at one time. An array of struct berval's is supplied.
+                * The array is terminated by a struct berval with a NULL bv_val.
+                * Note that a construct like '{W}' is required to get an actual SEQUENCE OF octet strings.
+                * But here we are using 'tW' which allows passing a collection of octets w/out a nesting within a sequence.
+         */
+               ber_printf( ber, "tW",
+                               LDAP_TAG_EXOP_RBAC_ROLES, roles);
+
+               // TODO: determine why free on roles array causes a seg fault:
+               //ber_bvarray_free_x(roles, NULL);
+
+               // close the sequence:
+               ber_printf( ber, /*{*/ "N}" );
+       }
+
+       if ( rc < 0 ) {
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+       } else {
+               (void)ber_flatten( ber, &rs->sr_rspdata );
+               rs->sr_err = LDAP_SUCCESS;
+       }
+       ber_free_buf( ber );
+       // END LOAD ROLES INTO RESPONSE
+
+done:;
+       rs->sr_err = rc;
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_SESSION_ROLES.bv_val );
+
+       /* generate audit log */
+       rbac_audit(
+                       op, SessionRoles, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+       rbac_free_session( sessp );
+       rbac_free_req( reqp );
+       return rs->sr_err;
+}
+
+static int
+rbac_session_rolesx( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       const struct berval rbac_op = BER_BVC("SessionRoles");
+       rbac_session_t *sessp = NULL;
+       rbac_req_t *reqp = NULL;
+       int rc;
+
+       rs->sr_err = slap_parse_rbac_session_roles(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       /* get the session using the session id */
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+                               "session not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* checking whether the session is owned by the user */
+       if ( !rbac_is_session_owner( sessp, reqp ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+                               "session not owned by user\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       rc = rbac_int_delete_session( op, sessp );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_roles: "
+                               "unable to delete session\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /*
+        * If we wanted to...
+        * load these roles into a response with a sequence nested within a
+        * sequence: (No, we're not actually doing this here.)
+        *              0x30 LL         ber_printf( ber,  "{" );
+        *                      0x04 L1
+        *                              0x04 L2 a b c d
+        *                              0x04 L3 e f g h
+        *                              0x04 L4 i j k l
+        * add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+        * close it      ber_printf( ber, "t{W}", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+        */
+
+       /*
+        * Instead we are...
+        * loading these roles into the response within a sequence:  (Yes, we are doing this here.)
+        *              0x30 LL         ber_printf( ber,  "{" );
+        *                      0x04 L1 a b c d
+        *                      0x04 L2 e f g h
+        *                      0x04 L3 i j k l
+        *      add all three ber_bvarray_add_x( &roles, &tmpbv, NULL );
+        *      close it      ber_printf( ber, "tW", LDAP_TAG_EXOP_RBAC_ROLES, roles );
+        */
+       BerElementBuffer berbuf;
+       BerElement *ber = (BerElement *)&berbuf;
+       ber_init_w_nullc( ber, LBER_USE_DER );
+       BerVarray roles = NULL;
+       if ( sessp->roles ) {
+               struct berval tmpbv;
+               // open the sequence:
+               ber_printf( ber, "{" /*}*/ );
+               //char *role;
+               int i = 0;
+               //BerVarray roles = NULL;
+               for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+                       tmpbv.bv_val = sessp->roles[i].bv_val;
+                       tmpbv.bv_len = sessp->roles[i].bv_len;
+                       // add role name:
+                       ber_bvarray_add_x( &roles, &tmpbv, NULL );
+
+                       // first attempt at sequence within a sequence...
+                       // open another sequence:
+                       /*
+                       ber_printf( ber, "{" } );
+                       // add role name (again):
+                       ber_bvarray_add_x(&roles, &tmpbv, NULL);
+                       // close the nested sequence:
+                       ber_printf( ber, { "}" );
+*/
+                       // end 2nd sequence
+               }
+               /*
+                * This is how we add several octet strings at one time. An array of struct berval's is supplied.
+                * The array is terminated by a struct berval with a NULL bv_val.
+                * Note that a construct like '{W}' is required to get an actual SEQUENCE OF octet strings.
+                * But here we are using 'tW' which allows passing a collection of octets w/out a nesting within a sequence.
+         */
+               ber_printf( ber, "tW",
+                               LDAP_TAG_EXOP_RBAC_ROLES, roles);
+
+               // TODO: determine why free on roles array causes a seg fault:
+               //ber_bvarray_free_x(roles, NULL);
+
+               // close the sequence:
+               ber_printf( ber, /*{*/ "N}" );
+       }
+
+       if ( rc < 0 ) {
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+       } else {
+               (void)ber_flatten( ber, &rs->sr_rspdata );
+               rs->sr_err = LDAP_SUCCESS;
+       }
+       ber_free_buf( ber );
+       // END LOAD ROLES INTO RESPONSE
+
+done:;
+       rs->sr_err = rc;
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_SESSION_ROLES.bv_val );
+
+       /* generate audit log */
+       rbac_audit(
+                       op, SessionRoles, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+       rbac_free_session( sessp );
+       rbac_free_req( reqp );
+       return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_create_session
+ */
+static int
+slap_parse_rbac_create_session(
+               struct berval *in,
+               rbac_req_t **reqpp,
+               const char **text,
+               void *ctx )
+{
+       int rc = LDAP_SUCCESS;
+       struct berval reqdata = BER_BVNULL;
+       rbac_req_t *reqp = NULL;
+       BerElementBuffer berbuf;
+       BerElement *ber = (BerElement *)&berbuf;
+       ber_tag_t tag;
+       ber_len_t len = -1;
+
+       *text = NULL;
+
+       if ( in == NULL || in->bv_len == 0 ) {
+               *text = "empty request data field in rbac_create_session exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       reqp = rbac_alloc_req( RBAC_REQ_CREATE_SESSION );
+
+       if ( !reqp ) {
+               *text = "unable to allocate memory for bac_create_session exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       ber_dupbv_x( &reqdata, in, ctx );
+
+       /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+       ber_init2( ber, &reqdata, 0 );
+
+       tag = ber_scanf( ber, "{" /*}*/ );
+
+       if ( tag == LBER_ERROR ) {
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                               "decoding error.\n" );
+               goto decoding_error;
+       }
+
+       // Order: 1. sessionId, 2. tenantId, 3. userId, 4. password and 5. roles
+       /* must-have */
+       tag = ber_peek_tag( ber, &len );
+
+       // 1. SESSIONID
+       if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID ) {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                                       "session id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->sessid, &bv, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       // 2. TENANT ID
+       if ( tag == LDAP_TAG_EXOP_RBAC_TENANT_ID ) {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                                       "tenant id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->tenantid, &bv, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       // 3. USERID
+       if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID ) {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                                       "user id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->uid, &bv, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       // 4. PASSWORD
+       if ( tag == LDAP_TAG_EXOP_RBAC_AUTHTOK ) {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv);
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                                       "authtok parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->authtok, &bv, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       // 5. ROLES
+       if ( tag == LDAP_TAG_EXOP_RBAC_ACTIVE_ROLE ) {
+               tag = ber_scanf( ber, "W", &reqp->roles);
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                                       "role parse failed.\n" );
+                       goto decoding_error;
+               }
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                               "decoding error, len=%ld\n",
+                               (long)len );
+               rc = LDAP_PROTOCOL_ERROR;
+               *text = "data decoding error";
+       }
+
+       if ( rc == LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_create_session: "
+                               "SUCCESS\n" );
+
+               *reqpp = reqp;
+       } else {
+               Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_create_session: "
+                               "NO SUCCESS RC=%d\n", rc );
+
+               rbac_free_req( reqp );
+               *reqpp = NULL;
+       }
+
+       if ( !BER_BVISNULL( &reqdata ) ) {
+               ber_memfree_x( reqdata.bv_val, ctx );
+       }
+
+       return rc;
+}
+
+/*
+ * rbac_create_session:
+ *     1. authenticate user
+ *     2. evaluate pwd policy
+ *     3. create session
+ */
+static int
+rbac_create_session( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       struct berval rbac_op = BER_BVC("CreateSession");
+       BerElementBuffer berbuf;
+       BerElement *ber = (BerElement *)&berbuf;
+       int rc = LDAP_SUCCESS;
+       rbac_session_t *sessp = NULL;
+       rbac_user_t *userp = NULL;
+       rbac_req_t *reqp = NULL;
+
+       rs->sr_err = slap_parse_rbac_create_session(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       /* read user entry */
+       userp = rbac_read_user( op, reqp );
+       if ( !userp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+                               "unable to read user entry\n" );
+               rs->sr_err = LDAP_NO_SUCH_OBJECT;
+               rs->sr_text = "rbac_create_session: unable to read user entry";
+               goto done;
+       }
+
+       if ( !BER_BVISNULL( &userp->password ) ) {
+               /* if request is with pwd, authenticate the user */
+               rc = rbac_authenticate_user( op, userp );
+               if ( rc != LDAP_SUCCESS ) {
+                       Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+                                       "rbac_authenticate_user failed!\n" );
+                       rs->sr_err = LDAP_INVALID_CREDENTIALS;
+                       rs->sr_text = "rbac_create_session: invalid credential";
+                       goto done;
+               }
+               /*
+               rbac_user_t *ui = op->o_callback->sc_private;
+               int pVal = ui->authz;
+               printf("password reset val=%d", pVal );
+*/
+
+       } else {
+               /* no pwd is provided, check whether the requesting session */
+               /* id has the access privilege to create a session on behalf */
+               /* of the user */
+               rc = rbac_create_session_acl_check( &reqp->sessid, userp );
+               if ( rc != LDAP_SUCCESS ) {
+                       Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+                                       "rbac_authenticate_user failed!\n" );
+                       rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+                       rs->sr_text = "rbac_create_session: session permission denied";
+                       goto done;
+               }
+       }
+
+       /* check user temporal constraint */
+       rc = rbac_user_temporal_constraint( userp );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+                               "rbac_user_temporal_constraint() failed!\n" );
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_text = "rbac_create_session: temporal constraint violation";
+               goto done;
+       }
+
+       sessp = rbac_alloc_session();
+       if ( !sessp ) {
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_text = "rbac_create_session: unable to allocate session";
+               goto done;
+       }
+
+       rc = activate_session_roles( sessp, reqp, userp );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+                               "failed to activate roles to session!\n" );
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_text =
+                               "rbac_create_session: failed to activate roles into session";
+               goto done;
+       }
+
+       /* store uid and tenant id in session */
+       ber_dupbv( &sessp->userdn, &userp->dn );
+       ber_dupbv( &sessp->uid, &reqp->uid );
+       ber_dupbv( &sessp->tenantid, &reqp->tenantid );
+
+       /* register RBAC session */
+       rc = rbac_register_session( op, rs, sessp );
+       if ( rc != LDAP_SUCCESS ) {
+               goto done;
+       }
+
+       ber_init_w_nullc( ber, LBER_USE_DER );
+       rc = ber_printf( ber, "{tO}", LDAP_TAG_EXOP_RBAC_SESSION_ID,
+                       &sessp->sessid );
+       if ( rc < 0 ) {
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+       } else {
+               (void)ber_flatten( ber, &rs->sr_rspdata );
+               rs->sr_rspoid = ch_strdup( slap_EXOP_CREATE_SESSION.bv_val );
+               rs->sr_err = LDAP_SUCCESS;
+       }
+
+       ber_free_buf( ber );
+
+done:;
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_CREATE_SESSION.bv_val );
+       /* generate audit log */
+       rbac_audit(
+                       op, CreateSession, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+       rbac_free_req( reqp );
+       rbac_free_session( sessp );
+
+       //if (rs->sr_err != LDAP_SUCCESS) {
+       //send_ldap_result( op, rs );
+       //}
+
+       return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_check_access
+ */
+static int
+slap_parse_rbac_check_access(
+               struct berval *in,
+               rbac_req_t **reqpp,
+               const char **text,
+               void *ctx )
+{
+       int rc = LDAP_SUCCESS;
+       struct berval reqdata = BER_BVNULL;
+       rbac_req_t *reqp = NULL;
+       BerElementBuffer berbuf;
+       BerElement *ber = (BerElement *)&berbuf;
+       ber_tag_t tag;
+       ber_len_t len;
+
+       *text = NULL;
+       reqp = rbac_alloc_req( RBAC_REQ_CHECK_ACCESS );
+
+       if ( !reqp ) {
+               *text = "unable to allocate memory for slap_parse_rbac_check_access";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       if ( in == NULL || in->bv_len == 0 ) {
+               *text = "empty request data field in rbac_check_access exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       ber_dupbv_x( &reqdata, in, ctx );
+
+       /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+       ber_init2( ber, &reqdata, 0 );
+
+       tag = ber_scanf( ber, "{" /*}*/ );
+
+       if ( tag == LBER_ERROR ) {
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                               "decoding error.\n" );
+               goto decoding_error;
+       }
+
+       // sessionId is required:
+       tag = ber_peek_tag( ber, &len );
+       if ( tag != LDAP_TAG_EXOP_RBAC_SESSION_ID ) {
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                               "decoding error.\n" );
+               goto decoding_error;
+       } else {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                                       "session id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->sessid, &bv, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       // operationName is required:
+       if ( tag != LDAP_TAG_EXOP_RBAC_OPNAME ) {
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                               "decoding error.\n" );
+               goto decoding_error;
+       } else {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                                       "opname parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->opname, &bv, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       // objectName is required:
+       if ( tag != LDAP_TAG_EXOP_RBAC_OBJNAME ) {
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                               "decoding error.\n" );
+               goto decoding_error;
+       } else {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                                       "objname parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->objname, &bv, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       // objectId is optional:
+       if ( tag == LDAP_TAG_EXOP_RBAC_OBJID ) {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                                       "objid parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->objid, &bv, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_check_access: "
+                               "decoding error, len=%ld\n",
+                               (long)len );
+               rc = LDAP_PROTOCOL_ERROR;
+               *text = "data decoding error";
+       }
+
+       if ( rc == LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_check_access: "
+                               "SUCCESS\n" );
+               *reqpp = reqp;
+       } else {
+               Debug( LDAP_DEBUG_ANY, "slap_parse_rbac_check_access: "
+                               "FAIL\n" );
+               rbac_free_req( reqp );
+       }
+
+       if ( !BER_BVISNULL( &reqdata ) ) {
+               ber_memfree_x( reqdata.bv_val, ctx );
+       }
+
+       return rc;
+}
+
+// checkAcess F  (ALL)
+static int
+rbac_check_access( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_session_t *sessp = NULL;
+       rbac_permission_t *permp = NULL;
+       rbac_constraint_t *cp = NULL;
+       rbac_req_t *reqp = NULL;
+       const struct berval rbac_op = BER_BVC("CheckAccess");
+       int rc = LDAP_SUCCESS;
+       int found = 0;
+
+       rs->sr_err = slap_parse_rbac_check_access(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       BER_BVZERO( &op->o_req_dn );
+       BER_BVZERO( &op->o_req_ndn );
+
+       /* get the session using the session id */
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+                               "session not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* read the permission using objectName and OpName */
+       permp = rbac_read_permission( op, reqp );
+       if ( !permp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+                               "permission not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       // Convert the user-role constraint data from BerVarray to rbac_constraint_t format
+       cp = rbac_user_role_constraints( sessp->role_constraints );
+
+       // Now do the actual rbac checkAccess:
+       rc = rbac_check_session_permission( sessp, permp, cp );
+
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_user_permission: "
+                               "failed\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+done:
+
+       rs->sr_err = rc;
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+
+       /* generate audit log */
+       rbac_audit( op, CheckAccess, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+       rbac_free_permission( permp );
+       rbac_free_req( reqp );
+       rbac_free_session( sessp );
+       rbac_free_constraints( cp );
+
+       return rs->sr_err;
+}
+
+// checkAcess A loop back
+static int
+rbac_check_accessA( Operation *op, SlapReply *rs )
+{
+       int rc = LDAP_SUCCESS;
+
+       //rs->sr_err = slap_parse_rbac_check_access(op->ore_reqdata,
+       //      &reqp, &rs->sr_text, NULL);
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+       rs->sr_err = rc;
+
+       return rc;
+}
+
+// checkAcess B parse
+static int
+rbac_check_accessB( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_req_t *reqp = NULL;
+       const struct berval rbac_op = BER_BVC("CheckAccess");
+       int rc = LDAP_SUCCESS;
+
+       Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+       rs->sr_err = slap_parse_rbac_check_access(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       BER_BVZERO( &op->o_req_dn );
+       BER_BVZERO( &op->o_req_ndn );
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+       rs->sr_err = rc;
+
+       rbac_free_req( reqp );
+
+       return rc;
+}
+
+// checkAcess C - parse request & read session record
+static int
+rbac_check_accessC( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_session_t *sessp = NULL;
+       rbac_req_t *reqp = NULL;
+       const struct berval rbac_op = BER_BVC("CheckAccess");
+       int rc = LDAP_SUCCESS;
+
+       Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+       rs->sr_err = slap_parse_rbac_check_access(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       BER_BVZERO( &op->o_req_dn );
+       BER_BVZERO( &op->o_req_ndn );
+
+       /* get the session using the session id */
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+                               "session not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+done:
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+       rs->sr_err = rc;
+
+       rbac_free_req( reqp );
+       rbac_free_session( sessp );
+       return rc;
+}
+
+// checkAcess D, parse, read perm
+static int
+rbac_check_accessD( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_permission_t *permp = NULL;
+       rbac_req_t *reqp = NULL;
+       const struct berval rbac_op = BER_BVC("CheckAccess");
+       int rc = LDAP_SUCCESS;
+
+       Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+       rs->sr_err = slap_parse_rbac_check_access(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       BER_BVZERO( &op->o_req_dn );
+       BER_BVZERO( &op->o_req_ndn );
+
+       /* get the session using the session id */
+       /*
+       sessp = rbac_session_byid(op, reqp);
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+                               "session not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+*/
+
+       /* read the permission using objectName and OpName */
+       permp = rbac_read_permission( op, reqp );
+       if ( !permp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+                               "permission not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+done:
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+       rs->sr_err = rc;
+
+       rbac_free_permission( permp );
+       rbac_free_req( reqp );
+
+       return rc;
+}
+
+// checkAcess E everything but the audit insert
+static int
+rbac_check_accessE( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_session_t *sessp = NULL;
+       rbac_permission_t *permp = NULL;
+       rbac_constraint_t *cp = NULL;
+       rbac_req_t *reqp = NULL;
+       const struct berval rbac_op = BER_BVC("CheckAccess");
+       int rc = LDAP_SUCCESS;
+
+       Debug( LDAP_DEBUG_ANY, "rbac_check_access\n" );
+
+       rs->sr_err = slap_parse_rbac_check_access(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       BER_BVZERO( &op->o_req_dn );
+       BER_BVZERO( &op->o_req_ndn );
+
+       /* get the session using the session id */
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+                               "session not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* read the permission using objectName and OpName */
+       permp = rbac_read_permission( op, reqp );
+       if ( !permp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_access: "
+                               "permission not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       // Convert the user-role constraint data from BerVarray to rbac_constraint_t format
+       cp = rbac_user_role_constraints( sessp->role_constraints );
+
+       // Now do the actual rbac checkAccess:
+       rc = rbac_check_session_permission( sessp, permp, cp );
+
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_check_user_permission: "
+                               "failed\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+done:
+
+       rs->sr_err = rc;
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_CHECK_ACCESS.bv_val );
+
+       /* generate audit log */
+       //rbac_audit(op, CheckAccess, sessp, reqp, rs->sr_err,
+       //      (char *) rs->sr_text);
+
+       rbac_free_permission( permp );
+       rbac_free_req( reqp );
+       rbac_free_session( sessp );
+       rbac_free_constraints( cp );
+
+       return rs->sr_err;
+}
+
+/* check whether role exists and role assigned to the user */
+static int
+rbac_check_user_role(
+               rbac_req_t *reqp,
+               rbac_session_t *sessp,
+               rbac_user_t *userp )
+{
+       int rc = 0;
+       int i;
+
+       //assert(!BER_BVISEMPTY(&reqp->roles[0]));
+       assert( !BER_BVISEMPTY( &reqp->role ) );
+
+       for ( i = 0; !BER_BVISNULL( &userp->roles[i] ); i++ ) {
+               //if (!ber_bvstrcasecmp(&userp->roles[i], &reqp->roles[0])) {
+               if ( !ber_bvstrcasecmp( &userp->roles[i], &reqp->role ) ) {
+                       rc = 1; /* found the match */
+                       goto done;
+               }
+       }
+
+done:;
+
+       return rc;
+}
+
+/* check whether role exists and role assigned to the session */
+static int
+rbac_check_session_role( rbac_req_t *reqp, rbac_session_t *sessp )
+{
+       int rc = 0;
+       int i;
+
+       for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+               //if (!ber_bvstrcasecmp(&sessp->roles[i], &reqp->roles[0])) {
+               if ( !ber_bvstrcasecmp( &sessp->roles[i], &reqp->role ) ) {
+                       rc = 1; /* found the match */
+                       goto done;
+               }
+       }
+
+done:;
+
+       return rc;
+}
+
+/* make sure user is the owner of the session */
+static int
+rbac_check_user_session( rbac_session_t *sessp, rbac_req_t *reqp )
+{
+       int rc = 0;
+
+       if ( BER_BVISNULL( &sessp->uid ) || BER_BVISNULL( &reqp->uid ) ||
+                       sessp->uid.bv_len != reqp->uid.bv_len ) {
+               goto done;
+       }
+
+       if ( !strncasecmp(
+                               sessp->uid.bv_val, reqp->uid.bv_val, reqp->uid.bv_len ) ) {
+               rc = 1;
+               goto done;
+       }
+
+done:;
+
+       return rc;
+}
+
+/*
+ * slap_parse_rbac_active_role
+ */
+static int
+slap_parse_rbac_active_role(
+               struct berval *in,
+               int add_or_drop_role,
+               rbac_req_t **reqpp,
+               const char **text,
+               void *ctx )
+{
+       int rc = LDAP_SUCCESS;
+       struct berval reqdata = BER_BVNULL;
+       rbac_req_t *reqp = NULL;
+       BerElementBuffer berbuf;
+       BerElement *ber = (BerElement *)&berbuf;
+       ber_tag_t tag;
+       ber_len_t len = -1;
+
+       *text = NULL;
+
+       if ( in == NULL || in->bv_len == 0 ) {
+               *text = "empty request data field in rbac_create_session exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       reqp = rbac_alloc_req( add_or_drop_role );
+
+       if ( !reqp ) {
+               *text = "unable to allocate memory for rbac_add_drop_active_role exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       ber_dupbv_x( &reqdata, in, ctx );
+
+       /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+       ber_init2( ber, &reqdata, 0 );
+
+       tag = ber_scanf( ber, "{" /*}*/ );
+
+       if ( tag == LBER_ERROR ) {
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+                               "decoding error.\n" );
+               goto decoding_error;
+       }
+
+       tag = ber_peek_tag( ber, &len );
+       //if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID ) {
+       if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+                                       "user id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv( &reqp->uid, &bv );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_active_role: "
+                                       "session id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv( &reqp->sessid, &bv );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       if ( tag == LDAP_TAG_EXOP_RBAC_ROLE_NM_SESS ) {
+               struct berval bv;
+               tag = ber_scanf( ber, "m", &bv );
+               //tag = ber_scanf( ber, "W", &reqp->roles);
+               //tag = ber_scanf( ber, "m", &reqp->roles);
+               //tag = ber_scanf( ber, "m", &reqp->roles[0]);
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                                       "role parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv( &reqp->role, &bv );
+               //ber_dupbv(&reqp->roles[0], &bv);
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_create_session: "
+                               "decoding error, len=%ld\n",
+                               (long)len );
+               rc = LDAP_PROTOCOL_ERROR;
+               *text = "data decoding error";
+       }
+
+       if ( rc == LDAP_SUCCESS ) {
+               *reqpp = reqp;
+       } else {
+               rbac_free_req( reqp );
+               *reqpp = NULL;
+       }
+
+       if ( !BER_BVISNULL( &reqdata ) ) {
+               ber_memfree_x( reqdata.bv_val, ctx );
+       }
+
+       return rc;
+}
+
+static int
+rbac_add_active_role( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       struct berval rbac_op = BER_BVC("AddActiveRole");
+       rbac_req_t *reqp = NULL;
+       rbac_user_t *userp = NULL;
+       rbac_session_t *sessp;
+       int rc = LDAP_SUCCESS;
+
+       rs->sr_err = slap_parse_rbac_active_role( op->ore_reqdata,
+                       RBAC_REQ_ADD_ACTIVE_ROLE, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       /* get the session using the session id */
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+                               "session not found\n" );
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_text = "rbac_add_active_role: session not found";
+               goto done;
+       }
+
+       /* read user entry */
+       userp = rbac_read_user( op, reqp );
+       if ( !userp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+                               "unable to read user entry\n" );
+               rs->sr_err = LDAP_NO_SUCH_OBJECT;
+               rs->sr_text = "rbac_add_active_role: unable to read user entry";
+               goto done;
+       }
+
+       /* make sure role exists and role assigned to the user */
+       if ( !rbac_check_user_role( reqp, sessp, userp ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+                               "role not assigned to the user\n" );
+               rs->sr_err = LDAP_NO_SUCH_OBJECT;
+               rs->sr_text = "rbac_add_active_role: role not assigned to the user";
+               goto done;
+       }
+
+       /* make sure user is the owner of the session */
+       if ( !rbac_check_user_session( sessp, reqp ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+                               "user not owner of session\n" );
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_text = "rbac_add_active_role: user not owner of the session";
+               goto done;
+       }
+
+       /* add the role to the session */
+       rc = rbac_session_add_role( op, sessp, reqp );
+       if ( rc != LDAP_SUCCESS ) {
+               rs->sr_err = rc;
+               if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+                       rs->sr_text =
+                                       "rbac_add_active_role: role already activated in session";
+                       Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+                                       "role already activated in session\n" );
+               } else {
+                       rs->sr_text = "rbac_add_active_role: unable to add role to session";
+                       Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+                                       "unable to add role to session\n" );
+               }
+               goto done;
+       }
+
+done:
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_ADD_ACTIVE_ROLE.bv_val );
+
+       /* generate audit log */
+       rbac_audit(
+                       op, AddActiveRole, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+       rbac_free_session( sessp );
+       rbac_free_user( userp );
+       rbac_free_req( reqp );
+
+       return rs->sr_err;
+}
+
+static int
+rbac_drop_active_role( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       const struct berval rbac_op = BER_BVC("DropActiveRole");
+       rbac_session_t *sessp;
+       rbac_req_t *reqp = NULL;
+       int rc = LDAP_SUCCESS;
+
+       rs->sr_err = slap_parse_rbac_active_role( op->ore_reqdata,
+                       RBAC_REQ_DROP_ACTIVE_ROLE, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       /* get the session using the session id */
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+                               "session not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       if ( BER_BVISNULL( &reqp->role ) || !sessp->roles ||
+                       BER_BVISNULL( &sessp->roles[0] ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+                               "unavailable role\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* make sure role exists and role assigned to the user */
+       if ( !rbac_check_session_role( reqp, sessp ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+                               "role not assigned to session\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* make sure user is the owner of the session */
+       if ( !rbac_check_user_session( sessp, reqp ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+                               "user not owner of session\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_text = "rbac_drop_active_role: user not owner of the session";
+               goto done;
+       }
+
+       /* drop the role to the session */
+       rc = rbac_session_drop_role( op, sessp, reqp );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_drop_active_role: "
+                               "unable to drop active role from session\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               rs->sr_text = "rbac_drop_active_role: unable to drop role from session";
+               goto done;
+       }
+
+done:
+       rs->sr_err = rc;
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_DROP_ACTIVE_ROLE.bv_val );
+
+       /* generate audit log */
+       rbac_audit(
+                       op, DropActiveRole, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+       rbac_free_session( sessp );
+       rbac_free_req( reqp );
+
+       return rs->sr_err;
+}
+
+/*
+ * slap_parse_rbac_delete_session
+ */
+static int
+slap_parse_rbac_delete_session(
+               struct berval *in,
+               rbac_req_t **reqpp,
+               const char **text,
+               void *ctx )
+{
+       int rc = LDAP_SUCCESS;
+       struct berval reqdata = BER_BVNULL;
+       rbac_req_t *reqp = NULL;
+       BerElementBuffer berbuf;
+       BerElement *ber = (BerElement *)&berbuf;
+       ber_tag_t tag;
+       ber_len_t len = -1;
+
+       *text = NULL;
+
+       if ( in == NULL || in->bv_len == 0 ) {
+               *text = "empty request data field in rbac_delete_session exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       reqp = rbac_alloc_req( RBAC_REQ_DELETE_SESSION );
+
+       if ( !reqp ) {
+               *text = "unable to allocate memory for rbac_delete_session exop";
+               return LDAP_PROTOCOL_ERROR;
+       }
+
+       ber_dupbv_x( &reqdata, in, ctx );
+
+       /* ber_init2 uses reqdata directly, doesn't allocate new buffers */
+       ber_init2( ber, &reqdata, 0 );
+
+       tag = ber_scanf( ber, "{" /*}*/ );
+
+       if ( tag == LBER_ERROR ) {
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+                               "decoding error.\n" );
+               goto decoding_error;
+       }
+
+       tag = ber_peek_tag( ber, &len );
+       if ( tag == LDAP_TAG_EXOP_RBAC_USER_ID_SESS ) {
+               struct berval uid;
+               tag = ber_scanf( ber, "m", &uid );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+                                       "user id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->uid, &uid, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       //tag = ber_peek_tag( ber, &len );
+       if ( tag == LDAP_TAG_EXOP_RBAC_SESSION_ID_SESS ) {
+               struct berval sessid;
+               tag = ber_scanf( ber, "m", &sessid );
+               if ( tag == LBER_ERROR ) {
+                       Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+                                       "session id parse failed.\n" );
+                       goto decoding_error;
+               }
+               ber_dupbv_x( &reqp->sessid, &sessid, ctx );
+               tag = ber_peek_tag( ber, &len );
+       }
+
+       if ( tag != LBER_DEFAULT || len != 0 ) {
+decoding_error:;
+
+               Debug( LDAP_DEBUG_TRACE, "slap_parse_rbac_delete_session: "
+                               "decoding error, len=%ld\n",
+                               (long)len );
+               rc = LDAP_PROTOCOL_ERROR;
+               *text = "data decoding error";
+       }
+
+       if ( rc == LDAP_SUCCESS ) {
+               *reqpp = reqp;
+       } else {
+               rbac_free_req( reqp );
+               *reqpp = NULL;
+       }
+
+       if ( !BER_BVISNULL( &reqdata ) ) {
+               ber_memfree_x( reqdata.bv_val, ctx );
+       }
+
+       return rc;
+}
+
+static int
+rbac_delete_session( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       const struct berval rbac_op = BER_BVC("DeleteSession");
+       rbac_session_t *sessp = NULL;
+       rbac_req_t *reqp = NULL;
+       int rc;
+
+       rs->sr_err = slap_parse_rbac_delete_session(
+                       op->ore_reqdata, &reqp, &rs->sr_text, NULL );
+
+       assert( rs->sr_err == LDAP_SUCCESS );
+
+       /* get the session using the session id */
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_delete_session: "
+                               "session not found\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* checking whether the session is owned by the user */
+       if ( !rbac_is_session_owner( sessp, reqp ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_delete_session: "
+                               "session not owned by user\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       rc = rbac_int_delete_session( op, sessp );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_int_delete_session: "
+                               "unable to delete session\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+done:;
+
+       rs->sr_err = rc;
+
+       // always put the OID in the response:
+       rs->sr_rspoid = ch_strdup( slap_EXOP_DELETE_SESSION.bv_val );
+
+       /* generate audit log */
+       rbac_audit(
+                       op, DeleteSession, sessp, reqp, rs->sr_err, (char *)rs->sr_text );
+
+       rbac_free_session( sessp );
+       rbac_free_req( reqp );
+
+       return rs->sr_err;
+}
+
+/* returns the permissions associated with a session */
+static int
+rbac_session_permissions( Operation *op, SlapReply *rs, rbac_req_t *reqp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       const struct berval rbac_op = BER_BVC("SessionPermissions");
+       rbac_session_t *sessp;
+
+       sessp = rbac_session_byid( op, reqp );
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_permissions: "
+                               "session id not found\n" );
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       rs->sr_err = rbac_int_session_permissions( op, rs, reqp, sessp );
+       if ( rs->sr_err != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_permissions: "
+                               "permissions not found\n" );
+               goto done;
+       }
+
+done:;
+       return rs->sr_err;
+}
+
+/* extract session permission info from op */
+int
+rbac_search_parse_session_permissions_req(
+               Operation *op,
+               rbac_req_t **reqpp,
+               const char **text,
+               void *ctx )
+{
+       int rc = LDAP_SUCCESS;
+       struct berval *sessid = NULL;
+       rbac_req_t *reqp = NULL;
+       *text = NULL;
+       struct berval rbac_session_id = BER_BVC("sessionID");
+       struct berval rbac_session_permissions_attr =
+                       BER_BVC("sessionPermissions");
+       AttributeDescription *ad = NULL;
+       Filter *f;
+
+       /* check simple assertion (sessionID=<session id>) */
+       f = op->ors_filter;
+       ad = f->f_ava->aa_desc;
+       if ( !ad || ber_bvstrcasecmp( &rbac_session_id, &ad->ad_cname ) ) {
+               goto done;
+       }
+       sessid = &f->f_ava->aa_value;
+
+       if ( !rbac_is_valid_session_id( sessid ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_search_parse_session_permissions_req: "
+                               "invalid session id\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* check requested attr */
+
+       if ( !op->oq_search.rs_attrs ||
+                       BER_BVISNULL( &op->oq_search.rs_attrs[0].an_name ) ||
+                       ber_bvstrcasecmp( &op->oq_search.rs_attrs[0].an_name,
+                                       &rbac_session_permissions_attr ) ||
+                       !BER_BVISNULL( &op->oq_search.rs_attrs[1].an_name ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_search_parse_session_permissions_req: "
+                               "only sessionPermissions allowed\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       reqp = rbac_alloc_req( RBAC_REQ_SESSION_PERMISSIONS );
+       if ( !reqp ) {
+               *text = "unable to allocate memory for rbac_session_permissions req";
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* retrieve session id from search filter */
+       ber_dupbv_x( &reqp->sessid, sessid, ctx );
+
+done:;
+
+       if ( rc == LDAP_SUCCESS ) {
+               *reqpp = reqp;
+       } else {
+               rbac_free_req( reqp );
+               *reqpp = NULL;
+       }
+
+       return rc;
+}
+
+static int
+rbac_search( Operation *op, SlapReply *rs )
+{
+       Debug( LDAP_DEBUG_ANY, "rbac_search entry\n" );
+
+       return SLAP_CB_CONTINUE;
+}
+
+/*
+static int
+rbac_search( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_req_t *reqp = NULL;
+       int rc = SLAP_CB_CONTINUE;
+
+        only session_permissions is implemented for now
+       rc = rbac_search_parse_session_permissions_req(
+                       op, &reqp, &rs->sr_text, NULL );
+       if ( !reqp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_search: "
+                               "invalid search for session permissions\n" );
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       rc = rbac_session_permissions( op, rs, reqp );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_search: "
+                               "session permissions failed\n" );
+               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       rs->sr_err = LDAP_SUCCESS;
+
+done:;
+       send_ldap_result( op, rs );
+
+       return rc;
+}
+*/
+
+static struct exop {
+       struct berval oid;
+       BI_op_extended *extended;
+} rbac_exop_table[] = {
+       { BER_BVC(LDAP_RBAC_EXOP_CREATE_SESSION), rbac_create_session },
+       { BER_BVC(LDAP_RBAC_EXOP_CHECK_ACCESS), rbac_check_access },
+       { BER_BVC(LDAP_RBAC_EXOP_ADD_ACTIVE_ROLE), rbac_add_active_role },
+       { BER_BVC(LDAP_RBAC_EXOP_DROP_ACTIVE_ROLE), rbac_drop_active_role },
+       { BER_BVC(LDAP_RBAC_EXOP_DELETE_SESSION), rbac_delete_session },
+       { BER_BVC(LDAP_RBAC_EXOP_SESSION_ROLES), rbac_session_roles },
+
+       { BER_BVNULL, NULL }
+};
+
+static int
+rbac_add( Operation *op, SlapReply *rs )
+{
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_bind( Operation *op, SlapReply *rs )
+{
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_compare( Operation *op, SlapReply *rs )
+{
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_delete( Operation *op, SlapReply *rs )
+{
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_modify( Operation *op, SlapReply *rs )
+{
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_extended( Operation *op, SlapReply *rs )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       int rc = SLAP_CB_CONTINUE;
+       int i;
+
+       for ( i = 0; rbac_exop_table[i].extended != NULL; i++ ) {
+               if ( bvmatch( &rbac_exop_table[i].oid, &op->oq_extended.rs_reqoid ) ) {
+                       rc = rbac_exop_table[i].extended( op, rs );
+                       switch ( rc ) {
+                               case LDAP_SUCCESS:
+                                       break;
+                               case SLAP_CB_CONTINUE:
+                               case SLAPD_ABANDON:
+                                       return rc;
+                               default:
+                                       send_ldap_result( op, rs );
+                                       return rc;
+                       }
+                       break;
+               }
+       }
+
+       return rc;
+}
+
+static int
+rbac_db_init( BackendDB *be, ConfigReply *cr )
+{
+       slap_overinst *on = (slap_overinst *)be->bd_info;
+
+       return 0;
+}
+
+static int
+rbac_db_open( BackendDB *be, ConfigReply *cr )
+{
+       int rc = LDAP_SUCCESS;
+
+       rc = rbac_initialize_tenants( be, cr );
+
+       return rc;
+}
+
+static int
+rbac_db_close( BackendDB *be, ConfigReply *cr )
+{
+       return 0;
+}
+
+int
+rbac_initialize()
+{
+       int rc;
+
+       rc = load_extop2( (struct berval *)&slap_EXOP_CREATE_SESSION,
+                       SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_create_session, 0 );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                               "unable to register rbac_create_session exop: %d\n",
+                               rc );
+               return rc;
+       }
+
+       rc = load_extop2( (struct berval *)&slap_EXOP_CHECK_ACCESS,
+                       SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_check_access, 0 );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                               "unable to register rbac_check_access exop: %d\n",
+                               rc );
+               return rc;
+       }
+
+       rc = load_extop2( (struct berval *)&slap_EXOP_ADD_ACTIVE_ROLE,
+                       SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_add_active_role, 0 );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                               "unable to register rbac_add_active_role exop: %d\n",
+                               rc );
+               return rc;
+       }
+
+       rc = load_extop2( (struct berval *)&slap_EXOP_DROP_ACTIVE_ROLE,
+                       SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_drop_active_role, 0 );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                               "unable to register rbac_drop_active_role exop: %d\n",
+                               rc );
+               return rc;
+       }
+
+       rc = load_extop2( (struct berval *)&slap_EXOP_DELETE_SESSION,
+                       SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_delete_session, 0 );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                               "unable to register rbac_delete_session exop: %d\n",
+                               rc );
+               return rc;
+       }
+
+       rc = load_extop2( (struct berval *)&slap_EXOP_SESSION_ROLES,
+                       SLAP_EXOP_WRITES|SLAP_EXOP_HIDE, rbac_session_roles, 0 );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_initialize: "
+                               "unable to register rbac_session_roles exop: %d\n",
+                               rc );
+               return rc;
+       }
+
+       rbac.on_bi.bi_type = "rbac";
+       rbac.on_bi.bi_db_init = rbac_db_init;
+       rbac.on_bi.bi_db_open = rbac_db_open;
+       rbac.on_bi.bi_db_close = rbac_db_close;
+
+       rbac.on_bi.bi_op_add = rbac_add;
+       rbac.on_bi.bi_op_bind = rbac_bind;
+       rbac.on_bi.bi_op_compare = rbac_compare;
+       rbac.on_bi.bi_op_delete = rbac_delete;
+       rbac.on_bi.bi_op_modify = rbac_modify;
+       rbac.on_bi.bi_op_search = rbac_search;
+       rbac.on_bi.bi_extended = rbac_extended;
+       rbac.on_bi.bi_cf_ocs = rbac_ocs;
+
+       /*      rbac.on_bi.bi_connection_destroy = rbac_connection_destroy; */
+
+       rc = config_register_schema( rbaccfg, rbac_ocs );
+       if ( rc ) return rc;
+
+       rc = rbac_initialize_repository();
+       if ( rc != LDAP_SUCCESS ) {
+               return rc;
+       }
+
+       return overlay_register( &rbac );
+}
+
+int
+init_module( int argc, char *argv[] )
+{
+       return rbac_initialize();
+}
diff --git a/contrib/slapd-modules/rbac/rbac.h b/contrib/slapd-modules/rbac/rbac.h
new file mode 100644 (file)
index 0000000..9c89c8e
--- /dev/null
@@ -0,0 +1,402 @@
+/* rbac.h -  */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2021 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ *
+ */
+
+#ifndef RBAC_H
+#define RBAC_H
+
+LDAP_BEGIN_DECL
+
+#include "ldap_rbac.h"
+
+#define USE_NEW_THREAD_CONTEXT 1
+#define RBAC_BUFLEN 1024
+
+/* tenant initialization op */
+#define INIT_AUDIT_CONTAINER 0x01
+#define INIT_SESSION_CONTAINER 0x02
+
+typedef struct rbac_ad {
+       int type;
+       struct berval attr;
+       AttributeDescription **ad;
+} rbac_ad_t;
+
+/* RBAC AttributeDescriptions */
+struct slap_rbac_internal_schema {
+       /* slapd schema */
+       AttributeDescription *ad_uid;
+
+       /* RBAC tenant */
+       AttributeDescription *ad_tenant_id;
+
+       /* RBAC sessions */
+       AttributeDescription *ad_session_id;
+       AttributeDescription *ad_session_user_dn;
+       AttributeDescription *ad_session_roles;
+       AttributeDescription *ad_session_role_constraints;
+
+       /* RBAC session permissions */
+       AttributeDescription *ad_permission_opname;
+       AttributeDescription *ad_permission_objname;
+       AttributeDescription *ad_permission_rolename;
+
+       /* RBAC audit */
+       AttributeDescription *ad_audit_op; /* rbac op: create_session */
+       AttributeDescription *ad_audit_id;
+       AttributeDescription *ad_audit_roles;
+       AttributeDescription *ad_audit_requested_roles;
+       AttributeDescription *ad_audit_timestamp;
+       AttributeDescription *ad_audit_resources;
+       AttributeDescription *ad_audit_objects;
+       AttributeDescription *ad_audit_operations; /* resource ops */
+       AttributeDescription *ad_audit_result;
+       AttributeDescription *ad_audit_properties;
+       AttributeDescription *ad_audit_messages;
+
+       /* RBAC session attributes */
+       AttributeName *session_attrs;
+};
+
+extern struct slap_rbac_internal_schema slap_rbac_schema;
+
+/* attributes in tenant repository */
+struct slap_rbac_tenant_schema {
+       /* user role assignments, role constraints, and user constraint */
+       AttributeDescription *ad_role;
+       AttributeDescription *ad_role_constraint;
+       AttributeDescription *ad_user_constraint;
+       AttributeDescription *ad_uid;
+
+       /* session permission */
+       AttributeDescription *ad_permission_users;
+       AttributeDescription *ad_permission_roles;
+       AttributeDescription *ad_permission_objname;
+       AttributeDescription *ad_permission_opname;
+
+       /* the list of attributes when doing searches in the jts repo */
+       AttributeName *user_attrs;
+       AttributeName *perm_attrs; /* attrs to retrieve for check access */
+       AttributeName *session_perm_attrs; /* attrs for session permissions */
+
+       /* the corresponding list of attribute description mapping */
+       rbac_ad_t *user_ads;
+       rbac_ad_t *permission_ads;
+       rbac_ad_t *session_permissions_ads;
+};
+
+extern struct slap_rbac_tenant_schema slap_rbac_jts_schema;
+
+/* types of RBAC requests */
+typedef struct rbac_request {
+       int req_type;
+       struct berval sessid;
+       struct berval tenantid;
+
+       /* session creation */
+       struct berval uid;
+       struct berval authtok;
+       BerVarray roles;
+       struct berval role;
+
+       /* check access */
+       struct berval opname;
+       struct berval objname;
+       struct berval objid;
+} rbac_req_t;
+
+typedef struct rbac_constraint {
+       struct berval name; /* user name or role name */
+       int allowed_inactivity; /* secs */
+       int begin_time; /* secs */
+       int end_time; /* secs */
+       lutil_timet begin_date;
+       lutil_timet end_date;
+       lutil_timet begin_lock_date;
+       lutil_timet end_lock_date;
+       int day_mask;
+       struct rbac_constraint *next;
+} rbac_constraint_t;
+
+/* holds RBAC info */
+typedef struct tenant_info {
+       struct berval tid; /* tenant id */
+       struct berval admin;
+       struct berval pwd;
+       struct berval users_basedn;
+       struct berval roles_basedn;
+       struct berval audit_basedn;
+       struct berval permissions_basedn;
+       struct berval sessions_basedn;
+       struct berval session_admin;
+       struct berval session_admin_pwd;
+       struct slap_rbac_tenant_schema *schema;
+} tenant_info_t;
+
+typedef struct rbac_tenant {
+       tenant_info_t tenant_info;
+       struct rbac_tenant *next;
+} rbac_tenant_t;
+
+/* for RBAC callback */
+typedef struct rbac_callback_info {
+       tenant_info_t *tenantp;
+       void *private;
+} rbac_callback_info_t;
+
+/* RBAC user */
+typedef struct rbac_user {
+       struct berval tenantid;
+       struct berval uid;
+       struct berval dn;
+       struct berval constraints;
+       struct berval password;
+       struct berval msg;
+       int authz; /* flag for bind (pwd policy) info */
+       BerVarray roles;
+       BerVarray role_constraints;
+#if 0 /* additional parameters from Fortress */
+       private String userId;
+       @XmlElement(nillable = true)
+               private char[] password;
+       @XmlElement(nillable = true)
+               private char[] newPassword;
+       private String internalId;
+       @XmlElement(nillable = true)
+               private List<UserRole> roles;
+       @XmlElement(nillable = true)
+               private List<UserAdminRole> adminRoles;
+       private String pwPolicy;
+       private String cn;
+       private String sn;
+       private String dn;
+       private String ou;
+       private String description;
+       private String beginTime;
+       private String endTime;
+       private String beginDate;
+       private String endDate;
+       private String beginLockDate;
+       private String endLockDate;
+       private String dayMask;
+       private String name;
+       private int timeout;
+       private boolean reset;
+       private boolean locked;
+       private Boolean system;
+       @XmlElement(nillable = true)
+               private Props props = new Props();
+       @XmlElement(nillable = true)
+               private Address address;
+       @XmlElement(nillable = true)
+               private List<String> phones;
+       @XmlElement(nillable = true)
+               private List<String> mobiles;
+       @XmlElement(nillable = true)
+               private List<String> emails;
+#endif /* 0 */
+} rbac_user_t;
+
+enum {
+       RBAC_NONE = 0,
+       RBAC_TENANT,
+       RBAC_TENANT_ID,
+       RBAC_USERS_BASE_DN,
+       RBAC_ROLES_BASE_DN,
+       RBAC_PERMISSIONS_BASE_DN,
+       RBAC_ADMIN_DN,
+       RBAC_ADMIN_PWD,
+       RBAC_SESSIONS_BASE_DN,
+       RBAC_SESSION_ADMIN_DN,
+       RBAC_SESSION_ADMIN_PWD,
+       RBAC_ROLE_ASSIGNMENT,
+       RBAC_ROLE_CONSTRAINTS,
+       RBAC_USER_CONSTRAINTS,
+       RBAC_UID,
+       RBAC_USERS,
+       RBAC_ROLES,
+       RBAC_OBJ_NAME,
+       RBAC_OP_NAME,
+       RBAC_ROLE_NAME,
+       RBAC_SESSION_ID,
+       RBAC_USER_DN,
+       RBAC_AUDIT_ROLES,
+       RBAC_AUDIT_RESOURCES,
+       RBAC_AUDIT_RESULT,
+       RBAC_AUDIT_TIMESTAMP,
+       RBAC_AUDIT_PROPERTIES,
+       RBAC_AUDIT_OP,
+       RBAC_AUDIT_ID,
+       RBAC_AUDIT_REQUESTED_ROLES,
+       RBAC_AUDIT_OBJS,
+       RBAC_AUDIT_OPS,
+       RBAC_AUDIT_MSGS,
+       RBAC_LAST
+};
+
+enum {
+       RBAC_DEFAULT_TENANT_ID = RBAC_LAST,
+       RBAC_DEFAULT_USERS_BASE_DN,
+       RBAC_DEFAULT_PERMISSIONS_BASE_DN,
+       RBAC_DEFAULT_ROLES_BASE_DN,
+       RBAC_DEFAULT_SESSIONS_BASE_DN,
+       RBAC_DEFAULT_AUDIT_BASE_DN
+};
+
+typedef struct rbac_user_idlist {
+       char *user_id;
+       struct rbac_user_idlist *next;
+} rbac_user_idlist_t;
+
+/* RBAC sessions */
+#define RBAC_SESSION_RDN_EQ "rbacSessid="
+#define RBAC_AUDIT_RDN_EQ "rbacAuditId="
+
+typedef struct rbac_session {
+       rbac_user_t *user;
+       struct berval tenantid;
+       struct berval sessid;
+       struct berval uid;
+       struct berval userdn;
+       char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
+       struct berval sessdn;
+       long last_access;
+       int timeout;
+       int warning_id;
+       int error_id;
+       int grace_logins;
+       int expiration_secs;
+       int is_authenticated; /* boolean */
+       struct berval message;
+       BerVarray roles;
+       BerVarray role_constraints;
+} rbac_session_t;
+
+/* RBAC roles */
+typedef struct rbac_role {
+       char *name;
+       char *description;
+       struct rbac_role *parent;
+       struct rbac_role *next;
+} rbac_role_t;
+
+typedef struct rbac_role_list {
+       char *name;
+       struct rbac_role_list *next;
+} rbac_role_list_t;
+
+/* RBAC permissions */
+typedef struct rbac_permission {
+       struct berval dn;
+       int admin; /* boolean */
+       struct berval internalId;
+       BerVarray opName;
+       BerVarray objName;
+       struct berval objectId;
+       struct berval abstractName;
+       struct berval type;
+       BerVarray roles;
+       BerVarray uids;
+       struct rbac_permission *next;
+} rbac_permission_t;
+
+/* RBAC Audit */
+typedef enum {
+       CreateSession = 0,
+       CheckAccess,
+       AddActiveRole,
+       DropActiveRole,
+       SessionPermissions,
+       DeleteSession,
+       SessionRoles
+} audit_op_t;
+
+/* function prototypes */
+
+int rbac_initialize_repository( void );
+int rbac_initialize_tenants( BackendDB *be, ConfigReply *cr );
+
+/* RBAC tenant information */
+tenant_info_t *rbac_tid2tenant( struct berval *tid );
+
+rbac_req_t *rbac_alloc_req( int type );
+void rbac_free_req( rbac_req_t *reqp );
+
+rbac_user_t *rbac_read_user( Operation *op, rbac_req_t *rabc_reqp );
+int rbac_authenticate_user( Operation *op, rbac_user_t *user );
+int rbac_user_temporal_constraint( rbac_user_t *userp );
+void rbac_free_user( rbac_user_t *user );
+
+rbac_session_t *rbac_alloc_session( void );
+int rbac_is_valid_session_id( struct berval *sessid );
+rbac_session_t *rbac_session_byid( Operation *op, rbac_req_t *reqp );
+int rbac_is_session_owner( rbac_session_t *sessp, rbac_req_t *reqp );
+int rbac_register_session( Operation *op, SlapReply *rs, rbac_session_t *sess );
+int rbac_int_delete_session( Operation *op, rbac_session_t *sessp );
+int rbac_session_add_role(
+       Operation *op,
+       rbac_session_t *sessp,
+       rbac_req_t *reqp );
+int rbac_session_drop_role(
+       Operation *op,
+       rbac_session_t *sessp,
+       rbac_req_t *reqp );
+int rbac_int_session_permissions(
+       Operation *op,
+       SlapReply *rs,
+       rbac_req_t *reqp,
+       rbac_session_t *sessp );
+int activate_session_roles(
+       rbac_session_t *sessp,
+       rbac_req_t *reqp,
+       rbac_user_t *userp );
+void rbac_free_session( rbac_session_t *sessp );
+
+rbac_constraint_t *rbac_user_role_constraints( BerVarray values );
+rbac_constraint_t *rbac_role2constraint(
+       struct berval *role,
+       rbac_constraint_t *role_constraints );
+rbac_constraint_t *rbac_bv2constraint( struct berval *bv );
+int rbac_check_time_constraint( rbac_constraint_t *cp );
+void rbac_free_constraint( rbac_constraint_t *cp );
+void rbac_free_constraints( rbac_constraint_t *constraints );
+
+rbac_permission_t *rbac_read_permission( Operation *op, rbac_req_t *rbac_reqp );
+int rbac_check_session_permission(
+       rbac_session_t *sessp,
+       rbac_permission_t *permp,
+       rbac_constraint_t *role_constraints );
+void rbac_free_permission( rbac_permission_t *permp );
+
+/* audit functions */
+void rbac_audit(
+       Operation *op,
+       audit_op_t rbac_op,
+       rbac_session_t *sessp,
+       rbac_req_t *reqp,
+       int result,
+       char *msg );
+
+/* acl functions */
+int rbac_create_session_acl_check( struct berval *sessid, rbac_user_t *userp );
+
+void rbac_to_lower( struct berval *bv );
+
+LDAP_END_DECL
+
+#endif /* RBAC_H */
diff --git a/contrib/slapd-modules/rbac/rbacacl.c b/contrib/slapd-modules/rbac/rbacacl.c
new file mode 100644 (file)
index 0000000..269dcf5
--- /dev/null
@@ -0,0 +1,37 @@
+/* rbacacl.c - RBAC ACL */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+int
+rbac_create_session_acl_check( struct berval *sessid, rbac_user_t *userp )
+{
+       int rc = LDAP_SUCCESS;
+
+       return rc;
+}
diff --git a/contrib/slapd-modules/rbac/rbacaudit.c b/contrib/slapd-modules/rbac/rbacaudit.c
new file mode 100644 (file)
index 0000000..ef04ece
--- /dev/null
@@ -0,0 +1,233 @@
+/* rbacaudit.c - RBAC Audit */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static struct rbac_audit_op {
+       audit_op_t op;
+       struct berval op_bv;
+} rbac_audit_ops[] = {
+       { CreateSession, BER_BVC("CreateSession") },
+       { CheckAccess, BER_BVC("CheckAccess") },
+       { AddActiveRole, BER_BVC("AddActiveRole") },
+       { DropActiveRole, BER_BVC("DropActiveRole") },
+       { SessionPermissions, BER_BVC("SessionPermissions") },
+       { DeleteSession, BER_BVC("DeleteSession") },
+       { SessionRoles, BER_BVC("SessionRoles") },
+
+       { -1, BER_BVNULL }
+};
+
+static int
+rbac_audit_fake_cb( Operation *op, SlapReply *rs )
+{
+       Debug( LDAP_DEBUG_ANY, "rbac_audit_fake_cb\n" );
+
+       return 0;
+}
+
+void
+rbac_audit(
+               Operation *op,
+               audit_op_t rbac_op,
+               rbac_session_t *sessp,
+               rbac_req_t *reqp,
+               int result,
+               char *msg )
+{
+       int op_idx, rc = LDAP_SUCCESS;
+       int found = 0;
+       struct berval timestamp;
+       tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+       slap_callback cb = { 0 };
+       SlapReply rs2 = { REP_RESULT };
+       Entry *e = NULL;
+       struct berval auditObjectClass = BER_BVC("rbacAudit");
+       struct berval auditResultSuccess = BER_BVC("success");
+       struct berval auditResultFailed = BER_BVC("failed");
+       struct berval bv, rdn, nrdn;
+       char rdnbuf[RBAC_BUFLEN];
+       time_t now;
+       char nowstr[LDAP_LUTIL_GENTIME_BUFSIZE];
+
+       for ( op_idx = 0; rbac_audit_ops[op_idx].op != -1; op_idx++ ) {
+               if ( rbac_op == rbac_audit_ops[op_idx].op ) {
+                       /* legit audit op */
+                       found = 1;
+                       break;
+               }
+       }
+
+       if ( !found ) goto done;
+
+       e = entry_alloc();
+
+       /* audit timestamp */
+       now = slap_get_time(); /* stored for later consideration */
+       timestamp.bv_val = nowstr;
+       timestamp.bv_len = sizeof(nowstr);
+       slap_timestamp( &now, &timestamp );
+
+       /* construct audit record DN; FIXME: random() call  */
+       sprintf( rdnbuf, "%s%d", RBAC_AUDIT_RDN_EQ, (int)op->o_tid );
+       strcat( rdnbuf, "-" );
+       strncat( rdnbuf, timestamp.bv_val, timestamp.bv_len );
+       bv.bv_val = &rdnbuf[0];
+       bv.bv_len = strlen( &rdnbuf[0] );
+
+       rc = dnPrettyNormal( NULL, &bv, &rdn, &nrdn, NULL );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_audit: "
+                               "unable to normalize audit rDN (rc=%d)\n", rc );
+               goto done;
+       }
+
+       /* FIXME: audit_basedn should have been normalized */
+       build_new_dn( &e->e_name, &tenantp->audit_basedn, &rdn, NULL );
+       build_new_dn( &e->e_nname, &tenantp->audit_basedn, &nrdn, NULL );
+
+       ch_free( rdn.bv_val );
+       ch_free( nrdn.bv_val );
+
+       /* add timestamp */
+       attr_merge_one( e, slap_rbac_schema.ad_audit_timestamp, &timestamp, NULL );
+
+       /* add rbac audit objectClass */
+
+       attr_merge_one( e, slap_schema.si_ad_objectClass, &auditObjectClass, NULL );
+       attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+                       &auditObjectClass, NULL );
+
+       /* audit op */
+       attr_merge_one( e, slap_rbac_schema.ad_audit_op,
+                       &rbac_audit_ops[op_idx].op_bv, NULL );
+
+       /* userid */
+       if ( sessp && !BER_BVISNULL( &sessp->uid ) ) {
+               attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+       }
+
+       /* session id */
+
+       if ( sessp && !BER_BVISNULL( &sessp->sessid ) ) {
+               AttributeDescription *ad = NULL;
+               const char *text = NULL;
+               struct berval sessid = BER_BVC("rbacSessid");
+
+               rc = slap_bv2ad( &sessid, &ad, &text );
+               if ( rc != LDAP_SUCCESS ) {
+                       goto done;
+               }
+               attr_merge_one( e, ad, &sessp->sessid, NULL );
+       }
+
+       /* audit result */
+       attr_merge_one( e, slap_rbac_schema.ad_audit_result,
+                       result == LDAP_SUCCESS ? &auditResultSuccess : &auditResultFailed,
+                       NULL );
+
+       switch ( rbac_op ) {
+               case CreateSession:
+                       /* audit roles */
+                       if ( sessp && sessp->roles ) {
+                               attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+                                               NULL );
+                       }
+                       if ( reqp && reqp->roles ) {
+                               attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+                                               reqp->roles, NULL );
+                       }
+                       break;
+
+               case CheckAccess:
+                       if ( sessp && sessp->roles ) {
+                               attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+                                               NULL );
+                       }
+                       if ( reqp && !BER_BVISEMPTY( &reqp->opname ) ) {
+                               attr_merge_one( e, slap_rbac_schema.ad_audit_operations,
+                                               &reqp->opname, NULL );
+                       }
+                       if ( reqp && !BER_BVISEMPTY( &reqp->objname ) ) {
+                               attr_merge_one( e, slap_rbac_schema.ad_audit_objects,
+                                               &reqp->objname, NULL );
+                       }
+                       break;
+
+               case AddActiveRole:
+                       if ( reqp && reqp->roles ) {
+                               attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+                                               reqp->roles, NULL );
+                       }
+                       break;
+
+               case DropActiveRole:
+                       /* audit roles */
+                       if ( reqp && reqp->roles ) {
+                               attr_merge( e, slap_rbac_schema.ad_audit_requested_roles,
+                                               reqp->roles, NULL );
+                       }
+                       break;
+
+               case SessionPermissions:
+                       if ( sessp && sessp->roles ) {
+                               attr_merge( e, slap_rbac_schema.ad_audit_roles, sessp->roles,
+                                               NULL );
+                       }
+                       break;
+
+               case DeleteSession:
+               case SessionRoles:
+               default:
+                       break;
+       }
+
+       /* record the audit record */
+       Operation op2 = *op;
+       rbac_callback_info_t rbac_cb;
+       cb.sc_private = &rbac_cb;
+       cb.sc_response = rbac_audit_fake_cb;
+       op2.o_callback = &cb;
+
+       op2.o_tag = LDAP_REQ_ADD;
+       op2.o_protocol = LDAP_VERSION3;
+       op2.o_req_dn = e->e_name;
+       op2.o_req_ndn = e->e_nname;
+       op2.ora_e = e;
+       op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+       op2.o_dn = op2.o_bd->be_rootdn;
+       op2.o_ndn = op2.o_bd->be_rootndn;
+
+       op2.ors_limit = NULL;
+       rc = op2.o_bd->be_add( &op2, &rs2 );
+
+done:
+       if ( e ) entry_free( e );
+
+       return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacperm.c b/contrib/slapd-modules/rbac/rbacperm.c
new file mode 100644 (file)
index 0000000..e1f6d79
--- /dev/null
@@ -0,0 +1,233 @@
+/* rbacperm.c - RBAC permission */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static int
+rbac_read_permission_cb( Operation *op, SlapReply *rs )
+{
+       rbac_callback_info_t *cbp = op->o_callback->sc_private;
+       rbac_ad_t *permission_ads;
+       rbac_permission_t *permp;
+       int i;
+
+       if ( rs->sr_type != REP_SEARCH ) return 0;
+
+       assert( cbp );
+
+       permp = ch_calloc( 1, sizeof(rbac_permission_t) );
+       permission_ads = cbp->tenantp->schema->permission_ads;
+
+       ber_dupbv( &permp->dn, &rs->sr_entry->e_name );
+       for ( i = 0; !BER_BVISNULL( &permission_ads[i].attr ); i++ ) {
+               Attribute *attr = NULL;
+               attr = attr_find( rs->sr_entry->e_attrs, *permission_ads[i].ad );
+               if ( attr != NULL ) {
+                       switch ( permission_ads[i].type ) {
+                               case RBAC_USERS:
+                                       ber_bvarray_dup_x( &permp->uids, attr->a_nvals, NULL );
+                                       break;
+                               case RBAC_ROLES:
+                                       ber_bvarray_dup_x( &permp->roles, attr->a_nvals, NULL );
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+       }
+
+       cbp->private = (void *)permp;
+
+       return 0;
+}
+
+/*
+ * check whether roles assigned to a user allows access to roles in
+ * a permission, subject to role constraints
+ */
+int
+rbac_check_session_permission(
+               rbac_session_t *sessp,
+               rbac_permission_t *permp,
+               rbac_constraint_t *role_constraints )
+{
+       int rc = LDAP_INSUFFICIENT_ACCESS;
+       rbac_constraint_t *cp = NULL;
+       int i, j;
+
+       if ( !sessp->roles || !permp->roles ) goto done;
+
+       for ( i = 0; !BER_BVISNULL( &sessp->roles[i] ); i++ ) {
+               for ( j = 0; !BER_BVISNULL( &permp->roles[j] ); j++ ) {
+                       if ( ber_bvstrcasecmp( &sessp->roles[i], &permp->roles[j] ) == 0 ) {
+                               /* role temporal constraint */
+                               cp = rbac_role2constraint( &permp->roles[j], role_constraints );
+                               if ( !cp || rbac_check_time_constraint( cp ) == LDAP_SUCCESS ) {
+                                       rc = LDAP_SUCCESS;
+                                       goto done;
+                               }
+                       }
+               }
+       }
+done:;
+       return rc;
+}
+
+rbac_permission_t *
+rbac_read_permission( Operation *op, rbac_req_t *reqp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_callback_info_t rbac_cb;
+       int rc = LDAP_SUCCESS;
+       char fbuf[1024];
+       struct berval filter = { sizeof(fbuf), fbuf };
+       char permbuf[1024];
+       struct berval permdn = { sizeof(permbuf), permbuf };
+       struct berval permndn = BER_BVNULL;
+       char pcls[] = "(objectClass=ftOperation)";
+       SlapReply rs2 = { REP_RESULT };
+       slap_callback cb = { 0 };
+       tenant_info_t *tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+#if 0 /* check valid object name and op name */
+       if ( !is_valid_opname( &reqp->opname ) ||
+                       !is_valid_objname( &reqp->objname ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+                               "invalid opname (%s) or objname (%s)\n",
+                               reqp->opname.bv_val, reqp->objname.bv_val );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+#endif
+
+       if ( !tenantp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+                               "missing tenant information\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       if ( reqp->objid.bv_val != NULL ) {
+               permdn.bv_len = snprintf( permdn.bv_val, permdn.bv_len,
+                               "ftObjId=%s+ftOpNm=%s,ftObjNm=%s,%s", reqp->objid.bv_val,
+                               reqp->opname.bv_val, reqp->objname.bv_val,
+                               tenantp->permissions_basedn.bv_val );
+       } else {
+               permdn.bv_len = snprintf( permdn.bv_val, permdn.bv_len,
+                               "ftOpNm=%s,ftObjNm=%s,%s", reqp->opname.bv_val,
+                               reqp->objname.bv_val, tenantp->permissions_basedn.bv_val );
+       }
+
+       rc = dnNormalize( 0, NULL, NULL, &permdn, &permndn, NULL );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+                               "unable to normalize permission DN\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       filter.bv_val = pcls;
+       filter.bv_len = strlen( pcls );
+       rbac_cb.tenantp = tenantp;
+       rbac_cb.private = NULL;
+
+       Operation op2 = *op;
+       cb.sc_private = &rbac_cb;
+       cb.sc_response = rbac_read_permission_cb;
+       op2.o_callback = &cb;
+       op2.o_tag = LDAP_REQ_SEARCH;
+       op2.o_dn = tenantp->admin;
+       op2.o_ndn = tenantp->admin;
+       op2.o_req_dn = permdn;
+       op2.o_req_ndn = permndn;
+       op2.ors_filterstr = filter;
+       op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+       op2.ors_scope = LDAP_SCOPE_BASE;
+       op2.ors_attrs = tenantp->schema->perm_attrs;
+       op2.ors_tlimit = SLAP_NO_LIMIT;
+       op2.ors_slimit = SLAP_NO_LIMIT;
+       op2.ors_attrsonly = 0;
+       op2.ors_limit = NULL;
+       op2.o_bd = frontendDB;
+       rc = op2.o_bd->be_search( &op2, &rs2 );
+       filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:;
+       ch_free( permndn.bv_val );
+
+       if ( rc != LDAP_SUCCESS ) {
+               rbac_free_permission((rbac_permission_t *)rbac_cb.private);
+       }
+
+       return (rbac_permission_t *)rbac_cb.private;
+}
+
+void
+rbac_free_permission( rbac_permission_t *permp )
+{
+       if ( !permp ) return;
+
+       if ( !BER_BVISNULL( &permp->dn ) ) {
+               ber_memfree( permp->dn.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &permp->internalId ) ) {
+               ber_memfree( permp->internalId.bv_val );
+       }
+
+       if ( permp->opName ) {
+               ber_bvarray_free( permp->opName );
+       }
+
+       if ( permp->objName ) {
+               ber_bvarray_free( permp->objName );
+       }
+
+       if ( !BER_BVISNULL( &permp->objectId ) ) {
+               ber_memfree( permp->objectId.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &permp->abstractName ) ) {
+               ber_memfree( permp->abstractName.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &permp->type ) ) {
+               ber_memfree( permp->type.bv_val );
+       }
+
+       if ( permp->roles ) {
+               ber_bvarray_free( permp->roles );
+       }
+
+       if ( permp->uids ) {
+               ber_bvarray_free( permp->uids );
+       }
+       ch_free( permp );
+
+       return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacreq.c b/contrib/slapd-modules/rbac/rbacreq.c
new file mode 100644 (file)
index 0000000..9942a00
--- /dev/null
@@ -0,0 +1,89 @@
+/* rbacreq.c - RBAC requests */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+rbac_req_t *
+rbac_alloc_req( int type )
+{
+       rbac_req_t *reqp = NULL;
+
+       reqp = ch_calloc( 1, sizeof(rbac_req_t) );
+
+       reqp->req_type = type;
+       BER_BVZERO( &reqp->sessid );
+       BER_BVZERO( &reqp->tenantid );
+       /* session creation */
+       BER_BVZERO( &reqp->uid );
+       BER_BVZERO( &reqp->authtok );
+       reqp->roles = NULL;
+       /* check access  */
+       BER_BVZERO( &reqp->opname );
+       BER_BVZERO( &reqp->objname );
+       BER_BVZERO( &reqp->objid );
+       /* add/drop role */
+       BER_BVZERO( &reqp->role );
+
+       return reqp;
+}
+
+void
+rbac_free_req( rbac_req_t *reqp )
+{
+       if ( !reqp ) return;
+
+       if ( !BER_BVISNULL( &reqp->sessid ) )
+               ber_memfree( reqp->sessid.bv_val );
+
+       if ( !BER_BVISNULL( &reqp->tenantid ) )
+               ber_memfree( reqp->tenantid.bv_val );
+
+       /* session creation */
+       if ( !BER_BVISNULL( &reqp->uid ) )
+               ber_memfree( reqp->uid.bv_val );
+
+       if ( !BER_BVISNULL( &reqp->authtok ) )
+               ber_memfree( reqp->authtok.bv_val );
+
+       if ( reqp->roles )
+               ber_bvarray_free( reqp->roles );
+
+       /* check access  */
+       if ( !BER_BVISNULL( &reqp->opname ) )
+               ber_memfree( reqp->opname.bv_val );
+
+       if ( !BER_BVISNULL( &reqp->objname ) )
+               ber_memfree( reqp->objname.bv_val );
+
+       if ( !BER_BVISNULL( &reqp->objid ) )
+               ber_memfree( reqp->objid.bv_val );
+
+       ch_free( reqp );
+
+       return;
+}
diff --git a/contrib/slapd-modules/rbac/rbacsess.c b/contrib/slapd-modules/rbac/rbacsess.c
new file mode 100644 (file)
index 0000000..6830bbb
--- /dev/null
@@ -0,0 +1,999 @@
+/* rbacsess.c - RBAC session */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static slap_callback nullsc = { NULL, NULL, NULL, NULL };
+
+extern rbac_ad_t rbac_session_permission_ads[];
+extern rbac_ad_t rbac_session_ads[];
+
+struct berval slapo_session_oc = BER_BVC("rbacSession");
+
+typedef struct session_perm_req {
+       Operation *op;
+       SlapReply *rs;
+       struct berval *sessid;
+       struct berval permdn;
+       tenant_info_t *tenantp;
+} session_perm_req_t;
+
+static int
+rbac_sess_fake_cb( Operation *op, SlapReply *rs )
+{
+       Debug( LDAP_DEBUG_ANY, "rbac_sess_fake_cb\n" );
+
+       return 0;
+}
+
+static int
+rbac_send_session_permission(
+               session_perm_req_t *sess_perm_reqp,
+               rbac_permission_t *perm )
+{
+       int i, rc = LDAP_SUCCESS;
+       Operation *op = sess_perm_reqp->op;
+       SlapReply *rs = sess_perm_reqp->rs;
+       struct berval *sessidp = sess_perm_reqp->sessid;
+       struct berval *permdnp = &sess_perm_reqp->permdn;
+
+       Entry *e = entry_alloc();
+       e->e_attrs = NULL;
+       ber_dupbv( &e->e_name, permdnp );
+       ber_dupbv( &e->e_nname, permdnp );
+       e->e_private = NULL;
+       attr_merge_one( e, slap_rbac_schema.ad_session_id, sessidp, NULL );
+
+       for ( i = 0; !BER_BVISNULL( &rbac_session_permission_ads[i].attr ); i++ ) {
+               switch ( rbac_session_permission_ads[i].type ) {
+                       case RBAC_OP_NAME:
+                               attr_merge_one( e, *rbac_session_permission_ads[i].ad,
+                                               &perm->opName[0], NULL );
+                               break;
+                       case RBAC_OBJ_NAME:
+                               attr_merge_one( e, *rbac_session_permission_ads[i].ad,
+                                               &perm->objName[0], NULL );
+                               break;
+                       case RBAC_ROLE_NAME:
+                               attr_merge( e, *rbac_session_permission_ads[i].ad, perm->roles,
+                                               NULL );
+                               break;
+                       default:
+                               break;
+               }
+       }
+
+       rs->sr_entry = e;
+       rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+       rc = send_search_entry( op, rs );
+
+       return rc;
+}
+
+static int
+rbac_session_permissions_cb( Operation *op, SlapReply *rs )
+{
+       session_perm_req_t *sess_perm_reqp = op->o_callback->sc_private;
+       tenant_info_t *tenantp = NULL;
+       rbac_permission_t *permp = NULL;
+       rbac_ad_t *session_permissions_ads;
+       int i;
+
+       if ( rs->sr_type != REP_SEARCH ) return 0;
+
+       assert( sess_perm_reqp );
+
+       tenantp = sess_perm_reqp->tenantp;
+       session_permissions_ads = tenantp->schema->session_permissions_ads;
+
+       permp = ch_calloc( 1, sizeof(rbac_permission_t) );
+
+       for ( i = 0; !BER_BVISNULL( &session_permissions_ads[i].attr ); i++ ) {
+               Attribute *attr = NULL;
+
+               attr = attr_find(
+                               rs->sr_entry->e_attrs, *session_permissions_ads[i].ad );
+               if ( attr != NULL ) {
+                       switch ( session_permissions_ads[i].type ) {
+                               case RBAC_USERS:
+                                       ber_bvarray_dup_x( &permp->uids, attr->a_nvals, NULL );
+                                       break;
+                               case RBAC_ROLES:
+                                       ber_bvarray_dup_x( &permp->roles, attr->a_nvals, NULL );
+                                       break;
+                               case RBAC_OBJ_NAME:
+                                       ber_bvarray_dup_x( &permp->objName, attr->a_nvals, NULL );
+                                       break;
+                               case RBAC_OP_NAME:
+                                       ber_bvarray_dup_x( &permp->opName, attr->a_nvals, NULL );
+                                       break;
+                       }
+               }
+       }
+
+       rbac_send_session_permission( sess_perm_reqp, permp );
+       rbac_free_permission( permp );
+       permp = NULL;
+
+       return SLAP_CB_CONTINUE;
+}
+
+static int
+rbac_read_session_cb( Operation *op, SlapReply *rs )
+{
+       rbac_session_t *sessp = op->o_callback->sc_private;
+       int i;
+
+       if ( rs->sr_type != REP_SEARCH ) return 0;
+
+       ber_dupbv( &sessp->sessdn, &rs->sr_entry->e_name );
+
+       for ( i = 0; !BER_BVISNULL( &rbac_session_ads[i].attr ); i++ ) {
+               Attribute *attr = NULL;
+               attr = attr_find( rs->sr_entry->e_attrs, *rbac_session_ads[i].ad );
+               if ( attr != NULL ) {
+                       switch ( rbac_session_ads[i].type ) {
+                               case RBAC_SESSION_ID:
+                                       ber_dupbv( &sessp->sessid, &attr->a_vals[0] );
+                                       break;
+                               case RBAC_USER_DN:
+                                       ber_dupbv( &sessp->userdn, &attr->a_vals[0] );
+                                       break;
+                               case RBAC_ROLES:
+                                       ber_bvarray_dup_x( &sessp->roles, attr->a_nvals, NULL );
+                                       break;
+                               case RBAC_ROLE_CONSTRAINTS:
+                                       ber_bvarray_dup_x(
+                                                       &sessp->role_constraints, attr->a_nvals, NULL );
+                                       break;
+                               case RBAC_UID:
+                                       ber_dupbv( &sessp->uid, &attr->a_vals[0] );
+                                       break;
+                               case RBAC_TENANT_ID:
+                                       ber_dupbv( &sessp->tenantid, &attr->a_vals[0] );
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+       }
+
+       //return SLAP_CB_CONTINUE;
+       return 0;
+}
+
+/* check whether the session is owned by the user */
+int
+rbac_is_session_owner( rbac_session_t *sessp, rbac_req_t *reqp )
+{
+       int rc = 0;
+
+       if ( BER_BVISEMPTY( &sessp->uid ) || BER_BVISEMPTY( &reqp->uid ) ) {
+               Debug( LDAP_DEBUG_ANY, "session not owned by user\n" );
+               rc = 0;
+               goto done;
+       }
+
+       if ( !ber_bvstrcasecmp( &sessp->uid, &reqp->uid ) ) {
+               rc = 1;
+               goto done;
+       }
+
+done:;
+       return rc;
+}
+
+int
+rbac_session_add_role( Operation *op, rbac_session_t *sessp, rbac_req_t *reqp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       slap_callback cb = { 0 };
+       SlapReply rs2 = { REP_RESULT };
+       Operation op2 = *op;
+       rbac_callback_info_t rbac_cb;
+       tenant_info_t *tenantp = NULL;
+       struct berval vals[2];
+       Modifications mod;
+       int rc = LDAP_SUCCESS;
+
+       tenantp = rbac_tid2tenant( &reqp->tenantid );
+       if ( !tenantp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_add_role: "
+                               "no tenant info with the req\n" );
+               goto done;
+       }
+
+       // convert the role name to lower case:
+       rbac_to_lower( &reqp->role );
+
+       //ber_dupbv( &vals[0], &reqp->roles[0]);
+       ber_dupbv( &vals[0], &reqp->role );
+       BER_BVZERO( &vals[1] );
+
+       /* create mod list */
+       mod.sml_op = LDAP_MOD_ADD;
+       mod.sml_flags = 0;
+       mod.sml_type = slap_rbac_schema.ad_session_roles->ad_cname;
+       mod.sml_desc = slap_rbac_schema.ad_session_roles;
+       mod.sml_numvals = 1;
+       mod.sml_values = vals;
+       mod.sml_nvalues = NULL;
+       mod.sml_next = NULL;
+
+       cb.sc_private = &rbac_cb;
+       cb.sc_response = rbac_sess_fake_cb;
+       op2.o_callback = &cb;
+
+       op2.o_tag = LDAP_REQ_MODIFY;
+       op2.orm_modlist = &mod;
+       op2.o_req_dn = sessp->sessdn;
+       op2.o_req_ndn = sessp->sessdn;
+       op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+       op2.o_dn = op2.o_bd->be_rootdn;
+       op2.o_ndn = op2.o_bd->be_rootdn;
+       op2.ors_limit = NULL;
+       rc = op2.o_bd->be_modify( &op2, &rs2 );
+       ch_free( vals[0].bv_val );
+
+done:;
+       if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_add_active_role: "
+                               "role already activated in session\n" );
+       }
+       return rc;
+}
+
+int
+rbac_session_drop_role( Operation *op, rbac_session_t *sessp, rbac_req_t *reqp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       slap_callback cb = { 0 };
+       SlapReply rs2 = { REP_RESULT };
+       Operation op2 = *op;
+       rbac_callback_info_t rbac_cb;
+       tenant_info_t *tenantp = NULL;
+       Modifications *m = NULL;
+       int rc = LDAP_SUCCESS;
+
+       tenantp = rbac_tid2tenant( &reqp->tenantid );
+       if ( !tenantp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_drop_role: "
+                               "no tenant info with the req\n" );
+               goto done;
+       }
+
+       /* create mod list */
+       m = ch_calloc( sizeof(Modifications), 1 );
+       m->sml_op = LDAP_MOD_DELETE;
+       m->sml_flags = 0;
+       m->sml_type = slap_rbac_schema.ad_session_roles->ad_cname;
+       m->sml_desc = slap_rbac_schema.ad_session_roles;
+       m->sml_numvals = 1;
+       m->sml_values = ch_calloc( sizeof(struct berval), 2 );
+       m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );
+       //ber_dupbv( &m->sml_values[0], &reqp->roles[0]);
+
+       // convert the role name to lower case:
+       rbac_to_lower( &reqp->role );
+
+       ber_dupbv( &m->sml_values[0], &reqp->role );
+
+       // todo: determine if this needs to be done:
+       //BER_BVZERO(&m->sml_values[1]);
+
+       ber_dupbv( &m->sml_nvalues[0], &reqp->role );
+       BER_BVZERO( &m->sml_nvalues[1] );
+
+       //ber_dupbv( &m->sml_nvalues[0], &reqp->roles[0]);
+       //ber_dupbv( &m->sml_nvalues[0], &reqp->role);
+       //BER_BVZERO(&m->sml_nvalues[1]);
+
+       m->sml_next = NULL;
+
+       cb.sc_private = &rbac_cb;
+       cb.sc_response = rbac_sess_fake_cb;
+       op2.o_callback = &cb;
+
+       op2.o_dn = tenantp->session_admin;
+       op2.o_ndn = tenantp->session_admin;
+       op2.o_tag = LDAP_REQ_MODIFY;
+       op2.orm_modlist = m;
+       op2.o_req_dn = sessp->sessdn;
+       op2.o_req_ndn = sessp->sessdn;
+       op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+
+       op2.ors_limit = NULL;
+       rc = op2.o_bd->be_modify( &op2, &rs2 );
+
+done:;
+       if ( m ) {
+               slap_mods_free( m, 1 );
+       }
+
+       return rc;
+}
+
+/* delete the session */
+int
+rbac_int_delete_session( Operation *op, rbac_session_t *sessp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       slap_callback cb = { 0 };
+       SlapReply rs2 = { REP_RESULT };
+       Operation op2 = *op;
+       rbac_callback_info_t rbac_cb;
+       tenant_info_t *tenantp = NULL;
+       int rc = LDAP_SUCCESS;
+
+       tenantp = rbac_tid2tenant( &sessp->tenantid );
+       if ( !tenantp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_drop_role: "
+                               "no tenant info with the req\n" );
+               goto done;
+       }
+
+       /* delete RBAC session */
+       cb.sc_private = &rbac_cb;
+       cb.sc_response = rbac_sess_fake_cb;
+       op2.o_callback = &cb;
+
+       op2.o_dn = tenantp->session_admin;
+       op2.o_ndn = tenantp->session_admin;
+       op2.o_tag = LDAP_REQ_DELETE;
+       op2.o_req_dn = sessp->sessdn;
+       op2.o_req_ndn = sessp->sessdn;
+       op2.o_bd = select_backend( &op2.o_req_ndn, 0 );
+       rc = op2.o_bd->be_delete( &op2, &rs2 );
+
+done:;
+       return rc;
+}
+
+rbac_session_t *
+rbac_alloc_session()
+{
+       rbac_session_t *sessp = NULL;
+
+       sessp = ch_malloc( sizeof(rbac_session_t) );
+       sessp->sessid.bv_len =
+                       lutil_uuidstr( sessp->uuidbuf, sizeof(sessp->uuidbuf) );
+       sessp->sessid.bv_val = sessp->uuidbuf;
+
+       sessp->user = NULL;
+       BER_BVZERO( &sessp->tenantid );
+       BER_BVZERO( &sessp->uid );
+       BER_BVZERO( &sessp->userdn );
+       BER_BVZERO( &sessp->sessdn );
+       BER_BVZERO( &sessp->message );
+
+       sessp->last_access = 0;
+       sessp->timeout = 0;
+       sessp->warning_id = 0;
+       sessp->error_id = 0;
+       sessp->grace_logins = 0;
+       sessp->expiration_secs = 0;
+       sessp->is_authenticated = 0;
+
+       sessp->roles = NULL;
+       sessp->role_constraints = NULL;
+
+       return sessp;
+}
+
+int
+rbac_register_session( Operation *op, SlapReply *rs, rbac_session_t *sessp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       struct berval rdn, nrdn;
+       SlapReply rs2 = { REP_RESULT };
+       OperationBuffer opbuf;
+       Operation *op2;
+       Connection conn = { 0 };
+       Entry *e = NULL;
+       int rc = LDAP_SUCCESS;
+       char rdnbuf[
+               STRLENOF(RBAC_SESSION_RDN_EQ) + LDAP_LUTIL_UUIDSTR_BUFSIZE + 1];
+       tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+#ifdef USE_NEW_THREAD_CONTEXT
+       void *thrctx = ldap_pvt_thread_pool_context();
+#else
+       void *thrctx = op->o_tmpmemctx;
+#endif
+
+       if ( !sessp ) {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* dynamic objects */
+       e = entry_alloc();
+
+       strcpy( rdnbuf, RBAC_SESSION_RDN_EQ );
+       strncat( rdnbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+       rdn.bv_val = rdnbuf;
+       rdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+       nrdn.bv_val = rdnbuf;
+       nrdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+
+       build_new_dn( &e->e_name, &tenantp->sessions_basedn, &rdn, NULL );
+       build_new_dn( &e->e_nname, &tenantp->sessions_basedn, &nrdn, NULL );
+
+       attr_merge_one( e, slap_schema.si_ad_objectClass, &slapo_session_oc, NULL );
+       attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+                       &slapo_session_oc, NULL );
+       attr_merge_one( e, slap_rbac_schema.ad_session_id, &sessp->sessid, NULL );
+
+       if ( !BER_BVISNULL( &sessp->uid ) ) {
+               attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+       }
+
+       /* add tenant id */
+       if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+               attr_merge_one(
+                               e, slap_rbac_schema.ad_tenant_id, &sessp->tenantid, NULL );
+       }
+
+       /* add the userdn */
+       if ( !BER_BVISNULL( &sessp->userdn ) ) {
+               attr_merge_one(
+                               e, slap_rbac_schema.ad_session_user_dn, &sessp->userdn, NULL );
+       }
+
+       if ( sessp->roles ) {
+               attr_merge( e, slap_rbac_schema.ad_session_roles, sessp->roles, NULL );
+       }
+
+       // TODO: ensure this is correct way to store constraints in session:
+       if ( sessp->role_constraints ) {
+               attr_merge( e, slap_rbac_schema.ad_session_role_constraints,
+                               sessp->role_constraints, NULL );
+       }
+       /* rendered dynmaicObject */
+       attr_merge_one( e, slap_schema.si_ad_objectClass,
+                       &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+       /* store RBAC session */
+       connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+       op2 = &opbuf.ob_op;
+       //Operation op2 = *op;
+       //op2.o_callback = &nullsc;
+       //rbac_callback_info_t rbac_cb;
+       //cb.sc_private      = &rbac_cb;
+       //cb.sc_response     = rbac_sess_fake_cb;
+       //op2.o_callback    = &cb;
+       //op2.ors_limit     = NULL;
+       op->o_callback = &nullsc;
+       op2->o_dn = tenantp->session_admin;
+       op2->o_ndn = tenantp->session_admin;
+       op2->o_tag = LDAP_REQ_ADD;
+       op2->o_protocol = LDAP_VERSION3;
+       op2->o_req_dn = e->e_name;
+       op2->o_req_ndn = e->e_nname;
+       op2->ora_e = e;
+       op2->o_bd = frontendDB;
+
+       rc = op2->o_bd->be_add( op2, &rs2 );
+
+done:;
+       if ( e ) entry_free( e );
+       return rc;
+}
+
+int
+rbac_register_session2( Operation *op, SlapReply *rs, rbac_session_t *sessp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       struct berval rdn, nrdn;
+       SlapReply rs2 = { REP_RESULT };
+       Operation op2 = *op;
+       rbac_callback_info_t rbac_cb;
+       //OperationBuffer opbuf;
+       //Connection conn = {0};
+       Entry *e = NULL;
+       int rc = LDAP_SUCCESS;
+       char rdnbuf[STRLENOF(RBAC_SESSION_RDN_EQ) + LDAP_LUTIL_UUIDSTR_BUFSIZE +
+                       1];
+       tenant_info_t *tenantp = rbac_tid2tenant( &sessp->tenantid );
+       slap_callback cb = { 0 };
+       //#ifdef USE_NEW_THREAD_CONTEXT
+       //      void *thrctx = ldap_pvt_thread_pool_context();
+       //#else
+       //      void *thrctx = op->o_tmpmemctx;
+       //#endif
+
+       if ( !sessp ) {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* dynamic objects */
+       e = entry_alloc();
+
+       strcpy( rdnbuf, RBAC_SESSION_RDN_EQ );
+       strncat( rdnbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+       rdn.bv_val = rdnbuf;
+       rdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+       nrdn.bv_val = rdnbuf;
+       nrdn.bv_len = STRLENOF(RBAC_SESSION_RDN_EQ) + sessp->sessid.bv_len;
+
+       build_new_dn( &e->e_name, &tenantp->sessions_basedn, &rdn, NULL );
+       build_new_dn( &e->e_nname, &tenantp->sessions_basedn, &nrdn, NULL );
+
+       attr_merge_one( e, slap_schema.si_ad_objectClass, &slapo_session_oc, NULL );
+       attr_merge_one( e, slap_schema.si_ad_structuralObjectClass,
+                       &slapo_session_oc, NULL );
+       attr_merge_one( e, slap_rbac_schema.ad_session_id, &sessp->sessid, NULL );
+
+       if ( !BER_BVISNULL( &sessp->uid ) ) {
+               attr_merge_one( e, slap_schema.si_ad_uid, &sessp->uid, NULL );
+       }
+
+       /* add tenant id */
+       if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+               attr_merge_one(
+                               e, slap_rbac_schema.ad_tenant_id, &sessp->tenantid, NULL );
+       }
+
+       /* add the userdn */
+       if ( !BER_BVISNULL( &sessp->userdn ) ) {
+               attr_merge_one(
+                               e, slap_rbac_schema.ad_session_user_dn, &sessp->userdn, NULL );
+       }
+
+       if ( sessp->roles ) {
+               attr_merge( e, slap_rbac_schema.ad_session_roles, sessp->roles, NULL );
+       }
+
+       // TODO: ensure this is correct way to store constraints in session:
+       if ( sessp->role_constraints ) {
+               attr_merge( e, slap_rbac_schema.ad_session_role_constraints,
+                               sessp->role_constraints, NULL );
+       }
+       /* rendered dynmaicObject */
+       attr_merge_one( e, slap_schema.si_ad_objectClass,
+                       &slap_schema.si_oc_dynamicObject->soc_cname, NULL );
+
+       /* store RBAC session */
+       //connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+       //op2 = &opbuf.ob_op;
+       //op2.o_ctrlflag = op->o_ctrlflag;
+       // todo this ain't right"
+       //op2.o_ctrlflag = 0;
+       //OperationBuffer *opbuf;
+       //memset( opbuf, 0, sizeof(OperationBuffer));
+       //op2.o_hdr = &opbuf->ob_hdr;
+       //op2.o_controls = opbuf->ob_controls;
+
+       // fails on modify.c:353 with segfault
+
+       //op2.o_callback = &nullsc;
+       cb.sc_private = &rbac_cb;
+       cb.sc_response = rbac_sess_fake_cb;
+       op2.o_callback = &cb;
+       op2.o_dn = tenantp->session_admin;
+       op2.o_ndn = tenantp->session_admin;
+       op2.o_tag = LDAP_REQ_ADD;
+       op2.o_protocol = LDAP_VERSION3;
+       op2.o_req_dn = e->e_name;
+       op2.o_req_ndn = e->e_nname;
+       op2.ora_e = e;
+       op2.o_bd = frontendDB;
+       //op2.ors_limit     = NULL;
+
+       rc = op2.o_bd->be_add( &op2, &rs2 );
+
+done:;
+       if ( e ) entry_free( e );
+
+       return rc;
+}
+
+int
+rbac_is_valid_session_id( struct berval *sessid )
+{
+       /* TODO: simple test */
+       if ( !sessid || sessid->bv_len != 36 ) {
+               if ( !sessid ) {
+                       Debug( LDAP_DEBUG_ANY, "rbac_is_valid_session_id: "
+                                       "null sessid\n" );
+               } else {
+                       Debug( LDAP_DEBUG_ANY, "rbac_is_valid_session_id: "
+                                       "len (%lu)\n",
+                                       sessid->bv_len );
+               }
+               return 0;
+       }
+
+       else {
+               return 1;
+       }
+}
+
+/* create an rbac request with the session ID */
+rbac_req_t *
+rbac_is_search_session_permissions( Operation *op )
+{
+       rbac_req_t *reqp = NULL;
+
+       /* check whether the search for sessionPermissions and *
+        * with a valid sessionID */
+
+       return reqp;
+}
+
+rbac_session_t *
+rbac_session_byid_fake( Operation *op, rbac_req_t *reqp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_session_t *sessp = NULL;
+       int rc = LDAP_SUCCESS;
+       char fbuf[RBAC_BUFLEN];
+       struct berval filter = { sizeof(fbuf), fbuf };
+       SlapReply rs2 = { REP_RESULT };
+       Operation op2 = *op;
+       rbac_callback_info_t rbac_cb;
+       slap_callback cb = { 0 };
+       tenant_info_t *tenantp = NULL;
+
+       if ( !rbac_is_valid_session_id( &reqp->sessid ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+                               "invalid session id (%s)\n",
+                               reqp->sessid.bv_val );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       sessp = rbac_alloc_session();
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+                               "unable to allocate session memory\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+       /* session id filter */
+       memset( fbuf, 0, sizeof(fbuf) );
+       strcpy( fbuf, RBAC_SESSION_RDN_EQ );
+       strncpy( &fbuf[0] + sizeof(RBAC_SESSION_RDN_EQ) - 1, reqp->sessid.bv_val,
+                       reqp->sessid.bv_len );
+       filter.bv_val = fbuf;
+       filter.bv_len = strlen( fbuf );
+
+       //cb.sc_private     = sessp;
+       //cb.sc_response    = rbac_read_session_cb;
+       cb.sc_private = &rbac_cb;
+       cb.sc_response = rbac_sess_fake_cb;
+       op2.o_callback = &cb;
+       op2.o_tag = LDAP_REQ_SEARCH;
+       op2.o_dn = tenantp->session_admin;
+       op2.o_ndn = tenantp->session_admin;
+       op2.o_req_dn = tenantp->sessions_basedn;
+       op2.o_req_ndn = tenantp->sessions_basedn;
+       op2.ors_filterstr = filter;
+       op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+       op2.ors_scope = LDAP_SCOPE_SUBTREE;
+       op2.ors_attrs = slap_rbac_schema.session_attrs;
+       op2.ors_tlimit = SLAP_NO_LIMIT;
+       op2.ors_slimit = SLAP_NO_LIMIT;
+       op2.o_bd = frontendDB;
+       // hyc change to fix seg fault:
+       op2.ors_limit = NULL;
+
+       rc = op2.o_bd->be_search( &op2, &rs2 );
+       filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:
+       // TODO: find equivilant way of check nentries (broke with fake connection fix)
+       //if ( rc != LDAP_SUCCESS || rs2.sr_nentries <= 0 ) {
+       if ( rc != LDAP_SUCCESS ) {
+               rbac_free_session( sessp );
+               sessp = NULL;
+       }
+
+       return sessp;
+}
+
+rbac_session_t *
+rbac_session_byid( Operation *op, rbac_req_t *reqp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       rbac_session_t *sessp = NULL;
+       int rc = LDAP_SUCCESS;
+       char fbuf[RBAC_BUFLEN];
+       struct berval filter = { sizeof(fbuf), fbuf };
+       SlapReply rs2 = { REP_RESULT };
+       Operation op2 = *op;
+       slap_callback cb = { 0 };
+       tenant_info_t *tenantp = NULL;
+
+       if ( !rbac_is_valid_session_id( &reqp->sessid ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+                               "invalid session id (%s)\n",
+                               reqp->sessid.bv_val );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       sessp = rbac_alloc_session();
+       if ( !sessp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_session_byid: "
+                               "unable to allocate session memory\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+       /* session id filter */
+       memset( fbuf, 0, sizeof(fbuf) );
+       strcpy( fbuf, RBAC_SESSION_RDN_EQ );
+       strncpy( &fbuf[0] + sizeof(RBAC_SESSION_RDN_EQ) - 1, reqp->sessid.bv_val,
+                       reqp->sessid.bv_len );
+       filter.bv_val = fbuf;
+       filter.bv_len = strlen( fbuf );
+
+       cb.sc_private = sessp;
+       cb.sc_response = rbac_read_session_cb;
+       op2.o_callback = &cb;
+       op2.o_tag = LDAP_REQ_SEARCH;
+       op2.o_dn = tenantp->session_admin;
+       op2.o_ndn = tenantp->session_admin;
+       op2.o_req_dn = tenantp->sessions_basedn;
+       op2.o_req_ndn = tenantp->sessions_basedn;
+       op2.ors_filterstr = filter;
+       op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+       op2.ors_scope = LDAP_SCOPE_SUBTREE;
+       op2.ors_attrs = slap_rbac_schema.session_attrs;
+       op2.ors_tlimit = SLAP_NO_LIMIT;
+       op2.ors_slimit = SLAP_NO_LIMIT;
+       op2.o_bd = frontendDB;
+       // hyc change to fix seg fault:
+       op2.ors_limit = NULL;
+
+       rc = op2.o_bd->be_search( &op2, &rs2 );
+       filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:
+       // TODO: find equivalent way of check nentries (broke with fake connection fix)
+       //if ( rc != LDAP_SUCCESS || rs2.sr_nentries <= 0 ) {
+       if ( rc != LDAP_SUCCESS ) {
+               rbac_free_session( sessp );
+               sessp = NULL;
+       }
+
+       return sessp;
+}
+
+static char *
+rbac_int_session_permissions_filterstr( Operation *op, rbac_session_t *sessp )
+{
+       char filterbuf[RBAC_BUFLEN];
+       int i;
+
+       memset( filterbuf, 0, sizeof(filterbuf) );
+
+       strcat( filterbuf, "(&(objectClass=ftOperation)(|" );
+       strcat( filterbuf, "(ftUsers=" );
+       strcat( filterbuf, sessp->uid.bv_val );
+       strcat( filterbuf, ")" );
+
+       /* add ftRoles filters */
+       for ( i = 0; !BER_BVISEMPTY( &sessp->roles[i] ); i++ ) {
+               strcat( filterbuf, "(ftRoles=" );
+               strncat( filterbuf, sessp->roles[i].bv_val, sessp->roles[i].bv_len );
+               strcat( filterbuf, ")" );
+       }
+       strcat( filterbuf, "))" );
+       return strdup( filterbuf );
+}
+
+int
+rbac_int_session_permissions(
+               Operation *op,
+               SlapReply *rs,
+               rbac_req_t *reqp,
+               rbac_session_t *sessp )
+{
+       slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
+       tenant_info_t *tenantp = NULL;
+       int rc;
+       struct berval filter;
+       char *filterstr;
+       struct berval permndn = BER_BVNULL;
+       OperationBuffer opbuf;
+       Connection conn = { 0 };
+       SlapReply rs2 = { REP_RESULT };
+       Operation *op2;
+       slap_callback cb = { 0 };
+       char permbuf[1024];
+       session_perm_req_t sess_perm_req;
+#ifdef USE_NEW_THREAD_CONTEXT
+       void *thrctx = ldap_pvt_thread_pool_context();
+#else
+       void *thrctx = op->o_tmpmemctx;
+#endif
+
+       tenantp = rbac_tid2tenant( &reqp->tenantid );
+
+       /* construct session permissions dn */
+       memset( permbuf, 0, sizeof(permbuf) );
+       strcat( permbuf, "rbacSessid=" );
+       strncat( permbuf, sessp->sessid.bv_val, sessp->sessid.bv_len );
+       strcat( permbuf, ",dc=rbac" );
+       sess_perm_req.op = op;
+       sess_perm_req.rs = rs;
+       sess_perm_req.permdn.bv_val = permbuf;
+       sess_perm_req.permdn.bv_len = strlen( permbuf );
+       sess_perm_req.sessid = &reqp->sessid;
+       sess_perm_req.tenantp = tenantp;
+
+       filterstr = rbac_int_session_permissions_filterstr( op, sessp );
+       if ( !filterstr ) {
+               Debug( LDAP_DEBUG_ANY, "unable to construct filter for session permissions\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+       filter.bv_val = filterstr;
+       filter.bv_len = strlen( filterstr );
+
+       rc = dnNormalize(
+                       0, NULL, NULL, &tenantp->permissions_basedn, &permndn, NULL );
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_permission: "
+                               "unable to normalize permission DN\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       connection_fake_init2( &conn, &opbuf, thrctx, 0 );
+       op2 = &opbuf.ob_op;
+       //Operation op2 = *op;
+       cb.sc_private = &sess_perm_req;
+       cb.sc_response = rbac_session_permissions_cb;
+       op2->o_callback = &cb;
+       op2->o_tag = LDAP_REQ_SEARCH;
+       op2->o_dn = tenantp->admin;
+       op2->o_ndn = tenantp->admin;
+       op2->o_req_dn = tenantp->permissions_basedn;
+       op2->o_req_ndn = permndn;
+       op2->ors_filterstr = filter;
+       op2->ors_filter = str2filter_x( op, filter.bv_val );
+       op2->ors_scope = LDAP_SCOPE_SUB;
+       op2->ors_attrs = tenantp->schema->session_perm_attrs;
+       op2->ors_tlimit = SLAP_NO_LIMIT;
+       op2->ors_slimit = SLAP_NO_LIMIT;
+       op2->ors_attrsonly = 0;
+       op2->o_bd = frontendDB;
+       //op2.ors_limit     = NULL;
+       rc = op2->o_bd->be_search( op2, &rs2 );
+       filter_free_x( op, op2->ors_filter, 1 );
+
+done:;
+       /* generate audit log */
+       rbac_audit( op, SessionPermissions, sessp, reqp, rc, (char *)rs->sr_text );
+
+       rs->sr_err = rc;
+       return rs->sr_err;
+}
+
+void
+rbac_free_session( rbac_session_t *sessp )
+{
+       if ( !sessp ) return;
+
+       if ( sessp->user ) {
+               rbac_free_user( sessp->user );
+       }
+
+       if ( !BER_BVISNULL( &sessp->uid ) ) {
+               ber_memfree( sessp->uid.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &sessp->tenantid ) ) {
+               ber_memfree( sessp->tenantid.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &sessp->userdn ) ) {
+               ber_memfree( sessp->userdn.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &sessp->sessdn ) ) {
+               ber_memfree( sessp->sessdn.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &sessp->message ) ) {
+               ber_memfree( sessp->message.bv_val );
+       }
+
+       if ( sessp->roles ) {
+               ber_bvarray_free( sessp->roles );
+       }
+
+       if ( sessp->role_constraints ) {
+               ber_bvarray_free( sessp->role_constraints );
+       }
+
+       ch_free( sessp );
+
+       return;
+}
+
+/* roles included from request are activated into a session only when
+ * they exist and have been assigned to the user. If no roles included in request, all
+ * roles assigned to the user are activated into the rbac session.
+ */
+int
+activate_session_roles(
+               rbac_session_t *sessp,
+               rbac_req_t *reqp,
+               rbac_user_t *userp )
+{
+       int i, j, rc = LDAP_UNWILLING_TO_PERFORM;
+       if ( !sessp || !reqp || !userp ) {
+               goto done;
+       }
+
+       /* no role requested, assign all roles from the user to the session. */
+       if ( reqp->roles == NULL || BER_BVISNULL( &reqp->roles[0] ) ) {
+               //if (!reqp->roles || BER_BVISNULL(&reqp->roles[0])) {
+               /* no roles assigned to the user */
+               if ( !userp->roles || BER_BVISNULL( &userp->roles[0] ) ) goto done;
+               for ( i = 0; !BER_BVISNULL( &userp->roles[i] ); i++ ) {
+                       struct berval role;
+                       ber_dupbv_x( &role, &userp->roles[i], NULL );
+                       ber_bvarray_add( &sessp->roles, &role );
+                       rc = LDAP_SUCCESS;
+               }
+
+               // TODO: smm 20141218 - make sure this is correct way to add constraints to user session.
+               for ( i = 0; !BER_BVISNULL( &userp->role_constraints[i] ); i++ ) {
+                       struct berval roleconstraint;
+                       ber_dupbv_x( &roleconstraint, &userp->role_constraints[i], NULL );
+                       ber_bvarray_add( &sessp->role_constraints, &roleconstraint );
+                       rc = LDAP_SUCCESS;
+               }
+
+       } else {
+               for ( i = 0; !BER_BVISNULL( &reqp->roles[i] ); i++ ) {
+                       for ( j = 0; !BER_BVISNULL( &userp->roles[j] ); j++ ) {
+                               if ( !ber_bvstrcasecmp( &reqp->roles[i], &userp->roles[j] ) ) {
+                                       /* requested role is assigned to the user */
+                                       struct berval role;
+                                       ber_dupbv_x( &role, &userp->roles[i], NULL );
+                                       ber_bvarray_add( &sessp->roles, &role );
+                                       rc = LDAP_SUCCESS;
+                               }
+                       }
+               }
+       }
+
+done:;
+       return rc;
+}
diff --git a/contrib/slapd-modules/rbac/rbacuser.c b/contrib/slapd-modules/rbac/rbacuser.c
new file mode 100644 (file)
index 0000000..59d3c01
--- /dev/null
@@ -0,0 +1,620 @@
+/* rbacuser.c - RBAC users */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+static int ppolicy_cid = -1;
+
+static rbac_user_t *
+rbac_alloc_user()
+{
+       rbac_user_t *userp = ch_calloc( 1, sizeof(rbac_user_t) );
+
+       BER_BVZERO( &userp->tenantid );
+       BER_BVZERO( &userp->uid );
+       BER_BVZERO( &userp->dn );
+       BER_BVZERO( &userp->password );
+       BER_BVZERO( &userp->constraints );
+       BER_BVZERO( &userp->msg );
+       userp->roles = NULL;
+       userp->role_constraints = NULL;
+
+       return userp;
+}
+
+static int
+rbac_read_user_cb( Operation *op, SlapReply *rs )
+{
+       rbac_callback_info_t *cbp = op->o_callback->sc_private;
+       rbac_ad_t *user_ads;
+       rbac_user_t *userp = NULL;
+       int rc = 0, i;
+
+       Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb\n" );
+
+       if ( rs->sr_type != REP_SEARCH ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb: "
+                               "sr_type != REP_SEARCH\n" );
+               return 0;
+       }
+
+       assert( cbp );
+
+       user_ads = cbp->tenantp->schema->user_ads;
+
+       userp = rbac_alloc_user();
+       if ( !userp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_user_cb: "
+                               "rbac_alloc_user failed\n" );
+
+               goto done;
+       }
+
+       ber_dupbv( &userp->dn, &rs->sr_entry->e_name );
+
+       Debug( LDAP_DEBUG_ANY, "DEBUG rbac_read_user_cb (%s): "
+                       "rc (%d)\n",
+                       userp->dn.bv_val, rc );
+
+       for ( i = 0; !BER_BVISNULL( &user_ads[i].attr ); i++ ) {
+               Attribute *attr = NULL;
+
+               attr = attr_find( rs->sr_entry->e_attrs, *user_ads[i].ad );
+               if ( attr != NULL ) {
+                       switch ( user_ads[i].type ) {
+                               case RBAC_ROLE_ASSIGNMENT:
+                                       ber_bvarray_dup_x( &userp->roles, attr->a_nvals, NULL );
+                                       break;
+                               case RBAC_ROLE_CONSTRAINTS:
+                                       ber_bvarray_dup_x(
+                                                       &userp->role_constraints, attr->a_nvals, NULL );
+                                       break;
+                               case RBAC_USER_CONSTRAINTS:
+                                       ber_dupbv_x( &userp->constraints, &attr->a_nvals[0], NULL );
+                                       break;
+                               case RBAC_UID:
+                                       ber_dupbv_x( &userp->uid, &attr->a_nvals[0], NULL );
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+       }
+
+done:;
+       cbp->private = userp;
+
+       return 0;
+}
+
+static int
+rbac_bind_cb( Operation *op, SlapReply *rs )
+{
+       rbac_user_t *ui = op->o_callback->sc_private;
+
+       LDAPControl *ctrl = ldap_control_find(
+                       LDAP_CONTROL_PASSWORDPOLICYRESPONSE, rs->sr_ctrls, NULL );
+       if ( ctrl ) {
+               LDAP *ld;
+               ber_int_t expire, grace;
+               LDAPPasswordPolicyError error;
+
+               ldap_create( &ld );
+               if ( ld ) {
+                       int rc = ldap_parse_passwordpolicy_control(
+                                       ld, ctrl, &expire, &grace, &error );
+                       if ( rc == LDAP_SUCCESS ) {
+                               ui->authz = RBAC_PASSWORD_GOOD;
+                               if ( grace > 0 ) {
+                                       //ui->msg.bv_len = sprintf(ui->msg.bv_val,
+                                       //              "Password expired; %d grace logins remaining",
+                                       //              grace);
+                                       ui->authz = RBAC_BIND_NEW_AUTHTOK_REQD;
+                               } else if ( error != PP_noError ) {
+                                       ber_str2bv( ldap_passwordpolicy_err2txt( error ), 0, 0,
+                                                       &ui->msg );
+
+                                       switch ( error ) {
+                                               case PP_passwordExpired:
+                                                       ui->authz = RBAC_PASSWORD_EXPIRATION_WARNING;
+
+                                                       if ( expire >= 0 ) {
+                                                               char *unit = "seconds";
+                                                               if ( expire > 60 ) {
+                                                                       expire /= 60;
+                                                                       unit = "minutes";
+                                                               }
+                                                               if ( expire > 60 ) {
+                                                                       expire /= 60;
+                                                                       unit = "hours";
+                                                               }
+                                                               if ( expire > 24 ) {
+                                                                       expire /= 24;
+                                                                       unit = "days";
+                                                               }
+#if 0 /* Who warns about expiration so far in advance? */
+                                                               if (expire > 7) {
+                                                                       expire /= 7;
+                                                                       unit = "weeks";
+                                                               }
+                                                               if (expire > 4) {
+                                                                       expire /= 4;
+                                                                       unit = "months";
+                                                               }
+                                                               if (expire > 12) {
+                                                                       expire /= 12;
+                                                                       unit = "years";
+                                                               }
+#endif
+                                                       }
+
+                                                       //rs->sr_err = ;
+                                                       break;
+                                               case PP_accountLocked:
+                                                       ui->authz = RBAC_ACCOUNT_LOCKED;
+                                                       //rs->sr_err = ;
+                                                       break;
+                                               case PP_changeAfterReset:
+                                                       ui->authz = RBAC_CHANGE_AFTER_RESET;
+                                                       rs->sr_err = LDAP_SUCCESS;
+                                                       break;
+                                               case PP_passwordModNotAllowed:
+                                                       ui->authz = RBAC_NO_MODIFICATIONS;
+                                                       //rs->sr_err = ;
+                                                       break;
+                                               case PP_mustSupplyOldPassword:
+                                                       ui->authz = RBAC_MUST_SUPPLY_OLD;
+                                                       //rs->sr_err = ;
+                                                       break;
+                                               case PP_insufficientPasswordQuality:
+                                                       ui->authz = RBAC_INSUFFICIENT_QUALITY;
+                                                       //rs->sr_err = ;
+                                                       break;
+                                               case PP_passwordTooShort:
+                                                       ui->authz = RBAC_PASSWORD_TOO_SHORT;
+                                                       //rs->sr_err = ;
+                                                       break;
+                                               case PP_passwordTooYoung:
+                                                       ui->authz = RBAC_PASSWORD_TOO_YOUNG;
+                                                       //rs->sr_err = ;
+                                                       break;
+                                               case PP_passwordInHistory:
+                                                       ui->authz = RBAC_HISTORY_VIOLATION;
+                                                       //rs->sr_err = ;
+                                                       break;
+                                               case PP_noError:
+                                               default:
+                                                       // do nothing
+                                                       //ui->authz = RBAC_PASSWORD_GOOD;
+                                                       rs->sr_err = LDAP_SUCCESS;
+                                                       break;
+                                       }
+
+//                                     switch (error) {
+//                                     case PP_passwordExpired:
+                                               /* report this during authz */
+//                                             rs->sr_err = LDAP_SUCCESS;
+                                               /* fallthru */
+//                                     case PP_changeAfterReset:
+//                                             ui->authz = RBAC_BIND_NEW_AUTHTOK_REQD;
+//                                     }
+                               }
+                       }
+                       ldap_unbind_ext( ld, NULL, NULL );
+               }
+       }
+
+       return 0;
+}
+
+/* exported user functions */
+int
+rbac_authenticate_user( Operation *op, rbac_user_t *userp )
+{
+       int rc = LDAP_SUCCESS;
+       slap_callback cb = { 0 };
+       SlapReply rs2 = { REP_RESULT };
+       Operation op2 = *op;
+       LDAPControl *sctrls[4];
+       LDAPControl sctrl[3];
+       int nsctrls = 0;
+       LDAPControl c;
+       struct berval ber_bvnull = BER_BVNULL;
+       struct berval dn, ndn;
+
+       rc = dnPrettyNormal( 0, &userp->dn, &dn, &ndn, NULL );
+       if ( rc != LDAP_SUCCESS ) {
+               goto done;
+       }
+
+       cb.sc_response = rbac_bind_cb;
+       cb.sc_private = userp;
+       op2.o_callback = &cb;
+       op2.o_dn = ber_bvnull;
+       op2.o_ndn = ber_bvnull;
+       op2.o_tag = LDAP_REQ_BIND;
+       op2.o_protocol = LDAP_VERSION3;
+       op2.orb_method = LDAP_AUTH_SIMPLE;
+       op2.orb_cred = userp->password;
+       op2.o_req_dn = dn;
+       op2.o_req_ndn = ndn;
+
+       // loading the ldap pw policy controls loaded into here, added by smm:
+       c.ldctl_oid = LDAP_CONTROL_PASSWORDPOLICYREQUEST;
+       c.ldctl_value.bv_val = NULL;
+       c.ldctl_value.bv_len = 0;
+       c.ldctl_iscritical = 0;
+       sctrl[nsctrls] = c;
+       sctrls[nsctrls] = &sctrl[nsctrls];
+       sctrls[++nsctrls] = NULL;
+       op2.o_ctrls = sctrls;
+
+       if ( ppolicy_cid < 0 ) {
+               rc = slap_find_control_id( LDAP_CONTROL_PASSWORDPOLICYREQUEST,
+                               &ppolicy_cid );
+               if ( rc != LDAP_SUCCESS ) {
+                       goto done;
+               }
+       }
+       // smm - need to set the control flag too:
+       op2.o_ctrlflag[ppolicy_cid] = SLAP_CONTROL_CRITICAL;
+
+       slap_op_time( &op2.o_time, &op2.o_tincr );
+       op2.o_bd = frontendDB;
+       rc = op2.o_bd->be_bind( &op2, &rs2 );
+       if ( userp->authz > 0 ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_authenticate_user (%s): "
+                               "password policy violation (%d)\n",
+                               userp->dn.bv_val ? userp->dn.bv_val : "NULL", userp->authz );
+       }
+
+done:;
+       ch_free( dn.bv_val );
+       ch_free( ndn.bv_val );
+
+       Debug( LDAP_DEBUG_ANY, "rbac_authenticate_user (%s): "
+                       "rc (%d)\n",
+                       userp->dn.bv_val ? userp->dn.bv_val : "NULL", rc );
+       return rc;
+}
+
+/*
+       isvalidusername(): from OpenLDAP ~/contrib/slapd-modules/nssov/passwd.c
+       Checks to see if the specified name is a valid user name.
+
+    This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, 3.426 User Name
+        and 3.276 Portable Filename Character Set):
+        http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426
+        http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276
+
+        The standard defines user names valid if they contain characters from
+        the set [A-Za-z0-9._-] where the hyphen should not be used as first
+        character. As an extension this test allows the dolar '$' sign as the last
+        character to support Samba special accounts.
+*/
+static int
+isvalidusername( struct berval *bv )
+{
+       int i;
+       char *name = bv->bv_val;
+       if ( (name == NULL) || ( name[0] == '\0' ) ) return 0;
+       /* check first character */
+       if ( !( ( name[0] >= 'A' && name[0] <= 'Z' ) ||
+                                ( name[0] >= 'a' && name[0] <= 'z' ) ||
+                                ( name[0] >= '0' && name[0] <= '9' ) || name[0] == '.' ||
+                                name[0] == '_' ) )
+               return 0;
+       /* check other characters */
+       for ( i = 1; i < bv->bv_len; i++ ) {
+               if ( name[i] == '$' ) {
+                       /* if the char is $ we require it to be the last char */
+                       if ( name[i + 1] != '\0' ) return 0;
+               } else if ( !( ( name[i] >= 'A' && name[i] <= 'Z' ) ||
+                                                       ( name[i] >= 'a' && name[i] <= 'z' ) ||
+                                                       ( name[i] >= '0' && name[i] <= '9' ) ||
+                                                       name[i] == '.' || name[i] == '_' ||
+                                                       name[i] == '-' ) )
+                       return 0;
+       }
+       /* no test failed so it must be good */
+       return -1;
+}
+
+rbac_user_t *
+rbac_read_user( Operation *op, rbac_req_t *reqp )
+{
+       int rc = LDAP_SUCCESS;
+       tenant_info_t *tenantp = rbac_tid2tenant( &reqp->tenantid );
+       rbac_user_t *userp = NULL;
+       char fbuf[RBAC_BUFLEN];
+       struct berval filter = { sizeof(fbuf), fbuf };
+       SlapReply rs2 = { REP_RESULT };
+       Operation op2 = *op;
+       slap_callback cb = { 0 };
+       rbac_callback_info_t rbac_cb;
+
+       if ( !tenantp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+                               "missing tenant information\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* uid is a pre-requisite for reading the user information */
+       if ( BER_BVISNULL( &reqp->uid ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+                               "missing uid, unable to read user entry\n" );
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       if ( !isvalidusername( &reqp->uid ) ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_read_user: "
+                               "invalid user id\n" );
+               rc = LDAP_NO_SUCH_OBJECT;
+               goto done;
+       }
+
+       rbac_cb.tenantp = tenantp;
+       rbac_cb.private = NULL;
+
+       memset( fbuf, 0, sizeof(fbuf) );
+       strcpy( fbuf, "uid=" );
+       strncat( fbuf, reqp->uid.bv_val, reqp->uid.bv_len );
+       filter.bv_val = fbuf;
+       filter.bv_len = strlen( fbuf );
+
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_create_session: "
+                               "invalid DN syntax\n" );
+               goto done;
+       }
+
+       cb.sc_private = &rbac_cb;
+       cb.sc_response = rbac_read_user_cb;
+       op2.o_callback = &cb;
+       op2.o_tag = LDAP_REQ_SEARCH;
+       op2.o_dn = tenantp->admin;
+       op2.o_ndn = tenantp->admin;
+       op2.o_req_dn = tenantp->users_basedn;
+       op2.o_req_ndn = tenantp->users_basedn;
+       op2.ors_filterstr = filter;
+       op2.ors_filter = str2filter_x( &op2, filter.bv_val );
+       op2.ors_scope = LDAP_SCOPE_SUBTREE;
+       op2.ors_attrs = tenantp->schema->user_attrs;
+       op2.ors_tlimit = SLAP_NO_LIMIT;
+       op2.ors_slimit = SLAP_NO_LIMIT;
+       op2.ors_attrsonly = 0;
+       op2.o_bd = frontendDB;
+       op2.ors_limit = NULL;
+       rc = op2.o_bd->be_search( &op2, &rs2 );
+       filter_free_x( &op2, op2.ors_filter, 1 );
+
+done:;
+       if ( rc == LDAP_SUCCESS && rbac_cb.private ) {
+               userp = (rbac_user_t *)rbac_cb.private;
+               if ( !BER_BVISNULL( &reqp->authtok ) )
+                       ber_dupbv( &userp->password, &reqp->authtok );
+               rbac_cb.private = NULL;
+               return userp;
+       } else {
+               userp = (rbac_user_t *)rbac_cb.private;
+               rbac_free_user( userp );
+               return NULL;
+       }
+}
+
+/* evaluate temporal constraints for the user */
+int
+rbac_user_temporal_constraint( rbac_user_t *userp )
+{
+       int rc = LDAP_SUCCESS;
+       rbac_constraint_t *cp = NULL;
+
+       if ( BER_BVISNULL( &userp->constraints ) ) {
+               /* no temporal constraint */
+               goto done;
+       }
+
+       cp = rbac_bv2constraint( &userp->constraints );
+       if ( !cp ) {
+               Debug( LDAP_DEBUG_ANY, "rbac_user_temporal_constraint: "
+                               "invalid user constraint \n" );
+               rc = LDAP_OTHER;
+               goto done;
+       }
+
+       rc = rbac_check_time_constraint( cp );
+
+done:;
+       rbac_free_constraint( cp );
+
+       return rc;
+}
+
+/*
+rbac_constraint_t *
+rbac_user_role_constraintsx(rbac_user_t *userp)
+{
+       rbac_constraint_t *tmp, *cp = NULL;
+       int i = 0;
+
+       if (!userp || !userp->role_constraints)
+               goto done;
+
+       while (!BER_BVISNULL(&userp->role_constraints[i])) {
+               tmp = rbac_bv2constraint(&userp->role_constraints[i++]);
+               if (tmp) {
+                       if (!cp) {
+                               cp = tmp;
+                       } else {
+                               tmp->next = cp;
+                               cp = tmp;
+                       }
+               }
+       }
+
+done:;
+       return cp;
+}
+*/
+
+rbac_constraint_t *
+rbac_user_role_constraints( BerVarray values )
+{
+       rbac_constraint_t *curr, *head = NULL;
+       int i = 0;
+
+       if ( values ) {
+               while ( !BER_BVISNULL( &values[i] ) ) {
+                       curr = rbac_bv2constraint( &values[i++] );
+                       if ( curr ) {
+                               curr->next = head;
+                               head = curr;
+                       }
+               }
+       }
+
+       return head;
+}
+
+/*
+
+void main() {
+   item * curr, * head;
+   int i;
+
+   head = NULL;
+
+   for(i=1;i<=10;i++) {
+      curr = (item *)malloc(sizeof(item));
+      curr->val = i;
+      curr->next  = head;
+      head = curr;
+   }
+
+   curr = head;
+
+   while(curr) {
+      printf("%d\n", curr->val);
+      curr = curr->next ;
+   }
+}
+
+ */
+
+/*
+ *
+rbac_user_role_constraints2(BerVarray values)
+{
+       rbac_constraint_t *tmp, *cp = NULL;
+       int i = 0;
+
+       if (!values)
+               goto done;
+
+       while (!BER_BVISNULL(&values[i])) {
+               tmp = rbac_bv2constraint(&values[i++]);
+               if (tmp) {
+                       if (!cp) {
+                               cp = tmp;
+                       } else {
+                               tmp->next = cp;
+                               cp = tmp;
+                               //cp->next = tmp;
+                               //cp = tmp->next;
+
+                       }
+               }
+       }
+
+done:;
+       return cp;
+}
+
+
+rbac_user_role_constraints3(rbac_constraint_t *values)
+{
+       rbac_constraint_t *tmp, *cp = NULL;
+       int i = 0;
+
+       if (!values)
+               goto done;
+
+       while (!BER_BVISNULL(values[i])) {
+               tmp = rbac_bv2constraint(&values[i++]);
+               if (tmp) {
+                       if (!cp) {
+                               cp = tmp;
+                       } else {
+                               tmp->next = cp;
+                               cp = tmp;
+                       }
+               }
+       }
+
+done:;
+       return cp;
+}
+*/
+
+void
+rbac_free_user( rbac_user_t *userp )
+{
+       if ( !userp ) return;
+
+       if ( !BER_BVISNULL( &userp->tenantid ) ) {
+               ber_memfree( userp->tenantid.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &userp->uid ) ) {
+               ber_memfree( userp->uid.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &userp->dn ) ) {
+               ber_memfree( userp->dn.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &userp->constraints ) ) {
+               ber_memfree( userp->constraints.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &userp->password ) ) {
+               ber_memfree( userp->password.bv_val );
+       }
+
+       if ( !BER_BVISNULL( &userp->msg ) ) {
+               ber_memfree( userp->msg.bv_val );
+       }
+
+       if ( userp->roles ) ber_bvarray_free( userp->roles );
+
+       if ( userp->role_constraints ) ber_bvarray_free( userp->role_constraints );
+
+       ch_free( userp );
+}
diff --git a/contrib/slapd-modules/rbac/slapo-rbac.5 b/contrib/slapd-modules/rbac/slapo-rbac.5
new file mode 100644 (file)
index 0000000..453bcbc
--- /dev/null
@@ -0,0 +1,157 @@
+.TH SLAPO_RBAC 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 1999-2021 SYMAS Corporation All Rights Reserved.
+.\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapo\-rbac \- RBAC0 overlay to slapd
+.SH SYNOPSIS
+ETCDIR/slapd.conf
+.SH DESCRIPTION
+.LP
+The 
+.B slapo-rbac
+overlay
+is an implementation of the ANSI INCITS 359 Role-Based Access Control (RBAC) Core.
+When instantiated, it intercepts, decodes and enforces specific RBAC policies per the Apache Fortress RBAC data formats.
+.P
+The overlay provides a set of extended operations.
+They include session create/delete, checkAccess, addActiveRole, dropActiveRole and sessionRoles.
+.P
+
+.SH CONFIGURATION
+These 
+.B slapd.conf
+configuration options apply to the slapo-rbac overlay. 
+
+.TP
+.B overlay     rbac
+This tag gets applied to the RBAC configuration db (see example below).
+.TP
+.B rbac-default-users-base-dn "ou=People,dc=example,dc=com"
+Points to the container that contains the Apache Fortress users.
+.TP
+.B rbac-default-roles-base-dn "ou=Roles,ou=RBAC,dc=example,dc=com"
+Points to the container that contains the Apache Fortress roles.
+.TP
+.B rbac-default-permissions-base-dn "ou=Permissions,ou=RBAC,dc=example,dc=com"
+Points to the container that contains the Apache Fortress perms.
+.TP
+.B rbac-default-sessions-base-dn "cn=rbac"
+Points to the suffix of the RBAC sessions.
+.TP
+.B rbac-default-audit-base-dn "cn=audit"
+Points to the suffix where the audit records are stored.
+.TP
+.B rbac-admin "cn=manager,dc=example,dc=com"
+A service account that has read access to the entire Apache Fortress DIT.
+.TP
+.B rbac-pwd "{SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU"
+The password according to the service account.
+.TP
+.B rbac-session-admin "cn=manager,cn=rbac"
+The root dn of the RBAC sessions database.
+.TP
+.B rbac-session-admin-pwd {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+The password corresponding with the session database.
+.TP
+.RE
+
+.SH EXAMPLES
+.LP
+.RS
+.nf
+
+This overlay requires the
+.B rbac.schema
+loaded and three additional database config sections, one to store rbac
+sessions, second to store the audit records and third to hold the overlay's
+config parameters. They should appear after the existing Apache Fortress db
+config.
+
+.TP 
+1. Session Database: Used to store the RBAC sessions corresponding to a logged in user.
+.B database    mdb
+.B suffix      "cn=rbac"
+.B rootdn      "cn=manager,cn=rbac"
+.B rootpw      {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B index       rbacSessid  eq
+.B directory   "/var/openldap/rbacsess"
+.B overlay     dds
+.B dds-default-ttl     3600
+.B dds-max-dynamicObjects      100000
+.B dbnosync
+.B checkpoint  64 5
+.PP
+
+.TP
+2. Audit Database: Stores records that track user's activities.
+.B database    mdb
+.B suffix      "cn=audit"
+.B rootdn      "cn=manager,cn=audit"
+.B rootpw      {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B directory   "/var/openldap/rbacaudit"
+.B dbnosync
+.B checkpoint    64 5
+
+.PP
+
+.TP
+3. Config Database: Stores the parameters needed for this overlay to work.
+.B database    mdb
+.B suffix              "dc=rbac"
+.B rootdn              "cn=manager,dc=rbac"
+.B rootpw              {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+.B directory   "/var/openldap/rbacoverlay"
+.B overlay     rbac
+.B rbac-default-tenant-id "example"
+.B rbac-default-users-base-dn "ou=People,dc=example,dc=com"
+.B rbac-default-roles-base-dn "ou=Roles,ou=RBAC,dc=example,dc=com"
+.B rbac-default-permissions-base-dn "ou=Permissions,ou=RBAC,dc=example,dc=com"
+.B rbac-default-sessions-base-dn "cn=rbac"
+.B rbac-default-audit-base-dn "cn=audit"
+.B rbac-admin "cn=manager,dc=example,dc=com"
+.B rbac-pwd "{SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU"
+.B rbac-session-admin "cn=manager,cn=rbac"
+.B rbac-session-admin-pwd {SSHA}pSOV2TpCxj2NMACijkcMko4fGrFopctU
+
+.fi
+.RE
+
+.SH SEE ALSO
+.BR ldap (3),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-chain (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.BR ldap (3),
+.BR slapd.conf (5),
+.BR slapd\-config (5),
+.BR slapo\-chain (5).
+.LP
+"OpenLDAP Administrator's Guide" (http://www.OpenLDAP.org/doc/admin/)
+.LP
+
+.UR https://profsandhu.com/journals/tissec/ANSI+INCITS+359-2004.pdf
+.UE ANSI INCITS 359 Role-Based Access Control specification
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/README.md
+.UE Apache Fortress README
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/README-QUICKSTART-SLAPD.md
+.UE Apache Fortress OpenLDAP Quickstart 
+
+.UR https://github.com/apache/directory-fortress-core/blob/master/ldap/schema/fortress.schema
+.UE Apache Fortress RBAC schema
+
+.SH BUGS
+This overlay is experimental.
+
+.SH ACKNOWLEDGEMENTS
+.P
+This module was written in 2013 by Ted Cheng of Symas Corporation
+with a little help from Matt Hardin, Howard Chu, Shawn McKinney.
+.P
+.so ../Project
diff --git a/contrib/slapd-modules/rbac/util.c b/contrib/slapd-modules/rbac/util.c
new file mode 100644 (file)
index 0000000..d25d02e
--- /dev/null
@@ -0,0 +1,531 @@
+/* util.c - RBAC utility */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+
+#include <ac/ctype.h>
+#include <ac/string.h>
+
+#include "slap.h"
+#include "slap-config.h"
+#include "lutil.h"
+
+#include "rbac.h"
+
+#define DELIMITER '$'
+
+#define SUNDAY 0x01
+#define MONDAY 0x02
+#define TUESDAY 0x04
+#define WEDNESDAY 0x08
+#define THURSDAY 0x10
+#define FRIDAY 0x20
+#define SATURDAY 0x40
+
+#define ALL_WEEK "all"
+
+void
+rbac_free_constraint( rbac_constraint_t *cp )
+{
+       if ( !cp ) return;
+
+       if ( !BER_BVISNULL( &cp->name ) ) {
+               ch_free( cp->name.bv_val );
+       }
+
+       ch_free( cp );
+}
+
+void
+rbac_free_constraints( rbac_constraint_t *constraints )
+{
+       rbac_constraint_t *cp, *tmp;
+
+       if ( !constraints ) return;
+
+       tmp = constraints;
+       while ( tmp ) {
+               cp = tmp->next;
+               rbac_free_constraint( tmp );
+               tmp = cp;
+       }
+
+       return;
+}
+
+rbac_constraint_t *
+rbac_alloc_constraint()
+{
+       rbac_constraint_t *cp = NULL;
+
+       cp = ch_calloc( 1, sizeof(rbac_constraint_t) );
+       return cp;
+}
+
+static int
+is_well_formed_constraint( struct berval *bv )
+{
+       int rc = LDAP_SUCCESS;
+
+       /* assume well-formed role/user-constraints, for the moment */
+
+       if ( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY, "is_well_formed_constraint: "
+                               "rbac role/user constraint not well-formed: %s\n",
+                               bv->bv_val );
+       }
+
+       return rc;
+}
+
+/* input contains 4 digits, representing time */
+/* in hhmm format */
+static int
+constraint_parse_time( char *input )
+{
+       int btime;
+       char *ptr = input;
+
+       btime = ( *ptr++ - '0' ) * 12;
+       btime += ( *ptr++ - '0' );
+       btime *= 60; /* turning into mins */
+       btime += ( *ptr++ - '0' ) * 10;
+       btime += ( *ptr++ - '0' );
+       btime *= 60; /* turning into secs */
+
+       return btime;
+}
+
+/* input contains 4 digits, representing year */
+/* in yyyy format */
+static int
+constraint_parse_year( char *input )
+{
+       int i;
+       int year = 0;
+       char *ptr = input;
+
+       for ( i = 0; i <= 3; i++, ptr++ ) {
+               year = year * 10 + *ptr - '0';
+       }
+
+       return year;
+}
+
+/* input contains 2 digits, representing month */
+/* in mm format */
+static int
+constraint_parse_month( char *input )
+{
+       int i;
+       int month = 0;
+       char *ptr = input;
+
+       for ( i = 0; i < 2; i++, ptr++ ) {
+               month = month * 10 + *ptr - '0';
+       }
+
+       return month;
+}
+
+/* input contains 2 digits, representing day in month */
+/* in dd format */
+static int
+constraint_parse_day_in_month( char *input )
+{
+       int i;
+       int day_in_month = 0;
+       char *ptr = input;
+
+       for ( i = 0; i < 2; i++, ptr++ ) {
+               day_in_month = day_in_month * 10 + *ptr - '0';
+       }
+
+       return day_in_month;
+}
+
+rbac_constraint_t *
+rbac_bv2constraint( struct berval *bv )
+{
+       rbac_constraint_t *cp = NULL;
+       int rc = LDAP_SUCCESS;
+       char *ptr, *endp = NULL;
+       int len = 0;
+       int year, month, mday;
+
+       if ( !bv || BER_BVISNULL( bv ) ) goto done;
+
+       rc = is_well_formed_constraint( bv );
+       if ( rc != LDAP_SUCCESS ) {
+               goto done;
+       }
+
+       cp = rbac_alloc_constraint();
+       if ( !cp ) {
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* constraint name */
+       ptr = bv->bv_val;
+       endp = ptr;
+       while ( *endp != DELIMITER ) {
+               endp++;
+               len++;
+       }
+
+       if ( len > 0 ) {
+               cp->name.bv_val = ch_malloc( len + 1 );
+               strncpy( cp->name.bv_val, ptr, len );
+               cp->name.bv_val[len] = '\0';
+               cp->name.bv_len = len;
+       } else {
+               rc = LDAP_OTHER;
+               goto done;
+       }
+
+       /* allowed inactivity period */
+       ptr = endp;
+       endp++;
+       if ( isdigit( *endp ) ) {
+               int secs = 0;
+               while ( isdigit( *endp ) ) {
+                       secs = secs * 10 + *endp - '0';
+                       endp++;
+               }
+               cp->allowed_inactivity = secs;
+       } else if ( *endp != DELIMITER ) {
+               rc = LDAP_OTHER;
+               goto done;
+       }
+
+       ptr = endp;
+       endp = ptr + 1;
+
+       /* begin time */
+       if ( isdigit( *endp ) ) {
+               cp->begin_time = constraint_parse_time( endp );
+               while ( isdigit( *endp ) )
+                       endp++;
+       }
+
+       ptr = endp;
+       while ( *ptr != DELIMITER )
+               ptr++;
+       endp = ptr + 1;
+
+       /* end time */
+       if ( isdigit( *endp ) ) {
+               cp->end_time = constraint_parse_time( endp );
+               while ( isdigit( *endp ) )
+                       endp++;
+       }
+
+       ptr = endp;
+       while ( *ptr != DELIMITER )
+               ptr++;
+       endp = ptr + 1;
+
+       /* begin year/month/day_in_month */
+       if ( isdigit( *endp ) ) {
+               lutil_tm tm;
+               year = constraint_parse_year( endp );
+               endp += 4;
+               month = constraint_parse_month( endp );
+               endp += 2;
+               mday = constraint_parse_day_in_month( endp );
+               endp += 2;
+
+               tm.tm_year = year - 1900;
+               tm.tm_mon = month - 1;
+               tm.tm_mday = mday;
+               tm.tm_sec = 0;
+               tm.tm_min = 0;
+               tm.tm_hour = 0;
+
+               lutil_tm2time( &tm, &cp->begin_date );
+       }
+
+       ptr = endp;
+       while ( *ptr != DELIMITER )
+               ptr++;
+       endp = ptr + 1;
+
+       /* end year/month/day_in_month */
+       if ( isdigit( *endp ) ) {
+               lutil_tm tm;
+               year = constraint_parse_year( endp );
+               endp += 4;
+               month = constraint_parse_month( endp );
+               endp += 2;
+               mday = constraint_parse_day_in_month( endp );
+               endp += 2;
+
+               tm.tm_year = year - 1900;
+               tm.tm_mon = month - 1;
+               tm.tm_mday = mday;
+               tm.tm_sec = 0;
+               tm.tm_min = 0;
+               tm.tm_hour = 0;
+
+               lutil_tm2time( &tm, &cp->end_date );
+       }
+
+       ptr = endp;
+       while ( *ptr != DELIMITER )
+               ptr++;
+       endp = ptr + 1;
+
+       /* begin lock year/month/day_in_month */
+       if ( isdigit( *endp ) ) {
+               lutil_tm tm;
+               year = constraint_parse_year( endp );
+               endp += 4;
+               month = constraint_parse_month( endp );
+               endp += 2;
+               mday = constraint_parse_day_in_month( endp );
+               endp += 2;
+
+               tm.tm_year = year - 1900;
+               tm.tm_mon = month - 1;
+               tm.tm_mday = mday;
+               tm.tm_sec = 0;
+               tm.tm_min = 0;
+               tm.tm_hour = 0;
+
+               lutil_tm2time( &tm, &cp->begin_lock_date );
+       }
+
+       ptr = endp;
+       while ( *ptr != DELIMITER )
+               ptr++;
+       endp = ptr + 1;
+
+       /* end lock year/month/day_in_month */
+       if ( isdigit( *endp ) ) {
+               lutil_tm tm;
+
+               year = constraint_parse_year( endp );
+               endp += 4;
+               month = constraint_parse_month( endp );
+               endp += 2;
+               mday = constraint_parse_day_in_month( endp );
+               endp += 2;
+
+               tm.tm_year = year - 1900;
+               tm.tm_mon = month - 1;
+               tm.tm_mday = mday;
+               tm.tm_sec = 0;
+               tm.tm_min = 0;
+               tm.tm_hour = 0;
+
+               lutil_tm2time( &tm, &cp->end_lock_date );
+       }
+
+       ptr = endp;
+       while ( *ptr != DELIMITER )
+               ptr++;
+       endp = ptr + 1;
+
+       /* dayMask */
+
+       /* allow "all" to mean the entire week */
+       if ( strncasecmp( endp, ALL_WEEK, strlen( ALL_WEEK ) ) == 0 ) {
+               cp->day_mask = SUNDAY | MONDAY | TUESDAY | WEDNESDAY | THURSDAY |
+                               FRIDAY | SATURDAY;
+       }
+
+       while ( *endp && isdigit( *endp ) ) {
+               switch ( *endp - '0' ) {
+                       case 1:
+                               cp->day_mask |= SUNDAY;
+                               break;
+                       case 2:
+                               cp->day_mask |= MONDAY;
+                               break;
+                       case 3:
+                               cp->day_mask |= TUESDAY;
+                               break;
+                       case 4:
+                               cp->day_mask |= WEDNESDAY;
+                               break;
+                       case 5:
+                               cp->day_mask |= THURSDAY;
+                               break;
+                       case 6:
+                               cp->day_mask |= FRIDAY;
+                               break;
+                       case 7:
+                               cp->day_mask |= SATURDAY;
+                               break;
+                       default:
+                               /* should not be here */
+                               rc = LDAP_OTHER;
+                               goto done;
+               }
+               endp++;
+       }
+
+done:;
+       if ( rc != LDAP_SUCCESS ) {
+               rbac_free_constraint( cp );
+               cp = NULL;
+       }
+
+       return cp;
+}
+
+static int
+constraint_day_of_week( rbac_constraint_t *cp, int wday )
+{
+       int rc = LDAP_UNWILLING_TO_PERFORM;
+
+       /* assumption: Monday is 1st day of a week */
+       switch ( wday ) {
+               case 1:
+                       if ( !(cp->day_mask & MONDAY) ) goto done;
+                       break;
+               case 2:
+                       if ( !(cp->day_mask & TUESDAY) ) goto done;
+                       break;
+               case 3:
+                       if ( !(cp->day_mask & WEDNESDAY) ) goto done;
+                       break;
+               case 4:
+                       if ( !(cp->day_mask & THURSDAY) ) goto done;
+                       break;
+               case 5:
+                       if ( !(cp->day_mask & FRIDAY) ) goto done;
+                       break;
+               case 6:
+                       if ( !(cp->day_mask & SATURDAY) ) goto done;
+                       break;
+               case 0:
+               case 7:
+                       if ( !(cp->day_mask & SUNDAY) ) goto done;
+                       break;
+               default:
+                       /* should not be here */
+                       goto done;
+       }
+
+       rc = LDAP_SUCCESS;
+
+done:;
+       return rc;
+}
+
+int
+rbac_check_time_constraint( rbac_constraint_t *cp )
+{
+       int rc = LDAP_UNWILLING_TO_PERFORM;
+       time_t now;
+       struct tm result, *resultp;
+
+       now = slap_get_time();
+
+       /*
+        * does slapd support day-of-week (wday)?
+        * using native routine for now.
+        * Win32's gmtime call is already thread-safe, to the _r
+        * decorator is unneeded.
+        */
+#ifdef _WIN32
+       resultp = gmtime( &now );
+#else
+       resultp = gmtime_r( &now, &result );
+#endif
+       if ( !resultp ) goto done;
+#if 0
+       timestamp.bv_val = timebuf;
+       timestamp.bv_len = sizeof(timebuf);
+       slap_timestamp(&now, &timestamp);
+       lutil_parsetime(timestamp.bv_val, &now_tm);
+       lutil_tm2time(&now_tm, &now_tt);
+#endif
+
+       if ( ( cp->begin_date.tt_sec > 0 && cp->begin_date.tt_sec > now ) ||
+                       ( cp->end_date.tt_sec > 0 && cp->end_date.tt_sec < now ) ) {
+               /* not within allowed time period */
+               goto done;
+       }
+
+       /* allowed time period during a day */
+       if ( cp->begin_time > 0 && cp->end_time > 0 ) {
+               int timeofday = ( resultp->tm_hour * 60 + resultp->tm_min ) * 60 +
+                               resultp->tm_sec;
+               if ( timeofday < cp->begin_time || timeofday > cp->end_time ) {
+                       /* not withing allowed time period in a day */
+                       goto done;
+               }
+       }
+
+       /* allowed day in a week */
+       if ( cp->day_mask > 0 ) {
+               rc = constraint_day_of_week( cp, resultp->tm_wday );
+               if ( rc != LDAP_SUCCESS ) goto done;
+       }
+
+       /* during lock-out period? */
+       if ( ( cp->begin_lock_date.tt_sec > 0 &&
+                                cp->begin_lock_date.tt_sec < now ) &&
+                       ( cp->end_lock_date.tt_sec > 0 &&
+                                       cp->end_lock_date.tt_sec > now ) ) {
+               /* within locked out period */
+               rc = LDAP_UNWILLING_TO_PERFORM;
+               goto done;
+       }
+
+       /* passed all tests */
+       rc = LDAP_SUCCESS;
+
+done:;
+       return rc;
+}
+
+rbac_constraint_t *
+rbac_role2constraint( struct berval *role, rbac_constraint_t *role_constraints )
+{
+       rbac_constraint_t *cp = NULL;
+
+       if ( !role_constraints || !role ) goto done;
+
+       cp = role_constraints;
+       while ( cp ) {
+               if ( ber_bvstrcasecmp( role, &cp->name ) == 0 ) {
+                       /* found the role constraint */
+                       goto done;
+               }
+               cp = cp->next;
+       }
+
+done:;
+       return cp;
+}
+
+void
+rbac_to_lower( struct berval *bv )
+{
+       // convert the berval to lower case:
+       int i;
+       for ( i = 0; i < bv->bv_len; i++ ) {
+               bv->bv_val[i] = tolower( bv->bv_val[i] );
+       }
+}