automember.c 110 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2011 Red Hat, Inc.
  3. * All rights reserved.
  4. *
  5. * License: GPL (version 3 or any later version).
  6. * See LICENSE for details.
  7. * END COPYRIGHT BLOCK **/
  8. #ifdef HAVE_CONFIG_H
  9. #include <config.h>
  10. #endif
  11. /*
  12. * Auto Membership Plug-in
  13. */
  14. #include "automember.h"
  15. /*
  16. * Plug-in globals
  17. */
  18. static PRCList *g_automember_config = NULL;
  19. static Slapi_RWLock *g_automember_config_lock = NULL;
  20. static void *_PluginID = NULL;
  21. static Slapi_DN *_PluginDN = NULL;
  22. static Slapi_DN *_ConfigAreaDN = NULL;
  23. static Slapi_PluginDesc pdesc = {AUTOMEMBER_FEATURE_DESC,
  24. VENDOR,
  25. DS_PACKAGE_VERSION,
  26. AUTOMEMBER_PLUGIN_DESC};
  27. /*
  28. * Plug-in management functions
  29. */
  30. int automember_init(Slapi_PBlock *pb);
  31. static int automember_start(Slapi_PBlock *pb);
  32. static int automember_close(Slapi_PBlock *pb);
  33. static int automember_postop_init(Slapi_PBlock *pb);
  34. static int automember_internal_postop_init(Slapi_PBlock *pb);
  35. /*
  36. * Operation callbacks (where the real work is done)
  37. */
  38. static int automember_mod_post_op(Slapi_PBlock *pb);
  39. static int automember_add_post_op(Slapi_PBlock *pb);
  40. static int automember_del_post_op(Slapi_PBlock *pb);
  41. static int automember_modrdn_post_op(Slapi_PBlock *pb);
  42. static int automember_pre_op(Slapi_PBlock *pb, int modop);
  43. static int automember_mod_pre_op(Slapi_PBlock *pb);
  44. static int automember_add_pre_op(Slapi_PBlock *pb);
  45. /*
  46. * Config cache management functions
  47. */
  48. static int automember_load_config(void);
  49. static void automember_delete_config(void);
  50. static int automember_parse_config_entry(Slapi_Entry *e, int apply);
  51. static void automember_free_config_entry(struct configEntry **entry);
  52. /*
  53. * helpers
  54. */
  55. static Slapi_DN *automember_get_sdn(Slapi_PBlock *pb);
  56. static Slapi_DN *automember_get_config_area(void);
  57. static void automember_set_config_area(Slapi_DN *sdn);
  58. static int automember_dn_is_config(Slapi_DN *sdn);
  59. static int automember_oktodo(Slapi_PBlock *pb);
  60. static int automember_isrepl(Slapi_PBlock *pb);
  61. static void automember_parse_regex_entry(struct configEntry *config, Slapi_Entry *e);
  62. static struct automemberRegexRule *automember_parse_regex_rule(char *rule_string);
  63. static void automember_free_regex_rule(struct automemberRegexRule *rule);
  64. static int automember_parse_grouping_attr(char *value, char **grouping_attr, char **grouping_value);
  65. static int automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileDesc *ldif_fd);
  66. static int automember_update_member_value(Slapi_Entry *member_e, const char *group_dn, char *grouping_attr,
  67. char *grouping_value, PRFileDesc *ldif_fd, int add);
  68. /*
  69. * task functions
  70. */
  71. static int automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
  72. static int automember_task_add_export_updates(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
  73. static int automember_task_add_map_entries(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter, int *returncode, char *returntext, void *arg);
  74. void automember_rebuild_task_thread(void *arg);
  75. void automember_export_task_thread(void *arg);
  76. void automember_map_task_thread(void *arg);
  77. static void automember_task_destructor(Slapi_Task *task);
  78. static void automember_task_export_destructor(Slapi_Task *task);
  79. static void automember_task_map_destructor(Slapi_Task *task);
  80. #define DEFAULT_FILE_MODE PR_IRUSR | PR_IWUSR
  81. static uint64_t plugin_do_modify = 1;
  82. static uint64_t plugin_is_betxn = 0;
  83. /*
  84. * Config cache locking functions
  85. */
  86. void
  87. automember_config_read_lock()
  88. {
  89. slapi_rwlock_rdlock(g_automember_config_lock);
  90. }
  91. void
  92. automember_config_write_lock()
  93. {
  94. slapi_rwlock_wrlock(g_automember_config_lock);
  95. }
  96. void
  97. automember_config_unlock()
  98. {
  99. slapi_rwlock_unlock(g_automember_config_lock);
  100. }
  101. /*
  102. * Plugin identity functions
  103. */
  104. void
  105. automember_set_plugin_id(void *pluginID)
  106. {
  107. _PluginID = pluginID;
  108. }
  109. void *
  110. automember_get_plugin_id()
  111. {
  112. return _PluginID;
  113. }
  114. void
  115. automember_set_plugin_sdn(Slapi_DN *pluginDN)
  116. {
  117. _PluginDN = pluginDN;
  118. }
  119. Slapi_DN *
  120. automember_get_plugin_sdn(void)
  121. {
  122. return _PluginDN;
  123. }
  124. /*
  125. * Plug-in initialization functions
  126. */
  127. int
  128. automember_init(Slapi_PBlock *pb)
  129. {
  130. int status = 0;
  131. char *plugin_identity = NULL;
  132. Slapi_Entry *plugin_entry = NULL;
  133. char *plugin_type = NULL;
  134. int preadd = SLAPI_PLUGIN_PRE_ADD_FN;
  135. int premod = SLAPI_PLUGIN_PRE_MODIFY_FN;
  136. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  137. "--> automember_init\n");
  138. /* get args */
  139. if ((slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &plugin_entry) == 0) && plugin_entry &&
  140. (plugin_type = slapi_entry_attr_get_charptr(plugin_entry, "nsslapd-plugintype")) &&
  141. plugin_type && strstr(plugin_type, "betxn")) {
  142. plugin_is_betxn = 1;
  143. preadd = SLAPI_PLUGIN_BE_TXN_PRE_ADD_FN;
  144. premod = SLAPI_PLUGIN_BE_TXN_PRE_MODIFY_FN;
  145. }
  146. slapi_ch_free_string(&plugin_type);
  147. /* Store the plugin identity for later use.
  148. * Used for internal operations. */
  149. slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &plugin_identity);
  150. PR_ASSERT(plugin_identity);
  151. automember_set_plugin_id(plugin_identity);
  152. /* Register callbacks */
  153. if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
  154. SLAPI_PLUGIN_VERSION_01) != 0 ||
  155. slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
  156. (void *)automember_start) != 0 ||
  157. slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
  158. (void *)automember_close) != 0 ||
  159. slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
  160. (void *)&pdesc) != 0 ||
  161. slapi_pblock_set(pb, premod, (void *)automember_mod_pre_op) != 0 ||
  162. slapi_pblock_set(pb, preadd, (void *)automember_add_pre_op) != 0) {
  163. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  164. "automember_init - Failed to register plugin\n");
  165. status = -1;
  166. }
  167. if (!plugin_is_betxn && !status &&
  168. slapi_register_plugin("internalpostoperation", /* op type */
  169. 1, /* Enabled */
  170. "automember_init", /* this function desc */
  171. automember_internal_postop_init, /* init func */
  172. AUTOMEMBER_INT_POSTOP_DESC, /* plugin desc */
  173. NULL, /* ? */
  174. plugin_identity /* access control */
  175. )) {
  176. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  177. "automember_init - Failed to register internalpostoperation plugin\n");
  178. status = -1;
  179. }
  180. if (!status) {
  181. plugin_type = "postoperation";
  182. if (plugin_is_betxn) {
  183. plugin_type = "betxnpostoperation";
  184. }
  185. if (slapi_register_plugin(plugin_type, /* op type */
  186. 1, /* Enabled */
  187. "automember_init", /* this function desc */
  188. automember_postop_init, /* init func for post op */
  189. AUTOMEMBER_POSTOP_DESC, /* plugin desc */
  190. NULL, /* ? */
  191. plugin_identity /* access control */
  192. )) {
  193. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  194. "automember_init - Failed to register postop plugin\n");
  195. status = -1;
  196. }
  197. }
  198. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  199. "<-- automember_init\n");
  200. return status;
  201. }
  202. /* not used when using plugin as a betxn plugin - betxn plugins are called for both internal and external ops */
  203. static int
  204. automember_internal_postop_init(Slapi_PBlock *pb)
  205. {
  206. int status = 0;
  207. if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
  208. SLAPI_PLUGIN_VERSION_01) != 0 ||
  209. slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
  210. (void *)&pdesc) != 0 ||
  211. slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_ADD_FN,
  212. (void *)automember_add_post_op) != 0 ||
  213. slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_DELETE_FN,
  214. (void *)automember_del_post_op) != 0 ||
  215. slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODIFY_FN,
  216. (void *)automember_mod_post_op) != 0 ||
  217. slapi_pblock_set(pb, SLAPI_PLUGIN_INTERNAL_POST_MODRDN_FN,
  218. (void *)automember_modrdn_post_op) != 0) {
  219. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  220. "automember_internal_postop_init - Failed to register plugin\n");
  221. status = -1;
  222. }
  223. return status;
  224. }
  225. static int
  226. automember_postop_init(Slapi_PBlock *pb)
  227. {
  228. int status = 0;
  229. int addfn = SLAPI_PLUGIN_POST_ADD_FN;
  230. int delfn = SLAPI_PLUGIN_POST_DELETE_FN;
  231. int modfn = SLAPI_PLUGIN_POST_MODIFY_FN;
  232. int mdnfn = SLAPI_PLUGIN_POST_MODRDN_FN;
  233. if (plugin_is_betxn) {
  234. addfn = SLAPI_PLUGIN_BE_TXN_POST_ADD_FN;
  235. delfn = SLAPI_PLUGIN_BE_TXN_POST_DELETE_FN;
  236. modfn = SLAPI_PLUGIN_BE_TXN_POST_MODIFY_FN;
  237. mdnfn = SLAPI_PLUGIN_BE_TXN_POST_MODRDN_FN;
  238. }
  239. if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
  240. SLAPI_PLUGIN_VERSION_01) != 0 ||
  241. slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
  242. (void *)&pdesc) != 0 ||
  243. slapi_pblock_set(pb, addfn, (void *)automember_add_post_op) != 0 ||
  244. slapi_pblock_set(pb, delfn, (void *)automember_del_post_op) != 0 ||
  245. slapi_pblock_set(pb, modfn, (void *)automember_mod_post_op) != 0 ||
  246. slapi_pblock_set(pb, mdnfn, (void *)automember_modrdn_post_op) != 0) {
  247. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  248. "automember_postop_init - Failed to register plugin\n");
  249. status = -1;
  250. }
  251. return status;
  252. }
  253. /* Stash the config area in the pblock for start functions? */
  254. /*
  255. * automember_start()
  256. *
  257. * Creates config lock and loads config cache.
  258. */
  259. static int
  260. automember_start(Slapi_PBlock *pb)
  261. {
  262. Slapi_DN *plugindn = NULL;
  263. Slapi_Entry *plugin_entry = NULL;
  264. char *config_area = NULL;
  265. const char *do_modify = NULL;
  266. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  267. "--> automember_start\n");
  268. slapi_plugin_task_register_handler("automember rebuild membership", automember_task_add, pb);
  269. slapi_plugin_task_register_handler("automember export updates", automember_task_add_export_updates, pb);
  270. slapi_plugin_task_register_handler("automember map updates", automember_task_add_map_entries, pb);
  271. if ((g_automember_config_lock = slapi_new_rwlock()) == NULL) {
  272. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  273. "automember_start - Lock creation failed\n");
  274. return -1;
  275. }
  276. /*
  277. * Get the plug-in target dn from the system
  278. * and store it for future use. */
  279. slapi_pblock_get(pb, SLAPI_TARGET_SDN, &plugindn);
  280. if (NULL == plugindn || 0 == slapi_sdn_get_ndn_len(plugindn)) {
  281. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  282. "automember_start - Unable to retrieve plugin dn\n");
  283. return -1;
  284. }
  285. automember_set_plugin_sdn(slapi_sdn_dup(plugindn));
  286. /* Set the alternate config area if one is defined. */
  287. slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_AREA, &config_area);
  288. if (config_area) {
  289. automember_set_config_area(slapi_sdn_new_normdn_byval(config_area));
  290. }
  291. /*
  292. * Load the config cache
  293. */
  294. g_automember_config = (PRCList *)slapi_ch_calloc(1, sizeof(struct configEntry));
  295. PR_INIT_CLIST(g_automember_config);
  296. if (automember_load_config() != 0) {
  297. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  298. "automember_start - Unable to load plug-in configuration\n");
  299. return -1;
  300. }
  301. /* Check and set if we should process modify operations */
  302. plugin_do_modify = 1; /* default is "on" */
  303. if ((slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &plugin_entry) == 0) && plugin_entry){
  304. if ((do_modify = slapi_fetch_attr(plugin_entry, AUTOMEMBER_DO_MODIFY, NULL)) ) {
  305. if (strcasecmp(do_modify, "on") && strcasecmp(do_modify, "off")) {
  306. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  307. "automember_start - %s: invalid value \"%s\". Valid values are \"on\" or \"off\". Using default of \"on\"\n",
  308. AUTOMEMBER_DO_MODIFY, do_modify);
  309. } else if (strcasecmp(do_modify, "off") == 0 ){
  310. plugin_do_modify = 0;
  311. }
  312. }
  313. }
  314. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  315. "automember_start - ready for service\n");
  316. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  317. "<-- automember_start\n");
  318. return 0;
  319. }
  320. /*
  321. * automember_close()
  322. *
  323. * Cleans up the config cache.
  324. */
  325. static int
  326. automember_close(Slapi_PBlock *pb __attribute__((unused)))
  327. {
  328. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  329. "--> automember_close\n");
  330. /* unregister the tasks */
  331. slapi_plugin_task_unregister_handler("automember rebuild membership",
  332. automember_task_add);
  333. slapi_plugin_task_unregister_handler("automember export updates",
  334. automember_task_add_export_updates);
  335. slapi_plugin_task_unregister_handler("automember map updates",
  336. automember_task_add_map_entries);
  337. automember_delete_config();
  338. slapi_sdn_free(&_PluginDN);
  339. slapi_sdn_free(&_ConfigAreaDN);
  340. slapi_destroy_rwlock(g_automember_config_lock);
  341. g_automember_config_lock = NULL;
  342. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  343. "<-- automember_close\n");
  344. return 0;
  345. }
  346. /*
  347. * automember_get_config()
  348. *
  349. * Get the config list.
  350. */
  351. PRCList *
  352. automember_get_config()
  353. {
  354. return g_automember_config;
  355. }
  356. /*
  357. * automember_load_config()
  358. *
  359. * Parse and load the config entries.
  360. */
  361. static int
  362. automember_load_config(void)
  363. {
  364. int status = 0;
  365. int result;
  366. int i;
  367. Slapi_PBlock *search_pb;
  368. Slapi_Entry **entries = NULL;
  369. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  370. "--> automember_load_config\n");
  371. /* Clear out any old config. */
  372. automember_config_write_lock();
  373. automember_delete_config();
  374. g_automember_config = (PRCList *)slapi_ch_calloc(1, sizeof(struct configEntry));
  375. PR_INIT_CLIST(g_automember_config);
  376. search_pb = slapi_pblock_new();
  377. /* If an alternate config area is configured, find
  378. * the config entries that are beneath it, otherwise
  379. * we load the entries beneath our top-level plug-in
  380. * config entry. */
  381. if (automember_get_config_area()) {
  382. /* Find the config entries beneath the alternate config area. */
  383. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  384. "automember_load_config - Looking for config entries "
  385. "beneath \"%s\".\n",
  386. slapi_sdn_get_ndn(automember_get_config_area()));
  387. slapi_search_internal_set_pb(search_pb, slapi_sdn_get_ndn(automember_get_config_area()),
  388. LDAP_SCOPE_SUBTREE, AUTOMEMBER_DEFINITION_FILTER,
  389. NULL, 0, NULL, NULL, automember_get_plugin_id(), 0);
  390. } else {
  391. /* Find the config entries beneath our plugin entry. */
  392. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  393. "automember_load_config - Looking for config entries "
  394. "beneath \"%s\".\n",
  395. slapi_sdn_get_ndn(automember_get_plugin_sdn()));
  396. slapi_search_internal_set_pb(search_pb, slapi_sdn_get_ndn(automember_get_plugin_sdn()),
  397. LDAP_SCOPE_SUBTREE, AUTOMEMBER_DEFINITION_FILTER,
  398. NULL, 0, NULL, NULL, automember_get_plugin_id(), 0);
  399. }
  400. slapi_search_internal_pb(search_pb);
  401. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
  402. if (LDAP_SUCCESS != result) {
  403. if (automember_get_config_area() && (result == LDAP_NO_SUCH_OBJECT)) {
  404. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  405. "automember_load_config - Config container \"%s\" does "
  406. "not exist.\n",
  407. slapi_sdn_get_ndn(automember_get_config_area()));
  408. goto cleanup;
  409. } else {
  410. status = -1;
  411. goto cleanup;
  412. }
  413. }
  414. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
  415. &entries);
  416. /* Loop through all of the entries we found and parse them. */
  417. for (i = 0; entries && (entries[i] != NULL); i++) {
  418. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  419. "automember_load_config - Parsing config entry "
  420. "\"%s\".\n",
  421. slapi_entry_get_dn(entries[i]));
  422. /* We don't care about the status here because we may have
  423. * some invalid config entries, but we just want to continue
  424. * looking for valid ones. */
  425. automember_parse_config_entry(entries[i], 1);
  426. }
  427. cleanup:
  428. slapi_free_search_results_internal(search_pb);
  429. slapi_pblock_destroy(search_pb);
  430. automember_config_unlock();
  431. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  432. "<-- automember_load_config\n");
  433. return status;
  434. }
  435. /*
  436. * automember_parse_config_entry()
  437. *
  438. * Parses a single config entry. If apply is non-zero, then
  439. * we will load and start using the new config. You can simply
  440. * validate config without making any changes by setting apply
  441. * to 0.
  442. *
  443. * Returns 0 if the entry is valid and -1 if it is invalid.
  444. */
  445. static int
  446. automember_parse_config_entry(Slapi_Entry *e, int apply)
  447. {
  448. char *value = NULL;
  449. char **values = NULL;
  450. struct configEntry *entry = NULL;
  451. struct configEntry *config_entry;
  452. PRCList *list;
  453. Slapi_PBlock *search_pb = NULL;
  454. Slapi_Entry **rule_entries = NULL;
  455. char *filter_str = NULL;
  456. Slapi_DN *dn = NULL;
  457. Slapi_Filter *filter = NULL;
  458. int result;
  459. int entry_added = 0;
  460. int i = 0;
  461. int ret = 0;
  462. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  463. "--> automember_parse_config_entry\n");
  464. /* If this is the main plug-in config entry or
  465. * the config area container, just bail. */
  466. if ((slapi_sdn_compare(automember_get_plugin_sdn(), slapi_entry_get_sdn(e)) == 0) ||
  467. (automember_get_config_area() && (slapi_sdn_compare(automember_get_config_area(),
  468. slapi_entry_get_sdn(e)) == 0))) {
  469. goto bail;
  470. }
  471. /* If this entry is not an automember config definition entry, just bail. */
  472. filter_str = slapi_ch_strdup(AUTOMEMBER_DEFINITION_FILTER);
  473. filter = slapi_str2filter(filter_str);
  474. if (slapi_filter_test_simple(e, filter) != 0) {
  475. goto bail;
  476. }
  477. /* If marked as disabled, just bail. */
  478. value = slapi_entry_attr_get_charptr(e, AUTOMEMBER_DISABLED_TYPE);
  479. if (value) {
  480. slapi_ch_free_string(&value);
  481. goto bail;
  482. }
  483. entry = (struct configEntry *)slapi_ch_calloc(1, sizeof(struct configEntry));
  484. if (NULL == entry) {
  485. ret = -1;
  486. goto bail;
  487. }
  488. value = slapi_entry_get_ndn(e);
  489. if (value) {
  490. entry->dn = slapi_ch_strdup(value);
  491. } else {
  492. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  493. "automember_parse_config_entry - Error "
  494. "reading dn from config entry\n");
  495. ret = -1;
  496. goto bail;
  497. }
  498. slapi_log_err(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  499. "----------> dn [%s]\n", entry->dn);
  500. /* Load the scope */
  501. value = slapi_entry_attr_get_charptr(e, AUTOMEMBER_SCOPE_TYPE);
  502. if (value) {
  503. entry->scope = value;
  504. } else {
  505. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  506. "automember_parse_config_entry - The %s config "
  507. "setting is required for config entry \"%s\".\n",
  508. AUTOMEMBER_SCOPE_TYPE, entry->dn);
  509. ret = -1;
  510. goto bail;
  511. }
  512. /* Load the filter */
  513. value = slapi_entry_attr_get_charptr(e, AUTOMEMBER_FILTER_TYPE);
  514. if (value) {
  515. /* Convert to a Slapi_Filter to improve performance. */
  516. if (NULL == (entry->filter = slapi_str2filter(value))) {
  517. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  518. "automember_parse_config_entry - Invalid search filter in "
  519. "%s config setting for config entry \"%s\" "
  520. "(filter = \"%s\").\n",
  521. AUTOMEMBER_FILTER_TYPE, entry->dn, value);
  522. ret = -1;
  523. }
  524. slapi_ch_free_string(&value);
  525. if (ret != 0) {
  526. goto bail;
  527. }
  528. } else {
  529. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  530. "automember_parse_config_entry - The %s config "
  531. "setting is required for config entry \"%s\".\n",
  532. AUTOMEMBER_FILTER_TYPE, entry->dn);
  533. ret = -1;
  534. goto bail;
  535. }
  536. /* Load the default groups */
  537. values = slapi_entry_attr_get_charray(e, AUTOMEMBER_DEFAULT_GROUP_TYPE);
  538. if (values) {
  539. /* Just hand off the values */
  540. entry->default_groups = values;
  541. /*
  542. * If we set the config area, we need to make sure that the default groups are not
  543. * in the config area, or else we could deadlock on updates.
  544. */
  545. if (automember_get_config_area()) {
  546. for (i = 0; values && values[i]; i++) {
  547. dn = slapi_sdn_new_dn_byref(values[i]);
  548. if (slapi_sdn_issuffix(dn, automember_get_config_area())) {
  549. /* The groups are under the config area - not good */
  550. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  551. "automember_parse_config_entry - The default group \"%s\" can not be "
  552. "a child of the plugin config area \"%s\".\n",
  553. values[i], slapi_sdn_get_dn(automember_get_config_area()));
  554. slapi_sdn_free(&dn);
  555. ret = -1;
  556. goto bail;
  557. }
  558. slapi_sdn_free(&dn);
  559. }
  560. }
  561. values = NULL;
  562. }
  563. /* Load the grouping attr */
  564. value = slapi_entry_attr_get_charptr(e, AUTOMEMBER_GROUPING_ATTR_TYPE);
  565. if (value) {
  566. if (automember_parse_grouping_attr(value, &(entry->grouping_attr),
  567. &(entry->grouping_value)) != 0) {
  568. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  569. "automember_parse_config_entry - Invalid "
  570. "%s config setting for config entry \"%s\" "
  571. "(value: \"%s\").\n",
  572. AUTOMEMBER_GROUPING_ATTR_TYPE,
  573. entry->dn, value);
  574. ret = -1;
  575. }
  576. slapi_ch_free_string(&value);
  577. if (ret != 0) {
  578. goto bail;
  579. }
  580. } else {
  581. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  582. "automember_parse_config_entry - The %s config "
  583. "setting is required for config entry \"%s\".\n",
  584. AUTOMEMBER_GROUPING_ATTR_TYPE, entry->dn);
  585. ret = -1;
  586. goto bail;
  587. }
  588. /* Find all child regex rule entries */
  589. search_pb = slapi_pblock_new();
  590. slapi_search_internal_set_pb(search_pb, entry->dn, LDAP_SCOPE_SUBTREE,
  591. AUTOMEMBER_REGEX_RULE_FILTER, NULL, 0, NULL,
  592. NULL, automember_get_plugin_id(), 0);
  593. slapi_search_internal_pb(search_pb);
  594. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
  595. /* If this is a new config entry being added, it won't exist yet
  596. * when we are simply validating config. We can just ignore no
  597. * such object errors. */
  598. if ((LDAP_SUCCESS != result) && (LDAP_NO_SUCH_OBJECT != result)) {
  599. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  600. "automember_parse_config_entry - Error searching "
  601. "for child rule entries for config \"%s\" (err=%d).",
  602. entry->dn, result);
  603. ret = -1;
  604. goto bail;
  605. }
  606. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
  607. &rule_entries);
  608. /* Go through each child rule entry and parse it. */
  609. for (i = 0; rule_entries && (rule_entries[i] != NULL); i++) {
  610. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  611. "automember_parse_config_entry - Parsing regex rule entry "
  612. "\"%s\".\n",
  613. slapi_entry_get_dn(rule_entries[i]));
  614. automember_parse_regex_entry(entry, rule_entries[i]);
  615. }
  616. /* If we were only called to validate config, we can
  617. * just bail out before applying the config changes */
  618. if (apply == 0) {
  619. goto bail;
  620. }
  621. /* Add the config object to the list. We order by scope. */
  622. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  623. list = PR_LIST_HEAD(g_automember_config);
  624. while (list != g_automember_config) {
  625. config_entry = (struct configEntry *)list;
  626. /* If the config entry we are adding has a scope that is
  627. * a child of the scope of the current list item, we insert
  628. * the entry before that list item. */
  629. if (slapi_dn_issuffix(entry->scope, config_entry->scope)) {
  630. PR_INSERT_BEFORE(&(entry->list), list);
  631. slapi_log_err(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  632. "automember_parse_config_entry - store [%s] before [%s] \n", entry->dn,
  633. config_entry->dn);
  634. entry_added = 1;
  635. break;
  636. }
  637. list = PR_NEXT_LINK(list);
  638. /* If we hit the end of the list, add to the tail. */
  639. if (g_automember_config == list) {
  640. PR_INSERT_BEFORE(&(entry->list), list);
  641. slapi_log_err(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  642. "automember_parse_config_entry - store [%s] at tail\n", entry->dn);
  643. entry_added = 1;
  644. break;
  645. }
  646. }
  647. } else {
  648. /* first entry */
  649. PR_INSERT_LINK(&(entry->list), g_automember_config);
  650. slapi_log_err(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  651. "automember_parse_config_entry - store [%s] at head \n", entry->dn);
  652. entry_added = 1;
  653. }
  654. bail:
  655. if (0 == entry_added) {
  656. /* Don't log error if we weren't asked to apply config */
  657. if ((apply != 0) && (entry != NULL)) {
  658. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  659. "automember_parse_config_entry - Invalid config entry "
  660. "[%s] skipped\n",
  661. entry->dn);
  662. }
  663. automember_free_config_entry(&entry);
  664. } else {
  665. ret = 0;
  666. }
  667. slapi_ch_free_string(&filter_str);
  668. slapi_filter_free(filter, 1);
  669. slapi_free_search_results_internal(search_pb);
  670. slapi_pblock_destroy(search_pb);
  671. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  672. "<-- automember_parse_config_entry\n");
  673. return ret;
  674. }
  675. static void
  676. automember_free_config_entry(struct configEntry **entry)
  677. {
  678. struct configEntry *e = *entry;
  679. if (e == NULL)
  680. return;
  681. if (e->dn) {
  682. slapi_log_err(SLAPI_LOG_CONFIG, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  683. "automember_free_config_entry - Freeing config entry [%s]\n", e->dn);
  684. slapi_ch_free_string(&e->dn);
  685. }
  686. if (e->scope) {
  687. slapi_ch_free_string(&e->scope);
  688. }
  689. if (e->filter) {
  690. slapi_filter_free(e->filter, 1);
  691. }
  692. if (e->exclusive_rules) {
  693. PRCList *list;
  694. /* Clear out the list contents. */
  695. while (!PR_CLIST_IS_EMPTY((PRCList *)e->exclusive_rules)) {
  696. list = PR_LIST_HEAD((PRCList *)e->exclusive_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->exclusive_rules));
  702. }
  703. /* Clear out the list contents. */
  704. if (e->inclusive_rules) {
  705. PRCList *list;
  706. while (!PR_CLIST_IS_EMPTY((PRCList *)e->inclusive_rules)) {
  707. list = PR_LIST_HEAD((PRCList *)e->inclusive_rules);
  708. PR_REMOVE_LINK(list);
  709. automember_free_regex_rule((struct automemberRegexRule *)list);
  710. }
  711. /* Free the list itself. */
  712. slapi_ch_free((void **)&(e->inclusive_rules));
  713. }
  714. if (e->default_groups) {
  715. slapi_ch_array_free(e->default_groups);
  716. }
  717. if (e->grouping_attr) {
  718. slapi_ch_free_string(&e->grouping_attr);
  719. }
  720. if (e->grouping_value) {
  721. slapi_ch_free_string(&e->grouping_value);
  722. }
  723. slapi_ch_free((void **)entry);
  724. }
  725. static void
  726. automember_delete_configEntry(PRCList *entry)
  727. {
  728. PR_REMOVE_LINK(entry);
  729. automember_free_config_entry((struct configEntry **)&entry);
  730. }
  731. static void
  732. automember_delete_config(void)
  733. {
  734. PRCList *list;
  735. /* Delete the config cache. */
  736. while (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  737. list = PR_LIST_HEAD(g_automember_config);
  738. automember_delete_configEntry(list);
  739. }
  740. slapi_ch_free((void **)&g_automember_config);
  741. return;
  742. }
  743. static Slapi_DN *
  744. automember_get_sdn(Slapi_PBlock *pb)
  745. {
  746. Slapi_DN *sdn = 0;
  747. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  748. "--> automember_get_sdn\n");
  749. slapi_pblock_get(pb, SLAPI_TARGET_SDN, &sdn);
  750. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  751. "<-- automember_get_sdn\n");
  752. return sdn;
  753. }
  754. void
  755. automember_set_config_area(Slapi_DN *sdn)
  756. {
  757. _ConfigAreaDN = sdn;
  758. }
  759. Slapi_DN *
  760. automember_get_config_area(void)
  761. {
  762. return _ConfigAreaDN;
  763. }
  764. /*
  765. * automember_dn_is_config()
  766. *
  767. * Checks if dn is an auto membership config entry.
  768. */
  769. static int
  770. automember_dn_is_config(Slapi_DN *sdn)
  771. {
  772. int ret = 0;
  773. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  774. "--> automember_dn_is_config\n");
  775. if (sdn == NULL) {
  776. goto bail;
  777. }
  778. /* If an alternate config area is configured, treat it's child
  779. * entries as config entries. If the alternate config area is
  780. * not configured, treat children of the top-level plug-in
  781. * config entry as our config entries. */
  782. if (automember_get_config_area()) {
  783. if (slapi_sdn_issuffix(sdn, automember_get_config_area()) &&
  784. slapi_sdn_compare(sdn, automember_get_config_area())) {
  785. ret = 1;
  786. }
  787. } else {
  788. if (slapi_sdn_issuffix(sdn, automember_get_plugin_sdn()) &&
  789. slapi_sdn_compare(sdn, automember_get_plugin_sdn())) {
  790. ret = 1;
  791. }
  792. }
  793. bail:
  794. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  795. "<-- automember_dn_is_config\n");
  796. return ret;
  797. }
  798. /*
  799. * automember_oktodo()
  800. *
  801. * Check if we want to process this operation. We need to be
  802. * sure that the operation succeeded.
  803. */
  804. static int
  805. automember_oktodo(Slapi_PBlock *pb)
  806. {
  807. int ret = 1;
  808. int oprc = 0;
  809. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  810. "--> automember_oktodo\n");
  811. if (slapi_pblock_get(pb, SLAPI_PLUGIN_OPRETURN, &oprc) != 0) {
  812. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  813. "automember_oktodo - Could not get parameters\n");
  814. ret = -1;
  815. }
  816. /* This plugin should only execute if the operation succeeded. */
  817. if (oprc != 0) {
  818. ret = 0;
  819. }
  820. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  821. "<-- automember_oktodo\n");
  822. return ret;
  823. }
  824. /*
  825. * automember_isrepl()
  826. *
  827. * Returns 1 if the operation associated with pb
  828. * is a replicated op. Returns 0 otherwise.
  829. */
  830. static int
  831. automember_isrepl(Slapi_PBlock *pb)
  832. {
  833. int is_repl = 0;
  834. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  835. "--> automember_isrepl\n");
  836. slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl);
  837. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  838. "<-- automember_isrepl\n");
  839. return is_repl;
  840. }
  841. /*
  842. * automember_parse_regex_entry()
  843. *
  844. * Parses a rule entry and adds the regex rules to the
  845. * passed in config struct. Invalid regex rules will
  846. * be skipped and logged at the fatal log level.
  847. */
  848. static void
  849. automember_parse_regex_entry(struct configEntry *config, Slapi_Entry *e)
  850. {
  851. char *target_group = NULL;
  852. char **values = NULL;
  853. Slapi_DN *group_dn = NULL;
  854. PRCList *list;
  855. int i = 0;
  856. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  857. "--> automember_parse_regex_entry\n");
  858. /* Make sure the target group was specified. */
  859. target_group = slapi_entry_attr_get_charptr(e, AUTOMEMBER_TARGET_GROUP_TYPE);
  860. if (!target_group) {
  861. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  862. "automember_parse_regex_entry - The %s config "
  863. "setting is required for rule entry \"%s\".\n",
  864. AUTOMEMBER_TARGET_GROUP_TYPE, slapi_entry_get_ndn(e));
  865. goto bail;
  866. }
  867. /* Ensure that the target group DN is valid. */
  868. if (slapi_dn_syntax_check(NULL, target_group, 1) != 0) {
  869. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  870. "automember_parse_regex_entry - Invalid target group DN "
  871. "in rule \"%s\" (dn=\"%s\").\n",
  872. slapi_entry_get_ndn(e),
  873. target_group);
  874. goto bail;
  875. }
  876. /* normalize the group dn and compare it to the configArea DN */
  877. if (automember_get_config_area()) {
  878. group_dn = slapi_sdn_new_dn_byref(target_group);
  879. if (slapi_sdn_issuffix(group_dn, automember_get_config_area())) {
  880. /* The target group is under the plugin config area - not good */
  881. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  882. "automember_parse_regex_entry - The target group \"%s\" can not be "
  883. "a child of the plugin config area \"%s\".\n",
  884. slapi_sdn_get_dn(group_dn), slapi_sdn_get_dn(automember_get_config_area()));
  885. slapi_sdn_free(&group_dn);
  886. goto bail;
  887. }
  888. slapi_sdn_free(&group_dn);
  889. }
  890. /* Load inclusive rules */
  891. values = slapi_entry_attr_get_charray(e, AUTOMEMBER_INC_REGEX_TYPE);
  892. if (values) {
  893. struct automemberRegexRule *rule = NULL;
  894. /* If we haven't loaded any inclusive rules for
  895. * this config definition yet, create a new list. */
  896. if (config->inclusive_rules == NULL) {
  897. /* Create a list to hold our regex rules */
  898. config->inclusive_rules = (struct automemberRegexRule *)slapi_ch_calloc(1, sizeof(struct automemberRegexRule));
  899. PR_INIT_CLIST((PRCList *)config->inclusive_rules);
  900. }
  901. for (i = 0; values && values[i]; ++i) {
  902. rule = automember_parse_regex_rule(values[i]);
  903. if (rule) {
  904. /* Fill in the target group. */
  905. rule->target_group_dn = slapi_sdn_new_normdn_byval(target_group);
  906. if (!PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
  907. list = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
  908. while (list != (PRCList *)config->inclusive_rules) {
  909. struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)list;
  910. /* Order rules by target group DN */
  911. if (slapi_sdn_compare(rule->target_group_dn, curr_rule->target_group_dn) < 0) {
  912. PR_INSERT_BEFORE(&(rule->list), list);
  913. rule = NULL;
  914. break;
  915. }
  916. list = PR_NEXT_LINK(list);
  917. /* If we hit the end of the list, add to the tail. */
  918. if ((PRCList *)config->inclusive_rules == list) {
  919. PR_INSERT_BEFORE(&(rule->list), list);
  920. rule = NULL;
  921. break;
  922. }
  923. }
  924. automember_free_regex_rule(rule);
  925. } else {
  926. /* Add to head of list */
  927. PR_INSERT_LINK(&(rule->list), (PRCList *)config->inclusive_rules);
  928. }
  929. } else {
  930. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  931. "automember_parse_regex_entry - Skipping invalid inclusive "
  932. "regex rule in rule entry \"%s\" (rule = \"%s\").\n",
  933. slapi_entry_get_ndn(e), values[i]);
  934. }
  935. }
  936. slapi_ch_array_free(values);
  937. values = NULL;
  938. }
  939. /* Load exclusive rules. */
  940. values = slapi_entry_attr_get_charray(e, AUTOMEMBER_EXC_REGEX_TYPE);
  941. if (values) {
  942. struct automemberRegexRule *rule = NULL;
  943. /* If we haven't loaded any exclusive rules for
  944. * this config definition yet, create a new list. */
  945. if (config->exclusive_rules == NULL) {
  946. /* Create a list to hold our regex rules */
  947. config->exclusive_rules = (struct automemberRegexRule *)slapi_ch_calloc(1, sizeof(struct automemberRegexRule));
  948. PR_INIT_CLIST((PRCList *)config->exclusive_rules);
  949. }
  950. for (i = 0; values && values[i]; ++i) {
  951. rule = automember_parse_regex_rule(values[i]);
  952. if (rule) {
  953. /* Fill in the target group. */
  954. rule->target_group_dn = slapi_sdn_new_normdn_byval(target_group);
  955. if (!PR_CLIST_IS_EMPTY((PRCList *)config->exclusive_rules)) {
  956. list = PR_LIST_HEAD((PRCList *)config->exclusive_rules);
  957. while (list != (PRCList *)config->exclusive_rules) {
  958. struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)list;
  959. /* Order rules by target group DN */
  960. if (slapi_sdn_compare(rule->target_group_dn, curr_rule->target_group_dn) < 0) {
  961. PR_INSERT_BEFORE(&(rule->list), list);
  962. rule = NULL;
  963. break;
  964. }
  965. list = PR_NEXT_LINK(list);
  966. /* If we hit the end of the list, add to the tail. */
  967. if ((PRCList *)config->exclusive_rules == list) {
  968. PR_INSERT_BEFORE(&(rule->list), list);
  969. rule = NULL;
  970. break;
  971. }
  972. }
  973. automember_free_regex_rule(rule);
  974. } else {
  975. /* Add to head of list */
  976. PR_INSERT_LINK(&(rule->list), (PRCList *)config->exclusive_rules);
  977. }
  978. } else {
  979. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  980. "automember_parse_regex_entry - Skipping invalid exclusive "
  981. "regex rule in rule entry \"%s\" (rule = \"%s\").\n",
  982. slapi_entry_get_ndn(e), values[i]);
  983. }
  984. }
  985. slapi_ch_array_free(values);
  986. values = NULL;
  987. }
  988. bail:
  989. slapi_ch_free_string(&target_group);
  990. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  991. "<-- automember_parse_regex_entry\n");
  992. }
  993. /*
  994. * automember_parse_regex_rule()
  995. *
  996. * Parses a regex rule and returns a regex rule struct. The caller
  997. * will need to free this struct when it is finished with it. If
  998. * there is a problem parsing the regex rule, an error will be
  999. * logged and NULL will be returned.
  1000. */
  1001. static struct automemberRegexRule *
  1002. automember_parse_regex_rule(char *rule_string)
  1003. {
  1004. struct automemberRegexRule *rule = NULL;
  1005. char *attr = NULL;
  1006. Slapi_Regex *regex = NULL;
  1007. const char *recomp_result = NULL;
  1008. char *p = NULL;
  1009. char *p2 = NULL;
  1010. /* A rule is in the form "attr=regex". */
  1011. /* Find the comparison attribute name. */
  1012. if ((p = strchr(rule_string, '=')) == NULL) {
  1013. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1014. "automember_parse_regex_rule - Unable to parse "
  1015. "regex rule (missing '=' delimeter).\n");
  1016. goto bail;
  1017. }
  1018. /* Make sure the attribute name is not empty. */
  1019. if (p == rule_string) {
  1020. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1021. "automember_parse_regex_rule - Unable to parse "
  1022. " regex rule (missing comparison attribute).\n");
  1023. goto bail;
  1024. }
  1025. if ((attr = strndup(rule_string, p - rule_string)) == NULL) {
  1026. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1027. "automember_parse_regex_rule - Unable to allocate "
  1028. "memory.\n");
  1029. goto bail;
  1030. }
  1031. /* Validate the attribute. */
  1032. for (p2 = attr; p2 && (*p2 != '\0'); p2++) {
  1033. if (!IS_ATTRDESC_CHAR(*p2)) {
  1034. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1035. "automember_parse_regex_rule - Invalid comparison "
  1036. "attribute name \"%s\".\n",
  1037. attr);
  1038. goto bail;
  1039. }
  1040. }
  1041. /* Find the regex. */
  1042. p++;
  1043. if (*p == '\0') {
  1044. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1045. "automember_parse_regex_rule - Unable to parse "
  1046. "regex rule (missing regex).\n");
  1047. goto bail;
  1048. }
  1049. /* Compile the regex to validate it. */
  1050. regex = slapi_re_comp(p, &recomp_result);
  1051. if (!regex) {
  1052. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1053. "automember_parse_regex_rule - Unable to parse "
  1054. "regex rule (invalid regex). Error \"%s\".\n",
  1055. recomp_result ? recomp_result : "unknown");
  1056. }
  1057. /* Validation has passed, so create the regex rule struct and fill it in.
  1058. * We hand off everything we have allocated. All of this will be free'd
  1059. * when the rule struct itself is freed. */
  1060. rule = (struct automemberRegexRule *)slapi_ch_calloc(1, sizeof(struct automemberRegexRule));
  1061. rule->attr = attr;
  1062. rule->regex_str = slapi_ch_strdup(p);
  1063. rule->regex = regex;
  1064. bail:
  1065. /* Cleanup if we didn't successfully create a rule. */
  1066. if (!rule) {
  1067. slapi_ch_free_string(&attr);
  1068. slapi_re_free(regex);
  1069. }
  1070. return rule;
  1071. }
  1072. /*
  1073. * automember_free_regex_rule()
  1074. *
  1075. * Frees a regex rule and all of it's contents from memory.
  1076. */
  1077. static void
  1078. automember_free_regex_rule(struct automemberRegexRule *rule)
  1079. {
  1080. if (rule) {
  1081. if (rule->target_group_dn) {
  1082. slapi_sdn_free(&(rule->target_group_dn));
  1083. }
  1084. if (rule->attr) {
  1085. slapi_ch_free_string(&(rule->attr));
  1086. }
  1087. if (rule->regex_str) {
  1088. slapi_ch_free_string(&(rule->regex_str));
  1089. }
  1090. if (rule->regex) {
  1091. slapi_re_free(rule->regex);
  1092. }
  1093. }
  1094. slapi_ch_free((void **)&rule);
  1095. }
  1096. /*
  1097. * automember_parse_grouping_attr()
  1098. *
  1099. * Parses a grouping attribute and grouping value from
  1100. * the passed in config string. Memory will be allocated
  1101. * for grouping_attr and grouping_value, so it is up to
  1102. * the called to free them when they are no longer needed.
  1103. * Returns 0 upon success and 1 upon failure.
  1104. */
  1105. static int
  1106. automember_parse_grouping_attr(char *value, char **grouping_attr, char **grouping_value)
  1107. {
  1108. int ret = 0;
  1109. char *p = NULL;
  1110. /* Clear out any existing type or value. */
  1111. slapi_ch_free_string(grouping_attr);
  1112. slapi_ch_free_string(grouping_value);
  1113. /* split out the type from the value (use the first ':') */
  1114. if ((p = strchr(value, ':')) == NULL) {
  1115. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1116. "automember_parse_grouping_attr - Value for grouping attribute "
  1117. "is not in the correct format. (value: \"%s\").\n",
  1118. value);
  1119. ret = 1;
  1120. goto bail;
  1121. }
  1122. /* Ensure the type is not empty. */
  1123. if (p == value) {
  1124. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1125. "automember_parse_grouping_attr - Value for grouping attribute "
  1126. "is not in the correct format. The grouping attribute is missing. "
  1127. "(value: \"%s\").\n",
  1128. value);
  1129. ret = 1;
  1130. goto bail;
  1131. }
  1132. /* Duplicate the type to be returned. */
  1133. *grouping_attr = strndup(value, p - value);
  1134. /* Advance p to point to the beginning of the value. */
  1135. p++;
  1136. while (*p == ' ') {
  1137. p++;
  1138. }
  1139. /* Ensure the value is not empty. */
  1140. if (*p == '\0') {
  1141. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1142. "automember_parse_grouping_attr - Value for grouping attribute "
  1143. "is not in the correct format. The grouping value is "
  1144. "missing. (value: \"%s\").\n",
  1145. value);
  1146. ret = 1;
  1147. goto bail;
  1148. }
  1149. /* Duplicate the value to be returned. */
  1150. *grouping_value = slapi_ch_strdup(p);
  1151. /* Ensure that memory was allocated successfully. */
  1152. if ((*grouping_attr == NULL) || (*grouping_value == NULL)) {
  1153. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1154. "automember_parse_grouping_attr - Error allocating memory.\n");
  1155. ret = 1;
  1156. goto bail;
  1157. }
  1158. /* Ensure that the grouping attr is a legal attr name. */
  1159. for (p = *grouping_attr; p && (*p != '\0'); p++) {
  1160. if (!IS_ATTRDESC_CHAR(*p)) {
  1161. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1162. "automember_parse_grouping_attr: Invalid value for "
  1163. "grouping attribute. The grouping attribute type is "
  1164. "illegal. (type: \"%s\").\n",
  1165. *grouping_attr);
  1166. ret = 1;
  1167. goto bail;
  1168. }
  1169. }
  1170. /* Ensure that the grouping value type is a legal attr name. */
  1171. for (p = *grouping_value; p && (*p != '\0'); p++) {
  1172. if (!IS_ATTRDESC_CHAR(*p)) {
  1173. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1174. "automember_parse_grouping_attr - Invalid value for "
  1175. "grouping attribute. The grouping value type is "
  1176. "illegal. (type: \"%s\").\n",
  1177. *grouping_value);
  1178. ret = 1;
  1179. goto bail;
  1180. }
  1181. }
  1182. bail:
  1183. if (ret != 0) {
  1184. slapi_ch_free_string(grouping_attr);
  1185. slapi_ch_free_string(grouping_value);
  1186. }
  1187. return ret;
  1188. }
  1189. /*
  1190. * Free the exclusion and inclusion group dn's created by
  1191. * automember_get_membership_lists()
  1192. */
  1193. static void
  1194. automember_free_membership_lists(PRCList *exclusions, PRCList *targets)
  1195. {
  1196. struct automemberDNListItem *dnitem = NULL;
  1197. /*
  1198. * Free the exclusions and targets lists. Remember that the DN's
  1199. * are not ours, so don't free them!
  1200. */
  1201. while (!PR_CLIST_IS_EMPTY(exclusions)) {
  1202. dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(exclusions);
  1203. PR_REMOVE_LINK((PRCList *)dnitem);
  1204. slapi_ch_free((void **)&dnitem);
  1205. }
  1206. while (!PR_CLIST_IS_EMPTY(targets)) {
  1207. dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(targets);
  1208. PR_REMOVE_LINK((PRCList *)dnitem);
  1209. slapi_ch_free((void **)&dnitem);
  1210. }
  1211. }
  1212. /*
  1213. * Populate the exclusion and inclusion(target) PRCLists based on the
  1214. * slapi entry and configEntry provided. The PRCLists should be freed
  1215. * using automember_free_membership_lists()
  1216. */
  1217. static void
  1218. automember_get_membership_lists(struct configEntry *config, PRCList *exclusions, PRCList *targets, Slapi_Entry *e)
  1219. {
  1220. PRCList *rule = NULL;
  1221. struct automemberRegexRule *curr_rule = NULL;
  1222. struct automemberDNListItem *dnitem = NULL;
  1223. Slapi_DN *last = NULL;
  1224. PRCList *curr_exclusion = NULL;
  1225. char **vals = NULL;
  1226. int i = 0;
  1227. PR_INIT_CLIST(exclusions);
  1228. PR_INIT_CLIST(targets);
  1229. /* Go through exclusive rules and build an exclusion list. */
  1230. if (config->exclusive_rules) {
  1231. if (!PR_CLIST_IS_EMPTY((PRCList *)config->exclusive_rules)) {
  1232. rule = PR_LIST_HEAD((PRCList *)config->exclusive_rules);
  1233. while (rule != (PRCList *)config->exclusive_rules) {
  1234. curr_rule = (struct automemberRegexRule *)rule;
  1235. /* Regex rules are sorted by the target group DN. This means
  1236. * we can skip all rules for the last target group DN that we
  1237. * added to the exclusions list. */
  1238. if ((last == NULL) || slapi_sdn_compare(last, curr_rule->target_group_dn) != 0) {
  1239. /* Get comparison attr and loop through values. */
  1240. vals = slapi_entry_attr_get_charray(e, curr_rule->attr);
  1241. for (i = 0; vals && vals[i]; ++i) {
  1242. /* Evaluate the regex. */
  1243. if (slapi_re_exec_nt(curr_rule->regex, vals[i]) == 1) {
  1244. /* Found a match. Add to end of the exclusion list
  1245. * and set last as a hint to ourselves. */
  1246. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1247. "automember_get_membership_lists - Adding \"%s\" "
  1248. "to list of excluded groups for \"%s\" "
  1249. "(matched: \"%s=%s\").\n",
  1250. slapi_sdn_get_dn(curr_rule->target_group_dn),
  1251. slapi_entry_get_dn(e), curr_rule->attr,
  1252. curr_rule->regex_str);
  1253. dnitem = (struct automemberDNListItem *)slapi_ch_calloc(1, sizeof(struct automemberDNListItem));
  1254. /* We are just referencing the dn from the regex rule. We
  1255. * will not free it when we clean up this list. This list
  1256. * is more short-lived than the regex rule list, so we can
  1257. * get away with this optimization. */
  1258. dnitem->dn = curr_rule->target_group_dn;
  1259. PR_APPEND_LINK(&(dnitem->list), exclusions);
  1260. last = curr_rule->target_group_dn;
  1261. }
  1262. }
  1263. slapi_ch_array_free(vals);
  1264. vals = NULL;
  1265. }
  1266. rule = PR_NEXT_LINK(rule);
  1267. }
  1268. }
  1269. }
  1270. /* Go through inclusive rules and build the target list. */
  1271. if (config->inclusive_rules) {
  1272. if (!PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
  1273. /* Clear out our last hint from processing exclusions. */
  1274. last = NULL;
  1275. /* Get the first excluded target for exclusion checking. */
  1276. if (!PR_CLIST_IS_EMPTY(exclusions)) {
  1277. curr_exclusion = PR_LIST_HEAD(exclusions);
  1278. }
  1279. rule = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
  1280. while (rule != (PRCList *)config->inclusive_rules) {
  1281. curr_rule = (struct automemberRegexRule *)rule;
  1282. /* The excluded targets are stored in alphabetical order. Instead
  1283. * of going through the entire exclusion list for each inclusive
  1284. * rule, we can simply go through the exclusion list once and keep
  1285. * track of our position. If the curent exclusion comes after
  1286. * the target DN used in the current inclusive rule, it can't be
  1287. * excluded. If the current exclusion comes before the target
  1288. * in the current rule, we need to go through the exclusion list
  1289. * until we find a target that is the same or comes after the
  1290. * current rule. */
  1291. if (curr_exclusion) {
  1292. while ((curr_exclusion != exclusions) && (slapi_sdn_compare(
  1293. ((struct automemberDNListItem *)curr_exclusion)->dn,
  1294. curr_rule->target_group_dn) < 0)) {
  1295. curr_exclusion = PR_NEXT_LINK(curr_exclusion);
  1296. }
  1297. }
  1298. /* Regex rules are sorted by the target group DN. This means
  1299. * we can skip all rules for the last target group DN that we
  1300. * added to the targets list. We also skip any rules for
  1301. * target groups that have been excluded by an exclusion rule. */
  1302. if (((curr_exclusion == NULL) || (curr_exclusion == exclusions) ||
  1303. slapi_sdn_compare(((struct automemberDNListItem *)curr_exclusion)->dn,
  1304. curr_rule->target_group_dn) != 0) &&
  1305. ((last == NULL) ||
  1306. (slapi_sdn_compare(last, curr_rule->target_group_dn) != 0))) {
  1307. /* Get comparison attr and loop through values. */
  1308. vals = slapi_entry_attr_get_charray(e, curr_rule->attr);
  1309. for (i = 0; vals && vals[i]; ++i) {
  1310. /* Evaluate the regex. */
  1311. if (slapi_re_exec_nt(curr_rule->regex, vals[i]) == 1) {
  1312. /* Found a match. Add to the end of the targets list
  1313. * and set last as a hint to ourselves. */
  1314. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1315. "automember_get_membership_lists - Adding \"%s\" "
  1316. "to list of target groups for \"%s\" "
  1317. "(matched: \"%s=%s\").\n",
  1318. slapi_sdn_get_dn(curr_rule->target_group_dn),
  1319. slapi_entry_get_dn(e), curr_rule->attr,
  1320. curr_rule->regex_str);
  1321. dnitem = (struct automemberDNListItem *)slapi_ch_calloc(1,
  1322. sizeof(struct automemberDNListItem));
  1323. /* We are just referencing the dn from the regex rule. We
  1324. * will not free it when we clean up this list. This list
  1325. * is more short-lived than the regex rule list, so we can
  1326. * get away with this optimization. */
  1327. dnitem->dn = curr_rule->target_group_dn;
  1328. PR_APPEND_LINK(&(dnitem->list), targets);
  1329. last = curr_rule->target_group_dn;
  1330. }
  1331. }
  1332. slapi_ch_array_free(vals);
  1333. vals = NULL;
  1334. }
  1335. rule = PR_NEXT_LINK(rule);
  1336. }
  1337. }
  1338. }
  1339. }
  1340. /*
  1341. * automember_update_membership()
  1342. *
  1343. * Determines which target groups need to be updated according to
  1344. * the rules in config, then performs the updates.
  1345. *
  1346. * Return SLAPI_PLUGIN_FAILURE for failures, or
  1347. * SLAPI_PLUGIN_SUCCESS for success (no memberships updated), or
  1348. * MEMBERSHIP_UPDATED for success (memberships updated)
  1349. */
  1350. static int
  1351. automember_update_membership(struct configEntry *config, Slapi_Entry *e, PRFileDesc *ldif_fd)
  1352. {
  1353. PRCList exclusions;
  1354. PRCList targets;
  1355. struct automemberDNListItem *dnitem = NULL;
  1356. int rc = SLAPI_PLUGIN_SUCCESS;
  1357. int i = 0;
  1358. if (!config || !e) {
  1359. return SLAPI_PLUGIN_FAILURE;
  1360. }
  1361. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1362. "automember_update_membership - Processing \"%s\" "
  1363. "definition entry for candidate entry \"%s\".\n",
  1364. config->dn, slapi_entry_get_dn(e));
  1365. /* Initialize our lists that keep track of targets. */
  1366. PR_INIT_CLIST(&exclusions);
  1367. PR_INIT_CLIST(&targets);
  1368. /* get the membership lists */
  1369. automember_get_membership_lists(config, &exclusions, &targets, e);
  1370. /* If no targets, update default groups if set. Otherwise, update
  1371. * targets. Use a helper to do the actual updates. We can just pass an
  1372. * array of target group DNs along with our entry DN, the grouping attr,
  1373. * and the grouping value. */
  1374. if (PR_CLIST_IS_EMPTY(&targets)) {
  1375. /* Add to each default group. */
  1376. for (i = 0; config->default_groups && config->default_groups[i]; i++) {
  1377. if (automember_update_member_value(e, config->default_groups[i], config->grouping_attr,
  1378. config->grouping_value, ldif_fd, ADD_MEMBER))
  1379. {
  1380. rc = SLAPI_PLUGIN_FAILURE;
  1381. goto out;
  1382. }
  1383. rc = MEMBERSHIP_UPDATED;
  1384. }
  1385. } else {
  1386. /* Update the target groups. */
  1387. dnitem = (struct automemberDNListItem *)PR_LIST_HEAD(&targets);
  1388. while ((PRCList *)dnitem != &targets) {
  1389. if (automember_update_member_value(e, slapi_sdn_get_dn(dnitem->dn), config->grouping_attr,
  1390. config->grouping_value, ldif_fd, ADD_MEMBER))
  1391. {
  1392. rc = SLAPI_PLUGIN_FAILURE;
  1393. goto out;
  1394. }
  1395. dnitem = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dnitem);
  1396. rc = MEMBERSHIP_UPDATED;
  1397. }
  1398. }
  1399. /* Free the exclusions and targets lists */
  1400. automember_free_membership_lists(&exclusions, &targets);
  1401. out:
  1402. return rc;
  1403. }
  1404. /*
  1405. * automember_update_member_value()
  1406. *
  1407. * Adds a member entry to a group.
  1408. */
  1409. static int
  1410. automember_update_member_value(Slapi_Entry *member_e, const char *group_dn, char *grouping_attr, char *grouping_value, PRFileDesc *ldif_fd, int add)
  1411. {
  1412. Slapi_PBlock *mod_pb = slapi_pblock_new();
  1413. int result = LDAP_SUCCESS;
  1414. LDAPMod mod;
  1415. LDAPMod *mods[2];
  1416. char *vals[2];
  1417. char *member_value = NULL;
  1418. int freeit = 0;
  1419. int rc = 0;
  1420. /* If grouping_value is dn, we need to fetch the dn instead. */
  1421. if (slapi_attr_type_cmp(grouping_value, "dn", SLAPI_TYPE_CMP_EXACT) == 0) {
  1422. member_value = slapi_entry_get_ndn(member_e);
  1423. } else {
  1424. member_value = slapi_entry_attr_get_charptr(member_e, grouping_value);
  1425. freeit = 1;
  1426. }
  1427. /*
  1428. * If ldif_fd is set, we are performing an export task. Write the changes to the
  1429. * file instead of performing them
  1430. */
  1431. if (ldif_fd) {
  1432. PR_fprintf(ldif_fd, "dn: %s\n", group_dn);
  1433. PR_fprintf(ldif_fd, "changetype: modify\n");
  1434. PR_fprintf(ldif_fd, "add: %s\n", grouping_attr);
  1435. PR_fprintf(ldif_fd, "%s: %s\n", grouping_attr, member_value);
  1436. PR_fprintf(ldif_fd, "\n");
  1437. goto out;
  1438. }
  1439. if (member_value) {
  1440. /* Set up the operation. */
  1441. vals[0] = member_value;
  1442. vals[1] = 0;
  1443. if (add) {
  1444. mod.mod_op = LDAP_MOD_ADD;
  1445. } else {
  1446. mod.mod_op = LDAP_MOD_DELETE;
  1447. }
  1448. mod.mod_type = grouping_attr;
  1449. mod.mod_values = vals;
  1450. mods[0] = &mod;
  1451. mods[1] = 0;
  1452. /* Perform the modify operation. */
  1453. if (add){
  1454. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1455. "automember_update_member_value - Adding \"%s\" as "
  1456. "a \"%s\" value to group \"%s\".\n",
  1457. member_value, grouping_attr, group_dn);
  1458. } else {
  1459. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1460. "automember_update_member_value - Deleting \"%s\" as "
  1461. "a \"%s\" value from group \"%s\".\n",
  1462. member_value, grouping_attr, group_dn);
  1463. }
  1464. slapi_modify_internal_set_pb(mod_pb, group_dn,
  1465. mods, 0, 0, automember_get_plugin_id(), 0);
  1466. slapi_modify_internal_pb(mod_pb);
  1467. slapi_pblock_get(mod_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
  1468. if(add){
  1469. if ((result != LDAP_SUCCESS) && (result != LDAP_TYPE_OR_VALUE_EXISTS)) {
  1470. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1471. "automember_update_member_value - Unable to add \"%s\" as "
  1472. "a \"%s\" value to group \"%s\" (%s).\n",
  1473. member_value, grouping_attr, group_dn,
  1474. ldap_err2string(result));
  1475. rc = result;
  1476. }
  1477. } else {
  1478. /* delete value */
  1479. if ((result != LDAP_SUCCESS) && (result != LDAP_NO_SUCH_ATTRIBUTE)) {
  1480. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1481. "automember_update_member_value - Unable to delete \"%s\" as "
  1482. "a \"%s\" value from group \"%s\" (%s).\n",
  1483. member_value, grouping_attr, group_dn,
  1484. ldap_err2string(result));
  1485. rc = result;
  1486. }
  1487. }
  1488. } else {
  1489. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1490. "automember_update_member_value - Unable to find grouping "
  1491. "value attribute \"%s\" in entry \"%s\".\n",
  1492. grouping_value, slapi_entry_get_dn(member_e));
  1493. }
  1494. out:
  1495. /* Cleanup */
  1496. if (freeit) {
  1497. slapi_ch_free_string(&member_value);
  1498. }
  1499. slapi_pblock_destroy(mod_pb);
  1500. return rc;
  1501. }
  1502. /*
  1503. * Operation callback functions
  1504. */
  1505. /*
  1506. * automember_pre_op()
  1507. *
  1508. * Checks if an operation affects the auto membership
  1509. * config and validates the config changes.
  1510. */
  1511. static int
  1512. automember_pre_op(Slapi_PBlock *pb, int modop)
  1513. {
  1514. Slapi_DN *sdn = 0;
  1515. Slapi_Entry *e = 0;
  1516. Slapi_Mods *smods = 0;
  1517. LDAPMod **mods;
  1518. int free_entry = 0;
  1519. char *errstr = NULL;
  1520. int ret = SLAPI_PLUGIN_SUCCESS;
  1521. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1522. "--> automember_pre_op\n");
  1523. if (0 == (sdn = automember_get_sdn(pb)))
  1524. goto bail;
  1525. if (automember_dn_is_config(sdn)) {
  1526. /* Validate config changes, but don't apply them.
  1527. * This allows us to reject invalid config changes
  1528. * here at the pre-op stage. Applying the config
  1529. * needs to be done at the post-op stage. */
  1530. if (LDAP_CHANGETYPE_ADD == modop) {
  1531. slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &e);
  1532. /* If the entry doesn't exist, just bail and
  1533. * let the server handle it. */
  1534. if (e == NULL) {
  1535. goto bail;
  1536. }
  1537. } else if (LDAP_CHANGETYPE_MODIFY == modop) {
  1538. /* Fetch the entry being modified so we can
  1539. * create the resulting entry for validation. */
  1540. if (sdn) {
  1541. slapi_search_internal_get_entry(sdn, 0, &e, automember_get_plugin_id());
  1542. free_entry = 1;
  1543. }
  1544. /* If the entry doesn't exist, just bail and
  1545. * let the server handle it. */
  1546. if (e == NULL) {
  1547. goto bail;
  1548. }
  1549. /* Grab the mods. */
  1550. slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
  1551. smods = slapi_mods_new();
  1552. slapi_mods_init_byref(smods, mods);
  1553. /* Apply the mods to create the resulting entry. */
  1554. if (mods && (slapi_entry_apply_mods(e, mods) != LDAP_SUCCESS)) {
  1555. /* The mods don't apply cleanly, so we just let this op go
  1556. * to let the main server handle it. */
  1557. goto bailmod;
  1558. }
  1559. } else {
  1560. errstr = slapi_ch_smprintf("automember_pre_op - Invalid op type %d",
  1561. modop);
  1562. ret = LDAP_PARAM_ERROR;
  1563. goto bail;
  1564. }
  1565. if (automember_parse_config_entry(e, 0) != 0) {
  1566. /* Refuse the operation if config parsing failed. */
  1567. ret = LDAP_UNWILLING_TO_PERFORM;
  1568. if (LDAP_CHANGETYPE_ADD == modop) {
  1569. errstr = slapi_ch_smprintf("Not a valid auto membership configuration entry.");
  1570. } else {
  1571. errstr = slapi_ch_smprintf("Changes result in an invalid "
  1572. "auto membership configuration.");
  1573. }
  1574. }
  1575. }
  1576. bailmod:
  1577. /* Clean up smods. */
  1578. if (LDAP_CHANGETYPE_MODIFY == modop) {
  1579. slapi_mods_free(&smods);
  1580. }
  1581. bail:
  1582. if (free_entry && e)
  1583. slapi_entry_free(e);
  1584. if (ret) {
  1585. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1586. "automember_pre_op - Operation failure [%d]\n", ret);
  1587. slapi_send_ldap_result(pb, ret, NULL, errstr, 0, NULL);
  1588. slapi_ch_free((void **)&errstr);
  1589. slapi_pblock_set(pb, SLAPI_RESULT_CODE, &ret);
  1590. ret = SLAPI_PLUGIN_FAILURE;
  1591. }
  1592. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1593. "<-- automember_pre_op\n");
  1594. return ret;
  1595. }
  1596. static int
  1597. automember_add_pre_op(Slapi_PBlock *pb)
  1598. {
  1599. return automember_pre_op(pb, LDAP_CHANGETYPE_ADD);
  1600. }
  1601. static int
  1602. automember_mod_pre_op(Slapi_PBlock *pb)
  1603. {
  1604. return automember_pre_op(pb, LDAP_CHANGETYPE_MODIFY);
  1605. }
  1606. /*
  1607. * automember_mod_post_op()
  1608. *
  1609. * Reloads the auto membership config
  1610. * if config changes were made.
  1611. */
  1612. static int
  1613. automember_mod_post_op(Slapi_PBlock *pb)
  1614. {
  1615. Slapi_Entry *post_e = NULL;
  1616. Slapi_Entry *pre_e = NULL;
  1617. Slapi_DN *sdn = NULL;
  1618. struct configEntry *config = NULL;
  1619. PRCList *list = NULL;
  1620. int rc = SLAPI_PLUGIN_SUCCESS;
  1621. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1622. "--> automember_mod_post_op\n");
  1623. if (automember_oktodo(pb) && (sdn = automember_get_sdn(pb))) {
  1624. if (automember_dn_is_config(sdn)) {
  1625. /*
  1626. * The config is being modified, reload it
  1627. */
  1628. automember_load_config();
  1629. } else if ( !automember_isrepl(pb) && plugin_do_modify) {
  1630. /*
  1631. * We might be applying an update that will invoke automembership changes...
  1632. */
  1633. slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e);
  1634. slapi_pblock_get(pb, SLAPI_ENTRY_PRE_OP, &pre_e);
  1635. if (post_e) {
  1636. /*
  1637. * Check if a config entry applies to the entry being modified
  1638. */
  1639. automember_config_read_lock();
  1640. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  1641. list = PR_LIST_HEAD(g_automember_config);
  1642. while (list != g_automember_config) {
  1643. config = (struct configEntry *)list;
  1644. /* Does the entry meet scope and filter requirements? */
  1645. if (slapi_dn_issuffix(slapi_sdn_get_dn(sdn), config->scope) &&
  1646. slapi_filter_test_simple(post_e, config->filter) == 0)
  1647. {
  1648. /* Find out what membership changes are needed and make them. */
  1649. if ((rc = automember_update_membership(config, post_e, NULL)) == SLAPI_PLUGIN_FAILURE) {
  1650. /* Failed to update our groups, break out */
  1651. break;
  1652. } else if (rc == MEMBERSHIP_UPDATED) {
  1653. /*
  1654. * We updated one of our groups, but we might need to do some cleanup in other groups
  1655. */
  1656. PRCList exclusions_post, targets_post;
  1657. PRCList exclusions_pre, targets_pre;
  1658. struct automemberDNListItem *dn_pre = NULL;
  1659. struct automemberDNListItem *dn_post = NULL;
  1660. int i;
  1661. /* reset rc */
  1662. rc = SLAPI_PLUGIN_SUCCESS;
  1663. /* Get the group lists */
  1664. automember_get_membership_lists(config, &exclusions_post, &targets_post, post_e);
  1665. automember_get_membership_lists(config, &exclusions_pre, &targets_pre, pre_e);
  1666. /* Process the before and after lists */
  1667. if (PR_CLIST_IS_EMPTY(&targets_pre) && !PR_CLIST_IS_EMPTY(&targets_post)) {
  1668. /*
  1669. * We were in the default groups, but not anymore
  1670. */
  1671. for (i = 0; config->default_groups && config->default_groups[i]; i++) {
  1672. if (automember_update_member_value(post_e, config->default_groups[i], config->grouping_attr,
  1673. config->grouping_value, NULL, DEL_MEMBER))
  1674. {
  1675. rc = SLAPI_PLUGIN_FAILURE;
  1676. break;
  1677. }
  1678. }
  1679. } else if (!PR_CLIST_IS_EMPTY(&targets_pre) && PR_CLIST_IS_EMPTY(&targets_post)) {
  1680. /*
  1681. * We were in non-default groups, but not anymore
  1682. */
  1683. dn_pre = (struct automemberDNListItem *)PR_LIST_HEAD(&targets_pre);
  1684. while ((PRCList *)dn_pre != &targets_pre) {
  1685. if (automember_update_member_value(post_e, slapi_sdn_get_dn(dn_pre->dn), config->grouping_attr,
  1686. config->grouping_value, NULL, DEL_MEMBER))
  1687. {
  1688. rc = SLAPI_PLUGIN_FAILURE;
  1689. break;
  1690. }
  1691. dn_pre = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dn_pre);
  1692. }
  1693. } else {
  1694. /*
  1695. * We were previously in non-default groups, and still in non-default groups.
  1696. * Compare before and after memberships and cleanup the orphaned memberships
  1697. */
  1698. dn_pre = (struct automemberDNListItem *)PR_LIST_HEAD(&targets_pre);
  1699. while ((PRCList *)dn_pre != &targets_pre) {
  1700. int found = 0;
  1701. dn_post = (struct automemberDNListItem *)PR_LIST_HEAD(&targets_post);
  1702. while ((PRCList *)dn_post != &targets_post) {
  1703. if (slapi_sdn_compare(dn_pre->dn, dn_post->dn) == 0) {
  1704. /* found */
  1705. found = 1;
  1706. break;
  1707. }
  1708. /* Next dn */
  1709. dn_post = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dn_post);
  1710. }
  1711. if (!found){
  1712. /* Remove user from dn_pre->dn */
  1713. if (automember_update_member_value(post_e, slapi_sdn_get_dn(dn_pre->dn), config->grouping_attr,
  1714. config->grouping_value, NULL, DEL_MEMBER))
  1715. {
  1716. rc = SLAPI_PLUGIN_FAILURE;
  1717. break;
  1718. }
  1719. }
  1720. /* Next dn */
  1721. dn_pre = (struct automemberDNListItem *)PR_NEXT_LINK((PRCList *)dn_pre);
  1722. }
  1723. }
  1724. /* All done with this config entry, free the lists */
  1725. automember_free_membership_lists(&exclusions_post, &targets_post);
  1726. automember_free_membership_lists(&exclusions_pre, &targets_pre);
  1727. if (rc == SLAPI_PLUGIN_FAILURE) {
  1728. break;
  1729. }
  1730. }
  1731. }
  1732. list = PR_NEXT_LINK(list);
  1733. }
  1734. }
  1735. automember_config_unlock();
  1736. }
  1737. }
  1738. }
  1739. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1740. "<-- automember_mod_post_op (%d)\n", rc);
  1741. return rc;
  1742. }
  1743. static int
  1744. automember_add_post_op(Slapi_PBlock *pb)
  1745. {
  1746. Slapi_Entry *e = NULL;
  1747. Slapi_DN *sdn = NULL;
  1748. struct configEntry *config = NULL;
  1749. PRCList *list = NULL;
  1750. int rc = SLAPI_PLUGIN_SUCCESS;
  1751. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1752. "--> automember_add_post_op\n");
  1753. /* Reload config if a config entry was added. */
  1754. if ((sdn = automember_get_sdn(pb))) {
  1755. if (automember_dn_is_config(sdn)) {
  1756. automember_load_config();
  1757. }
  1758. } else {
  1759. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1760. "automember_add_post_op - Error retrieving dn\n");
  1761. rc = SLAPI_PLUGIN_FAILURE;
  1762. goto bail;
  1763. }
  1764. /* If replication, just bail. */
  1765. if (automember_isrepl(pb)) {
  1766. return SLAPI_PLUGIN_SUCCESS;
  1767. }
  1768. /* Get the newly added entry. */
  1769. slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &e);
  1770. if (e) {
  1771. /* If the entry is a tombstone, just bail. */
  1772. Slapi_Value *tombstone = slapi_value_new_string(SLAPI_ATTR_VALUE_TOMBSTONE);
  1773. int is_tombstone = slapi_entry_attr_has_syntax_value(e, SLAPI_ATTR_OBJECTCLASS,
  1774. tombstone);
  1775. slapi_value_free(&tombstone);
  1776. if (is_tombstone) {
  1777. return SLAPI_PLUGIN_SUCCESS;
  1778. }
  1779. /* Check if a config entry applies
  1780. * to the entry being added. */
  1781. automember_config_read_lock();
  1782. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  1783. list = PR_LIST_HEAD(g_automember_config);
  1784. while (list != g_automember_config) {
  1785. config = (struct configEntry *)list;
  1786. /* Does the entry meet scope and filter requirements? */
  1787. if (slapi_dn_issuffix(slapi_sdn_get_dn(sdn), config->scope) &&
  1788. (slapi_filter_test_simple(e, config->filter) == 0)) {
  1789. /* Find out what membership changes are needed and make them. */
  1790. if (automember_update_membership(config, e, NULL) == SLAPI_PLUGIN_FAILURE) {
  1791. rc = SLAPI_PLUGIN_FAILURE;
  1792. break;
  1793. }
  1794. }
  1795. list = PR_NEXT_LINK(list);
  1796. }
  1797. }
  1798. automember_config_unlock();
  1799. } else {
  1800. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1801. "automember_add_post_op - Error "
  1802. "retrieving post-op entry %s\n",
  1803. slapi_sdn_get_dn(sdn));
  1804. }
  1805. bail:
  1806. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1807. "<-- automember_add_post_op (%d)\n", rc);
  1808. if (rc) {
  1809. char errtxt[SLAPI_DSE_RETURNTEXT_SIZE];
  1810. int result = LDAP_UNWILLING_TO_PERFORM;
  1811. PR_snprintf(errtxt, SLAPI_DSE_RETURNTEXT_SIZE, "Automember Plugin update unexpectedly failed.\n");
  1812. slapi_pblock_set(pb, SLAPI_RESULT_CODE, &result);
  1813. slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, &errtxt);
  1814. }
  1815. return rc;
  1816. }
  1817. /*
  1818. * automember_del_post_op()
  1819. *
  1820. * Removes deleted config.
  1821. */
  1822. static int
  1823. automember_del_post_op(Slapi_PBlock *pb)
  1824. {
  1825. Slapi_DN *sdn = NULL;
  1826. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1827. "--> automember_del_post_op\n");
  1828. /* Reload config if a config entry was deleted. */
  1829. if ((sdn = automember_get_sdn(pb))) {
  1830. if (automember_dn_is_config(sdn))
  1831. automember_load_config();
  1832. } else {
  1833. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1834. "automember_del_post_op - Error retrieving dn\n");
  1835. }
  1836. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1837. "<-- automember_del_post_op\n");
  1838. return SLAPI_PLUGIN_SUCCESS;
  1839. }
  1840. typedef struct _task_data
  1841. {
  1842. char *filter_str;
  1843. char *ldif_out;
  1844. char *ldif_in;
  1845. Slapi_DN *base_dn;
  1846. char *bind_dn;
  1847. int scope;
  1848. } task_data;
  1849. static void
  1850. automember_task_destructor(Slapi_Task *task)
  1851. {
  1852. if (task) {
  1853. task_data *mydata = (task_data *)slapi_task_get_data(task);
  1854. while (slapi_task_get_refcount(task) > 0) {
  1855. /* Yield to wait for the fixup task finishes. */
  1856. DS_Sleep(PR_MillisecondsToInterval(100));
  1857. }
  1858. if (mydata) {
  1859. slapi_ch_free_string(&mydata->bind_dn);
  1860. slapi_sdn_free(&mydata->base_dn);
  1861. slapi_ch_free_string(&mydata->filter_str);
  1862. slapi_ch_free((void **)&mydata);
  1863. }
  1864. }
  1865. }
  1866. static void
  1867. automember_task_export_destructor(Slapi_Task *task)
  1868. {
  1869. if (task) {
  1870. task_data *mydata = (task_data *)slapi_task_get_data(task);
  1871. while (slapi_task_get_refcount(task) > 0) {
  1872. /* Yield to wait for the fixup task finishes. */
  1873. DS_Sleep(PR_MillisecondsToInterval(100));
  1874. }
  1875. if (mydata) {
  1876. slapi_ch_free_string(&mydata->ldif_out);
  1877. slapi_ch_free_string(&mydata->bind_dn);
  1878. slapi_sdn_free(&mydata->base_dn);
  1879. slapi_ch_free_string(&mydata->filter_str);
  1880. slapi_ch_free((void **)&mydata);
  1881. }
  1882. }
  1883. }
  1884. static void
  1885. automember_task_map_destructor(Slapi_Task *task)
  1886. {
  1887. if (task) {
  1888. task_data *mydata = (task_data *)slapi_task_get_data(task);
  1889. while (slapi_task_get_refcount(task) > 0) {
  1890. /* Yield to wait for the fixup task finishes. */
  1891. DS_Sleep(PR_MillisecondsToInterval(100));
  1892. }
  1893. if (mydata) {
  1894. slapi_ch_free_string(&mydata->ldif_out);
  1895. slapi_ch_free_string(&mydata->ldif_in);
  1896. slapi_ch_free_string(&mydata->bind_dn);
  1897. slapi_ch_free((void **)&mydata);
  1898. }
  1899. }
  1900. }
  1901. /*
  1902. * automember_task_add
  1903. *
  1904. * This task is designed to "retro-fit" entries that existed prior to
  1905. * enabling this plugin. This can be an expensive task to run, but it's
  1906. * better than processing every modify operation in an attempt to catch
  1907. * entries that have not been processed.
  1908. *
  1909. * task entry:
  1910. *
  1911. * dn: cn=my rebuild task, cn=automember rebuild membership,cn=tasks,cn=config
  1912. * objectClass: top
  1913. * objectClass: extensibleObject
  1914. * cn: my rebuild task
  1915. * basedn: dc=example,dc=com
  1916. * filter: (uid=*)
  1917. * scope: sub
  1918. *
  1919. * basedn and filter are required. If scope is omitted, the default is sub
  1920. */
  1921. static int
  1922. automember_task_add(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attribute__((unused)), int *returncode, char *returntext __attribute__((unused)), void *arg)
  1923. {
  1924. int rv = SLAPI_DSE_CALLBACK_OK;
  1925. task_data *mytaskdata = NULL;
  1926. Slapi_Task *task = NULL;
  1927. PRThread *thread = NULL;
  1928. char *bind_dn = NULL;
  1929. const char *base_dn;
  1930. const char *filter;
  1931. const char *scope;
  1932. *returncode = LDAP_SUCCESS;
  1933. /*
  1934. * Grab the task params
  1935. */
  1936. if ((base_dn = slapi_fetch_attr(e, "basedn", 0)) == NULL) {
  1937. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1938. rv = SLAPI_DSE_CALLBACK_ERROR;
  1939. goto out;
  1940. }
  1941. if ((filter = slapi_fetch_attr(e, "filter", 0)) == NULL) {
  1942. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  1943. rv = SLAPI_DSE_CALLBACK_ERROR;
  1944. goto out;
  1945. }
  1946. scope = slapi_fetch_attr(e, "scope", "sub");
  1947. /*
  1948. * setup our task data
  1949. */
  1950. mytaskdata = (task_data *)slapi_ch_calloc(1, sizeof(task_data));
  1951. if (mytaskdata == NULL) {
  1952. *returncode = LDAP_OPERATIONS_ERROR;
  1953. rv = SLAPI_DSE_CALLBACK_ERROR;
  1954. goto out;
  1955. }
  1956. slapi_pblock_get(pb, SLAPI_REQUESTOR_DN, &bind_dn);
  1957. mytaskdata->bind_dn = slapi_ch_strdup(bind_dn);
  1958. mytaskdata->base_dn = slapi_sdn_new_dn_byval(base_dn);
  1959. mytaskdata->filter_str = slapi_ch_strdup(filter);
  1960. if (scope) {
  1961. if (strcasecmp(scope, "sub") == 0) {
  1962. mytaskdata->scope = 2;
  1963. } else if (strcasecmp(scope, "one") == 0) {
  1964. mytaskdata->scope = 1;
  1965. } else if (strcasecmp(scope, "base") == 0) {
  1966. mytaskdata->scope = 0;
  1967. } else {
  1968. /* Hmm, possible typo, use subtree */
  1969. mytaskdata->scope = 2;
  1970. }
  1971. } else {
  1972. /* subtree by default */
  1973. mytaskdata->scope = 2;
  1974. }
  1975. task = slapi_plugin_new_task(slapi_entry_get_ndn(e), arg);
  1976. slapi_task_set_destructor_fn(task, automember_task_destructor);
  1977. slapi_task_set_data(task, mytaskdata);
  1978. /*
  1979. * Start the task as a separate thread
  1980. */
  1981. thread = PR_CreateThread(PR_USER_THREAD, automember_rebuild_task_thread,
  1982. (void *)task, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  1983. PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
  1984. if (thread == NULL) {
  1985. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  1986. "automember_task_add - Unable to create task thread!\n");
  1987. *returncode = LDAP_OPERATIONS_ERROR;
  1988. slapi_task_finish(task, *returncode);
  1989. rv = SLAPI_DSE_CALLBACK_ERROR;
  1990. } else {
  1991. rv = SLAPI_DSE_CALLBACK_OK;
  1992. }
  1993. out:
  1994. return rv;
  1995. }
  1996. /*
  1997. * automember_rebuild_task_thread()
  1998. *
  1999. * Search using the basedn, filter, and scope provided from the task data.
  2000. * Then loop of each entry, and apply the membership if applicable.
  2001. */
  2002. void
  2003. automember_rebuild_task_thread(void *arg)
  2004. {
  2005. Slapi_Task *task = (Slapi_Task *)arg;
  2006. struct configEntry *config = NULL;
  2007. Slapi_PBlock *search_pb = NULL, *fixup_pb = NULL;
  2008. Slapi_Entry **entries = NULL;
  2009. task_data *td = NULL;
  2010. PRCList *list = NULL;
  2011. PRCList *include_list = NULL;
  2012. int result = 0;
  2013. size_t i = 0, ii = 0;
  2014. if (!task) {
  2015. return; /* no task */
  2016. }
  2017. slapi_task_inc_refcount(task);
  2018. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2019. "automember_rebuild_task_thread - Refcount incremented.\n");
  2020. /*
  2021. * Fetch our task data from the task
  2022. */
  2023. td = (task_data *)slapi_task_get_data(task);
  2024. slapi_task_begin(task, 1);
  2025. slapi_task_log_notice(task, "Automember rebuild task starting (base dn: (%s) filter (%s)...\n",
  2026. slapi_sdn_get_dn(td->base_dn), td->filter_str);
  2027. slapi_task_log_status(task, "Automember rebuild task starting (base dn: (%s) filter (%s)...\n",
  2028. slapi_sdn_get_dn(td->base_dn), td->filter_str);
  2029. /*
  2030. * Set the bind dn in the local thread data
  2031. */
  2032. slapi_td_set_dn(slapi_ch_strdup(td->bind_dn));
  2033. /*
  2034. * Search the database
  2035. */
  2036. search_pb = slapi_pblock_new();
  2037. slapi_search_internal_set_pb_ext(search_pb, td->base_dn, td->scope, td->filter_str, NULL,
  2038. 0, NULL, NULL, automember_get_plugin_id(), 0);
  2039. slapi_search_internal_pb(search_pb);
  2040. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
  2041. if (LDAP_SUCCESS != result) {
  2042. slapi_task_log_notice(task, "Automember rebuild membership task unable to search"
  2043. " on base (%s) filter (%s) error (%d)\n",
  2044. slapi_sdn_get_dn(td->base_dn),
  2045. td->filter_str, result);
  2046. slapi_task_log_status(task, "Automember rebuild membership task unable to search"
  2047. " on base (%s) filter (%s) error (%d)\n",
  2048. slapi_sdn_get_dn(td->base_dn),
  2049. td->filter_str, result);
  2050. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2051. "automember_rebuild_task_thread - Unable to search on base (%s) filter (%s) error (%d)\n",
  2052. slapi_sdn_get_dn(td->base_dn), td->filter_str, result);
  2053. goto out;
  2054. }
  2055. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
  2056. /*
  2057. * If this is a backend txn plugin, start the transaction
  2058. */
  2059. if (plugin_is_betxn) {
  2060. Slapi_Backend *be = slapi_be_select(td->base_dn);
  2061. if (be) {
  2062. fixup_pb = slapi_pblock_new();
  2063. slapi_pblock_set(fixup_pb, SLAPI_BACKEND, be);
  2064. if (slapi_back_transaction_begin(fixup_pb) != LDAP_SUCCESS) {
  2065. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2066. "automember_rebuild_task_thread - Failed to start transaction\n");
  2067. }
  2068. } else {
  2069. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2070. "automember_rebuild_task_thread - Failed to get be backend from %s\n",
  2071. slapi_sdn_get_dn(td->base_dn));
  2072. }
  2073. }
  2074. /*
  2075. * Grab the config read lock, and loop over the entries
  2076. */
  2077. automember_config_read_lock();
  2078. for (i = 0; entries && (entries[i] != NULL); i++) {
  2079. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  2080. list = PR_LIST_HEAD(g_automember_config);
  2081. while (list != g_automember_config) {
  2082. config = (struct configEntry *)list;
  2083. /* Does the entry meet scope and filter requirements? */
  2084. if (slapi_dn_issuffix(slapi_entry_get_dn(entries[i]), config->scope) &&
  2085. (slapi_filter_test_simple(entries[i], config->filter) == 0))
  2086. {
  2087. /* First clear out all the defaults groups */
  2088. for (ii = 0; config->default_groups && config->default_groups[ii]; ii++) {
  2089. if ((result = automember_update_member_value(entries[i], config->default_groups[ii],
  2090. config->grouping_attr, config->grouping_value, NULL, DEL_MEMBER)))
  2091. {
  2092. slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
  2093. "member from default group (%s) error (%d)\n",
  2094. config->default_groups[ii], result);
  2095. slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
  2096. "member from default group (%s) error (%d)\n",
  2097. config->default_groups[ii], result);
  2098. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2099. "automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
  2100. config->default_groups[ii], result);
  2101. automember_config_unlock();
  2102. goto out;
  2103. }
  2104. }
  2105. /* Then clear out the non-default group */
  2106. if (config->inclusive_rules && !PR_CLIST_IS_EMPTY((PRCList *)config->inclusive_rules)) {
  2107. include_list = PR_LIST_HEAD((PRCList *)config->inclusive_rules);
  2108. while (include_list != (PRCList *)config->inclusive_rules) {
  2109. struct automemberRegexRule *curr_rule = (struct automemberRegexRule *)include_list;
  2110. if ((result = automember_update_member_value(entries[i], slapi_sdn_get_dn(curr_rule->target_group_dn),
  2111. config->grouping_attr, config->grouping_value, NULL, DEL_MEMBER)))
  2112. {
  2113. slapi_task_log_notice(task, "Automember rebuild membership task unable to delete "
  2114. "member from group (%s) error (%d)\n",
  2115. slapi_sdn_get_dn(curr_rule->target_group_dn), result);
  2116. slapi_task_log_status(task, "Automember rebuild membership task unable to delete "
  2117. "member from group (%s) error (%d)\n",
  2118. slapi_sdn_get_dn(curr_rule->target_group_dn), result);
  2119. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2120. "automember_rebuild_task_thread - Unable to unable to delete from (%s) error (%d)\n",
  2121. slapi_sdn_get_dn(curr_rule->target_group_dn), result);
  2122. automember_config_unlock();
  2123. goto out;
  2124. }
  2125. include_list = PR_NEXT_LINK(include_list);
  2126. }
  2127. }
  2128. /* Update the memberships for this entries */
  2129. if (slapi_is_shutting_down() ||
  2130. automember_update_membership(config, entries[i], NULL) == SLAPI_PLUGIN_FAILURE)
  2131. {
  2132. result = SLAPI_PLUGIN_FAILURE;
  2133. automember_config_unlock();
  2134. goto out;
  2135. }
  2136. }
  2137. list = PR_NEXT_LINK(list);
  2138. }
  2139. }
  2140. }
  2141. automember_config_unlock();
  2142. out:
  2143. if (plugin_is_betxn && fixup_pb) {
  2144. if (i == 0 || result != 0) { /* no updates performed */
  2145. slapi_back_transaction_abort(fixup_pb);
  2146. } else {
  2147. slapi_back_transaction_commit(fixup_pb);
  2148. }
  2149. slapi_pblock_destroy(fixup_pb);
  2150. }
  2151. slapi_free_search_results_internal(search_pb);
  2152. slapi_pblock_destroy(search_pb);
  2153. if (result) {
  2154. /* error */
  2155. slapi_task_log_notice(task, "Automember rebuild task aborted. Error (%d)", result);
  2156. slapi_task_log_status(task, "Automember rebuild task aborted. Error (%d)", result);
  2157. } else {
  2158. slapi_task_log_notice(task, "Automember rebuild task finished. Processed (%d) entries.", i);
  2159. slapi_task_log_status(task, "Automember rebuild task finished. Processed (%d) entries.", i);
  2160. }
  2161. slapi_task_inc_progress(task);
  2162. slapi_task_finish(task, result);
  2163. slapi_task_dec_refcount(task);
  2164. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2165. "automember_rebuild_task_thread - Refcount decremented.\n");
  2166. }
  2167. /*
  2168. * Export an ldif of the changes that would be made if we ran the automember rebuild membership task
  2169. *
  2170. * task entry:
  2171. *
  2172. * dn: cn=my export task, cn=automember export updates,cn=tasks,cn=config
  2173. * objectClass: top
  2174. * objectClass: extensibleObject
  2175. * cn: my export task
  2176. * basedn: dc=example,dc=com
  2177. * filter: (uid=*)
  2178. * scope: sub
  2179. * ldif: /tmp/automem-updates.ldif
  2180. */
  2181. static int
  2182. automember_task_add_export_updates(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attribute__((unused)), int *returncode, char *returntext __attribute__((unused)), void *arg)
  2183. {
  2184. int rv = SLAPI_DSE_CALLBACK_OK;
  2185. task_data *mytaskdata = NULL;
  2186. Slapi_Task *task = NULL;
  2187. PRThread *thread = NULL;
  2188. char *bind_dn = NULL;
  2189. const char *base_dn = NULL;
  2190. const char *filter = NULL;
  2191. const char *ldif = NULL;
  2192. const char *scope = NULL;
  2193. *returncode = LDAP_SUCCESS;
  2194. if ((ldif = slapi_fetch_attr(e, "ldif", 0)) == NULL) {
  2195. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  2196. rv = SLAPI_DSE_CALLBACK_ERROR;
  2197. goto out;
  2198. }
  2199. if ((base_dn = slapi_fetch_attr(e, "basedn", 0)) == NULL) {
  2200. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  2201. rv = SLAPI_DSE_CALLBACK_ERROR;
  2202. goto out;
  2203. }
  2204. if ((filter = slapi_fetch_attr(e, "filter", 0)) == NULL) {
  2205. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  2206. rv = SLAPI_DSE_CALLBACK_ERROR;
  2207. goto out;
  2208. }
  2209. scope = slapi_fetch_attr(e, "scope", "sub");
  2210. slapi_pblock_get(pb, SLAPI_REQUESTOR_DN, &bind_dn);
  2211. mytaskdata = (task_data *)slapi_ch_calloc(1, sizeof(task_data));
  2212. if (mytaskdata == NULL) {
  2213. *returncode = LDAP_OPERATIONS_ERROR;
  2214. rv = SLAPI_DSE_CALLBACK_ERROR;
  2215. goto out;
  2216. }
  2217. mytaskdata->bind_dn = slapi_ch_strdup(bind_dn);
  2218. mytaskdata->ldif_out = slapi_ch_strdup(ldif);
  2219. mytaskdata->base_dn = slapi_sdn_new_dn_byval(base_dn);
  2220. mytaskdata->filter_str = slapi_ch_strdup(filter);
  2221. if (scope) {
  2222. if (strcasecmp(scope, "sub") == 0) {
  2223. mytaskdata->scope = 2;
  2224. } else if (strcasecmp(scope, "one") == 0) {
  2225. mytaskdata->scope = 1;
  2226. } else if (strcasecmp(scope, "base") == 0) {
  2227. mytaskdata->scope = 0;
  2228. } else {
  2229. /* Hmm, possible typo, use subtree */
  2230. mytaskdata->scope = 2;
  2231. }
  2232. } else {
  2233. /* subtree by default */
  2234. mytaskdata->scope = 2;
  2235. }
  2236. task = slapi_plugin_new_task(slapi_entry_get_ndn(e), arg);
  2237. slapi_task_set_destructor_fn(task, automember_task_export_destructor);
  2238. slapi_task_set_data(task, mytaskdata);
  2239. /*
  2240. * Start the task as a separate thread
  2241. */
  2242. thread = PR_CreateThread(PR_USER_THREAD, automember_export_task_thread,
  2243. (void *)task, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  2244. PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
  2245. if (thread == NULL) {
  2246. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2247. "automember_task_add_export_updates - Unable to create export task thread!\n");
  2248. *returncode = LDAP_OPERATIONS_ERROR;
  2249. rv = SLAPI_DSE_CALLBACK_ERROR;
  2250. slapi_task_finish(task, *returncode);
  2251. } else {
  2252. rv = SLAPI_DSE_CALLBACK_OK;
  2253. }
  2254. out:
  2255. return rv;
  2256. }
  2257. void
  2258. automember_export_task_thread(void *arg)
  2259. {
  2260. Slapi_Task *task = (Slapi_Task *)arg;
  2261. Slapi_PBlock *search_pb = NULL;
  2262. Slapi_Entry **entries = NULL;
  2263. int result = SLAPI_DSE_CALLBACK_OK;
  2264. struct configEntry *config = NULL;
  2265. PRCList *list = NULL;
  2266. task_data *td = NULL;
  2267. PRFileDesc *ldif_fd;
  2268. int i = 0;
  2269. int rc = 0;
  2270. if (!task) {
  2271. return; /* no task */
  2272. }
  2273. slapi_task_inc_refcount(task);
  2274. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2275. "automember_export_task_thread - Refcount incremented.\n");
  2276. td = (task_data *)slapi_task_get_data(task);
  2277. slapi_task_begin(task, 1);
  2278. slapi_task_log_notice(task, "Automember export task starting. Exporting changes to (%s)", td->ldif_out);
  2279. slapi_task_log_status(task, "Automember export task starting. Exporting changes to (%s)", td->ldif_out);
  2280. /* make sure we can open the ldif file */
  2281. if ((ldif_fd = PR_Open(td->ldif_out, PR_CREATE_FILE | PR_WRONLY, DEFAULT_FILE_MODE)) == NULL) {
  2282. rc = PR_GetOSError();
  2283. slapi_task_log_notice(task, "Automember export task could not open ldif file \"%s\" for writing, error %d (%s)\n",
  2284. td->ldif_out, rc, slapi_system_strerror(rc));
  2285. slapi_task_log_status(task, "Automember export task could not open ldif file \"%s\" for writing, error %d (%s)\n",
  2286. td->ldif_out, rc, slapi_system_strerror(rc));
  2287. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2288. "automember_export_task_thread - Could not open ldif file \"%s\" for writing, error %d (%s)\n",
  2289. td->ldif_out, rc, slapi_system_strerror(rc));
  2290. result = SLAPI_DSE_CALLBACK_ERROR;
  2291. goto out;
  2292. }
  2293. /*
  2294. * Set the bind dn in the local thread data
  2295. */
  2296. slapi_td_set_dn(slapi_ch_strdup(td->bind_dn));
  2297. /*
  2298. * Search the database
  2299. */
  2300. search_pb = slapi_pblock_new();
  2301. slapi_search_internal_set_pb_ext(search_pb, td->base_dn, td->scope, td->filter_str, NULL,
  2302. 0, NULL, NULL, automember_get_plugin_id(), 0);
  2303. slapi_search_internal_pb(search_pb);
  2304. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &result);
  2305. if (LDAP_SUCCESS != result) {
  2306. slapi_task_log_notice(task, "Automember task failed to search on base (%s) filter (%s) error (%d)\n",
  2307. slapi_sdn_get_dn(td->base_dn), td->filter_str, result);
  2308. slapi_task_log_status(task, "Automember task failed to search on base (%s) filter (%s) error (%d)\n",
  2309. slapi_sdn_get_dn(td->base_dn), td->filter_str, result);
  2310. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2311. "automember_export_task_thread - Unable to search on base (%s) filter (%s) error (%d)\n",
  2312. slapi_sdn_get_dn(td->base_dn), td->filter_str, result);
  2313. goto out;
  2314. }
  2315. slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
  2316. /*
  2317. * Grab the config read lock, and loop over the entries
  2318. */
  2319. automember_config_read_lock();
  2320. for (i = 0; entries && (entries[i] != NULL); i++) {
  2321. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  2322. list = PR_LIST_HEAD(g_automember_config);
  2323. while (list != g_automember_config) {
  2324. config = (struct configEntry *)list;
  2325. if (slapi_dn_issuffix(slapi_sdn_get_dn(td->base_dn), config->scope) &&
  2326. (slapi_filter_test_simple(entries[i], config->filter) == 0)) {
  2327. if (slapi_is_shutting_down() ||
  2328. automember_update_membership(config, entries[i], ldif_fd) == SLAPI_PLUGIN_FAILURE) {
  2329. result = SLAPI_DSE_CALLBACK_ERROR;
  2330. automember_config_unlock();
  2331. goto out;
  2332. }
  2333. }
  2334. list = PR_NEXT_LINK(list);
  2335. }
  2336. }
  2337. }
  2338. automember_config_unlock();
  2339. out:
  2340. slapi_free_search_results_internal(search_pb);
  2341. slapi_pblock_destroy(search_pb);
  2342. if (ldif_fd) {
  2343. PR_Close(ldif_fd);
  2344. }
  2345. if (result) {
  2346. /* error */
  2347. slapi_task_log_notice(task, "Automember export task aborted. Error (%d)", result);
  2348. slapi_task_log_status(task, "Automember export task aborted. Error (%d)", result);
  2349. } else {
  2350. slapi_task_log_notice(task, "Automember export task finished. Processed (%d) entries.", i);
  2351. slapi_task_log_status(task, "Automember export task finished. Processed (%d) entries.", i);
  2352. }
  2353. slapi_task_inc_progress(task);
  2354. slapi_task_finish(task, result);
  2355. slapi_task_dec_refcount(task);
  2356. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2357. "automember_export_task_thread - Refcount decremented.\n");
  2358. }
  2359. /*
  2360. * Export an ldif of the changes that would be made from the entries
  2361. * in the provided ldif file
  2362. *
  2363. * task entry:
  2364. *
  2365. * dn: cn=my map task, cn=automember map updates,cn=tasks,cn=config
  2366. * objectClass: top
  2367. * objectClass: extensibleObject
  2368. * cn: my export task
  2369. * ldif_in: /tmp/entries.ldif
  2370. * ldif_out: /tmp/automem-updates.ldif
  2371. */
  2372. static int
  2373. automember_task_add_map_entries(Slapi_PBlock *pb, Slapi_Entry *e, Slapi_Entry *eAfter __attribute__((unused)), int *returncode, char *returntext __attribute__((unused)), void *arg)
  2374. {
  2375. int rv = SLAPI_DSE_CALLBACK_OK;
  2376. task_data *mytaskdata = NULL;
  2377. Slapi_Task *task = NULL;
  2378. PRThread *thread = NULL;
  2379. char *bind_dn;
  2380. const char *ldif_out;
  2381. const char *ldif_in;
  2382. *returncode = LDAP_SUCCESS;
  2383. /*
  2384. * Get the params
  2385. */
  2386. if ((ldif_in = slapi_fetch_attr(e, "ldif_in", 0)) == NULL) {
  2387. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  2388. rv = SLAPI_DSE_CALLBACK_ERROR;
  2389. goto out;
  2390. }
  2391. if ((ldif_out = slapi_fetch_attr(e, "ldif_out", 0)) == NULL) {
  2392. *returncode = LDAP_OBJECT_CLASS_VIOLATION;
  2393. rv = SLAPI_DSE_CALLBACK_ERROR;
  2394. goto out;
  2395. }
  2396. /*
  2397. * Setup the task data
  2398. */
  2399. mytaskdata = (task_data *)slapi_ch_calloc(1, sizeof(task_data));
  2400. if (mytaskdata == NULL) {
  2401. *returncode = LDAP_OPERATIONS_ERROR;
  2402. rv = SLAPI_DSE_CALLBACK_ERROR;
  2403. goto out;
  2404. }
  2405. slapi_pblock_get(pb, SLAPI_REQUESTOR_DN, &bind_dn);
  2406. mytaskdata->bind_dn = slapi_ch_strdup(bind_dn);
  2407. mytaskdata->ldif_out = slapi_ch_strdup(ldif_out);
  2408. mytaskdata->ldif_in = slapi_ch_strdup(ldif_in);
  2409. task = slapi_plugin_new_task(slapi_entry_get_ndn(e), arg);
  2410. slapi_task_set_destructor_fn(task, automember_task_map_destructor);
  2411. slapi_task_set_data(task, mytaskdata);
  2412. /*
  2413. * Start the task as a separate thread
  2414. */
  2415. thread = PR_CreateThread(PR_USER_THREAD, automember_map_task_thread,
  2416. (void *)task, PR_PRIORITY_NORMAL, PR_GLOBAL_THREAD,
  2417. PR_UNJOINABLE_THREAD, SLAPD_DEFAULT_THREAD_STACKSIZE);
  2418. if (thread == NULL) {
  2419. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2420. "automember_task_add_map_entries - Unable to create map task thread!\n");
  2421. *returncode = LDAP_OPERATIONS_ERROR;
  2422. rv = SLAPI_DSE_CALLBACK_ERROR;
  2423. slapi_task_finish(task, *returncode);
  2424. } else {
  2425. rv = SLAPI_DSE_CALLBACK_OK;
  2426. }
  2427. out:
  2428. return rv;
  2429. }
  2430. /*
  2431. * Read in the text entries from ldif_in, and convert them to slapi_entries.
  2432. * Then, write to ldif_out what the updates would be if these entries were added
  2433. */
  2434. void
  2435. automember_map_task_thread(void *arg)
  2436. {
  2437. Slapi_Task *task = (Slapi_Task *)arg;
  2438. Slapi_Entry *e = NULL;
  2439. int result = SLAPI_DSE_CALLBACK_OK;
  2440. struct configEntry *config = NULL;
  2441. PRCList *list = NULL;
  2442. task_data *td = NULL;
  2443. PRFileDesc *ldif_fd_out = NULL;
  2444. char *entrystr = NULL;
  2445. char *errstr = NULL;
  2446. #if defined(USE_OPENLDAP)
  2447. int buflen = 0;
  2448. LDIFFP *ldif_fd_in = NULL;
  2449. ldif_record_lineno_t lineno = 0;
  2450. #else
  2451. FILE *ldif_fd_in = NULL;
  2452. int lineno = 0;
  2453. #endif
  2454. int rc = 0;
  2455. if (!task) {
  2456. return; /* no task */
  2457. }
  2458. slapi_task_inc_refcount(task);
  2459. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2460. "automember_map_task_thread - Refcount incremented.\n");
  2461. td = (task_data *)slapi_task_get_data(task);
  2462. slapi_task_begin(task, 1);
  2463. slapi_task_log_notice(task, "Automember map task starting... Reading entries from (%s)"
  2464. " and writing the updates to (%s)",
  2465. td->ldif_in, td->ldif_out);
  2466. slapi_task_log_status(task, "Automember map task starting... Reading entries from (%s)"
  2467. " and writing the updates to (%s)",
  2468. td->ldif_in, td->ldif_out);
  2469. /* make sure we can open the ldif files */
  2470. if ((ldif_fd_out = PR_Open(td->ldif_out, PR_CREATE_FILE | PR_WRONLY, DEFAULT_FILE_MODE)) == NULL) {
  2471. rc = PR_GetOSError();
  2472. slapi_task_log_notice(task, "The ldif file %s could not be accessed, error %d (%s). Aborting task.\n",
  2473. td->ldif_out, rc, slapi_system_strerror(rc));
  2474. slapi_task_log_status(task, "The ldif file %s could not be accessed, error %d (%s). Aborting task.\n",
  2475. td->ldif_out, rc, slapi_system_strerror(rc));
  2476. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2477. "automember_map_task_thread - Could not open ldif file \"%s\" for writing, error %d (%s)\n",
  2478. td->ldif_out, rc, slapi_system_strerror(rc));
  2479. result = SLAPI_DSE_CALLBACK_ERROR;
  2480. goto out;
  2481. }
  2482. #if defined(USE_OPENLDAP)
  2483. if ((ldif_fd_in = ldif_open(td->ldif_in, "r")) == NULL) {
  2484. rc = errno;
  2485. errstr = strerror(rc);
  2486. #else
  2487. if ((ldif_fd_in = fopen(td->ldif_in, "r")) == NULL) {
  2488. rc = PR_GetOSError();
  2489. errstr = (char *)slapi_system_strerror(rc);
  2490. #endif
  2491. slapi_task_log_notice(task, "The ldif file %s could not be accessed, error %d (%s). Aborting task.\n",
  2492. td->ldif_in, rc, errstr);
  2493. slapi_task_log_status(task, "The ldif file %s could not be accessed, error %d (%s). Aborting task.\n",
  2494. td->ldif_in, rc, errstr);
  2495. slapi_log_err(SLAPI_LOG_ERR, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2496. "automember_map_task_thread - Could not open ldif file \"%s\" for reading, error %d (%s)\n",
  2497. td->ldif_in, rc, errstr);
  2498. result = SLAPI_DSE_CALLBACK_ERROR;
  2499. goto out;
  2500. }
  2501. /*
  2502. * Convert each LDIF entry to a slapi_entry
  2503. */
  2504. automember_config_read_lock();
  2505. #if defined(USE_OPENLDAP)
  2506. while (ldif_read_record(ldif_fd_in, &lineno, &entrystr, &buflen)) {
  2507. buflen = 0;
  2508. #else
  2509. while ((entrystr = ldif_get_entry(ldif_fd_in, &lineno)) != NULL) {
  2510. #endif
  2511. e = slapi_str2entry(entrystr, 0);
  2512. if (e != NULL) {
  2513. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  2514. list = PR_LIST_HEAD(g_automember_config);
  2515. while (list != g_automember_config) {
  2516. config = (struct configEntry *)list;
  2517. if (slapi_dn_issuffix(slapi_entry_get_dn_const(e), config->scope) &&
  2518. (slapi_filter_test_simple(e, config->filter) == 0)) {
  2519. if (slapi_is_shutting_down() ||
  2520. automember_update_membership(config, e, ldif_fd_out) == SLAPI_PLUGIN_FAILURE) {
  2521. result = SLAPI_DSE_CALLBACK_ERROR;
  2522. slapi_entry_free(e);
  2523. slapi_ch_free_string(&entrystr);
  2524. automember_config_unlock();
  2525. goto out;
  2526. }
  2527. }
  2528. list = PR_NEXT_LINK(list);
  2529. }
  2530. }
  2531. slapi_entry_free(e);
  2532. } else {
  2533. /* invalid entry */
  2534. slapi_task_log_notice(task, "Automember map task, skipping invalid entry.");
  2535. slapi_task_log_status(task, "Automember map task, skipping invalid entry.");
  2536. }
  2537. slapi_ch_free_string(&entrystr);
  2538. }
  2539. automember_config_unlock();
  2540. out:
  2541. if (ldif_fd_out) {
  2542. PR_Close(ldif_fd_out);
  2543. }
  2544. if (ldif_fd_in) {
  2545. #if defined(USE_OPENLDAP)
  2546. ldif_close(ldif_fd_in);
  2547. #else
  2548. fclose(ldif_fd_in);
  2549. #endif
  2550. }
  2551. slapi_task_inc_progress(task);
  2552. slapi_task_finish(task, result);
  2553. slapi_task_dec_refcount(task);
  2554. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2555. "automember_map_task_thread - Refcount decremented.\n");
  2556. }
  2557. /*
  2558. * automember_modrdn_post_op()
  2559. *
  2560. * Reloads config if config entries move
  2561. * into or out of our config scope.
  2562. */
  2563. static int
  2564. automember_modrdn_post_op(Slapi_PBlock *pb)
  2565. {
  2566. Slapi_Entry *post_e = NULL;
  2567. Slapi_DN *old_sdn = NULL;
  2568. Slapi_DN *new_sdn = NULL;
  2569. struct configEntry *config = NULL;
  2570. PRCList *list = NULL;
  2571. int rc = SLAPI_PLUGIN_SUCCESS;
  2572. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2573. "--> automember_modrdn_post_op\n");
  2574. /* Just bail if we aren't ready to service requests yet. */
  2575. if (!automember_oktodo(pb)) {
  2576. return SLAPI_PLUGIN_SUCCESS;
  2577. }
  2578. /*
  2579. * Reload config if an existing config entry was renamed,
  2580. * or if the new dn brings an entry into the scope of the
  2581. * config entries.
  2582. */
  2583. slapi_pblock_get(pb, SLAPI_ENTRY_POST_OP, &post_e);
  2584. if (post_e) {
  2585. new_sdn = slapi_entry_get_sdn(post_e);
  2586. } else {
  2587. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2588. "automember_modrdn_post_op - Error "
  2589. "retrieving post-op entry\n");
  2590. return SLAPI_PLUGIN_FAILURE;
  2591. }
  2592. if ((old_sdn = automember_get_sdn(pb))) {
  2593. if (automember_dn_is_config(old_sdn) || automember_dn_is_config(new_sdn)) {
  2594. automember_load_config();
  2595. }
  2596. } else {
  2597. slapi_log_err(SLAPI_LOG_PLUGIN, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2598. "automember_modrdn_post_op - Error "
  2599. "retrieving dn\n");
  2600. return SLAPI_PLUGIN_FAILURE;
  2601. }
  2602. /* If replication, just bail. */
  2603. if (automember_isrepl(pb)) {
  2604. return SLAPI_PLUGIN_SUCCESS;
  2605. }
  2606. /*
  2607. * Check if a config entry applies to the entry(post modrdn)
  2608. */
  2609. automember_config_read_lock();
  2610. if (!PR_CLIST_IS_EMPTY(g_automember_config)) {
  2611. list = PR_LIST_HEAD(g_automember_config);
  2612. while (list != g_automember_config) {
  2613. config = (struct configEntry *)list;
  2614. /* Does the entry meet scope and filter requirements? */
  2615. if (slapi_dn_issuffix(slapi_sdn_get_dn(new_sdn), config->scope) &&
  2616. (slapi_filter_test_simple(post_e, config->filter) == 0)) {
  2617. /* Find out what membership changes are needed and make them. */
  2618. if (automember_update_membership(config, post_e, NULL) == SLAPI_PLUGIN_FAILURE) {
  2619. rc = SLAPI_PLUGIN_FAILURE;
  2620. break;
  2621. }
  2622. }
  2623. list = PR_NEXT_LINK(list);
  2624. }
  2625. }
  2626. automember_config_unlock();
  2627. if (rc) {
  2628. char errtxt[SLAPI_DSE_RETURNTEXT_SIZE];
  2629. int result = LDAP_UNWILLING_TO_PERFORM;
  2630. PR_snprintf(errtxt, SLAPI_DSE_RETURNTEXT_SIZE, "Automember Plugin update unexpectedly failed. "
  2631. "Please see the server errors log for more information.\n");
  2632. slapi_pblock_set(pb, SLAPI_RESULT_CODE, &result);
  2633. slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, &errtxt);
  2634. }
  2635. slapi_log_err(SLAPI_LOG_TRACE, AUTOMEMBER_PLUGIN_SUBSYSTEM,
  2636. "<-- automember_modrdn_post_op (%d)\n", rc);
  2637. return rc;
  2638. }