saslbind.c 29 KB

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