modify.c 22.4 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
27
#include "lutil.h"

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

34
int
Kurt Zeilenga's avatar
Kurt Zeilenga committed
35
36
do_modify(
    Connection	*conn,
37
    Operation	*op )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
38
{
39
	struct berval dn = { 0, NULL };
Howard Chu's avatar
Howard Chu committed
40
41
	struct berval pdn = { 0, NULL };
	struct berval ndn = { 0, NULL };
Kurt Zeilenga's avatar
Kurt Zeilenga committed
42
	char		*last;
43
44
	ber_tag_t	tag;
	ber_len_t	len;
45
46
	Modifications	*modlist = NULL;
	Modifications	**modtail = &modlist;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
47
#ifdef LDAP_DEBUG
48
	Modifications *tmp;
49
50
51
52
#endif
#ifdef LDAP_SLAPI
	LDAPMod		**modv = NULL;
	Slapi_PBlock *pb = op->o_pb;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
53
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
54
	Backend		*be;
55
	int rc;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
56
	const char	*text;
57
	int manageDSAit;
58

59
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
60
	LDAP_LOG( OPERATION, ENTRY, "do_modify: enter\n", 0, 0, 0 );
61
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
62
	Debug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 );
63
64
#endif

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

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

91
92
		send_ldap_disconnect( conn, op,
			LDAP_PROTOCOL_ERROR, "decoding error" );
93
		return SLAPD_DISCONNECT;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
94
95
	}

96
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
97
	LDAP_LOG( OPERATION, ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
98
#else
99
	Debug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
100
101
#endif

102

Kurt Zeilenga's avatar
Kurt Zeilenga committed
103
	/* collect modifications & save for later */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
104

Kurt Zeilenga's avatar
Kurt Zeilenga committed
105
106
107
108
	for ( tag = ber_first_element( op->o_ber, &len, &last );
	    tag != LBER_DEFAULT;
	    tag = ber_next_element( op->o_ber, &len, last ) )
	{
109
		ber_int_t mop;
110
		Modifications tmp, *mod;
111

Kurt Zeilenga's avatar
Kurt Zeilenga committed
112
113
114
#ifdef SLAP_NVALUES
		tmp.sml_nvalues = NULL;
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
115

116
		if ( ber_scanf( op->o_ber, "{i{m[W]}}", &mop,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
117
		    &tmp.sml_type, &tmp.sml_values )
Kurt Zeilenga's avatar
Kurt Zeilenga committed
118
119
		    == LBER_ERROR )
		{
120
121
			send_ldap_disconnect( conn, op,
				LDAP_PROTOCOL_ERROR, "decoding modlist error" );
122
			rc = SLAPD_DISCONNECT;
Howard Chu's avatar
Howard Chu committed
123
			goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
124
125
		}

Howard Chu's avatar
Howard Chu committed
126
		mod = (Modifications *) ch_malloc( sizeof(Modifications) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
127
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
128
		mod->sml_type = tmp.sml_type;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
129
130
131
132
		mod->sml_values = tmp.sml_values;
#ifdef SLAP_NVALUES
		mod->sml_nvalues = NULL;
#endif
133
		mod->sml_desc = NULL;
134
		mod->sml_next = NULL;
135
136
		*modtail = mod;

137
138
		switch( mop ) {
		case LDAP_MOD_ADD:
Kurt Zeilenga's avatar
Kurt Zeilenga committed
139
			if ( mod->sml_values == NULL ) {
140
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
141
				LDAP_LOG( OPERATION, ERR, 
142
					"do_modify: modify/add operation (%ld) requires values\n",
Julius Enarusai's avatar
   
Julius Enarusai committed
143
					(long)mop, 0, 0 );
144
#else
145
146
147
				Debug( LDAP_DEBUG_ANY,
					"do_modify: modify/add operation (%ld) requires values\n",
					(long) mop, 0, 0 );
148
149
#endif

150
151
152
153
154
155
				send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
					NULL, "modify/add operation requires values",
					NULL, NULL );
				rc = LDAP_PROTOCOL_ERROR;
				goto cleanup;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
156

157
158
159
160
161
162
163
			/* fall through */

		case LDAP_MOD_DELETE:
		case LDAP_MOD_REPLACE:
			break;

		default: {
164
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
165
166
				LDAP_LOG( OPERATION, ERR, 
					"do_modify: invalid modify operation (%ld)\n", (long)mop, 0, 0 );
167
#else
168
169
170
				Debug( LDAP_DEBUG_ANY,
					"do_modify: invalid modify operation (%ld)\n",
					(long) mop, 0, 0 );
171
172
#endif

173
174
175
176
177
				send_ldap_result( conn, op, LDAP_PROTOCOL_ERROR,
					NULL, "unrecognized modify operation", NULL, NULL );
				rc = LDAP_PROTOCOL_ERROR;
				goto cleanup;
			}
Kurt Zeilenga's avatar
Kurt Zeilenga committed
178
		}
179

180
		modtail = &mod->sml_next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
181
182
183
	}
	*modtail = NULL;

184
	if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
185
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
186
		LDAP_LOG( OPERATION, ERR, "do_modify: get_ctrls failed\n", 0, 0, 0 );
187
#else
188
		Debug( LDAP_DEBUG_ANY, "do_modify: get_ctrls failed\n", 0, 0, 0 );
189
190
#endif

191
192
193
		goto cleanup;
	}

Howard Chu's avatar
Howard Chu committed
194
	rc = dnPrettyNormal( NULL, &dn, &pdn, &ndn );
195
	if( rc != LDAP_SUCCESS ) {
196
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
197
198
		LDAP_LOG( OPERATION, INFO, "do_modify: conn %d  invalid dn (%s)\n",
			conn->c_connid, dn.bv_val, 0 );
199
#else
200
		Debug( LDAP_DEBUG_ANY,
201
			"do_modify: invalid dn (%s)\n", dn.bv_val, 0, 0 );
202
#endif
203
204
205
206
		send_ldap_result( conn, op, rc = LDAP_INVALID_DN_SYNTAX, NULL,
		    "invalid DN", NULL, NULL );
		goto cleanup;
	}
207

Howard Chu's avatar
Howard Chu committed
208
	if( ndn.bv_len == 0 ) {
209
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
210
211
		LDAP_LOG( OPERATION, ERR, 
			"do_modify: attempt to modify root DSE.\n",0, 0, 0 );
212
#else
213
		Debug( LDAP_DEBUG_ANY, "do_modify: root dse!\n", 0, 0, 0 );
214
215
#endif

216
217
218
		send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
			NULL, "modify upon the root DSE not supported", NULL, NULL );
		goto cleanup;
219

220
	} else if ( bvmatch( &ndn, &global_schemandn ) ) {
221
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
222
223
		LDAP_LOG( OPERATION, ERR,
			"do_modify: attempt to modify subschema subentry.\n" , 0, 0, 0  );
224
225
226
227
228
229
230
231
#else
		Debug( LDAP_DEBUG_ANY, "do_modify: subschema subentry!\n", 0, 0, 0 );
#endif

		send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
			NULL, "modification of subschema subentry not supported",
			NULL, NULL );
		goto cleanup;
