modify.c 20.2 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
#include "portable.h"
19
#include "slapi_common.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
20

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

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

27
28
#include "lutil.h"

29
#include "ldap_pvt.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
30
#include "slap.h"
31
#include "slapi.h"
Kurt Zeilenga's avatar
Kurt Zeilenga committed
32

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

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

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

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

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

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

101

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

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
111

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

Howard Chu's avatar
Howard Chu committed
122
		mod = (Modifications *) ch_malloc( sizeof(Modifications) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
123
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
124
		mod->sml_type = tmp.sml_type;
125
126
		mod->sml_bvalues = tmp.sml_bvalues;
		mod->sml_desc = NULL;
127
		mod->sml_next = NULL;
128
129
		*modtail = mod;

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

143
144
145
146
147
148
				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
149

150
151
152
153
154
155
156
			/* fall through */

		case LDAP_MOD_DELETE:
		case LDAP_MOD_REPLACE:
			break;

		default: {
157
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
158
159
				LDAP_LOG( OPERATION, ERR, 
					"do_modify: invalid modify operation (%ld)\n", (long)mop, 0, 0 );
160
#else
161
162
163
				Debug( LDAP_DEBUG_ANY,
					"do_modify: invalid modify operation (%ld)\n",
					(long) mop, 0, 0 );
164
165
#endif

166
167
168
169
170
				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
171
		}
172

173
		modtail = &mod->sml_next;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
174
175
176
	}
	*modtail = NULL;

177
	if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
178
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
179
		LDAP_LOG( OPERATION, ERR, "do_modify: get_ctrls failed\n", 0, 0, 0 );
180
#else
181
		Debug( LDAP_DEBUG_ANY, "do_modify: get_ctrls failed\n", 0, 0, 0 );
182
183
#endif

184
185
186
		goto cleanup;
	}

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

Howard Chu's avatar
Howard Chu committed
201
	if( ndn.bv_len == 0 ) {
202
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
203
204
		LDAP_LOG( OPERATION, ERR, 
			"do_modify: attempt to modify root DSE.\n",0, 0, 0 );
205
#else
206
		Debug( LDAP_DEBUG_ANY, "do_modify: root dse!\n", 0, 0, 0 );
207
208
#endif

209
210
211
		send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
			NULL, "modify upon the root DSE not supported", NULL, NULL );
		goto cleanup;
212

213
	} else if ( bvmatch( &ndn, &global_schemandn ) ) {
214
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
215
216
		LDAP_LOG( OPERATION, ERR,
			"do_modify: attempt to modify subschema subentry.\n" , 0, 0, 0  );
217
218
219
220
221
222
223
224
#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;
225
226
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
227
#ifdef LDAP_DEBUG
228
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
229
	LDAP_LOG( OPERATION, DETAIL1, "do_modify: modifications:\n", 0, 0, 0  );
230
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
231
	Debug( LDAP_DEBUG_ARGS, "modifications:\n", 0, 0, 0 );
232
233
#endif

234
	for ( tmp = modlist; tmp != NULL; tmp = tmp->sml_next ) {
235
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
236
237
238
239
		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 );
240

241
		if ( tmp->sml_bvalues == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
242
			LDAP_LOG( OPERATION, DETAIL1, "\t\tno values", 0, 0, 0 );
243
		} else if ( tmp->sml_bvalues[0].bv_val == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
244
			LDAP_LOG( OPERATION, DETAIL1, "\t\tzero values", 0, 0, 0 );
245
		} else if ( tmp->sml_bvalues[1].bv_val == NULL ) {
Julius Enarusai's avatar
   
Julius Enarusai committed
246
			LDAP_LOG( OPERATION, DETAIL1, "\t\tone value", 0, 0, 0 );
247
		} else {
Julius Enarusai's avatar
   
Julius Enarusai committed
248
			LDAP_LOG( OPERATION, DETAIL1, "\t\tmultiple values", 0, 0, 0 );
249
250
		}

251
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
252
		Debug( LDAP_DEBUG_ARGS, "\t%s: %s\n",
253
254
255
			tmp->sml_op == LDAP_MOD_ADD
				? "add" : (tmp->sml_op == LDAP_MOD_DELETE
					? "delete" : "replace"), tmp->sml_type.bv_val, 0 );
256

257
		if ( tmp->sml_bvalues == NULL ) {
258
259
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tno values", NULL, NULL );
260
		} else if ( tmp->sml_bvalues[0].bv_val == NULL ) {
261
262
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tzero values", NULL, NULL );
263
		} else if ( tmp->sml_bvalues[1].bv_val == NULL ) {
264
			Debug( LDAP_DEBUG_ARGS, "%s, length %ld\n",
265
			   "\t\tone value", (long) tmp->sml_bvalues[0].bv_len, NULL );
266
267
268
269
270
		} else {
			Debug( LDAP_DEBUG_ARGS, "%s\n",
			   "\t\tmultiple values", NULL, NULL );
		}
#endif
Kurt Zeilenga's avatar
Kurt Zeilenga committed
271
272
273
	}
