bind.c 15.2 KB
Newer Older
Kurt Zeilenga's avatar
Kurt Zeilenga committed
1
/* bind.c - decode an ldap bind operation and pass it to a backend db */
2
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
4
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 1998-2020 The OpenLDAP Foundation.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
7
8
9
10
11
12
13
14
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
15
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
16
/* Portions Copyright (c) 1995 Regents of the University of Michigan.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
17
18
19
20
21
22
23
24
25
26
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that this notice is preserved and that due credit is given
 * to the University of Michigan at Ann Arbor. The name of the University
 * may not be used to endorse or promote products derived from this
 * software without specific prior written permission. This software
 * is provided ``as is'' without express or implied warranty.
 */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
27
28
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
29
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
30
31
32
33

#include <ac/string.h>
#include <ac/socket.h>

34
#include "lutil.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
35
#include "slap.h"
36

37
int
Kurt Zeilenga's avatar
Kurt Zeilenga committed
38
do_bind(
39
    Operation	*op,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
40
    SlapReply	*rs )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
41
{
42
43
	BerElement *ber = op->o_ber;
	ber_int_t version;
44
	ber_tag_t method;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
45
46
	struct berval mech = BER_BVNULL;
	struct berval dn = BER_BVNULL;
47
	ber_tag_t tag;
48
	Backend *be = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
49

50
	Debug( LDAP_DEBUG_TRACE, "%s do_bind\n",
51
		op->o_log_prefix );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
52

53
	/*
54
	 * Force the connection to "anonymous" until bind succeeds.
55
	 */
56
	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
57
58
59
	if ( op->o_conn->c_sasl_bind_in_progress ) {
		be = op->o_conn->c_authz_backend;
	}
60
	if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
61
		/* log authorization identity demotion */
62
		Debug( LDAP_DEBUG_STATS,
63
64
			"%s BIND anonymous mech=implicit bind_ssf=0 ssf=%d\n",
			op->o_log_prefix, op->o_conn->c_ssf );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
65
	}
66
	connection2anonymous( op->o_conn );
67
68
69
	if ( op->o_conn->c_sasl_bind_in_progress ) {
		op->o_conn->c_authz_backend = be;
	}
70
	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
71
72
73
74
75
	if ( !BER_BVISNULL( &op->o_dn ) ) {
		/* NOTE: temporarily wasting few bytes
		 * (until bind is completed), but saving
		 * a couple of ch_free() and ch_strdup("") */ 
		op->o_dn.bv_val[0] = '\0';
76
		op->o_dn.bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
77
	}
78
79
	if ( !BER_BVISNULL( &op->o_ndn ) ) {
		op->o_ndn.bv_val[0] = '\0';
80
		op->o_ndn.bv_len = 0;
81
82
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
83
84
85
86
87
88
89
90
	/*
	 * Parse the bind request.  It looks like this:
	 *
	 *	BindRequest ::= SEQUENCE {
	 *		version		INTEGER,		 -- version
	 *		name		DistinguishedName,	 -- dn
	 *		authentication	CHOICE {
	 *			simple		[0] OCTET STRING -- passwd
Howard Chu's avatar
Howard Chu committed
91
92
	 *			krbv42ldap	[1] OCTET STRING -- OBSOLETE
	 *			krbv42dsa	[2] OCTET STRING -- OBSOLETE
93
	 *			SASL		[3] SaslCredentials
Kurt Zeilenga's avatar
Kurt Zeilenga committed
94
	 *		}
95
96
97
	 *	}
	 *
	 *	SaslCredentials ::= SEQUENCE {
98
99
	 *		mechanism	    LDAPString,
	 *		credentials	    OCTET STRING OPTIONAL
Kurt Zeilenga's avatar
Kurt Zeilenga committed
100
101
102
	 *	}
	 */

103
	tag = ber_scanf( ber, "{imt" /*}*/, &version, &dn, &method );
104

105
	if ( tag == LBER_ERROR ) {
106
		Debug( LDAP_DEBUG_ANY, "%s do_bind: ber_scanf failed\n",
107
			op->o_log_prefix );
108
		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
109
		rs->sr_err = SLAPD_DISCONNECT;
110
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
111
	}
Kurt Zeilenga's avatar
ldap.h:    
Kurt Zeilenga committed
112

Kurt Zeilenga's avatar
Kurt Zeilenga committed
113
	op->o_protocol = version;
114
	op->orb_method = method;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
115

116
	if( op->orb_method != LDAP_AUTH_SASL ) {
Howard Chu's avatar
Howard Chu committed
117
		tag = ber_scanf( ber, /*{*/ "m}", &op->orb_cred );
118
119

	} else {
120
		tag = ber_scanf( ber, "{m" /*}*/, &mech );
121

122
		if ( tag != LBER_ERROR ) {
123
			ber_len_t len;
124
			tag = ber_peek_tag( ber, &len );
125

126
			if ( tag == LDAP_TAG_LDAPCRED ) { 
Howard Chu's avatar
Howard Chu committed
127
				tag = ber_scanf( ber, "m", &op->orb_cred );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
128
129
			} else {
				tag = LDAP_TAG_LDAPCRED;
130
				BER_BVZERO( &op->orb_cred );
131
132
			}

133
134
			if ( tag != LBER_ERROR ) {
				tag = ber_scanf( ber, /*{{*/ "}}" );
135
136
137
138
			}
		}
	}

139
	if ( tag == LBER_ERROR ) {
140
		Debug( LDAP_DEBUG_ANY, "%s do_bind: ber_scanf failed\n",
141
			op->o_log_prefix );
142
		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
143
		rs->sr_err = SLAPD_DISCONNECT;
144
		goto cleanup;
145
146
	}

147
	if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
148
		Debug( LDAP_DEBUG_ANY, "%s do_bind: get_ctrls failed\n",
149
			op->o_log_prefix );
150
		goto cleanup;
151
152
	} 

