]> git.sur5r.net Git - openldap/blob - contrib/slapd-modules/authzid/authzid.c
check restrictions; overlay must be global
[openldap] / contrib / slapd-modules / authzid / authzid.c
1 /* authzid.c - RFC 3829 Authzid Control */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2010-2011 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Pierangelo Masarati for inclusion
18  * in OpenLDAP Software.
19  */
20
21 /*
22  * RFC 3829 Authzid
23  *
24  * must be instantiated as a global overlay
25  */
26
27 #include "portable.h"
28
29 #include "slap.h"
30 #include "config.h"
31 #include "lutil.h"
32 #include "ac/string.h"
33
34 typedef struct authzid_conn_t {
35         Connection *conn;
36         int refcnt;
37         char authzid_flag;
38 } authzid_conn_t;
39
40 static ldap_pvt_thread_mutex_t authzid_mutex;
41 static Avlnode *authzid_tree;
42
43 static int
44 authzid_conn_cmp( const void *c1, const void *c2 )
45 {
46         const authzid_conn_t *ac1 = (const authzid_conn_t *)c1;
47         const authzid_conn_t *ac2 = (const authzid_conn_t *)c2;
48
49         return SLAP_PTRCMP( ac1->conn, ac2->conn );
50 }
51
52 static int
53 authzid_conn_dup( void *c1, void *c2 )
54 {
55         authzid_conn_t *ac1 = (authzid_conn_t *)c1;
56         authzid_conn_t *ac2 = (authzid_conn_t *)c2;
57
58         if ( ac1->conn == ac2->conn ) {
59                 return -1;
60         }
61
62         return 0;
63 }
64
65 static int authzid_cid;
66 static slap_overinst authzid;
67
68 static authzid_conn_t *
69 authzid_conn_find( Connection *c )
70 {
71         authzid_conn_t *ac = NULL, tmp = { 0 };
72
73         tmp.conn = c;
74         ac = (authzid_conn_t *)avl_find( authzid_tree, (caddr_t)&tmp, authzid_conn_cmp );
75         if ( ac == NULL || ( ac != NULL && ac->refcnt != 0 ) ) {
76                 ac = NULL;
77                 ldap_pvt_thread_mutex_unlock( &authzid_mutex );
78         }
79         if ( ac ) {
80                 ac->refcnt++;
81         }
82
83         return ac;
84 }
85
86 static authzid_conn_t *
87 authzid_conn_get( Connection *c )
88 {
89         authzid_conn_t *ac = NULL;
90
91         ldap_pvt_thread_mutex_lock( &authzid_mutex );
92         ac = authzid_conn_find( c );
93         if ( ac && ac->refcnt ) ac = NULL;
94         if ( ac ) ac->refcnt++;
95         ldap_pvt_thread_mutex_unlock( &authzid_mutex );
96
97         return ac;
98 }
99
100 static void
101 authzid_conn_release( authzid_conn_t *ac )
102 {
103         ldap_pvt_thread_mutex_lock( &authzid_mutex );
104         ac->refcnt--;
105         ldap_pvt_thread_mutex_unlock( &authzid_mutex );
106 }
107
108 static int
109 authzid_conn_insert( Connection *c, char flag )
110 {
111         authzid_conn_t *ac;
112         int rc;
113
114         ldap_pvt_thread_mutex_lock( &authzid_mutex );
115         ac = authzid_conn_find( c );
116         if ( ac ) {
117                 ldap_pvt_thread_mutex_unlock( &authzid_mutex );
118                 return -1;
119         }
120
121         ac = SLAP_MALLOC( sizeof( authzid_conn_t ) );
122         ac->conn = c;
123         ac->refcnt = 0;
124         ac->authzid_flag = flag;
125         rc = avl_insert( &authzid_tree, (caddr_t)ac,
126                 authzid_conn_cmp, authzid_conn_dup );
127         ldap_pvt_thread_mutex_unlock( &authzid_mutex );
128
129         return rc;
130 }
131
132 static int
133 authzid_conn_remove( Connection *c )
134 {
135         authzid_conn_t *ac, *tmp;
136
137         ldap_pvt_thread_mutex_lock( &authzid_mutex );
138         ac = authzid_conn_find( c );
139         if ( !ac ) {
140                 ldap_pvt_thread_mutex_unlock( &authzid_mutex );
141                 return -1;
142         }
143         tmp = avl_delete( &authzid_tree, (caddr_t)ac, authzid_conn_cmp );
144         ldap_pvt_thread_mutex_unlock( &authzid_mutex );
145
146         assert( tmp == ac );
147         SLAP_FREE( ac );
148
149         return 0;
150 }
151
152 static int
153 authzid_response(
154         Operation *op,
155         SlapReply *rs )
156 {
157         LDAPControl **ctrls;
158         struct berval edn = BER_BVNULL;
159         ber_len_t len = 0;
160         int n = 0;
161
162         assert( rs->sr_tag = LDAP_RES_BIND );
163
164         if ( rs->sr_err == LDAP_SASL_BIND_IN_PROGRESS ) {
165                 authzid_conn_t *ac = op->o_controls[ authzid_cid ];
166                 if ( ac ) {
167                         authzid_conn_release( ac );
168                 } else {
169                         (void)authzid_conn_insert( op->o_conn, op->o_ctrlflag[ authzid_cid ] );
170                 }
171                 return SLAP_CB_CONTINUE;
172         }
173
174         (void)authzid_conn_remove( op->o_conn );
175
176         if ( rs->sr_err != LDAP_SUCCESS ) {
177                 return SLAP_CB_CONTINUE;
178         }
179
180         if ( !BER_BVISEMPTY( &op->orb_edn ) ) {
181                 edn = op->orb_edn;
182
183         } else if ( !BER_BVISEMPTY( &op->o_conn->c_dn ) ) {
184                 edn = op->o_conn->c_dn;
185         }
186
187         if ( !BER_BVISEMPTY( &edn ) ) {
188                 ber_tag_t save_tag = op->o_tag;
189                 struct berval save_dn = op->o_dn;
190                 struct berval save_ndn = op->o_ndn;
191                 int rc;
192
193                 /* pretend it's an extop without data,
194                  * so it is treated as a generic write
195                  */
196                 op->o_tag = LDAP_REQ_EXTENDED;
197                 op->o_dn = edn;
198                 op->o_ndn = edn;
199                 rc = backend_check_restrictions( op, rs, NULL );
200                 op->o_tag = save_tag;
201                 op->o_dn = save_dn;
202                 op->o_ndn = save_ndn;
203                 if ( rc != LDAP_SUCCESS ) {
204                         rs->sr_err = LDAP_CONFIDENTIALITY_REQUIRED;
205                         return SLAP_CB_CONTINUE;
206                 }
207
208                 len = STRLENOF("dn:") + edn.bv_len;
209         }
210
211         /* save original controls in sc_private;
212          * will be restored by sc_cleanup
213          */
214         if ( rs->sr_ctrls != NULL ) {
215                 op->o_callback->sc_private = rs->sr_ctrls;
216                 for ( ; rs->sr_ctrls[n] != NULL; n++ )
217                         ;
218         }
219
220         ctrls = op->o_tmpalloc( sizeof( LDAPControl * )*( n + 2 ), op->o_tmpmemctx );
221         n = 0;
222         if ( rs->sr_ctrls ) {
223                 for ( ; rs->sr_ctrls[n] != NULL; n++ ) {
224                         ctrls[n] = rs->sr_ctrls[n];
225                 }
226         }
227
228         /* anonymous: "", otherwise "dn:<dn>" */
229         ctrls[n] = op->o_tmpalloc( sizeof( LDAPControl ) + len + 1, op->o_tmpmemctx );
230         ctrls[n]->ldctl_oid = LDAP_CONTROL_AUTHZID_RESPONSE;
231         ctrls[n]->ldctl_iscritical = 0;
232         ctrls[n]->ldctl_value.bv_len = len;
233         ctrls[n]->ldctl_value.bv_val = (char *)&ctrls[n][1];
234         if ( len ) {
235                 char *ptr;
236
237                 ptr = lutil_strcopy( ctrls[n]->ldctl_value.bv_val, "dn:" );
238                 ptr = lutil_strncopy( ptr, edn.bv_val, edn.bv_len );
239         }
240         ctrls[n]->ldctl_value.bv_val[len] = '\0';
241         ctrls[n + 1] = NULL;
242
243         rs->sr_ctrls = ctrls;
244
245         return SLAP_CB_CONTINUE;
246 }
247
248 static int
249 authzid_cleanup(
250         Operation *op,
251         SlapReply *rs )
252 {
253         if ( rs->sr_ctrls ) {
254                 LDAPControl *ctrl;
255
256                 /* if ours, cleanup */
257                 ctrl = ldap_control_find( LDAP_CONTROL_AUTHZID_RESPONSE, rs->sr_ctrls, NULL );
258                 if ( ctrl ) {
259                         op->o_tmpfree( rs->sr_ctrls, op->o_tmpmemctx );
260                         rs->sr_ctrls = NULL;
261                 }
262
263                 if ( op->o_callback->sc_private != NULL ) {
264                         rs->sr_ctrls = (LDAPControl **)op->o_callback->sc_private;
265                         op->o_callback->sc_private = NULL;
266                 }
267         }
268
269         op->o_tmpfree( op->o_callback, op->o_tmpmemctx );
270         op->o_callback = NULL;
271
272         return SLAP_CB_CONTINUE;
273 }
274
275 static int
276 authzid_op_bind(
277         Operation *op,
278         SlapReply *rs )
279 {
280         slap_callback *sc;
281
282         if ( op->o_ctrlflag[ authzid_cid ] <= SLAP_CONTROL_IGNORED ) {
283                 authzid_conn_t *ac = authzid_conn_get( op->o_conn );
284                 if ( ac ) {
285                         op->o_ctrlflag[ authzid_cid ] = ac->authzid_flag;
286                         op->o_controls[ authzid_cid] = ac;
287                 }
288         }
289
290         if ( op->o_ctrlflag[ authzid_cid ] > SLAP_CONTROL_IGNORED ) {
291                 sc = op->o_callback;
292                 op->o_callback = op->o_tmpalloc( sizeof( slap_callback ), op->o_tmpmemctx );
293                 op->o_callback->sc_response = authzid_response;
294                 op->o_callback->sc_cleanup = authzid_cleanup;
295                 op->o_callback->sc_private = NULL;
296                 op->o_callback->sc_next = sc;
297         }
298
299         return SLAP_CB_CONTINUE;
300 }
301
302 static int
303 parse_authzid_ctrl(
304         Operation       *op,
305         SlapReply       *rs,
306         LDAPControl     *ctrl )
307 {
308         if ( op->o_ctrlflag[ authzid_cid ] != SLAP_CONTROL_NONE ) {
309                 rs->sr_text = "authzid control specified multiple times";
310                 return LDAP_PROTOCOL_ERROR;
311         }
312
313         if ( !BER_BVISNULL( &ctrl->ldctl_value ) ) {
314                 rs->sr_text = "authzid control value not absent";
315                 return LDAP_PROTOCOL_ERROR;
316         }
317
318         /* drop ongoing requests */
319         (void)authzid_conn_remove( op->o_conn );
320
321         op->o_ctrlflag[ authzid_cid ] = ctrl->ldctl_iscritical ?  SLAP_CONTROL_CRITICAL : SLAP_CONTROL_NONCRITICAL;
322
323         return LDAP_SUCCESS;
324 }
325
326 static int
327 authzid_db_init( BackendDB *be, ConfigReply *cr )
328 {
329         if ( !SLAP_ISGLOBALOVERLAY( be ) ) {
330                 /* do not allow slapo-ppolicy to be global by now (ITS#5858) */
331                 if ( cr ) {
332                         snprintf( cr->msg, sizeof(cr->msg), 
333                                 "slapo-authzid must be global" );
334                         Debug( LDAP_DEBUG_ANY, "%s\n", cr->msg, 0, 0 );
335                 }
336                 return 1;
337         }
338                 
339         int rc;
340
341         rc = register_supported_control( LDAP_CONTROL_AUTHZID_REQUEST,
342                 SLAP_CTRL_GLOBAL|SLAP_CTRL_BIND|SLAP_CTRL_HIDE, NULL,
343                 parse_authzid_ctrl, &authzid_cid );
344         if ( rc != LDAP_SUCCESS ) {
345                 Debug( LDAP_DEBUG_ANY,
346                         "authzid_initialize: Failed to register control '%s' (%d)\n",
347                         LDAP_CONTROL_AUTHZID_REQUEST, rc, 0 );
348                 return rc;
349         }
350
351         return LDAP_SUCCESS;
352 }
353
354 /*
355  * Almost pointless, by now, since this overlay needs to be global,
356  * and global overlays deletion is not supported yet.
357  */
358 static int
359 authzid_db_destroy( BackendDB *be, ConfigReply *cr )
360 {
361 #ifdef SLAP_CONFIG_DELETE
362         overlay_unregister_control( be, LDAP_CONTROL_AUTHZID_REQUEST );
363 #endif /* SLAP_CONFIG_DELETE */
364
365         unregister_supported_control( LDAP_CONTROL_AUTHZID_REQUEST );
366
367         return 0;
368 }
369
370 static int
371 authzid_initialize( void )
372 {
373         ldap_pvt_thread_mutex_init( &authzid_mutex );
374
375         authzid.on_bi.bi_type = "authzid";
376
377         authzid.on_bi.bi_db_init = authzid_db_init;
378         authzid.on_bi.bi_db_destroy = authzid_db_destroy;
379         authzid.on_bi.bi_op_bind = authzid_op_bind;
380
381         return overlay_register( &authzid );
382 }
383
384 int
385 init_module( int argc, char *argv[] )
386 {
387         return authzid_initialize();
388 }
389