#endif

Howard Chu's avatar
Howard Chu committed
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
	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 );
		}
	}
300

301
302
	manageDSAit = get_manageDSAit( op );

Kurt Zeilenga's avatar
Kurt Zeilenga committed
303
304
305
306
307
	/*
	 * 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
308
	if ( (be = select_backend( &ndn, manageDSAit, 0 )) == NULL ) {
309
		BerVarray ref = referral_rewrite( default_referral,
Howard Chu's avatar
Howard Chu committed
310
			NULL, &pdn, LDAP_SCOPE_DEFAULT );
311

312
		send_ldap_result( conn, op, rc = LDAP_REFERRAL,
313
314
			NULL, NULL, ref ? ref : default_referral, NULL );

315
		ber_bvarray_free( ref );
Howard Chu's avatar
Howard Chu committed
316
		goto cleanup;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
317
318
	}

319
320
	/* check restrictions */
	rc = backend_check_restrictions( be, conn, op, NULL, &text ) ;
321
322
	if( rc != LDAP_SUCCESS ) {
		send_ldap_result( conn, op, rc,
323
			NULL, text, NULL, NULL );
324
		goto cleanup;
325
326
	}

327
	/* check for referrals */
Howard Chu's avatar
Howard Chu committed
328
	rc = backend_check_referrals( be, conn, op, &pdn, &ndn );
329
330
331
332
	if ( rc != LDAP_SUCCESS ) {
		goto cleanup;
	}

Kurt Zeilenga's avatar
Kurt Zeilenga committed
333
	/* deref suffix alias if appropriate */
Howard Chu's avatar
Howard Chu committed
334
	suffix_alias( be, &ndn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
335

336
#if defined( LDAP_SLAPI )
337
338
339
	slapi_x_backend_set_pb( pb, be );
	slapi_x_connection_set_pb( pb, conn );
	slapi_x_operation_set_pb( pb, op );
340
	slapi_pblock_set( pb, SLAPI_MODIFY_TARGET, (void *)dn.bv_val );
341
	slapi_pblock_set( pb, SLAPI_MANAGEDSAIT, (void *)(1) );
342
	modv = slapi_x_modifications2ldapmods( &modlist );
343
	slapi_pblock_set( pb, SLAPI_MODIFY_MODS, (void *)modv );
344
345

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

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

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

399
			rc = slap_mods_check( modlist, update, &text,
400
				textbuf, textlen );
401
402
403
404
405
406
407

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

408
			if ( !repl_user ) {
409
410
411
				for( modtail = &modlist;
					*modtail != NULL;
					modtail = &(*modtail)->sml_next )
412
413
414
				{
					/* empty */
				}
415

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

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

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

443
			send_ldap_result( conn, op, rc = LDAP_REFERRAL, NULL, NULL,
444
445
				ref ? ref : defref, NULL );

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

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

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

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

489
	for( ; ml != NULL; ml = ml->sml_next ) {
490
		AttributeDescription *ad = NULL;
491
492

		/* convert to attribute description */
493
		rc = slap_bv2ad( &ml->sml_type, &ml->sml_desc, text );
494
495

		if( rc != LDAP_SUCCESS ) {
496
			snprintf( textbuf, textlen, "%s: %s",
497
				ml->sml_type.bv_val, *text );
498
			*text = textbuf;
499
500
501
			return rc;
		}

502
		ad = ml->sml_desc;
503

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

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

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

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

544
		if ( is_at_obsolete( ad->ad_type ) &&
545
			( ml->sml_op == LDAP_MOD_ADD || ml->sml_bvalues != NULL ) )
546
547
548
549
550
551
552
		{
			/*
			 * attribute is obsolete,
			 * only allow replace/delete with no values
			 */
			snprintf( textbuf, textlen,
				"%s: attribute is obsolete",
553
				ml->sml_type.bv_val );
554
555
556
557
			*text = textbuf;
			return LDAP_CONSTRAINT_VIOLATION;
		}

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

578
579
			/*
			 * check that each value is valid per syntax
580
			 *	and pretty if appropriate
581
			 */
582
			for( nvals = 0; ml->sml_bvalues[nvals].bv_val; nvals++ ) {
583
				struct berval pval;
584
585
				if( pretty ) {
					rc = pretty( ad->ad_type->sat_syntax,
586
						&ml->sml_bvalues[nvals], &pval );
587
588
				} else {
					rc = validate( ad->ad_type->sat_syntax,
589
						&ml->sml_bvalues[nvals] );
590
				}
591
592

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

				if( pretty ) {
601
602
					ber_memfree( ml->sml_bvalues[nvals].bv_val );
					ml->sml_bvalues[nvals] = pval;
603
				}
604
605
606
607
608
609
			}

			/*
			 * a rough single value check... an additional check is needed
			 * to catch add of single value to existing single valued attribute
			 */
610
			if( ( ml->sml_op == LDAP_MOD_ADD || ml->sml_op == LDAP_MOD_REPLACE )
611
612
				&& nvals > 1 && is_at_single_value( ad->ad_type ))
			{
613
614
				snprintf( textbuf, textlen,
					"%s: multiple value provided",
615
					ml->sml_type.bv_val );
616
				*text = textbuf;
617
				return LDAP_CONSTRAINT_VIOLATION;
618
619
620
621
622
623
624
625
			}
		}
	}

	return LDAP_SUCCESS;
}

