authzid.c 8.66 KB
Newer Older
1
/* authzid.c - RFC 3829 Authzid Control */
2
3
4
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Quanah Gibson-Mount's avatar
Quanah Gibson-Mount committed
5
 * Copyright 2010-2020 The OpenLDAP Foundation.
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Pierangelo Masarati for inclusion
 * in OpenLDAP Software.
 */

/*
 * RFC 3829 Authzid
23
24
 *
 * must be instantiated as a global overlay
25
26
27
28
29
 */

#include "portable.h"

#include "slap.h"
30
#include "config.h"
31
32
33
#include "lutil.h"
#include "ac/string.h"

34
35
36
typedef struct authzid_conn_t {
	Connection *conn;
	int refcnt;
37
	char authzid_flag;
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
} authzid_conn_t;

static ldap_pvt_thread_mutex_t authzid_mutex;
static Avlnode *authzid_tree;

static int
authzid_conn_cmp( const void *c1, const void *c2 )
{
	const authzid_conn_t *ac1 = (const authzid_conn_t *)c1;
	const authzid_conn_t *ac2 = (const authzid_conn_t *)c2;

	return SLAP_PTRCMP( ac1->conn, ac2->conn );
}

static int
authzid_conn_dup( void *c1, void *c2 )
{
	authzid_conn_t *ac1 = (authzid_conn_t *)c1;
	authzid_conn_t *ac2 = (authzid_conn_t *)c2;

	if ( ac1->conn == ac2->conn ) {
		return -1;
	}

	return 0;
}
64

65
static int authzid_cid;
66
67
static slap_overinst authzid;

68
69
static authzid_conn_t *
authzid_conn_find( Connection *c )
70
{
71
	authzid_conn_t *ac = NULL, tmp = { 0 };
72

73
74
75
76
77
78
79
	tmp.conn = c;
	ac = (authzid_conn_t *)avl_find( authzid_tree, (caddr_t)&tmp, authzid_conn_cmp );
	if ( ac == NULL || ( ac != NULL && ac->refcnt != 0 ) ) {
		ac = NULL;
	}
	if ( ac ) {
		ac->refcnt++;
80
81
	}

82
	return ac;
83
84
}

85
86
static authzid_conn_t *
authzid_conn_get( Connection *c )
87
{
88
	authzid_conn_t *ac = NULL;
89

90
91
92
93
94
	ldap_pvt_thread_mutex_lock( &authzid_mutex );
	ac = authzid_conn_find( c );
	if ( ac && ac->refcnt ) ac = NULL;
	if ( ac ) ac->refcnt++;
	ldap_pvt_thread_mutex_unlock( &authzid_mutex );
95

96
97
	return ac;
}
98

99
100
101
102
103
104
static void
authzid_conn_release( authzid_conn_t *ac )
{
	ldap_pvt_thread_mutex_lock( &authzid_mutex );
	ac->refcnt--;
	ldap_pvt_thread_mutex_unlock( &authzid_mutex );
105
106
107
}

static int
108
authzid_conn_insert( Connection *c, char flag )
109
{
110
111
112
113
114
115
116
117
	authzid_conn_t *ac;
	int rc;

	ldap_pvt_thread_mutex_lock( &authzid_mutex );
	ac = authzid_conn_find( c );
	if ( ac ) {
		ldap_pvt_thread_mutex_unlock( &authzid_mutex );
		return -1;
118
119
	}

120
	ac = ch_malloc( sizeof( authzid_conn_t ) );
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
	ac->conn = c;
	ac->refcnt = 0;
	ac->authzid_flag = flag;
	rc = avl_insert( &authzid_tree, (caddr_t)ac,
		authzid_conn_cmp, authzid_conn_dup );
	ldap_pvt_thread_mutex_unlock( &authzid_mutex );

	return rc;
}

static int
authzid_conn_remove( Connection *c )
{
	authzid_conn_t *ac, *tmp;

	ldap_pvt_thread_mutex_lock( &authzid_mutex );
	ac = authzid_conn_find( c );
	if ( !ac ) {
		ldap_pvt_thread_mutex_unlock( &authzid_mutex );
		return -1;
	}
	tmp = avl_delete( &authzid_tree, (caddr_t)ac, authzid_conn_cmp );
	ldap_pvt_thread_mutex_unlock( &authzid_mutex );

	assert( tmp == ac );
146
	ch_free( ac );
147
148

	return 0;
149
}
150
151
152
153
154
155

