summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/rbac
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:32 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:32 +0000
commit5ea77a75dd2d2158401331879f3c8f47940a732c (patch)
treed89dc06e9f4850a900f161e25f84e922c4f86cc8 /contrib/slapd-modules/rbac
parentInitial commit. (diff)
downloadopenldap-5ea77a75dd2d2158401331879f3c8f47940a732c.tar.xz
openldap-5ea77a75dd2d2158401331879f3c8f47940a732c.zip
Adding upstream version 2.5.13+dfsg.upstream/2.5.13+dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'contrib/slapd-modules/rbac')
-rwxr-xr-xcontrib/slapd-modules/rbac/Makefile63
-rw-r--r--contrib/slapd-modules/rbac/init.c324
-rw-r--r--contrib/slapd-modules/rbac/jts.c198
-rw-r--r--contrib/slapd-modules/rbac/ldap_rbac.h55
-rw-r--r--contrib/slapd-modules/rbac/rbac.c2169
-rw-r--r--contrib/slapd-modules/rbac/rbac.h402
-rw-r--r--contrib/slapd-modules/rbac/rbacacl.c37
-rw-r--r--contrib/slapd-modules/rbac/rbacaudit.c233
-rw-r--r--contrib/slapd-modules/rbac/rbacperm.c233
-rw-r--r--contrib/slapd-modules/rbac/rbacreq.c89
-rw-r--r--contrib/slapd-modules/rbac/rbacsess.c999
-rw-r--r--contrib/slapd-modules/rbac/rbacuser.c620
-rw-r--r--contrib/slapd-modules/rbac/slapo-rbac.5157
-rw-r--r--contrib/slapd-modules/rbac/util.c531
14 files changed, 6110 insertions, 0 deletions
diff --git a/contrib/slapd-modules/rbac/Makefile b/contrib/slapd-modules/rbac/Makefile
new file mode 100755
index 0000000..1180bd6
--- /dev/null
+++ b/contrib/slapd-modules/rbac/Makefile
@@ -0,0 +1,63 @@
+# $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
+INSTALL = /usr/bin/install
+CC = gcc
+OPT = -g -O2
+DEFS = -DSLAPD_OVER_RBAC=SLAPD_MOD_DYNAMIC
+INCS = $(LDAP_INC)
+LIBS = $(LDAP_LIB)
+
+PROGRAMS = rbac.la
+MANPAGES = slapo-rbac.5
+
+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)
+mandir = $(exec_prefix)/share/man
+man5dir = $(mandir)/man5
+
+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) $(CFLAGS) $(OPT) $(CPPFLAGS) $(DEFS) $(INCS) -c $<
+
+all: $(PROGRAMS)
+
+rbac.la: $(LOBJS)
+ $(LIBTOOL) --mode=link $(CC) $(LDFLAGS) -version-info $(LTVER) \
+ -rpath $(moduledir) -module -o $@ $^ $(LIBS)
+
+clean:
+ rm -rf *.o *.lo *.la .libs
+
+install: install-lib install-man FORCE
+
+install-lib: $(PROGRAMS)
+ mkdir -p $(DESTDIR)$(moduledir)
+ for p in $(PROGRAMS) ; do \
+ $(LIBTOOL) --mode=install cp $$p $(DESTDIR)$(moduledir) ; \
+ done
+
+install-man: $(MANPAGES)
+ mkdir -p $(DESTDIR)$(man5dir)
+ $(INSTALL) -m 644 $(MANPAGES) $(DESTDIR)$(man5dir)
+
+FORCE:
+
diff --git a/contrib/slapd-modules/rbac/init.c b/contrib/slapd-modules/rbac/init.c
new file mode 100644
index 0000000..1925ae5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/init.c
@@ -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
index 0000000..c7c072b
--- /dev/null
+++ b/contrib/slapd-modules/rbac/jts.c
@@ -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
index 0000000..d57fe6e
--- /dev/null
+++ b/contrib/slapd-modules/rbac/ldap_rbac.h
@@ -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
index 0000000..4d2cff5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbac.c
@@ -0,0 +1,2169 @@
+/* rbac.c - RBAC main file */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2013-2022 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
index 0000000..4461236
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbac.h
@@ -0,0 +1,402 @@
+/* rbac.h - */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 1999-2022 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
index 0000000..269dcf5
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacacl.c
@@ -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
index 0000000..ef04ece
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacaudit.c
@@ -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
index 0000000..e1f6d79
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacperm.c
@@ -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
index 0000000..9942a00
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacreq.c
@@ -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
index 0000000..d18e312
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacsess.c
@@ -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 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;
+}
+
+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
index 0000000..59d3c01
--- /dev/null
+++ b/contrib/slapd-modules/rbac/rbacuser.c
@@ -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
index 0000000..453bcbc
--- /dev/null
+++ b/contrib/slapd-modules/rbac/slapo-rbac.5
@@ -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
index 0000000..11a5e54
--- /dev/null
+++ b/contrib/slapd-modules/rbac/util.c
@@ -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 within 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] );
+ }
+}