232
233
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
234
#ifdef LDAP_DEBUG
235
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
236
	LDAP_LOG( OPERATION, DETAIL1, "do_modify: modifications:\n", 0, 0, 0  );
237
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
238
	Debug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 );
239
240
#endif

241
	for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
242
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
243
244
245
246
		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 );
247

Kurt Zeilenga's avatar
Kurt Zeilenga committed
248
		if ( tmp->sml_values == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
249
			LDAP_LOG( OPERATION, DETAIL1, "\t\tno values", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
250
		} else if ( tmp->sml_values[0].bv_val == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
251
			LDAP_LOG( OPERATION, DETAIL1, "\t\tzero values", 0, 0, 0 );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
252
		} else if ( tmp->sml_values[1].bv_val == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
253
			LDAP_LOG( OPERATION, DETAIL1, "\t\tone value", 0, 0, 0 );
254
		} else {
Julius Enarusai's avatar
   
Julius Enarusai committed
255
			LDAP_LOG( OPERATION, DETAIL1, "\t\tmultiple values", 0, 0, 0 );
256
257
		}

258
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
259
		Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
260
261
262
			tmp->sml_op == LDAP_MOD_ADD
				? "add" : (tmp->sml_op == LDAP_MOD_DELETE
					? "delete" : "replace"), tmp->sml_type.bv_val, 0 );
263

Kurt Zeilenga's avatar
Kurt Zeilenga committed
264
		if ( tmp->sml_values == NULL ) {
265
266
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tno values", NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
267
		} else if ( tmp->sml_values[0].bv_val == NULL ) {
268
269
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tzero values", NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
270
		} else if ( tmp->sml_values[1].bv_val == NULL ) {
271
			Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
272
			   "\t\tone value", (long) tmp->sml_values[0].bv_len, NULL );
