modify.c 23.7 KB
Newer Older
1
/* $OpenLDAP$ */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
2
/*
Kurt Zeilenga's avatar
Kurt Zeilenga committed
3
 * Copyright 1998-2003 The OpenLDAP Foundation, All Rights Reserved.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
4
5
 * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
6
7
8
9
10
11
12
13
14
15
16
17
/*
 * Copyright (c) 1995 Regents of the University of Michigan.
 * 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
18
19
#include "portable.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
20
#include <stdio.h>
Kurt Zeilenga's avatar
Kurt Zeilenga committed
21
22
23
24
25

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

26
#include "ldap_pvt.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
27
#include "slap.h"
28
#ifdef LDAP_SLAPI
29
#include "slapi.h"
30
#endif
31
32
#include "lutil.h"

Kurt Zeilenga's avatar
Kurt Zeilenga committed
33

34
int
Kurt Zeilenga's avatar
Kurt Zeilenga committed
35
do_modify(
36
37
    Operation	*op,
    SlapReply	*rs )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
38
{
39
	struct berval dn = { 0, NULL };
Kurt Zeilenga's avatar
Kurt Zeilenga committed
40
	char		*last;
41
42
	ber_tag_t	tag;
	ber_len_t	len;
43
44
	Modifications	*modlist = NULL;
	Modifications	**modtail = &modlist;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
45
#ifdef LDAP_DEBUG
46
	Modifications *tmp;
47
48
49
50
#endif
#ifdef LDAP_SLAPI
	LDAPMod		**modv = NULL;
	Slapi_PBlock *pb = op->o_pb;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
51
#endif
52
	int manageDSAit;
53

54
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
55
	LDAP_LOG( OPERATION, ENTRY, "do_modify: enter\n", 0, 0, 0 );
56
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
57
	Debug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 );
58
59
#endif

Kurt Zeilenga's avatar
Kurt Zeilenga committed
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
	/*
	 * Parse the modify request.  It looks like this:
	 *
	 *	ModifyRequest := [APPLICATION 6] SEQUENCE {
	 *		name	DistinguishedName,
	 *		mods	SEQUENCE OF SEQUENCE {
	 *			operation	ENUMERATED {
	 *				add	(0),
	 *				delete	(1),
	 *				replace	(2)
	 *			},
	 *			modification	SEQUENCE {
	 *				type	AttributeType,
	 *				values	SET OF AttributeValue
	 *			}
	 *		}
	 *	}
	 */

79
	if ( ber_scanf( op->o_ber, "{m" /*}*/, &dn ) == LBER_ERROR ) {
80
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
81
		LDAP_LOG( OPERATION, ERR, "do_modify: ber_scanf failed\n", 0, 0, 0 );
82
#else
83
		Debug( LDAP_DEBUG_ANY, "do_modify: ber_scanf failed\n", 0, 0, 0 );
84
85
#endif

86
		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
87
		return SLAPD_DISCONNECT;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
88
89
	}

