saslbind.c 35 KB

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