xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/slapmodify.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: slapmodify.c,v 1.2 2021/08/14 16:14:58 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2021 The OpenLDAP Foundation.
7  * Portions Copyright 1998-2003 Kurt D. Zeilenga.
8  * Portions Copyright 2003 IBM Corporation.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Pierangelo Masarati for inclusion
21  * in OpenLDAP Software.
22  */
23 
24 #include <sys/cdefs.h>
25 __RCSID("$NetBSD: slapmodify.c,v 1.2 2021/08/14 16:14:58 christos Exp $");
26 
27 #include "portable.h"
28 
29 #include <stdio.h>
30 
31 #include "ac/stdlib.h"
32 
33 #include "ac/ctype.h"
34 #include "ac/string.h"
35 #include "ac/socket.h"
36 #include "ac/unistd.h"
37 
38 #include "lber.h"
39 #include "ldif.h"
40 #include "lutil.h"
41 #include "lutil_meter.h"
42 #include <sys/stat.h>
43 
44 #include "slapcommon.h"
45 
46 extern int slap_DN_strict;	/* dn.c */
47 
48 static char csnbuf[ LDAP_PVT_CSNSTR_BUFSIZE ];
49 
50 int
slapmodify(int argc,char ** argv)51 slapmodify( int argc, char **argv )
52 {
53 	char *buf = NULL;
54 	const char *text;
55 	char textbuf[SLAP_TEXT_BUFLEN] = { '\0' };
56 	size_t textlen = sizeof textbuf;
57 	const char *progname = "slapmodify";
58 
59 	struct berval csn;
60 	unsigned long sid;
61 	struct berval bvtext;
62 	ID id;
63 	OperationBuffer opbuf;
64 	Operation *op;
65 
66 	int checkvals, ldifrc;
67 	unsigned long lineno, nextline;
68 	int lmax;
69 	int rc = EXIT_SUCCESS;
70 
71 	int enable_meter = 0;
72 	lutil_meter_t meter;
73 	struct stat stat_buf;
74 
75 	/* default "000" */
76 	csnsid = 0;
77 
78 	if ( isatty (2) ) enable_meter = 1;
79 	slap_tool_init( progname, SLAPMODIFY, argc, argv );
80 
81 	memset( &opbuf, 0, sizeof(opbuf) );
82 	op = &opbuf.ob_op;
83 	op->o_hdr = &opbuf.ob_hdr;
84 	op->o_bd = be;
85 
86 	if ( !be->be_entry_open ||
87 		!be->be_entry_close ||
88 		!be->be_entry_put ||
89 		!be->be_dn2id_get ||
90 		!be->be_entry_get ||
91 		!be->be_entry_modify )
92 	{
93 		fprintf( stderr, "%s: database doesn't support necessary operations.\n",
94 			progname );
95 		if ( dryrun ) {
96 			fprintf( stderr, "\t(dry) continuing...\n" );
97 
98 		} else {
99 			exit( EXIT_FAILURE );
100 		}
101 	}
102 
103 	checkvals = (slapMode & SLAP_TOOL_QUICK) ? 0 : 1;
104 
105 	lmax = 0;
106 	nextline = 0;
107 
108 	/* enforce schema checking unless not disabled and allow unknown
109 	 * attributes otherwise */
110 	if ( (slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) == 0) {
111 		SLAP_DBFLAGS(be) &= ~(SLAP_DBFLAG_NO_SCHEMA_CHECK);
112 	} else {
113 		slap_DN_strict = 0;
114 	}
115 
116 	if( !dryrun && be->be_entry_open( be, 1 ) != 0 ) {
117 		fprintf( stderr, "%s: could not open database.\n",
118 			progname );
119 		exit( EXIT_FAILURE );
120 	}
121 
122 	(void)slap_tool_update_ctxcsn_init();
123 
124 	if ( enable_meter
125 #ifdef LDAP_DEBUG
126 		/* tools default to "none" */
127 		&& slap_debug == LDAP_DEBUG_NONE
128 #endif
129 		&& !fstat ( fileno ( ldiffp->fp ), &stat_buf )
130 		&& S_ISREG(stat_buf.st_mode) ) {
131 		enable_meter = !lutil_meter_open(
132 			&meter,
133 			&lutil_meter_text_display,
134 			&lutil_meter_linear_estimator,
135 			stat_buf.st_size);
136 	} else {
137 		enable_meter = 0;
138 	}
139 
140 	/* nextline is the line number of the end of the current entry */
141 	for( lineno=1; ( ldifrc = ldif_read_record( ldiffp, &nextline, &buf, &lmax )) > 0;
142 		lineno=nextline+1 )
143 	{
144 		BackendDB *bd;
145 		Entry *e_orig = NULL, *e = NULL;
146 		struct berval rbuf;
147 		LDIFRecord lr;
148 		struct berval ndn = BER_BVNULL;
149 		int n;
150 		int is_oc = 0;
151 		int local_rc;
152 		int mod_err = 0;
153 		char *request = "(unknown)";
154 
155 		ber_str2bv( buf, 0, 0, &rbuf );
156 
157 		if ( lineno < jumpline )
158 			continue;
159 
160 		if ( enable_meter )
161 			lutil_meter_update( &meter,
162 					 ftell( ldiffp->fp ),
163 					 0);
164 
165 		/*
166 		 * Initialize text buffer
167 		 */
168 		bvtext.bv_len = textlen;
169 		bvtext.bv_val = textbuf;
170 		bvtext.bv_val[0] = '\0';
171 
172 		local_rc = ldap_parse_ldif_record( &rbuf, lineno, &lr,
173 			"slapmodify", LDIF_NO_CONTROLS );
174 
175 		if ( local_rc != LDAP_SUCCESS ) {
176 			fprintf( stderr, "%s: could not parse entry (line=%lu)\n",
177 				progname, lineno );
178 			rc = EXIT_FAILURE;
179 			if( continuemode ) continue;
180 			break;
181 		}
182 
183 		switch ( lr.lr_op ) {
184 		case LDAP_REQ_ADD:
185 			request = "add";
186 			break;
187 
188 		case LDAP_REQ_MODIFY:
189 			request = "modify";
190 			break;
191 
192 		case LDAP_REQ_DELETE:
193 			if ( be->be_entry_delete )
194 			{
195 				request = "delete";
196 				break;
197 			}
198 			/* backend does not support delete, fallthru */
199 
200 		case LDAP_REQ_MODRDN:
201 			fprintf( stderr, "%s: request 0x%lx not supported (line=%lu)\n",
202 				progname, (unsigned long)lr.lr_op, lineno );
203 			rc = EXIT_FAILURE;
204 			goto cleanup;
205 
206 		default:
207 			/* record skipped e.g. version: or comment or something we don't handle yet */
208 			goto cleanup;
209 		}
210 
211 		local_rc = dnNormalize( 0, NULL, NULL, &lr.lr_dn, &ndn, NULL );
212 		if ( local_rc != LDAP_SUCCESS ) {
213 			fprintf( stderr, "%s: DN=\"%s\" normalization failed (line=%lu)\n",
214 				progname, lr.lr_dn.bv_val, lineno );
215 			rc = EXIT_FAILURE;
216 			goto cleanup;
217 		}
218 
219 		/* make sure the DN is not empty */
220 		if( BER_BVISEMPTY( &ndn ) &&
221 			!BER_BVISEMPTY( be->be_nsuffix ))
222 		{
223 			fprintf( stderr, "%s: line %lu: "
224 				"%s entry with empty dn=\"\"",
225 				progname, lineno, request );
226 			bd = select_backend( &ndn, nosubordinates );
227 			if ( bd ) {
228 				BackendDB *bdtmp;
229 				int dbidx = 0;
230 				LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
231 					if ( bdtmp == bd ) break;
232 					dbidx++;
233 				}
234 
235 				assert( bdtmp != NULL );
236 
237 				fprintf( stderr, "; did you mean to use database #%d (%s)?",
238 					dbidx,
239 					bd->be_suffix[0].bv_val );
240 
241 			}
242 			fprintf( stderr, "\n" );
243 			rc = EXIT_FAILURE;
244 			goto cleanup;
245 		}
246 
247 		/* check backend */
248 		bd = select_backend( &ndn, nosubordinates );
249 		if ( bd != be ) {
250 			fprintf( stderr, "%s: line %lu: "
251 				"database #%d (%s) not configured to hold \"%s\"",
252 				progname, lineno,
253 				dbnum,
254 				be->be_suffix[0].bv_val,
255 				lr.lr_dn.bv_val );
256 			if ( bd ) {
257 				BackendDB *bdtmp;
258 				int dbidx = 0;
259 				LDAP_STAILQ_FOREACH( bdtmp, &backendDB, be_next ) {
260 					if ( bdtmp == bd ) break;
261 					dbidx++;
262 				}
263 
264 				assert( bdtmp != NULL );
265 
266 				fprintf( stderr, "; did you mean to use database #%d (%s)?",
267 					dbidx,
268 					bd->be_suffix[0].bv_val );
269 
270 			} else {
271 				fprintf( stderr, "; no database configured for that naming context" );
272 			}
273 			fprintf( stderr, "\n" );
274 			rc = EXIT_FAILURE;
275 			goto cleanup;
276 		}
277 
278 		/* get id and/or entry */
279 		switch ( lr.lr_op ) {
280 			case LDAP_REQ_ADD:
281 				e = entry_alloc();
282 				ber_dupbv( &e->e_name, &lr.lr_dn );
283 				ber_dupbv( &e->e_nname, &ndn );
284 				break;
285 
286 			//case LDAP_REQ_MODRDN:
287 			case LDAP_REQ_DELETE:
288 			case LDAP_REQ_MODIFY:
289 				id = be->be_dn2id_get( be, &ndn );
290 				rc = (id == NOID);
291 				if ( rc == LDAP_SUCCESS && lr.lr_op != LDAP_REQ_DELETE ) {
292 					e_orig = be->be_entry_get( be, id );
293 					if ( e_orig )
294 						e = entry_dup( e_orig );
295 					rc = (e == NULL);
296 				}
297 				break;
298 		}
299 
300 		if ( rc != LDAP_SUCCESS ) {
301 			fprintf( stderr, "%s: no such entry \"%s\" in database (lineno=%lu)\n",
302 				progname, ndn.bv_val, lineno );
303 			rc = EXIT_FAILURE;
304 			goto cleanup;
305 		}
306 
307 		if ( lr.lrop_mods ) {
308 			for ( n = 0; lr.lrop_mods && lr.lrop_mods[ n ] != NULL; n++ ) {
309 				LDAPMod *mod = lr.lrop_mods[ n ];
310 				Modification mods = { 0 };
311 				unsigned i = 0;
312 				int bin = (mod->mod_op & LDAP_MOD_BVALUES);
313 				int pretty = 0;
314 				int normalize = 0;
315 
316 				local_rc = slap_str2ad( mod->mod_type, &mods.sm_desc, &text );
317 				/*
318 				 * Usually this would be a bad idea (way too dangerous, risks
319 				 * corrupting the DB), but ITS#7786 documents this as a last
320 				 * resort to fix cn=config and missing attributes are one of
321 				 * the possible issues we might encounter.
322 				 */
323 				if ( local_rc == LDAP_UNDEFINED_TYPE &&
324 						(slapMode & SLAP_TOOL_NO_SCHEMA_CHECK) ) {
325 					local_rc = slap_str2undef_ad( mod->mod_type, &mods.sm_desc, &text, 0 );
326 				}
327 				if ( local_rc != LDAP_SUCCESS ) {
328 					fprintf( stderr, "%s: slap_str2ad(\"%s\") failed for entry \"%s\" (%d: %s, lineno=%lu)\n",
329 						progname, mod->mod_type, lr.lr_dn.bv_val, local_rc, text, lineno );
330 					rc = EXIT_FAILURE;
331 					goto cleanup;
332 				}
333 
334 				mods.sm_type = mods.sm_desc->ad_cname;
335 
336 				if ( mods.sm_desc->ad_type->sat_syntax->ssyn_pretty ) {
337 					pretty = 1;
338 
339 				} else {
340 					assert( mods.sm_desc->ad_type->sat_syntax->ssyn_validate != NULL );
341 				}
342 
343 				if ( mods.sm_desc->ad_type->sat_equality &&
344 					mods.sm_desc->ad_type->sat_equality->smr_normalize )
345 				{
346 					normalize = 1;
347 				}
348 
349 				if ( bin && mod->mod_bvalues ) {
350 					for ( i = 0; mod->mod_bvalues[ i ] != NULL; i++ )
351 						;
352 
353 				} else if ( !bin && mod->mod_values ) {
354 					for ( i = 0; mod->mod_values[ i ] != NULL; i++ )
355 						;
356 				}
357 
358 				if ( i != 0 )
359 				{
360 					mods.sm_values = ch_calloc( sizeof( struct berval ), i + 1 );
361 					if ( normalize ) {
362 						mods.sm_nvalues = ch_calloc( sizeof( struct berval ), i + 1 );
363 					} else {
364 						mods.sm_nvalues = NULL;
365 					}
366 				}
367 				mods.sm_numvals = i;
368 
369 				for ( i = 0; i < mods.sm_numvals; i++ ) {
370 					struct berval bv;
371 
372 					if ( bin ) {
373 						bv = *mod->mod_bvalues[ i ];
374 					} else {
375 						ber_str2bv( mod->mod_values[ i ], 0, 0, &bv );
376 					}
377 
378 					if ( pretty ) {
379 						local_rc = ordered_value_pretty( mods.sm_desc,
380 						&bv, &mods.sm_values[i], NULL );
381 
382 					} else {
383 						local_rc = ordered_value_validate( mods.sm_desc,
384 							&bv, 0 );
385 					}
386 
387 					if ( local_rc != LDAP_SUCCESS ) {
388 						fprintf( stderr, "%s: DN=\"%s\": unable to %s attr=%s value #%d\n",
389 							progname, e->e_dn, pretty ? "prettify" : "validate",
390 							mods.sm_desc->ad_cname.bv_val, i );
391 						/* handle error */
392 						rc = EXIT_FAILURE;
393 						ber_bvarray_free( mods.sm_values );
394 						ber_bvarray_free( mods.sm_nvalues );
395 						goto cleanup;
396 					}
397 
398 					if ( !pretty ) {
399 						ber_dupbv( &mods.sm_values[i], &bv );
400 					}
401 
402 					if ( normalize ) {
403 						local_rc = ordered_value_normalize(
404 							SLAP_MR_VALUE_OF_ATTRIBUTE_SYNTAX,
405 							mods.sm_desc,
406 							mods.sm_desc->ad_type->sat_equality,
407 							&mods.sm_values[i], &mods.sm_nvalues[i],
408 							NULL );
409 						if ( local_rc != LDAP_SUCCESS ) {
410 							fprintf( stderr, "%s: DN=\"%s\": unable to normalize attr=%s value #%d\n",
411 								progname, e->e_dn, mods.sm_desc->ad_cname.bv_val, i );
412 							/* handle error */
413 							rc = EXIT_FAILURE;
414 							ber_bvarray_free( mods.sm_values );
415 							ber_bvarray_free( mods.sm_nvalues );
416 							goto cleanup;
417 						}
418 					}
419 				}
420 
421 				mods.sm_op = (mod->mod_op & ~LDAP_MOD_BVALUES);
422 				mods.sm_flags = 0;
423 
424 				if ( mods.sm_desc == slap_schema.si_ad_objectClass ) {
425 					is_oc = 1;
426 				}
427 
428 				switch ( mods.sm_op ) {
429 				case LDAP_MOD_ADD:
430 					local_rc = modify_add_values( e, &mods,
431 						0, &text, textbuf, textlen );
432 					break;
433 
434 				case LDAP_MOD_DELETE:
435 					local_rc = modify_delete_values( e, &mods,
436 						0, &text, textbuf, textlen );
437 					break;
438 
439 				case LDAP_MOD_REPLACE:
440 					local_rc = modify_replace_values( e, &mods,
441 						0, &text, textbuf, textlen );
442 					break;
443 
444 				case LDAP_MOD_INCREMENT:
445 					local_rc = modify_increment_values( e, &mods,
446 						0, &text, textbuf, textlen );
447 					break;
448 				}
449 
450 				ber_bvarray_free( mods.sm_values );
451 				ber_bvarray_free( mods.sm_nvalues );
452 
453 				if ( local_rc != LDAP_SUCCESS ) {
454 					fprintf( stderr, "%s: DN=\"%s\": unable to modify attr=%s\n",
455 						progname, e->e_dn, mods.sm_desc->ad_cname.bv_val );
456 					rc = EXIT_FAILURE;
457 					goto cleanup;
458 				}
459 			}
460 
461 			rc = slap_tool_entry_check( progname, op, e, lineno, &text, textbuf, textlen );
462 			if ( rc != LDAP_SUCCESS ) {
463 				rc = EXIT_FAILURE;
464 				goto cleanup;
465 			}
466 		}
467 
468 		if ( SLAP_LASTMOD(be) && e != NULL ) {
469 			time_t now = slap_get_time();
470 			char uuidbuf[ LDAP_LUTIL_UUIDSTR_BUFSIZE ];
471 			struct berval vals[ 2 ];
472 
473 			struct berval name, timestamp;
474 
475 			struct berval nvals[ 2 ];
476 			struct berval nname;
477 			char timebuf[ LDAP_LUTIL_GENTIME_BUFSIZE ];
478 
479 			Attribute *a;
480 
481 			vals[1].bv_len = 0;
482 			vals[1].bv_val = NULL;
483 
484 			nvals[1].bv_len = 0;
485 			nvals[1].bv_val = NULL;
486 
487 			csn.bv_len = ldap_pvt_csnstr( csnbuf, sizeof( csnbuf ), csnsid, 0 );
488 			csn.bv_val = csnbuf;
489 
490 			timestamp.bv_val = timebuf;
491 			timestamp.bv_len = sizeof(timebuf);
492 
493 			slap_timestamp( &now, &timestamp );
494 
495 			if ( BER_BVISEMPTY( &be->be_rootndn ) ) {
496 				BER_BVSTR( &name, SLAPD_ANONYMOUS );
497 				nname = name;
498 			} else {
499 				name = be->be_rootdn;
500 				nname = be->be_rootndn;
501 			}
502 
503 			a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
504 			if ( a != NULL ) {
505 				if ( a->a_vals != a->a_nvals ) {
506 					SLAP_FREE( a->a_nvals[0].bv_val );
507 					SLAP_FREE( a->a_nvals );
508 				}
509 				SLAP_FREE( a->a_vals[0].bv_val );
510 				SLAP_FREE( a->a_vals );
511 				a->a_vals = NULL;
512 				a->a_nvals = NULL;
513 				a->a_numvals = 0;
514 			}
515 			vals[0].bv_len = lutil_uuidstr( uuidbuf, sizeof( uuidbuf ) );
516 			vals[0].bv_val = uuidbuf;
517 			attr_merge_normalize_one( e, slap_schema.si_ad_entryUUID, vals, NULL );
518 
519 			a = attr_find( e->e_attrs, slap_schema.si_ad_creatorsName );
520 			if ( a == NULL ) {
521 				vals[0] = name;
522 				nvals[0] = nname;
523 				attr_merge( e, slap_schema.si_ad_creatorsName, vals, nvals );
524 
525 			} else {
526 				ber_bvreplace( &a->a_vals[0], &name );
527 				ber_bvreplace( &a->a_nvals[0], &nname );
528 			}
529 
530 			a = attr_find( e->e_attrs, slap_schema.si_ad_createTimestamp );
531 			if ( a == NULL ) {
532 				vals[0] = timestamp;
533 				attr_merge( e, slap_schema.si_ad_createTimestamp, vals, NULL );
534 
535 			} else {
536 				ber_bvreplace( &a->a_vals[0], &timestamp );
537 			}
538 
539 			a = attr_find( e->e_attrs, slap_schema.si_ad_entryCSN );
540 			if ( a == NULL ) {
541 				vals[0] = csn;
542 				attr_merge( e, slap_schema.si_ad_entryCSN, vals, NULL );
543 
544 			} else {
545 				ber_bvreplace( &a->a_vals[0], &csn );
546 			}
547 
548 			a = attr_find( e->e_attrs, slap_schema.si_ad_modifiersName );
549 			if ( a == NULL ) {
550 				vals[0] = name;
551 				nvals[0] = nname;
552 				attr_merge( e, slap_schema.si_ad_modifiersName, vals, nvals );
553 
554 			} else {
555 				ber_bvreplace( &a->a_vals[0], &name );
556 				ber_bvreplace( &a->a_nvals[0], &nname );
557 			}
558 
559 			a = attr_find( e->e_attrs, slap_schema.si_ad_modifyTimestamp );
560 			if ( a == NULL ) {
561 				vals[0] = timestamp;
562 				attr_merge( e, slap_schema.si_ad_modifyTimestamp, vals, NULL );
563 
564 			} else {
565 				ber_bvreplace( &a->a_vals[0], &timestamp );
566 			}
567 		}
568 
569 		/* check schema, objectClass etc */
570 
571 		if ( !dryrun ) {
572 			switch ( lr.lr_op ) {
573 			case LDAP_REQ_ADD:
574 				id = be->be_entry_put( be, e, &bvtext );
575 				rc = (id == NOID);
576 				break;
577 
578 			case LDAP_REQ_MODIFY:
579 				id = be->be_entry_modify( be, e, &bvtext );
580 				rc = (id == NOID);
581 				break;
582 
583 			case LDAP_REQ_DELETE:
584 				rc = be->be_entry_delete( be, &ndn, &bvtext );
585 				break;
586 
587 			}
588 
589 			if( rc != LDAP_SUCCESS ) {
590 				fprintf( stderr, "%s: could not %s entry dn=\"%s\" "
591 					"(line=%lu): %s\n", progname, request, ndn.bv_val,
592 					lineno, bvtext.bv_val );
593 				rc = EXIT_FAILURE;
594 				goto cleanup;
595 			}
596 
597 			sid = slap_tool_update_ctxcsn_check( progname, e );
598 
599 			if ( verbose )
600 				fprintf( stderr, "%s: \"%s\" (%08lx)\n",
601 					request, ndn.bv_val, (long) id );
602 		} else {
603 			if ( verbose )
604 				fprintf( stderr, "%s: \"%s\"\n",
605 					request, ndn.bv_val );
606 		}
607 
608 cleanup:;
609 		ldap_ldif_record_done( &lr );
610 		SLAP_FREE( ndn.bv_val );
611 		if ( e ) entry_free( e );
612 		if ( e_orig ) be_entry_release_w( op, e_orig );
613 		if ( rc != LDAP_SUCCESS && !continuemode ) break;
614 	}
615 
616 	if ( ldifrc < 0 )
617 		rc = EXIT_FAILURE;
618 
619 	bvtext.bv_len = textlen;
620 	bvtext.bv_val = textbuf;
621 	bvtext.bv_val[0] = '\0';
622 
623 	if ( enable_meter ) {
624 		lutil_meter_update( &meter, ftell( ldiffp->fp ), 1);
625 		lutil_meter_close( &meter );
626 	}
627 
628 	if ( rc == EXIT_SUCCESS ) {
629 		rc = slap_tool_update_ctxcsn( progname, sid, &bvtext );
630 	}
631 
632 	ch_free( buf );
633 
634 	if ( !dryrun ) {
635 		if ( enable_meter ) {
636 			fprintf( stderr, "Closing DB..." );
637 		}
638 		if( be->be_entry_close( be ) ) {
639 			rc = EXIT_FAILURE;
640 		}
641 
642 		if( be->be_sync ) {
643 			be->be_sync( be );
644 		}
645 		if ( enable_meter ) {
646 			fprintf( stderr, "\n" );
647 		}
648 	}
649 
650 	if ( slap_tool_destroy())
651 		rc = EXIT_FAILURE;
652 
653 	return rc;
654 }
655 
656