273
274
275
276
277
		} else {
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tmultiple values", NULL, NULL );
		}
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
278
279
280
	}
#endif

Howard Chu's avatar
Howard Chu committed
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
	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 );
		}
	}
307

308
309
	manageDSAit = get_manageDSAit( op );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
310
311
312
313
314
	/*
	 * 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.
	 */
Howard Chu's avatar
Howard Chu committed
315
	if ( (be = select_backend( &ndn, manageDSAit, 0 )) == NULL ) {
316
		BerVarray ref = referral_rewrite( default_referral,
Howard Chu's avatar
Howard Chu committed
317
			NULL, &pdn, LDAP_SCOPE_DEFAULT );
318

319
		send_ldap_result( conn, op, rc = LDAP_REFERRAL,
320
321
			NULL, NULL, ref ? ref : default_referral, NULL );

322
		ber_bvarray_free( ref );
Howard Chu's avatar
Howard Chu committed
323
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
324
325
	}

326
327
	/* check restrictions */
	rc = backend_check_restrictions( be, conn, op, NULL, &text ) ;
328
329
	if( rc != LDAP_SUCCESS ) {
		send_ldap_result( conn, op, rc,
330
			NULL, text, NULL, NULL );
331
		goto cleanup;
332
333
	}

334
	/* check for referrals */
Howard Chu's avatar
Howard Chu committed
335
	rc = backend_check_referrals( be, conn, op, &pdn, &ndn );
336
337
338
339
	if ( rc != LDAP_SUCCESS ) {
		goto cleanup;
	}

340
#if defined( LDAP_SLAPI )
341
342
343
	slapi_x_backend_set_pb( pb, be );
	slapi_x_connection_set_pb( pb, conn );
	slapi_x_operation_set_pb( pb, op );
344
	slapi_pblock_set( pb, SLAPI_MODIFY_TARGET, (void *)dn.bv_val );
345
	slapi_pblock_set( pb, SLAPI_MANAGEDSAIT, (void *)manageDSAit );
346
	modv = slapi_x_modifications2ldapmods( &modlist );
347
	slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)modv );
348
349

	rc = doPluginFNs( be, SLAPI_PLUGIN_PRE_MODIFY_FN, pb );
350
	if ( rc != 0 ) {
351
		/*
352
353
		 * A preoperation plugin failure will abort the
		 * entire operation.
354
355
		 */
#ifdef NEW_LOGGING
356
		LDAP_LOG( OPERATION, INFO, "do_modify: modify preoperation plugin "
357
				"failed\n", 0, 0, 0 );
358
#else
359
		Debug(LDAP_DEBUG_TRACE, "do_modify: modify preoperation plugin failed.\n",
360
				0, 0, 0);
361
#endif
362
		if ( slapi_pblock_get( pb, SLAPI_RESULT_CODE, (void *)&rc ) != 0) {
363
			rc = LDAP_OTHER;
364
365
366
		}
		ldap_mods_free( modv, 1 );
		modv = NULL;
367
		goto cleanup;
368
	}
369
370
371
372
373
374

	/*
	 * It's possible that the preoperation plugin changed the
	 * modification array, so we need to convert it back to
	 * a Modification list.
	 *
375
	 * Calling slapi_x_modifications2ldapmods() destroyed modlist so
376
377
378
	 * we don't need to free it.
	 */
	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, (void **)&modv );
379
	modlist = slapi_x_ldapmods2modifications( modv );
380
381
#endif /* defined( LDAP_SLAPI ) */

Kurt Zeilenga's avatar
Kurt Zeilenga committed
382
383
384
385
	/*
	 * 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;
386
	 * 3) it's a replica and the dn supplied is the update_ndn.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
387
	 */
