modify.c 25.3 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
	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 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
319
320

	    		len = 0;
Howard Chu's avatar
Howard Chu committed
321
				ptr = abuf;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
322
323
324
325
326
327

				if( 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, tmp->sml_type.bv_val, 0, 0 );
					continue;
				}
Howard Chu's avatar
Howard Chu committed
328
329
330
331
332
333
334
335
336
337
338
339
340
			}
			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
341
#endif	/* LDAP_DEBUG */
342

343
344
	manageDSAit = get_manageDSAit( op );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
345
346
347
348
349
	/*
	 * 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
350
351
	op->o_bd = select_backend( &op->o_req_ndn, manageDSAit, 0 );
	if ( op->o_bd == NULL ) {
352
353
354
		rs->sr_ref = referral_rewrite( default_referral,
			NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
		if (!rs->sr_ref) rs->sr_ref = default_referral;
355

Pierangelo Masarati's avatar
Pierangelo Masarati committed
356
357
358
		if (rs->sr_ref != NULL ) {
			rs->sr_err = LDAP_REFERRAL;
			send_ldap_result( op, rs );
359

Pierangelo Masarati's avatar
Pierangelo Masarati committed
360
361
362
			if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
		} else {
			send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
363
				"referral missing" );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
364
		}
Howard Chu's avatar
Howard Chu committed
365
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
366
367
	}

368
	/* check restrictions */
369
370
	if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
		send_ldap_result( op, rs );
371
		goto cleanup;
372
373
	}

374
	/* check for referrals */
375
	if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
376
377
378
		goto cleanup;
	}

379
380
381
382
383
384
	/* 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" );
	}

385
#if defined( LDAP_SLAPI )
Luke Howard's avatar
Luke Howard committed
386
	slapi_x_pblock_set_operation( pb, op );
387
	slapi_pblock_set( pb, SLAPI_MODIFY_TARGET, (void *)dn.bv_val );
388
	slapi_pblock_set( pb, SLAPI_MANAGEDSAIT, (void *)manageDSAit );
389
	modv = slapi_x_modifications2ldapmods( &modlist );
390
	slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)modv );
391

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

	/*
	 * It's possible that the preoperation plugin changed the
	 * modification array, so we need to convert it back to
	 * a Modification list.
	 *
419
	 * Calling slapi_x_modifications2ldapmods() destroyed modlist so
420
421
	 * we don't need to free it.
	 */
Howard Chu's avatar
Howard Chu committed
422
423
	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, (void **)&modv );
	modlist = slapi_x_ldapmods2modifications( modv );
424
425
426
427
428
429
430
431

	/*
	 * 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).
432
433
434
	 *
	 * However, the post-operation plugin should still be 
	 * called.
435
	 */
