saslbind.c 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953
  1. /** BEGIN COPYRIGHT BLOCK
  2. * Copyright 2001 Sun Microsystems, Inc.
  3. * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
  4. * All rights reserved.
  5. * END COPYRIGHT BLOCK **/
  6. #undef CYRUS_SASL
  7. #include <slap.h>
  8. #include <fe.h>
  9. #include <sasl.h>
  10. #include <saslplug.h>
  11. #ifndef CYRUS_SASL
  12. #include <saslmod.h>
  13. #endif
  14. #ifndef _WIN32
  15. #include <unistd.h>
  16. #endif
  17. /* No GSSAPI on Windows */
  18. #if !defined(_WIN32)
  19. #define BUILD_GSSAPI 1
  20. #endif
  21. static char *serverfqdn;
  22. /*
  23. * utility functions needed by the sasl library
  24. */
  25. int sasl_os_gethost(char *buf, int len)
  26. {
  27. int rc;
  28. rc = gethostname(buf, len);
  29. LDAPDebug(LDAP_DEBUG_TRACE, "sasl_os_gethost %s\n", buf, 0, 0);
  30. return ( rc == 0 ? SASL_OK : SASL_FAIL );
  31. }
  32. void *sasl_mutex_alloc(void)
  33. {
  34. return PR_NewLock();
  35. }
  36. int sasl_mutex_lock(void *mutex)
  37. {
  38. PR_Lock(mutex);
  39. return SASL_OK;
  40. }
  41. int sasl_mutex_unlock(void *mutex)
  42. {
  43. if (PR_Unlock(mutex) == PR_SUCCESS) return SASL_OK;
  44. return SASL_FAIL;
  45. }
  46. void sasl_mutex_free(void *mutex)
  47. {
  48. PR_DestroyLock(mutex);
  49. }
  50. /*
  51. * sasl library callbacks
  52. */
  53. static int ids_sasl_getopt(
  54. void *context,
  55. const char *plugin_name,
  56. const char *option,
  57. const char **result,
  58. unsigned *len
  59. )
  60. {
  61. unsigned tmplen;
  62. LDAPDebug(LDAP_DEBUG_TRACE, "ids_sasl_getopt: plugin=%s option=%s\n",
  63. plugin_name ? plugin_name : "", option, 0);
  64. if (len == NULL) len = &tmplen;
  65. *result = NULL;
  66. *len = 0;
  67. if (strcasecmp(option, "enable") == 0) {
  68. *result = "USERDB/DIGEST-MD5,GSSAPI/GSSAPI";
  69. } else if (strcasecmp(option, "has_plain_passwords") == 0) {
  70. *result = "yes";
  71. } else if (strcasecmp(option, "LOG_LEVEL") == 0) {
  72. if (LDAPDebugLevelIsSet(LDAP_DEBUG_TRACE)) {
  73. *result = "6"; /* SASL_LOG_TRACE */
  74. }
  75. }
  76. if (*result) *len = strlen(*result);
  77. return SASL_OK;
  78. }
  79. static int ids_sasl_log(
  80. void *context,
  81. int level,
  82. const char *message
  83. )
  84. {
  85. switch (level) {
  86. case SASL_LOG_ERR: /* log unusual errors (default) */
  87. slapi_log_error(SLAPI_LOG_FATAL, "sasl", "%s", message);
  88. break;
  89. case SASL_LOG_FAIL: /* log all authentication failures */
  90. case SASL_LOG_WARN: /* log non-fatal warnings */
  91. case SASL_LOG_NOTE: /* more verbose than LOG_WARN */
  92. case SASL_LOG_DEBUG: /* more verbose than LOG_NOTE */
  93. case SASL_LOG_TRACE: /* traces of internal protocols */
  94. case SASL_LOG_PASS: /* traces of internal protocols, including
  95. * passwords */
  96. LDAPDebug(LDAP_DEBUG_ANY, "sasl(%d): %s", level, message, 0);
  97. break;
  98. case SASL_LOG_NONE: /* don't log anything */
  99. default:
  100. break;
  101. }
  102. return SASL_OK;
  103. }
  104. static int ids_sasl_proxy_policy(
  105. sasl_conn_t *conn,
  106. void *context,
  107. const char *requested_user, int rlen,
  108. const char *auth_identity, int alen,
  109. const char *def_realm, int urlen,
  110. struct propctx *propctx
  111. )
  112. {
  113. int retVal = SASL_OK;
  114. /* do not permit sasl proxy authorization */
  115. /* if the auth_identity is null or empty string, allow the sasl request to go thru */
  116. if ( (auth_identity != NULL ) && ( strlen(auth_identity) > 0 ) ) {
  117. Slapi_DN authId , reqUser;
  118. slapi_sdn_init_dn_byref(&authId,auth_identity);
  119. slapi_sdn_init_dn_byref(&reqUser,requested_user);
  120. if (slapi_sdn_compare((const Slapi_DN *)&reqUser,(const Slapi_DN *) &authId) != 0) {
  121. LDAPDebug(LDAP_DEBUG_TRACE,
  122. "sasl proxy auth not permitted authid=%s user=%s\n",
  123. auth_identity, requested_user, 0);
  124. retVal = SASL_NOAUTHZ;
  125. }
  126. slapi_sdn_done(&authId);
  127. slapi_sdn_done(&reqUser);
  128. }
  129. return retVal;
  130. }
  131. static void ids_sasl_user_search(
  132. char *basedn,
  133. int scope,
  134. char *filter,
  135. LDAPControl **ctrls,
  136. char **attrs,
  137. int attrsonly,
  138. Slapi_Entry **ep,
  139. int *foundp
  140. )
  141. {
  142. Slapi_Entry **entries = NULL;
  143. Slapi_PBlock *pb;
  144. int i, ret;
  145. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search basedn=\"%s\" filter=\"%s\"\n", basedn, filter, 0);
  146. /* TODO: set size and time limits */
  147. pb = slapi_search_internal(basedn, scope, filter,
  148. ctrls, attrs, attrsonly);
  149. if (pb == NULL) {
  150. LDAPDebug(LDAP_DEBUG_TRACE, "null pblock from slapi_search_internal\n", 0, 0, 0);
  151. goto out;
  152. }
  153. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
  154. if (ret != LDAP_SUCCESS) {
  155. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search failed basedn=\"%s\" "
  156. "filter=\"%s\": %s\n",
  157. basedn, filter, ldap_err2string(ret));
  158. goto out;
  159. }
  160. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
  161. if (entries == NULL) goto out;
  162. for (i = 0; entries[i]; i++) {
  163. (*foundp)++;
  164. if (*ep != NULL) {
  165. slapi_entry_free(*ep);
  166. }
  167. *ep = slapi_entry_dup(entries[i]);
  168. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found dn=%s\n",
  169. slapi_entry_get_dn(*ep), 0, 0);
  170. }
  171. out:
  172. if (pb) slapi_free_search_results_internal(pb);
  173. return;
  174. }
  175. /*
  176. * Search for an entry representing the sasl user.
  177. */
  178. static Slapi_Entry *ids_sasl_user_to_entry(
  179. sasl_conn_t *conn,
  180. void *context,
  181. const char *user,
  182. const char *user_realm
  183. )
  184. {
  185. int found = 0;
  186. unsigned fsize = 0, ulen, rlen = 0;
  187. int attrsonly = 0, scope = LDAP_SCOPE_SUBTREE;
  188. char filter[1024], *fptr = filter;
  189. LDAPControl **ctrls = NULL;
  190. Slapi_Entry *entry = NULL;
  191. Slapi_DN *sdn;
  192. char **attrs = NULL;
  193. char *userattr = "uid", *realmattr = NULL, *ufilter = NULL;
  194. void *node;
  195. int regexmatch = 0;
  196. char *regex_ldap_search_base = NULL;
  197. char *regex_ldap_search_filter = NULL;
  198. /* TODO: userattr & realmattr should be configurable */
  199. /*
  200. * Check for dn: prefix. See RFC 2829 section 9.
  201. */
  202. if (strncasecmp(user, "dn:", 3) == 0) {
  203. sprintf(fptr, "(objectclass=*)");
  204. scope = LDAP_SCOPE_BASE;
  205. ids_sasl_user_search((char*)user+3, scope, filter,
  206. ctrls, attrs, attrsonly,
  207. &entry, &found);
  208. } else {
  209. int offset = 0;
  210. if (strncasecmp(user,"u:",2) == 0 )
  211. offset = 2;
  212. /* TODO: quote the filter values */
  213. /* New regex-based identity mapping : we call it here before the old code.
  214. * If there's a match, we skip the old way, otherwise we plow ahead for backwards compatibility reasons
  215. */
  216. regexmatch = sasl_map_domap((char*)user, (char*)user_realm, &regex_ldap_search_base, &regex_ldap_search_filter);
  217. if (regexmatch) {
  218. ids_sasl_user_search(regex_ldap_search_base, scope, regex_ldap_search_filter,
  219. ctrls, attrs, attrsonly,
  220. &entry, &found);
  221. /* Free the filter etc */
  222. slapi_ch_free((void**)&regex_ldap_search_base);
  223. slapi_ch_free((void**)&regex_ldap_search_filter);
  224. } else {
  225. /* Ensure no buffer overflow. */
  226. /* We don't know what the upper limits on username and
  227. * realm lengths are. There don't seem to be any defined
  228. * in the relevant standards. We may find in the future
  229. * that a 1K buffer is insufficient for some mechanism,
  230. * but it seems unlikely given that the values are exposed
  231. * to the end user.
  232. */
  233. ulen = strlen(user+offset);
  234. fsize += strlen(userattr) + ulen;
  235. if (realmattr && user_realm) {
  236. rlen = strlen(user_realm);
  237. fsize += strlen(realmattr) + rlen;
  238. }
  239. if (ufilter) fsize += strlen(ufilter);
  240. fsize += 100; /* includes a good safety margin */
  241. if (fsize > 1024) {
  242. LDAPDebug(LDAP_DEBUG_ANY, "sasl user name and/or realm too long"
  243. " (ulen=%u, rlen=%u)\n", ulen, rlen, 0);
  244. return NULL;
  245. }
  246. /* now we can safely write the filter */
  247. sprintf(fptr, "(&(%s=%s)", userattr, user+offset);
  248. fptr += strlen(fptr);
  249. if (realmattr && user_realm) {
  250. sprintf(fptr, "(%s=%s)", realmattr, user_realm);
  251. fptr += strlen(fptr);
  252. }
  253. if (ufilter) {
  254. if (*ufilter == '(') {
  255. sprintf(fptr, "%s", ufilter);
  256. } else {
  257. sprintf(fptr, "(%s)", ufilter);
  258. }
  259. fptr += strlen(fptr);
  260. }
  261. sprintf(fptr, ")");
  262. /* iterate through the naming contexts */
  263. for (sdn = slapi_get_first_suffix(&node, 0); sdn != NULL;
  264. sdn = slapi_get_next_suffix(&node, 0)) {
  265. ids_sasl_user_search((char*)slapi_sdn_get_dn(sdn), scope, filter,
  266. ctrls, attrs, attrsonly,
  267. &entry, &found);
  268. }
  269. }
  270. }
  271. if (found == 1) {
  272. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found this entry: dn:%s, matching filter=%s\n", entry->e_sdn.dn, filter, 0);
  273. return entry;
  274. }
  275. if (found == 0) {
  276. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found no entries matching filter=%s\n", filter, 0, 0);
  277. } else {
  278. LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found more than one entry matching filter=%s\n", filter, 0, 0);
  279. }
  280. if (entry) slapi_entry_free(entry);
  281. return NULL;
  282. }
  283. static char *buf2str(const char *buf, unsigned buflen)
  284. {
  285. char *ret;
  286. ret = (char*)slapi_ch_malloc(buflen+1);
  287. memcpy(ret, buf, buflen);
  288. ret[buflen] = '\0';
  289. return ret;
  290. }
  291. /* Note that in this sasl1 API, when it says 'authid' it really means 'authzid'. */
  292. static int ids_sasl_canon_user(
  293. sasl_conn_t *conn,
  294. void *context,
  295. const char *userbuf, unsigned ulen,
  296. #ifndef CYRUS_SASL
  297. const char *authidbuf, unsigned alen,
  298. #endif
  299. unsigned flags, const char *user_realm,
  300. char *out_user, unsigned out_umax, unsigned *out_ulen
  301. #ifndef CYRUS_SASL
  302. ,char *out_authid, unsigned out_amax, unsigned *out_alen
  303. #endif
  304. )
  305. {
  306. struct propctx *propctx = sasl_auxprop_getctx(conn);
  307. Slapi_Entry *entry = NULL;
  308. Slapi_DN *sdn = NULL;
  309. char *pw = NULL;
  310. char *user = NULL;
  311. #ifndef CYRUS_SASL
  312. char *authid = NULL;
  313. #endif
  314. const char *dn;
  315. int isroot = 0;
  316. char *clear = NULL;
  317. int returnvalue = SASL_FAIL;
  318. user = buf2str(userbuf, ulen);
  319. if (user == NULL) {
  320. goto fail;
  321. }
  322. #ifdef CYRUS_SASL
  323. LDAPDebug(LDAP_DEBUG_TRACE,
  324. "ids_sasl_canon_user(user=%s, realm=%s)\n",
  325. user, user_realm ? user_realm : "", 0);
  326. #else
  327. authid = buf2str(authidbuf, alen);
  328. LDAPDebug(LDAP_DEBUG_TRACE,
  329. "ids_sasl_canon_user(user=%s, authzid=%s, realm=%s)\n",
  330. user, authid, user_realm ? user_realm : "");
  331. #endif
  332. if (strncasecmp(user, "dn:", 3) == 0) {
  333. sdn = slapi_sdn_new();
  334. slapi_sdn_set_dn_byval(sdn, user+3);
  335. isroot = slapi_dn_isroot(slapi_sdn_get_ndn(sdn));
  336. }
  337. if (isroot) {
  338. /* special case directory manager */
  339. dn = slapi_sdn_get_ndn(sdn);
  340. pw = config_get_rootpw();
  341. } else {
  342. /* map the sasl username into an entry */
  343. entry = ids_sasl_user_to_entry(conn, context, user, user_realm);
  344. if (entry == NULL) {
  345. #ifdef CYRUS_SASL
  346. /* Specific return value is supposed to be set instead of
  347. an generic error (SASL_FAIL) for Cyrus SASL */
  348. returnvalue = SASL_NOAUTHZ;
  349. #endif
  350. goto fail;
  351. }
  352. dn = slapi_entry_get_ndn(entry);
  353. pw = slapi_entry_attr_get_charptr(entry, "userpassword");
  354. }
  355. if (prop_set(propctx, "dn", dn, -1) != 0) {
  356. LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(dn) failed\n", 0, 0, 0);
  357. goto fail;
  358. }
  359. clear = pw;
  360. if (clear) {
  361. if (prop_set(propctx, "userpassword", clear, -1) != 0) {
  362. /* Failure is benign here because some mechanisms don't support this property */
  363. /*LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
  364. goto fail */ ;
  365. }
  366. }
  367. /* TODO: canonicalize */
  368. strcpy(out_user, dn);
  369. #ifdef CYRUS_SASL
  370. /* the length of out_user needs to be set for Cyrus SASL */
  371. *out_ulen = strlen(out_user);
  372. #else
  373. if (authid )
  374. {
  375. int offset = 0;
  376. /* The authid can start with dn:. In such case remove it */
  377. if (strncasecmp(authid,"dn:",3) == 0 )
  378. offset = 3;
  379. strcpy(out_authid, authid+offset);
  380. }
  381. *out_ulen = -1;
  382. *out_alen = -1;
  383. slapi_ch_free((void**)&authid);
  384. #endif
  385. slapi_entry_free(entry);
  386. slapi_ch_free((void**)&user);
  387. slapi_ch_free((void**)&pw);
  388. slapi_sdn_free(&sdn);
  389. return SASL_OK;
  390. fail:
  391. slapi_entry_free(entry);
  392. slapi_ch_free((void**)&user);
  393. #ifndef CYRUS_SASL
  394. slapi_ch_free((void**)&authid);
  395. #endif
  396. slapi_ch_free((void**)&pw);
  397. slapi_sdn_free(&sdn);
  398. return returnvalue;
  399. }
  400. static sasl_callback_t ids_sasl_callbacks[5] =
  401. {
  402. {
  403. SASL_CB_GETOPT,
  404. (IFP) ids_sasl_getopt,
  405. NULL
  406. },
  407. {
  408. SASL_CB_LOG,
  409. (IFP) ids_sasl_log,
  410. NULL
  411. },
  412. {
  413. SASL_CB_PROXY_POLICY,
  414. (IFP) ids_sasl_proxy_policy,
  415. NULL
  416. },
  417. {
  418. #ifdef CYRUS_SASL
  419. SASL_CB_CANON_USER,
  420. #else
  421. SASL_CB_SERVER_CANON_USER,
  422. #endif
  423. (IFP) ids_sasl_canon_user,
  424. NULL
  425. },
  426. {
  427. SASL_CB_LIST_END,
  428. (IFP) NULL,
  429. NULL
  430. }
  431. };
  432. static const char *dn_propnames[] = { "dn", 0 };
  433. /*
  434. * initialize the sasl library
  435. */
  436. int ids_sasl_init(void)
  437. {
  438. static int inited = 0;
  439. int result;
  440. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_init\n", 0, 0, 0 );
  441. PR_ASSERT(inited == 0);
  442. inited = 1;
  443. serverfqdn = get_localhost_DNS();
  444. LDAPDebug(LDAP_DEBUG_TRACE, "sasl service fqdn is: %s\n",
  445. serverfqdn, 0, 0);
  446. result = sasl_server_init(ids_sasl_callbacks, "iDS");
  447. if (result != SASL_OK) {
  448. LDAPDebug(LDAP_DEBUG_TRACE, "failed to initialize sasl library\n",
  449. 0, 0, 0);
  450. return result;
  451. }
  452. #ifndef CYRUS_SASL
  453. result = sasl_server_add_plugin("USERDB", sasl_userdb_init);
  454. if (result != SASL_OK) {
  455. LDAPDebug(LDAP_DEBUG_TRACE, "failed to add LDAP sasl plugin\n",
  456. 0, 0, 0);
  457. return result;
  458. }
  459. #if defined(BUILD_GSSAPI)
  460. result = sasl_server_add_plugin("GSSAPI", sasl_gssapi_init);
  461. if (result != SASL_OK) {
  462. LDAPDebug(LDAP_DEBUG_TRACE, "failed to add LDAP gssapi plugin\n",
  463. 0, 0, 0);
  464. }
  465. #endif
  466. #endif
  467. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_init\n", 0, 0, 0 );
  468. return result;
  469. }
  470. /*
  471. * create a sasl server connection
  472. */
  473. void ids_sasl_server_new(Connection *conn)
  474. {
  475. int rc;
  476. sasl_conn_t *sasl_conn = NULL;
  477. struct propctx *propctx;
  478. sasl_security_properties_t secprops = {0};
  479. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_server_new (%s)\n", serverfqdn, 0, 0 );
  480. rc = sasl_server_new("ldap",
  481. serverfqdn,
  482. NULL, /* user_realm */
  483. NULL, /* iplocalport */
  484. NULL, /* ipremoteport */
  485. ids_sasl_callbacks,
  486. SASL_SUCCESS_DATA,
  487. &sasl_conn);
  488. if (rc != SASL_OK) {
  489. LDAPDebug(LDAP_DEBUG_ANY, "sasl_server_new: %s\n",
  490. sasl_errstring(rc, NULL, NULL), 0, 0);
  491. }
  492. if (rc == SASL_OK) {
  493. propctx = sasl_auxprop_getctx(sasl_conn);
  494. if (propctx != NULL) {
  495. prop_request(propctx, dn_propnames);
  496. }
  497. }
  498. /* Enable security for this connection */
  499. secprops.maxbufsize = 2048; /* DBDB: hack */
  500. secprops.max_ssf = 0xffffffff;
  501. rc = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
  502. if (rc != SASL_OK) {
  503. LDAPDebug(LDAP_DEBUG_ANY, "sasl_setprop: %s\n",
  504. sasl_errstring(rc, NULL, NULL), 0, 0);
  505. }
  506. conn->c_sasl_conn = sasl_conn;
  507. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_server_new\n", 0, 0, 0 );
  508. return;
  509. }
  510. /*
  511. * return sasl mechanisms available on this connection.
  512. * caller must free returned charray.
  513. */
  514. char **ids_sasl_listmech(Slapi_PBlock *pb)
  515. {
  516. char **ret, **others;
  517. const char *str;
  518. char *dupstr;
  519. sasl_conn_t *sasl_conn;
  520. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_listmech\n", 0, 0, 0 );
  521. PR_ASSERT(pb);
  522. /* hard-wired mechanisms and slapi plugin registered mechanisms */
  523. ret = slapi_get_supported_saslmechanisms_copy();
  524. if (pb->pb_conn == NULL) return ret;
  525. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  526. if (sasl_conn == NULL) return ret;
  527. /* sasl library mechanisms are connection dependent */
  528. PR_Lock(pb->pb_conn->c_mutex);
  529. if (sasl_listmech(sasl_conn,
  530. NULL, /* username */
  531. "", ",", "",
  532. &str, NULL, NULL) == SASL_OK) {
  533. LDAPDebug(LDAP_DEBUG_TRACE, "sasl library mechs: %s\n", str, 0, 0);
  534. /* merge into result set */
  535. dupstr = slapi_ch_strdup(str);
  536. others = str2charray(dupstr, ",");
  537. charray_merge(&ret, others, 1);
  538. charray_free(others);
  539. slapi_ch_free((void**)&dupstr);
  540. }
  541. PR_Unlock(pb->pb_conn->c_mutex);
  542. LDAPDebug( LDAP_DEBUG_TRACE, ">= ids_sasl_listmech\n", 0, 0, 0 );
  543. return ret;
  544. }
  545. /*
  546. * Determine whether a given sasl mechanism is supported by
  547. * this sasl connection. Returns true/false.
  548. */
  549. static int
  550. ids_sasl_mech_supported(Slapi_PBlock *pb, sasl_conn_t *sasl_conn, const char *mech)
  551. {
  552. int i, ret = 0;
  553. char **mechs;
  554. char *dupstr;
  555. const char *str;
  556. int sasl_result = 0;
  557. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_mech_supported\n", 0, 0, 0 );
  558. /* sasl_listmech is not thread-safe, so we lock here */
  559. PR_Lock(pb->pb_conn->c_mutex);
  560. sasl_result = sasl_listmech(sasl_conn,
  561. NULL, /* username */
  562. "", ",", "",
  563. &str, NULL, NULL);
  564. PR_Unlock(pb->pb_conn->c_mutex);
  565. if (sasl_result != SASL_OK) {
  566. return 0;
  567. }
  568. dupstr = slapi_ch_strdup(str);
  569. mechs = str2charray(dupstr, ",");
  570. for (i = 0; mechs[i] != NULL; i++) {
  571. if (strcasecmp(mech, mechs[i]) == 0) {
  572. ret = 1;
  573. break;
  574. }
  575. }
  576. charray_free(mechs);
  577. slapi_ch_free((void**)&dupstr);
  578. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_mech_supported\n", 0, 0, 0 );
  579. return ret;
  580. }
  581. /*
  582. * do a sasl bind and return a result
  583. */
  584. void ids_sasl_check_bind(Slapi_PBlock *pb)
  585. {
  586. int rc, isroot;
  587. long t;
  588. sasl_conn_t *sasl_conn;
  589. struct propctx *propctx;
  590. sasl_ssf_t *ssfp;
  591. char *activemech = NULL, *mech = NULL;
  592. char *username, *dn = NULL;
  593. const char *sdata, *errstr;
  594. unsigned slen;
  595. int continuing = 0;
  596. int pwresponse_requested = 0;
  597. LDAPControl **ctrls;
  598. struct berval bvr, *cred;
  599. struct propval dnval[2];
  600. char authtype[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */
  601. Slapi_Entry *bind_target_entry = NULL, *referral = NULL;
  602. Slapi_Backend *be = NULL;
  603. char errorbuf[BUFSIZ];
  604. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
  605. PR_ASSERT(pb);
  606. PR_ASSERT(pb->pb_conn);
  607. continuing = pb->pb_conn->c_flags & CONN_FLAG_SASL_CONTINUE;
  608. pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_CONTINUE; /* reset flag */
  609. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  610. if (sasl_conn == NULL) {
  611. send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  612. "sasl library unavailable", 0, NULL );
  613. return;
  614. }
  615. slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mech);
  616. slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);
  617. slapi_pblock_get(pb, SLAPI_PWPOLICY, &pwresponse_requested);
  618. PR_ASSERT(mech);
  619. PR_ASSERT(cred);
  620. /* Work around a bug in the sasl library. We've told the
  621. * library that CRAM-MD5 is disabled, but it gives us a
  622. * different error code to SASL_NOMECH.
  623. */
  624. if (!ids_sasl_mech_supported(pb, sasl_conn, mech)) {
  625. rc = SASL_NOMECH;
  626. goto sasl_check_result;
  627. }
  628. /* can't do any harm */
  629. if (cred->bv_len == 0) cred->bv_val = NULL;
  630. if (continuing) {
  631. /*
  632. * RFC 2251: a client may abort a sasl bind negotiation by
  633. * sending a bindrequest with a different value in the
  634. * mechanism field.
  635. */
  636. sasl_getprop(sasl_conn, SASL_MECHNAME, (const void**)&activemech);
  637. if (activemech == NULL) {
  638. LDAPDebug(LDAP_DEBUG_TRACE, "could not get active sasl mechanism\n", 0, 0, 0);
  639. goto sasl_start;
  640. }
  641. if (strcasecmp(activemech, mech) != 0) {
  642. LDAPDebug(LDAP_DEBUG_TRACE, "sasl mechanisms differ: active=%s current=%s\n", 0, 0, 0);
  643. goto sasl_start;
  644. }
  645. rc = sasl_server_step(sasl_conn,
  646. cred->bv_val, cred->bv_len,
  647. &sdata, &slen);
  648. goto sasl_check_result;
  649. }
  650. sasl_start:
  651. rc = sasl_server_start(sasl_conn, mech,
  652. cred->bv_val, cred->bv_len,
  653. &sdata, &slen);
  654. sasl_check_result:
  655. switch (rc) {
  656. case SASL_OK: /* complete */
  657. /* retrieve the authenticated username */
  658. if (sasl_getprop(sasl_conn, SASL_USERNAME,
  659. (const void**)&username) != SASL_OK) {
  660. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  661. "could not obtain sasl username", 0, NULL);
  662. break;
  663. }
  664. LDAPDebug(LDAP_DEBUG_TRACE, "sasl authenticated mech=%s user=%s\n",
  665. mech, username, 0);
  666. /*
  667. * Retrieve the DN corresponding to the authenticated user.
  668. * This should have been set by the user canon callback
  669. * in an auxiliary property called "dn".
  670. */
  671. propctx = sasl_auxprop_getctx(sasl_conn);
  672. if (prop_getnames(propctx, dn_propnames, dnval) == 1) {
  673. if (dnval[0].values && dnval[0].values[0]) {
  674. dn = slapi_ch_strdup(dnval[0].values[0]);
  675. }
  676. }
  677. if (dn == NULL) {
  678. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  679. "could not get auth dn from sasl", 0, NULL);
  680. break;
  681. }
  682. isroot = slapi_dn_isroot(dn);
  683. if (!isroot )
  684. {
  685. /* check if the account is locked */
  686. bind_target_entry = get_entry(pb, dn);
  687. if ( bind_target_entry == NULL )
  688. {
  689. break;
  690. }
  691. if ( check_account_lock(pb, bind_target_entry, pwresponse_requested) == 1) {
  692. slapi_entry_free(bind_target_entry);
  693. break;
  694. }
  695. }
  696. /* see if we negotiated a security layer */
  697. if ((sasl_getprop(sasl_conn, SASL_SSF,
  698. (const void**)&ssfp) == SASL_OK) && (*ssfp > 0)) {
  699. LDAPDebug(LDAP_DEBUG_TRACE, "sasl ssf=%u\n", (unsigned)*ssfp, 0, 0);
  700. if (pb->pb_conn->c_flags & CONN_FLAG_SSL) {
  701. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  702. "sasl encryption not supported over ssl",
  703. 0, NULL);
  704. if ( bind_target_entry != NULL )
  705. slapi_entry_free(bind_target_entry);
  706. break;
  707. } else {
  708. /* Enable SASL I/O on the connection now */
  709. /* Note that this doesn't go into effect until the next _read_ operation is done */
  710. if (0 != sasl_io_enable(pb->pb_conn) ) {
  711. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  712. "failed to enable sasl i/o",
  713. 0, NULL);
  714. }
  715. }
  716. }
  717. /* set the connection bind credentials */
  718. sprintf(authtype, "%s%s", SLAPD_AUTH_SASL, mech);
  719. bind_credentials_set(pb->pb_conn, authtype, dn,
  720. NULL, NULL, NULL, bind_target_entry);
  721. /* set the auth response control if requested */
  722. slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrls);
  723. if (slapi_control_present(ctrls, LDAP_CONTROL_AUTH_REQUEST,
  724. NULL, NULL)) {
  725. slapi_add_auth_response_control(pb, dn);
  726. }
  727. if (slapi_mapping_tree_select(pb, &be, &referral, errorbuf) != LDAP_SUCCESS) {
  728. send_nobackend_ldap_result( pb );
  729. be = NULL;
  730. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_check_bind\n", 0, 0, 0 );
  731. return;
  732. }
  733. if (referral) {
  734. send_referrals_from_entry(pb,referral);
  735. goto out;
  736. }
  737. slapi_pblock_set( pb, SLAPI_BACKEND, be );
  738. slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
  739. set_db_default_result_handlers(pb);
  740. /* check password expiry */
  741. if (!isroot) {
  742. int pwrc;
  743. pwrc = need_new_pw(pb, &t, bind_target_entry, pwresponse_requested);
  744. if ( bind_target_entry != NULL ) {
  745. slapi_entry_free(bind_target_entry);
  746. bind_target_entry = NULL;
  747. }
  748. switch (pwrc) {
  749. case 1:
  750. slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
  751. break;
  752. case 2:
  753. slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);
  754. break;
  755. case -1:
  756. goto out;
  757. default:
  758. break;
  759. }
  760. }
  761. /* attach the sasl data */
  762. if (slen != 0) {
  763. bvr.bv_val = (char*)sdata;
  764. bvr.bv_len = slen;
  765. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
  766. }
  767. /* send successful result */
  768. send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
  769. /* TODO: enable sasl security layer */
  770. /* remove the sasl data from the pblock */
  771. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
  772. break;
  773. case SASL_CONTINUE: /* another step needed */
  774. pb->pb_conn->c_flags |= CONN_FLAG_SASL_CONTINUE;
  775. /* attach the sasl data */
  776. bvr.bv_val = (char*)sdata;
  777. bvr.bv_len = slen;
  778. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
  779. /* send continuation result */
  780. send_ldap_result( pb, LDAP_SASL_BIND_IN_PROGRESS, NULL,
  781. NULL, 0, NULL );
  782. /* remove the sasl data from the pblock */
  783. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
  784. break;
  785. case SASL_NOMECH:
  786. send_ldap_result(pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  787. "sasl mechanism not supported", 0, NULL);
  788. break;
  789. default: /* other error */
  790. errstr = sasl_errdetail(sasl_conn);
  791. send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
  792. (char*)errstr, 0, NULL);
  793. break;
  794. }
  795. out:
  796. if (referral)
  797. slapi_entry_free(referral);
  798. if (be)
  799. slapi_be_Unlock(be);
  800. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
  801. return;
  802. }