mirror of
https://git.openldap.org/openldap/openldap.git
synced 2026-01-05 14:42:10 -05:00
620 lines
14 KiB
C
620 lines
14 KiB
C
/* 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 );
|
|
}
|