1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
|
/** 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_err(SLAPI_LOG_ERR, plugin_name,
"aclanom_init - 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_err(SLAPI_LOG_ACL, plugin_name, "aclanom_gen_anomProfile - 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_err(SLAPI_LOG_ACL, plugin_name,
"aclanom_gen_anomProfile - 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_err(SLAPI_LOG_ACL, plugin_name,
"aclanom_gen_anomProfile - 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_err(SLAPI_LOG_ACL, plugin_name,
"aclanom_gen_anomProfile - 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_err(SLAPI_LOG_ACL, plugin_name, "aclanom_gen_anomProfile - 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_err(SLAPI_LOG_ERR, plugin_name,
"aclanom_gen_anomProfile - 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_err(SLAPI_LOG_ACL, plugin_name,
"aclanom_gen_anomProfile - 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_err(loglevel, plugin_name,
"aclanom_match_profile - conn=%" PRIu64 " 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_err(loglevel, plugin_name,
"aclanom_match_profile - conn=%" PRIu64 " 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_err(loglevel, plugin_name,
"aclanom_match_profile - conn=%" PRIu64 " 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_err(loglevel, plugin_name,
"aclanom_match_profile - conn=%" PRIu64 " 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;
}
|