aclgroup.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright 2001 Sun Microsystems, Inc.
  3. * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
  4. * All rights reserved.
  5. * END COPYRIGHT BLOCK **/
  6. #include "acl.h"
  7. /***************************************************************************
  8. *
  9. * This module deals with the global user group cache.
  10. *
  11. * A LRU queue mechanism is used to maintain the groups the user currently in.
  12. * At this moment the QUEUE is invalidated if there is a group change. A better
  13. * way would have been to invalidate only the one which are effected.
  14. * However to accomplish that will require quite a bit of work which may not be
  15. * cost-efftive.
  16. **************************************************************************/
  17. static aclGroupCache *aclUserGroups;
  18. #define ACL_MAXCACHE_USERGROUPS 200
  19. #define ACLG_LOCK_GROUPCACHE_READ() PR_RWLock_Rlock ( aclUserGroups->aclg_rwlock )
  20. #define ACLG_LOCK_GROUPCACHE_WRITE() PR_RWLock_Wlock ( aclUserGroups->aclg_rwlock )
  21. #define ACLG_ULOCK_GROUPCACHE_WRITE() PR_RWLock_Unlock ( aclUserGroups->aclg_rwlock )
  22. #define ACLG_ULOCK_GROUPCACHE_READ() PR_RWLock_Unlock ( aclUserGroups->aclg_rwlock )
  23. static void __aclg__delete_userGroup ( aclUserGroup *u_group );
  24. int
  25. aclgroup_init ()
  26. {
  27. aclUserGroups = ( aclGroupCache * ) slapi_ch_calloc (1, sizeof ( aclGroupCache ) );
  28. if ( NULL == (aclUserGroups->aclg_rwlock = PR_NewRWLock( PR_RWLOCK_RANK_NONE,"Group LOCK"))) {
  29. slapi_log_error(SLAPI_LOG_FATAL, plugin_name, "Unable to allocate RWLOCK for group cache\n");
  30. return 1;
  31. }
  32. return 0;
  33. }
  34. /*
  35. * aclg_init_userGroup
  36. *
  37. * Go thru the Global Group CACHE and see if we have group information for
  38. * the user. The user's group cache is invalidated when a group is modified
  39. * (in which case ALL usergroups are invalidated) or when the user's entry
  40. * is modified in which case just his is invalidated.
  41. *
  42. * We need to scan the whole cache looking for a valid entry that matches
  43. * this user. If we find invalid entries along the way.
  44. *
  45. * If we don't have anything it's fine. we will allocate a space when we
  46. * need it i.e during the group evaluation.
  47. *
  48. * Inputs:
  49. * struct acl_pblock - ACL private block
  50. * char *dn - the client's dn
  51. * int got_lock - 1: already obtained WRITE Lock
  52. * - 0: Nope; get one
  53. * Returns:
  54. * None.
  55. */
  56. void
  57. aclg_init_userGroup ( struct acl_pblock *aclpb, const char *n_dn , int got_lock )
  58. {
  59. aclUserGroup *u_group = NULL;
  60. aclUserGroup *next_ugroup = NULL;
  61. aclUserGroup *p_group, *n_group;
  62. int found = 0;
  63. /* Check for Anonymous user */
  64. if ( n_dn && *n_dn == '\0') return;
  65. if ( !got_lock ) ACLG_LOCK_GROUPCACHE_WRITE ();
  66. u_group = aclUserGroups->aclg_first;
  67. aclpb->aclpb_groupinfo = NULL;
  68. while ( u_group != NULL ) {
  69. next_ugroup = u_group->aclug_next;
  70. if ( aclUserGroups->aclg_signature != u_group->aclug_signature) {
  71. /*
  72. * This means that this usergroup is no longer valid and
  73. * this operation so delete this one if no one is using it.
  74. */
  75. if ( !u_group->aclug_refcnt ) {
  76. slapi_log_error( SLAPI_LOG_ACL, plugin_name,
  77. "In traversal group deallocation\n", 0,0,0 );
  78. __aclg__delete_userGroup (u_group);
  79. }
  80. } else {
  81. /*
  82. * Here, u_group is valid--if it matches then take it.
  83. */
  84. if ( slapi_utf8casecmp((ACLUCHP)u_group->aclug_ndn,
  85. (ACLUCHP)n_dn ) == 0 ) {
  86. u_group->aclug_refcnt++;
  87. aclpb->aclpb_groupinfo = u_group;
  88. found = 1;
  89. break;
  90. }
  91. }
  92. u_group = next_ugroup;
  93. }
  94. /* Move the new one to the top of the queue */
  95. if ( found ) {
  96. p_group = u_group->aclug_prev;
  97. n_group = u_group->aclug_next;
  98. if ( p_group ) {
  99. aclUserGroup *t_group = NULL;
  100. p_group->aclug_next = n_group;
  101. if ( n_group ) n_group->aclug_prev = p_group;
  102. t_group = aclUserGroups->aclg_first;
  103. if ( t_group ) t_group->aclug_prev = u_group;
  104. u_group->aclug_prev = NULL;
  105. u_group->aclug_next = t_group;
  106. aclUserGroups->aclg_first = u_group;
  107. if ( u_group == aclUserGroups->aclg_last )
  108. aclUserGroups->aclg_last = p_group;
  109. }
  110. slapi_log_error(SLAPI_LOG_ACL, plugin_name, "acl_init_userGroup: found in cache for dn:%s\n", n_dn,0,0);
  111. }
  112. if (!got_lock ) ACLG_ULOCK_GROUPCACHE_WRITE ();
  113. }
  114. /*
  115. *
  116. * aclg_reset_userGroup
  117. * Reset the reference count to the user's group.
  118. *
  119. * Inputs:
  120. * struct acl_pblock -- The acl private block.
  121. * Returns:
  122. * None.
  123. *
  124. * Note: A WRITE Lock on the GroupCache is obtained during the change:
  125. */
  126. void
  127. aclg_reset_userGroup ( struct acl_pblock *aclpb )
  128. {
  129. aclUserGroup *u_group;
  130. ACLG_LOCK_GROUPCACHE_WRITE();
  131. if ( (u_group = aclpb->aclpb_groupinfo) != NULL ) {
  132. u_group->aclug_refcnt--;
  133. /* If I am the last one but I was using an invalid group cache
  134. ** in the meantime, it is time now to get rid of it so that we will
  135. ** not have duplicate cache.
  136. */
  137. if ( !u_group->aclug_refcnt &&
  138. ( aclUserGroups->aclg_signature != u_group->aclug_signature )) {
  139. __aclg__delete_userGroup ( u_group );
  140. }
  141. }
  142. ACLG_ULOCK_GROUPCACHE_WRITE();
  143. aclpb->aclpb_groupinfo = NULL;
  144. }
  145. /*
  146. * Find a user group in the global cache, returning a pointer to it,
  147. * ensuring that the refcnt has been bumped to stop
  148. * another thread freeing it underneath us.
  149. */
  150. aclUserGroup*
  151. aclg_find_userGroup(char *n_dn)
  152. {
  153. aclUserGroup *u_group = NULL;
  154. int i;
  155. /* Check for Anonymous user */
  156. if ( n_dn && *n_dn == '\0') return (NULL) ;
  157. ACLG_LOCK_GROUPCACHE_READ ();
  158. u_group = aclUserGroups->aclg_first;
  159. for ( i=0; i < aclUserGroups->aclg_num_userGroups; i++ ) {
  160. if ( aclUserGroups->aclg_signature == u_group->aclug_signature &&
  161. slapi_utf8casecmp((ACLUCHP)u_group->aclug_ndn,
  162. (ACLUCHP)n_dn ) == 0 ) {
  163. aclg_reader_incr_ugroup_refcnt(u_group);
  164. break;
  165. }
  166. u_group = u_group->aclug_next;
  167. }
  168. ACLG_ULOCK_GROUPCACHE_READ ();
  169. return(u_group);
  170. }
  171. /*
  172. * Mark a usergroup for removal from the usergroup cache.
  173. * It will be removed by the first operation traversing the cache
  174. * that finds it.
  175. */
  176. void
  177. aclg_markUgroupForRemoval ( aclUserGroup* u_group) {
  178. ACLG_LOCK_GROUPCACHE_WRITE ();
  179. aclg_regen_ugroup_signature(u_group);
  180. u_group->aclug_refcnt--;
  181. ACLG_ULOCK_GROUPCACHE_WRITE ();
  182. }
  183. /*
  184. *
  185. * aclg_get_usersGroup
  186. *
  187. * If we already have a the group info then we are done. If we
  188. * don't, then allocate a new one and attach it.
  189. *
  190. * Inputs:
  191. * struct acl_pblock -- The acl private block.
  192. * char *n_dn - normalized client's DN
  193. *
  194. * Returns:
  195. * aclUserGroup - The Group info block.
  196. *
  197. */
  198. aclUserGroup *
  199. aclg_get_usersGroup ( struct acl_pblock *aclpb , char *n_dn)
  200. {
  201. aclUserGroup *u_group, *f_group;
  202. if ( aclpb && aclpb->aclpb_groupinfo )
  203. return aclpb->aclpb_groupinfo;
  204. ACLG_LOCK_GROUPCACHE_WRITE();
  205. /* try it one more time. We might have one in the meantime */
  206. aclg_init_userGroup (aclpb, n_dn , 1 /* got the lock */);
  207. if ( aclpb && aclpb->aclpb_groupinfo ) {
  208. ACLG_ULOCK_GROUPCACHE_WRITE();
  209. return aclpb->aclpb_groupinfo;
  210. }
  211. /*
  212. * It is possible at this point that we already have a group cache for the user
  213. * but is is invalid. We can't use it anayway. So, we march along and allocate a new one.
  214. * That's fine as the invalid one will be deallocated when done.
  215. */
  216. slapi_log_error( SLAPI_LOG_ACL, plugin_name, "ALLOCATING GROUP FOR:%s\n", n_dn,0,0 );
  217. u_group = ( aclUserGroup * ) slapi_ch_calloc ( 1, sizeof ( aclUserGroup ) );
  218. u_group->aclug_refcnt = 1;
  219. if ( (u_group->aclug_refcnt_mutex = PR_NewLock()) == NULL ) {
  220. slapi_ch_free((void **)&u_group);
  221. ACLG_ULOCK_GROUPCACHE_WRITE();
  222. return(NULL);
  223. }
  224. u_group->aclug_member_groups = (char **)
  225. slapi_ch_calloc ( 1,
  226. (ACLUG_INCR_GROUPS_LIST * sizeof (char *)));
  227. u_group->aclug_member_group_size = ACLUG_INCR_GROUPS_LIST;
  228. u_group->aclug_numof_member_group = 0;
  229. u_group->aclug_notmember_groups = (char **)
  230. slapi_ch_calloc ( 1,
  231. (ACLUG_INCR_GROUPS_LIST * sizeof (char *)));
  232. u_group->aclug_notmember_group_size = ACLUG_INCR_GROUPS_LIST;
  233. u_group->aclug_numof_notmember_group = 0;
  234. u_group->aclug_ndn = slapi_ch_strdup ( n_dn ) ;
  235. u_group->aclug_signature = aclUserGroups->aclg_signature;
  236. /* Do we have alreday the max number. If we have then delete the last one */
  237. if ( aclUserGroups->aclg_num_userGroups >= ACL_MAXCACHE_USERGROUPS - 5 ) {
  238. aclUserGroup *d_group;
  239. /* We need to traverse thru backwards and delete the one with a refcnt = 0 */
  240. d_group = aclUserGroups->aclg_last;
  241. while ( d_group ) {
  242. if ( !d_group->aclug_refcnt ) {
  243. __aclg__delete_userGroup ( d_group );
  244. break;
  245. } else {
  246. d_group = d_group->aclug_prev;
  247. }
  248. }
  249. /* If we didn't find any, which should be never,
  250. ** we have 5 more tries to do it.
  251. */
  252. }
  253. f_group = aclUserGroups->aclg_first;
  254. u_group->aclug_next = f_group;
  255. if ( f_group ) f_group->aclug_prev = u_group;
  256. aclUserGroups->aclg_first = u_group;
  257. if ( aclUserGroups->aclg_last == NULL )
  258. aclUserGroups->aclg_last = u_group;
  259. aclUserGroups->aclg_num_userGroups++;
  260. /* Put it in the queue */
  261. ACLG_ULOCK_GROUPCACHE_WRITE();
  262. /* Now hang on to it */
  263. aclpb->aclpb_groupinfo = u_group;
  264. return u_group;
  265. }
  266. /*
  267. *
  268. * __aclg__delete_userGroup
  269. *
  270. * Delete the User's Group cache.
  271. *
  272. * Inputs:
  273. * aclUserGroup - remove this one
  274. * Returns:
  275. * None.
  276. *
  277. * Note: A WRITE Lock on the GroupCache is obtained by the caller
  278. */
  279. static void
  280. __aclg__delete_userGroup ( aclUserGroup *u_group )
  281. {
  282. aclUserGroup *next_group, *prev_group;
  283. int i;
  284. if ( !u_group ) return;
  285. prev_group = u_group->aclug_prev;
  286. next_group = u_group->aclug_next;
  287. /*
  288. * At this point we must have a 0 refcnt or else we are in a bad shape.
  289. * If we don't have one then at least remove the user's dn so that it will
  290. * be in a condemned state and later deleted.
  291. */
  292. slapi_log_error( SLAPI_LOG_ACL, plugin_name, "DEALLOCATING GROUP FOR:%s\n", u_group->aclug_ndn,0,0 );
  293. slapi_ch_free ( (void **) &u_group->aclug_ndn );
  294. PR_DestroyLock(u_group->aclug_refcnt_mutex);
  295. /* Remove the member GROUPS */
  296. for (i=0; i < u_group->aclug_numof_member_group; i++ )
  297. slapi_ch_free ( (void **) &u_group->aclug_member_groups[i] );
  298. slapi_ch_free ( (void **) &u_group->aclug_member_groups );
  299. /* Remove the NOT member GROUPS */
  300. for (i=0; i < u_group->aclug_numof_notmember_group; i++ )
  301. slapi_ch_free ( (void **) &u_group->aclug_notmember_groups[i] );
  302. slapi_ch_free ( (void **) &u_group->aclug_notmember_groups );
  303. slapi_ch_free ( (void **) &u_group );
  304. if ( prev_group == NULL && next_group == NULL ) {
  305. aclUserGroups->aclg_first = NULL;
  306. aclUserGroups->aclg_last = NULL;
  307. } else if ( prev_group == NULL ) {
  308. next_group->aclug_prev = NULL;
  309. aclUserGroups->aclg_first = next_group;
  310. } else {
  311. prev_group->aclug_next = next_group;
  312. if ( next_group )
  313. next_group->aclug_prev = prev_group;
  314. else
  315. aclUserGroups->aclg_last = prev_group;
  316. }
  317. aclUserGroups->aclg_num_userGroups--;
  318. }
  319. void
  320. aclg_regen_group_signature( )
  321. {
  322. aclUserGroups->aclg_signature = aclutil_gen_signature ( aclUserGroups->aclg_signature );
  323. }
  324. void
  325. aclg_regen_ugroup_signature( aclUserGroup *ugroup)
  326. {
  327. ugroup->aclug_signature =
  328. aclutil_gen_signature ( ugroup->aclug_signature );
  329. }
  330. void
  331. aclg_lock_groupCache ( int type /* 1 for reader and 2 for writer */)
  332. {
  333. if (type == 1 )
  334. ACLG_LOCK_GROUPCACHE_READ();
  335. else
  336. ACLG_LOCK_GROUPCACHE_WRITE();
  337. }
  338. void
  339. aclg_unlock_groupCache ( int type /* 1 for reader and 2 for writer */)
  340. {
  341. if (type == 1 )
  342. ACLG_ULOCK_GROUPCACHE_READ();
  343. else
  344. ACLG_ULOCK_GROUPCACHE_WRITE();
  345. }
  346. /*
  347. * If you have the write lock on the group cache, you can
  348. * increment the refcnt without taking the mutex.
  349. * If you just have the reader lock on the refcnt then you need to
  350. * take the mutex on the refcnt to increment it--which is what this routine is
  351. * for.
  352. *
  353. */
  354. void
  355. aclg_reader_incr_ugroup_refcnt(aclUserGroup* u_group) {
  356. PR_Lock(u_group->aclug_refcnt_mutex);
  357. u_group->aclug_refcnt++;
  358. PR_Unlock(u_group->aclug_refcnt_mutex);
  359. }
  360. /* You need the usergroups read lock to call this routine*/
  361. int
  362. aclg_numof_usergroups(void) {
  363. return(aclUserGroups->aclg_num_userGroups);
  364. }