saslbind.c 37 KB

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