static int
authzid_response(
	Operation *op,
	SlapReply *rs )
{
156
157
158
159
160
161
162
163
	LDAPControl **ctrls;
	struct berval edn = BER_BVNULL;
	ber_len_t len = 0;
	int n = 0;

	assert( rs->sr_tag = LDAP_RES_BIND );

	if ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS ) {
164
165
166
167
168
		authzid_conn_t *ac = op->o_controls[ authzid_cid ];
		if ( ac ) {
			authzid_conn_release( ac );
		} else {
			(void)authzid_conn_insert( op->o_conn, op->o_ctrlflag[ authzid_cid ] );
169
170
171
		}
		return SLAP_CB_CONTINUE;
	}
172

173
	(void)authzid_conn_remove( op->o_conn );
174

175
176
177
	if ( rs->sr_err != LDAP_SUCCESS ) {
		return SLAP_CB_CONTINUE;
	}
Pierangelo Masarati's avatar
Pierangelo Masarati committed
178

179
180
	if ( !BER_BVISEMPTY( &op->orb_edn ) ) {
		edn = op->orb_edn;
181

182
183
184
	} else if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
		edn = op->o_conn->c_dn;
	}
185

186
	if ( !BER_BVISEMPTY( &edn ) ) {
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
		ber_tag_t save_tag = op->o_tag;
		struct berval save_dn = op->o_dn;
		struct berval save_ndn = op->o_ndn;
		int rc;

		/* pretend it's an extop without data,
		 * so it is treated as a generic write
		 */
		op->o_tag = LDAP_REQ_EXTENDED;
		op->o_dn = edn;
		op->o_ndn = edn;
		rc = backend_check_restrictions( op, rs, NULL );
		op->o_tag = save_tag;
		op->o_dn = save_dn;
		op->o_ndn = save_ndn;
		if ( rc != LDAP_SUCCESS ) {
			rs->sr_err = LDAP_CONFIDENTIALITY_REQUIRED;
			return SLAP_CB_CONTINUE;
		}

207
208
		len = STRLENOF("dn:") + edn.bv_len;
	}
209

210
211
212
213
214
215
216
217
	/* save original controls in sc_private;
	 * will be restored by sc_cleanup
	 */
	if ( rs->sr_ctrls != NULL ) {
		op->o_callback->sc_private = rs->sr_ctrls;
		for ( ; rs->sr_ctrls[n] != NULL; n++ )
			;
	}
218

219
220
221
222
223
	ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( n + 2 ), op->o_tmpmemctx );
	n = 0;
	if ( rs->sr_ctrls ) {
		for ( ; rs->sr_ctrls[n] != NULL; n++ ) {
			ctrls[n] = rs->sr_ctrls[n];
224
		}
225
	}
226

227
228
229
230
231
232
233
234
235
236
237
	/* anonymous: "", otherwise "dn:<dn>" */
	ctrls[n] = op->o_tmpalloc( sizeof( LDAPControl ) + len + 1, op->o_tmpmemctx );
	ctrls[n]->ldctl_oid = LDAP_CONTROL_AUTHZID_RESPONSE;
	ctrls[n]->ldctl_iscritical = 0;
	ctrls[n]->ldctl_value.bv_len = len;
	ctrls[n]->ldctl_value.bv_val = (char *)&ctrls[n][1];
	if ( len ) {
		char *ptr;

		ptr = lutil_strcopy( ctrls[n]->ldctl_value.bv_val, "dn:" );
		ptr = lutil_strncopy( ptr, edn.bv_val, edn.bv_len );
238
	}
239
240
241
242
	ctrls[n]->ldctl_value.bv_val[len] = '\0';
	ctrls[n + 1] = NULL;

	rs->sr_ctrls = ctrls;
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258

	return SLAP_CB_CONTINUE;
}

static int
authzid_cleanup(
	Operation *op,
	SlapReply *rs )
{
	if ( rs->sr_ctrls ) {
		LDAPControl *ctrl;

		/* if ours, cleanup */
		ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE, rs->sr_ctrls, NULL );
		if ( ctrl ) {
			op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
259
			rs->sr_ctrls = NULL;
260
261
262
263
264
265
266
267
268
269
270
271
272
273
		}

		if ( op->o_callback->sc_private != NULL ) {
			rs->sr_ctrls = (LDAPControl **)op->o_callback->sc_private;
			op->o_callback->sc_private = NULL;
		}
	}

	op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
	op->o_callback = NULL;

	return SLAP_CB_CONTINUE;
}

