sasl_map.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  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. #ifdef HAVE_CONFIG_H
  38. # include <config.h>
  39. #endif
  40. #include "slap.h"
  41. #include "slapi-plugin.h"
  42. #include "fe.h"
  43. /*
  44. * Map SASL identities to LDAP searches
  45. */
  46. /*
  47. * We maintain a list of mappings to consult
  48. */
  49. typedef struct sasl_map_data_ sasl_map_data;
  50. struct sasl_map_data_ {
  51. char *name;
  52. char *regular_expression;
  53. char *template_base_dn;
  54. char *template_search_filter;
  55. sasl_map_data *next; /* For linked list */
  56. };
  57. typedef struct _sasl_map_private {
  58. PRLock *lock;
  59. sasl_map_data *map_data_list;
  60. } sasl_map_private;
  61. static char * configDN = "cn=mapping,cn=sasl,cn=config";
  62. /*
  63. * DBDB: this is ugly, but right now there is _no_ server-wide
  64. * dynamic structure (like a Slapi_Server * type thing). All the code
  65. * that needs such a thing instead maintains a static or global variable.
  66. * Until we implement the 'right thing', we'll just follow suit here :(
  67. */
  68. static sasl_map_private *sasl_map_static_priv = NULL;
  69. static
  70. sasl_map_private *sasl_map_get_global_priv()
  71. {
  72. /* ASSERT(sasl_map_static_priv) */
  73. return sasl_map_static_priv;
  74. }
  75. static
  76. sasl_map_private *sasl_map_new_private()
  77. {
  78. PRLock *new_lock = PR_NewLock();
  79. sasl_map_private *new_priv = NULL;
  80. if (NULL == new_lock) {
  81. return NULL;
  82. }
  83. new_priv = (sasl_map_private *)slapi_ch_calloc(1,sizeof(sasl_map_private));
  84. new_priv->lock = new_lock;
  85. if (NULL == new_lock) {
  86. slapi_ch_free((void**)new_priv);
  87. return NULL;
  88. }
  89. return new_priv;
  90. }
  91. static void
  92. sasl_map_free_private(sasl_map_private **priv)
  93. {
  94. PR_DestroyLock((*priv)->lock);
  95. slapi_ch_free((void**)priv);
  96. *priv = NULL;
  97. }
  98. /* 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() */
  99. static
  100. sasl_map_data *sasl_map_new_data(char *name, char *regex, char *dntemplate, char *filtertemplate)
  101. {
  102. sasl_map_data *new_dp = (sasl_map_data *) slapi_ch_calloc(1,sizeof(sasl_map_data));
  103. new_dp->name = name;
  104. new_dp->regular_expression = regex;
  105. new_dp->template_base_dn = dntemplate;
  106. new_dp->template_search_filter = filtertemplate;
  107. return new_dp;
  108. }
  109. static
  110. sasl_map_data *sasl_map_next(sasl_map_data *dp)
  111. {
  112. return dp->next;
  113. }
  114. static void
  115. sasl_map_free_data(sasl_map_data **dp)
  116. {
  117. slapi_ch_free_string(&(*dp)->name);
  118. slapi_ch_free_string(&(*dp)->regular_expression);
  119. slapi_ch_free_string(&(*dp)->template_base_dn);
  120. slapi_ch_free_string(&(*dp)->template_search_filter);
  121. slapi_ch_free((void**)dp);
  122. }
  123. static int
  124. sasl_map_remove_list_entry(sasl_map_private *priv, char *removeme)
  125. {
  126. int ret = 0;
  127. int foundit = 0;
  128. sasl_map_data *current = NULL;
  129. sasl_map_data *previous = NULL;
  130. PR_Lock(priv->lock);
  131. current = priv->map_data_list;
  132. while (current) {
  133. if (0 == strcmp(current->name,removeme)) {
  134. foundit = 1;
  135. if (previous) {
  136. /* Unlink it */
  137. previous->next = current->next;
  138. } else {
  139. /* That was the first list entry */
  140. priv->map_data_list = current->next;
  141. }
  142. /* Payload free */
  143. sasl_map_free_data(&current);
  144. /* And no need to look further */
  145. break;
  146. }
  147. previous = current;
  148. current = current->next;
  149. }
  150. if (!foundit) {
  151. ret = -1;
  152. }
  153. PR_Unlock(priv->lock);
  154. return ret;
  155. }
  156. static int
  157. sasl_map_insert_list_entry(sasl_map_private *priv, sasl_map_data *dp)
  158. {
  159. int ret = 0;
  160. int ishere = 0;
  161. sasl_map_data *current = NULL;
  162. PR_Lock(priv->lock);
  163. /* Check to see if it's here already */
  164. current = priv->map_data_list;
  165. while (current && current->next) {
  166. current = current->next;
  167. }
  168. if (ishere) {
  169. return -1;
  170. }
  171. /* current now points to the end of the list or NULL */
  172. if (NULL == priv->map_data_list) {
  173. priv->map_data_list = dp;
  174. } else {
  175. current->next = dp;
  176. }
  177. PR_Unlock(priv->lock);
  178. return ret;
  179. }
  180. /*
  181. * Functions to handle config operations
  182. */
  183. /**
  184. * Get a list of child DNs
  185. * DBDB these functions should be folded into libslapd because it's a copy of a function in ssl.c
  186. */
  187. static char **
  188. getChildren( char *dn ) {
  189. Slapi_PBlock *new_pb = NULL;
  190. Slapi_Entry **e;
  191. int search_result = 1;
  192. int nEntries = 0;
  193. char **list = NULL;
  194. new_pb = slapi_search_internal ( dn, LDAP_SCOPE_ONELEVEL,
  195. "(objectclass=nsSaslMapping)",
  196. NULL, NULL, 0);
  197. slapi_pblock_get( new_pb, SLAPI_NENTRIES, &nEntries);
  198. if ( nEntries > 0 ) {
  199. slapi_pblock_get( new_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);
  200. slapi_pblock_get( new_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &e);
  201. if ( e != NULL ) {
  202. int i;
  203. list = (char **)slapi_ch_malloc( sizeof(*list) * (nEntries + 1));
  204. for ( i = 0; e[i] != NULL; i++ ) {
  205. list[i] = slapi_ch_strdup(slapi_entry_get_dn(e[i]));
  206. }
  207. list[nEntries] = NULL;
  208. }
  209. }
  210. slapi_free_search_results_internal(new_pb);
  211. slapi_pblock_destroy(new_pb );
  212. return list;
  213. }
  214. /**
  215. * Free a list of child DNs
  216. */
  217. static void
  218. freeChildren( char **list ) {
  219. if ( list != NULL ) {
  220. int i;
  221. for ( i = 0; list[i] != NULL; i++ ) {
  222. slapi_ch_free( (void **)(&list[i]) );
  223. }
  224. slapi_ch_free( (void **)(&list) );
  225. }
  226. }
  227. /**
  228. * Get a particular entry
  229. */
  230. static Slapi_Entry *
  231. getConfigEntry( const char *dn, Slapi_Entry **e2 ) {
  232. Slapi_DN sdn;
  233. slapi_sdn_init_dn_byref( &sdn, dn );
  234. slapi_search_internal_get_entry( &sdn, NULL, e2,
  235. plugin_get_default_component_id());
  236. slapi_sdn_done( &sdn );
  237. return *e2;
  238. }
  239. /**
  240. * Free an entry
  241. */
  242. static void
  243. freeConfigEntry( Slapi_Entry ** e ) {
  244. if ( (e != NULL) && (*e != NULL) ) {
  245. slapi_entry_free( *e );
  246. *e = NULL;
  247. }
  248. }
  249. static int
  250. sasl_map_config_parse_entry(Slapi_Entry *entry, sasl_map_data **new_dp)
  251. {
  252. int ret = 0;
  253. char *regex = NULL;
  254. char *basedntemplate = NULL;
  255. char *filtertemplate = NULL;
  256. char *map_name = NULL;
  257. *new_dp = NULL;
  258. regex = slapi_entry_attr_get_charptr( entry, "nsSaslMapRegexString" );
  259. basedntemplate = slapi_entry_attr_get_charptr( entry, "nsSaslMapBaseDNTemplate" );
  260. filtertemplate = slapi_entry_attr_get_charptr( entry, "nsSaslMapFilterTemplate" );
  261. map_name = slapi_entry_attr_get_charptr( entry, "cn" );
  262. if ( (NULL == map_name) || (NULL == regex) ||
  263. (NULL == basedntemplate) || (NULL == filtertemplate) ) {
  264. /* Invalid entry */
  265. ret = -1;
  266. } else {
  267. /* Make the new dp */
  268. *new_dp = sasl_map_new_data(map_name, regex, basedntemplate, filtertemplate);
  269. }
  270. if (ret) {
  271. slapi_ch_free((void **) &map_name);
  272. slapi_ch_free((void **) &regex);
  273. slapi_ch_free((void **) &basedntemplate);
  274. slapi_ch_free((void **) &filtertemplate);
  275. }
  276. return ret;
  277. }
  278. static int
  279. sasl_map_read_config_startup(sasl_map_private *priv)
  280. {
  281. char **map_entry_list = NULL;
  282. int ret = 0;
  283. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_read_config_startup\n", 0, 0, 0 );
  284. if((map_entry_list = getChildren(configDN))) {
  285. char **map_entry = NULL;
  286. Slapi_Entry *entry = NULL;
  287. sasl_map_data *dp = NULL;
  288. for (map_entry = map_entry_list; *map_entry && !ret; map_entry++) {
  289. getConfigEntry( *map_entry, &entry );
  290. if ( entry == NULL ) {
  291. continue;
  292. }
  293. ret = sasl_map_config_parse_entry(entry,&dp);
  294. if (ret) {
  295. LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_read_config_startup failed to parse entry\n", 0, 0, 0 );
  296. } else {
  297. ret = sasl_map_insert_list_entry(priv,dp);
  298. if (ret) {
  299. LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_read_config_startup failed to insert entry\n", 0, 0, 0 );
  300. }
  301. }
  302. freeConfigEntry( &entry );
  303. }
  304. freeChildren( map_entry_list );
  305. }
  306. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_read_config_startup\n", 0, 0, 0 );
  307. return ret;
  308. }
  309. int
  310. sasl_map_config_add(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
  311. {
  312. int ret = 0;
  313. sasl_map_data *dp = NULL;
  314. sasl_map_private *priv = sasl_map_get_global_priv();
  315. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_config_add\n", 0, 0, 0 );
  316. ret = sasl_map_config_parse_entry(entryBefore,&dp);
  317. if (!ret && dp) {
  318. ret = sasl_map_insert_list_entry(priv,dp);
  319. }
  320. if (0 == ret) {
  321. ret = SLAPI_DSE_CALLBACK_OK;
  322. } else {
  323. returntext = "sasl map entry rejected";
  324. *returncode = LDAP_UNWILLING_TO_PERFORM;
  325. ret = SLAPI_DSE_CALLBACK_ERROR;
  326. }
  327. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_config_add\n", 0, 0, 0 );
  328. return ret;
  329. }
  330. int
  331. sasl_map_config_delete(Slapi_PBlock *pb, Slapi_Entry* entryBefore, Slapi_Entry* e, int *returncode, char *returntext, void *arg)
  332. {
  333. int ret = 0;
  334. sasl_map_private *priv = sasl_map_get_global_priv();
  335. char *entry_name = NULL;
  336. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_config_delete\n", 0, 0, 0 );
  337. entry_name = slapi_entry_attr_get_charptr( entryBefore, "cn" );
  338. if (entry_name) {
  339. /* remove this entry from the list */
  340. ret = sasl_map_remove_list_entry(priv,entry_name);
  341. slapi_ch_free((void **) &entry_name);
  342. }
  343. if (ret) {
  344. ret = SLAPI_DSE_CALLBACK_ERROR;
  345. returntext = "can't delete sasl map entry";
  346. *returncode = LDAP_OPERATIONS_ERROR;
  347. } else {
  348. ret = SLAPI_DSE_CALLBACK_OK;
  349. }
  350. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_config_delete\n", 0, 0, 0 );
  351. return ret;
  352. }
  353. /* Start and stop the sasl mapping code */
  354. int sasl_map_init()
  355. {
  356. int ret = 0;
  357. sasl_map_private *priv = NULL;
  358. /* Make the private structure */
  359. priv = sasl_map_new_private();
  360. if (priv) {
  361. /* Store in the static var */
  362. sasl_map_static_priv = priv;
  363. /* Read the config on startup */
  364. ret = sasl_map_read_config_startup(priv);
  365. } else {
  366. ret = -1;
  367. }
  368. return ret;
  369. }
  370. int sasl_map_done()
  371. {
  372. int ret = 0;
  373. sasl_map_private *priv = sasl_map_get_global_priv();
  374. sasl_map_data *dp = NULL;
  375. /* Free the map list */
  376. PR_Lock(priv->lock);
  377. dp = priv->map_data_list;
  378. while (dp) {
  379. sasl_map_data *dp_next = dp->next;
  380. sasl_map_free_data(&dp);
  381. dp = dp_next;
  382. }
  383. PR_Unlock(priv->lock);
  384. /* Free the private structure */
  385. sasl_map_free_private(&priv);
  386. return ret;
  387. }
  388. static sasl_map_data*
  389. sasl_map_first(sasl_map_private *priv)
  390. {
  391. sasl_map_data *result = NULL;
  392. PR_Lock(priv->lock);
  393. result = priv->map_data_list;
  394. PR_Unlock(priv->lock);
  395. return result;
  396. }
  397. static int
  398. sasl_map_check(sasl_map_data *dp, char *sasl_user_and_realm, char **ldap_search_base, char **ldap_search_filter)
  399. {
  400. int ret = 0;
  401. int matched = 0;
  402. char *recomp_result = NULL;
  403. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_check\n", 0, 0, 0 );
  404. /* DBDB: currently using the rather old internal slapd regex library, which is not thread-safe */
  405. /* So lock it first */
  406. slapd_re_lock();
  407. /* Compile the regex */
  408. recomp_result = slapd_re_comp(dp->regular_expression);
  409. if (recomp_result) {
  410. LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_check : re_comp failed for expression (%s)\n", dp->regular_expression, 0, 0 );
  411. } else {
  412. matched = slapd_re_exec(sasl_user_and_realm, -1 /* no timelimit */);
  413. LDAPDebug( LDAP_DEBUG_TRACE, "regex: %s, id: %s, %s\n", dp->regular_expression, sasl_user_and_realm, matched ? "matched" : "didn't match" );
  414. }
  415. if (matched) {
  416. if (matched == 1) {
  417. char escape_base[BUFSIZ];
  418. char escape_filt[BUFSIZ];
  419. /* Allocate buffers for the returned strings */
  420. /* We already computed this, so we could pass it in to speed up a little */
  421. size_t userrealmlen = strlen(sasl_user_and_realm);
  422. /* These lengths could be precomputed and stored in the dp */
  423. *ldap_search_base = (char *) slapi_ch_malloc(userrealmlen + strlen(dp->template_base_dn) + 1);
  424. *ldap_search_filter = (char *) slapi_ch_malloc(userrealmlen + strlen(dp->template_search_filter) + 1);
  425. slapd_re_subs(dp->template_base_dn,*ldap_search_base);
  426. slapd_re_subs(dp->template_search_filter,*ldap_search_filter);
  427. /* these values are internal regex representations with lots of
  428. unprintable control chars - escape for logging */
  429. LDAPDebug( LDAP_DEBUG_TRACE, "mapped base dn: %s, filter: %s\n",
  430. escape_string( *ldap_search_base, escape_base ),
  431. escape_string( *ldap_search_filter, escape_filt ), 0 );
  432. ret = 1;
  433. } else {
  434. LDAPDebug( LDAP_DEBUG_ANY, "sasl_map_check : re_exec failed\n", 0, 0, 0 );
  435. }
  436. }
  437. slapd_re_unlock();
  438. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_check\n", 0, 0, 0 );
  439. return ret;
  440. }
  441. static char *
  442. sasl_map_str_concat(char *s1, char *s2)
  443. {
  444. if (NULL == s2) {
  445. return (slapi_ch_strdup(s1));
  446. } else {
  447. char *newstr = slapi_ch_smprintf("%s@%s",s1,s2);
  448. return newstr;
  449. }
  450. }
  451. /* Actually perform a mapping
  452. * Takes a sasl identity string, and returns an LDAP search spec to be used to find the entry
  453. * returns 1 if matched, 0 otherwise
  454. */
  455. int
  456. sasl_map_domap(char *sasl_user, char *sasl_realm, char **ldap_search_base, char **ldap_search_filter)
  457. {
  458. int ret = 0;
  459. sasl_map_data *this_map = NULL;
  460. char *sasl_user_and_realm = NULL;
  461. sasl_map_private *priv = sasl_map_get_global_priv();
  462. *ldap_search_base = NULL;
  463. *ldap_search_filter = NULL;
  464. LDAPDebug( LDAP_DEBUG_TRACE, "-> sasl_map_domap\n", 0, 0, 0 );
  465. sasl_user_and_realm = sasl_map_str_concat(sasl_user,sasl_realm);
  466. /* Walk the list of maps */
  467. this_map = sasl_map_first(priv);
  468. while (this_map) {
  469. int matched = 0;
  470. /* If one matches, then make the search params */
  471. matched = sasl_map_check(this_map, sasl_user_and_realm, ldap_search_base, ldap_search_filter);
  472. if (1 == matched) {
  473. ret = 1;
  474. break;
  475. }
  476. this_map = sasl_map_next(this_map);
  477. }
  478. if (sasl_user_and_realm) {
  479. slapi_ch_free((void**)&sasl_user_and_realm);
  480. }
  481. LDAPDebug( LDAP_DEBUG_TRACE, "<- sasl_map_domap (%s)\n", (1 == ret) ? "mapped" : "not mapped", 0, 0 );
  482. return ret;
  483. }