modify.c 24.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
#ifdef LDAP_SLAPI
34
35
36
static LDAPMod **Modifications2LDAPMods (Modifications **modlist);
static Modifications *LDAPMods2Modifications (LDAPMod **mods);
static void FreeLDAPMods (LDAPMod **mods);
37
38
#endif /* LDAP_SLAPI */

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

64
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
65
	LDAP_LOG( OPERATION, ENTRY, "do_modify: enter\n", 0, 0, 0 );
66
#else
Kurt Zeilenga's avatar
Kurt Zeilenga committed
67
	Debug( LDAP_DEBUG_TRACE, "do_modify\n", 0, 0, 0 );
68
69
#endif

Kurt Zeilenga's avatar
Kurt Zeilenga committed
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
	/*
	 * 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
	 *			}
	 *		}
	 *	}
	 */

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

96
97
		send_ldap_disconnect( conn, op,
			LDAP_PROTOCOL_ERROR, "decoding error" );
98
		return SLAPD_DISCONNECT;
Kurt Zeilenga's avatar
Kurt Zeilenga committed
99
100
	}

101
#ifdef NEW_LOGGING
Julius Enarusai's avatar
   
Julius Enarusai committed
102
	LDAP_LOG( OPERATION, ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
103
#else
104
	Debug( LDAP_DEBUG_ARGS, "do_modify: dn (%s)\n", dn.bv_val, 0, 0 );
105
106
#endif

107

Kurt Zeilenga's avatar
Kurt Zeilenga committed
108
	/* collect modifications & save for later */
Kurt Zeilenga's avatar
Kurt Zeilenga committed
109

Kurt Zeilenga's avatar
Kurt Zeilenga committed
110
111
112
113
	for ( tag = ber_first_element( op->o_ber, &len, &last );
	    tag != LBER_DEFAULT;
	    tag = ber_next_element( op->o_ber, &len, last ) )
	{
114
		ber_int_t mop;
115
		Modifications tmp, *mod;
116

Kurt Zeilenga's avatar
Kurt Zeilenga committed
117

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

Howard Chu's avatar
Howard Chu committed
128
		mod = (Modifications *) ch_malloc( sizeof(Modifications) );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
129
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
130
		mod->sml_type = tmp.sml_type;
131
132
		mod->sml_bvalues = tmp.sml_bvalues;
		mod->sml_desc = NULL;
133
		mod->sml_next = NULL;
134
135
		*modtail = mod;

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

149
150
151
152
153
154
				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
155

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

		case LDAP_MOD_DELETE:
		case LDAP_MOD_REPLACE:
			break;

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

172
173
174
175
176
				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
177
		}
178

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

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

190
191
192
		goto cleanup;
	}

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

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

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

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

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

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

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

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

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

Howard Chu's avatar
Howard Chu committed
280
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
	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 );
		}
	}
306

307
308
	manageDSAit = get_manageDSAit( op );

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

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

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

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

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

Kurt Zeilenga's avatar
Kurt Zeilenga committed
339
	/* deref suffix alias if appropriate */
Howard Chu's avatar
Howard Chu committed
340
	suffix_alias( be, &ndn );
Kurt Zeilenga's avatar
Kurt Zeilenga committed
341

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

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

	/*
	 * It's possible that the preoperation plugin changed the
	 * modification array, so we need to convert it back to
	 * a Modification list.
	 *
	 * Calling Modifications2LDAPMods() destroyed modlist so
	 * we don't need to free it.
	 */
	slapi_pblock_get( pb, SLAPI_MODIFY_MODS, (void **)&modv );
	modlist = LDAPMods2Modifications( modv );
382
383
#endif /* defined( LDAP_SLAPI ) */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

508
		ad = ml->sml_desc;
509

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

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

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

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

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

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

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

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

				if( pretty ) {
607
608
					ber_memfree( ml->sml_bvalues[nvals].bv_val );
					ml->sml_bvalues[nvals] = pval;
609
				}
610
611
612
613
614
615
			}

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

	return LDAP_SUCCESS;
}

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

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

647
648
649
	assert( modtail != NULL );
	assert( *modtail == NULL );

650
651
652
	if( SLAP_LASTMOD(be) ) {
		struct tm *ltm;
		time_t now = slap_get_time();
653

654
655
		ldap_pvt_thread_mutex_lock( &gmtime_mutex );
		ltm = gmtime( &now );
Howard Chu's avatar
Howard Chu committed
656
		lutil_gentime( timebuf, sizeof(timebuf), ltm );
657

658
659
660
		csn.bv_len = lutil_csnstr( csnbuf, sizeof( csnbuf ), 0, 0 );
		ldap_pvt_thread_mutex_unlock( &gmtime_mutex );
		csn.bv_val = csnbuf;
661

662
663
664
665
666
667
668
669
670
		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;
		}
671
672
673
	}

	if( op->o_tag == LDAP_REQ_ADD ) {
674
		struct berval tmpval;
675

676
677
678
679
680
681
		if( global_schemacheck ) {
			int rc = mods_structural_class( mods, &tmpval,
				text, textbuf, textlen );
			if( rc != LDAP_SUCCESS ) {
				return rc;
			}
682
683
684
685
686
687
688
689
690
691
692

			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;
693
		}
694
695

		if( SLAP_LASTMOD(be) ) {
696
			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
697
698
699
700

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

			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;
733
		}
734
	}
735

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

748
		mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
749
		mod->sml_op = mop;
Howard Chu's avatar
Howard Chu committed
750
		mod->sml_type.bv_val = NULL;
