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
36typedef 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
44static const struct berval vc_exop_oid_bv = BER_BVC(LDAP_EXOP_VERIFY_CREDENTIALS);
45static ldap_pvt_thread_mutex_t vc_mutex;
46static Avlnode *vc_tree;
47
48static int
49vc_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
57static int
58vc_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
70static int
71vc_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
146done:;
147	ber_free_buf( ber );
148
149	return rc;
150}
151
152typedef struct vc_cb_t {
153	struct berval sasldata;
154	LDAPControl **ctrls;
155} vc_cb_t;
156
157static int
158vc_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
177static int
178vc_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
377done:;
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
423static int
424vc_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
441int
442init_module( int argc, char *argv[] )
443{
444	return vc_initialize();
445}
446
447