aclanom.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #include "acl.h"
  39. /************************************************************************
  40. Anonymous profile
  41. **************************************************************************/
  42. struct anom_targetacl {
  43. int anom_type; /* defines for anom types same as aci_type */
  44. int anom_access;
  45. Slapi_DN *anom_target; /* target of the ACL */
  46. Slapi_Filter *anom_filter; /* targetfilter part */
  47. char **anom_targetAttrs; /* list of attrs */
  48. };
  49. struct anom_profile {
  50. short anom_signature;
  51. short anom_numacls;
  52. struct anom_targetacl anom_targetinfo[ACL_ANOM_MAX_ACL];
  53. };
  54. static struct anom_profile *acl_anom_profile = NULL;
  55. static PRRWLock *anom_rwlock = NULL;
  56. #define ANOM_LOCK_READ() PR_RWLock_Rlock (anom_rwlock )
  57. #define ANOM_UNLOCK_READ() PR_RWLock_Unlock (anom_rwlock )
  58. #define ANOM_LOCK_WRITE() PR_RWLock_Wlock (anom_rwlock )
  59. #define ANOM_UNLOCK_WRITE() PR_RWLock_Unlock (anom_rwlock )
  60. static void __aclanom__del_profile ();
  61. /*
  62. * aclanom_init ();
  63. * Generate a profile for the anonymous user. We can use this profile
  64. * later to determine what resources the client is allowed to.
  65. *
  66. * Dependency:
  67. * Before calling this, it is assumed that all the ACLs have been read
  68. * and parsed.
  69. *
  70. * We will go thru all the ACL and pick the ANYONE ACL and generate the anom
  71. * profile.
  72. *
  73. */
  74. int
  75. aclanom_init ()
  76. {
  77. acl_anom_profile = (struct anom_profile * )
  78. slapi_ch_calloc (1, sizeof ( struct anom_profile ) );
  79. if (( anom_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,"ANOM LOCK") ) == NULL ) {
  80. slapi_log_error( SLAPI_LOG_FATAL, plugin_name,
  81. "Failed in getting the ANOM rwlock\n" );
  82. return 1;
  83. }
  84. return 0;
  85. }
  86. /*
  87. * Depending on the context, this routine may need to take the
  88. * acicache read lock.
  89. */
  90. void
  91. aclanom_gen_anomProfile (acl_lock_flag_t lock_flag)
  92. {
  93. aci_t *aci = NULL;
  94. int i;
  95. Targetattr **srcattrArray;
  96. Targetattr *attr;
  97. struct anom_profile *a_profile;
  98. PRUint32 cookie;
  99. PR_ASSERT( lock_flag == DO_TAKE_ACLCACHE_READLOCK ||
  100. lock_flag == DONT_TAKE_ACLCACHE_READLOCK);
  101. /*
  102. * This routine requires two locks:
  103. * the one for the global cache in acllist_acicache_READ_LOCK() and
  104. * the one for the anom profile.
  105. * They _must_ be taken in the order presented here or there
  106. * is a deadlock scenario with acllist_remove_aci_needsLock() which
  107. * takes them is this order.
  108. */
  109. if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
  110. acllist_acicache_READ_LOCK();
  111. }
  112. ANOM_LOCK_WRITE ();
  113. a_profile = acl_anom_profile;
  114. if ( (!acl_get_aclsignature()) || ( !a_profile) ||
  115. (a_profile->anom_signature == acl_get_aclsignature()) ) {
  116. ANOM_UNLOCK_WRITE ();
  117. if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
  118. acllist_acicache_READ_UNLOCK();
  119. }
  120. return;
  121. }
  122. /* D0 we have one already. If we do, then clean it up */
  123. __aclanom__del_profile();
  124. /* We have a new signature now */
  125. a_profile->anom_signature = acl_get_aclsignature();
  126. slapi_log_error(SLAPI_LOG_ACL, plugin_name, "GENERATING ANOM USER PROFILE\n", 0,0,0);
  127. /*
  128. ** Go thru the ACL list and find all the ACLs which apply to the
  129. ** anonymous user i.e anyone. we can generate a profile for that.
  130. ** We will llok at the simple case i.e it matches
  131. ** cases not handled:
  132. ** 1) When there is a mix if rule types ( allows & denies )
  133. **
  134. */
  135. aci = acllist_get_first_aci ( NULL, &cookie );
  136. while ( aci ) {
  137. int a_numacl;
  138. struct slapi_filter *f;
  139. char **destattrArray;
  140. /*
  141. * We must not have a rule like: deny ( all ) userdn != "xyz"
  142. * or groupdn !=
  143. */
  144. if ( (aci->aci_type & ACI_HAS_DENY_RULE) &&
  145. ( (aci->aci_type & ACI_CONTAIN_NOT_USERDN ) ||
  146. (aci->aci_type & ACI_CONTAIN_NOT_GROUPDN) ||
  147. (aci->aci_type & ACI_CONTAIN_NOT_ROLEDN)) ){
  148. slapi_log_error(SLAPI_LOG_ACL, plugin_name,
  149. "CANCELLING ANOM USER PROFILE BECAUSE OF DENY RULE\n", 0,0,0);
  150. goto cleanup;
  151. }
  152. /* Must be a anyone rule */
  153. if ( aci->aci_elevel != ACI_ELEVEL_USERDN_ANYONE ) {
  154. aci = acllist_get_next_aci ( NULL, aci, &cookie);
  155. continue;
  156. }
  157. if (! (aci->aci_access & ( SLAPI_ACL_READ | SLAPI_ACL_SEARCH)) ) {
  158. aci = acllist_get_next_aci ( NULL, aci, &cookie);
  159. continue;
  160. }
  161. /* If the rule has anything other than userdn = "ldap:///anyone"
  162. ** let's not consider complex rules - let's make this lean.
  163. */
  164. if ( aci->aci_ruleType & ~ACI_USERDN_RULE ){
  165. slapi_log_error(SLAPI_LOG_ACL, plugin_name,
  166. "CANCELLING ANOM USER PROFILE BECAUSE OF COMPLEX RULE\n", 0,0,0);
  167. goto cleanup;
  168. }
  169. /* Must not be a or have a
  170. ** 1 ) DENY RULE 2) targetfilter
  171. ** 3) no target pattern ( skip monitor acl )
  172. */
  173. if ( aci->aci_type & ( ACI_HAS_DENY_RULE | ACI_TARGET_PATTERN |
  174. ACI_TARGET_NOT | ACI_TARGET_FILTER_NOT )) {
  175. const char *dn = slapi_sdn_get_dn ( aci->aci_sdn );
  176. /* see if this is a monitor acl */
  177. if (( strcasecmp ( dn, "cn=monitor") == 0 ) ||
  178. ( strcasecmp ( dn, "cn=monitor,cn=ldbm") == 0 )) {
  179. aci = acllist_get_next_aci ( NULL, aci, &cookie);
  180. continue;
  181. } else {
  182. /* clean up before leaving */
  183. slapi_log_error(SLAPI_LOG_ACL, plugin_name,
  184. "CANCELLING ANOM USER PROFILE 1\n", 0,0,0);
  185. goto cleanup;
  186. }
  187. }
  188. /* Now we have an ALLOW ACL which applies to anyone */
  189. a_numacl = a_profile->anom_numacls++;
  190. if ( a_profile->anom_numacls == ACL_ANOM_MAX_ACL ) {
  191. slapi_log_error(SLAPI_LOG_ACL, plugin_name, "CANCELLING ANOM USER PROFILE 2\n", 0,0,0);
  192. goto cleanup;
  193. }
  194. if ( (f = aci->target) != NULL ) {
  195. char *avaType;
  196. struct berval *avaValue;
  197. slapi_filter_get_ava ( f, &avaType, &avaValue );
  198. a_profile->anom_targetinfo[a_numacl].anom_target =
  199. slapi_sdn_new_dn_byval ( avaValue->bv_val );
  200. } else {
  201. a_profile->anom_targetinfo[a_numacl].anom_target =
  202. slapi_sdn_dup ( aci->aci_sdn );
  203. }
  204. a_profile->anom_targetinfo[a_numacl].anom_filter = NULL;
  205. if ( aci->targetFilterStr )
  206. a_profile->anom_targetinfo[a_numacl].anom_filter = slapi_str2filter ( aci->targetFilterStr );
  207. i = 0;
  208. srcattrArray = aci->targetAttr;
  209. while ( srcattrArray[i])
  210. i++;
  211. a_profile->anom_targetinfo[a_numacl].anom_targetAttrs =
  212. (char **) slapi_ch_calloc ( 1, (i+1) * sizeof(char *));
  213. srcattrArray = aci->targetAttr;
  214. destattrArray = a_profile->anom_targetinfo[a_numacl].anom_targetAttrs;
  215. i = 0;
  216. while ( srcattrArray[i] ) {
  217. attr = srcattrArray[i];
  218. if ( attr->attr_type & ACL_ATTR_FILTER ) {
  219. /* Do'nt want to support these kind now */
  220. destattrArray[i] = NULL;
  221. /* clean up before leaving */
  222. __aclanom__del_profile ();
  223. slapi_log_error(SLAPI_LOG_ACL, plugin_name,
  224. "CANCELLING ANOM USER PROFILE 3\n", 0,0,0);
  225. goto cleanup;
  226. }
  227. destattrArray[i] = slapi_ch_strdup ( attr->u.attr_str );
  228. i++;
  229. }
  230. destattrArray[i] = NULL;
  231. aclutil_print_aci ( aci, "anom" );
  232. /* Here we are storing att the info from the acls. However
  233. ** we are only interested in a few things like ACI_TARGETATTR_NOT.
  234. */
  235. a_profile->anom_targetinfo[a_numacl].anom_type = aci->aci_type;
  236. a_profile->anom_targetinfo[a_numacl].anom_access = aci->aci_access;
  237. aci = acllist_get_next_aci ( NULL, aci, &cookie);
  238. }
  239. ANOM_UNLOCK_WRITE ();
  240. if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
  241. acllist_acicache_READ_UNLOCK();
  242. }
  243. return;
  244. cleanup:
  245. __aclanom__del_profile ();
  246. ANOM_UNLOCK_WRITE ();
  247. if ( lock_flag == DO_TAKE_ACLCACHE_READLOCK ) {
  248. acllist_acicache_READ_UNLOCK();
  249. }
  250. }
  251. void
  252. aclanom_invalidateProfile ()
  253. {
  254. ANOM_LOCK_WRITE();
  255. if ( acl_anom_profile && acl_anom_profile->anom_numacls )
  256. acl_anom_profile->anom_signature = 0;
  257. ANOM_UNLOCK_WRITE();
  258. }
  259. /*
  260. * __aclanom_del_profile
  261. *
  262. * Cleanup the anonymous user's profile we have.
  263. *
  264. * ASSUMPTION: A WRITE LOCK HAS BEEN OBTAINED
  265. *
  266. */
  267. static void
  268. __aclanom__del_profile (void)
  269. {
  270. int i;
  271. struct anom_profile *a_profile;
  272. if ( (a_profile = acl_anom_profile) == NULL ) {
  273. return;
  274. }
  275. for ( i=0; i < a_profile->anom_numacls; i++ ) {
  276. int j = 0;
  277. char **destArray = a_profile->anom_targetinfo[i].anom_targetAttrs;
  278. /* Deallocate target */
  279. slapi_sdn_free ( &a_profile->anom_targetinfo[i].anom_target );
  280. /* Deallocate filter */
  281. if ( a_profile->anom_targetinfo[i].anom_filter )
  282. slapi_filter_free ( a_profile->anom_targetinfo[i].anom_filter, 1 );
  283. /* Deallocate attrs */
  284. if ( destArray ) {
  285. while ( destArray[j] ) {
  286. slapi_ch_free ( (void **) &destArray[j] );
  287. j++;
  288. }
  289. slapi_ch_free ( (void **) &destArray );
  290. }
  291. a_profile->anom_targetinfo[i].anom_targetAttrs = NULL;
  292. a_profile->anom_targetinfo[i].anom_type = 0;
  293. a_profile->anom_targetinfo[i].anom_access = 0;
  294. }
  295. a_profile->anom_numacls = 0;
  296. /* Don't clean the signatue */
  297. }
  298. /*
  299. * This routine sets up a "context" for evaluation of access control
  300. * on a given entry for an anonymous user.
  301. * It just factors out the scope and targetfilter info into a list
  302. * of indices of the global anom profile list, that apply to this
  303. * entry, and stores them in the aclpb.
  304. * It's use relies on the way that access control is checked in the mailine search
  305. * code in the core server, namely: check filter, check entry, then check each
  306. * attribute. So, we call this in acl_access_allowed() before calling
  307. * aclanom_match_profile()--therafter, aclanom_match_profile() uses the
  308. * context to evaluate access to the entry and attributes.
  309. *
  310. * If there are no anom profiles, or the anom profiles get cancelled
  311. * due to complex anon acis, then that's OK, aclanom_match_profile()
  312. * returns -1 and the mainline acl code kicks in.
  313. *
  314. * The lifetime of this context info is the time it takes to check
  315. * access control for all parts of this entry (filter, entry, attributes).
  316. * So, if for an example an entry changes and a given anom profile entry
  317. * no longer applies, we will not notice until the next round of access
  318. * control checking on the entry--this is acceptable.
  319. *
  320. * The gain on doing this factoring in the following type of search
  321. * was approx 6%:
  322. * anon bind, 20 threads, exact match, ~20 attributes returned,
  323. * (searchrate & DirectoryMark).
  324. *
  325. */
  326. void
  327. aclanom_get_suffix_info(Slapi_Entry *e,
  328. struct acl_pblock *aclpb ) {
  329. int i;
  330. char *ndn = NULL;
  331. Slapi_DN *e_sdn;
  332. const char *aci_ndn;
  333. struct scoped_entry_anominfo *s_e_anominfo =
  334. &aclpb->aclpb_scoped_entry_anominfo;
  335. ANOM_LOCK_READ ();
  336. s_e_anominfo->anom_e_nummatched=0;
  337. ndn = slapi_entry_get_ndn ( e ) ;
  338. e_sdn= slapi_entry_get_sdn ( e ) ;
  339. for (i=acl_anom_profile->anom_numacls-1; i >= 0; i-- ) {
  340. aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target);
  341. if (!slapi_sdn_issuffix(e_sdn,acl_anom_profile->anom_targetinfo[i].anom_target)
  342. || (!slapi_is_rootdse(ndn) && slapi_is_rootdse(aci_ndn)))
  343. continue;
  344. if ( acl_anom_profile->anom_targetinfo[i].anom_filter ) {
  345. if ( slapi_vattr_filter_test( aclpb->aclpb_pblock, e,
  346. acl_anom_profile->anom_targetinfo[i].anom_filter,
  347. 0 /*don't do acess chk*/) != 0)
  348. continue;
  349. }
  350. s_e_anominfo->anom_e_targetInfo[s_e_anominfo->anom_e_nummatched]=i;
  351. s_e_anominfo->anom_e_nummatched++;
  352. }
  353. ANOM_UNLOCK_READ ();
  354. }
  355. /*
  356. * aclanom_match_profile
  357. * Look at the anonymous profile and see if we can use it or not.
  358. *
  359. *
  360. * Inputs:
  361. * Slapi_Pblock - The Pblock
  362. * Slapi_Entry *e - The entry for which we are asking permission.
  363. * char *attr - Attribute name
  364. * int access - access type
  365. *
  366. * Return:
  367. * LDAP_SUCCESS ( 0 ) - acess is allowed.
  368. * LDAP_INSUFFICIENT_ACCESS (50 ) - access denied.
  369. * -1 - didn't match the targets
  370. *
  371. * Assumptions:
  372. * The caller of this module has to make sure that the client is
  373. * an anonymous client.
  374. */
  375. int
  376. aclanom_match_profile (Slapi_PBlock *pb, struct acl_pblock *aclpb, Slapi_Entry *e,
  377. char *attr, int access )
  378. {
  379. struct anom_profile *a_profile;
  380. int result, i, k;
  381. char **destArray;
  382. int tmatched = 0;
  383. char ebuf[ BUFSIZ ];
  384. int loglevel;
  385. struct scoped_entry_anominfo *s_e_anominfo =
  386. &aclpb->aclpb_scoped_entry_anominfo;
  387. loglevel = slapi_is_loglevel_set(SLAPI_LOG_ACL) ? SLAPI_LOG_ACL : SLAPI_LOG_ACLSUMMARY;
  388. /* WE are only interested for READ/SEARCH */
  389. if ( !(access & ( SLAPI_ACL_SEARCH | SLAPI_ACL_READ)) )
  390. return -1;
  391. /* If we are here means, the client is doing a anonymous read/search */
  392. if ((a_profile = acl_anom_profile) == NULL ) {
  393. return -1;
  394. }
  395. ANOM_LOCK_READ ();
  396. /* Check the signature first */
  397. if ( a_profile->anom_signature != acl_get_aclsignature () ) {
  398. /* Need to regenrate the signature.
  399. * Need a WRITE lock to generate the anom profile -
  400. * which is obtained in acl__gen_anom_user_profile (). Since
  401. * I don't have upgrade lock -- I have to do this way.
  402. */
  403. ANOM_UNLOCK_READ ();
  404. aclanom_gen_anomProfile (DO_TAKE_ACLCACHE_READLOCK);
  405. aclanom_get_suffix_info(e, aclpb );
  406. ANOM_LOCK_READ ();
  407. }
  408. /* doing this early saves use a malloc/free/normalize cost */
  409. if ( !a_profile->anom_numacls ) {
  410. ANOM_UNLOCK_READ ();
  411. return -1;
  412. }
  413. result = LDAP_INSUFFICIENT_ACCESS;
  414. for ( k=0; k<s_e_anominfo->anom_e_nummatched; k++ ) {
  415. short matched = 0;
  416. short j = 0;
  417. i = s_e_anominfo->anom_e_targetInfo[k];
  418. /* Check for right */
  419. if ( !(a_profile->anom_targetinfo[i].anom_access & access) )
  420. continue;
  421. /*
  422. * XXX rbyrne Don't really understand the role of this
  423. * but not causing any obvious bugs...get back to it.
  424. */
  425. tmatched++;
  426. if ( attr == NULL ) {
  427. result = LDAP_SUCCESS;
  428. break;
  429. }
  430. destArray = a_profile->anom_targetinfo[i].anom_targetAttrs;
  431. while ( destArray[j] ) {
  432. if ( strcasecmp ( destArray[j], "*") == 0 ||
  433. slapi_attr_type_cmp ( attr, destArray[j], 1 ) == 0 ) {
  434. matched = 1;
  435. break;
  436. }
  437. j++;
  438. }
  439. if ( a_profile->anom_targetinfo[i].anom_type & ACI_TARGET_ATTR_NOT )
  440. result = matched ? LDAP_INSUFFICIENT_ACCESS : LDAP_SUCCESS;
  441. else
  442. result = matched ? LDAP_SUCCESS : LDAP_INSUFFICIENT_ACCESS;
  443. if ( result == LDAP_SUCCESS )
  444. break;
  445. } /* for */
  446. if ( slapi_is_loglevel_set(loglevel) ) {
  447. char *ndn = NULL;
  448. Slapi_Operation *op = NULL;
  449. ndn = slapi_entry_get_ndn ( e ) ;
  450. slapi_pblock_get(pb, SLAPI_OPERATION, &op);
  451. if ( result == LDAP_SUCCESS) {
  452. const char *aci_ndn;
  453. aci_ndn = slapi_sdn_get_ndn (acl_anom_profile->anom_targetinfo[i].anom_target);
  454. slapi_log_error(loglevel, plugin_name,
  455. "conn=%d op=%d: Allow access on entry(%s).attr(%s) to anonymous: acidn=\"%s\"\n",
  456. op->o_connid, op->o_opid,
  457. escape_string_with_punctuation(ndn, ebuf),
  458. attr ? attr:"NULL",
  459. escape_string_with_punctuation(aci_ndn, ebuf));
  460. } else {
  461. slapi_log_error(loglevel, plugin_name,
  462. "conn=%d op=%d: Deny access on entry(%s).attr(%s) to anonymous\n",
  463. op->o_connid, op->o_opid,
  464. escape_string_with_punctuation(ndn, ebuf), attr ? attr:"NULL" );
  465. }
  466. }
  467. ANOM_UNLOCK_READ ();
  468. if ( tmatched == 0)
  469. return -1;
  470. else
  471. return result;
  472. }
  473. int
  474. aclanom_is_client_anonymous ( Slapi_PBlock *pb )
  475. {
  476. char *clientDn;
  477. slapi_pblock_get ( pb, SLAPI_REQUESTOR_DN, &clientDn );
  478. if (acl_anom_profile->anom_numacls &&
  479. acl_anom_profile->anom_signature &&
  480. (( NULL == clientDn) || (clientDn && *clientDn == '\0')) )
  481. return 1;
  482. return 0;
  483. }