751
		mod->sml_desc = slap_schema.si_ad_modifiersName;
752
		mod->sml_bvalues = (BerVarray) ch_malloc( 2 * sizeof( struct berval ) );
753
754
755
		ber_dupbv( &mod->sml_bvalues[0], &name );
		mod->sml_bvalues[1].bv_val = NULL;
		assert( mod->sml_bvalues[0].bv_val );
756
757
758
		*modtail = mod;
		modtail = &mod->sml_next;

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

771
	*modtail = NULL;
772
773
	return LDAP_SUCCESS;
}
774
775
776
777

#ifdef LDAP_SLAPI
/*
 * Synthesise an LDAPMod array from a Modifications list to pass
778
779
780
781
782
 * to SLAPI. This synthesis is destructive and as such the 
 * Modifications list may not be used after calling this 
 * function.
 * 
 * This function must also be called before slap_mods_check().
783
 */
784
static LDAPMod **Modifications2LDAPMods(Modifications **pmodlist)
785
{
786
787
	Modifications *ml, *modlist;
	LDAPMod **mods, *modp;
788
789
	int i, j;

790
791
	modlist = *pmodlist;

792
	for( i = 0, ml = modlist; ml != NULL; i++, ml = ml->sml_next )
793
794
		;

795
	mods = (LDAPMod **)ch_malloc( (i + 1) * sizeof(LDAPMod *) );
796

797
	for( i = 0, ml = modlist; ml != NULL; ml = ml->sml_next ) {
798
799
800
801
802
803
804
805
		modp = mods[i];
		modp->mod_op = ml->sml_op | LDAP_MOD_BVALUES;

		/* Take ownership of original type. */
		modp->mod_type = ml->sml_type.bv_val;
		ml->sml_type.bv_val = NULL;

		if ( ml->sml_bvalues != NULL ) {
806
			for( j = 0; ml->sml_bvalues[j].bv_val != NULL; j++ )
807
				;
808
			modp->mod_bvalues = (struct berval **)ch_malloc( (j + 1) *
809
				sizeof(struct berval *) );
810
811
			for( j = 0; ml->sml_bvalues[j].bv_val != NULL; j++ ) {
				/* Take ownership of original values. */
812
				modp->mod_bvalues[j] = (struct berval *)ch_malloc( sizeof(struct berval) );
813
814
815
816
817
818
				modp->mod_bvalues[j]->bv_len = ml->sml_bvalues[j].bv_len;
				modp->mod_bvalues[j]->bv_val = ml->sml_bvalues[j].bv_val;
				ml->sml_bvalues[j].bv_len = 0;
				ml->sml_bvalues[j].bv_val = NULL;
			}
			modp->mod_bvalues[j] = NULL;
819
		} else {
820
			modp->mod_bvalues = NULL;
821
822
823
824
		}
		i++;
	}

825
826
827
828
829
830
	mods[i] = NULL;

	slap_mods_free( modlist );
	*pmodlist = NULL;

	return mods;
831
832
833
}

/*
834
835
836
837
838
839
 * Convert a potentially modified array of LDAPMods back to a
 * Modification list. 
 * 
 * The returned Modification list contains pointers into the
 * LDAPMods array; the latter MUST be freed with FreeLDAPMods()
 * (see below).
840
 */
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
static Modifications *LDAPMods2Modifications (LDAPMod **mods)
{
	Modifications *modlist, **modtail;
	LDAPMod **modp;

	modtail = &modlist;

	for( modp = mods; *modp != NULL; modp++ ) {
		Modifications *mod;
		int i;
		char **p;
		struct berval **bvp;

		mod = (Modifications *) ch_malloc( sizeof(Modifications) );
		mod->sml_op = (*modp)->mod_op & (~LDAP_MOD_BVALUES);
		mod->sml_type.bv_val = (*modp)->mod_type;
		mod->sml_type.bv_len = strlen( mod->sml_type.bv_val );
		mod->sml_desc = NULL;
		mod->sml_next = NULL;

		if ( (*modp)->mod_op & LDAP_MOD_BVALUES ) {
			for( i = 0, bvp = (*modp)->mod_bvalues; *bvp != NULL; bvp++, i++ )
				;
		} else {
			for( i = 0, p = (*modp)->mod_values; *p != NULL; p++, i++ )
				;
		}

		mod->sml_bvalues = (BerVarray) ch_malloc( (i + 1) * sizeof(struct berval) );

		/* NB: This implicitly trusts a plugin to return valid modifications. */
		if ( (*modp)->mod_op & LDAP_MOD_BVALUES ) {
			for( i = 0, bvp = (*modp)->mod_bvalues; *bvp != NULL; bvp++, i++ ) {
				mod->sml_bvalues[i].bv_val = (*bvp)->bv_val;
				mod->sml_bvalues[i].bv_len = (*bvp)->bv_len;
			}
		} else {
			for( i = 0, p = (*modp)->mod_values; *p != NULL; p++, i++ ) {
				mod->sml_bvalues[i].bv_val = *p;
				mod->sml_bvalues[i].bv_len = strlen( *p );
			}
		}
		mod->sml_bvalues[i].bv_val = NULL;

		*modtail = mod;
		modtail = &mod->sml_next;
	}
	
	return modlist;
}

/*
 * This function only frees the parts of the mods array that
 * are not shared with the Modification list that was created
 * by LDAPMods2Modifications. 
 *
 */
static void FreeLDAPMods (LDAPMod **mods)
899
{
900
	int i, j;
901

902
903
	if (mods == NULL)
		return;