Howard Chu's avatar
Howard Chu committed
436
437
438
	if ( modlist == NULL ) {
		rs->sr_err = LDAP_SUCCESS;
		send_ldap_result( op, rs );
439
	} else {
440
441
#endif /* defined( LDAP_SLAPI ) */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
442
443
444
445
	/*
	 * 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;
446
	 * 3) it's a replica and the dn supplied is the update_ndn.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
447
	 */
448
	if ( op->o_bd->be_modify ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
449
		/* do the update here */
450
		int repl_user = be_isupdate( op->o_bd, &op->o_ndn );
451

Kurt Zeilenga's avatar
Kurt Zeilenga committed
452
453
		/* Multimaster slapd does not have to check for replicator dn
		 * because it accepts each modify request
454
		 */
455
#ifndef SLAPD_MULTIMASTER
456
		if ( !op->o_bd->syncinfo &&
457
			( !op->o_bd->be_update_ndn.bv_len || repl_user ))
458
459
#else
		if ( !op->o_bd->syncinfo )
460
#endif
461
		{
462
			int update = op->o_bd->be_update_ndn.bv_len;
463
464
465
			char textbuf[SLAP_TEXT_BUFLEN];
			size_t textlen = sizeof textbuf;

466
			rs->sr_err = slap_mods_check( modlist, update, &rs->sr_text,
467
				textbuf, textlen, NULL );
468

469
470
			if( rs->sr_err != LDAP_SUCCESS ) {
				send_ldap_result( op, rs );
471
472
473
				goto cleanup;
			}

474
			if ( !repl_user ) {
475
476
477
				for( modtail = &modlist;
					*modtail != NULL;
					modtail = &(*modtail)->sml_next )
478
479
480
				{
					/* empty */
				}
481

482
483
484
485
				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
486
					goto cleanup;
487
488
489
				}
			}

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

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

532
#if defined( LDAP_SLAPI )
533
534
	} /* modlist != NULL */

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

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

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
548
	slap_graduate_commit_csn( op );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
549

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
722
723
724
725
726
727
728
			/* if the type has a normalizer, generate the
			 * normalized values. otherwise leave them NULL.
			 *
			 * this is different from the rule for attributes
			 * in an entry - in an attribute list, the normalized
			 * value is set equal to the non-normalized value
			 * when there is no normalizer.
			 */
729
			if( nvals && ad->ad_type->sat_equality &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
730
				ad->ad_type->sat_equality->smr_normalize )
731
			{
Kurt Zeilenga's avatar
cleanup    
Kurt Zeilenga committed
732
733
734
				ml->sml_nvalues = ber_memalloc_x(
					(nvals+1)*sizeof(struct berval), ctx );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
735
				for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
736
					rc = ad->ad_type->sat_equality->smr_normalize(
737
						SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
738
						ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
739
						ad->ad_type->sat_equality,
Howard Chu's avatar
Howard Chu committed
740
						&ml->sml_values[nvals], &ml->sml_nvalues[nvals], ctx );
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
					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
758

759
760
				ml->sml_nvalues[nvals].bv_val = NULL;
				ml->sml_nvalues[nvals].bv_len = 0;
761
			}
762
763
764
765
766
767
768
769
		}
	}

	return LDAP_SUCCESS;
}

int slap_mods_opattrs(
	Operation *op,
770
	Modifications *mods,
771
	Modifications **modtail,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
772
773
	const char **text,
	char *textbuf, size_t textlen )
774
{
775
	struct berval name, timestamp, csn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
776
	struct berval nname;
777
778
	char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
	char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
779
780
781
782
783
	Modifications *mod;

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

784
785
786
	assert( modtail != NULL );
	assert( *modtail == NULL );

787
	if ( SLAP_LASTMOD( op->o_bd )) {
788
789
		struct tm *ltm;
		time_t now = slap_get_time();
790

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

Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
795
		slap_get_csn( op, csnbuf, sizeof(csnbuf), &csn, 1 );
Jong Hyuk Choi's avatar
Jong Hyuk Choi committed
796
797

		ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
798

799
800
801
802
803
804
		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
805
			nname = name;
806
807
		} else {
			name = op->o_dn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
808
			nname = op->o_ndn;
809
		}
810
811
812
	}

	if( op->o_tag == LDAP_REQ_ADD ) {
813
		struct berval tmpval;
814

815
816
817
		if( global_schemacheck ) {
			int rc = mods_structural_class( mods, &tmpval,
				text, textbuf, textlen );
818
			if( rc != LDAP_SUCCESS ) return rc;
819
820
821
822
823

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

840
		if ( SLAP_LASTMOD( op->o_bd )) {
841
			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
842

843
844
			tmpval.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
			tmpval.bv_val = uuidbuf;
845
		
846
847
848
849
850
851
852
853
854
855
856
857
858
			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;
859
860
861
862
863

			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
864
865
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &name );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
866
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
867
868
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
869
870
871
872
873
874
			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 );
875
876
877
878
879
880
881
			*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
882
883
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &timestamp );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
884
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
885
886
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
887
			mod->sml_nvalues = NULL;
888
889
			*modtail = mod;
			modtail = &mod->sml_next;
890
		}
891
	}
892

893
	if ( SLAP_LASTMOD( op->o_bd )) {
894
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
895
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
896
		mod->sml_type.bv_val = NULL;
897
		mod->sml_desc = slap_schema.si_ad_entryCSN;
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], &csn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed