saslbind.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2009 Red Hat, Inc.
  4. * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
  5. * All rights reserved.
  6. *
  7. * Contributors:
  8. * Hewlett-Packard Development Company, L.P.
  9. * Bugfix for bug #193297
  10. *
  11. * License: GPL (version 3 or any later version).
  12. * See LICENSE for details.
  13. * END COPYRIGHT BLOCK **/
  14. #ifdef HAVE_CONFIG_H
  15. # include <config.h>
  16. #endif
  17. #include <slap.h>
  18. #include <fe.h>
  19. #include <sasl.h>
  20. #include <saslplug.h>
  21. #include <unistd.h>
  22. static char *serverfqdn;
  23. /*
  24. * utility functions needed by the sasl library
  25. */
  26. void *nssasl_mutex_alloc(void)
  27. {
  28. return PR_NewLock();
  29. }
  30. int nssasl_mutex_lock(void *mutex)
  31. {
  32. if (mutex) {
  33. PR_Lock(mutex);
  34. }
  35. return SASL_OK;
  36. }
  37. int nssasl_mutex_unlock(void *mutex)
  38. {
  39. if (mutex) {
  40. if (PR_Unlock(mutex) == PR_SUCCESS) return SASL_OK;
  41. return SASL_FAIL;
  42. } else {
  43. return SASL_OK;
  44. }
  45. }
  46. void nssasl_mutex_free(void *mutex)
  47. {
  48. if (mutex) {
  49. PR_DestroyLock(mutex);
  50. }
  51. }
  52. void nssasl_free(void *ptr)
  53. {
  54. slapi_ch_free(&ptr);
  55. }
  56. static Slapi_ComponentId *sasl_component_id = NULL;
  57. static void generate_component_id(void)
  58. {
  59. if (NULL == sasl_component_id) {
  60. sasl_component_id = generate_componentid(NULL /* Not a plugin */,
  61. COMPONENT_SASL);
  62. }
  63. }
  64. static Slapi_ComponentId *sasl_get_component_id(void)
  65. {
  66. return sasl_component_id;
  67. }
  68. /*
  69. * sasl library callbacks
  70. */
  71. /*
  72. * We've added this auxprop stuff as a workaround for RHDS bug 166229
  73. * and FDS bug 166081. The problem is that sasldb is configured and
  74. * enabled by default, but we don't want or need to use it. What
  75. * happens after canon_user is that sasl looks up any auxiliary
  76. * properties of that user. If you don't tell sasl which auxprop
  77. * plug-in to use, it tries all of them, including sasldb. In order
  78. * to avoid this, we create a "dummy" auxprop plug-in with the name
  79. * "iDS" and tell sasl to use this plug-in for auxprop lookups.
  80. * The reason we don't need auxprops is because when we grab the user's
  81. * entry from the internal database, at the same time we get any other
  82. * properties we need - it's more efficient that way.
  83. */
  84. #if SASL_AUXPROP_PLUG_VERSION > 4
  85. static int ids_auxprop_lookup(
  86. #else
  87. static void ids_auxprop_lookup(
  88. #endif
  89. void *glob_context,
  90. sasl_server_params_t *sparams,
  91. unsigned flags,
  92. const char *user,
  93. unsigned ulen)
  94. {
  95. /* do nothing - we don't need auxprops - we just do this to avoid
  96. sasldb_auxprop_lookup */
  97. #if SASL_AUXPROP_PLUG_VERSION > 4
  98. return 0;
  99. #endif
  100. }
  101. static sasl_auxprop_plug_t ids_auxprop_plugin = {
  102. 0, /* Features */
  103. 0, /* spare */
  104. NULL, /* glob_context */
  105. NULL, /* auxprop_free */
  106. ids_auxprop_lookup, /* auxprop_lookup */
  107. "iDS", /* name */
  108. NULL /* auxprop_store */
  109. };
  110. int ids_auxprop_plug_init(const sasl_utils_t *utils,
  111. int max_version,
  112. int *out_version,
  113. sasl_auxprop_plug_t **plug,
  114. const char *plugname)
  115. {
  116. if(!out_version || !plug) return SASL_BADPARAM;
  117. if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
  118. *out_version = SASL_AUXPROP_PLUG_VERSION;
  119. *plug = &ids_auxprop_plugin;
  120. return SASL_OK;
  121. }
  122. static int ids_sasl_getopt(
  123. void *context,
  124. const char *plugin_name,
  125. const char *option,
  126. const char **result,
  127. unsigned *len
  128. )
  129. {
  130. unsigned tmplen;
  131. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_getopt", "plugin=%s option=%s\n",
  132. plugin_name ? plugin_name : "", option);
  133. if (len == NULL) len = &tmplen;
  134. *result = NULL;
  135. *len = 0;
  136. if (strcasecmp(option, "enable") == 0) {
  137. *result = "USERDB/DIGEST-MD5,GSSAPI/GSSAPI";
  138. } else if (strcasecmp(option, "has_plain_passwords") == 0) {
  139. *result = "yes";
  140. } else if (strcasecmp(option, "LOG_LEVEL") == 0) {
  141. if (loglevel_is_set(LDAP_DEBUG_TRACE)) {
  142. *result = "6"; /* SASL_LOG_TRACE */
  143. }
  144. } else if (strcasecmp(option, "auxprop_plugin") == 0) {
  145. *result = "iDS";
  146. } else if (strcasecmp(option, "mech_list") == 0){
  147. *result = config_get_allowed_sasl_mechs();
  148. }
  149. if (*result) *len = strlen(*result);
  150. return SASL_OK;
  151. }
  152. static int ids_sasl_log(
  153. void *context,
  154. int level,
  155. const char *message
  156. )
  157. {
  158. switch (level) {
  159. case SASL_LOG_ERR: /* log unusual errors (default) */
  160. slapi_log_err(SLAPI_LOG_ERR, "ids_sasl_log", "%s\n", message);
  161. break;
  162. case SASL_LOG_FAIL: /* log all authentication failures */
  163. case SASL_LOG_WARN: /* log non-fatal warnings */
  164. case SASL_LOG_NOTE: /* more verbose than LOG_WARN */
  165. case SASL_LOG_DEBUG: /* more verbose than LOG_NOTE */
  166. case SASL_LOG_TRACE: /* traces of internal protocols */
  167. case SASL_LOG_PASS: /* traces of internal protocols, including
  168. * passwords */
  169. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_log", "(%d): %s\n", level, message);
  170. break;
  171. case SASL_LOG_NONE: /* don't log anything */
  172. default:
  173. break;
  174. }
  175. return SASL_OK;
  176. }
  177. static void ids_sasl_user_search(
  178. char *basedn,
  179. int scope,
  180. char *filter,
  181. LDAPControl **ctrls,
  182. char **attrs,
  183. int attrsonly,
  184. Slapi_Entry **ep,
  185. int *foundp
  186. )
  187. {
  188. Slapi_Entry **entries = NULL;
  189. Slapi_PBlock *pb = NULL;
  190. int i, ret;
  191. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_search",
  192. "sasl user search basedn=\"%s\" filter=\"%s\"\n", basedn, filter);
  193. /* TODO: set size and time limits */
  194. pb = slapi_pblock_new();
  195. if (!pb) {
  196. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_search", "NULL pblock for search_internal_pb\n");
  197. goto out;
  198. }
  199. slapi_search_internal_set_pb(pb, basedn, scope, filter, attrs, attrsonly, ctrls,
  200. NULL, sasl_get_component_id(), 0);
  201. slapi_search_internal_pb(pb);
  202. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
  203. if (ret != LDAP_SUCCESS) {
  204. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_search",
  205. "sasl user search failed basedn=\"%s\" filter=\"%s\": %s\n",
  206. basedn, filter, ldap_err2string(ret));
  207. goto out;
  208. }
  209. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
  210. if ((entries == NULL) || (entries[0] == NULL)) {
  211. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_search",
  212. "sasl user search found no entries\n");
  213. goto out;
  214. }
  215. for (i = 0; entries[i]; i++) {
  216. (*foundp)++;
  217. if (*ep != NULL) {
  218. slapi_entry_free(*ep);
  219. }
  220. *ep = slapi_entry_dup(entries[i]);
  221. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_search",
  222. "sasl user search found dn=%s\n", slapi_entry_get_dn(*ep));
  223. }
  224. out:
  225. if (pb) {
  226. slapi_free_search_results_internal(pb);
  227. slapi_pblock_destroy(pb);
  228. pb = NULL;
  229. }
  230. return;
  231. }
  232. /*
  233. * Search for an entry representing the sasl user.
  234. */
  235. static Slapi_Entry *ids_sasl_user_to_entry(
  236. sasl_conn_t *conn,
  237. void *context,
  238. const char *user,
  239. const char *user_realm
  240. )
  241. {
  242. LDAPControl **ctrls = NULL;
  243. sasl_map_data *map = NULL;
  244. Slapi_Entry *entry = NULL;
  245. char **attrs = NULL;
  246. char *base = NULL;
  247. char *filter = NULL;
  248. int attrsonly = 0, scope = LDAP_SCOPE_SUBTREE;
  249. int regexmatch = 0;
  250. int found = 0;
  251. /* Check for wildcards in the authid and realm. If we encounter one,
  252. * just fail the mapping without performing a costly internal search. */
  253. if (user && strchr(user, '*')) {
  254. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_to_entry",
  255. "sasl user search encountered a wildcard in "
  256. "the authid. Not attempting to map to entry. (authid=%s)\n", user);
  257. return NULL;
  258. } else if (user_realm && strchr(user_realm, '*')) {
  259. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_to_entry",
  260. "sasl user search encountered a wildcard in "
  261. "the realm. Not attempting to map to entry. (realm=%s)\n", user_realm);
  262. return NULL;
  263. }
  264. /* New regex-based identity mapping */
  265. sasl_map_read_lock();
  266. while(1){
  267. regexmatch = sasl_map_domap(&map, (char*)user, (char*)user_realm, &base, &filter);
  268. if (regexmatch) {
  269. ids_sasl_user_search(base, scope, filter,
  270. ctrls, attrs, attrsonly,
  271. &entry, &found);
  272. if (found == 1) {
  273. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_to_entry",
  274. "sasl user search found this entry: dn:%s, matching filter=%s\n",
  275. entry->e_sdn.dn, filter);
  276. } else if (found == 0) {
  277. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_to_entry",
  278. "sasl user search found no entries matchingfilter=%s\n", filter);
  279. } else {
  280. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_user_to_entry",
  281. "sasl user search found more than one entry matching filter=%s\n", filter);
  282. if (entry) {
  283. slapi_entry_free(entry);
  284. entry = NULL;
  285. }
  286. }
  287. /* Free the filter etc */
  288. slapi_ch_free_string(&base);
  289. slapi_ch_free_string(&filter);
  290. /* If we didn't find an entry, look at the other maps */
  291. if(found){
  292. break;
  293. }
  294. }
  295. /* break if the next map is NULL, or we are not checking all the mappings */
  296. if(map == NULL || !config_get_sasl_mapping_fallback()){
  297. break;
  298. }
  299. }
  300. sasl_map_read_unlock();
  301. return entry;
  302. }
  303. static char *buf2str(const char *buf, unsigned buflen)
  304. {
  305. char *ret;
  306. ret = (char*)slapi_ch_malloc(buflen+1);
  307. memcpy(ret, buf, buflen);
  308. ret[buflen] = '\0';
  309. return ret;
  310. }
  311. /* Note that in this sasl1 API, when it says 'authid' it really means 'authzid'. */
  312. static int ids_sasl_canon_user(
  313. sasl_conn_t *conn,
  314. void *context,
  315. const char *userbuf, unsigned ulen,
  316. unsigned flags, const char *user_realm,
  317. char *out_user, unsigned out_umax, unsigned *out_ulen
  318. )
  319. {
  320. struct propctx *propctx = sasl_auxprop_getctx(conn);
  321. Slapi_Entry *entry = NULL;
  322. Slapi_DN *sdn = NULL;
  323. char *pw = NULL;
  324. char *user = NULL;
  325. char *mech = NULL;
  326. const char *dn;
  327. int isroot = 0;
  328. char *clear = NULL;
  329. int returnvalue = SASL_FAIL;
  330. user = buf2str(userbuf, ulen);
  331. if (user == NULL) {
  332. goto fail;
  333. }
  334. slapi_log_err(SLAPI_LOG_TRACE,
  335. "ids_sasl_canon_user", "(user=%s, realm=%s)\n",
  336. user, user_realm ? user_realm : "");
  337. sasl_getprop(conn, SASL_MECHNAME, (const void**)&mech);
  338. if (mech == NULL) {
  339. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_canon_user",
  340. "Unable to read SASL mechanism while canonifying user.\n");
  341. goto fail;
  342. }
  343. if (strncasecmp(user, "dn:", 3) == 0) {
  344. sdn = slapi_sdn_new();
  345. slapi_sdn_set_dn_byval(sdn, user+3);
  346. isroot = slapi_dn_isroot(slapi_sdn_get_ndn(sdn));
  347. }
  348. if (isroot) {
  349. /* special case directory manager */
  350. dn = slapi_sdn_get_ndn(sdn);
  351. pw = config_get_rootpw();
  352. *out_ulen = PR_snprintf(out_user, out_umax, "dn: %s", dn);
  353. } else if (strcasecmp(mech, "ANONYMOUS") == 0) {
  354. /* SASL doesn't allow us to set the username to an empty string,
  355. * so we just set it to anonymous. */
  356. dn = "anonymous";
  357. PL_strncpyz(out_user, dn, out_umax);
  358. /* the length of out_user needs to be set for Cyrus SASL */
  359. *out_ulen = strlen(out_user);
  360. } else {
  361. /* map the sasl username into an entry */
  362. entry = ids_sasl_user_to_entry(conn, context, user, user_realm);
  363. if (entry == NULL) {
  364. /* Specific return value is supposed to be set instead of
  365. an generic error (SASL_FAIL) for Cyrus SASL */
  366. returnvalue = SASL_NOAUTHZ;
  367. goto fail;
  368. }
  369. dn = slapi_entry_get_ndn(entry);
  370. pw = slapi_entry_attr_get_charptr(entry, "userpassword");
  371. *out_ulen = PR_snprintf(out_user, out_umax, "dn: %s", dn);
  372. }
  373. /* Need to set dn property to an empty string for the ANONYMOUS mechanism. This
  374. * property determines what the bind identity will be if authentication succeeds. */
  375. if (strcasecmp(mech, "ANONYMOUS") == 0) {
  376. if (prop_set(propctx, "dn", "", -1) != 0) {
  377. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_canon_user", "prop_set(dn) failed\n");
  378. goto fail;
  379. }
  380. } else if (prop_set(propctx, "dn", dn, -1) != 0) {
  381. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_canon_user", "prop_set(dn) failed\n");
  382. goto fail;
  383. }
  384. /* We need to check if the first character of pw is an opening
  385. * brace since strstr will simply return it's first argument if
  386. * it is an empty string. */
  387. if (pw && (*pw == '{')) {
  388. if (strchr( pw, '}' )) {
  389. /* This password is stored in a non-cleartext format.
  390. * Any SASL mechanism that actually needs the
  391. * password is going to fail. We should print a warning
  392. * to aid in troubleshooting. */
  393. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_canon_user",
  394. "Warning: Detected a sasl bind attempt by an "
  395. "entry whose password is stored in a non-cleartext format. This "
  396. "will not work for mechanisms which require a cleartext password "
  397. "such as DIGEST-MD5 and CRAM-MD5.\n");
  398. } else {
  399. /* This password doesn't have a storage prefix but
  400. * just happens to start with the '{' character. We'll
  401. * assume that it's just a cleartext password without
  402. * the proper storage prefix. */
  403. clear = pw;
  404. }
  405. } else {
  406. /* This password has no storage prefix, or the password is empty */
  407. clear = pw;
  408. }
  409. if (clear) {
  410. /* older versions of sasl do not have SASL_AUX_PASSWORD_PROP, so omit it */
  411. #ifdef SASL_AUX_PASSWORD_PROP
  412. if (prop_set(propctx, SASL_AUX_PASSWORD_PROP, clear, -1) != 0) {
  413. /* Failure is benign here because some mechanisms don't support this property */
  414. /*slapi_log_err(SLAPI_LOG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
  415. goto fail */ ;
  416. }
  417. #endif /* SASL_AUX_PASSWORD_PROP */
  418. if (prop_set(propctx, SASL_AUX_PASSWORD, clear, -1) != 0) {
  419. /* Failure is benign here because some mechanisms don't support this property */
  420. /*slapi_log_err(SLAPI_LOG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
  421. goto fail */ ;
  422. }
  423. }
  424. slapi_entry_free(entry);
  425. slapi_ch_free((void**)&user);
  426. slapi_ch_free((void**)&pw);
  427. slapi_sdn_free(&sdn);
  428. return SASL_OK;
  429. fail:
  430. slapi_entry_free(entry);
  431. slapi_ch_free((void**)&user);
  432. slapi_ch_free((void**)&pw);
  433. slapi_sdn_free(&sdn);
  434. return returnvalue;
  435. }
  436. static int ids_sasl_getpluginpath(sasl_conn_t *conn, const char **path)
  437. {
  438. /* Try to get path from config, otherwise check for SASL_PATH environment
  439. * variable. If neither of these are set, default to /usr/lib64/sasl2 on
  440. * 64-bit Linux machines, and /usr/lib/sasl2 on all other platforms.
  441. */
  442. char *pluginpath = config_get_saslpath();
  443. if ((!pluginpath) || (*pluginpath == '\0')) {
  444. slapi_ch_free_string(&pluginpath);
  445. pluginpath = ldaputil_get_saslpath();
  446. }
  447. *path = pluginpath;
  448. return SASL_OK;
  449. }
  450. static sasl_callback_t ids_sasl_callbacks[] =
  451. {
  452. {
  453. SASL_CB_GETOPT,
  454. (IFP) ids_sasl_getopt,
  455. NULL
  456. },
  457. {
  458. SASL_CB_LOG,
  459. (IFP) ids_sasl_log,
  460. NULL
  461. },
  462. {
  463. SASL_CB_CANON_USER,
  464. (IFP) ids_sasl_canon_user,
  465. NULL
  466. },
  467. {
  468. SASL_CB_GETPATH,
  469. (IFP) ids_sasl_getpluginpath,
  470. NULL
  471. },
  472. {
  473. SASL_CB_LIST_END,
  474. (IFP) NULL,
  475. NULL
  476. }
  477. };
  478. static const char *dn_propnames[] = { "dn", 0 };
  479. /*
  480. * initialize the sasl library
  481. */
  482. int ids_sasl_init(void)
  483. {
  484. static int inited = 0;
  485. int result;
  486. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_init", "=>\n");
  487. PR_ASSERT(inited == 0);
  488. if (inited != 0) {
  489. slapi_log_err(SLAPI_LOG_ERR, "ids_sasl_init", "Called more than once.\n");
  490. }
  491. inited = 1;
  492. serverfqdn = get_localhost_DNS();
  493. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_init", "sasl service fqdn is: %s\n",
  494. serverfqdn);
  495. /* get component ID for internal operations */
  496. generate_component_id();
  497. /* Set SASL memory allocation callbacks */
  498. sasl_set_alloc(
  499. (sasl_malloc_t *)slapi_ch_malloc,
  500. (sasl_calloc_t *)slapi_ch_calloc,
  501. (sasl_realloc_t *)slapi_ch_realloc,
  502. (sasl_free_t *)nssasl_free );
  503. /* Set SASL mutex callbacks */
  504. sasl_set_mutex(
  505. (sasl_mutex_alloc_t *)nssasl_mutex_alloc,
  506. (sasl_mutex_lock_t *)nssasl_mutex_lock,
  507. (sasl_mutex_unlock_t *)nssasl_mutex_unlock,
  508. (sasl_mutex_free_t *)nssasl_mutex_free);
  509. result = sasl_server_init(ids_sasl_callbacks, "iDS");
  510. if (result != SASL_OK) {
  511. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_init", "Failed to initialize sasl library\n");
  512. return result;
  513. }
  514. result = sasl_auxprop_add_plugin("iDS", ids_auxprop_plug_init);
  515. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_init", "<= \n");
  516. return result;
  517. }
  518. /*
  519. * create a sasl server connection
  520. */
  521. void ids_sasl_server_new(Connection *conn)
  522. {
  523. int rc;
  524. sasl_conn_t *sasl_conn = NULL;
  525. struct propctx *propctx;
  526. sasl_security_properties_t secprops = {0};
  527. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_server_new", "=> (%s)\n", serverfqdn);
  528. rc = sasl_server_new("ldap",
  529. serverfqdn,
  530. NULL, /* user_realm */
  531. NULL, /* iplocalport */
  532. NULL, /* ipremoteport */
  533. ids_sasl_callbacks,
  534. SASL_SUCCESS_DATA,
  535. &sasl_conn);
  536. if (rc != SASL_OK) {
  537. slapi_log_err(SLAPI_LOG_ERR, "ids_sasl_server_new", "%s\n",
  538. sasl_errstring(rc, NULL, NULL));
  539. }
  540. if (rc == SASL_OK) {
  541. propctx = sasl_auxprop_getctx(sasl_conn);
  542. if (propctx != NULL) {
  543. prop_request(propctx, dn_propnames);
  544. }
  545. }
  546. /* Enable security for this connection */
  547. secprops.maxbufsize = config_get_sasl_maxbufsize();
  548. secprops.max_ssf = 0xffffffff;
  549. secprops.min_ssf = config_get_minssf();
  550. /* If anonymous access is disabled, set the appropriate flag */
  551. if (config_get_anon_access_switch() != SLAPD_ANON_ACCESS_ON) {
  552. secprops.security_flags = SASL_SEC_NOANONYMOUS;
  553. }
  554. rc = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
  555. if (rc != SASL_OK) {
  556. slapi_log_err(SLAPI_LOG_ERR, "ids_sasl_server_new", "sasl_setprop: %s\n",
  557. sasl_errstring(rc, NULL, NULL));
  558. }
  559. conn->c_sasl_conn = sasl_conn;
  560. conn->c_sasl_ssf = 0;
  561. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_server_new", "<=\n");
  562. return;
  563. }
  564. /*
  565. * return sasl mechanisms available on this connection.
  566. * caller must free returned charray.
  567. */
  568. char **ids_sasl_listmech(Slapi_PBlock *pb)
  569. {
  570. char **ret, **others;
  571. const char *str;
  572. char *dupstr;
  573. sasl_conn_t *sasl_conn;
  574. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "=>\n");
  575. PR_ASSERT(pb);
  576. /* hard-wired mechanisms and slapi plugin registered mechanisms */
  577. ret = slapi_get_supported_saslmechanisms_copy();
  578. if (pb->pb_conn == NULL) return ret;
  579. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  580. if (sasl_conn == NULL) return ret;
  581. /* sasl library mechanisms are connection dependent */
  582. PR_EnterMonitor(pb->pb_conn->c_mutex);
  583. if (sasl_listmech(sasl_conn,
  584. NULL, /* username */
  585. "", ",", "",
  586. &str, NULL, NULL) == SASL_OK) {
  587. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "sasl library mechs: %s\n", str);
  588. /* merge into result set */
  589. dupstr = slapi_ch_strdup(str);
  590. others = slapi_str2charray_ext(dupstr, ",", 0 /* don't list duplicate mechanisms */);
  591. charray_merge(&ret, others, 1);
  592. charray_free(others);
  593. slapi_ch_free((void**)&dupstr);
  594. }
  595. PR_ExitMonitor(pb->pb_conn->c_mutex);
  596. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_listmech", "<=\n");
  597. return ret;
  598. }
  599. /*
  600. * Determine whether a given sasl mechanism is supported by
  601. * this sasl connection. Returns true/false.
  602. * NOTE: caller must lock pb->pb_conn->c_mutex
  603. */
  604. static int
  605. ids_sasl_mech_supported(Slapi_PBlock *pb, const char *mech)
  606. {
  607. int i, ret = 0;
  608. char **mechs;
  609. char *dupstr;
  610. const char *str;
  611. int sasl_result = 0;
  612. sasl_conn_t *sasl_conn = (sasl_conn_t *)pb->pb_conn->c_sasl_conn;
  613. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_mech_supported", "=>\n");
  614. /* sasl_listmech is not thread-safe - caller must lock pb_conn */
  615. sasl_result = sasl_listmech(sasl_conn,
  616. NULL, /* username */
  617. "", ",", "",
  618. &str, NULL, NULL);
  619. if (sasl_result != SASL_OK) {
  620. return 0;
  621. }
  622. dupstr = slapi_ch_strdup(str);
  623. mechs = slapi_str2charray(dupstr, ",");
  624. for (i = 0; mechs[i] != NULL; i++) {
  625. if (strcasecmp(mech, mechs[i]) == 0) {
  626. ret = 1;
  627. break;
  628. }
  629. }
  630. charray_free(mechs);
  631. slapi_ch_free((void**)&dupstr);
  632. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_mech_supported", "<=\n");
  633. return ret;
  634. }
  635. /*
  636. * do a sasl bind and return a result
  637. */
  638. void ids_sasl_check_bind(Slapi_PBlock *pb)
  639. {
  640. int rc, isroot;
  641. long t;
  642. sasl_conn_t *sasl_conn;
  643. struct propctx *propctx;
  644. sasl_ssf_t *ssfp;
  645. char *activemech = NULL, *mech = NULL;
  646. char *username, *dn = NULL;
  647. const char *normdn = NULL;
  648. Slapi_DN *sdn = NULL;
  649. const char *sdata, *errstr;
  650. unsigned slen;
  651. int continuing = 0;
  652. int pwresponse_requested = 0;
  653. LDAPControl **ctrls;
  654. struct berval bvr, *cred;
  655. struct propval dnval[2];
  656. char authtype[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */
  657. Slapi_Entry *bind_target_entry = NULL, *referral = NULL;
  658. Slapi_Backend *be = NULL;
  659. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_check_bind", "=>\n");
  660. PR_ASSERT(pb);
  661. PR_ASSERT(pb->pb_conn);
  662. PR_EnterMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  663. continuing = pb->pb_conn->c_flags & CONN_FLAG_SASL_CONTINUE;
  664. pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_CONTINUE; /* reset flag */
  665. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  666. if (sasl_conn == NULL) {
  667. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  668. send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  669. "sasl library unavailable", 0, NULL );
  670. return;
  671. }
  672. slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mech);
  673. if (NULL == mech) {
  674. rc = SASL_NOMECH;
  675. goto sasl_check_result;
  676. }
  677. slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);
  678. if (NULL == cred) {
  679. rc = SASL_BADPARAM;
  680. goto sasl_check_result;
  681. }
  682. slapi_pblock_get(pb, SLAPI_PWPOLICY, &pwresponse_requested);
  683. /* Work around a bug in the sasl library. We've told the
  684. * library that CRAM-MD5 is disabled, but it gives us a
  685. * different error code to SASL_NOMECH. Must be called
  686. * while holding the pb_conn lock
  687. */
  688. if (!ids_sasl_mech_supported(pb, mech)) {
  689. rc = SASL_NOMECH;
  690. goto sasl_check_result;
  691. }
  692. /* can't do any harm */
  693. if (cred->bv_len == 0) cred->bv_val = NULL;
  694. if (continuing) {
  695. /*
  696. * RFC 2251: a client may abort a sasl bind negotiation by
  697. * sending a bindrequest with a different value in the
  698. * mechanism field.
  699. */
  700. sasl_getprop(sasl_conn, SASL_MECHNAME, (const void**)&activemech);
  701. if (activemech == NULL) {
  702. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_check_bind",
  703. "Could not get active sasl mechanism\n");
  704. goto sasl_start;
  705. }
  706. if (strcasecmp(activemech, mech) != 0) {
  707. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_check_bind",
  708. "sasl mechanisms differ: active=%s current=%s\n", activemech, mech);
  709. goto sasl_start;
  710. }
  711. rc = sasl_server_step(sasl_conn,
  712. cred->bv_val, cred->bv_len,
  713. &sdata, &slen);
  714. goto sasl_check_result;
  715. }
  716. sasl_start:
  717. /* Check if we are already authenticated via sasl. If so,
  718. * dispose of the current sasl_conn and create a new one
  719. * using the new mechanism. We also need to do this if the
  720. * mechanism changed in the middle of the SASL authentication
  721. * process. */
  722. if ((pb->pb_conn->c_flags & CONN_FLAG_SASL_COMPLETE) || continuing) {
  723. Slapi_Operation *operation;
  724. slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
  725. slapi_log_err(SLAPI_LOG_CONNS, "ids_sasl_check_bind",
  726. "cleaning up sasl IO conn=%" NSPRIu64 " op=%d complete=%d continuing=%d\n",
  727. pb->pb_conn->c_connid, operation->o_opid,
  728. (pb->pb_conn->c_flags & CONN_FLAG_SASL_COMPLETE), continuing);
  729. /* reset flag */
  730. pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_COMPLETE;
  731. /* remove any SASL I/O from the connection */
  732. connection_set_io_layer_cb(pb->pb_conn, NULL, sasl_io_cleanup, NULL);
  733. /* dispose of sasl_conn and create a new sasl_conn */
  734. sasl_dispose(&sasl_conn);
  735. ids_sasl_server_new(pb->pb_conn);
  736. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  737. if (sasl_conn == NULL) {
  738. send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  739. "sasl library unavailable", 0, NULL );
  740. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  741. return;
  742. }
  743. }
  744. rc = sasl_server_start(sasl_conn, mech,
  745. cred->bv_val, cred->bv_len,
  746. &sdata, &slen);
  747. sasl_check_result:
  748. switch (rc) {
  749. case SASL_OK: /* complete */
  750. /* retrieve the authenticated username */
  751. if (sasl_getprop(sasl_conn, SASL_USERNAME,
  752. (const void**)&username) != SASL_OK) {
  753. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  754. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  755. "could not obtain sasl username", 0, NULL);
  756. break;
  757. }
  758. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_check_bind", "sasl authenticated mech=%s user=%s\n",
  759. mech, username);
  760. /*
  761. * Retrieve the DN corresponding to the authenticated user.
  762. * This should have been set by the user canon callback
  763. * in an auxiliary property called "dn".
  764. */
  765. propctx = sasl_auxprop_getctx(sasl_conn);
  766. if (prop_getnames(propctx, dn_propnames, dnval) == 1) {
  767. if (dnval[0].values && dnval[0].values[0]) {
  768. dn = slapi_ch_smprintf("%s", dnval[0].values[0]);
  769. }
  770. }
  771. if (dn == NULL) {
  772. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  773. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  774. "could not get auth dn from sasl", 0, NULL);
  775. break;
  776. }
  777. /* clean up already set TARGET */
  778. slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &sdn);
  779. slapi_sdn_free(&sdn);
  780. sdn = slapi_sdn_new_dn_passin(dn);
  781. normdn = slapi_sdn_get_dn(sdn);
  782. slapi_pblock_set( pb, SLAPI_BIND_TARGET_SDN, sdn );
  783. if ((sasl_getprop(sasl_conn, SASL_SSF,
  784. (const void**)&ssfp) == SASL_OK) && (*ssfp > 0)) {
  785. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_check_bind", "sasl ssf=%u\n", (unsigned)*ssfp);
  786. } else {
  787. *ssfp = 0;
  788. }
  789. /* Set a flag to signify that sasl bind is complete */
  790. pb->pb_conn->c_flags |= CONN_FLAG_SASL_COMPLETE;
  791. /* note - we set this here in case there are pre-bind
  792. plugins that want to know what the negotiated
  793. ssf is - but this happens before we actually set
  794. up the socket for SASL encryption - so one
  795. consequence is that we attempt to do sasl
  796. encryption on the connection after the pre-bind
  797. plugin has been called, and sasl encryption fails
  798. and the operation returns an error */
  799. pb->pb_conn->c_sasl_ssf = (unsigned)*ssfp;
  800. /* set the connection bind credentials */
  801. PR_snprintf(authtype, sizeof(authtype), "%s%s", SLAPD_AUTH_SASL, mech);
  802. /* normdn is consumed by bind_credentials_set_nolock */
  803. bind_credentials_set_nolock(pb->pb_conn, authtype,
  804. slapi_ch_strdup(normdn),
  805. NULL, NULL, NULL, bind_target_entry);
  806. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  807. if (plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) != 0){
  808. break;
  809. }
  810. isroot = slapi_dn_isroot(normdn);
  811. if (!isroot )
  812. {
  813. /* check if the account is locked */
  814. bind_target_entry = get_entry(pb, normdn);
  815. if ( bind_target_entry == NULL )
  816. {
  817. goto out;
  818. }
  819. if ( slapi_check_account_lock(pb, bind_target_entry, pwresponse_requested, 1, 1) == 1) {
  820. goto out;
  821. }
  822. }
  823. /* set the auth response control if requested */
  824. slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrls);
  825. if (slapi_control_present(ctrls, LDAP_CONTROL_AUTH_REQUEST,
  826. NULL, NULL)) {
  827. slapi_add_auth_response_control(pb, normdn);
  828. }
  829. if (slapi_mapping_tree_select(pb, &be, &referral, NULL, 0) != LDAP_SUCCESS) {
  830. send_nobackend_ldap_result( pb );
  831. be = NULL;
  832. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_check_bind", "<=\n");
  833. return;
  834. }
  835. if (referral) {
  836. send_referrals_from_entry(pb,referral);
  837. goto out;
  838. }
  839. slapi_pblock_set( pb, SLAPI_BACKEND, be );
  840. slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
  841. set_db_default_result_handlers(pb);
  842. /* check password expiry */
  843. if (!isroot) {
  844. int pwrc;
  845. pwrc = need_new_pw(pb, &t, bind_target_entry, pwresponse_requested);
  846. switch (pwrc) {
  847. case 1:
  848. slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
  849. break;
  850. case 2:
  851. slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);
  852. break;
  853. case -1:
  854. goto out;
  855. default:
  856. break;
  857. }
  858. }
  859. /* attach the sasl data */
  860. if (slen != 0) {
  861. bvr.bv_val = (char*)sdata;
  862. bvr.bv_len = slen;
  863. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
  864. }
  865. /* see if we negotiated a security layer */
  866. if (*ssfp > 0) {
  867. /* Enable SASL I/O on the connection */
  868. PR_EnterMonitor(pb->pb_conn->c_mutex);
  869. connection_set_io_layer_cb(pb->pb_conn, sasl_io_enable, NULL, NULL);
  870. PR_ExitMonitor(pb->pb_conn->c_mutex);
  871. }
  872. /* send successful result */
  873. send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
  874. /* remove the sasl data from the pblock */
  875. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
  876. break;
  877. case SASL_CONTINUE: /* another step needed */
  878. pb->pb_conn->c_flags |= CONN_FLAG_SASL_CONTINUE;
  879. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  880. if (plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) != 0){
  881. break;
  882. }
  883. /* attach the sasl data */
  884. bvr.bv_val = (char*)sdata;
  885. bvr.bv_len = slen;
  886. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
  887. /* send continuation result */
  888. send_ldap_result( pb, LDAP_SASL_BIND_IN_PROGRESS, NULL,
  889. NULL, 0, NULL );
  890. /* remove the sasl data from the pblock */
  891. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
  892. break;
  893. case SASL_NOMECH:
  894. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  895. send_ldap_result(pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  896. "sasl mechanism not supported", 0, NULL);
  897. break;
  898. default: /* other error */
  899. errstr = sasl_errdetail(sasl_conn);
  900. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  901. slapi_pblock_set(pb, SLAPI_PB_RESULT_TEXT, (void *)errstr);
  902. send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL, NULL, 0, NULL);
  903. break;
  904. }
  905. out:
  906. if (referral)
  907. slapi_entry_free(referral);
  908. if (be)
  909. slapi_be_Unlock(be);
  910. if (bind_target_entry)
  911. slapi_entry_free(bind_target_entry);
  912. slapi_log_err(SLAPI_LOG_TRACE, "ids_sasl_check_bind", "<=\n");
  913. return;
  914. }