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.
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.
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.
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
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.
40
* Hewlett-Packard Development Company, L.P.
41
* Bugfix for bug #193297
43
* END COPYRIGHT BLOCK **/
58
static char *serverfqdn;
61
* utility functions needed by the sasl library
63
void *nssasl_mutex_alloc(void)
68
int nssasl_mutex_lock(void *mutex)
74
int nssasl_mutex_unlock(void *mutex)
76
if (PR_Unlock(mutex) == PR_SUCCESS) return SASL_OK;
80
void nssasl_mutex_free(void *mutex)
82
PR_DestroyLock(mutex);
85
void nssasl_free(void *ptr)
90
static Slapi_ComponentId *sasl_component_id = NULL;
92
static void generate_component_id()
94
if (NULL == sasl_component_id) {
95
sasl_component_id = generate_componentid(NULL /* Not a plugin */,
100
static Slapi_ComponentId *sasl_get_component_id()
102
return sasl_component_id;
106
* sasl library callbacks
110
* We've added this auxprop stuff as a workaround for RHDS bug 166229
111
* and FDS bug 166081. The problem is that sasldb is configured and
112
* enabled by default, but we don't want or need to use it. What
113
* happens after canon_user is that sasl looks up any auxiliary
114
* properties of that user. If you don't tell sasl which auxprop
115
* plug-in to use, it tries all of them, including sasldb. In order
116
* to avoid this, we create a "dummy" auxprop plug-in with the name
117
* "iDS" and tell sasl to use this plug-in for auxprop lookups.
118
* The reason we don't need auxprops is because when we grab the user's
119
* entry from the internal database, at the same time we get any other
120
* properties we need - it's more efficient that way.
122
#if SASL_AUXPROP_PLUG_VERSION > 4
123
static int ids_auxprop_lookup(
125
static void ids_auxprop_lookup(
128
sasl_server_params_t *sparams,
133
/* do nothing - we don't need auxprops - we just do this to avoid
134
sasldb_auxprop_lookup */
135
#if SASL_AUXPROP_PLUG_VERSION > 4
140
static sasl_auxprop_plug_t ids_auxprop_plugin = {
143
NULL, /* glob_context */
144
NULL, /* auxprop_free */
145
ids_auxprop_lookup, /* auxprop_lookup */
147
NULL /* auxprop_store */
150
int ids_auxprop_plug_init(const sasl_utils_t *utils,
153
sasl_auxprop_plug_t **plug,
154
const char *plugname)
156
if(!out_version || !plug) return SASL_BADPARAM;
158
if(max_version < SASL_AUXPROP_PLUG_VERSION) return SASL_BADVERS;
160
*out_version = SASL_AUXPROP_PLUG_VERSION;
162
*plug = &ids_auxprop_plugin;
167
static int ids_sasl_getopt(
169
const char *plugin_name,
177
LDAPDebug(LDAP_DEBUG_TRACE, "ids_sasl_getopt: plugin=%s option=%s\n",
178
plugin_name ? plugin_name : "", option, 0);
180
if (len == NULL) len = &tmplen;
185
if (strcasecmp(option, "enable") == 0) {
186
*result = "USERDB/DIGEST-MD5,GSSAPI/GSSAPI";
187
} else if (strcasecmp(option, "has_plain_passwords") == 0) {
189
} else if (strcasecmp(option, "LOG_LEVEL") == 0) {
190
if (LDAPDebugLevelIsSet(LDAP_DEBUG_TRACE)) {
191
*result = "6"; /* SASL_LOG_TRACE */
193
} else if (strcasecmp(option, "auxprop_plugin") == 0) {
195
} else if (strcasecmp(option, "mech_list") == 0){
196
*result = config_get_allowed_sasl_mechs();
199
if (*result) *len = strlen(*result);
204
static int ids_sasl_log(
211
case SASL_LOG_ERR: /* log unusual errors (default) */
212
slapi_log_error(SLAPI_LOG_FATAL, "sasl", "%s\n", message);
215
case SASL_LOG_FAIL: /* log all authentication failures */
216
case SASL_LOG_WARN: /* log non-fatal warnings */
217
case SASL_LOG_NOTE: /* more verbose than LOG_WARN */
218
case SASL_LOG_DEBUG: /* more verbose than LOG_NOTE */
219
case SASL_LOG_TRACE: /* traces of internal protocols */
220
case SASL_LOG_PASS: /* traces of internal protocols, including
222
LDAPDebug(LDAP_DEBUG_TRACE, "sasl(%d): %s\n", level, message, 0);
225
case SASL_LOG_NONE: /* don't log anything */
232
static int ids_sasl_proxy_policy(
235
const char *requested_user, int rlen,
236
const char *auth_identity, int alen,
237
const char *def_realm, int urlen,
238
struct propctx *propctx
241
int retVal = SASL_OK;
242
/* do not permit sasl proxy authorization */
243
/* if the auth_identity is null or empty string, allow the sasl request to go thru */
244
if ( (auth_identity != NULL ) && ( strlen(auth_identity) > 0 ) ) {
245
Slapi_DN authId , reqUser;
246
slapi_sdn_init_dn_byref(&authId,auth_identity);
247
slapi_sdn_init_dn_byref(&reqUser,requested_user);
248
if (slapi_sdn_compare((const Slapi_DN *)&reqUser,(const Slapi_DN *) &authId) != 0) {
249
LDAPDebug(LDAP_DEBUG_TRACE,
250
"sasl proxy auth not permitted authid=%s user=%s\n",
251
auth_identity, requested_user, 0);
252
retVal = SASL_NOAUTHZ;
254
slapi_sdn_done(&authId);
255
slapi_sdn_done(&reqUser);
260
static void ids_sasl_user_search(
271
Slapi_Entry **entries = NULL;
272
Slapi_PBlock *pb = NULL;
275
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search basedn=\"%s\" filter=\"%s\"\n", basedn, filter, 0);
277
/* TODO: set size and time limits */
278
pb = slapi_pblock_new();
280
LDAPDebug(LDAP_DEBUG_TRACE, "null pblock for search_internal_pb\n", 0, 0, 0);
284
slapi_search_internal_set_pb(pb, basedn, scope, filter, attrs, attrsonly, ctrls,
285
NULL, sasl_get_component_id(), 0);
287
slapi_search_internal_pb(pb);
289
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &ret);
290
if (ret != LDAP_SUCCESS) {
291
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search failed basedn=\"%s\" "
292
"filter=\"%s\": %s\n",
293
basedn, filter, ldap_err2string(ret));
297
slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
298
if ((entries == NULL) || (entries[0] == NULL)) {
299
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found no entries\n",
304
for (i = 0; entries[i]; i++) {
307
slapi_entry_free(*ep);
309
*ep = slapi_entry_dup(entries[i]);
310
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found dn=%s\n",
311
slapi_entry_get_dn(*ep), 0, 0);
317
slapi_free_search_results_internal(pb);
318
slapi_pblock_destroy(pb);
325
* Search for an entry representing the sasl user.
327
static Slapi_Entry *ids_sasl_user_to_entry(
331
const char *user_realm
334
LDAPControl **ctrls = NULL;
335
sasl_map_data *map = NULL;
336
Slapi_Entry *entry = NULL;
340
int attrsonly = 0, scope = LDAP_SCOPE_SUBTREE;
344
/* Check for wildcards in the authid and realm. If we encounter one,
345
* just fail the mapping without performing a costly internal search. */
346
if (user && strchr(user, '*')) {
347
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search encountered a wildcard in "
348
"the authid. Not attempting to map to entry. (authid=%s)\n", user, 0, 0);
350
} else if (user_realm && strchr(user_realm, '*')) {
351
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search encountered a wildcard in "
352
"the realm. Not attempting to map to entry. (realm=%s)\n", user_realm, 0, 0);
356
/* New regex-based identity mapping */
357
sasl_map_read_lock();
359
regexmatch = sasl_map_domap(&map, (char*)user, (char*)user_realm, &base, &filter);
361
ids_sasl_user_search(base, scope, filter,
362
ctrls, attrs, attrsonly,
365
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found this entry: dn:%s, "
366
"matching filter=%s\n", entry->e_sdn.dn, filter, 0);
367
} else if (found == 0) {
368
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found no entries matching "
369
"filter=%s\n", filter, 0, 0);
371
LDAPDebug(LDAP_DEBUG_TRACE, "sasl user search found more than one entry "
372
"matching filter=%s\n", filter, 0, 0);
374
slapi_entry_free(entry);
379
/* Free the filter etc */
380
slapi_ch_free_string(&base);
381
slapi_ch_free_string(&filter);
383
/* If we didn't find an entry, look at the other maps */
388
/* break if the next map is NULL, or we are not checking all the mappings */
389
if(map == NULL || !config_get_sasl_mapping_fallback()){
393
sasl_map_read_unlock();
398
static char *buf2str(const char *buf, unsigned buflen)
402
ret = (char*)slapi_ch_malloc(buflen+1);
403
memcpy(ret, buf, buflen);
409
/* Note that in this sasl1 API, when it says 'authid' it really means 'authzid'. */
410
static int ids_sasl_canon_user(
413
const char *userbuf, unsigned ulen,
414
unsigned flags, const char *user_realm,
415
char *out_user, unsigned out_umax, unsigned *out_ulen
418
struct propctx *propctx = sasl_auxprop_getctx(conn);
419
Slapi_Entry *entry = NULL;
420
Slapi_DN *sdn = NULL;
427
int returnvalue = SASL_FAIL;
429
user = buf2str(userbuf, ulen);
433
LDAPDebug(LDAP_DEBUG_TRACE,
434
"ids_sasl_canon_user(user=%s, realm=%s)\n",
435
user, user_realm ? user_realm : "", 0);
437
sasl_getprop(conn, SASL_MECHNAME, (const void**)&mech);
439
LDAPDebug0Args(LDAP_DEBUG_TRACE, "Unable to read SASL mechanism while "
440
"canonifying user.\n")
444
if (strncasecmp(user, "dn:", 3) == 0) {
445
sdn = slapi_sdn_new();
446
slapi_sdn_set_dn_byval(sdn, user+3);
447
isroot = slapi_dn_isroot(slapi_sdn_get_ndn(sdn));
451
/* special case directory manager */
452
dn = slapi_sdn_get_ndn(sdn);
453
pw = config_get_rootpw();
454
*out_ulen = PR_snprintf(out_user, out_umax, "dn: %s", dn);
455
} else if (strcasecmp(mech, "ANONYMOUS") == 0) {
456
/* SASL doesn't allow us to set the username to an empty string,
457
* so we just set it to anonymous. */
459
PL_strncpyz(out_user, dn, out_umax);
460
/* the length of out_user needs to be set for Cyrus SASL */
461
*out_ulen = strlen(out_user);
463
/* map the sasl username into an entry */
464
entry = ids_sasl_user_to_entry(conn, context, user, user_realm);
466
/* Specific return value is supposed to be set instead of
467
an generic error (SASL_FAIL) for Cyrus SASL */
468
returnvalue = SASL_NOAUTHZ;
471
dn = slapi_entry_get_ndn(entry);
472
pw = slapi_entry_attr_get_charptr(entry, "userpassword");
473
*out_ulen = PR_snprintf(out_user, out_umax, "dn: %s", dn);
476
/* Need to set dn property to an empty string for the ANONYMOUS mechanism. This
477
* property determines what the bind identity will be if authentication succeeds. */
478
if (strcasecmp(mech, "ANONYMOUS") == 0) {
479
if (prop_set(propctx, "dn", "", -1) != 0) {
480
LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(dn) failed\n", 0, 0, 0);
483
} else if (prop_set(propctx, "dn", dn, -1) != 0) {
484
LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(dn) failed\n", 0, 0, 0);
488
/* We need to check if the first character of pw is an opening
489
* brace since strstr will simply return it's first argument if
490
* it is an empty string. */
491
if (pw && (*pw == '{')) {
492
if (strchr( pw, '}' )) {
493
/* This password is stored in a non-cleartext format.
494
* Any SASL mechanism that actually needs the
495
* password is going to fail. We should print a warning
496
* to aid in troubleshooting. */
497
LDAPDebug(LDAP_DEBUG_TRACE, "Warning: Detected a sasl bind attempt by an "
498
"entry whose password is stored in a non-cleartext format. This "
499
"will not work for mechanisms which require a cleartext password "
500
"such as DIGEST-MD5 and CRAM-MD5.\n", 0, 0, 0);
502
/* This password doesn't have a storage prefix but
503
* just happens to start with the '{' character. We'll
504
* assume that it's just a cleartext password without
505
* the proper storage prefix. */
509
/* This password has no storage prefix, or the password is empty */
514
/* older versions of sasl do not have SASL_AUX_PASSWORD_PROP, so omit it */
515
#ifdef SASL_AUX_PASSWORD_PROP
516
if (prop_set(propctx, SASL_AUX_PASSWORD_PROP, clear, -1) != 0) {
517
/* Failure is benign here because some mechanisms don't support this property */
518
/*LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
521
#endif /* SASL_AUX_PASSWORD_PROP */
522
if (prop_set(propctx, SASL_AUX_PASSWORD, clear, -1) != 0) {
523
/* Failure is benign here because some mechanisms don't support this property */
524
/*LDAPDebug(LDAP_DEBUG_TRACE, "prop_set(userpassword) failed\n", 0, 0, 0);
529
slapi_entry_free(entry);
530
slapi_ch_free((void**)&user);
531
slapi_ch_free((void**)&pw);
532
slapi_sdn_free(&sdn);
537
slapi_entry_free(entry);
538
slapi_ch_free((void**)&user);
539
slapi_ch_free((void**)&pw);
540
slapi_sdn_free(&sdn);
545
static int ids_sasl_getpluginpath(sasl_conn_t *conn, const char **path)
547
/* Try to get path from config, otherwise check for SASL_PATH environment
548
* variable. If neither of these are set, default to /usr/lib64/sasl2 on
549
* 64-bit Linux machines, and /usr/lib/sasl2 on all other platforms.
551
char *pluginpath = config_get_saslpath();
552
if ((!pluginpath) || (*pluginpath == '\0')) {
553
if (!(pluginpath = getenv("SASL_PATH"))) {
554
#if defined(LINUX) && defined(__LP64__)
555
pluginpath = "/usr/lib64/sasl2";
556
if (PR_SUCCESS != PR_Access(pluginpath, PR_ACCESS_EXISTS)) {
557
/* Since the path does not exist, try gnu triplet. */
558
pluginpath = "/usr/lib/x86_64-linux-gnu/sasl2";
561
pluginpath = "/usr/lib/sasl2";
562
if (PR_SUCCESS != PR_Access(pluginpath, PR_ACCESS_EXISTS)) {
563
/* Since the path does not exist, try gnu triplet. */
564
pluginpath = "/usr/lib/i386-linux-gnu/sasl2";
573
static sasl_callback_t ids_sasl_callbacks[] =
577
(IFP) ids_sasl_getopt,
586
SASL_CB_PROXY_POLICY,
587
(IFP) ids_sasl_proxy_policy,
592
(IFP) ids_sasl_canon_user,
597
(IFP) ids_sasl_getpluginpath,
607
static const char *dn_propnames[] = { "dn", 0 };
610
* initialize the sasl library
612
int ids_sasl_init(void)
614
static int inited = 0;
617
LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_init\n", 0, 0, 0 );
619
PR_ASSERT(inited == 0);
622
serverfqdn = get_localhost_DNS();
624
LDAPDebug(LDAP_DEBUG_TRACE, "sasl service fqdn is: %s\n",
627
/* get component ID for internal operations */
628
generate_component_id();
630
/* Set SASL memory allocation callbacks */
632
(sasl_malloc_t *)slapi_ch_malloc,
633
(sasl_calloc_t *)slapi_ch_calloc,
634
(sasl_realloc_t *)slapi_ch_realloc,
635
(sasl_free_t *)nssasl_free );
637
/* Set SASL mutex callbacks */
639
(sasl_mutex_alloc_t *)nssasl_mutex_alloc,
640
(sasl_mutex_lock_t *)nssasl_mutex_lock,
641
(sasl_mutex_unlock_t *)nssasl_mutex_unlock,
642
(sasl_mutex_free_t *)nssasl_mutex_free);
644
result = sasl_server_init(ids_sasl_callbacks, "iDS");
646
if (result != SASL_OK) {
647
LDAPDebug(LDAP_DEBUG_TRACE, "failed to initialize sasl library\n",
652
result = sasl_auxprop_add_plugin("iDS", ids_auxprop_plug_init);
654
LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_init\n", 0, 0, 0 );
660
* create a sasl server connection
662
void ids_sasl_server_new(Connection *conn)
665
sasl_conn_t *sasl_conn = NULL;
666
struct propctx *propctx;
667
sasl_security_properties_t secprops = {0};
669
LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_server_new (%s)\n", serverfqdn, 0, 0 );
671
rc = sasl_server_new("ldap",
673
NULL, /* user_realm */
674
NULL, /* iplocalport */
675
NULL, /* ipremoteport */
681
LDAPDebug(LDAP_DEBUG_ANY, "sasl_server_new: %s\n",
682
sasl_errstring(rc, NULL, NULL), 0, 0);
686
propctx = sasl_auxprop_getctx(sasl_conn);
687
if (propctx != NULL) {
688
prop_request(propctx, dn_propnames);
692
/* Enable security for this connection */
693
secprops.maxbufsize = config_get_sasl_maxbufsize();
694
secprops.max_ssf = 0xffffffff;
695
secprops.min_ssf = config_get_minssf();
696
/* If anonymous access is disabled, set the appropriate flag */
697
if (config_get_anon_access_switch() != SLAPD_ANON_ACCESS_ON) {
698
secprops.security_flags = SASL_SEC_NOANONYMOUS;
701
rc = sasl_setprop(sasl_conn, SASL_SEC_PROPS, &secprops);
704
LDAPDebug(LDAP_DEBUG_ANY, "sasl_setprop: %s\n",
705
sasl_errstring(rc, NULL, NULL), 0, 0);
708
conn->c_sasl_conn = sasl_conn;
709
conn->c_sasl_ssf = 0;
711
LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_server_new\n", 0, 0, 0 );
717
* return sasl mechanisms available on this connection.
718
* caller must free returned charray.
720
char **ids_sasl_listmech(Slapi_PBlock *pb)
722
char **ret, **others;
725
sasl_conn_t *sasl_conn;
727
LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_listmech\n", 0, 0, 0 );
731
/* hard-wired mechanisms and slapi plugin registered mechanisms */
732
ret = slapi_get_supported_saslmechanisms_copy();
734
if (pb->pb_conn == NULL) return ret;
736
sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
737
if (sasl_conn == NULL) return ret;
739
/* sasl library mechanisms are connection dependent */
740
PR_Lock(pb->pb_conn->c_mutex);
741
if (sasl_listmech(sasl_conn,
744
&str, NULL, NULL) == SASL_OK) {
745
LDAPDebug(LDAP_DEBUG_TRACE, "sasl library mechs: %s\n", str, 0, 0);
746
/* merge into result set */
747
dupstr = slapi_ch_strdup(str);
748
others = slapi_str2charray_ext(dupstr, ",", 0 /* don't list duplicate mechanisms */);
749
charray_merge(&ret, others, 1);
750
charray_free(others);
751
slapi_ch_free((void**)&dupstr);
753
PR_Unlock(pb->pb_conn->c_mutex);
755
LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_listmech\n", 0, 0, 0 );
761
* Determine whether a given sasl mechanism is supported by
762
* this sasl connection. Returns true/false.
763
* NOTE: caller must lock pb->pb_conn->c_mutex
766
ids_sasl_mech_supported(Slapi_PBlock *pb, const char *mech)
773
sasl_conn_t *sasl_conn = (sasl_conn_t *)pb->pb_conn->c_sasl_conn;
775
LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_mech_supported\n", 0, 0, 0 );
778
/* sasl_listmech is not thread-safe - caller must lock pb_conn */
779
sasl_result = sasl_listmech(sasl_conn,
783
if (sasl_result != SASL_OK) {
787
dupstr = slapi_ch_strdup(str);
788
mechs = slapi_str2charray(dupstr, ",");
790
for (i = 0; mechs[i] != NULL; i++) {
791
if (strcasecmp(mech, mechs[i]) == 0) {
798
slapi_ch_free((void**)&dupstr);
800
LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_mech_supported\n", 0, 0, 0 );
806
* do a sasl bind and return a result
808
void ids_sasl_check_bind(Slapi_PBlock *pb)
812
sasl_conn_t *sasl_conn;
813
struct propctx *propctx;
815
char *activemech = NULL, *mech = NULL;
816
char *username, *dn = NULL;
817
const char *normdn = NULL;
818
Slapi_DN *sdn = NULL;
819
const char *sdata, *errstr;
822
int pwresponse_requested = 0;
824
struct berval bvr, *cred;
825
struct propval dnval[2];
826
char authtype[256]; /* >26 (strlen(SLAPD_AUTH_SASL)+SASL_MECHNAMEMAX+1) */
827
Slapi_Entry *bind_target_entry = NULL, *referral = NULL;
828
Slapi_Backend *be = NULL;
829
char errorbuf[BUFSIZ];
831
LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );
834
PR_ASSERT(pb->pb_conn);
836
PR_Lock(pb->pb_conn->c_mutex); /* BIG LOCK */
837
continuing = pb->pb_conn->c_flags & CONN_FLAG_SASL_CONTINUE;
838
pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_CONTINUE; /* reset flag */
840
sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
841
if (sasl_conn == NULL) {
842
PR_Unlock(pb->pb_conn->c_mutex);
843
send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
844
"sasl library unavailable", 0, NULL );
848
slapi_pblock_get(pb, SLAPI_BIND_SASLMECHANISM, &mech);
849
slapi_pblock_get(pb, SLAPI_BIND_CREDENTIALS, &cred);
850
slapi_pblock_get(pb, SLAPI_PWPOLICY, &pwresponse_requested);
854
/* Work around a bug in the sasl library. We've told the
855
* library that CRAM-MD5 is disabled, but it gives us a
856
* different error code to SASL_NOMECH. Must be called
857
* while holding the pb_conn lock
859
if (!ids_sasl_mech_supported(pb, mech)) {
861
goto sasl_check_result;
864
/* can't do any harm */
865
if (cred->bv_len == 0) cred->bv_val = NULL;
869
* RFC 2251: a client may abort a sasl bind negotiation by
870
* sending a bindrequest with a different value in the
873
sasl_getprop(sasl_conn, SASL_MECHNAME, (const void**)&activemech);
874
if (activemech == NULL) {
875
LDAPDebug(LDAP_DEBUG_TRACE, "could not get active sasl mechanism\n", 0, 0, 0);
878
if (strcasecmp(activemech, mech) != 0) {
879
LDAPDebug(LDAP_DEBUG_TRACE, "sasl mechanisms differ: active=%s current=%s\n", 0, 0, 0);
883
rc = sasl_server_step(sasl_conn,
884
cred->bv_val, cred->bv_len,
886
goto sasl_check_result;
891
/* Check if we are already authenticated via sasl. If so,
892
* dispose of the current sasl_conn and create a new one
893
* using the new mechanism. We also need to do this if the
894
* mechanism changed in the middle of the SASL authentication
896
if ((pb->pb_conn->c_flags & CONN_FLAG_SASL_COMPLETE) || continuing) {
897
Slapi_Operation *operation;
898
slapi_pblock_get( pb, SLAPI_OPERATION, &operation);
899
slapi_log_error(SLAPI_LOG_CONNS, "ids_sasl_check_bind",
900
"cleaning up sasl IO conn=%" NSPRIu64 " op=%d complete=%d continuing=%d\n",
901
(long long unsigned int)pb->pb_conn->c_connid, operation->o_opid,
902
(pb->pb_conn->c_flags & CONN_FLAG_SASL_COMPLETE), continuing);
904
pb->pb_conn->c_flags &= ~CONN_FLAG_SASL_COMPLETE;
906
/* remove any SASL I/O from the connection */
907
connection_set_io_layer_cb(pb->pb_conn, NULL, sasl_io_cleanup, NULL);
909
/* dispose of sasl_conn and create a new sasl_conn */
910
sasl_dispose(&sasl_conn);
911
ids_sasl_server_new(pb->pb_conn);
912
sasl_conn = (sasl_conn_t*)pb->pb_conn->c_sasl_conn;
914
if (sasl_conn == NULL) {
915
send_ldap_result( pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
916
"sasl library unavailable", 0, NULL );
917
PR_Unlock(pb->pb_conn->c_mutex); /* BIG LOCK */
922
rc = sasl_server_start(sasl_conn, mech,
923
cred->bv_val, cred->bv_len,
929
case SASL_OK: /* complete */
930
/* retrieve the authenticated username */
931
if (sasl_getprop(sasl_conn, SASL_USERNAME,
932
(const void**)&username) != SASL_OK) {
933
PR_Unlock(pb->pb_conn->c_mutex); /* BIG LOCK */
934
send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
935
"could not obtain sasl username", 0, NULL);
939
LDAPDebug(LDAP_DEBUG_TRACE, "sasl authenticated mech=%s user=%s\n",
943
* Retrieve the DN corresponding to the authenticated user.
944
* This should have been set by the user canon callback
945
* in an auxiliary property called "dn".
947
propctx = sasl_auxprop_getctx(sasl_conn);
948
if (prop_getnames(propctx, dn_propnames, dnval) == 1) {
949
if (dnval[0].values && dnval[0].values[0]) {
950
dn = slapi_ch_smprintf("%s", dnval[0].values[0]);
954
PR_Unlock(pb->pb_conn->c_mutex); /* BIG LOCK */
955
send_ldap_result(pb, LDAP_OPERATIONS_ERROR, NULL,
956
"could not get auth dn from sasl", 0, NULL);
960
/* clean up already set TARGET */
961
slapi_pblock_get(pb, SLAPI_BIND_TARGET_SDN, &sdn);
962
slapi_sdn_free(&sdn);
964
sdn = slapi_sdn_new_dn_passin(dn);
965
normdn = slapi_sdn_get_dn(sdn);
967
slapi_pblock_set( pb, SLAPI_BIND_TARGET_SDN, sdn );
969
if ((sasl_getprop(sasl_conn, SASL_SSF,
970
(const void**)&ssfp) == SASL_OK) && (*ssfp > 0)) {
971
LDAPDebug(LDAP_DEBUG_TRACE, "sasl ssf=%u\n", (unsigned)*ssfp, 0, 0);
976
/* Set a flag to signify that sasl bind is complete */
977
pb->pb_conn->c_flags |= CONN_FLAG_SASL_COMPLETE;
978
/* note - we set this here in case there are pre-bind
979
plugins that want to know what the negotiated
980
ssf is - but this happens before we actually set
981
up the socket for SASL encryption - so one
982
consequence is that we attempt to do sasl
983
encryption on the connection after the pre-bind
984
plugin has been called, and sasl encryption fails
985
and the operation returns an error */
986
pb->pb_conn->c_sasl_ssf = (unsigned)*ssfp;
988
/* set the connection bind credentials */
989
PR_snprintf(authtype, sizeof(authtype), "%s%s", SLAPD_AUTH_SASL, mech);
990
/* normdn is consumed by bind_credentials_set_nolock */
991
bind_credentials_set_nolock(pb->pb_conn, authtype,
992
slapi_ch_strdup(normdn),
993
NULL, NULL, NULL, bind_target_entry);
995
PR_Unlock(pb->pb_conn->c_mutex); /* BIG LOCK */
997
if (plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) != 0){
1001
isroot = slapi_dn_isroot(normdn);
1005
/* check if the account is locked */
1006
bind_target_entry = get_entry(pb, normdn);
1007
if ( bind_target_entry == NULL )
1011
if ( slapi_check_account_lock(pb, bind_target_entry, pwresponse_requested, 1, 1) == 1) {
1016
/* set the auth response control if requested */
1017
slapi_pblock_get(pb, SLAPI_REQCONTROLS, &ctrls);
1018
if (slapi_control_present(ctrls, LDAP_CONTROL_AUTH_REQUEST,
1020
slapi_add_auth_response_control(pb, normdn);
1023
if (slapi_mapping_tree_select(pb, &be, &referral, errorbuf) != LDAP_SUCCESS) {
1024
send_nobackend_ldap_result( pb );
1026
LDAPDebug( LDAP_DEBUG_TRACE, "<= ids_sasl_check_bind\n", 0, 0, 0 );
1031
send_referrals_from_entry(pb,referral);
1035
slapi_pblock_set( pb, SLAPI_BACKEND, be );
1037
slapi_pblock_set( pb, SLAPI_PLUGIN, be->be_database );
1038
set_db_default_result_handlers(pb);
1040
/* check password expiry */
1044
pwrc = need_new_pw(pb, &t, bind_target_entry, pwresponse_requested);
1048
slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRED, 0);
1051
slapi_add_pwd_control(pb, LDAP_CONTROL_PWEXPIRING, t);
1060
/* attach the sasl data */
1062
bvr.bv_val = (char*)sdata;
1064
slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
1067
/* see if we negotiated a security layer */
1069
/* Enable SASL I/O on the connection */
1070
PR_Lock(pb->pb_conn->c_mutex);
1071
connection_set_io_layer_cb(pb->pb_conn, sasl_io_enable, NULL, NULL);
1072
PR_Unlock(pb->pb_conn->c_mutex);
1075
/* send successful result */
1076
send_ldap_result( pb, LDAP_SUCCESS, NULL, NULL, 0, NULL );
1078
/* remove the sasl data from the pblock */
1079
slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
1083
case SASL_CONTINUE: /* another step needed */
1084
pb->pb_conn->c_flags |= CONN_FLAG_SASL_CONTINUE;
1085
PR_Unlock(pb->pb_conn->c_mutex); /* BIG LOCK */
1087
if (plugin_call_plugins( pb, SLAPI_PLUGIN_PRE_BIND_FN ) != 0){
1091
/* attach the sasl data */
1092
bvr.bv_val = (char*)sdata;
1094
slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, &bvr);
1096
/* send continuation result */
1097
send_ldap_result( pb, LDAP_SASL_BIND_IN_PROGRESS, NULL,
1100
/* remove the sasl data from the pblock */
1101
slapi_pblock_set(pb, SLAPI_BIND_RET_SASLCREDS, NULL);
1107
PR_Unlock(pb->pb_conn->c_mutex); /* BIG LOCK */
1108
send_ldap_result(pb, LDAP_AUTH_METHOD_NOT_SUPPORTED, NULL,
1109
"sasl mechanism not supported", 0, NULL);
1112
default: /* other error */
1113
errstr = sasl_errdetail(sasl_conn);
1115
PR_Unlock(pb->pb_conn->c_mutex); /* BIG LOCK */
1116
send_ldap_result(pb, LDAP_INVALID_CREDENTIALS, NULL,
1117
(char*)errstr, 0, NULL);
1123
slapi_entry_free(referral);
1125
slapi_be_Unlock(be);
1126
if (bind_target_entry)
1127
slapi_entry_free(bind_target_entry);
1129
LDAPDebug( LDAP_DEBUG_TRACE, "=> ids_sasl_check_bind\n", 0, 0, 0 );