aclcache.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609
  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 <prlog.h>
  39. #include <base/crit.h>
  40. #include <base/ereport.h>
  41. #include <plhash.h>
  42. #include <libaccess/acl.h>
  43. #include "aclpriv.h"
  44. #include <libaccess/aclproto.h>
  45. #include <libaccess/aclglobal.h>
  46. #include <libaccess/usrcache.h>
  47. #include <libaccess/las.h>
  48. #include <libaccess/ldapacl.h>
  49. #include "aclutil.h"
  50. #include "permhash.h"
  51. #include "aclcache.h"
  52. static CRITICAL acl_hash_crit = NULL; /* Controls Global Hash */
  53. enum {
  54. ACL_URI_HASH,
  55. ACL_URI_GET_HASH
  56. };
  57. /* ACL_ListHashKeyHash
  58. * Given an ACL List address, computes a randomized hash value of the
  59. * ACL structure pointer addresses by simply adding them up. Returns
  60. * the resultant hash value.
  61. */
  62. static PLHashNumber
  63. ACL_ListHashKeyHash(const void *Iacllist)
  64. {
  65. PLHashNumber hash=0;
  66. ACLWrapper_t *wrap;
  67. ACLListHandle_t *acllist=(ACLListHandle_t *)Iacllist;
  68. for (wrap=acllist->acl_list_head; wrap; wrap=wrap->wrap_next) {
  69. hash += (PLHashNumber)(PRSize)wrap->acl;
  70. }
  71. return (hash);
  72. }
  73. /* ACL_ListHashKeyCompare
  74. * Given two acl lists, compares the addresses of the acl pointers within
  75. * them to see if theyre identical. Returns 1 if equal, 0 otherwise.
  76. */
  77. static int
  78. ACL_ListHashKeyCompare(const void *Iacllist1, const void *Iacllist2)
  79. {
  80. ACLWrapper_t *wrap1, *wrap2;
  81. ACLListHandle_t *acllist1=(ACLListHandle_t *)Iacllist1;
  82. ACLListHandle_t *acllist2=(ACLListHandle_t *)Iacllist2;
  83. if (acllist1->acl_count != acllist2->acl_count)
  84. return 0;
  85. wrap1 = acllist1->acl_list_head;
  86. wrap2 = acllist2->acl_list_head;
  87. while ((wrap1 != NULL) && (wrap2 != NULL)) {
  88. if (wrap1->acl != wrap2->acl)
  89. return 0;
  90. wrap1 = wrap1->wrap_next;
  91. wrap2 = wrap2->wrap_next;
  92. }
  93. if ((wrap1 != NULL) || (wrap2 != NULL))
  94. return 0;
  95. else
  96. return 1;
  97. }
  98. /* ACL_ListHashValueCompare
  99. * Returns 1 if equal, 0 otherwise
  100. */
  101. static int
  102. ACL_ListHashValueCompare(const void *acllist1, const void *acllist2)
  103. {
  104. return (acllist1 == acllist2);
  105. }
  106. void
  107. ACL_ListHashInit()
  108. {
  109. ACLListHash = PR_NewHashTable(200,
  110. ACL_ListHashKeyHash,
  111. ACL_ListHashKeyCompare,
  112. ACL_ListHashValueCompare,
  113. &ACLPermAllocOps,
  114. NULL);
  115. if (ACLListHash == NULL) {
  116. ereport(LOG_SECURITY, "Unable to allocate ACL List Hash\n");
  117. return;
  118. }
  119. return;
  120. }
  121. static void
  122. ACL_ListHashDestroy()
  123. {
  124. if (ACLListHash) {
  125. PR_HashTableDestroy(ACLListHash);
  126. ACLListHash = NULL;
  127. }
  128. return;
  129. }
  130. /* ACL_ListHashUpdate
  131. * Typically called with the &rq->acllist. Checks if the newly generated
  132. * acllist matches one that's already been created. If so, toss the new
  133. * list and set the pointer to the old list in its place.
  134. */
  135. void
  136. ACL_ListHashUpdate(ACLListHandle_t **acllistp)
  137. {
  138. NSErr_t *errp = 0;
  139. ACLListHandle_t *tmp_acllist;
  140. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  141. tmp_acllist = (ACLListHandle_t *)PR_HashTableLookup(ACLListHash, *acllistp);
  142. if (tmp_acllist && tmp_acllist != *acllistp) {
  143. PR_ASSERT(*acllistp && *acllistp != ACL_LIST_NO_ACLS);
  144. ACL_ListDestroy(errp, *acllistp);
  145. *acllistp = tmp_acllist;
  146. PR_ASSERT(ACL_CritHeld());
  147. tmp_acllist->ref_count++; /* we're gonna use it */
  148. } else { /* Wasn't in the list */
  149. PR_HashTableAdd(ACLListHash, *acllistp, *acllistp);
  150. }
  151. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  152. return;
  153. }
  154. /* ACL_ListCacheEnter
  155. * In some cases, the URI cache is useless. E.g. when virtual servers are used.
  156. * When that happens, the List Cache is still useful, because the cached ACL
  157. * List has the Eval cache in it, plus any LAS caches.
  158. */
  159. NSAPI_PUBLIC void
  160. ACL_ListHashEnter(ACLListHandle_t **acllistp)
  161. {
  162. ACL_CritEnter();
  163. /* Look for a matching ACL List and use it if we find one. */
  164. if (*acllistp) {
  165. PR_ASSERT(*acllistp != ACL_LIST_NO_ACLS);
  166. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  167. ACL_ListHashUpdate(acllistp);
  168. } else {
  169. *acllistp = ACL_LIST_NO_ACLS;
  170. }
  171. ACL_CritExit();
  172. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  173. return;
  174. }
  175. /* ACL_ListHashCheck
  176. * When Virtual Servers are active, and the ACL URI cache is inactive, someone
  177. * with an old ACL List pointer can check to see if it's still valid. This will
  178. * also increment the reference count on it.
  179. */
  180. NSAPI_PUBLIC int
  181. ACL_ListHashCheck(ACLListHandle_t **acllistp)
  182. {
  183. ACLListHandle_t *tmp_acllist;
  184. if (*acllistp == ACL_LIST_NO_ACLS) return 1;
  185. ACL_CritEnter();
  186. tmp_acllist = (ACLListHandle_t *)PR_HashTableLookup(ACLListHash, *acllistp);
  187. if (tmp_acllist) {
  188. PR_ASSERT(*acllistp && *acllistp != ACL_LIST_NO_ACLS);
  189. *acllistp = tmp_acllist;
  190. PR_ASSERT(ACL_CritHeld());
  191. tmp_acllist->ref_count++; /* we're gonna use it */
  192. ACL_CritExit();
  193. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  194. return 1; /* Normal path */
  195. } else { /* Wasn't in the list */
  196. ACL_CritExit();
  197. return 0;
  198. }
  199. }
  200. void
  201. ACL_UriHashDestroy(void)
  202. {
  203. if (acl_uri_hash) {
  204. PR_HashTableDestroy(acl_uri_hash);
  205. acl_uri_hash = NULL;
  206. }
  207. if (acl_uri_get_hash) {
  208. PR_HashTableDestroy(acl_uri_get_hash);
  209. acl_uri_get_hash = NULL;
  210. }
  211. pool_destroy((void **)acl_uri_hash_pool);
  212. acl_uri_hash_pool = NULL;
  213. }
  214. void
  215. ACL_Destroy(void)
  216. {
  217. ACL_ListHashDestroy();
  218. ACL_UriHashDestroy();
  219. ACL_LasHashDestroy();
  220. }
  221. /* Only used in ASSERT statements to verify that we have the lock */
  222. int
  223. ACL_CritHeld(void)
  224. {
  225. return (crit_owner_is_me(acl_hash_crit));
  226. }
  227. NSAPI_PUBLIC void
  228. ACL_CritEnter(void)
  229. {
  230. crit_enter(acl_hash_crit);
  231. }
  232. NSAPI_PUBLIC void
  233. ACL_CritExit(void)
  234. {
  235. crit_exit(acl_hash_crit);
  236. }
  237. void
  238. ACL_CritInit(void)
  239. {
  240. acl_hash_crit = crit_init();
  241. }
  242. void
  243. ACL_UriHashInit(void)
  244. {
  245. acl_uri_hash = PR_NewHashTable(200,
  246. PR_HashString,
  247. PR_CompareStrings,
  248. PR_CompareValues,
  249. &ACLPermAllocOps,
  250. NULL);
  251. acl_uri_get_hash = PR_NewHashTable(200,
  252. PR_HashString,
  253. PR_CompareStrings,
  254. PR_CompareValues,
  255. &ACLPermAllocOps,
  256. NULL);
  257. acl_uri_hash_pool = pool_create();
  258. }
  259. /* ACL_CacheCheck
  260. * INPUT
  261. * uri A URI string pointer
  262. * acllistp A pointer to an acllist placeholder. E.g. &rq->acllist
  263. * OUTPUT
  264. * return 1 if cached. 0 if not. The reference count on the ACL List
  265. * is INCREMENTED, and will be decremented when ACL_EvalDestroy or
  266. * ACL_ListDecrement is
  267. * called.
  268. */
  269. int
  270. ACL_INTCacheCheck(int which, char *uri, ACLListHandle_t **acllistp)
  271. {
  272. PLHashTable *hash;
  273. PR_ASSERT(uri && acl_uri_hash && acl_uri_get_hash);
  274. /* ACL cache: If the ACL List is already in the cache, there's no need
  275. * to go through the pathcheck directives.
  276. * NULL means that the URI hasn't been accessed before.
  277. * ACL_LIST_NO_ACLS
  278. * means that the URI has no ACLs.
  279. * Anything else is a pointer to the acllist.
  280. */
  281. ACL_CritEnter();
  282. /* Get the pointer to the hash table after acquiring the lock */
  283. if (which == ACL_URI_HASH)
  284. hash = acl_uri_hash;
  285. else
  286. hash = acl_uri_get_hash;
  287. *acllistp = (ACLListHandle_t *)PR_HashTableLookup(hash, uri);
  288. if (*acllistp != NULL) {
  289. if (*acllistp != ACL_LIST_NO_ACLS) {
  290. PR_ASSERT((*acllistp)->ref_count >= 0);
  291. PR_ASSERT(ACL_CritHeld());
  292. (*acllistp)->ref_count++;
  293. }
  294. ACL_CritExit();
  295. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  296. return 1; /* Normal path */
  297. }
  298. ACL_CritExit();
  299. return 0;
  300. }
  301. int
  302. ACL_CacheCheckGet(char *uri, ACLListHandle_t **acllistp)
  303. {
  304. return (ACL_INTCacheCheck(ACL_URI_GET_HASH, uri, acllistp));
  305. }
  306. int
  307. ACL_CacheCheck(char *uri, ACLListHandle_t **acllistp)
  308. {
  309. return (ACL_INTCacheCheck(ACL_URI_HASH, uri, acllistp));
  310. }
  311. /* ACL_CacheEnter
  312. * INPUT
  313. * acllist or 0 if there were no ACLs that applied.
  314. * OUTPUT
  315. * The acllist address may be changed if it matches an existing one.
  316. */
  317. static void
  318. ACL_INTCacheEnter(int which, char *uri, ACLListHandle_t **acllistp)
  319. {
  320. ACLListHandle_t *tmpacllist;
  321. NSErr_t *errp = 0;
  322. PLHashTable *hash;
  323. PR_ASSERT(uri);
  324. ACL_CritEnter();
  325. /* Get the pointer to the hash table after acquiring the lock */
  326. if (which == ACL_URI_HASH)
  327. hash = acl_uri_hash;
  328. else
  329. hash = acl_uri_get_hash;
  330. /* Check again (now that we're in the critical section) to see if
  331. * someone else created an ACL List for this URI. If so, discard the
  332. * list that we made and replace it with the one just found.
  333. */
  334. tmpacllist = (ACLListHandle_t *)PR_HashTableLookup(hash, uri);
  335. if (tmpacllist != NULL) {
  336. if (tmpacllist != ACL_LIST_NO_ACLS) {
  337. PR_ASSERT(ACL_CritHeld());
  338. tmpacllist->ref_count++; /* we're going to use it */
  339. }
  340. ACL_CritExit();
  341. if (*acllistp && *acllistp != ACL_LIST_NO_ACLS) {
  342. ACL_ListDestroy(errp, *acllistp);
  343. }
  344. *acllistp = tmpacllist;
  345. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  346. return;
  347. }
  348. /* Didn't find another list, so put ours in. */
  349. /* Look for a matching ACL List and use it if we find one. */
  350. if (*acllistp) {
  351. PR_ASSERT(*acllistp != ACL_LIST_NO_ACLS);
  352. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  353. ACL_ListHashUpdate(acllistp);
  354. } else {
  355. *acllistp = ACL_LIST_NO_ACLS;
  356. }
  357. PR_HashTableAdd(hash, pool_strdup((void **)acl_uri_hash_pool, uri), (void *)*acllistp);
  358. ACL_CritExit();
  359. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  360. return;
  361. }
  362. void
  363. ACL_CacheEnter(char *uri, ACLListHandle_t **acllistp)
  364. {
  365. ACL_INTCacheEnter(ACL_URI_HASH, uri, acllistp);
  366. return;
  367. }
  368. void
  369. ACL_CacheEnterGet(char *uri, ACLListHandle_t **acllistp)
  370. {
  371. ACL_INTCacheEnter(ACL_URI_GET_HASH, uri, acllistp);
  372. return;
  373. }
  374. /* ACL_AddAclName
  375. * Adds the ACLs for just the terminal object specified in a pathname.
  376. * INPUT
  377. * path The filesystem pathname of the terminal object.
  378. * acllistp The address of the list of ACLs found thus far.
  379. * Could be NULL. If so, a new acllist will be allocated (if any
  380. * acls are found). Otherwise the existing list will be added to.
  381. * masterlist Usually acl_root_30.
  382. */
  383. void
  384. ACL_AddAclName(char *path, ACLListHandle_t **acllistp, ACLListHandle_t
  385. *masterlist)
  386. {
  387. ACLHandle_t *acl;
  388. NSErr_t *errp = 0;
  389. #ifdef XP_WIN32
  390. acl = ACL_ListFind(errp, masterlist, path, ACL_CASE_INSENSITIVE);
  391. #else
  392. acl = ACL_ListFind(errp, masterlist, path, ACL_CASE_SENSITIVE);
  393. #endif
  394. if (!acl)
  395. return;
  396. PR_ASSERT(ACL_AssertAcl(acl));
  397. if (!*acllistp)
  398. *acllistp = ACL_ListNew(errp);
  399. ACL_ListAppend(NULL, *acllistp, acl, 0);
  400. PR_ASSERT(ACL_AssertAcllist(*acllistp));
  401. return;
  402. }
  403. /* ACL_GetPathAcls
  404. * Adds the ACLs for all directories plus the terminal object along a given
  405. * filesystem pathname. For each pathname component, look for the name, the
  406. * name + "/", and the name + "/*". The last one is because the resource
  407. * picker likes to postpend "/*" for directories.
  408. * INPUT
  409. * path The filesystem pathname of the terminal object.
  410. * acllistp The address of the list of ACLs found thus far.
  411. * Could be NULL. If so, a new acllist will be allocated (if any
  412. * acls are found). Otherwise the existing list will be added to.
  413. * prefix A string to be prepended to the path component when looking
  414. * for a matching ACL tag.
  415. */
  416. void
  417. ACL_GetPathAcls(char *path, ACLListHandle_t **acllistp, char *prefix,
  418. ACLListHandle_t *masterlist)
  419. {
  420. char *slashp=path;
  421. int slashidx;
  422. char ppath[ACL_PATH_MAX];
  423. int prefixlen;
  424. char *dst;
  425. PR_ASSERT(path);
  426. PR_ASSERT(prefix);
  427. dst = strncpy(ppath, prefix, ACL_PATH_MAX);
  428. if (dst >= (ppath+ACL_PATH_MAX-1)) {
  429. ereport(LOG_SECURITY, "Abort - the path is too long for ACL_GetPathAcls to handle\n");
  430. abort();
  431. }
  432. prefixlen = strlen(ppath);
  433. /* Handle the first "/". i.e. the root directory */
  434. if (*path == '/') {
  435. ppath[prefixlen]='/';
  436. ppath[prefixlen+1]='\0';
  437. ACL_AddAclName(ppath, acllistp, masterlist);
  438. strcat(ppath, "*");
  439. ACL_AddAclName(ppath, acllistp, masterlist);
  440. slashp = path;
  441. }
  442. do {
  443. slashp = strchr(++slashp, '/');
  444. if (slashp) {
  445. slashidx = slashp - path;
  446. strncpy(&ppath[prefixlen], path, slashidx);
  447. ppath[slashidx+prefixlen] = '\0';
  448. ACL_AddAclName(ppath, acllistp, masterlist);
  449. /* Must also handle "/a/b/" in addition to "/a/b" */
  450. strcat(ppath, "/");
  451. ACL_AddAclName(ppath, acllistp, masterlist);
  452. strcat(ppath, "*");
  453. ACL_AddAclName(ppath, acllistp, masterlist);
  454. continue;
  455. }
  456. strcpy(&ppath[prefixlen], path);
  457. ACL_AddAclName(ppath, acllistp, masterlist);
  458. strcat(ppath, "/");
  459. ACL_AddAclName(ppath, acllistp, masterlist);
  460. strcat(ppath, "*");
  461. ACL_AddAclName(ppath, acllistp, masterlist);
  462. break;
  463. } while (slashp);
  464. }
  465. static int get_is_owner_default (NSErr_t *errp, PList_t subject,
  466. PList_t resource, PList_t auth_info,
  467. PList_t global_auth, void *unused)
  468. {
  469. /* Make sure we don't generate error "all getters declined" message from
  470. * ACL_GetAttribute.
  471. */
  472. PListInitProp(subject, ACL_ATTR_IS_OWNER_INDEX, ACL_ATTR_IS_OWNER,
  473. "true", 0);
  474. return LAS_EVAL_TRUE;
  475. }
  476. NSAPI_PUBLIC int
  477. ACL_Init(void)
  478. {
  479. ACL_InitAttr2Index();
  480. ACLGlobal = (ACLGlobal_p)PERM_CALLOC(sizeof(ACLGlobal_s));
  481. oldACLGlobal = (ACLGlobal_p)PERM_CALLOC(sizeof(ACLGlobal_s));
  482. PR_ASSERT(ACLGlobal && oldACLGlobal);
  483. ACL_DATABASE_POOL = pool_create();
  484. ACL_METHOD_POOL = pool_create();
  485. ACL_CritInit();
  486. ACL_UriHashInit();
  487. ACL_ListHashInit();
  488. ACL_LasHashInit();
  489. ACL_Init2();
  490. init_ldb_rwlock();
  491. ACL_RegisterInit();
  492. return 0;
  493. }
  494. /* This one gets called at startup AND at cache flush time. */
  495. void
  496. ACL_Init2(void)
  497. {
  498. /* Register the ACL functions */
  499. ACL_LasRegister(NULL, "timeofday", LASTimeOfDayEval, LASTimeOfDayFlush);
  500. ACL_LasRegister(NULL, "dayofweek", LASDayOfWeekEval, LASDayOfWeekFlush);
  501. ACL_LasRegister(NULL, "ip", LASIpEval, LASIpFlush);
  502. ACL_LasRegister(NULL, "dns", LASDnsEval, LASDnsFlush);
  503. ACL_LasRegister(NULL, "dnsalias", LASDnsEval, LASDnsFlush);
  504. ACL_LasRegister(NULL, "group", LASGroupEval, (LASFlushFunc_t)NULL);
  505. ACL_LasRegister(NULL, "user", LASUserEval, (LASFlushFunc_t)NULL);
  506. #ifdef MCC_ADMSERV
  507. ACL_LasRegister(NULL, "program", LASProgramEval, (LASFlushFunc_t)NULL);
  508. #endif
  509. ACL_AttrGetterRegister(NULL, ACL_ATTR_USERDN,
  510. get_userdn_ldap,
  511. ACL_METHOD_ANY, ACL_DBTYPE_ANY,
  512. ACL_AT_END, NULL);
  513. return;
  514. }
  515. NSAPI_PUBLIC int
  516. ACL_InitPostMagnus(void)
  517. {
  518. int rv;
  519. rv = ACL_AttrGetterRegister(NULL, ACL_ATTR_IS_OWNER,
  520. get_is_owner_default,
  521. ACL_METHOD_ANY, ACL_DBTYPE_ANY,
  522. ACL_AT_END, NULL);
  523. return rv;
  524. }
  525. NSAPI_PUBLIC int
  526. ACL_LateInitPostMagnus(void)
  527. {
  528. return acl_usr_cache_init();
  529. }