1 /* $NetBSD: pguid.c,v 1.3 2021/08/14 16:14:53 christos Exp $ */
2
3 /* pguid.c - Parent GUID value overlay */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6 *
7 * Copyright 1998-2021 The OpenLDAP Foundation.
8 * Portions Copyright 2008 Pierangelo Masarati.
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 the 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
21 * for inclusion in OpenLDAP Software.
22 */
23
24 #include <sys/cdefs.h>
25 __RCSID("$NetBSD: pguid.c,v 1.3 2021/08/14 16:14:53 christos Exp $");
26
27 #include "portable.h"
28
29 #ifdef SLAPD_OVER_PGUID
30
31 #include <stdio.h>
32
33 #include "ac/string.h"
34 #include "ac/socket.h"
35
36 #include "slap.h"
37 #include "slap-config.h"
38
39 #include "lutil.h"
40
41 /*
42 * Maintain an attribute (parentUUID) that contains the value
43 * of the entryUUID of the parent entry (used by Samba4)
44 */
45
46 static AttributeDescription *ad_parentUUID;
47
48 static slap_overinst pguid;
49
50 static int
pguid_op_add(Operation * op,SlapReply * rs)51 pguid_op_add( Operation *op, SlapReply *rs )
52 {
53 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
54
55 struct berval pdn, pndn;
56 Entry *e = NULL;
57 Attribute *a;
58 int rc;
59
60 /* don't care about suffix entry */
61 if ( dn_match( &op->o_req_ndn, &op->o_bd->be_nsuffix[0] ) ) {
62 return SLAP_CB_CONTINUE;
63 }
64
65 dnParent( &op->o_req_dn, &pdn );
66 dnParent( &op->o_req_ndn, &pndn );
67
68 rc = overlay_entry_get_ov( op, &pndn, NULL, slap_schema.si_ad_entryUUID, 0, &e, on );
69 if ( rc != LDAP_SUCCESS || e == NULL ) {
70 Debug( LDAP_DEBUG_ANY, "%s: pguid_op_add: unable to get parent entry DN=\"%s\" (%d)\n",
71 op->o_log_prefix, pdn.bv_val, rc );
72 return SLAP_CB_CONTINUE;
73 }
74
75 a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
76 if ( a == NULL ) {
77 Debug( LDAP_DEBUG_ANY, "%s: pguid_op_add: unable to find entryUUID of parent entry DN=\"%s\" (%d)\n",
78 op->o_log_prefix, pdn.bv_val, rc );
79
80 } else {
81 assert( a->a_numvals == 1 );
82
83 if ( op->ora_e != NULL ) {
84 attr_merge_one( op->ora_e, ad_parentUUID, &a->a_vals[0], a->a_nvals == a->a_vals ? NULL : &a->a_nvals[0] );
85
86 } else {
87 Modifications *ml;
88 Modifications *mod;
89
90 assert( op->ora_modlist != NULL );
91
92 for ( ml = op->ora_modlist; ml != NULL; ml = ml->sml_next ) {
93 if ( ml->sml_mod.sm_desc == slap_schema.si_ad_entryUUID ) {
94 break;
95 }
96 }
97
98 if ( ml == NULL ) {
99 ml = op->ora_modlist;
100 }
101
102 mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
103 mod->sml_flags = SLAP_MOD_INTERNAL;
104 mod->sml_op = LDAP_MOD_ADD;
105 mod->sml_desc = ad_parentUUID;
106 mod->sml_type = ad_parentUUID->ad_cname;
107 mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
108 mod->sml_nvalues = NULL;
109 mod->sml_numvals = 1;
110
111 ber_dupbv( &mod->sml_values[0], &a->a_vals[0] );
112 BER_BVZERO( &mod->sml_values[1] );
113
114 mod->sml_next = ml->sml_next;
115 ml->sml_next = mod;
116 }
117 }
118
119 if ( e != NULL ) {
120 (void)overlay_entry_release_ov( op, e, 0, on );
121 }
122
123 return SLAP_CB_CONTINUE;
124 }
125
126 static int
pguid_op_rename(Operation * op,SlapReply * rs)127 pguid_op_rename( Operation *op, SlapReply *rs )
128 {
129 slap_overinst *on = (slap_overinst *)op->o_bd->bd_info;
130
131 Entry *e = NULL;
132 Attribute *a;
133 int rc;
134
135 if ( op->orr_nnewSup == NULL ) {
136 return SLAP_CB_CONTINUE;
137 }
138
139 rc = overlay_entry_get_ov( op, op->orr_nnewSup, NULL, slap_schema.si_ad_entryUUID, 0, &e, on );
140 if ( rc != LDAP_SUCCESS || e == NULL ) {
141 Debug( LDAP_DEBUG_ANY, "%s: pguid_op_rename: unable to get newSuperior entry DN=\"%s\" (%d)\n",
142 op->o_log_prefix, op->orr_newSup->bv_val, rc );
143 return SLAP_CB_CONTINUE;
144 }
145
146 a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
147 if ( a == NULL ) {
148 Debug( LDAP_DEBUG_ANY, "%s: pguid_op_rename: unable to find entryUUID of newSuperior entry DN=\"%s\" (%d)\n",
149 op->o_log_prefix, op->orr_newSup->bv_val, rc );
150
151 } else {
152 Modifications *mod;
153
154 assert( a->a_numvals == 1 );
155
156 mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
157 mod->sml_flags = SLAP_MOD_INTERNAL;
158 mod->sml_op = LDAP_MOD_REPLACE;
159 mod->sml_desc = ad_parentUUID;
160 mod->sml_type = ad_parentUUID->ad_cname;
161 mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
162 mod->sml_nvalues = NULL;
163 mod->sml_numvals = 1;
164
165 ber_dupbv( &mod->sml_values[0], &a->a_vals[0] );
166 BER_BVZERO( &mod->sml_values[1] );
167
168 mod->sml_next = op->orr_modlist;
169 op->orr_modlist = mod;
170 }
171
172 if ( e != NULL ) {
173 (void)overlay_entry_release_ov( op, e, 0, on );
174 }
175
176 return SLAP_CB_CONTINUE;
177 }
178
179 static int
pguid_db_init(BackendDB * be,ConfigReply * cr)180 pguid_db_init(
181 BackendDB *be,
182 ConfigReply *cr)
183 {
184 if ( SLAP_ISGLOBALOVERLAY( be ) ) {
185 Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
186 "pguid_db_init: pguid cannot be used as global overlay.\n" );
187 return 1;
188 }
189
190 if ( be->be_nsuffix == NULL ) {
191 Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
192 "pguid_db_init: database must have suffix\n" );
193 return 1;
194 }
195
196 if ( BER_BVISNULL( &be->be_rootndn ) || BER_BVISEMPTY( &be->be_rootndn ) ) {
197 Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
198 "pguid_db_init: missing rootdn for database DN=\"%s\", YMMV\n",
199 be->be_suffix[ 0 ].bv_val );
200 }
201
202 return 0;
203 }
204
205 typedef struct pguid_mod_t {
206 struct berval ndn;
207 struct berval pguid;
208 struct pguid_mod_t *next;
209 } pguid_mod_t;
210
211 typedef struct {
212 slap_overinst *on;
213 pguid_mod_t *mods;
214 } pguid_repair_cb_t;
215
216 static int
pguid_repair_cb(Operation * op,SlapReply * rs)217 pguid_repair_cb( Operation *op, SlapReply *rs )
218 {
219 int rc;
220 pguid_repair_cb_t *pcb = op->o_callback->sc_private;
221 Entry *e = NULL;
222 Attribute *a;
223 struct berval pdn, pndn;
224
225 switch ( rs->sr_type ) {
226 case REP_SEARCH:
227 break;
228
229 case REP_SEARCHREF:
230 case REP_RESULT:
231 return rs->sr_err;
232
233 default:
234 assert( 0 );
235 }
236
237 assert( rs->sr_entry != NULL );
238
239 dnParent( &rs->sr_entry->e_name, &pdn );
240 dnParent( &rs->sr_entry->e_nname, &pndn );
241
242 rc = overlay_entry_get_ov( op, &pndn, NULL, slap_schema.si_ad_entryUUID, 0, &e, pcb->on );
243 if ( rc != LDAP_SUCCESS || e == NULL ) {
244 Debug( LDAP_DEBUG_ANY, "%s: pguid_repair_cb: unable to get parent entry DN=\"%s\" (%d)\n",
245 op->o_log_prefix, pdn.bv_val, rc );
246 return 0;
247 }
248
249 a = attr_find( e->e_attrs, slap_schema.si_ad_entryUUID );
250 if ( a == NULL ) {
251 Debug( LDAP_DEBUG_ANY, "%s: pguid_repair_cb: unable to find entryUUID of parent entry DN=\"%s\" (%d)\n",
252 op->o_log_prefix, pdn.bv_val, rc );
253
254 } else {
255 ber_len_t len;
256 pguid_mod_t *mod;
257
258 assert( a->a_numvals == 1 );
259
260 len = sizeof( pguid_mod_t ) + rs->sr_entry->e_nname.bv_len + 1 + a->a_vals[0].bv_len + 1;
261 mod = op->o_tmpalloc( len, op->o_tmpmemctx );
262 mod->ndn.bv_len = rs->sr_entry->e_nname.bv_len;
263 mod->ndn.bv_val = (char *)&mod[1];
264 mod->pguid.bv_len = a->a_vals[0].bv_len;
265 mod->pguid.bv_val = (char *)&mod->ndn.bv_val[mod->ndn.bv_len + 1];
266 lutil_strncopy( mod->ndn.bv_val, rs->sr_entry->e_nname.bv_val, rs->sr_entry->e_nname.bv_len );
267 lutil_strncopy( mod->pguid.bv_val, a->a_vals[0].bv_val, a->a_vals[0].bv_len );
268
269 mod->next = pcb->mods;
270 pcb->mods = mod;
271
272 Debug( LDAP_DEBUG_TRACE, "%s: pguid_repair_cb: scheduling entry DN=\"%s\" for repair\n",
273 op->o_log_prefix, rs->sr_entry->e_name.bv_val );
274 }
275
276 if ( e != NULL ) {
277 (void)overlay_entry_release_ov( op, e, 0, pcb->on );
278 }
279
280 return 0;
281 }
282
283 static int
pguid_repair(BackendDB * be)284 pguid_repair( BackendDB *be )
285 {
286 slap_overinst *on = (slap_overinst *)be->bd_info;
287 void *ctx = ldap_pvt_thread_pool_context();
288 Connection conn = { 0 };
289 OperationBuffer opbuf;
290 Operation *op;
291 slap_callback sc = { 0 };
292 pguid_repair_cb_t pcb = { 0 };
293 SlapReply rs = { REP_RESULT };
294 pguid_mod_t *pmod;
295 int nrepaired = 0;
296
297 connection_fake_init2( &conn, &opbuf, ctx, 0 );
298 op = &opbuf.ob_op;
299
300 op->o_tag = LDAP_REQ_SEARCH;
301 memset( &op->oq_search, 0, sizeof( op->oq_search ) );
302
303 op->o_bd = select_backend( &be->be_nsuffix[ 0 ], 0 );
304
305 op->o_req_dn = op->o_bd->be_suffix[ 0 ];
306 op->o_req_ndn = op->o_bd->be_nsuffix[ 0 ];
307
308 op->o_dn = op->o_bd->be_rootdn;
309 op->o_ndn = op->o_bd->be_rootndn;
310
311 op->ors_scope = LDAP_SCOPE_SUBORDINATE;
312 op->ors_tlimit = SLAP_NO_LIMIT;
313 op->ors_slimit = SLAP_NO_LIMIT;
314 op->ors_attrs = slap_anlist_no_attrs;
315
316 op->ors_filterstr.bv_len = STRLENOF( "(!(=*))" ) + ad_parentUUID->ad_cname.bv_len;
317 op->ors_filterstr.bv_val = op->o_tmpalloc( op->ors_filterstr.bv_len + 1, op->o_tmpmemctx );
318 snprintf( op->ors_filterstr.bv_val, op->ors_filterstr.bv_len + 1,
319 "(!(%s=*))", ad_parentUUID->ad_cname.bv_val );
320
321 op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
322 if ( op->ors_filter == NULL ) {
323 rs.sr_err = LDAP_OTHER;
324 goto done_search;
325 }
326
327 op->o_callback = ≻
328 sc.sc_response = pguid_repair_cb;
329 sc.sc_private = &pcb;
330 pcb.on = on;
331
332 (void)op->o_bd->bd_info->bi_op_search( op, &rs );
333
334 op->o_tag = LDAP_REQ_MODIFY;
335 sc.sc_response = slap_null_cb;
336 sc.sc_private = NULL;
337 memset( &op->oq_modify, 0, sizeof( req_modify_s ) );
338
339 for ( pmod = pcb.mods; pmod != NULL; ) {
340 pguid_mod_t *pnext;
341
342 Modifications *mod;
343 SlapReply rs2 = { REP_RESULT };
344
345 mod = (Modifications *) ch_malloc( sizeof( Modifications ) );
346 mod->sml_flags = SLAP_MOD_INTERNAL;
347 mod->sml_op = LDAP_MOD_REPLACE;
348 mod->sml_desc = ad_parentUUID;
349 mod->sml_type = ad_parentUUID->ad_cname;
350 mod->sml_values = ch_malloc( sizeof( struct berval ) * 2 );
351 mod->sml_nvalues = NULL;
352 mod->sml_numvals = 1;
353 mod->sml_next = NULL;
354
355 ber_dupbv( &mod->sml_values[0], &pmod->pguid );
356 BER_BVZERO( &mod->sml_values[1] );
357
358 op->o_req_dn = pmod->ndn;
359 op->o_req_ndn = pmod->ndn;
360
361 op->orm_modlist = mod;
362 op->o_bd->be_modify( op, &rs2 );
363 slap_mods_free( op->orm_modlist, 1 );
364 if ( rs2.sr_err == LDAP_SUCCESS ) {
365 Debug( LDAP_DEBUG_TRACE, "%s: pguid_repair: entry DN=\"%s\" repaired\n",
366 op->o_log_prefix, pmod->ndn.bv_val );
367 nrepaired++;
368
369 } else {
370 Debug( LDAP_DEBUG_ANY, "%s: pguid_repair: entry DN=\"%s\" repair failed (%d)\n",
371 op->o_log_prefix, pmod->ndn.bv_val, rs2.sr_err );
372 }
373
374 pnext = pmod->next;
375 op->o_tmpfree( pmod, op->o_tmpmemctx );
376 pmod = pnext;
377 }
378
379 done_search:;
380 op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
381 filter_free_x( op, op->ors_filter, 1 );
382
383 Log( LDAP_DEBUG_STATS, LDAP_LEVEL_INFO,
384 "pguid: repaired=%d\n", nrepaired );
385
386 return rs.sr_err;
387 }
388
389 /* search all entries without parentUUID; "repair" them */
390 static int
pguid_db_open(BackendDB * be,ConfigReply * cr)391 pguid_db_open(
392 BackendDB *be,
393 ConfigReply *cr )
394 {
395 if ( SLAP_SINGLE_SHADOW( be ) ) {
396 Log( LDAP_DEBUG_ANY, LDAP_LEVEL_ERR,
397 "pguid incompatible with shadow database \"%s\".\n",
398 be->be_suffix[ 0 ].bv_val );
399 return 1;
400 }
401
402 pguid_repair( be );
403
404 return 0;
405 }
406
407 static struct {
408 char *desc;
409 AttributeDescription **adp;
410 } as[] = {
411 { "( 1.3.6.1.4.1.4203.666.1.59 "
412 "NAME 'parentUUID' "
413 "DESC 'the value of the entryUUID of the parent' "
414 "EQUALITY UUIDMatch "
415 "ORDERING UUIDOrderingMatch "
416 "SYNTAX 1.3.6.1.1.16.1 "
417 "USAGE dSAOperation "
418 "SINGLE-VALUE "
419 "NO-USER-MODIFICATION "
420 ")",
421 &ad_parentUUID },
422 { NULL }
423 };
424
425 int
pguid_initialize(void)426 pguid_initialize(void)
427 {
428 int code, i;
429
430 for ( i = 0; as[ i ].desc != NULL; i++ ) {
431 code = register_at( as[ i ].desc, as[ i ].adp, 0 );
432 if ( code ) {
433 Debug( LDAP_DEBUG_ANY,
434 "pguid_initialize: register_at #%d failed\n",
435 i );
436 return code;
437 }
438
439 /* Allow Manager to set these as needed */
440 if ( is_at_no_user_mod( (*as[ i ].adp)->ad_type ) ) {
441 (*as[ i ].adp)->ad_type->sat_flags |=
442 SLAP_AT_MANAGEABLE;
443 }
444 }
445
446 pguid.on_bi.bi_type = "pguid";
447
448 pguid.on_bi.bi_op_add = pguid_op_add;
449 pguid.on_bi.bi_op_modrdn = pguid_op_rename;
450
451 pguid.on_bi.bi_db_init = pguid_db_init;
452 pguid.on_bi.bi_db_open = pguid_db_open;
453
454 return overlay_register( &pguid );
455 }
456
457 #if SLAPD_OVER_PGUID == SLAPD_MOD_DYNAMIC
458 int
init_module(int argc,char * argv[])459 init_module( int argc, char *argv[] )
460 {
461 return pguid_initialize();
462 }
463 #endif /* SLAPD_OVER_PGUID == SLAPD_MOD_DYNAMIC */
464
465 #endif /* SLAPD_OVER_PGUID */
466