int slap_mods_opattrs(
626
	Backend *be,
627
	Operation *op,
628
	Modifications *mods,
629
	Modifications **modtail,
Kurt Zeilenga's avatar
Kurt Zeilenga committed
630
631
	const char **text,
	char *textbuf, size_t textlen )
632
{
633
	struct berval name, timestamp, csn;
634
635
	char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
	char csnbuf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
636
637
638
639
640
	Modifications *mod;

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

641
642
643
	assert( modtail != NULL );
	assert( *modtail == NULL );

644
645
646
	if( SLAP_LASTMOD(be) ) {
		struct tm *ltm;
		time_t now = slap_get_time();
647

648
649
		ldap_pvt_thread_mutex_lock( &gmtime_mutex );
		ltm = gmtime( &now );
Howard Chu's avatar
Howard Chu committed
650
		lutil_gentime( timebuf, sizeof(timebuf), ltm );
651

652
653
654
		csn.bv_len = lutil_csnstr( csnbuf, sizeof( csnbuf ), 0, 0 );
		ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
		csn.bv_val = csnbuf;
655

656
657
658
659
660
661
662
663
664
		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;
		} else {
			name = op->o_dn;
		}
665
666
667
	}

	if( op->o_tag == LDAP_REQ_ADD ) {
668
		struct berval tmpval;
669

670
671
672
673
674
675
		if( global_schemacheck ) {
			int rc = mods_structural_class( mods, &tmpval,
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
				return rc;
			}
676
677
678
679
680
681
682
683
684
685
686

			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
			mod->sml_op = mop;
			mod->sml_type.bv_val = NULL;
			mod->sml_desc = slap_schema.si_ad_structuralObjectClass;
			mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_bvalues[0], &tmpval );
			mod->sml_bvalues[1].bv_val = NULL;
			assert( mod->sml_bvalues[0].bv_val );
			*modtail = mod;
			modtail = &mod->sml_next;
687
		}
688
689

		if( SLAP_LASTMOD(be) ) {
690
			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
691
692
693
694

			tmpval.bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
			tmpval.bv_val = uuidbuf;
		
695
			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
696
			mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
697
			mod->sml_type.bv_val = NULL;
698
			mod->sml_desc = slap_schema.si_ad_entryUUID;
699
			mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
700
701
702
			ber_dupbv( &mod->sml_bvalues[0], &tmpval );
			mod->sml_bvalues[1].bv_val = NULL;
			assert( mod->sml_bvalues[0].bv_val );
703
704
			*modtail = mod;
			modtail = &mod->sml_next;
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726

			mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
			mod->sml_op = mop;
			mod->sml_type.bv_val = NULL;
			mod->sml_desc = slap_schema.si_ad_creatorsName;
			mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_bvalues[0], &name );
			mod->sml_bvalues[1].bv_val = NULL;
			assert( mod->sml_bvalues[0].bv_val );
			*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;
			mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
			ber_dupbv( &mod->sml_bvalues[0], &timestamp );
			mod->sml_bvalues[1].bv_val = NULL;
			assert( mod->sml_bvalues[0].bv_val );
			*modtail = mod;
			modtail = &mod->sml_next;
727
		}
728
	}
729

730
	if( SLAP_LASTMOD(be) ) {
731
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
732
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
733
		mod->sml_type.bv_val = NULL;
734
		mod->sml_desc = slap_schema.si_ad_entryCSN;
735
		mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
736
		ber_dupbv( &mod->sml_bvalues[0], &csn );
737
738
		mod->sml_bvalues[1].bv_val = NULL;
		assert( mod->sml_bvalues[0].bv_val );
739
740
741
		*modtail = mod;
		modtail = &mod->sml_next;

742
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
743
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
744
		mod->sml_type.bv_val = NULL;
745
		mod->sml_desc = slap_schema.si_ad_modifiersName;
746
		mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
747
748
749
		ber_dupbv( &mod->sml_bvalues[0], &name );
		mod->sml_bvalues[1].bv_val = NULL;
		assert( mod->sml_bvalues[0].bv_val );
750
751
752
		*modtail = mod;
		modtail = &mod->sml_next;

753
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
754
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
755
		mod->sml_type.bv_val = NULL;
756
		mod->sml_desc = slap_schema.si_ad_modifyTimestamp;
757
		mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
758
759
760
		ber_dupbv( &mod->sml_bvalues[0], &timestamp );
		mod->sml_bvalues[1].bv_val = NULL;
		assert( mod->sml_bvalues[0].bv_val );
761
762
763
764
		*modtail = mod;
		modtail = &mod->sml_next;
	}

765
	*modtail = NULL;
766
767
	return LDAP_SUCCESS;
}
768