1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2021 The OpenLDAP Foundation.
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted only as authorized by the OpenLDAP
9  * Public License.
10  *
11  * A copy of this license is available in the file LICENSE in the
12  * top-level directory of the distribution or, alternatively, at
13  * <http://www.OpenLDAP.org/license.html>.
14  */
15 
16 /*
17  * LDAPv3 Extended Operation Request
18  *	ExtendedRequest ::= [APPLICATION 23] SEQUENCE {
19  *		requestName	 [0] LDAPOID,
20  *		requestValue	 [1] OCTET STRING OPTIONAL
21  *	}
22  *
23  * LDAPv3 Extended Operation Response
24  *	ExtendedResponse ::= [APPLICATION 24] SEQUENCE {
25  *		COMPONENTS OF LDAPResult,
26  *		responseName	 [10] LDAPOID OPTIONAL,
27  *		response	 [11] OCTET STRING OPTIONAL
28  *	}
29  *
30  */
31 
32 #include "portable.h"
33 
34 #include <stdio.h>
35 
36 #include <ac/socket.h>
37 #include <ac/string.h>
38 
39 #include "slap.h"
40 #include "lber_pvt.h"
41 
42 static struct extop_list {
43 	struct extop_list *next;
44 	struct berval oid;
45 	slap_mask_t flags;
46 	SLAP_EXTOP_MAIN_FN *ext_main;
47 } *supp_ext_list = NULL;
48 
49 static SLAP_EXTOP_MAIN_FN whoami_extop;
50 
51 /* This list of built-in extops is for extops that are not part
52  * of backends or in external modules.	Essentially, this is
53  * just a way to get built-in extops onto the extop list without
54  * having a separate init routine for each built-in extop.
55  */
56 static struct {
57 	const struct berval *oid;
58 	slap_mask_t flags;
59 	SLAP_EXTOP_MAIN_FN *ext_main;
60 } builtin_extops[] = {
61 	{ &slap_EXOP_TXN_START, 0, txn_start_extop },
62 	{ &slap_EXOP_TXN_END, 0, txn_end_extop },
63 	{ &slap_EXOP_CANCEL, 0, cancel_extop },
64 	{ &slap_EXOP_WHOAMI, 0, whoami_extop },
65 	{ &slap_EXOP_MODIFY_PASSWD, SLAP_EXOP_WRITES, passwd_extop },
66 	{ NULL, 0, NULL }
67 };
68 
69 
70 static struct extop_list *find_extop(
71 	struct extop_list *list, struct berval *oid );
72 
73 struct berval *
get_supported_extop(int index)74 get_supported_extop (int index)
75 {
76 	struct extop_list *ext;
77 
78 	/* linear scan is slow, but this way doesn't force a
79 	 * big change on root_dse.c, where this routine is used.
80 	 */
81 	for (ext = supp_ext_list; ext != NULL && --index >= 0; ext = ext->next) {
82 		; /* empty */
83 	}
84 
85 	if (ext == NULL) return NULL;
86 
87 	return &ext->oid;
88 }
89 
90 
exop_root_dse_info(Entry * e)91 int exop_root_dse_info( Entry *e )
92 {
93 	AttributeDescription *ad_supportedExtension
94 		= slap_schema.si_ad_supportedExtension;
95 	struct berval vals[2];
96 	struct extop_list *ext;
97 
98 	vals[1].bv_val = NULL;
99 	vals[1].bv_len = 0;
100 
101 	for (ext = supp_ext_list; ext != NULL; ext = ext->next) {
102 		if( ext->flags & SLAP_EXOP_HIDE ) continue;
103 
104 		vals[0] = ext->oid;
105 
106 		if( attr_merge( e, ad_supportedExtension, vals, NULL ) ) {
107 			return LDAP_OTHER;
108 		}
109 	}
110 
111 	return LDAP_SUCCESS;
112 }
113 
114 int
do_extended(Operation * op,SlapReply * rs)115 do_extended(
116     Operation	*op,
117     SlapReply	*rs
118 )
119 {
120 	struct berval reqdata = {0, NULL};
121 	ber_len_t len;
122 
123 	Debug( LDAP_DEBUG_TRACE, "%s do_extended\n",
124 		op->o_log_prefix );
125 
126 	if( op->o_protocol < LDAP_VERSION3 ) {
127 		Debug( LDAP_DEBUG_ANY, "%s do_extended: protocol version (%d) too low\n",
128 			op->o_log_prefix, op->o_protocol );
129 		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "requires LDAPv3" );
130 		rs->sr_err = SLAPD_DISCONNECT;
131 		goto done;
132 	}
133 
134 	if ( ber_scanf( op->o_ber, "{m" /*}*/, &op->ore_reqoid ) == LBER_ERROR ) {
135 		Debug( LDAP_DEBUG_ANY, "%s do_extended: ber_scanf failed\n",
136 			op->o_log_prefix );
137 		send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
138 		rs->sr_err = SLAPD_DISCONNECT;
139 		goto done;
140 	}
141 
142 	if( ber_peek_tag( op->o_ber, &len ) == LDAP_TAG_EXOP_REQ_VALUE ) {
143 		if( ber_scanf( op->o_ber, "m", &reqdata ) == LBER_ERROR ) {
144 			Debug( LDAP_DEBUG_ANY, "%s do_extended: ber_scanf failed\n",
145 				op->o_log_prefix );
146 			send_ldap_discon( op, rs, LDAP_PROTOCOL_ERROR, "decoding error" );
147 			rs->sr_err = SLAPD_DISCONNECT;
148 			goto done;
149 		}
150 	}
151 
152 	if( get_ctrls( op, rs, 1 ) != LDAP_SUCCESS ) {
153 		Debug( LDAP_DEBUG_ANY, "%s do_extended: get_ctrls failed\n",
154 			op->o_log_prefix );
155 		return rs->sr_err;
156 	}
157 
158 	Debug( LDAP_DEBUG_STATS, "%s EXT oid=%s\n",
159 	    op->o_log_prefix, op->ore_reqoid.bv_val );
160 
161 	/* check for controls inappropriate for all extended operations */
162 	if( get_manageDSAit( op ) == SLAP_CONTROL_CRITICAL ) {
163 		send_ldap_error( op, rs,
164 			LDAP_UNAVAILABLE_CRITICAL_EXTENSION,
165 			"manageDSAit control inappropriate" );
166 		goto done;
167 	}
168 
169 	/* FIXME: temporary? */
170 	if ( reqdata.bv_val ) {
171 		op->ore_reqdata = &reqdata;
172 	}
173 
174 	op->o_bd = frontendDB;
175 	rs->sr_err = frontendDB->be_extended( op, rs );
176 
177 	if ( rs->sr_err == SLAPD_ASYNCOP ||
178 		rs->sr_err == LDAP_TXN_SPECIFY_OKAY ) {
179 		/* skip cleanup */
180 		return rs->sr_err;
181 	}
182 
183 	/* clean up in case some overlay set them? */
184 	if ( !BER_BVISNULL( &op->o_req_ndn ) ) {
185 		if ( !BER_BVISNULL( &op->o_req_dn )
186 			&& op->o_req_ndn.bv_val != op->o_req_dn.bv_val )
187 		{
188 			op->o_tmpfree( op->o_req_dn.bv_val, op->o_tmpmemctx );
189 		}
190 		op->o_tmpfree( op->o_req_ndn.bv_val, op->o_tmpmemctx );
191 		BER_BVZERO( &op->o_req_dn );
192 		BER_BVZERO( &op->o_req_ndn );
193 	}
194 
195 done:
196 	return rs->sr_err;
197 }
198 
199 int
fe_extended(Operation * op,SlapReply * rs)200 fe_extended( Operation *op, SlapReply *rs )
201 {
202 	struct extop_list	*ext = NULL;
203 	struct berval		reqdata = BER_BVNULL;
204 
205 	if (op->ore_reqdata) {
206 		reqdata = *op->ore_reqdata;
207 	}
208 
209 	ext = find_extop(supp_ext_list, &op->ore_reqoid );
210 	if ( ext == NULL ) {
211 		Debug( LDAP_DEBUG_ANY, "%s do_extended: unsupported operation \"%s\"\n",
212 			op->o_log_prefix, op->ore_reqoid.bv_val );
213 		send_ldap_error( op, rs, LDAP_PROTOCOL_ERROR,
214 			"unsupported extended operation" );
215 		goto done;
216 	}
217 
218 	op->ore_flags = ext->flags;
219 
220 	Debug( LDAP_DEBUG_ARGS, "do_extended: oid=%s\n",
221 		op->ore_reqoid.bv_val );
222 
223 	{ /* start of OpenLDAP extended operation */
224 		BackendDB	*bd = op->o_bd;
225 
226 		rs->sr_err = (ext->ext_main)( op, rs );
227 
228 		if( rs->sr_err != SLAPD_ABANDON ) {
229 			if ( rs->sr_err == LDAP_REFERRAL && rs->sr_ref == NULL ) {
230 				rs->sr_ref = referral_rewrite( default_referral,
231 					NULL, NULL, LDAP_SCOPE_DEFAULT );
232 				if ( !rs->sr_ref ) rs->sr_ref = default_referral;
233 				if ( !rs->sr_ref ) {
234 					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
235 					rs->sr_text = "referral missing";
236 				}
237 			}
238 
239 			if ( op->o_bd == NULL )
240 				op->o_bd = bd;
241 			send_ldap_extended( op, rs );
242 
243 			if ( rs->sr_ref != default_referral ) {
244 				ber_bvarray_free( rs->sr_ref );
245 				rs->sr_ref = NULL;
246 			}
247 		}
248 
249 		if ( rs->sr_rspoid != NULL ) {
250 			free( (char *)rs->sr_rspoid );
251 			rs->sr_rspoid = NULL;
252 		}
253 
254 		if ( rs->sr_rspdata != NULL ) {
255 			ber_bvfree( rs->sr_rspdata );
256 			rs->sr_rspdata = NULL;
257 		}
258 	} /* end of OpenLDAP extended operation */
259 
260 done:;
261 	return rs->sr_err;
262 }
263 
264 int
load_extop2(const struct berval * ext_oid,slap_mask_t ext_flags,SLAP_EXTOP_MAIN_FN * ext_main,unsigned flags)265 load_extop2(
266 	const struct berval *ext_oid,
267 	slap_mask_t ext_flags,
268 	SLAP_EXTOP_MAIN_FN *ext_main,
269 	unsigned flags )
270 {
271 	struct berval		oidm = BER_BVNULL;
272 	struct extop_list	*ext;
273 	int			insertme = 0;
274 
275 	if ( !ext_main ) {
276 		return -1;
277 	}
278 
279 	if ( ext_oid == NULL || BER_BVISNULL( ext_oid ) ||
280 		BER_BVISEMPTY( ext_oid ) )
281 	{
282 		return -1;
283 	}
284 
285 	if ( numericoidValidate( NULL, (struct berval *)ext_oid ) !=
286 		LDAP_SUCCESS )
287 	{
288 		oidm.bv_val = oidm_find( ext_oid->bv_val );
289 		if ( oidm.bv_val == NULL ) {
290 			return -1;
291 		}
292 		oidm.bv_len = strlen( oidm.bv_val );
293 		ext_oid = &oidm;
294 	}
295 
296 	for ( ext = supp_ext_list; ext; ext = ext->next ) {
297 		if ( bvmatch( ext_oid, &ext->oid ) ) {
298 			if ( flags == 1 ) {
299 				break;
300 			}
301 			return -1;
302 		}
303 	}
304 
305 	if ( flags == 0 || ext == NULL ) {
306 		ext = ch_calloc( 1, sizeof(struct extop_list) + ext_oid->bv_len + 1 );
307 		if ( ext == NULL ) {
308 			return(-1);
309 		}
310 
311 		ext->oid.bv_val = (char *)(ext + 1);
312 		AC_MEMCPY( ext->oid.bv_val, ext_oid->bv_val, ext_oid->bv_len );
313 		ext->oid.bv_len = ext_oid->bv_len;
314 		ext->oid.bv_val[ext->oid.bv_len] = '\0';
315 
316 		insertme = 1;
317 	}
318 
319 	ext->flags = ext_flags;
320 	ext->ext_main = ext_main;
321 
322 	if ( insertme ) {
323 		ext->next = supp_ext_list;
324 		supp_ext_list = ext;
325 	}
326 
327 	return(0);
328 }
329 
330 int
unload_extop(const struct berval * ext_oid,SLAP_EXTOP_MAIN_FN * ext_main,unsigned flags)331 unload_extop(
332 	const struct berval *ext_oid,
333 	SLAP_EXTOP_MAIN_FN *ext_main,
334 	unsigned flags )
335 {
336 	struct berval		oidm = BER_BVNULL;
337 	struct extop_list	*ext, **extp;
338 
339 	/* oid must be given */
340 	if ( ext_oid == NULL || BER_BVISNULL( ext_oid ) ||
341 		BER_BVISEMPTY( ext_oid ) )
342 	{
343 		return -1;
344 	}
345 
346 	/* if it's not an oid, check if it's a macto */
347 	if ( numericoidValidate( NULL, (struct berval *)ext_oid ) !=
348 		LDAP_SUCCESS )
349 	{
350 		oidm.bv_val = oidm_find( ext_oid->bv_val );
351 		if ( oidm.bv_val == NULL ) {
352 			return -1;
353 		}
354 		oidm.bv_len = strlen( oidm.bv_val );
355 		ext_oid = &oidm;
356 	}
357 
358 	/* lookup the oid */
359 	for ( extp = &supp_ext_list; *extp; extp = &(*extp)->next ) {
360 		if ( bvmatch( ext_oid, &(*extp)->oid ) ) {
361 			/* if ext_main is given, only remove if it matches */
362 			if ( ext_main != NULL && (*extp)->ext_main != ext_main ) {
363 				return -1;
364 			}
365 			break;
366 		}
367 	}
368 
369 	if ( *extp == NULL ) {
370 		return -1;
371 	}
372 
373 	ext = *extp;
374 	*extp = (*extp)->next;
375 
376 	ch_free( ext );
377 
378 	return 0;
379 }
380 
381 int
extops_init(void)382 extops_init (void)
383 {
384 	int i;
385 
386 	for ( i = 0; builtin_extops[i].oid != NULL; i++ ) {
387 		load_extop( (struct berval *)builtin_extops[i].oid,
388 			builtin_extops[i].flags,
389 			builtin_extops[i].ext_main );
390 	}
391 
392 	return(0);
393 }
394 
395 int
extops_kill(void)396 extops_kill (void)
397 {
398 	struct extop_list *ext;
399 
400 	/* we allocated the memory, so we have to free it, too. */
401 	while ((ext = supp_ext_list) != NULL) {
402 		supp_ext_list = ext->next;
403 		ch_free(ext);
404 	}
405 	return(0);
406 }
407 
408 static struct extop_list *
find_extop(struct extop_list * list,struct berval * oid)409 find_extop( struct extop_list *list, struct berval *oid )
410 {
411 	struct extop_list *ext;
412 
413 	for (ext = list; ext; ext = ext->next) {
414 		if (bvmatch(&ext->oid, oid))
415 			return(ext);
416 	}
417 	return(NULL);
418 }
419 
420 
421 const struct berval slap_EXOP_WHOAMI = BER_BVC(LDAP_EXOP_WHO_AM_I);
422 
423 static int
whoami_extop(Operation * op,SlapReply * rs)424 whoami_extop (
425 	Operation *op,
426 	SlapReply *rs )
427 {
428 	struct berval *bv;
429 
430 	if ( op->ore_reqdata != NULL ) {
431 		/* no request data should be provided */
432 		rs->sr_text = "no request data expected";
433 		return LDAP_PROTOCOL_ERROR;
434 	}
435 
436 	Debug( LDAP_DEBUG_STATS, "%s WHOAMI\n",
437 	    op->o_log_prefix );
438 
439 	op->o_bd = op->o_conn->c_authz_backend;
440 	if( backend_check_restrictions( op, rs,
441 		(struct berval *)&slap_EXOP_WHOAMI ) != LDAP_SUCCESS )
442 	{
443 		return rs->sr_err;
444 	}
445 
446 	bv = (struct berval *) ch_malloc( sizeof(struct berval) );
447 	if( op->o_dn.bv_len ) {
448 		bv->bv_len = op->o_dn.bv_len + STRLENOF( "dn:" );
449 		bv->bv_val = ch_malloc( bv->bv_len + 1 );
450 		AC_MEMCPY( bv->bv_val, "dn:", STRLENOF( "dn:" ) );
451 		AC_MEMCPY( &bv->bv_val[STRLENOF( "dn:" )], op->o_dn.bv_val,
452 			op->o_dn.bv_len );
453 		bv->bv_val[bv->bv_len] = '\0';
454 
455 	} else {
456 		bv->bv_len = 0;
457 		bv->bv_val = NULL;
458 	}
459 
460 	rs->sr_rspdata = bv;
461 	return LDAP_SUCCESS;
462 }
463