rdnval.c revision 1.4 1 1.3 christos /* $NetBSD: rdnval.c,v 1.4 2025/09/05 21:16:18 christos Exp $ */
2 1.1 adam
3 1.1 adam /* rdnval.c - RDN value overlay */
4 1.2 christos /* $OpenLDAP$ */
5 1.1 adam /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 1.1 adam *
7 1.4 christos * Copyright 1998-2024 The OpenLDAP Foundation.
8 1.1 adam * Portions Copyright 2008 Pierangelo Masarati.
9 1.1 adam * All rights reserved.
10 1.1 adam *
11 1.1 adam * Redistribution and use in source and binary forms, with or without
12 1.1 adam * modification, are permitted only as authorized by the OpenLDAP
13 1.1 adam * Public License.
14 1.1 adam *
15 1.1 adam * A copy of this license is available in the file LICENSE in the
16 1.1 adam * top-level directory of the distribution or, alternatively, at
17 1.1 adam * <http://www.OpenLDAP.org/license.html>.
18 1.1 adam */
19 1.1 adam /* ACKNOWLEDGEMENTS:
20 1.1 adam * This work was initially developed by Pierangelo Masarati
21 1.1 adam * for inclusion in OpenLDAP Software.
22 1.1 adam */
23 1.1 adam
24 1.2 christos #include <sys/cdefs.h>
25 1.3 christos __RCSID("$NetBSD: rdnval.c,v 1.4 2025/09/05 21:16:18 christos Exp $");
26 1.2 christos
27 1.1 adam #include "portable.h"
28 1.1 adam
29 1.1 adam #ifdef SLAPD_OVER_RDNVAL
30 1.1 adam
31 1.1 adam #include <stdio.h>
32 1.1 adam
33 1.1 adam #include "ac/string.h"
34 1.1 adam #include "ac/socket.h"
35 1.1 adam
36 1.1 adam #include "slap.h"
37 1.3 christos #include "slap-config.h"
38 1.1 adam
39 1.1 adam #include "lutil.h"
40 1.1 adam
41 1.1 adam /*
42 1.1 adam * Maintain an attribute (rdnValue) that contains the values of each AVA
43 1.1 adam * that builds up the RDN of an entry. This is required for interoperation
44 1.1 adam * with Samba4. It mimics the "name" attribute provided by Active Directory.
45 1.1 adam * The naming attributes must be directoryString-valued, or compatible.
46 1.1 adam * For example, IA5String values are cast into directoryString unless
47 1.1 adam * consisting of the empty string ("").
48 1.1 adam */
49 1.1 adam
50 1.1 adam static AttributeDescription *ad_rdnValue;
51 1.1 adam static Syntax *syn_IA5String;
52 1.1 adam
53 1.1 adam static slap_overinst rdnval;
54 1.1 adam
55 1.1 adam static int
56 1.1 adam rdnval_is_valid( AttributeDescription *desc, struct berval *value )
57 1.1 adam {
58 1.1 adam if ( desc->ad_type->sat_syntax == slap_schema.si_syn_directoryString ) {
59 1.1 adam return 1;
60 1.1 adam }
61 1.1 adam
62 1.1 adam if ( desc->ad_type->sat_syntax == syn_IA5String
63 1.1 adam && !BER_BVISEMPTY( value ) )
64 1.1 adam {
65 1.1 adam return 1;
66 1.1 adam }
67 1.1 adam
68 1.1 adam return 0;
69 1.1 adam }
70 1.1 adam
71 1.1 adam static int
72 1.1 adam rdnval_unique_check_cb( Operation *op, SlapReply *rs )
73 1.1 adam {
74 1.1 adam if ( rs->sr_type == REP_SEARCH ) {
75 1.1 adam int *p = (int *)op->o_callback->sc_private;
76 1.1 adam (*p)++;
77 1.1 adam }
78 1.1 adam
79 1.1 adam return 0;
80 1.1 adam }
81 1.1 adam
82 1.1 adam static int
83 1.1 adam rdnval_unique_check( Operation *op, BerVarray vals )
84 1.1 adam {
85 1.1 adam slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
86 1.1 adam
87 1.1 adam BackendDB db = *op->o_bd;
88 1.1 adam Operation op2 = *op;
89 1.1 adam SlapReply rs2 = { 0 };
90 1.1 adam int i;
91 1.1 adam BerVarray fvals;
92 1.1 adam char *ptr;
93 1.1 adam int gotit = 0;
94 1.1 adam slap_callback cb = { 0 };
95 1.1 adam
96 1.1 adam /* short-circuit attempts to add suffix entry */
97 1.1 adam if ( op->o_tag == LDAP_REQ_ADD
98 1.1 adam && be_issuffix( op->o_bd, &op->o_req_ndn ) )
99 1.1 adam {
100 1.1 adam return LDAP_SUCCESS;
101 1.1 adam }
102 1.1 adam
103 1.1 adam op2.o_bd = &db;
104 1.1 adam op2.o_bd->bd_info = (BackendInfo *)on->on_info;
105 1.1 adam op2.o_tag = LDAP_REQ_SEARCH;
106 1.1 adam op2.o_dn = op->o_bd->be_rootdn;
107 1.1 adam op2.o_ndn = op->o_bd->be_rootndn;
108 1.1 adam op2.o_callback = &cb;
109 1.1 adam cb.sc_response = rdnval_unique_check_cb;
110 1.1 adam cb.sc_private = (void *)&gotit;
111 1.1 adam
112 1.1 adam dnParent( &op->o_req_ndn, &op2.o_req_dn );
113 1.1 adam op2.o_req_ndn = op2.o_req_dn;
114 1.1 adam
115 1.1 adam op2.ors_limit = NULL;
116 1.1 adam op2.ors_slimit = 1;
117 1.1 adam op2.ors_tlimit = SLAP_NO_LIMIT;
118 1.1 adam op2.ors_attrs = slap_anlist_no_attrs;
119 1.1 adam op2.ors_attrsonly = 1;
120 1.1 adam op2.ors_deref = LDAP_DEREF_NEVER;
121 1.1 adam op2.ors_scope = LDAP_SCOPE_ONELEVEL;
122 1.1 adam
123 1.1 adam for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ )
124 1.1 adam /* just count */ ;
125 1.1 adam
126 1.1 adam fvals = op->o_tmpcalloc( sizeof( struct berval ), i + 1,
127 1.1 adam op->o_tmpmemctx );
128 1.1 adam
129 1.1 adam op2.ors_filterstr.bv_len = 0;
130 1.1 adam if ( i > 1 ) {
131 1.1 adam op2.ors_filterstr.bv_len = STRLENOF( "(&)" );
132 1.1 adam }
133 1.1 adam
134 1.1 adam for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
135 1.1 adam ldap_bv2escaped_filter_value_x( &vals[ i ], &fvals[ i ],
136 1.1 adam 1, op->o_tmpmemctx );
137 1.1 adam op2.ors_filterstr.bv_len += ad_rdnValue->ad_cname.bv_len
138 1.1 adam + fvals[ i ].bv_len + STRLENOF( "(=)" );
139 1.1 adam }
140 1.1 adam
141 1.1 adam op2.ors_filterstr.bv_val = op->o_tmpalloc( op2.ors_filterstr.bv_len + 1, op->o_tmpmemctx );
142 1.1 adam
143 1.1 adam ptr = op2.ors_filterstr.bv_val;
144 1.1 adam if ( i > 1 ) {
145 1.1 adam ptr = lutil_strcopy( ptr, "(&" );
146 1.1 adam }
147 1.1 adam for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
148 1.1 adam *ptr++ = '(';
149 1.1 adam ptr = lutil_strncopy( ptr, ad_rdnValue->ad_cname.bv_val, ad_rdnValue->ad_cname.bv_len );
150 1.1 adam *ptr++ = '=';
151 1.1 adam ptr = lutil_strncopy( ptr, fvals[ i ].bv_val, fvals[ i ].bv_len );
152 1.1 adam *ptr++ = ')';
153 1.1 adam }
154 1.1 adam
155 1.1 adam if ( i > 1 ) {
156 1.1 adam *ptr++ = ')';
157 1.1 adam }
158 1.1 adam *ptr = '\0';
159 1.1 adam
160 1.1 adam assert( ptr == op2.ors_filterstr.bv_val + op2.ors_filterstr.bv_len );
161 1.1 adam op2.ors_filter = str2filter_x( op, op2.ors_filterstr.bv_val );
162 1.1 adam assert( op2.ors_filter != NULL );
163 1.1 adam
164 1.1 adam (void)op2.o_bd->be_search( &op2, &rs2 );
165 1.1 adam
166 1.1 adam filter_free_x( op, op2.ors_filter, 1 );
167 1.1 adam op->o_tmpfree( op2.ors_filterstr.bv_val, op->o_tmpmemctx );
168 1.1 adam for ( i = 0; !BER_BVISNULL( &vals[ i ] ); i++ ) {
169 1.1 adam if ( vals[ i ].bv_val != fvals[ i ].bv_val ) {
170 1.1 adam op->o_tmpfree( fvals[ i ].bv_val, op->o_tmpmemctx );
171 1.1 adam }
172 1.1 adam }
173 1.1 adam op->o_tmpfree( fvals, op->o_tmpmemctx );
174 1.1 adam
175 1.1 adam if ( rs2.sr_err != LDAP_SUCCESS || gotit > 0 ) {
176 1.1 adam return LDAP_CONSTRAINT_VIOLATION;
177 1.1 adam }
178 1.1 adam
179 1.1 adam return LDAP_SUCCESS;
180 1.1 adam }
181 1.1 adam
182 1.1 adam static int
183 1.1 adam rdnval_rdn2vals(
184 1.1 adam Operation *op,
185 1.1 adam SlapReply *rs,
186 1.1 adam struct berval *dn,
187 1.1 adam struct berval *ndn,
188 1.1 adam BerVarray *valsp,
189 1.1 adam BerVarray *nvalsp,
190 1.1 adam int *numvalsp )
191 1.1 adam {
192 1.1 adam LDAPRDN rdn = NULL, nrdn = NULL;
193 1.1 adam int nAVA, i;
194 1.1 adam
195 1.1 adam assert( *valsp == NULL );
196 1.1 adam assert( *nvalsp == NULL );
197 1.1 adam
198 1.1 adam *numvalsp = 0;
199 1.1 adam
200 1.1 adam if ( ldap_bv2rdn_x( dn, &rdn, (char **)&rs->sr_text,
201 1.1 adam LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) )
202 1.1 adam {
203 1.1 adam Debug( LDAP_DEBUG_TRACE,
204 1.1 adam "%s rdnval: can't figure out "
205 1.1 adam "type(s)/value(s) of rdn DN=\"%s\"\n",
206 1.3 christos op->o_log_prefix, dn->bv_val );
207 1.1 adam rs->sr_err = LDAP_INVALID_DN_SYNTAX;
208 1.1 adam rs->sr_text = "unknown type(s) used in RDN";
209 1.1 adam
210 1.1 adam goto done;
211 1.1 adam }
212 1.1 adam
213 1.1 adam if ( ldap_bv2rdn_x( ndn, &nrdn,
214 1.1 adam (char **)&rs->sr_text, LDAP_DN_FORMAT_LDAP, op->o_tmpmemctx ) )
215 1.1 adam {
216 1.1 adam Debug( LDAP_DEBUG_TRACE,
217 1.1 adam "%s rdnval: can't figure out "
218 1.1 adam "type(s)/value(s) of normalized rdn DN=\"%s\"\n",
219 1.3 christos op->o_log_prefix, ndn->bv_val );
220 1.1 adam rs->sr_err = LDAP_INVALID_DN_SYNTAX;
221 1.1 adam rs->sr_text = "unknown type(s) used in RDN";
222 1.1 adam
223 1.1 adam goto done;
224 1.1 adam }
225 1.1 adam
226 1.1 adam for ( nAVA = 0; rdn[ nAVA ]; nAVA++ )
227 1.1 adam /* count'em */ ;
228 1.1 adam
229 1.1 adam /* NOTE: we assume rdn and nrdn contain the same AVAs! */
230 1.1 adam
231 1.3 christos *valsp = ch_calloc( sizeof( struct berval ), nAVA + 1 );
232 1.3 christos *nvalsp = ch_calloc( sizeof( struct berval ), nAVA + 1 );
233 1.1 adam
234 1.1 adam /* Add new attribute values to the entry */
235 1.1 adam for ( i = 0; rdn[ i ]; i++ ) {
236 1.1 adam AttributeDescription *desc = NULL;
237 1.1 adam
238 1.1 adam rs->sr_err = slap_bv2ad( &rdn[ i ]->la_attr,
239 1.1 adam &desc, &rs->sr_text );
240 1.1 adam
241 1.1 adam if ( rs->sr_err != LDAP_SUCCESS ) {
242 1.1 adam Debug( LDAP_DEBUG_TRACE,
243 1.1 adam "%s rdnval: %s: %s\n",
244 1.1 adam op->o_log_prefix,
245 1.1 adam rs->sr_text,
246 1.1 adam rdn[ i ]->la_attr.bv_val );
247 1.1 adam goto done;
248 1.1 adam }
249 1.1 adam
250 1.1 adam if ( !rdnval_is_valid( desc, &rdn[ i ]->la_value ) ) {
251 1.1 adam Debug( LDAP_DEBUG_TRACE,
252 1.1 adam "%s rdnval: syntax of naming attribute '%s' "
253 1.1 adam "not compatible with directoryString",
254 1.3 christos op->o_log_prefix, rdn[ i ]->la_attr.bv_val );
255 1.1 adam continue;
256 1.1 adam }
257 1.1 adam
258 1.1 adam if ( value_find_ex( desc,
259 1.1 adam SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
260 1.1 adam SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
261 1.1 adam *nvalsp,
262 1.1 adam &nrdn[ i ]->la_value,
263 1.1 adam op->o_tmpmemctx )
264 1.1 adam == LDAP_NO_SUCH_ATTRIBUTE )
265 1.1 adam {
266 1.1 adam ber_dupbv( &(*valsp)[ *numvalsp ], &rdn[ i ]->la_value );
267 1.1 adam ber_dupbv( &(*nvalsp)[ *numvalsp ], &nrdn[ i ]->la_value );
268 1.1 adam
269 1.1 adam (*numvalsp)++;
270 1.1 adam }
271 1.1 adam }
272 1.1 adam
273 1.1 adam if ( rdnval_unique_check( op, *valsp ) != LDAP_SUCCESS ) {
274 1.1 adam rs->sr_err = LDAP_CONSTRAINT_VIOLATION;
275 1.1 adam rs->sr_text = "rdnValue not unique within siblings";
276 1.1 adam goto done;
277 1.1 adam }
278 1.1 adam
279 1.1 adam done:;
280 1.1 adam if ( rdn != NULL ) {
281 1.1 adam ldap_rdnfree_x( rdn, op->o_tmpmemctx );
282 1.1 adam }
283 1.1 adam
284 1.1 adam if ( nrdn != NULL ) {
285 1.1 adam ldap_rdnfree_x( nrdn, op->o_tmpmemctx );
286 1.1 adam }
287 1.1 adam
288 1.1 adam if ( rs->sr_err != LDAP_SUCCESS ) {
289 1.1 adam if ( *valsp != NULL ) {
290 1.1 adam ber_bvarray_free( *valsp );
291 1.1 adam ber_bvarray_free( *nvalsp );
292 1.1 adam *valsp = NULL;
293 1.1 adam *nvalsp = NULL;
294 1.1 adam *numvalsp = 0;
295 1.1 adam }
296 1.1 adam }
297 1.1 adam
298 1.1 adam return rs->sr_err;
299 1.1 adam }
300 1.1 adam
301 1.1 adam static int
302 1.1 adam rdnval_op_add( Operation *op, SlapReply *rs )
303 1.1 adam {
304 1.1 adam Attribute *a, **ap;
305 1.1 adam int numvals = 0;
306 1.1 adam BerVarray vals = NULL, nvals = NULL;
307 1.1 adam int rc;
308 1.1 adam
309 1.1 adam /* NOTE: should we accept an entry still in mods format? */
310 1.1 adam assert( op->ora_e != NULL );
311 1.1 adam
312 1.1 adam if ( BER_BVISEMPTY( &op->ora_e->e_nname ) ) {
313 1.1 adam return SLAP_CB_CONTINUE;
314 1.1 adam }
315 1.1 adam
316 1.1 adam a = attr_find( op->ora_e->e_attrs, ad_rdnValue );
317 1.1 adam if ( a != NULL ) {
318 1.1 adam /* TODO: check consistency? */
319 1.1 adam return SLAP_CB_CONTINUE;
320 1.1 adam }
321 1.1 adam
322 1.1 adam rc = rdnval_rdn2vals( op, rs, &op->ora_e->e_name, &op->ora_e->e_nname,
323 1.1 adam &vals, &nvals, &numvals );
324 1.1 adam if ( rc != LDAP_SUCCESS ) {
325 1.1 adam send_ldap_result( op, rs );
326 1.1 adam }
327 1.1 adam
328 1.1 adam a = attr_alloc( ad_rdnValue );
329 1.1 adam
330 1.1 adam a->a_vals = vals;
331 1.1 adam a->a_nvals = nvals;
332 1.1 adam a->a_numvals = numvals;
333 1.1 adam
334 1.1 adam for ( ap = &op->ora_e->e_attrs; *ap != NULL; ap = &(*ap)->a_next )
335 1.1 adam /* goto tail */ ;
336 1.1 adam
337 1.1 adam *ap = a;
338 1.1 adam
339 1.1 adam return SLAP_CB_CONTINUE;
340 1.1 adam }
341 1.1 adam
342 1.1 adam static int
343 1.1 adam rdnval_op_rename( Operation *op, SlapReply *rs )
344 1.1 adam {
345 1.1 adam Modifications *ml, **mlp;
346 1.1 adam int numvals = 0;
347 1.1 adam BerVarray vals = NULL, nvals = NULL;
348 1.1 adam struct berval old;
349 1.1 adam int rc;
350 1.1 adam
351 1.2 christos dnRdn( &op->o_req_ndn, &old );
352 1.2 christos if ( dn_match( &old, &op->orr_nnewrdn ) ) {
353 1.2 christos return SLAP_CB_CONTINUE;
354 1.1 adam }
355 1.1 adam
356 1.1 adam rc = rdnval_rdn2vals( op, rs, &op->orr_newrdn, &op->orr_nnewrdn,
357 1.1 adam &vals, &nvals, &numvals );
358 1.1 adam if ( rc != LDAP_SUCCESS ) {
359 1.1 adam send_ldap_result( op, rs );
360 1.1 adam }
361 1.1 adam
362 1.3 christos ml = ch_calloc( sizeof( Modifications ), 1 );
363 1.1 adam ml->sml_values = vals;
364 1.1 adam ml->sml_nvalues = nvals;
365 1.1 adam
366 1.1 adam ml->sml_numvals = numvals;
367 1.1 adam
368 1.1 adam ml->sml_op = LDAP_MOD_REPLACE;
369 1.1 adam ml->sml_flags = SLAP_MOD_INTERNAL;
370 1.1 adam ml->sml_desc = ad_rdnValue;
371 1.1 adam ml->sml_type = ad_rdnValue->ad_cname;
372 1.1 adam
373 1.1 adam for ( mlp = &op->orr_modlist; *mlp != NULL; mlp = &(*mlp)->sml_next )
374 1.1 adam /* goto tail */ ;
375 1.1 adam
376 1.1 adam *mlp = ml;
377 1.1 adam
378 1.1 adam return SLAP_CB_CONTINUE;
379 1.1 adam }
380 1.1 adam
381 1.1 adam static int
382 1.1 adam rdnval_db_init(
383 1.1 adam BackendDB *be,
384 1.1 adam ConfigReply *cr)
385 1.1 adam {
386 1.1 adam if ( SLAP_ISGLOBALOVERLAY( be ) ) {
387 1.3 christos Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
388 1.1 adam "rdnval_db_init: rdnval cannot be used as global overlay.\n" );
389 1.1 adam return 1;
390 1.1 adam }
391 1.1 adam
392 1.1 adam if ( be->be_nsuffix == NULL ) {
393 1.3 christos Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
394 1.1 adam "rdnval_db_init: database must have suffix\n" );
395 1.1 adam return 1;
396 1.1 adam }
397 1.1 adam
398 1.1 adam if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
399 1.3 christos Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
400 1.1 adam "rdnval_db_init: missing rootdn for database DN=\"%s\", YMMV\n",
401 1.1 adam be->be_suffix[ 0 ].bv_val );
402 1.1 adam }
403 1.1 adam
404 1.1 adam return 0;
405 1.1 adam }
406 1.1 adam
407 1.1 adam typedef struct rdnval_mod_t {
408 1.1 adam struct berval ndn;
409 1.1 adam BerVarray vals;
410 1.1 adam BerVarray nvals;
411 1.1 adam int numvals;
412 1.1 adam struct rdnval_mod_t *next;
413 1.1 adam } rdnval_mod_t;
414 1.1 adam
415 1.1 adam typedef struct {
416 1.1 adam BackendDB *bd;
417 1.1 adam rdnval_mod_t *mods;
418 1.1 adam } rdnval_repair_cb_t;
419 1.1 adam
420 1.1 adam static int
421 1.1 adam rdnval_repair_cb( Operation *op, SlapReply *rs )
422 1.1 adam {
423 1.1 adam int rc;
424 1.1 adam rdnval_repair_cb_t *rcb = op->o_callback->sc_private;
425 1.1 adam rdnval_mod_t *mod;
426 1.1 adam BerVarray vals = NULL, nvals = NULL;
427 1.1 adam int numvals = 0;
428 1.1 adam ber_len_t len;
429 1.1 adam BackendDB *save_bd = op->o_bd;
430 1.1 adam
431 1.1 adam switch ( rs->sr_type ) {
432 1.1 adam case REP_SEARCH:
433 1.1 adam break;
434 1.1 adam
435 1.1 adam case REP_SEARCHREF:
436 1.1 adam case REP_RESULT:
437 1.1 adam return rs->sr_err;
438 1.1 adam
439 1.1 adam default:
440 1.1 adam assert( 0 );
441 1.1 adam }
442 1.1 adam
443 1.1 adam assert( rs->sr_entry != NULL );
444 1.1 adam
445 1.1 adam op->o_bd = rcb->bd;
446 1.1 adam rc = rdnval_rdn2vals( op, rs, &rs->sr_entry->e_name, &rs->sr_entry->e_nname,
447 1.1 adam &vals, &nvals, &numvals );
448 1.1 adam op->o_bd = save_bd;
449 1.1 adam if ( rc != LDAP_SUCCESS ) {
450 1.1 adam return 0;
451 1.1 adam }
452 1.1 adam
453 1.1 adam len = sizeof( rdnval_mod_t ) + rs->sr_entry->e_nname.bv_len + 1;
454 1.1 adam mod = op->o_tmpalloc( len, op->o_tmpmemctx );
455 1.1 adam mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len;
456 1.1 adam mod->ndn.bv_val = (char *)&mod[1];
457 1.1 adam lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
458 1.1 adam mod->vals = vals;
459 1.1 adam mod->nvals = nvals;
460 1.1 adam mod->numvals = numvals;
461 1.1 adam
462 1.1 adam mod->next = rcb->mods;
463 1.1 adam rcb->mods = mod;
464 1.1 adam
465 1.1 adam Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair_cb: scheduling entry DN=\"%s\" for repair\n",
466 1.3 christos op->o_log_prefix, rs->sr_entry->e_name.bv_val );
467 1.1 adam
468 1.1 adam return 0;
469 1.1 adam }
470 1.1 adam
471 1.1 adam static int
472 1.1 adam rdnval_repair( BackendDB *be )
473 1.1 adam {
474 1.1 adam slap_overinst *on = (slap_overinst *)be->bd_info;
475 1.1 adam void *ctx = ldap_pvt_thread_pool_context();
476 1.1 adam Connection conn = { 0 };
477 1.1 adam OperationBuffer opbuf;
478 1.1 adam Operation *op;
479 1.1 adam BackendDB db;
480 1.1 adam slap_callback sc = { 0 };
481 1.1 adam rdnval_repair_cb_t rcb = { 0 };
482 1.1 adam SlapReply rs = { REP_RESULT };
483 1.1 adam rdnval_mod_t *rmod;
484 1.1 adam int nrepaired = 0;
485 1.1 adam
486 1.1 adam connection_fake_init2( &conn, &opbuf, ctx, 0 );
487 1.1 adam op = &opbuf.ob_op;
488 1.1 adam
489 1.1 adam op->o_tag = LDAP_REQ_SEARCH;
490 1.1 adam memset( &op->oq_search, 0, sizeof( op->oq_search ) );
491 1.1 adam
492 1.1 adam assert( !BER_BVISNULL( &be->be_nsuffix[ 0 ] ) );
493 1.1 adam
494 1.1 adam op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 );
495 1.1 adam assert( op->o_bd != NULL );
496 1.1 adam assert( op->o_bd->be_nsuffix != NULL );
497 1.1 adam
498 1.1 adam op->o_req_dn = op->o_bd->be_suffix[ 0 ];
499 1.1 adam op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
500 1.1 adam
501 1.1 adam op->o_dn = op->o_bd->be_rootdn;
502 1.1 adam op->o_ndn = op->o_bd->be_rootndn;
503 1.1 adam
504 1.1 adam op->ors_scope = LDAP_SCOPE_SUBTREE;
505 1.1 adam op->ors_tlimit = SLAP_NO_LIMIT;
506 1.1 adam op->ors_slimit = SLAP_NO_LIMIT;
507 1.1 adam op->ors_attrs = slap_anlist_no_attrs;
508 1.1 adam
509 1.1 adam op->ors_filterstr.bv_len = STRLENOF( "(!(=*))" ) + ad_rdnValue->ad_cname.bv_len;
510 1.1 adam op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
511 1.1 adam snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
512 1.1 adam "(!(%s=*))", ad_rdnValue->ad_cname.bv_val );
513 1.1 adam
514 1.1 adam op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
515 1.1 adam if ( op->ors_filter == NULL ) {
516 1.1 adam rs.sr_err = LDAP_OTHER;
517 1.1 adam goto done_search;
518 1.1 adam }
519 1.1 adam
520 1.1 adam op->o_callback = ≻
521 1.1 adam sc.sc_response = rdnval_repair_cb;
522 1.1 adam sc.sc_private = &rcb;
523 1.1 adam rcb.bd = &db;
524 1.1 adam db = *be;
525 1.1 adam db.bd_info = (BackendInfo *)on;
526 1.1 adam
527 1.1 adam (void)op->o_bd->bd_info->bi_op_search( op, &rs );
528 1.1 adam
529 1.1 adam op->o_tag = LDAP_REQ_MODIFY;
530 1.1 adam sc.sc_response = slap_null_cb;
531 1.1 adam sc.sc_private = NULL;
532 1.1 adam memset( &op->oq_modify, 0, sizeof( req_modify_s ) );
533 1.1 adam
534 1.1 adam for ( rmod = rcb.mods; rmod != NULL; ) {
535 1.1 adam rdnval_mod_t *rnext;
536 1.1 adam
537 1.1 adam Modifications *mod;
538 1.1 adam SlapReply rs2 = { REP_RESULT };
539 1.1 adam
540 1.1 adam mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
541 1.1 adam mod->sml_flags = SLAP_MOD_INTERNAL;
542 1.1 adam mod->sml_op = LDAP_MOD_REPLACE;
543 1.1 adam mod->sml_desc = ad_rdnValue;
544 1.1 adam mod->sml_type = ad_rdnValue->ad_cname;
545 1.1 adam mod->sml_values = rmod->vals;
546 1.1 adam mod->sml_nvalues = rmod->nvals;
547 1.1 adam mod->sml_numvals = rmod->numvals;
548 1.1 adam mod->sml_next = NULL;
549 1.1 adam
550 1.1 adam op->o_req_dn = rmod->ndn;
551 1.1 adam op->o_req_ndn = rmod->ndn;
552 1.1 adam
553 1.1 adam op->orm_modlist = mod;
554 1.1 adam
555 1.1 adam op->o_bd->be_modify( op, &rs2 );
556 1.1 adam
557 1.1 adam slap_mods_free( op->orm_modlist, 1 );
558 1.1 adam if ( rs2.sr_err == LDAP_SUCCESS ) {
559 1.1 adam Debug( LDAP_DEBUG_TRACE, "%s: rdnval_repair: entry DN=\"%s\" repaired\n",
560 1.3 christos op->o_log_prefix, rmod->ndn.bv_val );
561 1.1 adam nrepaired++;
562 1.1 adam
563 1.1 adam } else {
564 1.1 adam Debug( LDAP_DEBUG_ANY, "%s: rdnval_repair: entry DN=\"%s\" repair failed (%d)\n",
565 1.1 adam op->o_log_prefix, rmod->ndn.bv_val, rs2.sr_err );
566 1.1 adam }
567 1.1 adam
568 1.1 adam rnext = rmod->next;
569 1.1 adam op->o_tmpfree( rmod, op->o_tmpmemctx );
570 1.1 adam rmod = rnext;
571 1.1 adam }
572 1.1 adam
573 1.1 adam done_search:;
574 1.1 adam op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
575 1.1 adam filter_free_x( op, op->ors_filter, 1 );
576 1.1 adam
577 1.3 christos Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
578 1.1 adam "rdnval: repaired=%d\n", nrepaired );
579 1.1 adam
580 1.1 adam return 0;
581 1.1 adam }
582 1.1 adam
583 1.1 adam /* search all entries without parentUUID; "repair" them */
584 1.1 adam static int
585 1.1 adam rdnval_db_open(
586 1.1 adam BackendDB *be,
587 1.1 adam ConfigReply *cr )
588 1.1 adam {
589 1.1 adam if ( SLAP_SINGLE_SHADOW( be ) ) {
590 1.3 christos Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
591 1.1 adam "rdnval incompatible with shadow database \"%s\".\n",
592 1.1 adam be->be_suffix[ 0 ].bv_val );
593 1.1 adam return 1;
594 1.1 adam }
595 1.1 adam
596 1.1 adam return rdnval_repair( be );
597 1.1 adam }
598 1.1 adam
599 1.1 adam static struct {
600 1.1 adam char *desc;
601 1.1 adam AttributeDescription **adp;
602 1.1 adam } as[] = {
603 1.1 adam { "( 1.3.6.1.4.1.4203.666.1.58 "
604 1.1 adam "NAME 'rdnValue' "
605 1.1 adam "DESC 'the value of the naming attributes' "
606 1.1 adam "SYNTAX '1.3.6.1.4.1.1466.115.121.1.15' "
607 1.1 adam "EQUALITY caseIgnoreMatch "
608 1.1 adam "USAGE dSAOperation "
609 1.1 adam "NO-USER-MODIFICATION "
610 1.1 adam ")",
611 1.1 adam &ad_rdnValue },
612 1.1 adam { NULL }
613 1.1 adam };
614 1.1 adam
615 1.1 adam int
616 1.1 adam rdnval_initialize(void)
617 1.1 adam {
618 1.1 adam int code, i;
619 1.1 adam
620 1.1 adam for ( i = 0; as[ i ].desc != NULL; i++ ) {
621 1.1 adam code = register_at( as[ i ].desc, as[ i ].adp, 0 );
622 1.1 adam if ( code ) {
623 1.1 adam Debug( LDAP_DEBUG_ANY,
624 1.1 adam "rdnval_initialize: register_at #%d failed\n",
625 1.3 christos i );
626 1.1 adam return code;
627 1.1 adam }
628 1.1 adam
629 1.1 adam /* Allow Manager to set these as needed */
630 1.1 adam if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) {
631 1.1 adam (*as[ i ].adp)->ad_type->sat_flags |=
632 1.1 adam SLAP_AT_MANAGEABLE;
633 1.1 adam }
634 1.1 adam }
635 1.1 adam
636 1.1 adam syn_IA5String = syn_find( "1.3.6.1.4.1.1466.115.121.1.26" );
637 1.1 adam if ( syn_IA5String == NULL ) {
638 1.1 adam Debug( LDAP_DEBUG_ANY,
639 1.3 christos "rdnval_initialize: unable to find syntax '1.3.6.1.4.1.1466.115.121.1.26' (IA5String)\n" );
640 1.1 adam return LDAP_OTHER;
641 1.1 adam }
642 1.1 adam
643 1.1 adam rdnval.on_bi.bi_type = "rdnval";
644 1.1 adam
645 1.1 adam rdnval.on_bi.bi_op_add = rdnval_op_add;
646 1.1 adam rdnval.on_bi.bi_op_modrdn = rdnval_op_rename;
647 1.1 adam
648 1.1 adam rdnval.on_bi.bi_db_init = rdnval_db_init;
649 1.1 adam rdnval.on_bi.bi_db_open = rdnval_db_open;
650 1.1 adam
651 1.1 adam return overlay_register( &rdnval );
652 1.1 adam }
653 1.1 adam
654 1.1 adam #if SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC
655 1.1 adam int
656 1.1 adam init_module( int argc, char *argv[] )
657 1.1 adam {
658 1.1 adam return rdnval_initialize();
659 1.1 adam }
660 1.1 adam #endif /* SLAPD_OVER_RDNVAL == SLAPD_MOD_DYNAMIC */
661 1.1 adam
662 1.1 adam #endif /* SLAPD_OVER_RDNVAL */
663