388
	if ( be->be_modify ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
389
		/* do the update here */
390
		int repl_user = be_isupdate( be, &op->o_ndn );
391
#ifndef SLAPD_MULTIMASTER
Kurt Zeilenga's avatar
Kurt Zeilenga committed
392
393
		/* Multimaster slapd does not have to check for replicator dn
		 * because it accepts each modify request
394
		 */
395
		if ( !be->be_update_ndn.bv_len || repl_user )
396
#endif
397
		{
398
			int update = be->be_update_ndn.bv_len;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
399
			const char *text;
400
401
402
			char textbuf[SLAP_TEXT_BUFLEN];
			size_t textlen = sizeof textbuf;

403
			rc = slap_mods_check( modlist, update, &text,
404
				textbuf, textlen );
405
406
407
408
409
410
411

			if( rc != LDAP_SUCCESS ) {
				send_ldap_result( conn, op, rc,
					NULL, text, NULL, NULL );
				goto cleanup;
			}

412
			if ( !repl_user ) {
413
414
415
				for( modtail = &modlist;
					*modtail != NULL;
					modtail = &(*modtail)->sml_next )
416
417
418
				{
					/* empty */
				}
419

420
				rc = slap_mods_opattrs( be, op, modlist, modtail, &text,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
421
					textbuf, textlen );
422
423
				if( rc != LDAP_SUCCESS ) {
					send_ldap_result( conn, op, rc,
424
						NULL, text,
425
						NULL, NULL );
Howard Chu's avatar
Howard Chu committed
426
					goto cleanup;
427
428
429
				}
			}

430
			if ( (*be->be_modify)( be, conn, op, &pdn, &ndn, modlist ) == 0
431
#ifdef SLAPD_MULTIMASTER
Kurt Zeilenga's avatar
Kurt Zeilenga committed
432
				&& !repl_user
433
434
435
#endif
			) {
				/* but we log only the ones not from a replicator user */
436
				replog( be, op, &pdn, &ndn, modlist );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
437
438
			}

439
#ifndef SLAPD_MULTIMASTER
Kurt Zeilenga's avatar
Kurt Zeilenga committed
440
441
		/* send a referral */
		} else {
442
			BerVarray defref = be->be_update_refs
443
				? be->be_update_refs : default_referral;
444
			BerVarray ref = referral_rewrite( defref,
Howard Chu's avatar
Howard Chu committed
445
				NULL, &pdn, LDAP_SCOPE_DEFAULT );
446

447
			send_ldap_result( conn, op, rc = LDAP_REFERRAL, NULL, NULL,
448
449
				ref ? ref : defref, NULL );

450
			ber_bvarray_free( ref );
451
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
452
453
		}
	} else {
454
		send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
455
456
		    NULL, "operation not supported within namingContext",
			NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
457
458
	}

459
#if defined( LDAP_SLAPI )
460
	if ( doPluginFNs( be, SLAPI_PLUGIN_POST_MODIFY_FN, pb ) != 0 ) {
461
#ifdef NEW_LOGGING
462
		LDAP_LOG( OPERATION, INFO, "do_modify: modify postoperation plugins "
463
				"failed\n", 0, 0, 0 );
464
#else
465
		Debug(LDAP_DEBUG_TRACE, "do_modify: modify postoperation plugins "
466
				"failed.\n", 0, 0, 0);
467
468
469
470
#endif
	}
#endif /* defined( LDAP_SLAPI ) */

Howard Chu's avatar
Howard Chu committed
471
cleanup:
Howard Chu's avatar
Howard Chu committed
472
473
	free( pdn.bv_val );
	free( ndn.bv_val );
474
	if ( modlist != NULL ) slap_mods_free( modlist );
475
#if defined( LDAP_SLAPI )
476
	if ( modv != NULL ) slapi_x_free_ldapmods( modv );
477
#endif
478
	return rc;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
479
480
}

481
482
483
/*
 * Do basic attribute type checking and syntax validation.
 */
484
485
int slap_mods_check(
	Modifications *ml,
486
	int update,
487
	const char **text,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
488
489
	char *textbuf,
	size_t textlen )
