diff options
Diffstat (limited to 'contrib/slapd-modules/rbac')
-rwxr-xr-x | contrib/slapd-modules/rbac/Makefile | 63 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/init.c | 324 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/jts.c | 198 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/ldap_rbac.h | 55 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/rbac.c | 2169 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/rbac.h | 402 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/rbacacl.c | 37 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/rbacaudit.c | 233 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/rbacperm.c | 233 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/rbacreq.c | 89 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/rbacsess.c | 999 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/rbacuser.c | 620 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/slapo-rbac.5 | 157 | ||||
-rw-r--r-- | contrib/slapd-modules/rbac/util.c | 531 |
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, ×tamp ); + + /* 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, ×tamp, 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, ×tamp); + 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] ); + } +} |