saslbind.c 35 KB

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