90
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
91
	LDAP_LOG( OPERATION, ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
92
#else
93
	Debug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
94
95
#endif

96

Kurt Zeilenga's avatar
Kurt Zeilenga committed
97
	/* collect modifications & save for later */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
98

Kurt Zeilenga's avatar
Kurt Zeilenga committed
99
100
101
102
	for ( tag = ber_first_element( op->o_ber, &len, &last );
	    tag != LBER_DEFAULT;
	    tag = ber_next_element( op->o_ber, &len, last ) )
	{
103
		ber_int_t mop;
104
		Modifications tmp, *mod;
105

Kurt Zeilenga's avatar
Kurt Zeilenga committed
106
		tmp.sml_nvalues = NULL;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
107

108
		if ( ber_scanf( op->o_ber, "{i{m[W]}}", &mop,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
109
		    &tmp.sml_type, &tmp.sml_values )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
110
111
		    == LBER_ERROR )
		{
112
113
			send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding modlist error" );
			rs->sr_err = SLAPD_DISCONNECT;
Howard Chu's avatar
Howard Chu committed
114
			goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
115
116
		}

Howard Chu's avatar
Howard Chu committed
117
		mod = (Modifications *) ch_malloc( sizeof(Modifications) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
118
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
119
		mod->sml_type = tmp.sml_type;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
120
121
		mod->sml_values = tmp.sml_values;
		mod->sml_nvalues = NULL;
122
		mod->sml_desc = NULL;
123
		mod->sml_next = NULL;
124
125
		*modtail = mod;

126
127
		switch( mop ) {
		case LDAP_MOD_ADD:
Kurt Zeilenga's avatar
Kurt Zeilenga committed
128
			if ( mod->sml_values == NULL ) {
129
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
130
				LDAP_LOG( OPERATION, ERR, 
131
					"do_modify: modify/add operation (%ld) requires values\n",
Julius Enarusai's avatar
   
Julius Enarusai committed
132
					(long)mop, 0, 0 );
133
#else
134
135
136
				Debug( LDAP_DEBUG_ANY,
					"do_modify: modify/add operation (%ld) requires values\n",
					(long) mop, 0, 0 );
137
138
#endif

139
140
				send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
					"modify/add operation requires values" );
141
142
				goto cleanup;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
143

144
145
146
147
148
149
150
			/* fall through */

		case LDAP_MOD_DELETE:
		case LDAP_MOD_REPLACE:
			break;

		default: {
151
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
152
				LDAP_LOG( OPERATION, ERR, 
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
153
154
					"do_modify: invalid modify operation (%ld)\n",
					(long)mop, 0, 0 );
155
#else
156
157
158
				Debug( LDAP_DEBUG_ANY,
					"do_modify: invalid modify operation (%ld)\n",
					(long) mop, 0, 0 );
159
160
#endif

161
162
				send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
					"unrecognized modify operation" );
163
164
				goto cleanup;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
165
		}
166

167
		modtail = &mod->sml_next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
168
169
170
	}
	*modtail = NULL;

171
	if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
172
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
173
		LDAP_LOG( OPERATION, ERR, "do_modify: get_ctrls failed\n", 0, 0, 0 );
174
#else
175
		Debug( LDAP_DEBUG_ANY, "do_modify: get_ctrls failed\n", 0, 0, 0 );
176
177
#endif

178
179
180
		goto cleanup;
	}

Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
181
182
	rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
		op->o_tmpmemctx );
183
	if( rs->sr_err != LDAP_SUCCESS ) {
184
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
185
		LDAP_LOG( OPERATION, INFO, "do_modify: conn %d  invalid dn (%s)\n",
186
			op->o_connid, dn.bv_val, 0 );
187
#else
188
		Debug( LDAP_DEBUG_ANY,
189
			"do_modify: invalid dn (%s)\n", dn.bv_val, 0, 0 );
190
#endif
191
		send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
192
193
		goto cleanup;
	}
194

195
	if( op->o_req_ndn.bv_len == 0 ) {
196
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
197
198
		LDAP_LOG( OPERATION, ERR, 
			"do_modify: attempt to modify root DSE.\n",0, 0, 0 );
199
#else
200
		Debug( LDAP_DEBUG_ANY, "do_modify: root dse!\n", 0, 0, 0 );
201
202
#endif

203
204
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
			"modify upon the root DSE not supported" );
205
		goto cleanup;
206

207
	} else if ( bvmatch( &op->o_req_ndn, &global_schemandn ) ) {
208
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
209
210
		LDAP_LOG( OPERATION, ERR,
			"do_modify: attempt to modify subschema subentry.\n" , 0, 0, 0  );
211
212
213
214
#else
		Debug( LDAP_DEBUG_ANY, "do_modify: subschema subentry!\n", 0, 0, 0 );
#endif

215
216
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
			"modification of subschema subentry not supported" );
217
		goto cleanup;
218
219
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
220
#ifdef LDAP_DEBUG
221
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
222
	LDAP_LOG( OPERATION, DETAIL1, "do_modify: modifications:\n", 0, 0, 0  );
223
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
224
	Debug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 );
225
226
#endif

227
	for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
228
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
229
230
231
232
		LDAP_LOG( OPERATION, DETAIL1, "\t%s:  %s\n", 
			tmp->sml_op == LDAP_MOD_ADD ?
			"add" : (tmp->sml_op == LDAP_MOD_DELETE ?
			"delete" : "replace"), tmp->sml_type.bv_val, 0 );
233

Kurt Zeilenga's avatar
Kurt Zeilenga committed
234
		if ( tmp->sml_values == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
235
			LDAP_LOG( OPERATION, DETAIL1, "\t\tno values", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
236
		} else if ( tmp->sml_values[0].bv_val == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
237
			LDAP_LOG( OPERATION, DETAIL1, "\t\tzero values", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
238
		} else if ( tmp->sml_values[1].bv_val == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
239
			LDAP_LOG( OPERATION, DETAIL1, "\t\tone value", 0, 0, 0 );
240
		} else {
Julius Enarusai's avatar
   
Julius Enarusai committed
241
			LDAP_LOG( OPERATION, DETAIL1, "\t\tmultiple values", 0, 0, 0 );
242
243
		}

244
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
245
		Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
246
247
248
			tmp->sml_op == LDAP_MOD_ADD
				? "add" : (tmp->sml_op == LDAP_MOD_DELETE
					? "delete" : "replace"), tmp->sml_type.bv_val, 0 );
249

Kurt Zeilenga's avatar
Kurt Zeilenga committed
250
		if ( tmp->sml_values == NULL ) {
251
252
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tno values", NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
253
		} else if ( tmp->sml_values[0].bv_val == NULL ) {
254
255
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tzero values", NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
256
		} else if ( tmp->sml_values[1].bv_val == NULL ) {
257
			Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
258
			   "\t\tone value", (long) tmp->sml_values[0].bv_len, NULL );
259
260
261
262
263
		} else {
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tmultiple values", NULL, NULL );
		}
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
264
265
	}

Howard Chu's avatar
Howard Chu committed
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
	if ( StatslogTest( LDAP_DEBUG_STATS ) ) {
		char abuf[BUFSIZ/2], *ptr = abuf;
		int len = 0;

		Statslog( LDAP_DEBUG_STATS, "conn=%lu op=%lu MOD dn=\"%s\"\n",
			op->o_connid, op->o_opid, dn.bv_val, 0, 0 );

		for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
			if (len + 1 + tmp->sml_type.bv_len > sizeof(abuf)) {
				Statslog( LDAP_DEBUG_STATS, "conn=%lu op=%lu MOD attr=%s\n",
				    op->o_connid, op->o_opid, abuf, 0, 0 );
	    			len = 0;
				ptr = abuf;
			}
			if (len) {
				*ptr++ = ' ';
				len++;
			}
			ptr = lutil_strcopy(ptr, tmp->sml_type.bv_val);
			len += tmp->sml_type.bv_len;
		}
		if (len) {
			Statslog( LDAP_DEBUG_STATS, "conn=%lu op=%lu MOD attr=%s\n",
	    			op->o_connid, op->o_opid, abuf, 0, 0 );
		}
	}
Howard Chu's avatar
Howard Chu committed
292
#endif	/* LDAP_DEBUG */
293

294
295
	manageDSAit = get_manageDSAit( op );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
296
297
298
299
300
	/*
	 * 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.
	 */
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
301
302
	op->o_bd = select_backend( &op->o_req_ndn, manageDSAit, 0 );
	if ( op->o_bd == NULL ) {
303
304
305
		rs->sr_ref = referral_rewrite( default_referral,
			NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
		if (!rs->sr_ref) rs->sr_ref = default_referral;
306

Pierangelo Masarati's avatar
Pierangelo Masarati committed
307
308
309
		if (rs->sr_ref != NULL ) {
			rs->sr_err = LDAP_REFERRAL;
			send_ldap_result( op, rs );
310

Pierangelo Masarati's avatar
Pierangelo Masarati committed
311
312
313
314
315
			if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
		} else {
			send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
					"referral missing" );
		}
Howard Chu's avatar
Howard Chu committed
316
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
317
318
	}

319
	/* check restrictions */
320
321
	if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
		send_ldap_result( op, rs );
322
		goto cleanup;
323
324
	}

325
	/* check for referrals */
326
	if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
327
328
329
		goto cleanup;
	}

330
#if defined( LDAP_SLAPI )
Luke Howard's avatar
Luke Howard committed
331
	slapi_x_pblock_set_operation( pb, op );
332
	slapi_pblock_set( pb, SLAPI_MODIFY_TARGET, (void *)dn.bv_val );
333
	slapi_pblock_set( pb, SLAPI_MANAGEDSAIT, (void *)manageDSAit );
334
	modv = slapi_x_modifications2ldapmods( &modlist );
335
	slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)modv );
336

Luke Howard's avatar
Luke Howard committed
337
	rs->sr_err = doPluginFNs( op->o_bd, SLAPI_PLUGIN_PRE_MODIFY_FN, pb );
Howard Chu's avatar
Howard Chu committed
338
	if ( rs->sr_err < 0 ) {
339
		/*
340
341
		 * A preoperation plugin failure will abort the
		 * entire operation.
342
343
		 */
#ifdef NEW_LOGGING
344
		LDAP_LOG( OPERATION, INFO, "do_modify: modify preoperation plugin "
345
				"failed\n", 0, 0, 0 );
346
#else
347
		Debug(LDAP_DEBUG_TRACE, "do_modify: modify preoperation plugin failed.\n",
348
				0, 0, 0);
349
#endif
350
351
		if ( ( slapi_pblock_get( op->o_pb, SLAPI_RESULT_CODE, (void *)&rs->sr_err ) != 0 )  ||
		     rs->sr_err == LDAP_SUCCESS ) {
352
			rs->sr_err = LDAP_OTHER;
353
		}
Luke Howard's avatar
Luke Howard committed
354
		slapi_x_free_ldapmods( modv );
355
		modv = NULL;
356
		goto cleanup;
357
	}
358
359
360
361
362
363

	/*
	 * It's possible that the preoperation plugin changed the
	 * modification array, so we need to convert it back to
	 * a Modification list.
	 *
364
	 * Calling slapi_x_modifications2ldapmods() destroyed modlist so
365
366
	 * we don't need to free it.
	 */
Howard Chu's avatar
Howard Chu committed
367
368
	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, (void **)&modv );
	modlist = slapi_x_ldapmods2modifications( modv );
369
370
371
372
373
374
375
376

	/*
	 * NB: it is valid for the plugin to return no modifications
	 * (for example, a plugin might store some attributes elsewhere
	 * and remove them from the modification list; if only those
	 * attribute types were included in the modification request,
	 * then slapi_x_ldapmods2modifications() above will return
	 * NULL).
377
378
379
	 *
	 * However, the post-operation plugin should still be 
	 * called.
380
	 */
Howard Chu's avatar
Howard Chu committed
381
382
383
	if ( modlist == NULL ) {
		rs->sr_err = LDAP_SUCCESS;
		send_ldap_result( op, rs );
384
	} else {
385
386
#endif /* defined( LDAP_SLAPI ) */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
387
388
389
390
	/*
	 * do the modify if 1 && (2 || 3)
	 * 1) there is a modify function implemented in this backend;
	 * 2) this backend is master for what it holds;
391
	 * 3) it's a replica and the dn supplied is the update_ndn.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
392
	 */
393
	if ( op->o_bd->be_modify ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
394
		/* do the update here */
395
		int repl_user = be_isupdate( op->o_bd, &op->o_ndn );
396

Kurt Zeilenga's avatar
Kurt Zeilenga committed
397
398
		/* Multimaster slapd does not have to check for replicator dn
		 * because it accepts each modify request
399
		 */
400
401
402
403
404
405
#if defined(LDAP_SYNCREPL) && !defined(SLAPD_MULTIMASTER)
		if ( !op->o_bd->syncinfo &&
				( !op->o_bd->be_update_ndn.bv_len || repl_user ))
#elif defined(LDAP_SYNCREPL) && defined(SLAPD_MULTIMASTER)
		if ( !op->o_bd->syncinfo )  /* LDAP_SYNCREPL overrides MM */
#elif !defined(LDAP_SYNCREPL) && !defined(SLAPD_MULTIMASTER)
406
		if ( !op->o_bd->be_update_ndn.bv_len || repl_user )
407
#endif
408
		{
409
			int update = op->o_bd->be_update_ndn.bv_len;
410
411
412
			char textbuf[SLAP_TEXT_BUFLEN];
			size_t textlen = sizeof textbuf;

413
			rs->sr_err = slap_mods_check( modlist, update, &rs->sr_text,
414
				textbuf, textlen, NULL );
415

416
417
			if( rs->sr_err != LDAP_SUCCESS ) {
				send_ldap_result( op, rs );
418
419
420
				goto cleanup;
			}

421
			if ( !repl_user ) {
422
423
424
				for( modtail = &modlist;
					*modtail != NULL;
					modtail = &(*modtail)->sml_next )
425
426
427
				{
					/* empty */
				}
428

429
430
431
432
				rs->sr_err = slap_mods_opattrs( op, modlist, modtail,
					&rs->sr_text, textbuf, textlen );
				if( rs->sr_err != LDAP_SUCCESS ) {
					send_ldap_result( op, rs );
Howard Chu's avatar
Howard Chu committed
433
					goto cleanup;
434
435
436
				}
			}

Howard Chu's avatar
Howard Chu committed
437
			op->orm_modlist = modlist;
438
			if ( (op->o_bd->be_modify)( op, rs ) == 0
439
#ifdef SLAPD_MULTIMASTER
Kurt Zeilenga's avatar
Kurt Zeilenga committed
440
				&& !repl_user
441
442
443
#endif
			) {
				/* but we log only the ones not from a replicator user */
444
				replog( op );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
445
446
			}

447
#if defined(LDAP_SYNCREPL) || !defined(SLAPD_MULTIMASTER)
Kurt Zeilenga's avatar
Kurt Zeilenga committed
448
449
		/* send a referral */
		} else {
450
451
452
			BerVarray defref = NULL;
#ifdef LDAP_SYNCREPL
			if ( op->o_bd->syncinfo ) {
453
				defref = op->o_bd->syncinfo->provideruri_bv;
454
455
456
457
458
459
			} else
#endif
			{
				defref = op->o_bd->be_update_refs
						? op->o_bd->be_update_refs : default_referral;
			}
460
461
462
463
			if ( defref != NULL ) {
				rs->sr_ref = referral_rewrite( defref,
					NULL, &op->o_req_dn,
					LDAP_SCOPE_DEFAULT );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
464
				if (!rs->sr_ref) rs->sr_ref = defref;
465
466
467
468
469
470
471
472
473
474
				rs->sr_err = LDAP_REFERRAL;
				send_ldap_result( op, rs );
				if (rs->sr_ref != defref) {
					ber_bvarray_free( rs->sr_ref );
				}
			} else {
				send_ldap_error( op, rs,
						LDAP_UNWILLING_TO_PERFORM,
		    				"referral missing" );
			}
475
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
476
477
		}
	} else {
478
479
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
		    "operation not supported within namingContext" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
480
481
	}

482
#if defined( LDAP_SLAPI )
483
484
	} /* modlist != NULL */

Howard Chu's avatar
Howard Chu committed
485
	if ( doPluginFNs( op->o_bd, SLAPI_PLUGIN_POST_MODIFY_FN, pb ) < 0 ) {
486
#ifdef NEW_LOGGING
487
		LDAP_LOG( OPERATION, INFO, "do_modify: modify postoperation plugins "
488
				"failed\n", 0, 0, 0 );
489
#else
490
		Debug(LDAP_DEBUG_TRACE, "do_modify: modify postoperation plugins "
491
				"failed.\n", 0, 0, 0);
492
493
494
495
#endif
	}
#endif /* defined( LDAP_SLAPI ) */

Howard Chu's avatar
Howard Chu committed
496
cleanup:
497
498
	op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
	op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
499
	if ( modlist != NULL ) slap_mods_free( modlist );
500
#if defined( LDAP_SLAPI )
501
	if ( modv != NULL ) slapi_x_free_ldapmods( modv );
502
#endif
503
	return rs->sr_err;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
504
505
}

506
507
508
/*
 * Do basic attribute type checking and syntax validation.
 */
509
510
int slap_mods_check(
	Modifications *ml,
511
	int update,
512
	const char **text,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
513
	char *textbuf,
Howard Chu's avatar
Howard Chu committed
514
515
	size_t textlen,
	void *ctx )
