saslbind.c 32 KB

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