modify.c 22.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

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

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

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

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

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

96

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

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

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

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

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

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

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

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

		case LDAP_MOD_DELETE:
		case LDAP_MOD_REPLACE:
			break;

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

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

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

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

177
178
179
		goto cleanup;
	}

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

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

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

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

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

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

225
	for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
226
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
227
228
229
230
		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 );
231

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

242
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
243
		Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
244
245
246
			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 ) {
249
250
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tno values", NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
251
		} else if ( tmp->sml_values[0].bv_val == NULL ) {
252
253
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tzero values", NULL, NULL );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
254
		} else if ( tmp->sml_values[1].bv_val == NULL ) {
255
			Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
Kurt Zeilenga's avatar
Kurt Zeilenga committed
256
			   "\t\tone value", (long) tmp->sml_values[0].bv_len, NULL );
257
258
259
260
261
		} else {
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tmultiple values", NULL, NULL );
		}
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
262
263
	}

Howard Chu's avatar
Howard Chu committed
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
	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
290
#endif	/* LDAP_DEBUG */
291

292
293
	manageDSAit = get_manageDSAit( op );

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

304
305
		rs->sr_err = LDAP_REFERRAL;
		send_ldap_result( op, rs );
306

307
		if (rs->sr_ref != default_referral) ber_bvarray_free( rs->sr_ref );
Howard Chu's avatar
Howard Chu committed
308
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
309
310
	}

311
	/* check restrictions */
312
313
	if( backend_check_restrictions( op, rs, NULL ) != LDAP_SUCCESS ) {
		send_ldap_result( op, rs );
314
		goto cleanup;
315
316
	}

317
	/* check for referrals */
318
	if( backend_check_referrals( op, rs ) != LDAP_SUCCESS ) {
319
320
321
		goto cleanup;
	}

322
#if defined( LDAP_SLAPI )
Luke Howard's avatar
Luke Howard committed
323
	slapi_x_pblock_set_operation( pb, op );
324
	slapi_pblock_set( pb, SLAPI_MODIFY_TARGET, (void *)dn.bv_val );
325
	slapi_pblock_set( pb, SLAPI_MANAGEDSAIT, (void *)manageDSAit );
326
	modv = slapi_x_modifications2ldapmods( &modlist );
327
	slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)modv );
328

Luke Howard's avatar
Luke Howard committed
329
	rs->sr_err = doPluginFNs( op->o_bd, SLAPI_PLUGIN_PRE_MODIFY_FN, pb );
330
	if ( rs->sr_err != 0 ) {
331
		/*
332
333
		 * A preoperation plugin failure will abort the
		 * entire operation.
334
335
		 */
#ifdef NEW_LOGGING
336
		LDAP_LOG( OPERATION, INFO, "do_modify: modify preoperation plugin "
337
				"failed\n", 0, 0, 0 );
338
#else
339
		Debug(LDAP_DEBUG_TRACE, "do_modify: modify preoperation plugin failed.\n",
340
				0, 0, 0);
341
#endif
342
343
		if ( slapi_pblock_get( pb, SLAPI_RESULT_CODE, (void *)&rs->sr_err ) != 0) {
			rs->sr_err = LDAP_OTHER;
344
		}
Luke Howard's avatar
Luke Howard committed
345
		slapi_x_free_ldapmods( modv );
346
		modv = NULL;
347
		goto cleanup;
348
	}
349
350
351
352
353
354

	/*
	 * It's possible that the preoperation plugin changed the
	 * modification array, so we need to convert it back to
	 * a Modification list.
	 *
355
	 * Calling slapi_x_modifications2ldapmods() destroyed modlist so
356
357
358
	 * we don't need to free it.
	 */
	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, (void **)&modv );
359
	modlist = slapi_x_ldapmods2modifications( modv );
360
361
362
363
364
365
366
367
368
369
370
371
372
373

	/*
	 * 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).
	 */
	if ( modlist == NULL ) {
		rs->sr_err = LDAP_SUCCESS;
		send_ldap_result( op, rs );
		goto cleanup;
	}
