sasl_map.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2005 Red Hat, Inc.
  35. * All rights reserved.
  36. * END COPYRIGHT BLOCK **/
  37. #include "slap.h"
  38. #include "slapi-plugin.h"
  39. #include "fe.h"
  40. /*
  41. * Map SASL identities to LDAP searches
  42. */
  43. /*
  44. * We maintain a list of mappings to consult
  45. */
  46. typedef struct sasl_map_data_ sasl_map_data;
  47. struct sasl_map_data_ {
  48. char *name;
  49. char *regular_expression;
  50. char *template_base_dn;
  51. char *template_search_filter;
  52. sasl_map_data *next; /* For linked list */
  53. };
  54. typedef struct _sasl_map_private {
  55. PRLock *lock;
  56. sasl_map_data *map_data_list;
  57. } sasl_map_private;
  58. static char * configDN = "cn=mapping,cn=sasl,cn=config";
  59. /*
  60. * DBDB: this is ugly, but right now there is _no_ server-wide
  61. * dynamic structure (like a Slapi_Server * type thing). All the code
  62. * that needs such a thing instead maintains a static or global variable.
  63. * Until we implement the 'right thing', we'll just follow suit here :(
  64. */
  65. static sasl_map_private *sasl_map_static_priv = NULL;
  66. static
  67. sasl_map_private *sasl_map_get_global_priv()
  68. {
  69. /* ASSERT(sasl_map_static_priv) */
  70. return sasl_map_static_priv;
  71. }
  72. static
  73. sasl_map_private *sasl_map_new_private()
  74. {
  75. PRLock *new_lock = PR_NewLock();
  76. sasl_map_private *new_priv = NULL;
  77. if (NULL == new_lock) {
  78. return NULL;
  79. }
  80. new_priv = (sasl_map_private *)slapi_ch_calloc(1,sizeof(sasl_map_private));
  81. new_priv->lock = new_lock;
  82. if (NULL == new_lock) {
  83. slapi_ch_free((void**)new_priv);
  84. return NULL;
  85. }
  86. return new_priv;
  87. }
  88. #if 0 /* unused for now */
  89. static void
  90. sasl_map_free_private(sasl_map_private **priv)
  91. {
  92. PR_DestroyLock((*priv)->lock);
  93. slapi_ch_free((void**)priv);
  94. *priv = NULL;
  95. }
  96. #endif
  97. /* This function does a shallow copy on the payload data supplied, so the caller should not free it, and it needs to be allocated using slapi_ch_malloc() */
  98. static
  99. sasl_map_data *sasl_map_new_data(char *name, char *regex, char *dntemplate, char *filtertemplate)
  100. {
  101. sasl_map_data *new_dp = (sasl_map_data *) slapi_ch_calloc(1,sizeof(sasl_map_data));
  102. new_dp->name = name;
  103. new_dp->regular_expression = regex;
  104. new_dp->template_base_dn = dntemplate;
  105. new_dp->template_search_filter = filtertemplate;
  106. return new_dp;
  107. }
  108. static
  109. sasl_map_data *sasl_map_next(sasl_map_data *dp)
  110. {
  111. return dp->next;
  112. }
  113. static void
  114. sasl_map_free_data(sasl_map_data **dp)
  115. {
  116. slapi_ch_free((void**)dp);
  117. }
  118. static int
  119. sasl_map_remove_list_entry(sasl_map_private *priv, char *removeme)
  120. {
  121. int ret = 0;
  122. int foundit = 0;
  123. sasl_map_data *current = NULL;
  124. sasl_map_data *previous = NULL;
  125. PR_Lock(priv->lock);
  126. current = priv->map_data_list;
  127. while (current) {
  128. if (0 == strcmp(current->name,removeme)) {
  129. foundit = 1;
  130. if (previous) {
  131. /* Unlink it */
  132. previous->next = current->next;
  133. } else {
  134. /* That was the only entry, and now there are none */
  135. priv->map_data_list = NULL;
  136. }
  137. /* Payload free */
  138. sasl_map_free_data(&current);
  139. /* And no need to look further */
  140. break;
  141. }
  142. previous = current;
  143. current = current->next;
  144. }
  145. if (!foundit) {
  146. ret = -1;
  147. }
  148. PR_Unlock(priv->lock);
  149. return ret;
  150. }
  151. static int
  152. sasl_map_insert_list_entry(sasl_map_private *priv, sasl_map_data *dp)
  153. {
  154. int ret = 0;
  155. int ishere = 0;
  156. sasl_map_data *current = NULL;
  157. PR_Lock(priv->lock);
  158. /* Check to see if it's here already */
  159. current = priv->map_data_list;
  160. while (current && current->next) {
  161. current = current->next;
  162. }
  163. if (ishere) {
  164. return -1;
  165. }
  166. /* current now points to the end of the list or NULL */
  167. if (NULL == priv->map_data_list) {
  168. priv->map_data_list = dp;
  169. } else {
  170. current->next = dp;
  171. }
  172. PR_Unlock(priv->lock);
  173. return ret;
  174. }
  175. /*
  176. * Functions to handle config operations
  177. */
  178. /**
  179. * Get a list of child DNs
  180. * DBDB these functions should be folded into libslapd because it's a copy of a function in ssl.c
  181. */
  182. static char **
  183. getChildren( char *dn ) {
  184. Slapi_PBlock *new_pb = NULL;
  185. Slapi_Entry **e;
  186. int search_result = 1;
  187. int nEntries = 0;
  188. char **list = NULL;
  189. new_pb = slapi_search_internal ( dn, LDAP_SCOPE_ONELEVEL,
  190. "(objectclass=nsSaslMapping)",
  191. NULL, NULL, 0);
  192. slapi_pblock_get( new_pb, SLAPI_NENTRIES, &nEntries);
  193. if ( nEntries > 0 ) {
  194. slapi_pblock_get( new_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);
  195. slapi_pblock_get( new_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &e);
  196. if ( e != NULL ) {
  197. int i;
  198. list = (char **)slapi_ch_malloc( sizeof(*list) * (nEntries + 1));
  199. for ( i = 0; e[i] != NULL; i++ ) {
  200. list[i] = slapi_ch_strdup(slapi_entry_get_dn(e[i]));
  201. }
  202. list[nEntries] = NULL;
  203. }
  204. }
  205. slapi_free_search_results_internal(new_pb);
  206. slapi_pblock_destroy(new_pb );
  207. return list;
  208. }
  209. /**
  210. * Free a list of child DNs
  211. */
  212. static void
  213. freeChildren( char **list ) {
  214. if ( list != NULL ) {
  215. int i;
  216. for ( i = 0; list[i] != NULL; i++ ) {
  217. slapi_ch_free( (void **)(&list[i]) );
  218. }
  219. slapi_ch_free( (void **)(&list) );
  220. }
  221. }
  222. /**
  223. * Get a particular entry
  224. */
  225. static Slapi_Entry *
  226. getConfigEntry( const char *dn, Slapi_Entry **e2 ) {
  227. Slapi_DN sdn;
  228. slapi_sdn_init_dn_byref( &sdn, dn );
  229. slapi_search_internal_get_entry( &sdn, NULL, e2,
  230. plugin_get_default_component_id());
  231. slapi_sdn_done( &sdn );
  232. return *e2;
  233. }
  234. /**
  235. * Free an entry
  236. */
  237. static void
  238. freeConfigEntry( Slapi_Entry ** e ) {
  239. if ( (e != NULL) && (*e != NULL) ) {
  240. slapi_entry_free( *e );
  241. *e = NULL;
  242. }
  243. }
  244. static int
  245. sasl_map_config_parse_entry(Slapi_Entry *entry, sasl_map_data **new_dp)
  246. {
  247. int ret = 0;
  248. char *regex = NULL;
  249. char *basedntemplate = NULL;
  250. char *filtertemplate = NULL;
  251. char *map_name = NULL;
  252. *new_dp = NULL;
  253. regex = slapi_entry_attr_get_charptr( entry, "nsSaslMapRegexString" );
  254. basedntemplate = slapi_entry_attr_get_charptr( entry, "nsSaslMapBaseDNTemplate" );
  255. filtertemplate = slapi_entry_attr_get_charptr( entry, "nsSaslMapFilterTemplate" );
  256. map_name = slapi_entry_attr_get_charptr( entry, "cn" );
  257. if ( (NULL == regex) || (NULL == basedntemplate) || (NULL == filtertemplate) ) {
  258. /* Invalid entry */
  259. ret = -1;
  260. } else {
  261. /* Make the new dp */
  262. *new_dp = sasl_map_new_data(map_name, regex, basedntemplate, filtertemplate);
  263. }
  264. if (ret) {
  265. slapi_ch_free((void **) &regex);
  266. slapi_ch_free((void **) &basedntemplate);
  267. slapi_ch_free((void **) &filtertemplate);
  268. }
  269. return ret;
  270. }
  271. static int
  272. sasl_map_read_config_startup(sasl_map_private *priv)
  273. {
  274. char **map_entry_list = NULL;
  275. int ret = 0;
  276. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_read_config_startup\n", 0, 0, 0 );
  277. if((map_entry_list = getChildren(configDN))) {
  278. char **map_entry = NULL;
  279. Slapi_Entry *entry = NULL;
  280. sasl_map_data *dp = NULL;
  281. for (map_entry = map_entry_list; *map_entry && !ret; map_entry++) {
  282. getConfigEntry( *map_entry, &entry );
  283. if ( entry == NULL ) {
  284. continue;
  285. }
  286. ret = sasl_map_config_parse_entry(entry,&dp);
  287. if (ret) {
  288. LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_read_config_startup failed to parse entry\n", 0, 0, 0 );
  289. } else {
  290. ret = sasl_map_insert_list_entry(priv,dp);
  291. if (ret) {
  292. LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_read_config_startup failed to insert entry\n", 0, 0, 0 );
  293. }
  294. }
  295. freeConfigEntry( &entry );
  296. }
  297. freeChildren( map_entry_list );
  298. }
  299. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_read_config_startup\n", 0, 0, 0 );
  300. return ret;
  301. }
  302. int
  303. sasl_map_config_add(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
  304. {
  305. int ret = 0;
  306. sasl_map_data *dp = NULL;
  307. sasl_map_private *priv = sasl_map_get_global_priv();
  308. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_config_add\n", 0, 0, 0 );
  309. ret = sasl_map_config_parse_entry(entryBefore,&dp);
  310. if (!ret && dp) {
  311. ret = sasl_map_insert_list_entry(priv,dp);
  312. }
  313. if (0 == ret) {
  314. ret = SLAPI_DSE_CALLBACK_OK;
  315. } else {
  316. returntext = "sasl map entry rejected";
  317. *returncode = LDAP_UNWILLING_TO_PERFORM;
  318. ret = SLAPI_DSE_CALLBACK_ERROR;
  319. }
  320. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_config_add\n", 0, 0, 0 );
  321. return ret;
  322. }
  323. int
  324. sasl_map_config_delete(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
  325. {
  326. int ret = 0;
  327. sasl_map_private *priv = sasl_map_get_global_priv();
  328. char *entry_name = NULL;
  329. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_config_delete\n", 0, 0, 0 );
  330. entry_name = slapi_entry_attr_get_charptr( entryBefore, "cn" );
  331. if (entry_name) {
  332. /* remove this entry from the list */
  333. ret = sasl_map_remove_list_entry(priv,entry_name);
  334. slapi_ch_free((void **) &entry_name);
  335. }
  336. if (ret) {
  337. ret = SLAPI_DSE_CALLBACK_ERROR;
  338. returntext = "can't delete sasl map entry";
  339. *returncode = LDAP_OPERATIONS_ERROR;
  340. } else {
  341. ret = SLAPI_DSE_CALLBACK_OK;
  342. }
  343. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_config_delete\n", 0, 0, 0 );
  344. return ret;
  345. }
  346. /* Start and stop the sasl mapping code */
  347. int sasl_map_init()
  348. {
  349. int ret = 0;
  350. sasl_map_private *priv = NULL;
  351. /* Make the private structure */
  352. priv = sasl_map_new_private();
  353. if (priv) {
  354. /* Store in the static var */
  355. sasl_map_static_priv = priv;
  356. /* Read the config on startup */
  357. ret = sasl_map_read_config_startup(priv);
  358. } else {
  359. ret = -1;
  360. }
  361. return ret;
  362. }
  363. int sasl_map_done()
  364. {
  365. int ret = 0;
  366. /* Free the map list */
  367. /* Free the private structure */
  368. return ret;
  369. }
  370. static sasl_map_data*
  371. sasl_map_first(sasl_map_private *priv)
  372. {
  373. sasl_map_data *result = NULL;
  374. PR_Lock(priv->lock);
  375. result = priv->map_data_list;
  376. PR_Unlock(priv->lock);
  377. return result;
  378. }
  379. static int
  380. sasl_map_check(sasl_map_data *dp, char *sasl_user_and_realm, char **ldap_search_base, char **ldap_search_filter)
  381. {
  382. int ret = 0;
  383. int matched = 0;
  384. char *recomp_result = NULL;
  385. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_check\n", 0, 0, 0 );
  386. /* DBDB: currently using the rather old internal slapd regex library, which is not thread-safe */
  387. /* So lock it first */
  388. slapd_re_lock();
  389. /* Compile the regex */
  390. recomp_result = slapd_re_comp(dp->regular_expression);
  391. if (recomp_result) {
  392. LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_check : re_comp failed for expression (%s)\n", dp->regular_expression, 0, 0 );
  393. }
  394. matched = slapd_re_exec(sasl_user_and_realm);
  395. LDAPDebug( LDAP_DEBUG_TRACE, "regex: %s, id: %s, %s\n", dp->regular_expression, sasl_user_and_realm, matched ? "matched" : "didn't match" );
  396. if (matched) {
  397. if (matched == 1) {
  398. /* Allocate buffers for the returned strings */
  399. /* We already computed this, so we could pass it in to speed up a little */
  400. size_t userrealmlen = strlen(sasl_user_and_realm);
  401. /* These lengths could be precomputed and stored in the dp */
  402. *ldap_search_base = (char *) slapi_ch_malloc(userrealmlen + strlen(dp->template_base_dn) + 1);
  403. *ldap_search_filter = (char *) slapi_ch_malloc(userrealmlen + strlen(dp->template_search_filter) + 1);
  404. slapd_re_subs(dp->template_base_dn,*ldap_search_base);
  405. slapd_re_subs(dp->template_search_filter,*ldap_search_filter);
  406. LDAPDebug( LDAP_DEBUG_TRACE, "mapped base dn: %s, filter: %s\n", ldap_search_base, ldap_search_filter, 0 );
  407. ret = 1;
  408. } else {
  409. LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_check : re_exec failed\n", 0, 0, 0 );
  410. }
  411. }
  412. slapd_re_unlock();
  413. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_check\n", 0, 0, 0 );
  414. return ret;
  415. }
  416. static char *
  417. sasl_map_str_concat(char *s1, char *s2)
  418. {
  419. if (NULL == s2) {
  420. return (slapi_ch_strdup(s1));
  421. } else {
  422. char *newstr = slapi_ch_smprintf("%s@%s",s1,s2);
  423. return newstr;
  424. }
  425. }
  426. /* Actually perform a mapping
  427. * Takes a sasl identity string, and returns an LDAP search spec to be used to find the entry
  428. * returns 1 if matched, 0 otherwise
  429. */
  430. int
  431. sasl_map_domap(char *sasl_user, char *sasl_realm, char **ldap_search_base, char **ldap_search_filter)
  432. {
  433. int ret = 0;
  434. sasl_map_data *this_map = NULL;
  435. char *sasl_user_and_realm = NULL;
  436. sasl_map_private *priv = sasl_map_get_global_priv();
  437. *ldap_search_base = NULL;
  438. *ldap_search_filter = NULL;
  439. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_domap\n", 0, 0, 0 );
  440. sasl_user_and_realm = sasl_map_str_concat(sasl_user,sasl_realm);
  441. /* Walk the list of maps */
  442. this_map = sasl_map_first(priv);
  443. while (this_map) {
  444. int matched = 0;
  445. /* If one matches, then make the search params */
  446. matched = sasl_map_check(this_map, sasl_user_and_realm, ldap_search_base, ldap_search_filter);
  447. if (1 == matched) {
  448. ret = 1;
  449. break;
  450. }
  451. this_map = sasl_map_next(this_map);
  452. }
  453. if (sasl_user_and_realm) {
  454. slapi_ch_free((void**)&sasl_user_and_realm);
  455. }
  456. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_domap (%s)\n", (1 == ret) ? "mapped" : "not mapped", 0, 0 );
  457. return ret;
  458. }