274
275
276
277
278
279
280
281
static int
authzid_op_bind(
	Operation *op,
	SlapReply *rs )
{
	slap_callback *sc;

	if ( op->o_ctrlflag[ authzid_cid ] <= SLAP_CONTROL_IGNORED ) {
282
283
284
285
		authzid_conn_t *ac = authzid_conn_get( op->o_conn );
		if ( ac ) {
			op->o_ctrlflag[ authzid_cid ] = ac->authzid_flag;
			op->o_controls[ authzid_cid] = ac;
286
287
288
289
290
291
292
293
294
		}
	}

	if ( op->o_ctrlflag[ authzid_cid ] > SLAP_CONTROL_IGNORED ) {
		sc = op->o_callback;
		op->o_callback = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
		op->o_callback->sc_response = authzid_response;
		op->o_callback->sc_cleanup = authzid_cleanup;
		op->o_callback->sc_private = NULL;
295
		op->o_callback->sc_writewait = NULL;
296
297
298
299
300
301
		op->o_callback->sc_next = sc;
	}

	return SLAP_CB_CONTINUE;
}

302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
static int
parse_authzid_ctrl(
	Operation	*op,
	SlapReply	*rs,
	LDAPControl	*ctrl )
{
	if ( op->o_ctrlflag[ authzid_cid ] != SLAP_CONTROL_NONE ) {
		rs->sr_text = "authzid control specified multiple times";
		return LDAP_PROTOCOL_ERROR;
	}

	if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
		rs->sr_text = "authzid control value not absent";
		return LDAP_PROTOCOL_ERROR;
	}

318
	/* drop ongoing requests */
319
	(void)authzid_conn_remove( op->o_conn );
320

321
322
	op->o_ctrlflag[ authzid_cid ] = ctrl->ldctl_iscritical ?  SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;

323
324
325
326
	return LDAP_SUCCESS;
}

static int
327
authzid_db_init( BackendDB *be, ConfigReply *cr )
328
{
329
330
331
332
333
	if ( !SLAP_ISGLOBALOVERLAY( be ) ) {
		/* do not allow slapo-ppolicy to be global by now (ITS#5858) */
		if ( cr ) {
			snprintf( cr->msg, sizeof(cr->msg), 
				"slapo-authzid must be global" );
334
			Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg );
335
		}
336
337
338
339
340
341
342
343
344
345
346
		return 1;
	}
		
	int rc;

	rc = register_supported_control( LDAP_CONTROL_AUTHZID_REQUEST,
		SLAP_CTRL_GLOBAL|SLAP_CTRL_BIND|SLAP_CTRL_HIDE, NULL,
		parse_authzid_ctrl, &authzid_cid );
	if ( rc != LDAP_SUCCESS ) {
		Debug( LDAP_DEBUG_ANY,
			"authzid_initialize: Failed to register control '%s' (%d)\n",
347
			LDAP_CONTROL_AUTHZID_REQUEST, rc );
348
		return rc;
349
	}
350
351
352
353

	return LDAP_SUCCESS;
}

354
355
356
357
/*
 * Almost pointless, by now, since this overlay needs to be global,
 * and global overlays deletion is not supported yet.
 */
358
static int
359
authzid_db_destroy( BackendDB *be, ConfigReply *cr )
360
{
361
362
363
364
#ifdef SLAP_CONFIG_DELETE
	overlay_unregister_control( be, LDAP_CONTROL_AUTHZID_REQUEST );
#endif /* SLAP_CONFIG_DELETE */

365
	unregister_supported_control( LDAP_CONTROL_AUTHZID_REQUEST );
366

367
368
369
370
371
372
	return 0;
}

static int
authzid_initialize( void )
{
373
374
	ldap_pvt_thread_mutex_init( &authzid_mutex );

375
376
	authzid.on_bi.bi_type = "authzid";

377
	authzid.on_bi.bi_flags = SLAPO_BFLAG_SINGLE;
378
379
380
381
382
	authzid.on_bi.bi_db_init = authzid_db_init;
	authzid.on_bi.bi_db_destroy = authzid_db_destroy;
	authzid.on_bi.bi_op_bind = authzid_op_bind;

	return overlay_register( &authzid );
383
384
385
386
387
388
389
390
}

int
init_module( int argc, char *argv[] )
{
	return authzid_initialize();
}