374
375
#endif /* defined( LDAP_SLAPI ) */

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

396
			rs->sr_err = slap_mods_check( modlist, update, &rs->sr_text,
397
				textbuf, textlen );
398

399
400
			if( rs->sr_err != LDAP_SUCCESS ) {
				send_ldap_result( op, rs );
401
402
403
				goto cleanup;
			}

404
			if ( !repl_user ) {
405
406
407
				for( modtail = &modlist;
					*modtail != NULL;
					modtail = &(*modtail)->sml_next )
408
409
410
				{
					/* empty */
				}
411

412
413
414
415
				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
416
					goto cleanup;
417
418
419
				}
			}

Howard Chu's avatar
Howard Chu committed
420
			op->orm_modlist = modlist;
421
			if ( (op->o_bd->be_modify)( op, rs ) == 0
422
#ifdef SLAPD_MULTIMASTER
Kurt Zeilenga's avatar
Kurt Zeilenga committed
423
				&& !repl_user
424
425
426
#endif
			) {
				/* but we log only the ones not from a replicator user */
427
				replog( op );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
428
429
			}

430
#ifndef SLAPD_MULTIMASTER
Kurt Zeilenga's avatar
Kurt Zeilenga committed
431
432
		/* send a referral */
		} else {
433
434
435
436
			BerVarray defref = op->o_bd->be_update_refs
				? op->o_bd->be_update_refs : default_referral;
			rs->sr_ref = referral_rewrite( defref,
				NULL, &op->o_req_dn, LDAP_SCOPE_DEFAULT );
437

438
439
440
441
			if (!rs->sr_ref) rs->sr_ref = defref;
			rs->sr_err = LDAP_REFERRAL;
			send_ldap_result( op, rs );
			if (rs->sr_ref != defref) ber_bvarray_free( rs->sr_ref );
442
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
443
444
		}
	} else {
445
446
		send_ldap_error( op, rs, LDAP_UNWILLING_TO_PERFORM,
		    "operation not supported within namingContext" );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
447
448
	}

449
#if defined( LDAP_SLAPI )
450
	if ( doPluginFNs( op->o_bd, SLAPI_PLUGIN_POST_MODIFY_FN, pb ) != 0 ) {
451
#ifdef NEW_LOGGING
452
		LDAP_LOG( OPERATION, INFO, "do_modify: modify postoperation plugins "
453
				"failed\n", 0, 0, 0 );
454
#else
455
		Debug(LDAP_DEBUG_TRACE, "do_modify: modify postoperation plugins "
456
				"failed.\n", 0, 0, 0);
457
458
459
460
#endif
	}
#endif /* defined( LDAP_SLAPI ) */

Howard Chu's avatar
Howard Chu committed
461
cleanup:
462
463
	free( op->o_req_dn.bv_val );
	free( op->o_req_ndn.bv_val );
464
	if ( modlist != NULL ) slap_mods_free( modlist );
465
#if defined( LDAP_SLAPI )
466
	if ( modv != NULL ) slapi_x_free_ldapmods( modv );
467
#endif
468
	return rs->sr_err;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
469
470
}

471
472
473
/*
 * Do basic attribute type checking and syntax validation.
 */
474
475
int slap_mods_check(
	Modifications *ml,
476
	int update,
477
	const char **text,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
478
479
	char *textbuf,
	size_t textlen )