516
517
518
{
	int rc;

519
	for( ; ml != NULL; ml = ml->sml_next ) {
520
		AttributeDescription *ad = NULL;
521
522

		/* convert to attribute description */
523
		rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
524
525

		if( rc != LDAP_SUCCESS ) {
526
			snprintf( textbuf, textlen, "%s: %s",
527
				ml->sml_type.bv_val, *text );
528
			*text = textbuf;
529
530
531
			return rc;
		}

532
		ad = ml->sml_desc;
533

534
535
		if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
			&& !slap_ad_is_binary( ad ))
536
537
		{
			/* attribute requires binary transfer */
538
539
			snprintf( textbuf, textlen,
				"%s: requires ;binary transfer",
540
				ml->sml_type.bv_val );
541
			*text = textbuf;
542
543
544
			return LDAP_UNDEFINED_TYPE;
		}

545
546
547
548
		if( !slap_syntax_is_binary( ad->ad_type->sat_syntax )
			&& slap_ad_is_binary( ad ))
		{
			/* attribute requires binary transfer */
549
550
			snprintf( textbuf, textlen,
				"%s: disallows ;binary transfer",
551
				ml->sml_type.bv_val );
552
			*text = textbuf;
553
554
555
			return LDAP_UNDEFINED_TYPE;
		}

556
		if( slap_ad_is_tag_range( ad )) {
557
558
			/* attribute requires binary transfer */
			snprintf( textbuf, textlen,
559
				"%s: inappropriate use of tag range option",
560
561
562
563
564
				ml->sml_type.bv_val );
			*text = textbuf;
			return LDAP_UNDEFINED_TYPE;
		}

565
566
		if (!update && is_at_no_user_mod( ad->ad_type )) {
			/* user modification disallowed */
567
568
			snprintf( textbuf, textlen,
				"%s: no user modification allowed",
569
				ml->sml_type.bv_val );
570
			*text = textbuf;
571
572
573
			return LDAP_CONSTRAINT_VIOLATION;
		}

574
		if ( is_at_obsolete( ad->ad_type ) &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
575
			( ml->sml_op == LDAP_MOD_ADD || ml->sml_values != NULL ) )
576
577
578
579
580
581
582
		{
			/*
			 * attribute is obsolete,
			 * only allow replace/delete with no values
			 */
			snprintf( textbuf, textlen,
				"%s: attribute is obsolete",
583
				ml->sml_type.bv_val );
584
585
586
587
			*text = textbuf;
			return LDAP_CONSTRAINT_VIOLATION;
		}

588
589
590
		/*
		 * check values
		 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
591
		if( ml->sml_values != NULL ) {
592
593
594
			ber_len_t nvals;
			slap_syntax_validate_func *validate =
				ad->ad_type->sat_syntax->ssyn_validate;
595
596
597
598
			slap_syntax_transform_func *pretty =
				ad->ad_type->sat_syntax->ssyn_pretty;
 
			if( !pretty && !validate ) {
599
				*text = "no validator for syntax";
600
601
				snprintf( textbuf, textlen,
					"%s: no validator for syntax %s",
602
					ml->sml_type.bv_val,
603
604
					ad->ad_type->sat_syntax->ssyn_oid );
				*text = textbuf;
605
606
607
				return LDAP_INVALID_SYNTAX;
			}

608
609
			/*
			 * check that each value is valid per syntax
610
			 *	and pretty if appropriate
611
			 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
612
			for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
613
				struct berval pval;
614
615
				if( pretty ) {
					rc = pretty( ad->ad_type->sat_syntax,
Howard Chu's avatar
Howard Chu committed
616
						&ml->sml_values[nvals], &pval, ctx );
617
618
				} else {
					rc = validate( ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
619
						&ml->sml_values[nvals] );
620
				}
621
622

				if( rc != 0 ) {
623
					snprintf( textbuf, textlen,
624
						"%s: value #%ld invalid per syntax",
625
						ml->sml_type.bv_val, (long) nvals );
626
					*text = textbuf;
627
628
					return LDAP_INVALID_SYNTAX;
				}
629
630

				if( pretty ) {
Howard Chu's avatar
Howard Chu committed
631
					ber_memfree_x( ml->sml_values[nvals].bv_val, ctx );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
632
					ml->sml_values[nvals] = pval;
633
				}
634
635
			}

636
637
638
639
640
641
642
643
644
645
646
647
648
649
			/*
			 * a rough single value check... an additional check is needed
			 * to catch add of single value to existing single valued attribute
			 */
			if ((ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE)
				&& nvals > 1 && is_at_single_value( ad->ad_type ))
			{
				snprintf( textbuf, textlen,
					"%s: multiple values provided",
					ml->sml_type.bv_val );
				*text = textbuf;
				return LDAP_CONSTRAINT_VIOLATION;
			}

650
			if( nvals && ad->ad_type->sat_equality &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
651
				ad->ad_type->sat_equality->smr_normalize )
652
			{
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
653
654
655
				ml->sml_nvalues = ber_memalloc_x(
					(nvals+1)*sizeof(struct berval), ctx );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
656
				for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
657
658
					rc = ad->ad_type->sat_equality->smr_normalize(
						0,
659
						ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
660
						ad->ad_type->sat_equality,
Howard Chu's avatar
Howard Chu committed
661
						&ml->sml_values[nvals], &ml->sml_nvalues[nvals], ctx );
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
					if( rc ) {
#ifdef NEW_LOGGING
						LDAP_LOG( OPERATION, DETAIL1,
							"str2entry:  NULL (ssyn_normalize %d)\n",
							rc, 0, 0 );
#else
						Debug( LDAP_DEBUG_ANY,
							"<= str2entry NULL (ssyn_normalize %d)\n",
							rc, 0, 0 );
#endif
						snprintf( textbuf, textlen,
							"%s: value #%ld normalization failed",
							ml->sml_type.bv_val, (long) nvals );
						*text = textbuf;
						return rc;
					}
				}
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
679

680
681
				ml->sml_nvalues[nvals].bv_val = NULL;
				ml->sml_nvalues[nvals].bv_len = 0;
682
683

			} else {
684
			}
685
686
687
688
689
690
691
692
		}
	}

	return LDAP_SUCCESS;
}

int slap_mods_opattrs(
	Operation *op,
693
	Modifications *mods,
694
	Modifications **modtail,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
695
696
	const char **text,
	char *textbuf, size_t textlen )
697
{
698
	struct berval name, timestamp, csn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
699
	struct berval nname;
700
701
	char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
	char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
702
703
704
705
706
	Modifications *mod;

	int mop = op->o_tag == LDAP_REQ_ADD
		? LDAP_MOD_ADD : LDAP_MOD_REPLACE;

707
708
709
710
#ifdef LDAP_SYNCREPL
	syncinfo_t *si = op->o_si;
#endif

711
712
713
	assert( modtail != NULL );
	assert( *modtail == NULL );

714
715
716
717
718
719
#ifdef LDAP_SYNCREPL
	if ( SLAP_LASTMOD(op->o_bd) && ( !si || si->lastmod == LASTMOD_GEN ))
#else
	if ( SLAP_LASTMOD(op->o_bd) )
#endif
	{
720
721
		struct tm *ltm;
		time_t now = slap_get_time();
722

723
724
		ldap_pvt_thread_mutex_lock( &gmtime_mutex );
		ltm = gmtime( &now );
Howard Chu's avatar
Howard Chu committed
725
		lutil_gentime( timebuf, sizeof(timebuf), ltm );
726

727
728
729
		csn.bv_len = lutil_csnstr( csnbuf, sizeof( csnbuf ), 0, 0 );
		ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
		csn.bv_val = csnbuf;
730

731
732
733
734
735
736
		timestamp.bv_val = timebuf;
		timestamp.bv_len = strlen(timebuf);

		if( op->o_dn.bv_len == 0 ) {
			name.bv_val = SLAPD_ANONYMOUS;
			name.bv_len = sizeof(SLAPD_ANONYMOUS)-1;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
737
			nname = name;
738
739
		} else {
			name = op->o_dn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
740
			nname = op->o_ndn;
741
		}
742
743
744
	}

	if( op->o_tag == LDAP_REQ_ADD ) {
745
		struct berval tmpval;
746

747
748
749
750
751
752
		if( global_schemacheck ) {
			int rc = mods_structural_class( mods, &tmpval,
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
				return rc;
			}
753
754
755
756
757

			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
			mod->sml_op = mop;
			mod->sml_type.bv_val = NULL;
			mod->sml_desc = slap_schema.si_ad_structuralObjectClass;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
758
759
			mod->sml_values =
				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
760
			ber_dupbv( &mod->sml_values[0], &tmpval );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
761
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
762
763
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
764
765
766
767
768
769
			mod->sml_nvalues =
				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_nvalues[0], &tmpval );
			mod->sml_nvalues[1].bv_len = 0;
			mod->sml_nvalues[1].bv_val = NULL;
			assert( mod->sml_nvalues[0].bv_val );
770
771
			*modtail = mod;
			modtail = &mod->sml_next;
772
		}
773

774
775
776
777
778
779
#ifdef LDAP_SYNCREPL
		if ( SLAP_LASTMOD(op->o_bd) && ( !si || si->lastmod == LASTMOD_GEN ))
#else
		if ( SLAP_LASTMOD(op->o_bd) )
#endif
		{
780
			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
781

782
783
784
785
786
#ifdef LDAP_SYNCREPL
			if ( !si ) {
#endif
				tmpval.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
				tmpval.bv_val = uuidbuf;
787
		
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
				mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
				mod->sml_op = mop;
				mod->sml_type.bv_val = NULL;
				mod->sml_desc = slap_schema.si_ad_entryUUID;
				mod->sml_values =
					(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
				ber_dupbv( &mod->sml_values[0], &tmpval );
				mod->sml_values[1].bv_len = 0;
				mod->sml_values[1].bv_val = NULL;
				assert( mod->sml_values[0].bv_val );
				mod->sml_nvalues = NULL;
				*modtail = mod;
				modtail = &mod->sml_next;
#ifdef LDAP_SYNCREPL
			}
#endif
804
805
806
807
808

			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
			mod->sml_op = mop;
			mod->sml_type.bv_val = NULL;
			mod->sml_desc = slap_schema.si_ad_creatorsName;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
809
810
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &name );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
811
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
812
813
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
814
815
816
817
818
819
			mod->sml_nvalues =
				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_nvalues[0], &nname );
			mod->sml_nvalues[1].bv_len = 0;
			mod->sml_nvalues[1].bv_val = NULL;
			assert( mod->sml_nvalues[0].bv_val );
820
821
822
823
824
825
826
			*modtail = mod;
			modtail = &mod->sml_next;

			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
			mod->sml_op = mop;
			mod->sml_type.bv_val = NULL;
			mod->sml_desc = slap_schema.si_ad_createTimestamp;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
827
828
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &timestamp );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
829
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
830
831
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
832
			mod->sml_nvalues = NULL;
833
834
			*modtail = mod;
			modtail = &mod->sml_next;
835
		}
836
	}
837

838
839
840
841
842
843
#ifdef LDAP_SYNCREPL
	if ( SLAP_LASTMOD(op->o_bd) && ( !si || si->lastmod == LASTMOD_GEN ))
#else
	if ( SLAP_LASTMOD(op->o_bd) )
#endif
	{
844
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
845
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
846
		mod->sml_type.bv_val = NULL;
847
		mod->sml_desc = slap_schema.si_ad_entryCSN;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
848
849
		mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_values[0], &csn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
850
		mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
851
852
		mod->sml_values[1].bv_val = NULL;
		assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
853
		mod->sml_nvalues = NULL;
854
855
856
		*modtail = mod;
		modtail = &mod->sml_next;

857
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
858
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
859
		mod->sml_type.bv_val = NULL;
860
		mod->sml_desc = slap_schema.si_ad_modifiersName;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
861
862
		mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_values[0], &name );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
863
		mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
864
865
		mod->sml_values[1].bv_val = NULL;
		assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
866
867
868
869
870
871
		mod->sml_nvalues =
			(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_nvalues[0], &nname );
		mod->sml_nvalues[1].bv_len = 0;
		mod->sml_nvalues[1].bv_val = NULL;
		assert( mod->sml_nvalues[0].bv_val );
872
873
874
		*modtail = mod;
		modtail = &mod->sml_next;

875
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
876
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
877
		mod->sml_type.bv_val = NULL;
878
		mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
879
880
		mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_values[0], &timestamp );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
881
		mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
882
883
		mod->sml_values[1].bv_val = NULL;
		assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
884
		mod->sml_nvalues = NULL;
885
886
887
888
		*modtail = mod;
		modtail = &mod