Howard Chu's avatar
Howard Chu committed
153
154
155
156
	/* We use the tmpmemctx here because it speeds up normalization.
	 * However, we must dup with regular malloc when storing any
	 * resulting DNs in the op or conn structures.
	 */
157
158
	rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
		op->o_tmpmemctx );
159
	if ( rs->sr_err != LDAP_SUCCESS ) {
160
		Debug( LDAP_DEBUG_ANY, "%s do_bind: invalid dn (%s)\n",
161
			op->o_log_prefix, dn.bv_val );
162
		send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
163
164
165
		goto cleanup;
	}

166
	Debug( LDAP_DEBUG_STATS, "%s BIND dn=\"%s\" method=%ld\n",
167
	    op->o_log_prefix, op->o_req_dn.bv_val,
168
		(unsigned long) op->orb_method );
169

170
	if( op->orb_method == LDAP_AUTH_SASL ) {
171
		Debug( LDAP_DEBUG_TRACE, "do_bind: dn (%s) SASL mech %s\n",
172
			op->o_req_dn.bv_val, mech.bv_val );
173

174
	} else {
175
176
		Debug( LDAP_DEBUG_TRACE,
			"do_bind: version=%ld dn=\"%s\" method=%ld\n",
177
			(unsigned long) version, op->o_req_dn.bv_val,
178
			(unsigned long) op->orb_method );
179
	}
180

181
	if ( version < LDAP_VERSION_MIN || version > LDAP_VERSION_MAX ) {
182
		Debug( LDAP_DEBUG_ANY, "%s do_bind: unknown version=%ld\n",
183
			op->o_log_prefix, (unsigned long) version );
184
185
		send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
			"requested protocol version not supported" );
186
		goto cleanup;
187

188
	} else if (!( global_allows & SLAP_ALLOW_BIND_V2 ) &&
189
190
		version < LDAP_VERSION3 )
	{
191
192
		send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
			"historical protocol version requested, use LDAPv3 instead" );
193
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
194
195
	}

196
197
	/*
	 * we set connection version regardless of whether bind succeeds or not.
198
	 */
199
200
201
	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
	op->o_conn->c_protocol = version;
	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
202

Pierangelo Masarati's avatar
Pierangelo Masarati committed
203
	op->orb_mech = mech;
204
205
206
207
208
209
210
211
212
213
214
215

	op->o_bd = frontendDB;
	rs->sr_err = frontendDB->be_bind( op, rs );

cleanup:
	if ( rs->sr_err == LDAP_SUCCESS ) {
		if ( op->orb_method != LDAP_AUTH_SASL ) {
			ber_dupbv( &op->o_conn->c_authmech, &mech );
		}
		op->o_conn->c_authtype = op->orb_method;
	}

216
	if( !BER_BVISNULL( &op->o_req_dn ) ) {
217
218
219
		slap_sl_free( op->o_req_dn.bv_val, op->o_tmpmemctx );
		BER_BVZERO( &op->o_req_dn );
	}
220
	if( !BER_BVISNULL( &op->o_req_ndn ) ) {
221
222
223
224
225
226
227
228
229
230
		slap_sl_free( op->o_req_ndn.bv_val, op->o_tmpmemctx );
		BER_BVZERO( &op->o_req_ndn );
	}

	return rs->sr_err;
}

int
fe_op_bind( Operation *op, SlapReply *rs )
{
231
	BackendDB	*bd = op->o_bd;
232

233
	/* check for inappropriate controls */
Howard Chu's avatar
Howard Chu committed
234
	if( get_manageDSAit( op ) == SLAP_CONTROL_CRITICAL ) {
235
236
237
		send_ldap_error( op, rs,
			LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
			"manageDSAit control inappropriate" );
238
239
240
		goto cleanup;
	}

241
	if ( op->orb_method == LDAP_AUTH_SASL ) {
242
		if ( op->o_protocol < LDAP_VERSION3 ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
243
			Debug( LDAP_DEBUG_ANY, "do_bind: sasl with LDAPv%ld\n",
244
				(unsigned long)op->o_protocol );
245
			send_ldap_discon( op, rs,
246
				LDAP_PROTOCOL_ERROR, "SASL bind requires LDAPv3" );
247
			rs->sr_err = SLAPD_DISCONNECT;
248
			goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
249
		}
250

Pierangelo Masarati's avatar
Pierangelo Masarati committed
251
		if( BER_BVISNULL( &op->orb_mech ) || BER_BVISEMPTY( &op->orb_mech ) ) {
252
			Debug( LDAP_DEBUG_ANY,
253
				"do_bind: no sasl mechanism provided\n" );
254
255
			send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED,
				"no SASL mechanism provided" );
256
			goto cleanup;
257
		}
258

259
		/* check restrictions */
Pierangelo Masarati's avatar
Pierangelo Masarati committed
260
		if( backend_check_restrictions( op, rs, &op->orb_mech ) != LDAP_SUCCESS ) {
261
			send_ldap_result( op, rs );
262
263
264
			goto cleanup;
		}

265
266
		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
		if ( op->o_conn->c_sasl_bind_in_progress ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
267
			if( !bvmatch( &op->o_conn->c_sasl_bind_mech, &op->orb_mech ) ) {
268
				/* mechanism changed between bind steps */
269
				slap_sasl_reset(op->o_conn);
270
			}
271
		} else {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
272
			ber_dupbv(&op->o_conn->c_sasl_bind_mech, &op->orb_mech);
273
		}
274
275
276
277

		/* Set the bindop for the benefit of in-directory SASL lookups */
		op->o_conn->c_sasl_bindop = op;

278
		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
279

280
		rs->sr_err = slap_sasl_bind( op, rs );
281
282
283

		goto cleanup;

284
	} else {
285
		/* Not SASL, cancel any in-progress bind */
286
		ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
287

288
289
		if ( !BER_BVISNULL( &op->o_conn->c_sasl_bind_mech ) ) {
			free( op->o_conn->c_sasl_bind_mech.bv_val );
290
			BER_BVZERO( &op->o_conn->c_sasl_bind_mech );
291
		}
292
		op->o_conn->c_sasl_bind_in_progress = 0;
293

294
295
		slap_sasl_reset( op->o_conn );
		ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
296
	}
297

