sasl_map.c 14 KB

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