automember.c 67 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930
  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) 2011 Red Hat, Inc.
  35. * All rights reserved.
  36. * END COPYRIGHT BLOCK **/
  37. #ifdef HAVE_CONFIG_H
  38. # include <config.h>
  39. #endif
  40. /*
  41. * Auto Membership Plug-in
  42. */
  43. #include "automember.h"
  44. /*
  45. * Plug-in globals
  46. */
  47. static PRCList *g_automember_config = NULL;
  48. static Slapi_RWLock *g_automember_config_lock;
  49. static void *_PluginID = NULL;
  50. static Slapi_DN *_PluginDN = NULL;
  51. static Slapi_DN *_ConfigAreaDN = NULL;
  52. static int g_plugin_started = 0;
  53. static Slapi_PluginDesc pdesc = { AUTOMEMBER_FEATURE_DESC,
  54. VENDOR,
  55. DS_PACKAGE_VERSION,
  56. AUTOMEMBER_PLUGIN_DESC };
  57. /*
  58. * Plug-in management functions
  59. */
  60. int automember_init(Slapi_PBlock * pb);
  61. static int automember_start(Slapi_PBlock * pb);
  62. static int automember_close(Slapi_PBlock * pb);
  63. static int automember_postop_init(Slapi_PBlock * pb);
  64. static int automember_internal_postop_init(Slapi_PBlock *pb);
  65. /*
  66. * Operation callbacks (where the real work is done)
  67. */
  68. static int automember_mod_post_op(Slapi_PBlock *pb);
  69. static int automember_add_post_op(Slapi_PBlock *pb);
  70. static int automember_del_post_op(Slapi_PBlock *pb);
  71. static int automember_modrdn_post_op(Slapi_PBlock *pb);
  72. static int automember_pre_op(Slapi_PBlock *pb, int modop);
  73. static int automember_mod_pre_op(Slapi_PBlock *pb);
  74. static int automember_add_pre_op(Slapi_PBlock *pb);
  75. /*
  76. * Config cache management functions
  77. */
  78. static int automember_load_config(Slapi_PBlock *pb);
  79. static void automember_delete_config();
  80. static int automember_parse_config_entry(Slapi_Entry * e, int apply, Slapi_PBlock *pb);
  81. static void automember_free_config_entry(struct configEntry ** entry);
  82. /*
  83. * helpers
  84. */
  85. static Slapi_DN *automember_get_sdn(Slapi_PBlock *pb);
  86. static Slapi_DN *automember_get_config_area();
  87. static void automember_set_config_area(Slapi_DN *sdn);
  88. static int automember_dn_is_config(Slapi_DN *sdn);
  89. static int automember_oktodo(Slapi_PBlock *pb);
  90. static int automember_isrepl(Slapi_PBlock *pb);
  91. static void automember_parse_regex_entry(struct configEntry *config, Slapi_Entry *e);
  92. static struct automemberRegexRule *automember_parse_regex_rule(char *rule_string);
  93. static void automember_free_regex_rule(struct automemberRegexRule *rule);
  94. static int automember_parse_grouping_attr(char *value, char **grouping_attr,
  95. char **grouping_value);
  96. static void automember_update_membership(struct configEntry *config, Slapi_Entry *e, void *txn);
  97. static void automember_add_member_value(Slapi_Entry *member_e, const char *group_dn,
  98. char *grouping_attr, char *grouping_value, void *txn);
  99. /*
  100. * Config cache locking functions
  101. */
  102. void
  103. automember_config_read_lock()
  104. {
  105. slapi_rwlock_rdlock(g_automember_config_lock);
  106. }
  107. void
  108. automember_config_write_lock()
  109. {
  110. slapi_rwlock_wrlock(g_automember_config_lock);
  111. }
  112. void
  113. automember_config_unlock()
  114. {
  115. slapi_rwlock_unlock(g_automember_config_lock);
  116. }
  117. /*
  118. * Plugin identity functions
  119. */
  120. void
  121. automember_set_plugin_id(void *pluginID)
  122. {
  123. _PluginID = pluginID;
  124. }
  125. void *
  126. automember_get_plugin_id()
  127. {
  128. return _PluginID;
  129. }
  130. void
  131. automember_set_plugin_sdn(Slapi_DN *pluginDN)
  132. {
  133. _PluginDN = pluginDN;
  134. }
  135. Slapi_DN *
  136. automember_get_plugin_sdn()
  137. {
  138. return _PluginDN;
  139. }
  140. static int plugin_is_betxn = 0;
  141. /*
  142. * Plug-in initialization functions
  143. */
  144. int
  145. automember_init(Slapi_PBlock *pb)
  146. {
  147. int status = 0;
  148. char *plugin_identity = NULL;
  149. Slapi_Entry *plugin_entry = NULL;
  150. char *plugin_type = NULL;
  151. int preadd = SLAPI_PLUGIN_PRE_ADD_FN;
  152. int premod = SLAPI_PLUGIN_PRE_MODIFY_FN;
  153. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  154. "--> automember_init\n");
  155. /* get args */
  156. if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) &&
  157. plugin_entry &&
  158. (plugin_type = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-plugintype")) &&
  159. plugin_type && strstr(plugin_type, "betxn")) {
  160. plugin_is_betxn = 1;
  161. preadd = SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN;
  162. premod = SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN;
  163. }
  164. slapi_ch_free_string(&plugin_type);
  165. /* Store the plugin identity for later use.
  166. * Used for internal operations. */
  167. slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
  168. PR_ASSERT(plugin_identity);
  169. automember_set_plugin_id(plugin_identity);
  170. /* Register callbacks */
  171. if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
  172. SLAPI_PLUGIN_VERSION_01) != 0 ||
  173. slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
  174. (void *) automember_start) != 0 ||
  175. slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
  176. (void *) automember_close) != 0 ||
  177. slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
  178. (void *) &pdesc) != 0 ||
  179. slapi_pblock_set(pb, premod, (void *) automember_mod_pre_op) != 0 ||
  180. slapi_pblock_set(pb, preadd, (void *) automember_add_pre_op) != 0) {
  181. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  182. "automember_init: failed to register plugin\n");
  183. status = -1;
  184. }
  185. if (!plugin_is_betxn && !status &&
  186. slapi_register_plugin("internalpostoperation", /* op type */
  187. 1, /* Enabled */
  188. "automember_init", /* this function desc */
  189. automember_internal_postop_init, /* init func */
  190. AUTOMEMBER_INT_POSTOP_DESC, /* plugin desc */
  191. NULL, /* ? */
  192. plugin_identity /* access control */
  193. )) {
  194. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  195. "automember_init: failed to register internalpostoperation plugin\n");
  196. status = -1;
  197. }
  198. if (!status) {
  199. plugin_type = "postoperation";
  200. if (plugin_is_betxn) {
  201. plugin_type = "betxnpostoperation";
  202. }
  203. if (slapi_register_plugin(plugin_type, /* op type */
  204. 1, /* Enabled */
  205. "automember_init", /* this function desc */
  206. automember_postop_init, /* init func for post op */
  207. AUTOMEMBER_POSTOP_DESC, /* plugin desc */
  208. NULL, /* ? */
  209. plugin_identity /* access control */
  210. )
  211. ) {
  212. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  213. "automember_init: failed to register postop plugin\n");
  214. status = -1;
  215. }
  216. }
  217. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  218. "<-- automember_init\n");
  219. return status;
  220. }
  221. /* not used when using plugin as a betxn plugin - betxn plugins are called for both internal and external ops */
  222. static int
  223. automember_internal_postop_init(Slapi_PBlock *pb)
  224. {
  225. int status = 0;
  226. if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
  227. SLAPI_PLUGIN_VERSION_01) != 0 ||
  228. slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
  229. (void *) &pdesc) != 0 ||
  230. slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN,
  231. (void *) automember_add_post_op) != 0 ||
  232. slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN,
  233. (void *) automember_del_post_op) != 0 ||
  234. slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN,
  235. (void *) automember_mod_post_op) != 0 ||
  236. slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN,
  237. (void *) automember_modrdn_post_op) != 0) {
  238. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  239. "automember_internal_postop_init: failed to register plugin\n");
  240. status = -1;
  241. }
  242. return status;
  243. }
  244. static int
  245. automember_postop_init(Slapi_PBlock *pb)
  246. {
  247. int status = 0;
  248. int addfn = SLAPI_PLUGIN_POST_ADD_FN;
  249. int delfn = SLAPI_PLUGIN_POST_DELETE_FN;
  250. int modfn = SLAPI_PLUGIN_POST_MODIFY_FN;
  251. int mdnfn = SLAPI_PLUGIN_POST_MODRDN_FN;
  252. if (plugin_is_betxn) {
  253. addfn = SLAPI_PLUGIN_BE_TXN_POST_ADD_FN;
  254. delfn = SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN;
  255. modfn = SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN;
  256. mdnfn = SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN;
  257. }
  258. if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
  259. SLAPI_PLUGIN_VERSION_01) != 0 ||
  260. slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
  261. (void *) &pdesc) != 0 ||
  262. slapi_pblock_set(pb, addfn, (void *) automember_add_post_op) != 0 ||
  263. slapi_pblock_set(pb, delfn, (void *) automember_del_post_op) != 0 ||
  264. slapi_pblock_set(pb, modfn, (void *) automember_mod_post_op) != 0 ||
  265. slapi_pblock_set(pb, mdnfn, (void *) automember_modrdn_post_op) != 0) {
  266. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  267. "automember_postop_init: failed to register plugin\n");
  268. status = -1;
  269. }
  270. return status;
  271. }
  272. /* Stash the config area in the pblock for start functions? */
  273. /*
  274. * automember_start()
  275. *
  276. * Creates config lock and loads config cache.
  277. */
  278. static int
  279. automember_start(Slapi_PBlock * pb)
  280. {
  281. Slapi_DN *plugindn = NULL;
  282. char *config_area = NULL;
  283. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  284. "--> automember_start\n");
  285. /* Check if we're already started */
  286. if (g_plugin_started) {
  287. goto done;
  288. }
  289. g_automember_config_lock = slapi_new_rwlock();
  290. if (!g_automember_config_lock) {
  291. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  292. "automember_start: lock creation failed\n");
  293. return -1;
  294. }
  295. /*
  296. * Get the plug-in target dn from the system
  297. * and store it for future use. */
  298. slapi_pblock_get(pb, SLAPI_TARGET_SDN, &plugindn);
  299. if (NULL == plugindn || 0 == slapi_sdn_get_ndn_len(plugindn)) {
  300. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  301. "automember_start: unable to retrieve plugin dn\n");
  302. return -1;
  303. }
  304. automember_set_plugin_sdn(slapi_sdn_dup(plugindn));
  305. /* Set the alternate config area if one is defined. */
  306. slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_AREA, &config_area);
  307. if (config_area) {
  308. automember_set_config_area(slapi_sdn_new_normdn_byval(config_area));
  309. }
  310. /*
  311. * Load the config cache
  312. */
  313. g_automember_config = (PRCList *)slapi_ch_calloc(1, sizeof(struct configEntry));
  314. PR_INIT_CLIST(g_automember_config);
  315. if (automember_load_config(pb) != 0) {
  316. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  317. "automember_start: unable to load plug-in configuration\n");
  318. return -1;
  319. }
  320. g_plugin_started = 1;
  321. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  322. "auto membership plug-in: ready for service\n");
  323. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  324. "<-- automember_start\n");
  325. done:
  326. return 0;
  327. }
  328. /*
  329. * automember_close()
  330. *
  331. * Cleans up the config cache.
  332. */
  333. static int
  334. automember_close(Slapi_PBlock * pb)
  335. {
  336. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  337. "--> automember_close\n");
  338. if (!g_plugin_started) {
  339. goto done;
  340. }
  341. automember_config_write_lock();
  342. g_plugin_started = 0;
  343. automember_delete_config();
  344. automember_config_unlock();
  345. slapi_ch_free((void **)&g_automember_config);
  346. slapi_sdn_free(&_PluginDN);
  347. slapi_sdn_free(&_ConfigAreaDN);
  348. /* We explicitly don't destroy the config lock here. If we did,
  349. * there is the slight possibility that another thread that just
  350. * passed the g_plugin_started check is about to try to obtain
  351. * a reader lock. We leave the lock around so these threads
  352. * don't crash the process. If we always check the started
  353. * flag again after obtaining a reader lock, no free'd resources
  354. * will be used. */
  355. done:
  356. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  357. "<-- automember_close\n");
  358. return 0;
  359. }
  360. /*
  361. * automember_get_config()
  362. *
  363. * Get the config list.
  364. */
  365. PRCList *
  366. automember_get_config()
  367. {
  368. return g_automember_config;
  369. }
  370. /*
  371. * automember_load_config()
  372. *
  373. * Parse and load the config entries.
  374. */
  375. static int
  376. automember_load_config(Slapi_PBlock *pb)
  377. {
  378. int status = 0;
  379. int result;
  380. int i;
  381. Slapi_PBlock *search_pb;
  382. Slapi_Entry **entries = NULL;
  383. void *txn = NULL;
  384. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  385. "--> automember_load_config\n");
  386. /* Clear out any old config. */
  387. automember_config_write_lock();
  388. automember_delete_config();
  389. slapi_pblock_get(pb, SLAPI_TXN, &txn);
  390. search_pb = slapi_pblock_new();
  391. /* If an alternate config area is configured, find
  392. * the config entries that are beneath it, otherwise
  393. * we load the entries beneath our top-level plug-in
  394. * config entry. */
  395. if (automember_get_config_area()) {
  396. /* Find the config entries beneath the alternate config area. */
  397. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  398. "automember_load_config: Looking for config entries "
  399. "beneath \"%s\".\n", slapi_sdn_get_ndn(automember_get_config_area()));
  400. slapi_search_internal_set_pb(search_pb, slapi_sdn_get_ndn(automember_get_config_area()),
  401. LDAP_SCOPE_SUBTREE, AUTOMEMBER_DEFINITION_FILTER,
  402. NULL, 0, NULL, NULL, automember_get_plugin_id(), 0);
  403. } else {
  404. /* Find the config entries beneath our plugin entry. */
  405. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  406. "automember_load_config: Looking for config entries "
  407. "beneath \"%s\".\n", slapi_sdn_get_ndn(automember_get_plugin_sdn()));
  408. slapi_search_internal_set_pb(search_pb, slapi_sdn_get_ndn(automember_get_plugin_sdn()),
  409. LDAP_SCOPE_SUBTREE, AUTOMEMBER_DEFINITION_FILTER,
  410. NULL, 0, NULL, NULL, automember_get_plugin_id(), 0);
  411. }
  412. slapi_pblock_set(search_pb, SLAPI_TXN, txn);
  413. slapi_search_internal_pb(search_pb);
  414. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
  415. if (LDAP_SUCCESS != result) {
  416. if (automember_get_config_area() && (result == LDAP_NO_SUCH_OBJECT)) {
  417. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  418. "automember_load_config: Config container \"%s\" does "
  419. "not exist.\n", slapi_sdn_get_ndn(automember_get_config_area()));
  420. goto cleanup;
  421. } else {
  422. status = -1;
  423. goto cleanup;
  424. }
  425. }
  426. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
  427. &entries);
  428. /* Loop through all of the entries we found and parse them. */
  429. for (i = 0; entries && (entries[i] != NULL); i++) {
  430. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  431. "automember_load_config: parsing config entry "
  432. "\"%s\".\n", slapi_entry_get_dn(entries[i]));
  433. /* We don't care about the status here because we may have
  434. * some invalid config entries, but we just want to continue
  435. * looking for valid ones. */
  436. automember_parse_config_entry(entries[i], 1, pb);
  437. }
  438. cleanup:
  439. slapi_free_search_results_internal(search_pb);
  440. slapi_pblock_destroy(search_pb);
  441. automember_config_unlock();
  442. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  443. "<-- automember_load_config\n");
  444. return status;
  445. }
  446. /*
  447. * automember_parse_config_entry()
  448. *
  449. * Parses a single config entry. If apply is non-zero, then
  450. * we will load and start using the new config. You can simply
  451. * validate config without making any changes by setting apply
  452. * to 0.
  453. *
  454. * Returns 0 if the entry is valid and -1 if it is invalid.
  455. */
  456. static int
  457. automember_parse_config_entry(Slapi_Entry * e, int apply, Slapi_PBlock *pb)
  458. {
  459. char *value = NULL;
  460. char **values = NULL;
  461. struct configEntry *entry = NULL;
  462. struct configEntry *config_entry;
  463. PRCList *list;
  464. Slapi_PBlock *search_pb = NULL;
  465. Slapi_Entry **rule_entries = NULL;
  466. char *filter_str = NULL;
  467. Slapi_Filter *filter = NULL;
  468. int result;
  469. int entry_added = 0;
  470. int i = 0;
  471. int ret = 0;
  472. void *txn = NULL;
  473. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  474. "--> automember_parse_config_entry\n");
  475. /* If this is the main plug-in config entry or
  476. * the config area container, just bail. */
  477. if ((slapi_sdn_compare(automember_get_plugin_sdn(), slapi_entry_get_sdn(e)) == 0) ||
  478. (automember_get_config_area() && (slapi_sdn_compare(automember_get_config_area(),
  479. slapi_entry_get_sdn(e)) == 0))) {
  480. goto bail;
  481. }
  482. /* If this entry is not an automember config definition entry, just bail. */
  483. filter_str = slapi_ch_strdup(AUTOMEMBER_DEFINITION_FILTER);
  484. filter = slapi_str2filter(filter_str);
  485. if (slapi_filter_test_simple(e, filter) != 0) {
  486. goto bail;
  487. }
  488. /* If marked as disabled, just bail. */
  489. value = slapi_entry_attr_get_charptr(e, AUTOMEMBER_DISABLED_TYPE);
  490. if (value) {
  491. slapi_ch_free_string(&value);
  492. goto bail;
  493. }
  494. entry = (struct configEntry *)slapi_ch_calloc(1, sizeof(struct configEntry));
  495. if (NULL == entry) {
  496. ret = -1;
  497. goto bail;
  498. }
  499. slapi_pblock_get(pb, SLAPI_TXN, &txn);
  500. value = slapi_entry_get_ndn(e);
  501. if (value) {
  502. entry->dn = slapi_ch_strdup(value);
  503. } else {
  504. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  505. "automember_parse_config_entry: Error "
  506. "reading dn from config entry\n");
  507. ret = -1;
  508. goto bail;
  509. }
  510. slapi_log_error(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  511. "----------> dn [%s]\n", entry->dn);
  512. /* Load the scope */
  513. value = slapi_entry_attr_get_charptr(e, AUTOMEMBER_SCOPE_TYPE);
  514. if (value) {
  515. entry->scope = value;
  516. } else {
  517. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  518. "automember_parse_config_entry: The %s config "
  519. "setting is required for config entry \"%s\".\n",
  520. AUTOMEMBER_SCOPE_TYPE, entry->dn);
  521. ret = -1;
  522. goto bail;
  523. }
  524. /* Load the filter */
  525. value = slapi_entry_attr_get_charptr(e, AUTOMEMBER_FILTER_TYPE);
  526. if (value) {
  527. /* Convert to a Slapi_Filter to improve performance. */
  528. if (NULL == (entry->filter = slapi_str2filter(value))) {
  529. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM ,
  530. "automember_parse_config_entry: Invalid search filter in "
  531. "%s config setting for config entry \"%s\" "
  532. "(filter = \"%s\").\n", AUTOMEMBER_FILTER_TYPE, entry->dn, value);
  533. ret = -1;
  534. }
  535. slapi_ch_free_string(&value);
  536. if (ret != 0) {
  537. goto bail;
  538. }
  539. } else {
  540. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  541. "automember_parse_config_entry: The %s config "
  542. "setting is required for config entry \"%s\".\n",
  543. AUTOMEMBER_FILTER_TYPE, entry->dn);
  544. ret = -1;
  545. goto bail;
  546. }
  547. /* Load the default groups */
  548. values = slapi_entry_attr_get_charray(e, AUTOMEMBER_DEFAULT_GROUP_TYPE);
  549. if (values) {
  550. /* Just hand off the values */
  551. entry->default_groups = values;
  552. values = NULL;
  553. }
  554. /* Load the grouping attr */
  555. value = slapi_entry_attr_get_charptr(e, AUTOMEMBER_GROUPING_ATTR_TYPE);
  556. if (value) {
  557. if (automember_parse_grouping_attr(value, &(entry->grouping_attr),
  558. &(entry->grouping_value)) != 0) {
  559. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  560. "automember_parse_config_entry: Invalid "
  561. "%s config setting for config entry \"%s\" "
  562. "(value: \"%s\").\n", AUTOMEMBER_GROUPING_ATTR_TYPE,
  563. entry->dn, value);
  564. ret = -1;
  565. }
  566. slapi_ch_free_string(&value);
  567. if (ret != 0) {
  568. goto bail;
  569. }
  570. } else {
  571. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  572. "automember_parse_config_entry: The %s config "
  573. "setting is required for config entry \"%s\".\n",
  574. AUTOMEMBER_GROUPING_ATTR_TYPE, entry->dn);
  575. ret = -1;
  576. goto bail;
  577. }
  578. /* Find all child regex rule entries */
  579. search_pb = slapi_pblock_new();
  580. slapi_search_internal_set_pb(search_pb, entry->dn, LDAP_SCOPE_SUBTREE,
  581. AUTOMEMBER_REGEX_RULE_FILTER, NULL, 0, NULL,
  582. NULL, automember_get_plugin_id(), 0);
  583. slapi_pblock_set(search_pb, SLAPI_TXN, txn);
  584. slapi_search_internal_pb(search_pb);
  585. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
  586. /* If this is a new config entry being added, it won't exist yet
  587. * when we are simply validating config. We can just ignore no
  588. * such object errors. */
  589. if ((LDAP_SUCCESS != result) && (LDAP_NO_SUCH_OBJECT != result)) {
  590. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  591. "automember_parse_config_entry: Error searching "
  592. "for child rule entries for config \"%s\" (err=%d).",
  593. entry->dn, result);
  594. ret = -1;
  595. goto bail;
  596. }
  597. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
  598. &rule_entries);
  599. /* Go through each child rule entry and parse it. */
  600. for (i = 0; rule_entries && (rule_entries[i] != NULL); i++) {
  601. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  602. "automember_parse_config_entry: parsing regex rule entry "
  603. "\"%s\".\n", slapi_entry_get_dn(rule_entries[i]));
  604. automember_parse_regex_entry(entry, rule_entries[i]);
  605. }
  606. /* If we were only called to validate config, we can
  607. * just bail out before applying the config changes */
  608. if (apply == 0) {
  609. goto bail;
  610. }
  611. /* Add the config object to the list. We order by scope. */
  612. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  613. list = PR_LIST_HEAD(g_automember_config);
  614. while (list != g_automember_config) {
  615. config_entry = (struct configEntry *) list;
  616. /* If the config entry we are adding has a scope that is
  617. * a child of the scope of the current list item, we insert
  618. * the entry before that list item. */
  619. if (slapi_dn_issuffix(entry->scope, config_entry->scope)) {
  620. PR_INSERT_BEFORE(&(entry->list), list);
  621. slapi_log_error(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  622. "store [%s] before [%s] \n", entry->dn,
  623. config_entry->dn);
  624. entry_added = 1;
  625. break;
  626. }
  627. list = PR_NEXT_LINK(list);
  628. /* If we hit the end of the list, add to the tail. */
  629. if (g_automember_config == list) {
  630. PR_INSERT_BEFORE(&(entry->list), list);
  631. slapi_log_error(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  632. "store [%s] at tail\n", entry->dn);
  633. entry_added = 1;
  634. break;
  635. }
  636. }
  637. } else {
  638. /* first entry */
  639. PR_INSERT_LINK(&(entry->list), g_automember_config);
  640. slapi_log_error(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  641. "store [%s] at head \n", entry->dn);
  642. entry_added = 1;
  643. }
  644. bail:
  645. if (0 == entry_added) {
  646. /* Don't log error if we weren't asked to apply config */
  647. if ((apply != 0) && (entry != NULL)) {
  648. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  649. "automember_parse_config_entry: Invalid config entry "
  650. "[%s] skipped\n", entry->dn);
  651. }
  652. automember_free_config_entry(&entry);
  653. } else {
  654. ret = 0;
  655. }
  656. slapi_ch_free_string(&filter_str);
  657. slapi_filter_free(filter, 1);
  658. slapi_free_search_results_internal(search_pb);
  659. slapi_pblock_destroy(search_pb);
  660. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  661. "<-- automember_parse_config_entry\n");
  662. return ret;
  663. }
  664. static void
  665. automember_free_config_entry(struct configEntry ** entry)
  666. {
  667. struct configEntry *e = *entry;
  668. if (e == NULL)
  669. return;
  670. if (e->dn) {
  671. slapi_log_error(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  672. "freeing config entry [%s]\n", e->dn);
  673. slapi_ch_free_string(&e->dn);
  674. }
  675. if (e->scope) {
  676. slapi_ch_free_string(&e->scope);
  677. }
  678. if (e->filter) {
  679. slapi_filter_free(e->filter, 1);
  680. }
  681. if (e->exclusive_rules) {
  682. PRCList *list;
  683. /* Clear out the list contents. */
  684. while (!PR_CLIST_IS_EMPTY((PRCList *)e->exclusive_rules)) {
  685. list = PR_LIST_HEAD((PRCList *)e->exclusive_rules);
  686. PR_REMOVE_LINK(list);
  687. automember_free_regex_rule((struct automemberRegexRule *)list);
  688. }
  689. /* Free the list itself. */
  690. slapi_ch_free((void **)&(e->exclusive_rules));
  691. }
  692. /* Clear out the list contents. */
  693. if (e->inclusive_rules) {
  694. PRCList *list;
  695. while (!PR_CLIST_IS_EMPTY((PRCList *)e->inclusive_rules)) {
  696. list = PR_LIST_HEAD((PRCList *)e->inclusive_rules);
  697. PR_REMOVE_LINK(list);
  698. automember_free_regex_rule((struct automemberRegexRule *)list);
  699. }
  700. /* Free the list itself. */
  701. slapi_ch_free((void **)&(e->inclusive_rules));
  702. }
  703. if (e->default_groups) {
  704. slapi_ch_array_free(e->default_groups);
  705. }
  706. if (e->grouping_attr) {
  707. slapi_ch_free_string(&e->grouping_attr);
  708. }
  709. if (e->grouping_value) {
  710. slapi_ch_free_string(&e->grouping_value);
  711. }
  712. slapi_ch_free((void **)entry);
  713. }
  714. static void
  715. automember_delete_configEntry(PRCList *entry)
  716. {
  717. PR_REMOVE_LINK(entry);
  718. automember_free_config_entry((struct configEntry **) &entry);
  719. }
  720. static void
  721. automember_delete_config()
  722. {
  723. PRCList *list;
  724. /* Delete the config cache. */
  725. while (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  726. list = PR_LIST_HEAD(g_automember_config);
  727. automember_delete_configEntry(list);
  728. }
  729. return;
  730. }
  731. static Slapi_DN *
  732. automember_get_sdn(Slapi_PBlock * pb)
  733. {
  734. Slapi_DN *sdn = 0;
  735. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  736. "--> automember_get_sdn\n");
  737. slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
  738. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  739. "<-- automember_get_sdn\n");
  740. return sdn;
  741. }
  742. void
  743. automember_set_config_area(Slapi_DN *sdn)
  744. {
  745. _ConfigAreaDN = sdn;
  746. }
  747. Slapi_DN *
  748. automember_get_config_area()
  749. {
  750. return _ConfigAreaDN;
  751. }
  752. /*
  753. * automember_dn_is_config()
  754. *
  755. * Checks if dn is an auto membership config entry.
  756. */
  757. static int
  758. automember_dn_is_config(Slapi_DN *sdn)
  759. {
  760. int ret = 0;
  761. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  762. "--> automember_dn_is_config\n");
  763. if (sdn == NULL) {
  764. goto bail;
  765. }
  766. /* If an alternate config area is configured, treat it's child
  767. * entries as config entries. If the alternate config area is
  768. * not configured, treat children of the top-level plug-in
  769. * config entry as our config entries. */
  770. if (automember_get_config_area()) {
  771. if (slapi_sdn_issuffix(sdn, automember_get_config_area()) &&
  772. slapi_sdn_compare(sdn, automember_get_config_area())) {
  773. ret = 1;
  774. }
  775. } else {
  776. if (slapi_sdn_issuffix(sdn, automember_get_plugin_sdn()) &&
  777. slapi_sdn_compare(sdn, automember_get_plugin_sdn())) {
  778. ret = 1;
  779. }
  780. }
  781. bail:
  782. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  783. "<-- automember_dn_is_config\n");
  784. return ret;
  785. }
  786. /*
  787. * automember_oktodo()
  788. *
  789. * Check if we want to process this operation. We need to be
  790. * sure that the operation succeeded.
  791. */
  792. static int
  793. automember_oktodo(Slapi_PBlock *pb)
  794. {
  795. int ret = 1;
  796. int oprc = 0;
  797. slapi_log_error( SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  798. "--> automember_oktodo\n" );
  799. if(slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0) {
  800. slapi_log_error( SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  801. "automember_oktodo: could not get parameters\n" );
  802. ret = -1;
  803. }
  804. /* This plugin should only execute if the operation succeeded. */
  805. if(oprc != 0) {
  806. ret = 0;
  807. }
  808. slapi_log_error( SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  809. "<-- automember_oktodo\n" );
  810. return ret;
  811. }
  812. /*
  813. * automember_isrepl()
  814. *
  815. * Returns 1 if the operation associated with pb
  816. * is a replicated op. Returns 0 otherwise.
  817. */
  818. static int
  819. automember_isrepl(Slapi_PBlock *pb)
  820. {
  821. int is_repl = 0;
  822. slapi_log_error( SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  823. "--> automember_isrepl\n" );
  824. slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl);
  825. slapi_log_error( SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  826. "<-- automember_isrepl\n" );
  827. return is_repl;
  828. }
  829. /*
  830. * automember_parse_regex_entry()
  831. *
  832. * Parses a rule entry and adds the regex rules to the
  833. * passed in config struct. Invalid regex rules will
  834. * be skipped and logged at the fatal log level.
  835. */
  836. static void
  837. automember_parse_regex_entry(struct configEntry *config, Slapi_Entry *e)
  838. {
  839. char *target_group = NULL;
  840. char **values = NULL;
  841. PRCList *list;
  842. int i = 0;
  843. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  844. "--> automember_parse_regex_entry\n");
  845. /* Make sure the target group was specified. */
  846. target_group = slapi_entry_attr_get_charptr(e, AUTOMEMBER_TARGET_GROUP_TYPE);
  847. if (!target_group) {
  848. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  849. "automember_parse_regex_entry: The %s config "
  850. "setting is required for rule entry \"%s\".\n",
  851. AUTOMEMBER_TARGET_GROUP_TYPE, slapi_entry_get_ndn(e));
  852. goto bail;
  853. }
  854. /* Ensure that the target group DN is valid. */
  855. if (slapi_dn_syntax_check(NULL, target_group, 1) != 0) {
  856. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  857. "automember_parse_regex_entry: invalid target group DN "
  858. "in rule \"%s\" (dn=\"%s\").\n", slapi_entry_get_ndn(e),
  859. target_group);
  860. goto bail;
  861. }
  862. /* Load inclusive rules */
  863. values = slapi_entry_attr_get_charray(e, AUTOMEMBER_INC_REGEX_TYPE);
  864. if (values) {
  865. struct automemberRegexRule *rule = NULL;
  866. /* If we haven't loaded any inclusive rules for
  867. * this config definition yet, create a new list. */
  868. if (config->inclusive_rules == NULL) {
  869. /* Create a list to hold our regex rules */
  870. config->inclusive_rules = (struct automemberRegexRule *)slapi_ch_calloc(1, sizeof(struct automemberRegexRule));
  871. PR_INIT_CLIST((PRCList *)config->inclusive_rules);
  872. }
  873. for (i = 0; values && values[i]; ++i) {
  874. rule = automember_parse_regex_rule(values[i]);
  875. if (rule) {
  876. /* Fill in the target group. */
  877. rule->target_group_dn = slapi_sdn_new_normdn_byval(target_group);
  878. if (!PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
  879. list = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
  880. while (list != (PRCList *)config->inclusive_rules) {
  881. struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)list;
  882. /* Order rules by target group DN */
  883. if (slapi_sdn_compare(rule->target_group_dn, curr_rule->target_group_dn) < 0) {
  884. PR_INSERT_BEFORE(&(rule->list), list);
  885. break;
  886. }
  887. list = PR_NEXT_LINK(list);
  888. /* If we hit the end of the list, add to the tail. */
  889. if ((PRCList *)config->inclusive_rules == list) {
  890. PR_INSERT_BEFORE(&(rule->list), list);
  891. break;
  892. }
  893. }
  894. } else {
  895. /* Add to head of list */
  896. PR_INSERT_LINK(&(rule->list), (PRCList *)config->inclusive_rules);
  897. }
  898. } else {
  899. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  900. "automember_parse_regex_entry: Skipping invalid inclusive "
  901. "regex rule in rule entry \"%s\" (rule = \"%s\").\n",
  902. slapi_entry_get_ndn(e), values[i]);
  903. }
  904. }
  905. slapi_ch_array_free(values);
  906. values = NULL;
  907. }
  908. /* Load exclusive rules. */
  909. values = slapi_entry_attr_get_charray(e, AUTOMEMBER_EXC_REGEX_TYPE);
  910. if (values) {
  911. struct automemberRegexRule *rule = NULL;
  912. /* If we haven't loaded any exclusive rules for
  913. * this config definition yet, create a new list. */
  914. if (config->exclusive_rules == NULL) {
  915. /* Create a list to hold our regex rules */
  916. config->exclusive_rules = (struct automemberRegexRule *)slapi_ch_calloc(1, sizeof(struct automemberRegexRule));
  917. PR_INIT_CLIST((PRCList *)config->exclusive_rules);
  918. }
  919. for (i = 0; values && values[i]; ++i) {
  920. rule = automember_parse_regex_rule(values[i]);
  921. if (rule) {
  922. /* Fill in the target group. */
  923. rule->target_group_dn = slapi_sdn_new_normdn_byval(target_group);
  924. if (!PR_CLIST_IS_EMPTY((PRCList *)config->exclusive_rules)) {
  925. list = PR_LIST_HEAD((PRCList *)config->exclusive_rules);
  926. while (list != (PRCList *)config->exclusive_rules) {
  927. struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)list;
  928. /* Order rules by target group DN */
  929. if (slapi_sdn_compare(rule->target_group_dn, curr_rule->target_group_dn) < 0) {
  930. PR_INSERT_BEFORE(&(rule->list), list);
  931. break;
  932. }
  933. list = PR_NEXT_LINK(list);
  934. /* If we hit the end of the list, add to the tail. */
  935. if ((PRCList *)config->exclusive_rules == list) {
  936. PR_INSERT_BEFORE(&(rule->list), list);
  937. break;
  938. }
  939. }
  940. } else {
  941. /* Add to head of list */
  942. PR_INSERT_LINK(&(rule->list), (PRCList *)config->exclusive_rules);
  943. }
  944. } else {
  945. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  946. "automember_parse_regex_entry: Skipping invalid exclusive "
  947. "regex rule in rule entry \"%s\" (rule = \"%s\").\n",
  948. slapi_entry_get_ndn(e), values[i]);
  949. }
  950. }
  951. slapi_ch_array_free(values);
  952. values = NULL;
  953. }
  954. bail:
  955. slapi_ch_free_string(&target_group);
  956. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  957. "<-- automember_parse_regex_entry\n");
  958. }
  959. /*
  960. * automember_parse_regex_rule()
  961. *
  962. * Parses a regex rule and returns a regex rule struct. The caller
  963. * will need to free this struct when it is finished with it. If
  964. * there is a problem parsing the regex rule, an error will be
  965. * logged and NULL will be returned.
  966. */
  967. static struct automemberRegexRule *
  968. automember_parse_regex_rule(char *rule_string)
  969. {
  970. struct automemberRegexRule *rule = NULL;
  971. char *attr = NULL;
  972. Slapi_Regex *regex = NULL;
  973. const char *recomp_result = NULL;
  974. char *p = NULL;
  975. char *p2 = NULL;
  976. /* A rule is in the form "attr=regex". */
  977. /* Find the comparison attribute name. */
  978. if ((p = strchr(rule_string, '=')) == NULL) {
  979. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  980. "automember_parse_regex_rule: Unable to parse "
  981. "regex rule (missing '=' delimeter).\n");
  982. goto bail;
  983. }
  984. /* Make sure the attribute name is not empty. */
  985. if (p == rule_string) {
  986. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  987. "automember_parse_regex_rule: Unable to parse "
  988. " regex rule (missing comparison attribute).\n");
  989. goto bail;
  990. }
  991. if ((attr = strndup(rule_string, p - rule_string)) == NULL) {
  992. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  993. "automember_parse_regex_rule: Unable to allocate "
  994. "memory.\n");
  995. goto bail;
  996. }
  997. /* Validate the attribute. */
  998. for (p2 = attr; p2 && (*p2 != '\0'); p2++) {
  999. if (!IS_ATTRDESC_CHAR(*p2)) {
  1000. slapi_log_error( SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1001. "automember_parse_regex_rule: Invalid comparison "
  1002. "attribute name \"%s\".\n", attr);
  1003. goto bail;
  1004. }
  1005. }
  1006. /* Find the regex. */
  1007. p++;
  1008. if (*p == '\0') {
  1009. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1010. "automember_parse_regex_rule: Unable to parse "
  1011. "regex rule (missing regex).\n");
  1012. goto bail;
  1013. }
  1014. /* Compile the regex to validate it. */
  1015. regex = slapi_re_comp(p, &recomp_result);
  1016. if (!regex) {
  1017. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1018. "automember_parse_regex_rule: Unable to parse "
  1019. "regex rule (invalid regex). Error \"%s\".\n",
  1020. recomp_result?recomp_result:"unknown");
  1021. }
  1022. /* Validation has passed, so create the regex rule struct and fill it in.
  1023. * We hand off everything we have allocated. All of this will be free'd
  1024. * when the rule struct itself is freed. */
  1025. rule = (struct automemberRegexRule *)slapi_ch_calloc(1, sizeof(struct automemberRegexRule));
  1026. rule->attr = attr;
  1027. rule->regex_str = slapi_ch_strdup(p);
  1028. rule->regex = regex;
  1029. bail:
  1030. /* Cleanup if we didn't successfully create a rule. */
  1031. if (!rule) {
  1032. slapi_ch_free_string(&attr);
  1033. slapi_re_free(regex);
  1034. }
  1035. return rule;
  1036. }
  1037. /*
  1038. * automember_free_regex_rule()
  1039. *
  1040. * Frees a regex rule and all of it's contents from memory.
  1041. */
  1042. static void
  1043. automember_free_regex_rule(struct automemberRegexRule *rule)
  1044. {
  1045. if (rule) {
  1046. if (rule->target_group_dn) {
  1047. slapi_sdn_free(&(rule->target_group_dn));
  1048. }
  1049. if (rule->attr) {
  1050. slapi_ch_free_string(&(rule->attr));
  1051. }
  1052. if (rule->regex_str) {
  1053. slapi_ch_free_string(&(rule->regex_str));
  1054. }
  1055. if (rule->regex) {
  1056. slapi_re_free(rule->regex);
  1057. }
  1058. }
  1059. slapi_ch_free((void **)&rule);
  1060. }
  1061. /*
  1062. * automember_parse_grouping_attr()
  1063. *
  1064. * Parses a grouping attribute and grouping value from
  1065. * the passed in config string. Memory will be allocated
  1066. * for grouping_attr and grouping_value, so it is up to
  1067. * the called to free them when they are no longer needed.
  1068. * Returns 0 upon success and 1 upon failure.
  1069. */
  1070. static int
  1071. automember_parse_grouping_attr(char *value, char **grouping_attr, char **grouping_value)
  1072. {
  1073. int ret = 0;
  1074. char *p = NULL;
  1075. /* Clear out any existing type or value. */
  1076. slapi_ch_free_string(grouping_attr);
  1077. slapi_ch_free_string(grouping_value);
  1078. /* split out the type from the value (use the first ':') */
  1079. if ((p = strchr(value, ':')) == NULL) {
  1080. slapi_log_error( SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1081. "automember_parse_grouping_attr: Value for grouping attribute "
  1082. "is not in the correct format. (value: \"%s\").\n", value);
  1083. ret = 1;
  1084. goto bail;
  1085. }
  1086. /* Ensure the type is not empty. */
  1087. if (p == value) {
  1088. slapi_log_error( SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1089. "automember_parse_grouping_attr: Value for grouping attribute "
  1090. "is not in the correct format. The grouping attribute is missing. "
  1091. "(value: \"%s\").\n", value);
  1092. ret = 1;
  1093. goto bail;
  1094. }
  1095. /* Duplicate the type to be returned. */
  1096. *grouping_attr = strndup(value, p - value);
  1097. /* Advance p to point to the beginning of the value. */
  1098. p++;
  1099. while (*p == ' ') {
  1100. p++;
  1101. }
  1102. /* Ensure the value is not empty. */
  1103. if (*p == '\0') {
  1104. slapi_log_error( SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1105. "automember_parse_grouping_attr: Value for grouping attribute "
  1106. "is not in the correct format. The grouping value is "
  1107. "missing. (value: \"%s\").\n", value);
  1108. ret = 1;
  1109. goto bail;
  1110. }
  1111. /* Duplicate the value to be returned. */
  1112. *grouping_value = slapi_ch_strdup(p);
  1113. /* Ensure that memory was allocated successfully. */
  1114. if ((*grouping_attr == NULL) || (*grouping_value == NULL)) {
  1115. slapi_log_error( SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1116. "automember_parse_grouping_attr: Error allocating memory.\n");
  1117. ret = 1;
  1118. goto bail;
  1119. }
  1120. /* Ensure that the grouping attr is a legal attr name. */
  1121. for (p = *grouping_attr; p && (*p != '\0'); p++) {
  1122. if (!IS_ATTRDESC_CHAR(*p)) {
  1123. slapi_log_error( SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1124. "automember_parse_grouping_attr: Invalid value for "
  1125. "grouping attribute. The grouping attribute type is "
  1126. "illegal. (type: \"%s\").\n", *grouping_attr);
  1127. ret = 1;
  1128. goto bail;
  1129. }
  1130. }
  1131. /* Ensure that the grouping value type is a legal attr name. */
  1132. for (p = *grouping_value; p && (*p != '\0'); p++) {
  1133. if (!IS_ATTRDESC_CHAR(*p)) {
  1134. slapi_log_error( SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1135. "automember_parse_grouping_attr: Invalid value for "
  1136. "grouping attribute. The grouping value type is "
  1137. "illegal. (type: \"%s\").\n", *grouping_value);
  1138. ret = 1;
  1139. goto bail;
  1140. }
  1141. }
  1142. bail:
  1143. if (ret != 0) {
  1144. slapi_ch_free_string(grouping_attr);
  1145. slapi_ch_free_string(grouping_value);
  1146. }
  1147. return ret;
  1148. }
  1149. /*
  1150. * automember_update_membership()
  1151. *
  1152. * Determines which target groups need to be updated according to
  1153. * the rules in config, then performs the updates.
  1154. */
  1155. static void
  1156. automember_update_membership(struct configEntry *config, Slapi_Entry *e, void *txn)
  1157. {
  1158. PRCList *rule = NULL;
  1159. struct automemberRegexRule *curr_rule = NULL;
  1160. PRCList exclusions;
  1161. PRCList targets;
  1162. struct automemberDNListItem *dnitem = NULL;
  1163. Slapi_DN *last = NULL;
  1164. PRCList *curr_exclusion = NULL;
  1165. char **vals = NULL;
  1166. int i = 0;
  1167. if (!config || !e) {
  1168. return;
  1169. }
  1170. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1171. "automember_update_membership: Processing \"%s\" "
  1172. "definition entry for candidate entry \"%s\".\n",
  1173. config->dn, slapi_entry_get_dn(e));
  1174. /* Initialize our lists that keep track of targets. */
  1175. PR_INIT_CLIST(&exclusions);
  1176. PR_INIT_CLIST(&targets);
  1177. /* Go through exclusive rules and build an exclusion list. */
  1178. if (config->exclusive_rules) {
  1179. if (!PR_CLIST_IS_EMPTY((PRCList *)config->exclusive_rules)) {
  1180. rule = PR_LIST_HEAD((PRCList *)config->exclusive_rules);
  1181. while (rule != (PRCList *)config->exclusive_rules) {
  1182. curr_rule = (struct automemberRegexRule *)rule;
  1183. /* Regex rules are sorted by the target group DN. This means
  1184. * we can skip all rules for the last target group DN that we
  1185. * added to the exclusions list. */
  1186. if ((last == NULL) || slapi_sdn_compare(last, curr_rule->target_group_dn) != 0) {
  1187. /* Get comparison attr and loop through values. */
  1188. vals = slapi_entry_attr_get_charray(e, curr_rule->attr);
  1189. for (i = 0; vals && vals[i]; ++i) {
  1190. /* Evaluate the regex. */
  1191. if (slapi_re_exec(curr_rule->regex, vals[i], -1) == 1) {
  1192. /* Found a match. Add to end of the exclusion list
  1193. * and set last as a hint to ourselves. */
  1194. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1195. "automember_update_membership: Adding \"%s\" "
  1196. "to list of excluded groups for \"%s\" "
  1197. "(matched: \"%s=%s\").\n",
  1198. slapi_sdn_get_dn(curr_rule->target_group_dn),
  1199. slapi_entry_get_dn(e), curr_rule->attr,
  1200. curr_rule->regex_str);
  1201. dnitem = (struct automemberDNListItem *)slapi_ch_calloc(1,
  1202. sizeof(struct automemberDNListItem));
  1203. /* We are just referencing the dn from the regex rule. We
  1204. * will not free it when we clean up this list. This list
  1205. * is more short-lived than the regex rule list, so we can
  1206. * get away with this optimization. */
  1207. dnitem->dn = curr_rule->target_group_dn;
  1208. PR_APPEND_LINK(&(dnitem->list), &exclusions);
  1209. last = curr_rule->target_group_dn;
  1210. }
  1211. }
  1212. slapi_ch_array_free(vals);
  1213. vals = NULL;
  1214. }
  1215. rule = PR_NEXT_LINK(rule);
  1216. }
  1217. }
  1218. }
  1219. /* Go through inclusive rules and build the target list. */
  1220. if (config->inclusive_rules) {
  1221. if (!PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
  1222. /* Clear out our last hint from processing exclusions. */
  1223. last = NULL;
  1224. /* Get the first excluded target for exclusion checking. */
  1225. if (!PR_CLIST_IS_EMPTY(&exclusions)) {
  1226. curr_exclusion = PR_LIST_HEAD(&exclusions);
  1227. }
  1228. rule = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
  1229. while (rule != (PRCList *)config->inclusive_rules) {
  1230. curr_rule = (struct automemberRegexRule *)rule;
  1231. /* The excluded targets are stored in alphabetical order. Instead
  1232. * of going through the entire exclusion list for each inclusive
  1233. * rule, we can simply go through the exclusion list once and keep
  1234. * track of our position. If the curent exclusion comes after
  1235. * the target DN used in the current inclusive rule, it can't be
  1236. * excluded. If the current exclusion comes before the target
  1237. * in the current rule, we need to go through the exclusion list
  1238. * until we find a target that is the same or comes after the
  1239. * current rule. */
  1240. if (curr_exclusion) {
  1241. while ((curr_exclusion != &exclusions) && (slapi_sdn_compare(
  1242. ((struct automemberDNListItem *)curr_exclusion)->dn,
  1243. curr_rule->target_group_dn) < 0)) {
  1244. curr_exclusion = PR_NEXT_LINK(curr_exclusion);
  1245. }
  1246. }
  1247. /* Regex rules are sorted by the target group DN. This means
  1248. * we can skip all rules for the last target group DN that we
  1249. * added to the targets list. We also skip any rules for
  1250. * target groups that have been excluded by an exclusion rule. */
  1251. if (((curr_exclusion == NULL) || (curr_exclusion == &exclusions) ||
  1252. slapi_sdn_compare(((struct automemberDNListItem *)curr_exclusion)->dn,
  1253. curr_rule->target_group_dn) != 0) && ((last == NULL) ||
  1254. (slapi_sdn_compare(last, curr_rule->target_group_dn) != 0))) {
  1255. /* Get comparison attr and loop through values. */
  1256. vals = slapi_entry_attr_get_charray(e, curr_rule->attr);
  1257. for (i = 0; vals && vals[i]; ++i) {
  1258. /* Evaluate the regex. */
  1259. if (slapi_re_exec(curr_rule->regex, vals[i], -1) == 1) {
  1260. /* Found a match. Add to the end of the targets list
  1261. * and set last as a hint to ourselves. */
  1262. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1263. "automember_update_membership: Adding \"%s\" "
  1264. "to list of target groups for \"%s\" "
  1265. "(matched: \"%s=%s\").\n",
  1266. slapi_sdn_get_dn(curr_rule->target_group_dn),
  1267. slapi_entry_get_dn(e), curr_rule->attr,
  1268. curr_rule->regex_str);
  1269. dnitem = (struct automemberDNListItem *)slapi_ch_calloc(1,
  1270. sizeof(struct automemberDNListItem));
  1271. /* We are just referencing the dn from the regex rule. We
  1272. * will not free it when we clean up this list. This list
  1273. * is more short-lived than the regex rule list, so we can
  1274. * get away with this optimization. */
  1275. dnitem->dn = curr_rule->target_group_dn;
  1276. PR_APPEND_LINK(&(dnitem->list), &targets);
  1277. last = curr_rule->target_group_dn;
  1278. }
  1279. }
  1280. slapi_ch_array_free(vals);
  1281. vals = NULL;
  1282. }
  1283. rule = PR_NEXT_LINK(rule);
  1284. }
  1285. }
  1286. }
  1287. /* If no targets, update default groups if set. Otherwise, update
  1288. * targets. Use a helper to do the actual updates. We can just pass an
  1289. * array of target group DNs along with our entry DN, the grouping attr,
  1290. * and the grouping value. */
  1291. if (PR_CLIST_IS_EMPTY(&targets)) {
  1292. /* Add to each default group. */
  1293. for (i = 0; config->default_groups && config->default_groups[i]; i++) {
  1294. automember_add_member_value(e, config->default_groups[i],
  1295. config->grouping_attr, config->grouping_value, txn);
  1296. }
  1297. } else {
  1298. /* Update the target groups. */
  1299. dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(&targets);
  1300. while ((PRCList *)dnitem != &targets) {
  1301. automember_add_member_value(e, slapi_sdn_get_dn(dnitem->dn),
  1302. config->grouping_attr, config->grouping_value, txn);
  1303. dnitem = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dnitem);
  1304. }
  1305. }
  1306. /* Free the exclusions and targets lists. Remember that the DN's
  1307. * are not ours, so don't free them! */
  1308. while (!PR_CLIST_IS_EMPTY(&exclusions)) {
  1309. dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(&exclusions);
  1310. PR_REMOVE_LINK((PRCList *)dnitem);
  1311. slapi_ch_free((void**)&dnitem);
  1312. }
  1313. while (!PR_CLIST_IS_EMPTY(&targets)) {
  1314. dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(&targets);
  1315. PR_REMOVE_LINK((PRCList *)dnitem);
  1316. slapi_ch_free((void**)&dnitem);
  1317. }
  1318. }
  1319. /*
  1320. * automember_add_member_value()
  1321. *
  1322. * Adds a member entry to a group.
  1323. */
  1324. static void
  1325. automember_add_member_value(Slapi_Entry *member_e, const char *group_dn,
  1326. char *grouping_attr, char *grouping_value, void *txn)
  1327. {
  1328. Slapi_PBlock *mod_pb = slapi_pblock_new();
  1329. int result = LDAP_SUCCESS;
  1330. LDAPMod mod;
  1331. LDAPMod *mods[2];
  1332. char *vals[2];
  1333. char *member_value = NULL;
  1334. int freeit = 0;
  1335. /* If grouping_value is dn, we need to fetch the dn instead. */
  1336. if (slapi_attr_type_cmp(grouping_value, "dn", SLAPI_TYPE_CMP_EXACT) == 0) {
  1337. member_value = slapi_entry_get_ndn(member_e);
  1338. } else {
  1339. member_value = slapi_entry_attr_get_charptr(member_e, grouping_value);
  1340. freeit = 1;
  1341. }
  1342. if (member_value) {
  1343. /* Set up the operation. */
  1344. vals[0] = member_value;
  1345. vals[1] = 0;
  1346. mod.mod_op = LDAP_MOD_ADD;
  1347. mod.mod_type = grouping_attr;
  1348. mod.mod_values = vals;
  1349. mods[0] = &mod;
  1350. mods[1] = 0;
  1351. /* Perform the modify operation. */
  1352. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1353. "automember_add_member_value: Adding \"%s\" as "
  1354. "a \"%s\" value to group \"%s\".\n",
  1355. member_value, grouping_attr, group_dn);
  1356. slapi_modify_internal_set_pb(mod_pb, group_dn,
  1357. mods, 0, 0, automember_get_plugin_id(), 0);
  1358. slapi_pblock_set(mod_pb, SLAPI_TXN, txn);
  1359. slapi_modify_internal_pb(mod_pb);
  1360. slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
  1361. if ((result != LDAP_SUCCESS) && (result != LDAP_TYPE_OR_VALUE_EXISTS)) {
  1362. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1363. "automember_add_member_value: Unable to add \"%s\" as "
  1364. "a \"%s\" value to group \"%s\" (%s).\n",
  1365. member_value, grouping_attr, group_dn,
  1366. ldap_err2string(result));
  1367. }
  1368. } else {
  1369. slapi_log_error(SLAPI_LOG_FATAL, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1370. "automember_add_member_value: Unable to find grouping "
  1371. "value attribute \"%s\" in entry \"%s\".\n",
  1372. grouping_value, slapi_entry_get_dn(member_e));
  1373. }
  1374. /* Cleanup */
  1375. if (freeit) {
  1376. slapi_ch_free_string(&member_value);
  1377. }
  1378. slapi_pblock_destroy(mod_pb);
  1379. }
  1380. /*
  1381. * Operation callback functions
  1382. */
  1383. /*
  1384. * automember_pre_op()
  1385. *
  1386. * Checks if an operation affects the auto membership
  1387. * config and validates the config changes.
  1388. */
  1389. static int
  1390. automember_pre_op(Slapi_PBlock * pb, int modop)
  1391. {
  1392. Slapi_DN *sdn = 0;
  1393. Slapi_Entry *e = 0;
  1394. Slapi_Mods *smods = 0;
  1395. LDAPMod **mods;
  1396. int free_entry = 0;
  1397. char *errstr = NULL;
  1398. int ret = 0;
  1399. void *txn = NULL;
  1400. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1401. "--> automember_pre_op\n");
  1402. /* Just bail if we aren't ready to service requests yet. */
  1403. if (!g_plugin_started)
  1404. goto bail;
  1405. if (0 == (sdn = automember_get_sdn(pb)))
  1406. goto bail;
  1407. slapi_pblock_get(pb, SLAPI_TXN, &txn);
  1408. if (automember_dn_is_config(sdn)) {
  1409. /* Validate config changes, but don't apply them.
  1410. * This allows us to reject invalid config changes
  1411. * here at the pre-op stage. Applying the config
  1412. * needs to be done at the post-op stage. */
  1413. if (LDAP_CHANGETYPE_ADD == modop) {
  1414. slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
  1415. /* If the entry doesn't exist, just bail and
  1416. * let the server handle it. */
  1417. if (e == NULL) {
  1418. goto bail;
  1419. }
  1420. } else if (LDAP_CHANGETYPE_MODIFY == modop) {
  1421. /* Fetch the entry being modified so we can
  1422. * create the resulting entry for validation. */
  1423. if (sdn) {
  1424. slapi_search_internal_get_entry_ext(sdn, 0, &e, automember_get_plugin_id(), txn);
  1425. free_entry = 1;
  1426. }
  1427. /* If the entry doesn't exist, just bail and
  1428. * let the server handle it. */
  1429. if (e == NULL) {
  1430. goto bail;
  1431. }
  1432. /* Grab the mods. */
  1433. slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
  1434. smods = slapi_mods_new();
  1435. slapi_mods_init_byref(smods, mods);
  1436. /* Apply the mods to create the resulting entry. */
  1437. if (mods && (slapi_entry_apply_mods(e, mods) != LDAP_SUCCESS)) {
  1438. /* The mods don't apply cleanly, so we just let this op go
  1439. * to let the main server handle it. */
  1440. goto bailmod;
  1441. }
  1442. } else {
  1443. errstr = slapi_ch_smprintf("automember_pre_op: invalid op type %d",
  1444. modop);
  1445. ret = LDAP_PARAM_ERROR;
  1446. goto bail;
  1447. }
  1448. if (automember_parse_config_entry(e, 0, pb) != 0) {
  1449. /* Refuse the operation if config parsing failed. */
  1450. ret = LDAP_UNWILLING_TO_PERFORM;
  1451. if (LDAP_CHANGETYPE_ADD == modop) {
  1452. errstr = slapi_ch_smprintf("Not a valid auto membership configuration entry.");
  1453. } else {
  1454. errstr = slapi_ch_smprintf("Changes result in an invalid "
  1455. "auto membership configuration.");
  1456. }
  1457. }
  1458. }
  1459. bailmod:
  1460. /* Clean up smods. */
  1461. if (LDAP_CHANGETYPE_MODIFY == modop) {
  1462. slapi_mods_free(&smods);
  1463. }
  1464. bail:
  1465. if (free_entry && e)
  1466. slapi_entry_free(e);
  1467. if (ret) {
  1468. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1469. "automember_pre_op: operation failure [%d]\n", ret);
  1470. slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL);
  1471. slapi_ch_free((void **)&errstr);
  1472. ret = -1;
  1473. }
  1474. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1475. "<-- automember_pre_op\n");
  1476. return ret;
  1477. }
  1478. static int
  1479. automember_add_pre_op(Slapi_PBlock * pb)
  1480. {
  1481. return automember_pre_op(pb, LDAP_CHANGETYPE_ADD);
  1482. }
  1483. static int
  1484. automember_mod_pre_op(Slapi_PBlock * pb)
  1485. {
  1486. return automember_pre_op(pb, LDAP_CHANGETYPE_MODIFY);
  1487. }
  1488. /*
  1489. * automember_mod_post_op()
  1490. *
  1491. * Reloads the auto membership config
  1492. * if config changes were made.
  1493. */
  1494. static int
  1495. automember_mod_post_op(Slapi_PBlock *pb)
  1496. {
  1497. Slapi_DN *sdn = NULL;
  1498. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1499. "--> automember_mod_post_op\n");
  1500. /* Just bail if we aren't ready to service requests yet. */
  1501. if (!g_plugin_started) {
  1502. goto bail;
  1503. }
  1504. if (automember_oktodo(pb) && (sdn = automember_get_sdn(pb))) {
  1505. /* Check if the config is being modified and reload if so. */
  1506. if (automember_dn_is_config(sdn)) {
  1507. automember_load_config(pb);
  1508. }
  1509. }
  1510. bail:
  1511. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1512. "<-- automember_mod_post_op\n");
  1513. return 0;
  1514. }
  1515. static int
  1516. automember_add_post_op(Slapi_PBlock *pb)
  1517. {
  1518. Slapi_Entry *e = NULL;
  1519. Slapi_DN *sdn = NULL;
  1520. struct configEntry *config = NULL;
  1521. PRCList *list = NULL;
  1522. void *txn = NULL;
  1523. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1524. "--> automember_add_post_op\n");
  1525. /* Just bail if we aren't ready to service requests yet. */
  1526. if (!g_plugin_started || !automember_oktodo(pb))
  1527. return 0;
  1528. /* Reload config if a config entry was added. */
  1529. if ((sdn = automember_get_sdn(pb))) {
  1530. if (automember_dn_is_config(sdn)) {
  1531. automember_load_config(pb);
  1532. }
  1533. } else {
  1534. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1535. "automember_add_post_op: Error "
  1536. "retrieving dn\n");
  1537. goto bail;
  1538. }
  1539. /* If replication, just bail. */
  1540. if (automember_isrepl(pb)) {
  1541. return 0;
  1542. }
  1543. slapi_pblock_get(pb, SLAPI_TXN, &txn);
  1544. /* Get the newly added entry. */
  1545. slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
  1546. if (e) {
  1547. /* If the entry is a tombstone, just bail. */
  1548. Slapi_Value *tombstone =
  1549. slapi_value_new_string(SLAPI_ATTR_VALUE_TOMBSTONE);
  1550. int rc = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS,
  1551. tombstone);
  1552. slapi_value_free(&tombstone);
  1553. if (rc) {
  1554. return 0;
  1555. }
  1556. /* Check if a config entry applies
  1557. * to the entry being added. */
  1558. automember_config_read_lock();
  1559. /* Bail out if the plug-in close function was just called. */
  1560. if (!g_plugin_started) {
  1561. automember_config_unlock();
  1562. return 0;
  1563. }
  1564. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  1565. list = PR_LIST_HEAD(g_automember_config);
  1566. while (list != g_automember_config) {
  1567. config = (struct configEntry *)list;
  1568. /* Does the entry meet scope and filter requirements? */
  1569. if (slapi_dn_issuffix(slapi_sdn_get_dn(sdn), config->scope) &&
  1570. (slapi_filter_test_simple(e, config->filter) == 0)) {
  1571. /* Find out what membership changes are needed and make them. */
  1572. automember_update_membership(config, e, txn);
  1573. }
  1574. list = PR_NEXT_LINK(list);
  1575. }
  1576. }
  1577. automember_config_unlock();
  1578. } else {
  1579. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1580. "automember_add_post_op: Error "
  1581. "retrieving post-op entry %s\n", slapi_sdn_get_dn(sdn));
  1582. }
  1583. bail:
  1584. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1585. "<-- automember_add_post_op\n");
  1586. return 0;
  1587. }
  1588. /*
  1589. * automember_del_post_op()
  1590. *
  1591. * Removes deleted config.
  1592. */
  1593. static int
  1594. automember_del_post_op(Slapi_PBlock *pb)
  1595. {
  1596. Slapi_DN *sdn = NULL;
  1597. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1598. "--> automember_del_post_op\n");
  1599. /* Just bail if we aren't ready to service requests yet. */
  1600. if (!g_plugin_started || !automember_oktodo(pb)) {
  1601. return 0;
  1602. }
  1603. /* Reload config if a config entry was deleted. */
  1604. if ((sdn = automember_get_sdn(pb))) {
  1605. if (automember_dn_is_config(sdn))
  1606. automember_load_config(pb);
  1607. } else {
  1608. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1609. "automember_del_post_op: Error "
  1610. "retrieving dn\n");
  1611. }
  1612. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1613. "<-- automember_del_post_op\n");
  1614. return 0;
  1615. }
  1616. /*
  1617. * automember_modrdn_post_op()
  1618. *
  1619. * Reloads config if config entries move
  1620. * into or out of our config scope.
  1621. */
  1622. static int
  1623. automember_modrdn_post_op(Slapi_PBlock *pb)
  1624. {
  1625. Slapi_DN *old_sdn = NULL;
  1626. Slapi_DN *new_sdn = NULL;
  1627. Slapi_Entry *post_e = NULL;
  1628. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1629. "--> automember_modrdn_post_op\n");
  1630. /* Just bail if we aren't ready to service requests yet. */
  1631. if (!g_plugin_started || !automember_oktodo(pb))
  1632. return 0;
  1633. /* Reload config if an existing config entry was renamed,
  1634. * or if the new dn brings an entry into the scope of the
  1635. * config entries. */
  1636. slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e);
  1637. if (post_e) {
  1638. new_sdn = slapi_entry_get_sdn(post_e);
  1639. } else {
  1640. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1641. "automember_modrdn_post_op: Error "
  1642. "retrieving post-op entry\n");
  1643. return 0;
  1644. }
  1645. if ((old_sdn = automember_get_sdn(pb))) {
  1646. if (automember_dn_is_config(old_sdn) || automember_dn_is_config(new_sdn))
  1647. automember_load_config(pb);
  1648. } else {
  1649. slapi_log_error(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1650. "automember_modrdn_post_op: Error "
  1651. "retrieving dn\n");
  1652. }
  1653. slapi_log_error(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1654. "<-- automember_modrdn_post_op\n");
  1655. return 0;
  1656. }