| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578 |
- /** BEGIN COPYRIGHT BLOCK
- * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
- * Copyright (C) 2005 Red Hat, Inc.
- * All rights reserved.
- *
- * License: GPL (version 3 or any later version).
- * See LICENSE for details.
- * END COPYRIGHT BLOCK **/
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- #include "acl.h"
- /************************************************************************
- Anonymous profile
- **************************************************************************/
- struct anom_targetacl {
- int anom_type; /* defines for anom types same as aci_type */
- int anom_access;
- Slapi_DN *anom_target; /* target of the ACL */
- Slapi_Filter *anom_filter; /* targetfilter part */
- char **anom_targetAttrs; /* list of attrs */
- };
- struct anom_profile {
- short anom_signature;
- short anom_numacls;
- struct anom_targetacl anom_targetinfo[ACL_ANOM_MAX_ACL];
- };
- static struct anom_profile *acl_anom_profile = NULL;
- static Slapi_RWLock *anom_rwlock = NULL;
- #define ANOM_LOCK_READ() slapi_rwlock_rdlock (anom_rwlock )
- #define ANOM_UNLOCK_READ() slapi_rwlock_unlock (anom_rwlock )
- #define ANOM_LOCK_WRITE() slapi_rwlock_wrlock (anom_rwlock )
- #define ANOM_UNLOCK_WRITE() slapi_rwlock_unlock (anom_rwlock )
- /*
- * aclanom_init ();
- * Generate a profile for the anonymous user. We can use this profile
- * later to determine what resources the client is allowed to.
- *
- * Dependency:
- * Before calling this, it is assumed that all the ACLs have been read
- * and parsed.
- *
- * We will go thru all the ACL and pick the ANYONE ACL and generate the anom
- * profile.
- *
- */
- int
- aclanom_init ()
- {
- acl_anom_profile = (struct anom_profile * )
- slapi_ch_calloc (1, sizeof ( struct anom_profile ) );
- if (( anom_rwlock = slapi_new_rwlock()) == NULL ) {
- slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
- "Failed in getting the ANOM rwlock\n" );
- return 1;
- }
- return 0;
- }
- /*
- * Depending on the context, this routine may need to take the
- * acicache read lock.
- */
- void
- aclanom_gen_anomProfile (acl_lock_flag_t lock_flag)
- {
- aci_t *aci = NULL;
- int i;
- Targetattr **srcattrArray;
- Targetattr *attr;
- struct anom_profile *a_profile;
- PRUint32 cookie;
- PR_ASSERT( lock_flag == DO_TAKE_ACLCACHE_READLOCK ||
- lock_flag == DONT_TAKE_ACLCACHE_READLOCK);
- /*
- * This routine requires two locks:
- * the one for the global cache in acllist_acicache_READ_LOCK() and
- * the one for the anom profile.
- * They _must_ be taken in the order presented here or there
- * is a deadlock scenario with acllist_remove_aci_needsLock() which
- * takes them is this order.
- */
- if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
- acllist_acicache_READ_LOCK();
- }
- ANOM_LOCK_WRITE ();
- a_profile = acl_anom_profile;
- if ( (!acl_get_aclsignature()) || ( !a_profile) ||
- (a_profile->anom_signature == acl_get_aclsignature()) ) {
- ANOM_UNLOCK_WRITE ();
- if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
- acllist_acicache_READ_UNLOCK();
- }
- return;
- }
- /* D0 we have one already. If we do, then clean it up */
- aclanom__del_profile(0);
- /* We have a new signature now */
- a_profile->anom_signature = acl_get_aclsignature();
- slapi_log_error(SLAPI_LOG_ACL, plugin_name, "GENERATING ANOM USER PROFILE\n");
- /*
- ** Go thru the ACL list and find all the ACLs which apply to the
- ** anonymous user i.e anyone. we can generate a profile for that.
- ** We will llok at the simple case i.e it matches
- ** cases not handled:
- ** 1) When there is a mix if rule types ( allows & denies )
- **
- */
- aci = acllist_get_first_aci ( NULL, &cookie );
- while ( aci ) {
- int a_numacl;
- struct slapi_filter *f;
- char **destattrArray;
- /*
- * We must not have a rule like: deny ( all ) userdn != "xyz"
- * or groupdn !=
- */
- if ( (aci->aci_type & ACI_HAS_DENY_RULE) &&
- ( (aci->aci_type & ACI_CONTAIN_NOT_USERDN ) ||
- (aci->aci_type & ACI_CONTAIN_NOT_GROUPDN) ||
- (aci->aci_type & ACI_CONTAIN_NOT_ROLEDN)) ){
- slapi_log_error(SLAPI_LOG_ACL, plugin_name,
- "CANCELLING ANOM USER PROFILE BECAUSE OF DENY RULE\n");
- goto cleanup;
- }
- /* Must be a anyone rule */
- if ( aci->aci_elevel != ACI_ELEVEL_USERDN_ANYONE ) {
- aci = acllist_get_next_aci ( NULL, aci, &cookie);
- continue;
- }
- if (! (aci->aci_access & ( SLAPI_ACL_READ | SLAPI_ACL_SEARCH)) ) {
- aci = acllist_get_next_aci ( NULL, aci, &cookie);
- continue;
- }
- /* If the rule has anything other than userdn = "ldap:///anyone"
- ** let's not consider complex rules - let's make this lean.
- */
- if ( aci->aci_ruleType & ~ACI_USERDN_RULE ){
- slapi_log_error(SLAPI_LOG_ACL, plugin_name,
- "CANCELLING ANOM USER PROFILE BECAUSE OF COMPLEX RULE\n");
- goto cleanup;
- }
- /* Must not be a or have a
- ** 1 ) DENY RULE 2) targetfilter
- ** 3) no target pattern ( skip monitor acl )
- */
- if ( aci->aci_type & ( ACI_HAS_DENY_RULE | ACI_TARGET_PATTERN |
- ACI_TARGET_NOT | ACI_TARGET_FILTER_NOT )) {
- const char *dn = slapi_sdn_get_dn ( aci->aci_sdn );
- /* see if this is a monitor acl */
- if (( strcasecmp ( dn, "cn=monitor") == 0 ) ||
- /* cn=monitor,cn=ldbm: No such object */
- ( strcasecmp ( dn, "cn=monitor,cn=ldbm") == 0 )) {
- aci = acllist_get_next_aci ( NULL, aci, &cookie);
- continue;
- } else {
- /* clean up before leaving */
- slapi_log_error(SLAPI_LOG_ACL, plugin_name,
- "CANCELLING ANOM USER PROFILE 1\n");
- goto cleanup;
- }
- }
- /* Now we have an ALLOW ACL which applies to anyone */
- a_numacl = a_profile->anom_numacls++;
- if ( a_profile->anom_numacls == ACL_ANOM_MAX_ACL ) {
- slapi_log_error(SLAPI_LOG_ACL, plugin_name, "CANCELLING ANOM USER PROFILE 2\n");
- goto cleanup;
- }
- if ( (f = aci->target) != NULL ) {
- char *avaType;
- struct berval *avaValue;
- slapi_filter_get_ava ( f, &avaType, &avaValue );
- a_profile->anom_targetinfo[a_numacl].anom_target =
- slapi_sdn_new_dn_byval ( avaValue->bv_val );
- } else {
- a_profile->anom_targetinfo[a_numacl].anom_target =
- slapi_sdn_dup ( aci->aci_sdn );
- }
- a_profile->anom_targetinfo[a_numacl].anom_filter = NULL;
- if ( aci->targetFilterStr ) {
- a_profile->anom_targetinfo[a_numacl].anom_filter = slapi_str2filter ( aci->targetFilterStr );
- if (NULL == a_profile->anom_targetinfo[a_numacl].anom_filter) {
- const char *dn = slapi_sdn_get_dn ( aci->aci_sdn );
- slapi_log_error(SLAPI_LOG_FATAL, plugin_name,
- "Error: invalid filter [%s] in anonymous aci in entry [%s]\n",
- aci->targetFilterStr, dn);
- goto cleanup;
- }
- }
- i = 0;
- srcattrArray = aci->targetAttr;
- while ( srcattrArray[i])
- i++;
- a_profile->anom_targetinfo[a_numacl].anom_targetAttrs =
- (char **) slapi_ch_calloc ( 1, (i+1) * sizeof(char *));
- srcattrArray = aci->targetAttr;
- destattrArray = a_profile->anom_targetinfo[a_numacl].anom_targetAttrs;
- i = 0;
- while ( srcattrArray[i] ) {
- attr = srcattrArray[i];
- if ( attr->attr_type & ACL_ATTR_FILTER ) {
- /* Do'nt want to support these kind now */
- destattrArray[i] = NULL;
- /* clean up before leaving */
- aclanom__del_profile (0);
- slapi_log_error(SLAPI_LOG_ACL, plugin_name,
- "CANCELLING ANOM USER PROFILE 3\n");
- goto cleanup;
- }
- destattrArray[i] = slapi_ch_strdup ( attr->u.attr_str );
- i++;
- }
- destattrArray[i] = NULL;
- aclutil_print_aci ( aci, "anom" );
- /* Here we are storing att the info from the acls. However
- ** we are only interested in a few things like ACI_TARGETATTR_NOT.
- */
- a_profile->anom_targetinfo[a_numacl].anom_type = aci->aci_type;
- a_profile->anom_targetinfo[a_numacl].anom_access = aci->aci_access;
-
- aci = acllist_get_next_aci ( NULL, aci, &cookie);
- }
- ANOM_UNLOCK_WRITE ();
- if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
- acllist_acicache_READ_UNLOCK();
- }
- return;
- cleanup:
- aclanom__del_profile (0);
- ANOM_UNLOCK_WRITE ();
- if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
- acllist_acicache_READ_UNLOCK();
- }
- }
- void
- aclanom_invalidateProfile ()
- {
- ANOM_LOCK_WRITE();
- if ( acl_anom_profile && acl_anom_profile->anom_numacls )
- acl_anom_profile->anom_signature = 0;
- ANOM_UNLOCK_WRITE();
- }
- /*
- * __aclanom_del_profile
- *
- * Cleanup the anonymous user's profile we have.
- *
- * ASSUMPTION: A WRITE LOCK HAS BEEN OBTAINED
- *
- */
- void
- aclanom__del_profile (int closing)
- {
- int i;
- struct anom_profile *a_profile;
- if ( (a_profile = acl_anom_profile) == NULL ) {
- return;
- }
- for ( i=0; i < a_profile->anom_numacls; i++ ) {
- int j = 0;
- char **destArray = a_profile->anom_targetinfo[i].anom_targetAttrs;
- /* Deallocate target */
- slapi_sdn_free ( &a_profile->anom_targetinfo[i].anom_target );
-
- /* Deallocate filter */
- if ( a_profile->anom_targetinfo[i].anom_filter )
- slapi_filter_free ( a_profile->anom_targetinfo[i].anom_filter, 1 );
- /* Deallocate attrs */
- if ( destArray ) {
- while ( destArray[j] ) {
- slapi_ch_free ( (void **) &destArray[j] );
- j++;
- }
- slapi_ch_free ( (void **) &destArray );
- }
- a_profile->anom_targetinfo[i].anom_targetAttrs = NULL;
- a_profile->anom_targetinfo[i].anom_type = 0;
- a_profile->anom_targetinfo[i].anom_access = 0;
- }
- a_profile->anom_numacls = 0;
- if(closing){
- slapi_destroy_rwlock(anom_rwlock);
- anom_rwlock = NULL;
- slapi_ch_free((void **)&acl_anom_profile);
- }
- /* Don't clean the signatue */
- }
- /*
- * This routine sets up a "context" for evaluation of access control
- * on a given entry for an anonymous user.
- * It just factors out the scope and targetfilter info into a list
- * of indices of the global anom profile list, that apply to this
- * entry, and stores them in the aclpb.
- * It's use relies on the way that access control is checked in the mailine search
- * code in the core server, namely: check filter, check entry, then check each
- * attribute. So, we call this in acl_access_allowed() before calling
- * aclanom_match_profile()--therafter, aclanom_match_profile() uses the
- * context to evaluate access to the entry and attributes.
- *
- * If there are no anom profiles, or the anom profiles get cancelled
- * due to complex anon acis, then that's OK, aclanom_match_profile()
- * returns -1 and the mainline acl code kicks in.
- *
- * The lifetime of this context info is the time it takes to check
- * access control for all parts of this entry (filter, entry, attributes).
- * So, if for an example an entry changes and a given anom profile entry
- * no longer applies, we will not notice until the next round of access
- * control checking on the entry--this is acceptable.
- *
- * The gain on doing this factoring in the following type of search
- * was approx 6%:
- * anon bind, 20 threads, exact match, ~20 attributes returned,
- * (searchrate & DirectoryMark).
- *
- */
- void
- aclanom_get_suffix_info(Slapi_Entry *e,
- struct acl_pblock *aclpb ) {
- int i;
- char *ndn = NULL;
- Slapi_DN *e_sdn;
- const char *aci_ndn;
- struct scoped_entry_anominfo *s_e_anominfo =
- &aclpb->aclpb_scoped_entry_anominfo;
- ANOM_LOCK_READ ();
-
- s_e_anominfo->anom_e_nummatched=0;
- ndn = slapi_entry_get_ndn ( e ) ;
- e_sdn= slapi_entry_get_sdn ( e ) ;
- for (i=acl_anom_profile->anom_numacls-1; i >= 0; i-- ) {
- aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target);
- if (!slapi_sdn_issuffix(e_sdn,acl_anom_profile->anom_targetinfo[i].anom_target)
- || (!slapi_is_rootdse(ndn) && slapi_is_rootdse(aci_ndn)))
- continue;
- if ( acl_anom_profile->anom_targetinfo[i].anom_filter ) {
- if ( slapi_vattr_filter_test( aclpb->aclpb_pblock, e,
- acl_anom_profile->anom_targetinfo[i].anom_filter,
- 0 /*don't do acess chk*/) != 0)
- continue;
- }
- s_e_anominfo->anom_e_targetInfo[s_e_anominfo->anom_e_nummatched]=i;
- s_e_anominfo->anom_e_nummatched++;
- }
- ANOM_UNLOCK_READ ();
- }
- /*
- * aclanom_match_profile
- * Look at the anonymous profile and see if we can use it or not.
- *
- *
- * Inputs:
- * Slapi_Pblock - The Pblock
- * Slapi_Entry *e - The entry for which we are asking permission.
- * char *attr - Attribute name
- * int access - access type
- *
- * Return:
- * LDAP_SUCCESS ( 0 ) - acess is allowed.
- * LDAP_INSUFFICIENT_ACCESS (50 ) - access denied.
- * -1 - didn't match the targets
- *
- * Assumptions:
- * The caller of this module has to make sure that the client is
- * an anonymous client.
- */
- int
- aclanom_match_profile (Slapi_PBlock *pb, struct acl_pblock *aclpb, Slapi_Entry *e,
- char *attr, int access )
- {
- struct anom_profile *a_profile;
- int result, i, k;
- char **destArray;
- int tmatched = 0;
- int loglevel;
- struct scoped_entry_anominfo *s_e_anominfo =
- &aclpb->aclpb_scoped_entry_anominfo;
- loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
- /* WE are only interested for READ/SEARCH */
- if ( !(access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) )
- return -1;
- /* If we are here means, the client is doing a anonymous read/search */
- if ((a_profile = acl_anom_profile) == NULL ) {
- return -1;
- }
- ANOM_LOCK_READ ();
- /* Check the signature first */
- if ( a_profile->anom_signature != acl_get_aclsignature () ) {
- /* Need to regenrate the signature.
- * Need a WRITE lock to generate the anom profile -
- * which is obtained in acl__gen_anom_user_profile (). Since
- * I don't have upgrade lock -- I have to do this way.
- */
- ANOM_UNLOCK_READ ();
- aclanom_gen_anomProfile (DO_TAKE_ACLCACHE_READLOCK);
- aclanom_get_suffix_info(e, aclpb );
- ANOM_LOCK_READ ();
- }
- /* doing this early saves use a malloc/free/normalize cost */
- if ( !a_profile->anom_numacls ) {
- ANOM_UNLOCK_READ ();
- return -1;
- }
- result = LDAP_INSUFFICIENT_ACCESS;
- for ( k=0; k<s_e_anominfo->anom_e_nummatched; k++ ) {
- short matched = 0;
- short j = 0;
-
- i = s_e_anominfo->anom_e_targetInfo[k];
-
- /* Check for right */
- if ( !(a_profile->anom_targetinfo[i].anom_access & access) )
- continue;
-
- /*
- * XXX rbyrne Don't really understand the role of this
- * but not causing any obvious bugs...get back to it.
- */
- tmatched++;
-
- if ( attr == NULL ) {
- result = LDAP_SUCCESS;
- break;
- }
- destArray = a_profile->anom_targetinfo[i].anom_targetAttrs;
- while ( destArray[j] ) {
- if ( strcasecmp ( destArray[j], "*") == 0 ||
- slapi_attr_type_cmp ( attr, destArray[j], 1 ) == 0 ) {
- matched = 1;
- break;
- }
- j++;
- }
-
- if ( a_profile->anom_targetinfo[i].anom_type & ACI_TARGET_ATTR_NOT )
- result = matched ? LDAP_INSUFFICIENT_ACCESS : LDAP_SUCCESS;
- else
- result = matched ? LDAP_SUCCESS : LDAP_INSUFFICIENT_ACCESS;
-
- if ( result == LDAP_SUCCESS )
- break;
- } /* for */
- if ( slapi_is_loglevel_set(loglevel) ) {
- char *ndn = NULL;
- Slapi_Operation *op = NULL;
- PRUint64 o_connid = 0xffffffffffffffff; /* no op */
- int o_opid = -1; /* no op */
-
- ndn = slapi_entry_get_ndn ( e ) ;
- slapi_pblock_get(pb, SLAPI_OPERATION, &op);
- if (op) {
- o_connid = op->o_connid;
- o_opid = op->o_opid;
- }
- if ( result == LDAP_SUCCESS) {
- const char *aci_ndn;
- aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target);
- if (access & SLAPI_ACL_MODDN) {
- slapi_log_error(loglevel, plugin_name,
- "conn=%" NSPRIu64 " op=%d: Allow access on entry(%s).attr(%s) (from %s) to anonymous: acidn=\"%s\"\n",
- o_connid, o_opid,
- ndn,
- attr ? attr:"NULL",
- aclpb->aclpb_moddn_source_sdn ? slapi_sdn_get_dn(aclpb->aclpb_moddn_source_sdn) : "NULL",
- aci_ndn);
-
- } else {
- slapi_log_error(loglevel, plugin_name,
- "conn=%" NSPRIu64 " op=%d: Allow access on entry(%s).attr(%s) to anonymous: acidn=\"%s\"\n",
- o_connid, o_opid,
- ndn,
- attr ? attr:"NULL",
- aci_ndn);
- }
- } else {
- if (access & SLAPI_ACL_MODDN) {
- slapi_log_error(loglevel, plugin_name,
- "conn=%" NSPRIu64 " op=%d: Deny access on entry(%s).attr(%s) (from %s) to anonymous\n",
- o_connid, o_opid,
- ndn, attr ? attr:"NULL" ,
- aclpb->aclpb_moddn_source_sdn ? slapi_sdn_get_dn(aclpb->aclpb_moddn_source_sdn) : "NULL");
- } else {
- slapi_log_error(loglevel, plugin_name,
- "conn=%" NSPRIu64 " op=%d: Deny access on entry(%s).attr(%s) to anonymous\n",
- o_connid, o_opid,
- ndn, attr ? attr:"NULL" );
- }
- }
- }
- ANOM_UNLOCK_READ ();
- if ( tmatched == 0)
- return -1;
- else
- return result;
- }
- int
- aclanom_is_client_anonymous ( Slapi_PBlock *pb )
- {
- char *clientDn;
- slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &clientDn );
- if (acl_anom_profile->anom_numacls &&
- acl_anom_profile->anom_signature &&
- (( NULL == clientDn) || (clientDn && *clientDn == '\0')) )
- return 1;
- return 0;
- }
|