480
481
482
{
	int rc;

483
	for( ; ml != NULL; ml = ml->sml_next ) {
484
		AttributeDescription *ad = NULL;
485
486

		/* convert to attribute description */
487
		rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
488
489

		if( rc != LDAP_SUCCESS ) {
490
			snprintf( textbuf, textlen, "%s: %s",
491
				ml->sml_type.bv_val, *text );
492
			*text = textbuf;
493
494
495
			return rc;
		}

496
		ad = ml->sml_desc;
497

498
499
		if( slap_syntax_is_binary( ad->ad_type->sat_syntax )
			&& !slap_ad_is_binary( ad ))
500
501
		{
			/* attribute requires binary transfer */
502
503
			snprintf( textbuf, textlen,
				"%s: requires ;binary transfer",
504
				ml->sml_type.bv_val );
505
			*text = textbuf;
506
507
508
			return LDAP_UNDEFINED_TYPE;
		}

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

520
		if( slap_ad_is_tag_range( ad )) {
521
522
			/* attribute requires binary transfer */
			snprintf( textbuf, textlen,
523
				"%s: inappropriate use of tag range option",
524
525
526
527
528
				ml->sml_type.bv_val );
			*text = textbuf;
			return LDAP_UNDEFINED_TYPE;
		}

529
530
		if (!update && is_at_no_user_mod( ad->ad_type )) {
			/* user modification disallowed */
531
532
			snprintf( textbuf, textlen,
				"%s: no user modification allowed",
533
				ml->sml_type.bv_val );
534
			*text = textbuf;
535
536
537
			return LDAP_CONSTRAINT_VIOLATION;
		}

538
		if ( is_at_obsolete( ad->ad_type ) &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
539
			( ml->sml_op == LDAP_MOD_ADD || ml->sml_values != NULL ) )
540
541
542
543
544
545
546
		{
			/*
			 * attribute is obsolete,
			 * only allow replace/delete with no values
			 */
			snprintf( textbuf, textlen,
				"%s: attribute is obsolete",
547
				ml->sml_type.bv_val );
548
549
550
551
			*text = textbuf;
			return LDAP_CONSTRAINT_VIOLATION;
		}

552
553
554
		/*
		 * check values
		 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
555
		if( ml->sml_values != NULL ) {
556
557
558
			ber_len_t nvals;
			slap_syntax_validate_func *validate =
				ad->ad_type->sat_syntax->ssyn_validate;
559
560
561
562
			slap_syntax_transform_func *pretty =
				ad->ad_type->sat_syntax->ssyn_pretty;
 
			if( !pretty && !validate ) {
563
				*text = "no validator for syntax";
564
565
				snprintf( textbuf, textlen,
					"%s: no validator for syntax %s",
566
					ml->sml_type.bv_val,
567
568
					ad->ad_type->sat_syntax->ssyn_oid );
				*text = textbuf;
569
570
571
				return LDAP_INVALID_SYNTAX;
			}

572
573
			/*
			 * check that each value is valid per syntax
574
			 *	and pretty if appropriate
575
			 */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
576
			for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
577
				struct berval pval;
578
579
				if( pretty ) {
					rc = pretty( ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
580
						&ml->sml_values[nvals], &pval );
581
582
				} else {
					rc = validate( ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
583
						&ml->sml_values[nvals] );
584
				}
585
586

				if( rc != 0 ) {
587
					snprintf( textbuf, textlen,
588
						"%s: value #%ld invalid per syntax",
589
						ml->sml_type.bv_val, (long) nvals );
590
					*text = textbuf;
591
592
					return LDAP_INVALID_SYNTAX;
				}
593
594

				if( pretty ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
595
596
					ber_memfree( ml->sml_values[nvals].bv_val );
					ml->sml_values[nvals] = pval;
597
				}
598
599
			}

600
601
602
603
604
605
606
607
608
609
610
611
612
613
			/*
			 * 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;
			}

614
			if( nvals && ad->ad_type->sat_equality &&
Kurt Zeilenga's avatar
Kurt Zeilenga committed
615
				ad->ad_type->sat_equality->smr_normalize )
616
617
			{
				ml->sml_nvalues = ch_malloc( (nvals+1)*sizeof(struct berval) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
618
				for( nvals = 0; ml->sml_values[nvals].bv_val; nvals++ ) {
Kurt Zeilenga's avatar
Kurt Zeilenga committed
619
620
					rc = ad->ad_type->sat_equality->smr_normalize(
						0,
621
						ad->ad_type->sat_syntax,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
622
						ad->ad_type->sat_equality,
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
						&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;
					}
				}
641
642
				ml->sml_nvalues[nvals].bv_val = NULL;
				ml->sml_nvalues[nvals].bv_len = 0;
643
			}
644
645
646
647
648
649
650
651
		}
	}

	return LDAP_SUCCESS;
}

int slap_mods_opattrs(
	Operation *op,
652
	Modifications *mods,
653
	Modifications **modtail,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
654
655
	const char **text,
	char *textbuf, size_t textlen )
656
{
657
	struct berval name, timestamp, csn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
658
	struct berval nname;
659
660
	char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
	char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
661
662
663
664
665
	Modifications *mod;

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

666
667
668
	assert( modtail != NULL );
	assert( *modtail == NULL );

669
	if( SLAP_LASTMOD(op->o_bd) ) {
670
671
		struct tm *ltm;
		time_t now = slap_get_time();
672

673
674
		ldap_pvt_thread_mutex_lock( &gmtime_mutex );
		ltm = gmtime( &now );
Howard Chu's avatar
Howard Chu committed
675
		lutil_gentime( timebuf, sizeof(timebuf), ltm );
676

677
678
679
		csn.bv_len = lutil_csnstr( csnbuf, sizeof( csnbuf ), 0, 0 );
		ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
		csn.bv_val = csnbuf;
680

681
682
683
684
685
686
		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
687
			nname = name;
688
689
		} else {
			name = op->o_dn;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
690
			nname = op->o_ndn;
691
		}
692
693
694
	}

	if( op->o_tag == LDAP_REQ_ADD ) {
695
		struct berval tmpval;
696

697
698
699
700
701
702
		if( global_schemacheck ) {
			int rc = mods_structural_class( mods, &tmpval,
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
				return rc;
			}
703
704
705
706
707

			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
708
709
			mod->sml_values =
				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
710
			ber_dupbv( &mod->sml_values[0], &tmpval );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
711
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
712
713
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
714
715
716
717
718
719
			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 );
720
721
			*modtail = mod;
			modtail = &mod->sml_next;
722
		}
723

724
		if( SLAP_LASTMOD(op->o_bd) ) {
725
			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
726
727
728
729

			tmpval.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
			tmpval.bv_val = uuidbuf;
		
730
			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
731
			mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
732
			mod->sml_type.bv_val = NULL;
733
			mod->sml_desc = slap_schema.si_ad_entryUUID;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
734
735
			mod->sml_values =
				(BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
736
			ber_dupbv( &mod->sml_values[0], &tmpval );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
737
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
738
739
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
740
			mod->sml_nvalues = NULL;
741
742
			*modtail = mod;
			modtail = &mod->sml_next;
743
744
745
746
747

			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
748
749
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &name );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
750
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
751
752
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
753
754
755
756
757
758
			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 );
759
760
761
762
763
764
765
			*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
766
767
			mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_values[0], &timestamp );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
768
			mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
769
770
			mod->sml_values[1].bv_val = NULL;
			assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
771
			mod->sml_nvalues = NULL;
772
773
			*modtail = mod;
			modtail = &mod->sml_next;
774
		}
775
	}
776

777
	if( SLAP_LASTMOD(op->o_bd) ) {
778
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
779
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
780
		mod->sml_type.bv_val = NULL;
781
		mod->sml_desc = slap_schema.si_ad_entryCSN;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
782
783
		mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_values[0], &csn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
784
		mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
785
786
		mod->sml_values[1].bv_val = NULL;
		assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
787
		mod->sml_nvalues = NULL;
788
789
790
		*modtail = mod;
		modtail = &mod->sml_next;

791
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
792
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
793
		mod->sml_type.bv_val = NULL;
794
		mod->sml_desc = slap_schema.si_ad_modifiersName;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
795
796
		mod->sml_values = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
		ber_dupbv( &mod->sml_values[0], &name );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
797
		mod->sml_values[1].bv_len = 0;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
798
799
		mod->sml_values[1].bv_val = NULL;
		assert( mod->sml_values[0].bv_val );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
800
801
802
803
804
805
		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 );
806
807
808
		*modtail = mod;
		modtail = &mod->sml_next;

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

823
	*modtail = NULL;
824
825
	return LDAP_SUCCESS;
}
826