modify.c 25.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
	int increment = 0;
54

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
	/*
	 * 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
	 *			}
	 *		}
	 *	}
	 */

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

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

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

97

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

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

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

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

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

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

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

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

		case LDAP_MOD_DELETE:
		case LDAP_MOD_REPLACE:
			break;

151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
		case LDAP_MOD_INCREMENT:
			if( op->o_protocol >= LDAP_VERSION3 ) {
				increment++;
				if ( mod->sml_values == NULL ) {
#ifdef NEW_LOGGING
					LDAP_LOG( OPERATION, ERR, "do_modify: "
						"modify/increment operation (%ld) requires value\n",
						(long)mop, 0, 0 );
#else
					Debug( LDAP_DEBUG_ANY, "do_modify: "
						"modify/increment operation (%ld) requires value\n",
						(long) mop, 0, 0 );
#endif

					send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
						"modify/increment operation requires value" );
					goto cleanup;
				}

				if( mod->sml_values[1].bv_val ) {
#ifdef NEW_LOGGING
					LDAP_LOG( OPERATION, ERR, "do_modify: modify/increment "
						"operation (%ld) requires single value\n",
						(long)mop, 0, 0 );
#else
					Debug( LDAP_DEBUG_ANY, "do_modify: modify/increment "
						"operation (%ld) requires single value\n",
						(long) mop, 0, 0 );
#endif

					send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
						"modify/increment operation requires single value" );
					goto cleanup;
				}

				break;
			}
			/* fall thru */

190
		default: {
191
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
192
				LDAP_LOG( OPERATION, ERR, 
193
					"do_modify: unrecognized modify operation (%ld)\n",
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
194
					(long)mop, 0, 0 );
195
#else
196
				Debug( LDAP_DEBUG_ANY,
197
					"do_modify: unrecognized modify operation (%ld)\n",
198
					(long) mop, 0, 0 );
199
200
#endif

201
202
				send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
					"unrecognized modify operation" );
203
204
				goto cleanup;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
205
		}
206

207
		modtail = &mod->sml_next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
208
209
210
	}
	*modtail = NULL;

211
	if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
212
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
213
		LDAP_LOG( OPERATION, ERR, "do_modify: get_ctrls failed\n", 0, 0, 0 );
214
#else
215
		Debug( LDAP_DEBUG_ANY, "do_modify: get_ctrls failed\n", 0, 0, 0 );
216
217
#endif

218
219
220
		goto cleanup;
	}

Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
221
222
	rs->sr_err = dnPrettyNormal( NULL, &dn, &op->o_req_dn, &op->o_req_ndn,
		op->o_tmpmemctx );
223
	if( rs->sr_err != LDAP_SUCCESS ) {
224
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
225
		LDAP_LOG( OPERATION, INFO, "do_modify: conn %d  invalid dn (%s)\n",
226
			op->o_connid, dn.bv_val, 0 );
227
#else
228
		Debug( LDAP_DEBUG_ANY,
229
			"do_modify: invalid dn (%s)\n", dn.bv_val, 0, 0 );
230
#endif
231
		send_ldap_error( op, rs, LDAP_INVALID_DN_SYNTAX, "invalid DN" );
232
233
		goto cleanup;
	}
234

235
	if( op->o_req_ndn.bv_len == 0 ) {
236
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
237
238
		LDAP_LOG( OPERATION, ERR, 
			"do_modify: attempt to modify root DSE.\n",0, 0, 0 );
239
#else
240
		Debug( LDAP_DEBUG_ANY, "do_modify: root dse!\n", 0, 0, 0 );
241
242
#endif

243
244
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
			"modify upon the root DSE not supported" );
245
		goto cleanup;
246

247
	} else if ( bvmatch( &op->o_req_ndn, &global_schemandn ) ) {
248
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
249
250
		LDAP_LOG( OPERATION, ERR,
			"do_modify: attempt to modify subschema subentry.\n" , 0, 0, 0  );
251
252
253
254
#else
		Debug( LDAP_DEBUG_ANY, "do_modify: subschema subentry!\n", 0, 0, 0 );
#endif

255
256
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
			"modification of subschema subentry not supported" );
257
		goto cleanup;