298
	if ( op->orb_method == LDAP_AUTH_SIMPLE ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
299
		BER_BVSTR( &op->orb_mech, "SIMPLE" );
300
		/* accept "anonymous" binds */
301
		if ( BER_BVISEMPTY( &op->orb_cred ) || BER_BVISEMPTY( &op->o_req_ndn ) ) {
302
			rs->sr_err = LDAP_SUCCESS;
303

304
			if( !BER_BVISEMPTY( &op->orb_cred ) &&
305
				!( global_allows & SLAP_ALLOW_BIND_ANON_CRED ))
306
307
			{
				/* cred is not empty, disallow */
308
				rs->sr_err = LDAP_INVALID_CREDENTIALS;
309

310
			} else if ( !BER_BVISEMPTY( &op->o_req_ndn ) &&
311
				!( global_allows & SLAP_ALLOW_BIND_ANON_DN ))
312
313
			{
				/* DN is not empty, disallow */
314
				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
315
316
				rs->sr_text =
					"unauthenticated bind (DN with no password) disallowed";
317

318
			} else if ( global_disallows & SLAP_DISALLOW_BIND_ANON ) {
319
				/* disallow */
320
321
				rs->sr_err = LDAP_INAPPROPRIATE_AUTH;
				rs->sr_text = "anonymous bind disallowed";
322
323

			} else {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
324
				backend_check_restrictions( op, rs, &op->orb_mech );
325
326
327
328
329
330
			}

			/*
			 * we already forced connection to "anonymous",
			 * just need to send success
			 */
331
			send_ldap_result( op, rs );
332
			Debug( LDAP_DEBUG_TRACE, "do_bind: v%d anonymous bind\n",
333
				op->o_protocol );
334
335
			goto cleanup;

336
		} else if ( global_disallows & SLAP_DISALLOW_BIND_SIMPLE ) {
337
			/* disallow simple authentication */
338
339
			rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
			rs->sr_text = "unwilling to perform simple authentication";
340

341
			send_ldap_result( op, rs );
342
343
			Debug( LDAP_DEBUG_TRACE,
				"do_bind: v%d simple bind(%s) disallowed\n",
344
				op->o_protocol, op->o_req_ndn.bv_val );
345
346
			goto cleanup;
		}
347

348
	} else {
349
350
		rs->sr_err = LDAP_AUTH_METHOD_NOT_SUPPORTED;
		rs->sr_text = "unknown authentication method";
351

352
		send_ldap_result( op, rs );
353
		Debug( LDAP_DEBUG_TRACE,
354
			"do_bind: v%d unknown authentication method (%d)\n",
355
			op->o_protocol, op->orb_method );
356
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
357
358
359
360
361
362
363
364
	}

	/*
	 * We could be serving multiple database backends.  Select the
	 * appropriate one, or send a referral to our "referral server"
	 * if we don't hold it.
	 */

365
	if ( (op->o_bd = select_backend( &op->o_req_ndn, 0 )) == NULL ) {
366
367
368
		/* don't return referral for bind requests */
		/* noSuchObject is not allowed to be returned by bind */
		rs->sr_err = LDAP_INVALID_CREDENTIALS;
369
		op->o_bd = bd;
370
		send_ldap_result( op, rs );
371
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
372
373
	}

374
	/* check restrictions */
375
376
	if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
		send_ldap_result( op, rs );
377
		goto cleanup;
378
379
	}

380
	if( op->o_bd->be_bind ) {
381
382
		op->o_conn->c_authz_cookie = NULL;

383
		rs->sr_err = (op->o_bd->be_bind)( op, rs );
384

385
		if ( rs->sr_err == 0 ) {
386
			(void)fe_op_bind_success( op, rs );
387

388
		} else if ( !BER_BVISNULL( &op->orb_edn ) ) {
Howard Chu's avatar
Howard Chu committed
389
			free( op->orb_edn.bv_val );
390
			BER_BVZERO( &op->orb_edn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
391
		}
392

Kurt Zeilenga's avatar
Kurt Zeilenga committed
393
	} else {
394
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
395
			"operation not supported within naming context" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
396
397
	}

398
cleanup:;
399
	op->o_bd = bd;
400
	return rs->sr_err;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
401
}
402

