saslbind.c 33 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103
  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. #ifdef CYRUS_SASL
  486. #if !defined(LINUX)
  487. static int ids_sasl_getpluginpath(sasl_conn_t *conn, const char **path)
  488. {
  489. static char *pluginpath = "../../../lib/sasl2";
  490. *path = pluginpath;
  491. return SASL_OK;
  492. }
  493. #endif
  494. #endif
  495. static sasl_callback_t ids_sasl_callbacks[] =
  496. {
  497. {
  498. SASL_CB_GETOPT,
  499. (IFP) ids_sasl_getopt,
  500. NULL
  501. },
  502. {
  503. SASL_CB_LOG,
  504. (IFP) ids_sasl_log,
  505. NULL
  506. },
  507. {
  508. SASL_CB_PROXY_POLICY,
  509. (IFP) ids_sasl_proxy_policy,
  510. NULL
  511. },
  512. {
  513. #ifdef CYRUS_SASL
  514. SASL_CB_CANON_USER,
  515. #else
  516. SASL_CB_SERVER_CANON_USER,
  517. #endif
  518. (IFP) ids_sasl_canon_user,
  519. NULL
  520. },
  521. #ifdef CYRUS_SASL
  522. /* On Linux: we use system sasl and plugins are found in the default path
  523. * /usr/lib/sasl2
  524. * On other platforms: we need to tell cyrus sasl where they are localted.
  525. */
  526. #if !defined(LINUX)
  527. {
  528. SASL_CB_GETPATH,
  529. (IFP) ids_sasl_getpluginpath,
  530. NULL
  531. },
  532. #endif
  533. #endif
  534. {
  535. SASL_CB_LIST_END,
  536. (IFP) NULL,
  537. NULL
  538. }
  539. };
  540. static const char *dn_propnames[] = { "dn", 0 };
  541. /*
  542. * initialize the sasl library
  543. */
  544. int ids_sasl_init(void)
  545. {
  546. static int inited = 0;
  547. int result;
  548. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_init\n", 0, 0, 0 );
  549. PR_ASSERT(inited == 0);
  550. inited = 1;
  551. serverfqdn = get_localhost_DNS();
  552. LDAPDebug(LDAP_DEBUG_TRACE, "sasl service fqdn is: %s\n",
  553. serverfqdn, 0, 0);
  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. #ifndef CYRUS_SASL
  561. result = sasl_server_add_plugin("USERDB", sasl_userdb_init);
  562. if (result != SASL_OK) {
  563. LDAPDebug(LDAP_DEBUG_TRACE, "failed to add LDAP sasl plugin\n",
  564. 0, 0, 0);
  565. return result;
  566. }
  567. #if defined(BUILD_GSSAPI)
  568. result = sasl_server_add_plugin("GSSAPI", sasl_gssapi_init);
  569. if (result != SASL_OK) {
  570. LDAPDebug(LDAP_DEBUG_TRACE, "failed to add LDAP gssapi plugin\n",
  571. 0, 0, 0);
  572. }
  573. #endif
  574. #endif
  575. result = sasl_auxprop_add_plugin("iDS", ids_auxprop_plug_init);
  576. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_init\n", 0, 0, 0 );
  577. return result;
  578. }
  579. /*
  580. * create a sasl server connection
  581. */
  582. void ids_sasl_server_new(Connection *conn)
  583. {
  584. int rc;
  585. sasl_conn_t *sasl_conn = NULL;
  586. struct propctx *propctx;
  587. sasl_security_properties_t secprops = {0};
  588. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_server_new (%s)\n", serverfqdn, 0, 0 );
  589. rc = sasl_server_new("ldap",
  590. serverfqdn,
  591. NULL, /* user_realm */
  592. NULL, /* iplocalport */
  593. NULL, /* ipremoteport */
  594. ids_sasl_callbacks,
  595. SASL_SUCCESS_DATA,
  596. &sasl_conn);
  597. if (rc != SASL_OK) {
  598. LDAPDebug(LDAP_DEBUG_ANY, "sasl_server_new: %s\n",
  599. sasl_errstring(rc, NULL, NULL), 0, 0);
  600. }
  601. if (rc == SASL_OK) {
  602. propctx = sasl_auxprop_getctx(sasl_conn);
  603. if (propctx != NULL) {
  604. prop_request(propctx, dn_propnames);
  605. }
  606. }
  607. /* Enable security for this connection */
  608. secprops.maxbufsize = 2048; /* DBDB: hack */
  609. secprops.max_ssf = 0xffffffff;
  610. rc = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
  611. if (rc != SASL_OK) {
  612. LDAPDebug(LDAP_DEBUG_ANY, "sasl_setprop: %s\n",
  613. sasl_errstring(rc, NULL, NULL), 0, 0);
  614. }
  615. conn->c_sasl_conn = sasl_conn;
  616. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_server_new\n", 0, 0, 0 );
  617. return;
  618. }
  619. /*
  620. * return sasl mechanisms available on this connection.
  621. * caller must free returned charray.
  622. */
  623. char **ids_sasl_listmech(Slapi_PBlock *pb)
  624. {
  625. char **ret, **others;
  626. const char *str;
  627. char *dupstr;
  628. sasl_conn_t *sasl_conn;
  629. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_listmech\n", 0, 0, 0 );
  630. PR_ASSERT(pb);
  631. /* hard-wired mechanisms and slapi plugin registered mechanisms */
  632. ret = slapi_get_supported_saslmechanisms_copy();
  633. if (pb->pb_conn == NULL) return ret;
  634. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  635. if (sasl_conn == NULL) return ret;
  636. /* sasl library mechanisms are connection dependent */
  637. PR_Lock(pb->pb_conn->c_mutex);
  638. if (sasl_listmech(sasl_conn,
  639. NULL, /* username */
  640. "", ",", "",
  641. &str, NULL, NULL) == SASL_OK) {
  642. LDAPDebug(LDAP_DEBUG_TRACE, "sasl library mechs: %s\n", str, 0, 0);
  643. /* merge into result set */
  644. dupstr = slapi_ch_strdup(str);
  645. others = str2charray(dupstr, ",");
  646. charray_merge(&ret, others, 1);
  647. charray_free(others);
  648. slapi_ch_free((void**)&dupstr);
  649. }
  650. PR_Unlock(pb->pb_conn->c_mutex);
  651. LDAPDebug( LDAP_DEBUG_TRACE, ">= ids_sasl_listmech\n", 0, 0, 0 );
  652. return ret;
  653. }
  654. /*
  655. * Determine whether a given sasl mechanism is supported by
  656. * this sasl connection. Returns true/false.
  657. */
  658. static int
  659. ids_sasl_mech_supported(Slapi_PBlock *pb, sasl_conn_t *sasl_conn, const char *mech)
  660. {
  661. int i, ret = 0;
  662. char **mechs;
  663. char *dupstr;
  664. const char *str;
  665. int sasl_result = 0;
  666. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_mech_supported\n", 0, 0, 0 );
  667. /* sasl_listmech is not thread-safe, so we lock here */
  668. PR_Lock(pb->pb_conn->c_mutex);
  669. sasl_result = sasl_listmech(sasl_conn,
  670. NULL, /* username */
  671. "", ",", "",
  672. &str, NULL, NULL);
  673. PR_Unlock(pb->pb_conn->c_mutex);
  674. if (sasl_result != SASL_OK) {
  675. return 0;
  676. }
  677. dupstr = slapi_ch_strdup(str);
  678. mechs = str2charray(dupstr, ",");
  679. for (i = 0; mechs[i] != NULL; i++) {
  680. if (strcasecmp(mech, mechs[i]) == 0) {
  681. ret = 1;
  682. break;
  683. }
  684. }
  685. charray_free(mechs);
  686. slapi_ch_free((void**)&dupstr);
  687. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_mech_supported\n", 0, 0, 0 );
  688. return ret;
  689. }
  690. /*
  691. * do a sasl bind and return a result
  692. */
  693. void ids_sasl_check_bind(Slapi_PBlock *pb)
  694. {
  695. int rc, isroot;
  696. long t;
  697. sasl_conn_t *sasl_conn;
  698. struct propctx *propctx;
  699. sasl_ssf_t *ssfp;
  700. char *activemech = NULL, *mech = NULL;
  701. char *username, *dn = NULL;
  702. const char *sdata, *errstr;
  703. unsigned slen;
  704. int continuing = 0;
  705. int pwresponse_requested = 0;
  706. LDAPControl **ctrls;
  707. struct berval bvr, *cred;
  708. struct propval dnval[2];
  709. char authtype[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */
  710. Slapi_Entry *bind_target_entry = NULL, *referral = NULL;
  711. Slapi_Backend *be = NULL;
  712. char errorbuf[BUFSIZ];
  713. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
  714. PR_ASSERT(pb);
  715. PR_ASSERT(pb->pb_conn);
  716. continuing = pb->pb_conn->c_flags & CONN_FLAG_SASL_CONTINUE;
  717. pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_CONTINUE; /* reset flag */
  718. sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
  719. if (sasl_conn == NULL) {
  720. send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  721. "sasl library unavailable", 0, NULL );
  722. return;
  723. }
  724. slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mech);
  725. slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);
  726. slapi_pblock_get(pb, SLAPI_PWPOLICY, &pwresponse_requested);
  727. PR_ASSERT(mech);
  728. PR_ASSERT(cred);
  729. /* Work around a bug in the sasl library. We've told the
  730. * library that CRAM-MD5 is disabled, but it gives us a
  731. * different error code to SASL_NOMECH.
  732. */
  733. if (!ids_sasl_mech_supported(pb, sasl_conn, mech)) {
  734. rc = SASL_NOMECH;
  735. goto sasl_check_result;
  736. }
  737. /* can't do any harm */
  738. if (cred->bv_len == 0) cred->bv_val = NULL;
  739. if (continuing) {
  740. /*
  741. * RFC 2251: a client may abort a sasl bind negotiation by
  742. * sending a bindrequest with a different value in the
  743. * mechanism field.
  744. */
  745. sasl_getprop(sasl_conn, SASL_MECHNAME, (const void**)&activemech);
  746. if (activemech == NULL) {
  747. LDAPDebug(LDAP_DEBUG_TRACE, "could not get active sasl mechanism\n", 0, 0, 0);
  748. goto sasl_start;
  749. }
  750. if (strcasecmp(activemech, mech) != 0) {
  751. LDAPDebug(LDAP_DEBUG_TRACE, "sasl mechanisms differ: active=%s current=%s\n", 0, 0, 0);
  752. goto sasl_start;
  753. }
  754. rc = sasl_server_step(sasl_conn,
  755. cred->bv_val, cred->bv_len,
  756. &sdata, &slen);
  757. goto sasl_check_result;
  758. }
  759. sasl_start:
  760. /* Check if we are already authenticated via sasl. If so,
  761. * dispose of the current sasl_conn and create a new one
  762. * using the new mechanism. We also need to do this if the
  763. * mechanism changed in the middle of the SASL authentication
  764. * process. */
  765. if ((pb->pb_conn->c_flags & CONN_FLAG_SASL_COMPLETE) || continuing) {
  766. /* reset flag */
  767. pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_COMPLETE;
  768. /* Lock the connection mutex */
  769. PR_Lock(pb->pb_conn->c_mutex);
  770. /* remove any SASL I/O from the connection */
  771. sasl_io_cleanup(pb->pb_conn);
  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. /* Unlock the connection mutex */
  777. PR_Unlock(pb->pb_conn->c_mutex);
  778. if (sasl_conn == NULL) {
  779. send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  780. "sasl library unavailable", 0, NULL );
  781. return;
  782. }
  783. }
  784. rc = sasl_server_start(sasl_conn, mech,
  785. cred->bv_val, cred->bv_len,
  786. &sdata, &slen);
  787. sasl_check_result:
  788. switch (rc) {
  789. case SASL_OK: /* complete */
  790. /* Set a flag to signify that sasl bind is complete */
  791. pb->pb_conn->c_flags |= CONN_FLAG_SASL_COMPLETE;
  792. /* retrieve the authenticated username */
  793. if (sasl_getprop(sasl_conn, SASL_USERNAME,
  794. (const void**)&username) != SASL_OK) {
  795. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  796. "could not obtain sasl username", 0, NULL);
  797. break;
  798. }
  799. LDAPDebug(LDAP_DEBUG_TRACE, "sasl authenticated mech=%s user=%s\n",
  800. mech, username, 0);
  801. /*
  802. * Retrieve the DN corresponding to the authenticated user.
  803. * This should have been set by the user canon callback
  804. * in an auxiliary property called "dn".
  805. */
  806. propctx = sasl_auxprop_getctx(sasl_conn);
  807. if (prop_getnames(propctx, dn_propnames, dnval) == 1) {
  808. if (dnval[0].values && dnval[0].values[0]) {
  809. dn = slapi_ch_strdup(dnval[0].values[0]);
  810. }
  811. }
  812. if (dn == NULL) {
  813. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  814. "could not get auth dn from sasl", 0, NULL);
  815. break;
  816. }
  817. isroot = slapi_dn_isroot(dn);
  818. if (!isroot )
  819. {
  820. /* check if the account is locked */
  821. bind_target_entry = get_entry(pb, dn);
  822. if ( bind_target_entry == NULL )
  823. {
  824. break;
  825. }
  826. if ( check_account_lock(pb, bind_target_entry, pwresponse_requested) == 1) {
  827. slapi_entry_free(bind_target_entry);
  828. break;
  829. }
  830. }
  831. /* see if we negotiated a security layer */
  832. if ((sasl_getprop(sasl_conn, SASL_SSF,
  833. (const void**)&ssfp) == SASL_OK) && (*ssfp > 0)) {
  834. LDAPDebug(LDAP_DEBUG_TRACE, "sasl ssf=%u\n", (unsigned)*ssfp, 0, 0);
  835. if (pb->pb_conn->c_flags & CONN_FLAG_SSL) {
  836. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  837. "sasl encryption not supported over ssl",
  838. 0, NULL);
  839. if ( bind_target_entry != NULL )
  840. slapi_entry_free(bind_target_entry);
  841. break;
  842. } else {
  843. /* Enable SASL I/O on the connection now */
  844. /* Note that this doesn't go into effect until the next _read_ operation is done */
  845. if (0 != sasl_io_enable(pb->pb_conn) ) {
  846. send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
  847. "failed to enable sasl i/o",
  848. 0, NULL);
  849. }
  850. }
  851. }
  852. /* set the connection bind credentials */
  853. PR_snprintf(authtype, sizeof(authtype), "%s%s", SLAPD_AUTH_SASL, mech);
  854. bind_credentials_set(pb->pb_conn, authtype, dn,
  855. NULL, NULL, NULL, bind_target_entry);
  856. /* set the auth response control if requested */
  857. slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrls);
  858. if (slapi_control_present(ctrls, LDAP_CONTROL_AUTH_REQUEST,
  859. NULL, NULL)) {
  860. slapi_add_auth_response_control(pb, dn);
  861. }
  862. if (slapi_mapping_tree_select(pb, &be, &referral, errorbuf) != LDAP_SUCCESS) {
  863. send_nobackend_ldap_result( pb );
  864. be = NULL;
  865. LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_check_bind\n", 0, 0, 0 );
  866. return;
  867. }
  868. if (referral) {
  869. send_referrals_from_entry(pb,referral);
  870. goto out;
  871. }
  872. slapi_pblock_set( pb, SLAPI_BACKEND, be );
  873. slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
  874. set_db_default_result_handlers(pb);
  875. /* check password expiry */
  876. if (!isroot) {
  877. int pwrc;
  878. pwrc = need_new_pw(pb, &t, bind_target_entry, pwresponse_requested);
  879. if ( bind_target_entry != NULL ) {
  880. slapi_entry_free(bind_target_entry);
  881. bind_target_entry = NULL;
  882. }
  883. switch (pwrc) {
  884. case 1:
  885. slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
  886. break;
  887. case 2:
  888. slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);
  889. break;
  890. case -1:
  891. goto out;
  892. default:
  893. break;
  894. }
  895. }
  896. /* attach the sasl data */
  897. if (slen != 0) {
  898. bvr.bv_val = (char*)sdata;
  899. bvr.bv_len = slen;
  900. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
  901. }
  902. /* send successful result */
  903. send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
  904. /* TODO: enable sasl security layer */
  905. /* remove the sasl data from the pblock */
  906. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
  907. break;
  908. case SASL_CONTINUE: /* another step needed */
  909. pb->pb_conn->c_flags |= CONN_FLAG_SASL_CONTINUE;
  910. /* attach the sasl data */
  911. bvr.bv_val = (char*)sdata;
  912. bvr.bv_len = slen;
  913. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
  914. /* send continuation result */
  915. send_ldap_result( pb, LDAP_SASL_BIND_IN_PROGRESS, NULL,
  916. NULL, 0, NULL );
  917. /* remove the sasl data from the pblock */
  918. slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
  919. break;
  920. case SASL_NOMECH:
  921. send_ldap_result(pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
  922. "sasl mechanism not supported", 0, NULL);
  923. break;
  924. default: /* other error */
  925. errstr = sasl_errdetail(sasl_conn);
  926. send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
  927. (char*)errstr, 0, NULL);
  928. break;
  929. }
  930. out:
  931. if (referral)
  932. slapi_entry_free(referral);
  933. if (be)
  934. slapi_be_Unlock(be);
  935. LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
  936. return;
  937. }