saslbind.c 31 KB

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