saslbind.c 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068
  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()
  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()
  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. LDAPDebug(LDAP_DEBUG_TRACE, "ids_sasl_getopt: plugin=%s option=%s\n",
  132. plugin_name ? plugin_name : "", option, 0);
  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 (LDAPDebugLevelIsSet(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_error(SLAPI_LOG_FATAL, "sasl", "%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. LDAPDebug(LDAP_DEBUG_TRACE, "sasl(%d): %s\n", level, message, 0);
  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. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search basedn=\"%s\" filter=\"%s\"\n", basedn, filter, 0);
  192. /* TODO: set size and time limits */
  193. pb = slapi_pblock_new();
  194. if (!pb) {
  195. LDAPDebug(LDAP_DEBUG_TRACE, "null pblock for search_internal_pb\n", 0, 0, 0);
  196. goto out;
  197. }
  198. slapi_search_internal_set_pb(pb, basedn, scope, filter, attrs, attrsonly, ctrls,
  199. NULL, sasl_get_component_id(), 0);
  200. slapi_search_internal_pb(pb);
  201. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
  202. if (ret != LDAP_SUCCESS) {
  203. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search failed basedn=\"%s\" "
  204. "filter=\"%s\": %s\n",
  205. basedn, filter, ldap_err2string(ret));
  206. goto out;
  207. }
  208. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
  209. if ((entries == NULL) || (entries[0] == NULL)) {
  210. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found no entries\n",
  211. 0, 0, 0);
  212. goto out;
  213. }
  214. for (i = 0; entries[i]; i++) {
  215. (*foundp)++;
  216. if (*ep != NULL) {
  217. slapi_entry_free(*ep);
  218. }
  219. *ep = slapi_entry_dup(entries[i]);
  220. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found dn=%s\n",
  221. slapi_entry_get_dn(*ep), 0, 0);
  222. }
  223. out:
  224. if (pb) {
  225. slapi_free_search_results_internal(pb);
  226. slapi_pblock_destroy(pb);
  227. pb = NULL;
  228. }
  229. return;
  230. }
  231. /*
  232. * Search for an entry representing the sasl user.
  233. */
  234. static Slapi_Entry *ids_sasl_user_to_entry(
  235. sasl_conn_t *conn,
  236. void *context,
  237. const char *user,
  238. const char *user_realm
  239. )
  240. {
  241. LDAPControl **ctrls = NULL;
  242. sasl_map_data *map = NULL;
  243. Slapi_Entry *entry = NULL;
  244. char **attrs = NULL;
  245. char *base = NULL;
  246. char *filter = NULL;
  247. int attrsonly = 0, scope = LDAP_SCOPE_SUBTREE;
  248. int regexmatch = 0;
  249. int found = 0;
  250. /* Check for wildcards in the authid and realm. If we encounter one,
  251. * just fail the mapping without performing a costly internal search. */
  252. if (user && strchr(user, '*')) {
  253. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search encountered a wildcard in "
  254. "the authid. Not attempting to map to entry. (authid=%s)\n", user, 0, 0);
  255. return NULL;
  256. } else if (user_realm && strchr(user_realm, '*')) {
  257. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search encountered a wildcard in "
  258. "the realm. Not attempting to map to entry. (realm=%s)\n", user_realm, 0, 0);
  259. return NULL;
  260. }
  261. /* New regex-based identity mapping */
  262. sasl_map_read_lock();
  263. while(1){
  264. regexmatch = sasl_map_domap(&map, (char*)user, (char*)user_realm, &base, &filter);
  265. if (regexmatch) {
  266. ids_sasl_user_search(base, scope, filter,
  267. ctrls, attrs, attrsonly,
  268. &entry, &found);
  269. if (found == 1) {
  270. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found this entry: dn:%s, "
  271. "matching filter=%s\n", entry->e_sdn.dn, filter, 0);
  272. } else if (found == 0) {
  273. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found no entries matching "
  274. "filter=%s\n", filter, 0, 0);
  275. } else {
  276. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found more than one entry "
  277. "matching filter=%s\n", filter, 0, 0);
  278. if (entry) {
  279. slapi_entry_free(entry);
  280. entry = NULL;
  281. }
  282. }
  283. /* Free the filter etc */
  284. slapi_ch_free_string(&base);
  285. slapi_ch_free_string(&filter);
  286. /* If we didn't find an entry, look at the other maps */
  287. if(found){
  288. break;
  289. }
  290. }
  291. /* break if the next map is NULL, or we are not checking all the mappings */
  292. if(map == NULL || !config_get_sasl_mapping_fallback()){
  293. break;
  294. }
  295. }
  296. sasl_map_read_unlock();
  297. return entry;
  298. }
  299. static char *buf2str(const char *buf, unsigned buflen)
  300. {
  301. char *ret;
  302. ret = (char*)slapi_ch_malloc(buflen+1);
  303. memcpy(ret, buf, buflen);
  304. ret[buflen] = '\0';
  305. return ret;
  306. }
  307. /* Note that in this sasl1 API, when it says 'authid' it really means 'authzid'. */
  308. static int ids_sasl_canon_user(
  309. sasl_conn_t *conn,
  310. void *context,
  311. const char *userbuf, unsigned ulen,
  312. unsigned flags, const char *user_realm,
  313. char *out_user, unsigned out_umax, unsigned *out_ulen
  314. )
  315. {
  316. struct propctx *propctx = sasl_auxprop_getctx(conn);
  317. Slapi_Entry *entry = NULL;
  318. Slapi_DN *sdn = NULL;
  319. char *pw = NULL;
  320. char *user = NULL;
  321. char *mech = NULL;
  322. const char *dn;
  323. int isroot = 0;
  324. char *clear = NULL;
  325. int returnvalue = SASL_FAIL;
  326. user = buf2str(userbuf, ulen);
  327. if (user == NULL) {
  328. goto fail;
  329. }
  330. LDAPDebug(LDAP_DEBUG_TRACE,
  331. "ids_sasl_canon_user(user=%s, realm=%s)\n",
  332. user, user_realm ? user_realm : "", 0);
  333. sasl_getprop(conn, SASL_MECHNAME, (const void**)&mech);
  334. if (mech == NULL) {
  335. LDAPDebug0Args(LDAP_DEBUG_TRACE, "Unable to read SASL mechanism while "
  336. "canonifying user.\n")
  337. goto fail;
  338. }
  339. if (strncasecmp(user, "dn:", 3) == 0) {
  340. sdn = slapi_sdn_new();
  341. slapi_sdn_set_dn_byval(sdn, user+3);
  342. isroot = slapi_dn_isroot(slapi_sdn_get_ndn(sdn));
  343. }
  344. if (isroot) {
  345. /* special case directory manager */
  346. dn = slapi_sdn_get_ndn(sdn);
  347. pw = config_get_rootpw();
  348. *out_ulen = PR_snprintf(out_user, out_umax, "dn: %s", dn);
  349. } else if (strcasecmp(mech, "ANONYMOUS") == 0) {
  350. /* SASL doesn't allow us to set the username to an empty string,
  351. * so we just set it to anonymous. */
  352. dn = "anonymous";
  353. PL_strncpyz(out_user, dn, out_umax);
  354. /* the length of out_user needs to be set for Cyrus SASL */
  355. *out_ulen = strlen(out_user);
  356. } else {
  357. /* map the sasl username into an entry */
  358. entry = ids_sasl_user_to_entry(conn, context, user, user_realm);
  359. if (entry == NULL) {
  360. /* Specific return value is supposed to be set instead of
  361. an generic error (SASL_FAIL) for Cyrus SASL */
  362. returnvalue = SASL_NOAUTHZ;
  363. goto fail;
  364. }
  365. dn = slapi_entry_get_ndn(entry);
  366. pw = slapi_entry_attr_get_charptr(entry, "userpassword");
  367. *out_ulen = PR_snprintf(out_user, out_umax, "dn: %s", dn);
  368. }
  369. /* Need to set dn property to an empty string for the ANONYMOUS mechanism. This
  370. * property determines what the bind identity will be if authentication succeeds. */
  371. if (strcasecmp(mech, "ANONYMOUS") == 0) {
  372. if (prop_set(propctx, "dn", "", -1) != 0) {
  373. LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(dn) failed\n", 0, 0, 0);
  374. goto fail;
  375. }
  376. } else if (prop_set(propctx, "dn", dn, -1) != 0) {
  377. LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(dn) failed\n", 0, 0, 0);
  378. goto fail;
  379. }
  380. /* We need to check if the first character of pw is an opening
  381. * brace since strstr will simply return it's first argument if
  382. * it is an empty string. */
  383. if (pw && (*pw == '{')) {
  384. if (strchr( pw, '}' )) {
  385. /* This password is stored in a non-cleartext format.
  386. * Any SASL mechanism that actually needs the
  387. * password is going to fail. We should print a warning
  388. * to aid in troubleshooting. */
  389. LDAPDebug(LDAP_DEBUG_TRACE, "Warning: Detected a sasl bind attempt by an "
  390. "entry whose password is stored in a non-cleartext format. This "
  391. "will not work for mechanisms which require a cleartext password "
  392. "such as DIGEST-MD5 and CRAM-MD5.\n", 0, 0, 0);
  393. } else {
  394. /* This password doesn't have a storage prefix but
  395. * just happens to start with the '{' character. We'll
  396. * assume that it's just a cleartext password without
  397. * the proper storage prefix. */
  398. clear = pw;
  399. }
  400. } else {
  401. /* This password has no storage prefix, or the password is empty */
  402. clear = pw;
  403. }
  404. if (clear) {
  405. /* older versions of sasl do not have SASL_AUX_PASSWORD_PROP, so omit it */
  406. #ifdef SASL_AUX_PASSWORD_PROP
  407. if (prop_set(propctx, SASL_AUX_PASSWORD_PROP, clear, -1) != 0) {
  408. /* Failure is benign here because some mechanisms don't support this property */
  409. /*LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
  410. goto fail */ ;
  411. }
  412. #endif /* SASL_AUX_PASSWORD_PROP */
  413. if (prop_set(propctx, SASL_AUX_PASSWORD, clear, -1) != 0) {
  414. /* Failure is benign here because some mechanisms don't support this property */
  415. /*LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
  416. goto fail */ ;
  417. }
  418. }
  419. slapi_entry_free(entry);
  420. slapi_ch_free((void**)&user);
  421. slapi_ch_free((void**)&pw);
  422. slapi_sdn_free(&sdn);
  423. return SASL_OK;
  424. fail:
  425. slapi_entry_free(entry);
  426. slapi_ch_free((void**)&user);
  427. slapi_ch_free((void**)&pw);
  428. slapi_sdn_free(&sdn);
  429. return returnvalue;
  430. }
  431. static int ids_sasl_getpluginpath(sasl_conn_t *conn, const char **path)
  432. {
  433. /* Try to get path from config, otherwise check for SASL_PATH environment
  434. * variable. If neither of these are set, default to /usr/lib64/sasl2 on
  435. * 64-bit Linux machines, and /usr/lib/sasl2 on all other platforms.
  436. */
  437. char *pluginpath = config_get_saslpath();
  438. if ((!pluginpath) || (*pluginpath == '\0')) {
  439. slapi_ch_free_string(&pluginpath);
  440. pluginpath = ldaputil_get_saslpath();
  441. }
  442. *path = pluginpath;
  443. return SASL_OK;
  444. }
  445. static sasl_callback_t ids_sasl_callbacks[] =
  446. {
  447. {
  448. SASL_CB_GETOPT,
  449. (IFP) ids_sasl_getopt,
  450. NULL
  451. },
  452. {
  453. SASL_CB_LOG,
  454. (IFP) ids_sasl_log,
  455. NULL
  456. },
  457. {
  458. SASL_CB_CANON_USER,
  459. (IFP) ids_sasl_canon_user,
  460. NULL
  461. },
  462. {
  463. SASL_CB_GETPATH,
  464. (IFP) ids_sasl_getpluginpath,
  465. NULL
  466. },
  467. {
  468. SASL_CB_LIST_END,
  469. (IFP) NULL,
  470. NULL
  471. }
  472. };
  473. static const char *dn_propnames[] = { "dn", 0 };
  474. /*
  475. * initialize the sasl library
  476. */
  477. int ids_sasl_init(void)
  478. {
  479. static int inited = 0;
  480. int result;
  481. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_init\n", 0, 0, 0 );
  482. PR_ASSERT(inited == 0);
  483. inited = 1;
  484. serverfqdn = get_localhost_DNS();
  485. LDAPDebug(LDAP_DEBUG_TRACE, "sasl service fqdn is: %s\n",
  486. serverfqdn, 0, 0);
  487. /* get component ID for internal operations */
  488. generate_component_id();
  489. /* Set SASL memory allocation callbacks */
  490. sasl_set_alloc(
  491. (sasl_malloc_t *)slapi_ch_malloc,
  492. (sasl_calloc_t *)slapi_ch_calloc,
  493. (sasl_realloc_t *)slapi_ch_realloc,
  494. (sasl_free_t *)nssasl_free );
  495. /* Set SASL mutex callbacks */
  496. sasl_set_mutex(
  497. (sasl_mutex_alloc_t *)nssasl_mutex_alloc,
  498. (sasl_mutex_lock_t *)nssasl_mutex_lock,
  499. (sasl_mutex_unlock_t *)nssasl_mutex_unlock,
  500. (sasl_mutex_free_t *)nssasl_mutex_free);
  501. result = sasl_server_init(ids_sasl_callbacks, "iDS");
  502. if (result != SASL_OK) {
  503. LDAPDebug(LDAP_DEBUG_TRACE, "failed to initialize sasl library\n",
  504. 0, 0, 0);
  505. return result;
  506. }
  507. result = sasl_auxprop_add_plugin("iDS", ids_auxprop_plug_init);
  508. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_init\n", 0, 0, 0 );
  509. return result;
  510. }
  511. /*
  512. * create a sasl server connection
  513. */
  514. void ids_sasl_server_new(Connection *conn)
  515. {
  516. int rc;
  517. sasl_conn_t *sasl_conn = NULL;
  518. struct propctx *propctx;
  519. sasl_security_properties_t secprops = {0};
  520. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_server_new (%s)\n", serverfqdn, 0, 0 );
  521. rc = sasl_server_new("ldap",
  522. serverfqdn,
  523. NULL, /* user_realm */
  524. NULL, /* iplocalport */
  525. NULL, /* ipremoteport */
  526. ids_sasl_callbacks,
  527. SASL_SUCCESS_DATA,
  528. &sasl_conn);
  529. if (rc != SASL_OK) {
  530. LDAPDebug(LDAP_DEBUG_ANY, "sasl_server_new: %s\n",
  531. sasl_errstring(rc, NULL, NULL), 0, 0);
  532. }
  533. if (rc == SASL_OK) {
  534. propctx = sasl_auxprop_getctx(sasl_conn);
  535. if (propctx != NULL) {
  536. prop_request(propctx, dn_propnames);
  537. }
  538. }
  539. /* Enable security for this connection */
  540. secprops.maxbufsize = config_get_sasl_maxbufsize();
  541. secprops.max_ssf = 0xffffffff;
  542. secprops.min_ssf = config_get_minssf();
  543. /* If anonymous access is disabled, set the appropriate flag */
  544. if (config_get_anon_access_switch() != SLAPD_ANON_ACCESS_ON) {
  545. secprops.security_flags = SASL_SEC_NOANONYMOUS;
  546. }
  547. rc = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
  548. if (rc != SASL_OK) {
  549. LDAPDebug(LDAP_DEBUG_ANY, "sasl_setprop: %s\n",
  550. sasl_errstring(rc, NULL, NULL), 0, 0);
  551. }
  552. conn->c_sasl_conn = sasl_conn;
  553. conn->c_sasl_ssf = 0;
  554. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_server_new\n", 0, 0, 0 );
  555. return;
  556. }
  557. /*
  558. * return sasl mechanisms available on this connection.
  559. * caller must free returned charray.
  560. */
  561. char **ids_sasl_listmech(Slapi_PBlock *pb)
  562. {
  563. char **ret, **others;
  564. const char *str;
  565. char *dupstr;
  566. sasl_conn_t *sasl_conn;
  567. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_listmech\n", 0, 0, 0 );
  568. PR_ASSERT(pb);
  569. /* hard-wired mechanisms and slapi plugin registered mechanisms */
  570. ret = slapi_get_supported_saslmechanisms_copy();
  571. if (pb->pb_conn == NULL) return ret;
  572. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  573. if (sasl_conn == NULL) return ret;
  574. /* sasl library mechanisms are connection dependent */
  575. PR_EnterMonitor(pb->pb_conn->c_mutex);
  576. if (sasl_listmech(sasl_conn,
  577. NULL, /* username */
  578. "", ",", "",
  579. &str, NULL, NULL) == SASL_OK) {
  580. LDAPDebug(LDAP_DEBUG_TRACE, "sasl library mechs: %s\n", str, 0, 0);
  581. /* merge into result set */
  582. dupstr = slapi_ch_strdup(str);
  583. others = slapi_str2charray_ext(dupstr, ",", 0 /* don't list duplicate mechanisms */);
  584. charray_merge(&ret, others, 1);
  585. charray_free(others);
  586. slapi_ch_free((void**)&dupstr);
  587. }
  588. PR_ExitMonitor(pb->pb_conn->c_mutex);
  589. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_listmech\n", 0, 0, 0 );
  590. return ret;
  591. }
  592. /*
  593. * Determine whether a given sasl mechanism is supported by
  594. * this sasl connection. Returns true/false.
  595. * NOTE: caller must lock pb->pb_conn->c_mutex
  596. */
  597. static int
  598. ids_sasl_mech_supported(Slapi_PBlock *pb, const char *mech)
  599. {
  600. int i, ret = 0;
  601. char **mechs;
  602. char *dupstr;
  603. const char *str;
  604. int sasl_result = 0;
  605. sasl_conn_t *sasl_conn = (sasl_conn_t *)pb->pb_conn->c_sasl_conn;
  606. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_mech_supported\n", 0, 0, 0 );
  607. /* sasl_listmech is not thread-safe - caller must lock pb_conn */
  608. sasl_result = sasl_listmech(sasl_conn,
  609. NULL, /* username */
  610. "", ",", "",
  611. &str, NULL, NULL);
  612. if (sasl_result != SASL_OK) {
  613. return 0;
  614. }
  615. dupstr = slapi_ch_strdup(str);
  616. mechs = slapi_str2charray(dupstr, ",");
  617. for (i = 0; mechs[i] != NULL; i++) {
  618. if (strcasecmp(mech, mechs[i]) == 0) {
  619. ret = 1;
  620. break;
  621. }
  622. }
  623. charray_free(mechs);
  624. slapi_ch_free((void**)&dupstr);
  625. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_mech_supported\n", 0, 0, 0 );
  626. return ret;
  627. }
  628. /*
  629. * do a sasl bind and return a result
  630. */
  631. void ids_sasl_check_bind(Slapi_PBlock *pb)
  632. {
  633. int rc, isroot;
  634. long t;
  635. sasl_conn_t *sasl_conn;
  636. struct propctx *propctx;
  637. sasl_ssf_t *ssfp;
  638. char *activemech = NULL, *mech = NULL;
  639. char *username, *dn = NULL;
  640. const char *normdn = NULL;
  641. Slapi_DN *sdn = NULL;
  642. const char *sdata, *errstr;
  643. unsigned slen;
  644. int continuing = 0;
  645. int pwresponse_requested = 0;
  646. LDAPControl **ctrls;
  647. struct berval bvr, *cred;
  648. struct propval dnval[2];
  649. char authtype[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */
  650. Slapi_Entry *bind_target_entry = NULL, *referral = NULL;
  651. Slapi_Backend *be = NULL;
  652. char errorbuf[BUFSIZ];
  653. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
  654. PR_ASSERT(pb);
  655. PR_ASSERT(pb->pb_conn);
  656. PR_EnterMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  657. continuing = pb->pb_conn->c_flags & CONN_FLAG_SASL_CONTINUE;
  658. pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_CONTINUE; /* reset flag */
  659. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  660. if (sasl_conn == NULL) {
  661. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  662. send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  663. "sasl library unavailable", 0, NULL );
  664. return;
  665. }
  666. slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mech);
  667. if (NULL == mech) {
  668. rc = SASL_NOMECH;
  669. goto sasl_check_result;
  670. }
  671. slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);
  672. if (NULL == cred) {
  673. rc = SASL_BADPARAM;
  674. goto sasl_check_result;
  675. }
  676. slapi_pblock_get(pb, SLAPI_PWPOLICY, &pwresponse_requested);
  677. /* Work around a bug in the sasl library. We've told the
  678. * library that CRAM-MD5 is disabled, but it gives us a
  679. * different error code to SASL_NOMECH. Must be called
  680. * while holding the pb_conn lock
  681. */
  682. if (!ids_sasl_mech_supported(pb, mech)) {
  683. rc = SASL_NOMECH;
  684. goto sasl_check_result;
  685. }
  686. /* can't do any harm */
  687. if (cred->bv_len == 0) cred->bv_val = NULL;
  688. if (continuing) {
  689. /*
  690. * RFC 2251: a client may abort a sasl bind negotiation by
  691. * sending a bindrequest with a different value in the
  692. * mechanism field.
  693. */
  694. sasl_getprop(sasl_conn, SASL_MECHNAME, (const void**)&activemech);
  695. if (activemech == NULL) {
  696. LDAPDebug(LDAP_DEBUG_TRACE, "could not get active sasl mechanism\n", 0, 0, 0);
  697. goto sasl_start;
  698. }
  699. if (strcasecmp(activemech, mech) != 0) {
  700. LDAPDebug(LDAP_DEBUG_TRACE, "sasl mechanisms differ: active=%s current=%s\n", 0, 0, 0);
  701. goto sasl_start;
  702. }
  703. rc = sasl_server_step(sasl_conn,
  704. cred->bv_val, cred->bv_len,
  705. &sdata, &slen);
  706. goto sasl_check_result;
  707. }
  708. sasl_start:
  709. /* Check if we are already authenticated via sasl. If so,
  710. * dispose of the current sasl_conn and create a new one
  711. * using the new mechanism. We also need to do this if the
  712. * mechanism changed in the middle of the SASL authentication
  713. * process. */
  714. if ((pb->pb_conn->c_flags & CONN_FLAG_SASL_COMPLETE) || continuing) {
  715. Slapi_Operation *operation;
  716. slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
  717. slapi_log_error(SLAPI_LOG_CONNS, "ids_sasl_check_bind",
  718. "cleaning up sasl IO conn=%" NSPRIu64 " op=%d complete=%d continuing=%d\n",
  719. pb->pb_conn->c_connid, operation->o_opid,
  720. (pb->pb_conn->c_flags & CONN_FLAG_SASL_COMPLETE), continuing);
  721. /* reset flag */
  722. pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_COMPLETE;
  723. /* remove any SASL I/O from the connection */
  724. connection_set_io_layer_cb(pb->pb_conn, NULL, sasl_io_cleanup, NULL);
  725. /* dispose of sasl_conn and create a new sasl_conn */
  726. sasl_dispose(&sasl_conn);
  727. ids_sasl_server_new(pb->pb_conn);
  728. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  729. if (sasl_conn == NULL) {
  730. send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  731. "sasl library unavailable", 0, NULL );
  732. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  733. return;
  734. }
  735. }
  736. rc = sasl_server_start(sasl_conn, mech,
  737. cred->bv_val, cred->bv_len,
  738. &sdata, &slen);
  739. sasl_check_result:
  740. switch (rc) {
  741. case SASL_OK: /* complete */
  742. /* retrieve the authenticated username */
  743. if (sasl_getprop(sasl_conn, SASL_USERNAME,
  744. (const void**)&username) != SASL_OK) {
  745. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  746. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  747. "could not obtain sasl username", 0, NULL);
  748. break;
  749. }
  750. LDAPDebug(LDAP_DEBUG_TRACE, "sasl authenticated mech=%s user=%s\n",
  751. mech, username, 0);
  752. /*
  753. * Retrieve the DN corresponding to the authenticated user.
  754. * This should have been set by the user canon callback
  755. * in an auxiliary property called "dn".
  756. */
  757. propctx = sasl_auxprop_getctx(sasl_conn);
  758. if (prop_getnames(propctx, dn_propnames, dnval) == 1) {
  759. if (dnval[0].values && dnval[0].values[0]) {
  760. dn = slapi_ch_smprintf("%s", dnval[0].values[0]);
  761. }
  762. }
  763. if (dn == NULL) {
  764. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  765. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  766. "could not get auth dn from sasl", 0, NULL);
  767. break;
  768. }
  769. /* clean up already set TARGET */
  770. slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &sdn);
  771. slapi_sdn_free(&sdn);
  772. sdn = slapi_sdn_new_dn_passin(dn);
  773. normdn = slapi_sdn_get_dn(sdn);
  774. slapi_pblock_set( pb, SLAPI_BIND_TARGET_SDN, sdn );
  775. if ((sasl_getprop(sasl_conn, SASL_SSF,
  776. (const void**)&ssfp) == SASL_OK) && (*ssfp > 0)) {
  777. LDAPDebug(LDAP_DEBUG_TRACE, "sasl ssf=%u\n", (unsigned)*ssfp, 0, 0);
  778. } else {
  779. *ssfp = 0;
  780. }
  781. /* Set a flag to signify that sasl bind is complete */
  782. pb->pb_conn->c_flags |= CONN_FLAG_SASL_COMPLETE;
  783. /* note - we set this here in case there are pre-bind
  784. plugins that want to know what the negotiated
  785. ssf is - but this happens before we actually set
  786. up the socket for SASL encryption - so one
  787. consequence is that we attempt to do sasl
  788. encryption on the connection after the pre-bind
  789. plugin has been called, and sasl encryption fails
  790. and the operation returns an error */
  791. pb->pb_conn->c_sasl_ssf = (unsigned)*ssfp;
  792. /* set the connection bind credentials */
  793. PR_snprintf(authtype, sizeof(authtype), "%s%s", SLAPD_AUTH_SASL, mech);
  794. /* normdn is consumed by bind_credentials_set_nolock */
  795. bind_credentials_set_nolock(pb->pb_conn, authtype,
  796. slapi_ch_strdup(normdn),
  797. NULL, NULL, NULL, bind_target_entry);
  798. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  799. if (plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) != 0){
  800. break;
  801. }
  802. isroot = slapi_dn_isroot(normdn);
  803. if (!isroot )
  804. {
  805. /* check if the account is locked */
  806. bind_target_entry = get_entry(pb, normdn);
  807. if ( bind_target_entry == NULL )
  808. {
  809. goto out;
  810. }
  811. if ( slapi_check_account_lock(pb, bind_target_entry, pwresponse_requested, 1, 1) == 1) {
  812. goto out;
  813. }
  814. }
  815. /* set the auth response control if requested */
  816. slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrls);
  817. if (slapi_control_present(ctrls, LDAP_CONTROL_AUTH_REQUEST,
  818. NULL, NULL)) {
  819. slapi_add_auth_response_control(pb, normdn);
  820. }
  821. if (slapi_mapping_tree_select(pb, &be, &referral, errorbuf) != LDAP_SUCCESS) {
  822. send_nobackend_ldap_result( pb );
  823. be = NULL;
  824. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_check_bind\n", 0, 0, 0 );
  825. return;
  826. }
  827. if (referral) {
  828. send_referrals_from_entry(pb,referral);
  829. goto out;
  830. }
  831. slapi_pblock_set( pb, SLAPI_BACKEND, be );
  832. slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
  833. set_db_default_result_handlers(pb);
  834. /* check password expiry */
  835. if (!isroot) {
  836. int pwrc;
  837. pwrc = need_new_pw(pb, &t, bind_target_entry, pwresponse_requested);
  838. switch (pwrc) {
  839. case 1:
  840. slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
  841. break;
  842. case 2:
  843. slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);
  844. break;
  845. case -1:
  846. goto out;
  847. default:
  848. break;
  849. }
  850. }
  851. /* attach the sasl data */
  852. if (slen != 0) {
  853. bvr.bv_val = (char*)sdata;
  854. bvr.bv_len = slen;
  855. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
  856. }
  857. /* see if we negotiated a security layer */
  858. if (*ssfp > 0) {
  859. /* Enable SASL I/O on the connection */
  860. PR_EnterMonitor(pb->pb_conn->c_mutex);
  861. connection_set_io_layer_cb(pb->pb_conn, sasl_io_enable, NULL, NULL);
  862. PR_ExitMonitor(pb->pb_conn->c_mutex);
  863. }
  864. /* send successful result */
  865. send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
  866. /* remove the sasl data from the pblock */
  867. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
  868. break;
  869. case SASL_CONTINUE: /* another step needed */
  870. pb->pb_conn->c_flags |= CONN_FLAG_SASL_CONTINUE;
  871. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  872. if (plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) != 0){
  873. break;
  874. }
  875. /* attach the sasl data */
  876. bvr.bv_val = (char*)sdata;
  877. bvr.bv_len = slen;
  878. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
  879. /* send continuation result */
  880. send_ldap_result( pb, LDAP_SASL_BIND_IN_PROGRESS, NULL,
  881. NULL, 0, NULL );
  882. /* remove the sasl data from the pblock */
  883. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
  884. break;
  885. case SASL_NOMECH:
  886. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  887. send_ldap_result(pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  888. "sasl mechanism not supported", 0, NULL);
  889. break;
  890. default: /* other error */
  891. errstr = sasl_errdetail(sasl_conn);
  892. PR_ExitMonitor(pb->pb_conn->c_mutex); /* BIG LOCK */
  893. send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
  894. (char*)errstr, 0, NULL);
  895. break;
  896. }
  897. out:
  898. if (referral)
  899. slapi_entry_free(referral);
  900. if (be)
  901. slapi_be_Unlock(be);
  902. if (bind_target_entry)
  903. slapi_entry_free(bind_target_entry);
  904. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
  905. return;
  906. }