summaryrefslogtreecommitdiffstats
path: root/contrib/slapd-modules/rbac/rbacuser.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/slapd-modules/rbac/rbacuser.c')
-rw-r--r--contrib/slapd-modules/rbac/rbacuser.c620
1 files changed, 620 insertions, 0 deletions
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 );
+}