258
259
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
260
#ifdef LDAP_DEBUG
261
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
262
	LDAP_LOG( OPERATION, DETAIL1, "do_modify: modifications:\n", 0, 0, 0  );
263
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
264
	Debug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 );
265
266
#endif

267
	for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
268
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
269
		LDAP_LOG( OPERATION, DETAIL1, "\t%s:  %s\n", 
270
271
272
273
			tmp->sml_op == LDAP_MOD_ADD ? "add" :
				(tmp->sml_op == LDAP_MOD_INCREMENT ? "increment" :
					(tmp->sml_op == LDAP_MOD_DELETE ? "delete" :
						"replace")), tmp->sml_type.bv_val, 0 );
274

Kurt Zeilenga's avatar
Kurt Zeilenga committed
275
		if ( tmp->sml_values == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
276
			LDAP_LOG( OPERATION, DETAIL1, "\t\tno values", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
277
		} else if ( tmp->sml_values[0].bv_val == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
278
			LDAP_LOG( OPERATION, DETAIL1, "\t\tzero values", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
279
		} else if ( tmp->sml_values[1].bv_val == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
280
			LDAP_LOG( OPERATION, DETAIL1, "\t\tone value", 0, 0, 0 );
281
		} else {
Julius Enarusai's avatar
   
Julius Enarusai committed
282
			LDAP_LOG( OPERATION, DETAIL1, "\t\tmultiple values", 0, 0, 0 );
283
284
		}

285
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
286
		Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
287
288
289
290
			tmp->sml_op == LDAP_MOD_ADD ? "add" :
				(tmp->sml_op == LDAP_MOD_INCREMENT ? "increment" :
				(tmp->sml_op == LDAP_MOD_DELETE ? "delete" :
					"replace")), tmp->sml_type.bv_val, 0 );
291

Kurt Zeilenga's avatar
Kurt Zeilenga committed
292
		if ( tmp->sml_values == NULL ) {
293
294
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tno values", NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
295
		} else if ( tmp->sml_values[0].bv_val == NULL ) {
296
297
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tzero values", NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
298
		} else if ( tmp->sml_values[1].bv_val == NULL ) {
299
			Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
300
			   "\t\tone value", (long) tmp->sml_values[0].bv_len, NULL );
301
302
303
304
305
		} else {
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tmultiple values", NULL, NULL );
		}
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
306
307
	}

Howard Chu's avatar
Howard Chu committed
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
	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
334
#endif	/* LDAP_DEBUG */
335

336
337
	manageDSAit = get_manageDSAit( op );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
338
339
340
341
342
	/*
	 * 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
343
344
	op->o_bd = select_backend( &op->o_req_ndn, manageDSAit, 0 );
	if ( op->o_bd == NULL ) {
345
346
347
		rs->sr_ref = referral_rewrite( default_referral,
			NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
		if (!rs->sr_ref) rs->sr_ref = default_referral;
348

Pierangelo Masarati's avatar
Pierangelo Masarati committed
349
350
351
		if (rs->sr_ref != NULL ) {
			rs->sr_err = LDAP_REFERRAL;
			send_ldap_result( op, rs );
352

Pierangelo Masarati's avatar
Pierangelo Masarati committed
353
354
355
			if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
		} else {
			send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
356
				"referral missing" );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
357
		}
Howard Chu's avatar
Howard Chu committed
358
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
359
360
	}

361
	/* check restrictions */
362
363
	if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
		send_ldap_result( op, rs );
364
		goto cleanup;
365
366
	}

367
	/* check for referrals */
368
	if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
369
370
371
		goto cleanup;
	}

372
373
374
375
376
377
	/* check for modify/increment support */
	if( increment && !SLAP_INCREMENT( op->o_bd ) ) {
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
			"modify/increment not supported in context" );
	}

378
#if defined( LDAP_SLAPI )
Luke Howard's avatar
Luke Howard committed
379
	slapi_x_pblock_set_operation( pb, op );
380
	slapi_pblock_set( pb, SLAPI_MODIFY_TARGET, (void *)dn.bv_val );
381
	slapi_pblock_set( pb, SLAPI_MANAGEDSAIT, (void *)manageDSAit );
382
	modv = slapi_x_modifications2ldapmods( &modlist );
