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, ×tamp );
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], ×tamp );
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], ×tamp );
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