490
491
492
{
	int rc;

493
	for( ; ml != NULL; ml = ml->sml_next ) {
494
		AttributeDescription *ad = NULL;
495
496

		/* convert to attribute description */
497
		rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
498
499

		if( rc != LDAP_SUCCESS ) {
500
			snprintf( textbuf, textlen, "%s: %s",
501
				ml->sml_type.bv_val, *text );
502
			*text = textbuf;
503
504
505
			return rc;
		}

506
		ad = ml->sml_desc;
507

508
509
		if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
			&& !slap_ad_is_binary( ad ))
510
511
		{
			/* attribute requires binary transfer */
512
513
			snprintf( textbuf, textlen,
				"%s: requires ;binary transfer",
514
				ml->sml_type.bv_val );
515
			*text = textbuf;
516
517
518
			return LDAP_UNDEFINED_TYPE;
		}

519
520
521
522
		if( !slap_syntax_is_binary( ad->ad_type->sat_syntax )
			&& slap_ad_is_binary( ad ))
		{
			/* attribute requires binary transfer */
523
524
			snprintf( textbuf, textlen,
				"%s: disallows ;binary transfer",
525
				ml->sml_type.bv_val );
526
			*text = textbuf;
527
528
529
			return LDAP_UNDEFINED_TYPE;
		}

530
		if( slap_ad_is_tag_range( ad )) {
531
532
			/* attribute requires binary transfer */
			snprintf( textbuf, textlen,
533
				"%s: inappropriate use of tag range option",
534
535
536
537
538
				ml->sml_type.bv_val );
			*text = textbuf;
			return LDAP_UNDEFINED_TYPE;
		}

539
540
		if (!update && is_at_no_user_mod( ad->ad_type )) {
			/* user modification disallowed */
541
542
			snprintf( textbuf, textlen,
				"%s: no user modification allowed",
543
				ml->sml_type.bv_val );
544
			*text = textbuf;
545
546
547
			return LDAP_CONSTRAINT_VIOLATION;
		}

548
		if ( is_at_obsolete( ad->ad_type ) &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
549
			( ml->sml_op == LDAP_MOD_ADD || ml->sml_values != NULL ) )
550
551
552
553
554
555
556
		{
			/*
			 * attribute is obsolete,
			 * only allow replace/delete with no values
			 */
			snprintf( textbuf, textlen,
				"%s: attribute is obsolete",
557
				ml->sml_type.bv_val );
558
559
560
561
			*text = textbuf;
			return LDAP_CONSTRAINT_VIOLATION;
		}

562
563
564
		/*
		 * check values
		 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
565
		if( ml->sml_values != NULL ) {
566
567
568
			ber_len_t nvals;
			slap_syntax_validate_func *validate =
				ad->ad_type->sat_syntax->ssyn_validate;
569
570
571
572
			slap_syntax_transform_func *pretty =
				ad->ad_type->sat_syntax->ssyn_pretty;
 
			if( !pretty && !validate ) {
573
				*text = "no validator for syntax";
574
575
				snprintf( textbuf, textlen,
					"%s: no validator for syntax %s",
576
					ml->sml_type.bv_val,
577
578
					ad->ad_type->sat_syntax->ssyn_oid );
				*text = textbuf;
579
580
581
				return LDAP_INVALID_SYNTAX;
			}

582
583
			/*
			 * check that each value is valid per syntax
584
			 *	and pretty if appropriate
585
			 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
586
			for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
587
				struct berval pval;
588
589
				if( pretty ) {
					rc = pretty( ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
590
						&ml->sml_values[nvals], &pval );
591
592
				} else {
					rc = validate( ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
593
						&ml->sml_values[nvals] );
594
				}
595
596

				if( rc != 0 ) {
597
					snprintf( textbuf, textlen,
598
						"%s: value #%ld invalid per syntax",
599
						ml->sml_type.bv_val, (long) nvals );
600
					*text = textbuf;
601
602
					return LDAP_INVALID_SYNTAX;
				}
603
604

				if( pretty ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
605
606
					ber_memfree( ml->sml_values[nvals].bv_val );
					ml->sml_values[nvals] = pval;
607
				}
608
609
			}

610
611
612
613
614
615
#ifdef SLAP_NVALUES
			if( nvals && ad->ad_type->sat_equality &&
				ad->ad_type->sat_equality->smr_match &&
				ad->ad_type->sat_syntax->ssyn_normalize )
			{
				ml->sml_nvalues = ch_malloc( (nvals+1)*sizeof(struct berval) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
616
				for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
					rc = ad->ad_type->sat_syntax->ssyn_normalize(
						ad->ad_type->sat_syntax,
						&ml->sml_values[nvals], &ml->sml_nvalues[nvals] );
					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;
					}
				}
637
638
				ml->sml_nvalues[nvals].bv_val = NULL;
				ml->sml_nvalues[nvals].bv_len = 0;
639
640
641
			}
#endif

642
643
644
645
			/*
			 * a rough single value check... an additional check is needed
			 * to catch add of single value to existing single valued attribute
			 */
646
			if ((ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE)
647
648
				&& nvals > 1 && is_at_single_value( ad->ad_type ))
			{
649
650
				snprintf( textbuf, textlen,
					"%s: multiple value provided",
651
					ml->sml_type.bv_val );
652
				*text = textbuf;
653
				return LDAP_CONSTRAINT_VIOLATION;
654
655
656
657
658
659
660
661
			}
		}
	}

	return LDAP_SUCCESS;
}

int slap_mods_opattrs(
662
	Backend *be,
663
	Operation *op,
664
	Modifications *mods,
665
	Modifications **modtail,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
666
667
	const char **text,
	char *textbuf, size_t textlen )