383
	slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)modv );
384

Luke Howard's avatar
Luke Howard committed
385
	rs->sr_err = doPluginFNs( op->o_bd, SLAPI_PLUGIN_PRE_MODIFY_FN, pb );
Howard Chu's avatar
Howard Chu committed
386
	if ( rs->sr_err < 0 ) {
387
		/*
388
389
		 * A preoperation plugin failure will abort the
		 * entire operation.
390
391
		 */
#ifdef NEW_LOGGING
392
		LDAP_LOG( OPERATION, INFO, "do_modify: modify preoperation plugin "
393
				"failed\n", 0, 0, 0 );
394
#else
395
		Debug(LDAP_DEBUG_TRACE, "do_modify: modify preoperation plugin failed.\n",
396
				0, 0, 0);
397
#endif
398
399
		if ( ( slapi_pblock_get( op->o_pb, SLAPI_RESULT_CODE, (void *)&rs->sr_err ) != 0 )  ||
		     rs->sr_err == LDAP_SUCCESS ) {
400
			rs->sr_err = LDAP_OTHER;
401
		}
Luke Howard's avatar
Luke Howard committed
402
		slapi_x_free_ldapmods( modv );
403
		modv = NULL;
404
		goto cleanup;
405
	}
406
407
408
409
410
411

	/*
	 * It's possible that the preoperation plugin changed the
	 * modification array, so we need to convert it back to
	 * a Modification list.
	 *
412
	 * Calling slapi_x_modifications2ldapmods() destroyed modlist so
413
414
	 * we don't need to free it.
	 */
Howard Chu's avatar
Howard Chu committed
415
416
	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, (void **)&modv );
	modlist = slapi_x_ldapmods2modifications( modv );
417
418
419
420
421
422
423
424

	/*
	 * 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).
425
426
427
	 *
	 * However, the post-operation plugin should still be 
	 * called.
428
	 */
Howard Chu's avatar
Howard Chu committed
429
430
431
	if ( modlist == NULL ) {
		rs->sr_err = LDAP_SUCCESS;
		send_ldap_result( op, rs );
432
	} else {
433
434
#endif /* defined( LDAP_SLAPI ) */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
435
436
437
438
	/*
	 * 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;
439
	 * 3) it's a replica and the dn supplied is the update_ndn.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
440
	 */
441
	if ( op->o_bd->be_modify ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
442
		/* do the update here */
443
		int repl_user = be_isupdate( op->o_bd, &op->o_ndn );
444

Kurt Zeilenga's avatar
Kurt Zeilenga committed
445
446
		/* Multimaster slapd does not have to check for replicator dn
		 * because it accepts each modify request
447
		 */
448
449
#if defined(LDAP_SYNCREPL) && !defined(SLAPD_MULTIMASTER)
		if ( !op->o_bd->syncinfo &&
450
			( !op->o_bd->be_update_ndn.bv_len || repl_user ))
451
452
453
#elif defined(LDAP_SYNCREPL) && defined(SLAPD_MULTIMASTER)
		if ( !op->o_bd->syncinfo )  /* LDAP_SYNCREPL overrides MM */
#elif !defined(LDAP_SYNCREPL) && !defined(SLAPD_MULTIMASTER)
454
		if ( !op->o_bd->be_update_ndn.bv_len || repl_user )
455
#endif
456
		{
457
			int update = op->o_bd->be_update_ndn.bv_len;
458
459
460
			char textbuf[SLAP_TEXT_BUFLEN];
			size_t textlen = sizeof textbuf;

461
			rs->sr_err = slap_mods_check( modlist, update, &rs->sr_text,
462
				textbuf, textlen, NULL );
463

464
465
			if( rs->sr_err != LDAP_SUCCESS ) {
				send_ldap_result( op, rs );
466
467
468
				goto cleanup;
			}

469
			if ( !repl_user ) {
470
471
472
				for( modtail = &modlist;
					*modtail != NULL;
					modtail = &(*modtail)->sml_next )
473
474
475
				{
					/* empty */
				}
476

477
478
479
480
				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
481
					goto cleanup;
482
483
484
				}
			}

Howard Chu's avatar
Howard Chu committed
485
			op->orm_modlist = modlist;
486
			if ( (op->o_bd->be_modify)( op, rs ) == 0
487
#ifdef SLAPD_MULTIMASTER
Kurt Zeilenga's avatar
Kurt Zeilenga committed
488
				&& !repl_user
489
490
491
#endif
			) {
				/* but we log only the ones not from a replicator user */
492
				replog( op );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
493
494
			}

495
#if defined(LDAP_SYNCREPL) || !defined(SLAPD_MULTIMASTER)
Kurt Zeilenga's avatar
Kurt Zeilenga committed
496
497
		/* send a referral */
		} else {
498
499
500
			BerVarray defref = NULL;
#ifdef LDAP_SYNCREPL
			if ( op->o_bd->syncinfo ) {
501
				defref = op->o_bd->syncinfo->provideruri_bv;
502
503
504
505
506
507
			} else
#endif
			{
				defref = op->o_bd->be_update_refs
						? op->o_bd->be_update_refs : default_referral;
			}
508
509
510
511
			if ( defref != NULL ) {
				rs->sr_ref = referral_rewrite( defref,
					NULL, &op->o_req_dn,
					LDAP_SCOPE_DEFAULT );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
512
				if (!rs->sr_ref) rs->sr_ref = defref;
513
514
515
516
517
518
519
520
521
522
				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" );
			}
523
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
524
525
		}
	} else {
526
527
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
		    "operation not supported within namingContext" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
528
529
	}

530
#if defined( LDAP_SLAPI )
531
532
	} /* modlist != NULL */

Howard Chu's avatar
Howard Chu committed
533
	if ( doPluginFNs( op->o_bd, SLAPI_PLUGIN_POST_MODIFY_FN, pb ) < 0 ) {
534
#ifdef NEW_LOGGING
535
		LDAP_LOG( OPERATION, INFO, "do_modify: modify postoperation plugins "
536
				"failed\n", 0, 0, 0 );
537
#else
538
		Debug(LDAP_DEBUG_TRACE, "do_modify: modify postoperation plugins "
539
				"failed.\n", 0, 0, 0);
540
541
542
543
#endif
	}
#endif /* defined( LDAP_SLAPI ) */

Howard Chu's avatar
Howard Chu committed
544
cleanup:
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
545
546

#ifdef LDAP_SYNC
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
547
	slap_graduate_commit_csn( op );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
548
549
#endif

550
551
	op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
	op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
552
	if ( modlist != NULL ) slap_mods_free( modlist );
553
#if defined( LDAP_SLAPI )
554
	if ( modv != NULL ) slapi_x_free_ldapmods( modv );
555
#endif
556
	return rs->sr_err;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
557
558
}

559
560
561
/*
 * Do basic attribute type checking and syntax validation.
 */
562
563
int slap_mods_check(
	Modifications *ml,
564
	int update,
565
	const char **text,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
566
	char *textbuf,
Howard Chu's avatar
Howard Chu committed
567
568
	size_t textlen,
	void *ctx )
569
570
571
{
	int rc;

572
	for( ; ml != NULL; ml = ml->sml_next ) {
573
		AttributeDescription *ad = NULL;
574
575

		/* convert to attribute description */
576
		rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
577
578

		if( rc != LDAP_SUCCESS ) {
579
			snprintf( textbuf, textlen, "%s: %s",
580
				ml->sml_type.bv_val, *text );
581
			*text = textbuf;
582
583
584
			return rc;
		}

585
		ad = ml->sml_desc;
586

587
588
		if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
			&& !slap_ad_is_binary( ad ))
589
590
		{
			/* attribute requires binary transfer */
591
592
			snprintf( textbuf, textlen,
				"%s: requires ;binary transfer",
593
				ml->sml_type.bv_val );
594
			*text = textbuf;
595
596
597
			return LDAP_UNDEFINED_TYPE;
		}

598
599
600
601
		if( !slap_syntax_is_binary( ad->ad_type->sat_syntax )
			&& slap_ad_is_binary( ad ))
		{
			/* attribute requires binary transfer */
602
603
			snprintf( textbuf, textlen,
				"%s: disallows ;binary transfer",
604
				ml->sml_type.bv_val );
605
			*text = textbuf;
606
607
608
			return LDAP_UNDEFINED_TYPE;
		}

609
		if( slap_ad_is_tag_range( ad )) {
610
611
			/* attribute requires binary transfer */
			snprintf( textbuf, textlen,
612
				"%s: inappropriate use of tag range option",
613
614
615
616
617
				ml->sml_type.bv_val );
			*text = textbuf;
			return LDAP_UNDEFINED_TYPE;
		}

618
619
		if (!update && is_at_no_user_mod( ad->ad_type )) {
			/* user modification disallowed */
620
621
			snprintf( textbuf, textlen,
				"%s: no user modification allowed",
622
				ml->sml_type.bv_val );
623
			*text = textbuf;
624
625
626
			return LDAP_CONSTRAINT_VIOLATION;
		}

627
		if ( is_at_obsolete( ad->ad_type ) &&
628
629
630
			(( ml->sml_op != LDAP_MOD_REPLACE &&
				ml->sml_op != LDAP_MOD_DELETE ) ||
					ml->sml_values != NULL ))
631
632
633
634
635
636
637
		{
			/*
			 * attribute is obsolete,
			 * only allow replace/delete with no values
			 */
			snprintf( textbuf, textlen,
				"%s: attribute is obsolete",
638
				ml->sml_type.bv_val );
639
640
641
642
			*text = textbuf;
			return LDAP_CONSTRAINT_VIOLATION;
		}

643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
		if ( ml->sml_op == LDAP_MOD_INCREMENT &&
#ifdef SLAPD_REAL_SYNTAX
			!is_at_syntax( ad->ad_type, SLAPD_REAL_SYNTAX ) &&
#endif
			!is_at_syntax( ad->ad_type, SLAPD_INTEGER_SYNTAX ) )
		{
			/*
			 * attribute values must be INTEGER or REAL
			 */
			snprintf( textbuf, textlen,
				"%s: attribute syntax inappropriate for increment",
				ml->sml_type.bv_val );
			*text = textbuf;
			return LDAP_CONSTRAINT_VIOLATION;
		}

659
660
661
		/*
		 * check values
		 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
662
		if( ml->sml_values != NULL ) {
663
664
665
			ber_len_t nvals;
			slap_syntax_validate_func *validate =
				ad->ad_type->sat_syntax->ssyn_validate;
666
667
668
669
			slap_syntax_transform_func *pretty =
				ad->ad_type->sat_syntax->ssyn_pretty;
 
			if( !pretty && !validate ) {
670
				*text = "no validator for syntax";
671
672
				snprintf( textbuf, textlen,
					"%s: no validator for syntax %s",
673
					ml->sml_type.bv_val,
674
675
					ad->ad_type->sat_syntax->ssyn_oid );
				*text = textbuf;
676
677
678
				return LDAP_INVALID_SYNTAX;
			}

679
680
			/*
			 * check that each value is valid per syntax
681
			 *	and pretty if appropriate
682
			 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
683
			for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
684
				struct berval pval;
685
686
				if( pretty ) {
					rc = pretty( ad->ad_type->sat_syntax,
Howard Chu's avatar
Howard Chu committed
687
						&ml->sml_values[nvals], &pval, ctx );
688
689
				} else {
					rc = validate( ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
690
						&ml->sml_values[nvals] );
691
				}
692
693

				if( rc != 0 ) {
694
					snprintf( textbuf, textlen,
695
						"%s: value #%ld invalid per syntax",
696
						ml->sml_type.bv_val, (long) nvals );
697
					*text = textbuf;
698
699
					return LDAP_INVALID_SYNTAX;
				}
700
701

				if( pretty ) {
Howard Chu's avatar
Howard Chu committed
702
					ber_memfree_x( ml->sml_values[nvals].bv_val, ctx );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
703
					ml->sml_values[nvals] = pval;
704
				}
705
706
			}

707
708
709
710
711
712
713
714
715
716
717
718
719
720
			/*
			 * 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;
			}

721
			if( nvals && ad->ad_type->sat_equality &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
722
				ad->ad_type->sat_equality->smr_normalize )
723
			{
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
724
725
726
				ml->sml_nvalues = ber_memalloc_x(
					(nvals+1)*sizeof(struct berval), ctx );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
727
				for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
728
729
					rc = ad->ad_type->sat_equality->smr_normalize(
						0,
730
						ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
731
						ad->ad_type->sat_equality,
Howard Chu's avatar
Howard Chu committed
732
						&ml->sml_values[nvals], &ml->sml_nvalues[nvals], ctx );
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
					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
750

751
752
				ml->sml_nvalues[nvals].bv_val = NULL;
				ml->sml_nvalues[nvals].bv_len = 0;
753
			}
754
755
756
757
758
759
760
761
		}
	}

	return LDAP_SUCCESS;
}

int slap_mods_opattrs(
	Operation *op,
762
	Modifications *mods,
763
	Modifications **modtail,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
764
765
	const char **text,
	char *textbuf, size_t textlen )
766
{
767
	struct berval name, timestamp, csn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
768
	struct berval nname;
769
770
	char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
	char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
771
772
773
774
775
	Modifications *mod;

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

776
777
778
779
#ifdef LDAP_SYNCREPL
	syncinfo_t *si = op->o_si;
#endif

780
781
782
	assert( modtail != NULL );
	assert( *modtail == NULL );

783
784
785
786
787
788
#ifdef LDAP_SYNCREPL
	if ( SLAP_LASTMOD(op->o_bd) && ( !si || si->lastmod == LASTMOD_GEN ))
#else
	if ( SLAP_LASTMOD(op->o_bd) )
#endif
	{
789
790
		struct tm *ltm;
		time_t now = slap_get_time();
791

792
793
		ldap_pvt_thread_mutex_lock( &gmtime_mutex );
		ltm = gmtime( &now );
Howard Chu's avatar
Howard Chu committed
794
		lutil_gentime( timebuf, sizeof(timebuf), ltm );
795

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
796
#ifdef LDAP_SYNC
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
797
798
799
		slap_get_csn( op, csnbuf, sizeof(csnbuf), &csn, 1 );
#else
		slap_get_csn( op, csnbuf, sizeof(csnbuf), &csn, 0 );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
800
801
802
#endif

		ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
803

804
805
806
807
808
809
		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
810
			nname = name;
811
812
		} else {
			name = op->o_dn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
813
			nname = op->o_ndn;
814
		}
815
816
817
	}

	if( op->o_tag == LDAP_REQ_ADD ) {
818
		struct berval tmpval;
819

820
821
822
		if( global_schemacheck ) {
			int rc = mods_structural_class( mods, &tmpval,
				text, textbuf, textlen );
823
			if( rc != LDAP_SUCCESS ) return rc;
824
825
826
827
828

			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
829
830
			mod->sml_values =
				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
831
			ber_dupbv( &mod->sml_values[0], &tmpval );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
832
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
833
834
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
835
836
837
838
839
840
			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 );
841
842
			*modtail = mod;
			modtail = &mod->sml_next;
843
		}
844

845
846
847
848
849
850
#ifdef LDAP_SYNCREPL
		if ( SLAP_LASTMOD(op->o_bd) && ( !si || si->lastmod == LASTMOD_GEN ))
#else
		if ( SLAP_LASTMOD(op->o_bd) )
#endif
		{
851
			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
852

853
854
855
856
857
#ifdef LDAP_SYNCREPL
			if ( !si ) {
#endif
				tmpval.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
				tmpval.bv_val = uuidbuf;
858
		
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
				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
875
876
877
878
879

			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
880
881
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &name );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
882
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
883
884
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
885
886
887
888
889
890
			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 );
891
892
893
894
895
896
897
			*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
898
899
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &timestamp );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
900
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
901
902
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
903
			mod->sml_nvalues = NULL;
904
905
			*modtail = mod;
			modtail = &mod->sml_next;
906
		}
907
	}
908

909
910
911
912
913
914
#ifdef LDAP_SYNCREPL
	if ( SLAP_LASTMOD(op->o_bd) && ( !si || si->lastmod == LASTMOD_GEN ))
#else
	if ( SLAP_LASTMOD(op->o_bd) )
#endif
	{
915
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
916
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
917
		mod->sml_type.bv_val = NULL;
918
		mod->sml_desc = slap_schema.si_ad_entryCSN;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
919