403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
int
fe_op_lastbind( Operation *op )
{
	Operation op2 = *op;
	SlapReply r2 = { REP_RESULT };
	slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
	LDAPControl c, *ca[2];
	Modifications *m;
	Entry *e;
	Attribute *a;
	char nowstr[ LDAP_LUTIL_GENTIME_BUFSIZE ];
	struct berval timestamp;
	time_t bindtime = (time_t)-1;
	int rc;

	rc = be_entry_get_rw( op, &op->o_conn->c_ndn, NULL, NULL, 0, &e );
	if ( rc != LDAP_SUCCESS ) {
		return -1;
	}

	/* get authTimestamp attribute, if it exists */
	if ( (a = attr_find( e->e_attrs, slap_schema.si_ad_pwdLastSuccess )) != NULL ) {
		struct lutil_tm tm;
		struct lutil_timet tt;

		if ( lutil_parsetime( a->a_nvals[0].bv_val, &tm ) == 0 ) {
			lutil_tm2time( &tm, &tt );
			bindtime = tt.tt_sec;
		}
		Debug( LDAP_DEBUG_ANY, "fe_op_lastbind: "
				"old pwdLastSuccess value=%s %lds ago\n",
				a->a_nvals[0].bv_val, bindtime == (time_t)-1 ? -1 : op->o_time - bindtime );

		/*
		 * TODO: If the recorded bind time is within configurable precision,
		 * it doesn't need to be updated (save a write for nothing)
		 */
		if ( bindtime != (time_t)-1 && op->o_time <= bindtime ) {
			be_entry_release_r( op, e );
			return LDAP_SUCCESS;
		}
	}

	/* update the authTimestamp in the user's entry with the current time */
	timestamp.bv_val = nowstr;
	timestamp.bv_len = sizeof(nowstr);
	slap_timestamp( &op->o_time, &timestamp );

	m = ch_calloc( sizeof(Modifications), 1 );
	m->sml_op = LDAP_MOD_REPLACE;
	m->sml_flags = 0;
	m->sml_type = slap_schema.si_ad_pwdLastSuccess->ad_cname;
	m->sml_desc = slap_schema.si_ad_pwdLastSuccess;
	m->sml_numvals = 1;
	m->sml_values = ch_calloc( sizeof(struct berval), 2 );
	m->sml_nvalues = ch_calloc( sizeof(struct berval), 2 );

	ber_dupbv( &m->sml_values[0], &timestamp );
	ber_dupbv( &m->sml_nvalues[0], &timestamp );

	be_entry_release_r( op, e );

	op2.o_tag = LDAP_REQ_MODIFY;
	op2.o_req_dn = op->o_conn->c_dn;
	op2.o_req_ndn = op->o_conn->c_ndn;
	op2.o_callback = &cb;
	op2.orm_modlist = m;
	op2.orm_no_opattrs = 0;
	op2.o_dn = op->o_bd->be_rootdn;
	op2.o_ndn = op->o_bd->be_rootndn;

	/*
	 * TODO: this is core+frontend, not everything works the same way?
	 */
	/*
	 * Code for forwarding of updates adapted from ppolicy.c of slapo-ppolicy
	 *
	 * If this server is a shadow and forward_updates is true,
	 * use the frontend to perform this modify. That will trigger
	 * the update referral, which can then be forwarded by the
	 * chain overlay. Obviously the updateref and chain overlay
	 * must be configured appropriately for this to be useful.
	 */
	if ( SLAP_SHADOW( op->o_bd ) ) {
		/* Must use Relax control since these are no-user-mod */
		op2.o_relax = SLAP_CONTROL_CRITICAL;
		op2.o_ctrls = ca;
		ca[0] = &c;
		ca[1] = NULL;
		BER_BVZERO( &c.ldctl_value );
		c.ldctl_iscritical = 1;
		c.ldctl_oid = LDAP_CONTROL_RELAX;
	} else {
		/* If not forwarding, don't update opattrs and don't replicate */
		if ( SLAP_SINGLE_SHADOW( op->o_bd )) {
			op2.orm_no_opattrs = 1;
			op2.o_dont_replicate = 1;
		}
	}

	rc = op->o_bd->be_modify( &op2, &r2 );
	slap_mods_free( m, 1 );

done:
	return rc;
}

510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
int
fe_op_bind_success( Operation *op, SlapReply *rs )
{
	ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );

	if( op->o_conn->c_authz_backend == NULL ) {
		op->o_conn->c_authz_backend = op->o_bd;
	}

	/* be_bind returns regular/global edn */
	if( !BER_BVISEMPTY( &op->orb_edn ) ) {
		op->o_conn->c_dn = op->orb_edn;
	} else {
		ber_dupbv(&op->o_conn->c_dn, &op->o_req_dn);
	}

	ber_dupbv( &op->o_conn->c_ndn, &op->o_req_ndn );

528
529
	/* op->o_conn->c_sb may be 0 for internal operations */
	if( !BER_BVISEMPTY( &op->o_conn->c_dn ) && op->o_conn->c_sb != 0 ) {
530
531
532
533
534
535
		ber_len_t max = sockbuf_max_incoming_auth;
		ber_sockbuf_ctrl( op->o_conn->c_sb,
			LBER_SB_OPT_SET_MAX_INCOMING, &max );
	}

	/* log authorization identity */
536
	Debug( LDAP_DEBUG_STATS,
537
		"%s BIND dn=\"%s\" mech=%s bind_ssf=0 ssf=%d\n",
538
		op->o_log_prefix,
539
		op->o_conn->c_dn.bv_val, op->orb_mech.bv_val, op->o_conn->c_ssf );
540
541
542
543
544
545
546

	Debug( LDAP_DEBUG_TRACE,
		"do_bind: v%d bind: \"%s\" to \"%s\"\n",
		op->o_protocol, op->o_req_dn.bv_val, op->o_conn->c_dn.bv_val );

	ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );

547
548
549
550
	if ( SLAP_LASTBIND( op->o_bd ) ) {
		fe_op_lastbind( op );
	}

551
552
553
554
555
	/* send this here to avoid a race condition */
	send_ldap_result( op, rs );

	return LDAP_SUCCESS;
}