668
{
669
	struct berval name, timestamp, csn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
670
671
672
#ifdef SLAP_NVALUES
	struct berval nname;
#endif
673
674
	char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
	char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
675
676
677
678
679
	Modifications *mod;

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

680
681
682
	assert( modtail != NULL );
	assert( *modtail == NULL );

683
684
685
	if( SLAP_LASTMOD(be) ) {
		struct tm *ltm;
		time_t now = slap_get_time();
686

687
688
		ldap_pvt_thread_mutex_lock( &gmtime_mutex );
		ltm = gmtime( &now );
Howard Chu's avatar
Howard Chu committed
689
		lutil_gentime( timebuf, sizeof(timebuf), ltm );
690

691
692
693
		csn.bv_len = lutil_csnstr( csnbuf, sizeof( csnbuf ), 0, 0 );
		ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
		csn.bv_val = csnbuf;
694

695
696
697
698
699
700
		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
701
702
703
#ifdef SLAP_NVALUES
			nname = name;
#endif
704
705
		} else {
			name = op->o_dn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
706
707
708
#ifdef SLAP_NVALUES
			nname = op->o_ndn;
#endif
709
		}
710
711
712
	}

	if( op->o_tag == LDAP_REQ_ADD ) {
713
		struct berval tmpval;
714

715
716
717
718
719
720
		if( global_schemacheck ) {
			int rc = mods_structural_class( mods, &tmpval,
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
				return rc;
			}
721
722
723
724
725

			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
726
727
			mod->sml_values =
				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
728
			ber_dupbv( &mod->sml_values[0], &tmpval );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
729
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
730
731
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
732
733
734
735
736
737
738
739
#ifdef SLAP_NVALUES
			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 );
#endif
740
741
			*modtail = mod;
			modtail = &mod->sml_next;
742
		}
743
744

		if( SLAP_LASTMOD(be) ) {
745
			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
746
747
748
749

			tmpval.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
			tmpval.bv_val = uuidbuf;
		
750
			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
751
			mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
752
			mod->sml_type.bv_val = NULL;
753
			mod->sml_desc = slap_schema.si_ad_entryUUID;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
754
755
			mod->sml_values =
				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
756
			ber_dupbv( &mod->sml_values[0], &tmpval );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
757
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
758
759
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
760
761
762
#ifdef SLAP_NVALUES
			mod->sml_nvalues = NULL;
#endif
763
764
			*modtail = mod;
			modtail = &mod->sml_next;
765
766
767
768
769

			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
770
771
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &name );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
772
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
773
774
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
775
776
777
778
779
780
781
782
#ifdef SLAP_NVALUES
			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 );
#endif
783
784
785
786
787
788
789
			*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
790
791
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &timestamp );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
792
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
793
794
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
795
796
797
#ifdef SLAP_NVALUES
			mod->sml_nvalues = NULL;
#endif
798
799
			*modtail = mod;
			modtail = &mod->sml_next;
800
		}
801
	}
802

803
	if( SLAP_LASTMOD(be) ) {
804
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
805
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
806
		mod->sml_type.bv_val = NULL;
807
		mod->sml_desc = slap_schema.si_ad_entryCSN;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
808
809
		mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_values[0], &csn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
810
		mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
811
812
		mod->sml_values[1].bv_val = NULL;
		assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
813
814
815
#ifdef SLAP_NVALUES
		mod->sml_nvalues = NULL;
#endif
816
817
818
		*modtail = mod;
		modtail = &mod->sml_next;

819
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
820
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
821
		mod->sml_type.bv_val = NULL;
822
		mod->sml_desc = slap_schema.si_ad_modifiersName;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
823
824
		mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_values[0], &name );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
825
		mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
826
827
		mod->sml_values[1].bv_val = NULL;
		assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
828
829
830
831
832
833
834
835
#ifdef SLAP_NVALUES
		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 );
#endif
836
837
838
		*modtail = mod;
		modtail = &mod->sml_next;

839
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
840
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
841
		mod->sml_type.bv_val = NULL;
842
		mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
843
844
		mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_values[0], &timestamp );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
845
		mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
846
847
		mod->sml_values[1].bv_val = NULL;
		assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
848
849
850
#ifdef SLAP_NVALUES
		mod->sml_nvalues = NULL;
#endif
851
852
853
854
		*modtail = mod;
		modtail = &mod->sml_next;
	}

855
	*modtail = NULL;
856
857
	return LDAP_SUCCESS;
}
858