xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/vc/vc.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: vc.c,v 1.2 2021/08/14 16:14:54 christos Exp $	*/
2 
3 /* vc.c - LDAP Verify Credentials extop (no spec yet) */
4 /* $OpenLDAP$ */
5 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
6  *
7  * Copyright 2010-2021 The OpenLDAP Foundation.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Pierangelo Masarati for inclusion
20  * in OpenLDAP Software.
21  */
22 
23 /*
24  * LDAP Verify Credentials: suggested by Kurt Zeilenga
25  * no spec yet
26  */
27 
28 #include <sys/cdefs.h>
29 __RCSID("$NetBSD: vc.c,v 1.2 2021/08/14 16:14:54 christos Exp $");
30 
31 #include "portable.h"
32 
33 #include "slap.h"
34 #include "ac/string.h"
35 
36 typedef struct vc_conn_t {
37 	struct vc_conn_t *conn;
38 	Connection connbuf;
39 	OperationBuffer opbuf;
40 	Operation *op;
41 	int refcnt;
42 } vc_conn_t;
43 
44 static const struct berval vc_exop_oid_bv = BER_BVC(LDAP_EXOP_VERIFY_CREDENTIALS);
45 static ldap_pvt_thread_mutex_t vc_mutex;
46 static Avlnode *vc_tree;
47 
48 static int
vc_conn_cmp(const void * c1,const void * c2)49 vc_conn_cmp( const void *c1, const void *c2 )
50 {
51 	const vc_conn_t *vc1 = (const vc_conn_t *)c1;
52 	const vc_conn_t *vc2 = (const vc_conn_t *)c2;
53 
54 	return SLAP_PTRCMP( vc1->conn, vc2->conn );
55 }
56 
57 static int
vc_conn_dup(void * c1,void * c2)58 vc_conn_dup( void *c1, void *c2 )
59 {
60 	vc_conn_t *vc1 = (vc_conn_t *)c1;
61 	vc_conn_t *vc2 = (vc_conn_t *)c2;
62 
63 	if ( vc1->conn == vc2->conn ) {
64 		return -1;
65 	}
66 
67 	return 0;
68 }
69 
70 static int
vc_create_response(void * conn,int resultCode,const char * diagnosticMessage,struct berval * servercred,struct berval * authzid,LDAPControl ** ctrls,struct berval ** val)71 vc_create_response(
72 	void *conn,
73 	int resultCode,
74 	const char *diagnosticMessage,
75 	struct berval *servercred,
76 	struct berval *authzid,
77 	LDAPControl **ctrls,
78 	struct berval **val )
79 {
80 	BerElementBuffer berbuf;
81 	BerElement *ber = (BerElement *)&berbuf;
82 	struct berval bv;
83 	int rc;
84 
85 	assert( val != NULL );
86 
87 	*val = NULL;
88 
89 	ber_init2( ber, NULL, LBER_USE_DER );
90 
91 	(void)ber_printf( ber, "{is" /*}*/ , resultCode, diagnosticMessage ? diagnosticMessage : "" );
92 
93 	if ( conn ) {
94 		struct berval cookie;
95 
96 		cookie.bv_len = sizeof( conn );
97 		cookie.bv_val = (char *)&conn;
98 		(void)ber_printf( ber, "tO", 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE, &cookie );
99 	}
100 
101 	if ( servercred ) {
102 		ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_SCREDS, servercred );
103 	}
104 
105 #if 0
106 	if ( authzid ) {
107 		ber_printf( ber, "tO", LDAP_TAG_EXOP_VERIFY_CREDENTIALS_AUTHZID, authzid );
108 	}
109 #endif
110 
111 	if ( ctrls ) {
112 		int c;
113 
114 		rc = ber_printf( ber, "t{"/*}*/, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS );
115 		if ( rc == -1 ) goto done;
116 
117 		for ( c = 0; ctrls[c] != NULL; c++ ) {
118 			rc = ber_printf( ber, "{s" /*}*/, ctrls[c]->ldctl_oid );
119 
120 			if ( ctrls[c]->ldctl_iscritical ) {
121 				rc = ber_printf( ber, "b", (ber_int_t)ctrls[c]->ldctl_iscritical ) ;
122 				if ( rc == -1 ) goto done;
123 			}
124 
125 			if ( ctrls[c]->ldctl_value.bv_val != NULL ) {
126 				rc = ber_printf( ber, "O", &ctrls[c]->ldctl_value );
127 				if( rc == -1 ) goto done;
128 			}
129 
130 			rc = ber_printf( ber, /*{*/"N}" );
131 			if ( rc == -1 ) goto done;
132 		}
133 
134 		rc = ber_printf( ber, /*{*/"N}" );
135 		if ( rc == -1 ) goto done;
136 	}
137 
138 	rc = ber_printf( ber, /*{*/ "}" );
139 	if ( rc == -1 ) goto done;
140 
141 	rc = ber_flatten2( ber, &bv, 0 );
142 	if ( rc == 0 ) {
143 		*val = ber_bvdup( &bv );
144 	}
145 
146 done:;
147 	ber_free_buf( ber );
148 
149 	return rc;
150 }
151 
152 typedef struct vc_cb_t {
153 	struct berval sasldata;
154 	LDAPControl **ctrls;
155 } vc_cb_t;
156 
157 static int
vc_cb(Operation * op,SlapReply * rs)158 vc_cb(
159 	Operation	*op,
160 	SlapReply	*rs )
161 {
162 	vc_cb_t *vc = (vc_cb_t *)op->o_callback->sc_private;
163 
164 	if ( rs->sr_tag == LDAP_RES_BIND ) {
165 		if ( rs->sr_sasldata != NULL ) {
166 			ber_dupbv( &vc->sasldata, rs->sr_sasldata );
167 		}
168 
169 		if ( rs->sr_ctrls != NULL ) {
170 			vc->ctrls = ldap_controls_dup( rs->sr_ctrls );
171 		}
172 	}
173 
174 	return 0;
175 }
176 
177 static int
vc_exop(Operation * op,SlapReply * rs)178 vc_exop(
179 	Operation	*op,
180 	SlapReply	*rs )
181 {
182 	int rc = LDAP_SUCCESS;
183 	ber_tag_t tag;
184 	ber_len_t len = -1;
185 	BerElementBuffer berbuf;
186 	BerElement *ber = (BerElement *)&berbuf;
187 	struct berval reqdata = BER_BVNULL;
188 
189 	struct berval cookie = BER_BVNULL;
190 	struct berval bdn = BER_BVNULL;
191 	ber_tag_t authtag;
192 	struct berval cred = BER_BVNULL;
193 	struct berval ndn = BER_BVNULL;
194 	struct berval mechanism = BER_BVNULL;
195 
196 	vc_conn_t *conn = NULL;
197 	vc_cb_t vc = { 0 };
198 	slap_callback sc = { 0 };
199 	SlapReply rs2 = { 0 };
200 
201 	if ( op->ore_reqdata == NULL || op->ore_reqdata->bv_len == 0 ) {
202 		rs->sr_text = "empty request data field in VerifyCredentials exop";
203 		return LDAP_PROTOCOL_ERROR;
204 	}
205 
206 	/* optimistic */
207 	rs->sr_err = LDAP_SUCCESS;
208 
209 	ber_dupbv_x( &reqdata, op->ore_reqdata, op->o_tmpmemctx );
210 
211 	/* ber_init2 uses reqdata directly, doesn't allocate new buffers */
212 	ber_init2( ber, &reqdata, 0 );
213 
214 	tag = ber_scanf( ber, "{" /*}*/ );
215 	if ( tag != LBER_SEQUENCE ) {
216 		rs->sr_err = LDAP_PROTOCOL_ERROR;
217 		goto done;
218 	}
219 
220 	tag = ber_peek_tag( ber, &len );
221 	if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_COOKIE ) {
222 		/*
223 		 * cookie: the pointer to the connection
224 		 * of this operation
225 		 */
226 
227 		ber_scanf( ber, "m", &cookie );
228 		if ( cookie.bv_len != sizeof(Connection *) ) {
229 			rs->sr_err = LDAP_PROTOCOL_ERROR;
230 			goto done;
231 		}
232 	}
233 
234 	/* DN, authtag */
235 	tag = ber_scanf( ber, "mt", &bdn, &authtag );
236 	if ( tag == LBER_ERROR ) {
237 		rs->sr_err = LDAP_PROTOCOL_ERROR;
238 		goto done;
239 	}
240 
241 	rc = dnNormalize( 0, NULL, NULL, &bdn, &ndn, op->o_tmpmemctx );
242 	if ( rc != LDAP_SUCCESS ) {
243 		rs->sr_err = LDAP_PROTOCOL_ERROR;
244 		goto done;
245 	}
246 
247 	switch ( authtag ) {
248 	case LDAP_AUTH_SIMPLE:
249 		/* cookie only makes sense for SASL bind (so far) */
250 		if ( !BER_BVISNULL( &cookie ) ) {
251 			rs->sr_err = LDAP_PROTOCOL_ERROR;
252 			goto done;
253 		}
254 
255 		tag = ber_scanf( ber, "m", &cred );
256 		if ( tag == LBER_ERROR ) {
257 			rs->sr_err = LDAP_PROTOCOL_ERROR;
258 			goto done;
259 		}
260 		break;
261 
262 	case LDAP_AUTH_SASL:
263 		tag = ber_scanf( ber, "{m" /*}*/ , &mechanism );
264 		if ( tag == LBER_ERROR ||
265 			BER_BVISNULL( &mechanism ) || BER_BVISEMPTY( &mechanism ) )
266 		{
267 			rs->sr_err = LDAP_PROTOCOL_ERROR;
268 			goto done;
269 		}
270 
271 		tag = ber_peek_tag( ber, &len );
272 		if ( tag == LBER_OCTETSTRING ) {
273 			ber_scanf( ber, "m", &cred );
274 		}
275 
276 		tag = ber_scanf( ber, /*{*/ "}" );
277 		break;
278 
279 	default:
280 		rs->sr_err = LDAP_PROTOCOL_ERROR;
281 		goto done;
282 	}
283 
284 	if ( !BER_BVISNULL( &cookie ) ) {
285 		vc_conn_t tmp = { 0 };
286 
287 		AC_MEMCPY( (char *)&tmp.conn, (const char *)cookie.bv_val, cookie.bv_len );
288 		ldap_pvt_thread_mutex_lock( &vc_mutex );
289 		conn = (vc_conn_t *)ldap_avl_find( vc_tree, (caddr_t)&tmp, vc_conn_cmp );
290 		if ( conn == NULL || ( conn != NULL && conn->refcnt != 0 ) ) {
291 			conn = NULL;
292 			ldap_pvt_thread_mutex_unlock( &vc_mutex );
293 			rs->sr_err = LDAP_PROTOCOL_ERROR;
294 			goto done;
295 		}
296 		conn->refcnt++;
297 		ldap_pvt_thread_mutex_unlock( &vc_mutex );
298 
299 	} else {
300 		void *thrctx;
301 
302 		conn = (vc_conn_t *)SLAP_CALLOC( 1, sizeof( vc_conn_t ) );
303 		conn->refcnt = 1;
304 
305 		thrctx = ldap_pvt_thread_pool_context();
306 		connection_fake_init2( &conn->connbuf, &conn->opbuf, thrctx, 0 );
307 		conn->op = &conn->opbuf.ob_op;
308 		snprintf( conn->op->o_log_prefix, sizeof( conn->op->o_log_prefix ),
309 			"%s VERIFYCREDENTIALS", op->o_log_prefix );
310 	}
311 
312 	conn->op->o_tag = LDAP_REQ_BIND;
313 	memset( &conn->op->oq_bind, 0, sizeof( conn->op->oq_bind ) );
314 	conn->op->o_req_dn = ndn;
315 	conn->op->o_req_ndn = ndn;
316 	conn->op->o_protocol = LDAP_VERSION3;
317 	conn->op->orb_method = authtag;
318 	conn->op->o_callback = &sc;
319 
320 	/* TODO: controls */
321 	tag = ber_peek_tag( ber, &len );
322 	if ( tag == LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS ) {
323 		conn->op->o_ber = ber;
324 		rc = get_ctrls2( conn->op, &rs2, 0, LDAP_TAG_EXOP_VERIFY_CREDENTIALS_CONTROLS );
325 		if ( rc != LDAP_SUCCESS ) {
326 			rs->sr_err = LDAP_PROTOCOL_ERROR;
327 			goto done;
328 		}
329 	}
330 
331 	tag = ber_skip_tag( ber, &len );
332 	if ( len || tag != LBER_DEFAULT ) {
333 		rs->sr_err = LDAP_PROTOCOL_ERROR;
334 		goto done;
335 	}
336 
337 	switch ( authtag ) {
338 	case LDAP_AUTH_SIMPLE:
339 		break;
340 
341 	case LDAP_AUTH_SASL:
342 		conn->op->orb_mech = mechanism;
343 		break;
344 	}
345 
346 	conn->op->orb_cred = cred;
347 	sc.sc_response = vc_cb;
348 	sc.sc_private = &vc;
349 
350 	conn->op->o_bd = frontendDB;
351 	rs->sr_err = frontendDB->be_bind( conn->op, &rs2 );
352 
353 	if ( conn->op->o_conn->c_sasl_bind_in_progress ) {
354 		rc = vc_create_response( conn, rs2.sr_err, rs2.sr_text,
355 			!BER_BVISEMPTY( &vc.sasldata ) ? &vc.sasldata : NULL,
356 			NULL,
357 			vc.ctrls, &rs->sr_rspdata );
358 
359 	} else {
360 		rc = vc_create_response( NULL, rs2.sr_err, rs2.sr_text,
361 			NULL,
362 			&conn->op->o_conn->c_dn,
363 			vc.ctrls, &rs->sr_rspdata );
364 	}
365 
366 	if ( rc != 0 ) {
367 		rs->sr_err = LDAP_OTHER;
368 		goto done;
369 	}
370 
371 	if ( !BER_BVISNULL( &conn->op->o_conn->c_dn ) &&
372 		conn->op->o_conn->c_dn.bv_val != conn->op->o_conn->c_ndn.bv_val )
373 		ber_memfree( conn->op->o_conn->c_dn.bv_val );
374 	if ( !BER_BVISNULL( &conn->op->o_conn->c_ndn ) )
375 		ber_memfree( conn->op->o_conn->c_ndn.bv_val );
376 
377 done:;
378 	if ( conn ) {
379 		if ( conn->op->o_conn->c_sasl_bind_in_progress ) {
380 			if ( conn->conn == NULL ) {
381 				conn->conn = conn;
382 				conn->refcnt--;
383 				ldap_pvt_thread_mutex_lock( &vc_mutex );
384 				rc = ldap_avl_insert( &vc_tree, (caddr_t)conn,
385 					vc_conn_cmp, vc_conn_dup );
386 				ldap_pvt_thread_mutex_unlock( &vc_mutex );
387 				assert( rc == 0 );
388 
389 			} else {
390 				ldap_pvt_thread_mutex_lock( &vc_mutex );
391 				conn->refcnt--;
392 				ldap_pvt_thread_mutex_unlock( &vc_mutex );
393 			}
394 
395 		} else {
396 			if ( conn->conn != NULL ) {
397 				vc_conn_t *tmp;
398 
399 				ldap_pvt_thread_mutex_lock( &vc_mutex );
400 				tmp = ldap_avl_delete( &vc_tree, (caddr_t)conn, vc_conn_cmp );
401 				ldap_pvt_thread_mutex_unlock( &vc_mutex );
402 			}
403 			SLAP_FREE( conn );
404 		}
405 	}
406 
407 	if ( vc.ctrls ) {
408 		ldap_controls_free( vc.ctrls );
409 		vc.ctrls = NULL;
410 	}
411 
412 	if ( !BER_BVISNULL( &ndn ) ) {
413 		op->o_tmpfree( ndn.bv_val, op->o_tmpmemctx );
414 		BER_BVZERO( &ndn );
415 	}
416 
417 	op->o_tmpfree( reqdata.bv_val, op->o_tmpmemctx );
418 	BER_BVZERO( &reqdata );
419 
420         return rs->sr_err;
421 }
422 
423 static int
vc_initialize(void)424 vc_initialize( void )
425 {
426 	int rc;
427 
428 	rc = load_extop2( (struct berval *)&vc_exop_oid_bv,
429 		SLAP_EXOP_HIDE, vc_exop, 0 );
430 	if ( rc != LDAP_SUCCESS ) {
431 		Debug( LDAP_DEBUG_ANY,
432 			"vc_initialize: unable to register VerifyCredentials exop: %d.\n",
433 			rc );
434 	}
435 
436 	ldap_pvt_thread_mutex_init( &vc_mutex );
437 
438 	return rc;
439 }
440 
441 int
init_module(int argc,char * argv[])442 init_module( int argc, char *argv[] )
443 {
444 	return vc_initialize();
445 }
446 
447