1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/*  apr_ldap_rebind.c -- LDAP rebind callbacks for referrals
18 *
19 *  The LDAP SDK allows a callback to be set to enable rebinding
20 *  for referral processing.
21 *
22 */
23
24#include "apr.h"
25#include "apu.h"
26#include "apu_config.h"
27
28#if APU_DSO_BUILD
29#define APU_DSO_LDAP_BUILD
30#endif
31
32#include "apr_ldap.h"
33#include "apr_errno.h"
34#include "apr_strings.h"
35#include "apr_ldap_rebind.h"
36
37#include "stdio.h"
38
39#if APR_HAS_LDAP
40
41/* Used to store information about connections for use in the referral rebind callback. */
42struct apr_ldap_rebind_entry {
43    apr_pool_t *pool;
44    LDAP *index;
45    const char *bindDN;
46    const char *bindPW;
47    struct apr_ldap_rebind_entry *next;
48};
49typedef struct apr_ldap_rebind_entry apr_ldap_rebind_entry_t;
50
51
52#ifdef NETWARE
53#include "apr_private.h"
54#define get_apd                 APP_DATA* apd = (APP_DATA*)get_app_data(gLibId);
55#define apr_ldap_xref_lock      ((apr_thread_mutex_t *)(apd->gs_ldap_xref_lock))
56#define xref_head               ((apr_ldap_rebind_entry_t *)(apd->gs_xref_head))
57#else
58#if APR_HAS_THREADS
59static apr_thread_mutex_t *apr_ldap_xref_lock = NULL;
60#endif
61static apr_ldap_rebind_entry_t *xref_head = NULL;
62#endif
63
64static int apr_ldap_rebind_set_callback(LDAP *ld);
65static apr_status_t apr_ldap_rebind_remove_helper(void *data);
66
67static apr_status_t apr_ldap_pool_cleanup_set_null(void *data_)
68{
69    void **ptr = (void **)data_;
70    *ptr = NULL;
71    return APR_SUCCESS;
72}
73
74
75/* APR utility routine used to create the xref_lock. */
76APU_DECLARE_LDAP(apr_status_t) apr_ldap_rebind_init(apr_pool_t *pool)
77{
78    apr_status_t retcode = APR_SUCCESS;
79
80#ifdef NETWARE
81    get_apd
82#endif
83
84#if APR_HAS_THREADS
85    /* run after apr_thread_mutex_create cleanup */
86    apr_pool_cleanup_register(pool, &apr_ldap_xref_lock, apr_ldap_pool_cleanup_set_null,
87                              apr_pool_cleanup_null);
88
89    if (apr_ldap_xref_lock == NULL) {
90        retcode = apr_thread_mutex_create(&apr_ldap_xref_lock, APR_THREAD_MUTEX_DEFAULT, pool);
91    }
92#endif
93
94    return(retcode);
95}
96
97
98APU_DECLARE_LDAP(apr_status_t) apr_ldap_rebind_add(apr_pool_t *pool,
99                                                   LDAP *ld,
100                                                   const char *bindDN,
101                                                   const char *bindPW)
102{
103    apr_status_t retcode = APR_SUCCESS;
104    apr_ldap_rebind_entry_t *new_xref;
105
106#ifdef NETWARE
107    get_apd
108#endif
109
110    new_xref = (apr_ldap_rebind_entry_t *)apr_pcalloc(pool, sizeof(apr_ldap_rebind_entry_t));
111    if (new_xref) {
112        new_xref->pool = pool;
113        new_xref->index = ld;
114        if (bindDN) {
115            new_xref->bindDN = apr_pstrdup(pool, bindDN);
116        }
117        if (bindPW) {
118            new_xref->bindPW = apr_pstrdup(pool, bindPW);
119        }
120
121#if APR_HAS_THREADS
122       retcode = apr_thread_mutex_lock(apr_ldap_xref_lock);
123       if (retcode != APR_SUCCESS) {
124           return retcode;
125       }
126#endif
127
128        new_xref->next = xref_head;
129        xref_head = new_xref;
130
131#if APR_HAS_THREADS
132        retcode = apr_thread_mutex_unlock(apr_ldap_xref_lock);
133        if (retcode != APR_SUCCESS) {
134           return retcode;
135        }
136#endif
137    }
138    else {
139        return(APR_ENOMEM);
140    }
141
142    retcode = apr_ldap_rebind_set_callback(ld);
143    if (APR_SUCCESS != retcode) {
144        apr_ldap_rebind_remove(ld);
145        return retcode;
146    }
147
148    apr_pool_cleanup_register(pool, ld,
149                              apr_ldap_rebind_remove_helper,
150                              apr_pool_cleanup_null);
151
152    return(APR_SUCCESS);
153}
154
155
156APU_DECLARE_LDAP(apr_status_t) apr_ldap_rebind_remove(LDAP *ld)
157{
158    apr_ldap_rebind_entry_t *tmp_xref, *prev = NULL;
159    apr_status_t retcode = 0;
160
161#ifdef NETWARE
162    get_apd
163#endif
164
165#if APR_HAS_THREADS
166    retcode = apr_thread_mutex_lock(apr_ldap_xref_lock);
167    if (retcode != APR_SUCCESS) {
168        return retcode;
169    }
170#endif
171    tmp_xref = xref_head;
172
173    while ((tmp_xref) && (tmp_xref->index != ld)) {
174        prev = tmp_xref;
175        tmp_xref = tmp_xref->next;
176    }
177
178    if (tmp_xref) {
179        if (tmp_xref == xref_head) {
180            xref_head = xref_head->next;
181        }
182        else {
183            prev->next = tmp_xref->next;
184        }
185
186        /* tmp_xref and its contents were pool allocated so they don't need to be freed here. */
187
188        /* remove the cleanup, just in case this was done manually */
189        apr_pool_cleanup_kill(tmp_xref->pool, tmp_xref->index,
190                              apr_ldap_rebind_remove_helper);
191    }
192
193#if APR_HAS_THREADS
194    retcode = apr_thread_mutex_unlock(apr_ldap_xref_lock);
195    if (retcode != APR_SUCCESS) {
196       return retcode;
197    }
198#endif
199    return APR_SUCCESS;
200}
201
202
203static apr_status_t apr_ldap_rebind_remove_helper(void *data)
204{
205    LDAP *ld = (LDAP *)data;
206    apr_ldap_rebind_remove(ld);
207    return APR_SUCCESS;
208}
209
210#if APR_HAS_TIVOLI_LDAPSDK || APR_HAS_OPENLDAP_LDAPSDK || APR_HAS_NOVELL_LDAPSDK
211static apr_ldap_rebind_entry_t *apr_ldap_rebind_lookup(LDAP *ld)
212{
213    apr_ldap_rebind_entry_t *tmp_xref, *match = NULL;
214
215#ifdef NETWARE
216    get_apd
217#endif
218
219#if APR_HAS_THREADS
220    apr_thread_mutex_lock(apr_ldap_xref_lock);
221#endif
222    tmp_xref = xref_head;
223
224    while (tmp_xref) {
225        if (tmp_xref->index == ld) {
226            match = tmp_xref;
227            tmp_xref = NULL;
228        }
229        else {
230            tmp_xref = tmp_xref->next;
231        }
232    }
233
234#if APR_HAS_THREADS
235    apr_thread_mutex_unlock(apr_ldap_xref_lock);
236#endif
237
238    return (match);
239}
240#endif
241
242#if APR_HAS_TIVOLI_LDAPSDK
243
244/* LDAP_rebindproc() Tivoli LDAP style
245 *     Rebind callback function. Called when chasing referrals. See API docs.
246 * ON ENTRY:
247 *     ld       Pointer to an LDAP control structure. (input only)
248 *     binddnp  Pointer to an Application DName used for binding (in *or* out)
249 *     passwdp  Pointer to the password associated with the DName (in *or* out)
250 *     methodp  Pointer to the Auth method (output only)
251 *     freeit   Flag to indicate if this is a lookup or a free request (input only)
252 */
253static int LDAP_rebindproc(LDAP *ld, char **binddnp, char **passwdp, int *methodp, int freeit)
254{
255    if (!freeit) {
256        apr_ldap_rebind_entry_t *my_conn;
257
258        *methodp = LDAP_AUTH_SIMPLE;
259        my_conn = apr_ldap_rebind_lookup(ld);
260
261        if ((my_conn) && (my_conn->bindDN != NULL)) {
262            *binddnp = strdup(my_conn->bindDN);
263            *passwdp = strdup(my_conn->bindPW);
264        } else {
265            *binddnp = NULL;
266            *passwdp = NULL;
267        }
268    } else {
269        if (*binddnp) {
270            free(*binddnp);
271        }
272        if (*passwdp) {
273            free(*passwdp);
274        }
275    }
276
277    return LDAP_SUCCESS;
278}
279
280static int apr_ldap_rebind_set_callback(LDAP *ld)
281{
282    ldap_set_rebind_proc(ld, (LDAPRebindProc)LDAP_rebindproc);
283    return APR_SUCCESS;
284}
285
286#elif APR_HAS_OPENLDAP_LDAPSDK
287
288/* LDAP_rebindproc() openLDAP V3 style
289 * ON ENTRY:
290 *     ld       Pointer to an LDAP control structure. (input only)
291 *     url      Unused in this routine
292 *     request  Unused in this routine
293 *     msgid    Unused in this routine
294 *     params   Unused in this routine
295 *
296 *     or
297 *
298 *     ld       Pointer to an LDAP control structure. (input only)
299 *     url      Unused in this routine
300 *     request  Unused in this routine
301 *     msgid    Unused in this routine
302 */
303#if defined(LDAP_SET_REBIND_PROC_THREE)
304static int LDAP_rebindproc(LDAP *ld, LDAP_CONST char *url, ber_tag_t request,
305                           ber_int_t msgid, void *params)
306#else
307static int LDAP_rebindproc(LDAP *ld, LDAP_CONST char *url, int request,
308                           ber_int_t msgid)
309#endif
310{
311    apr_ldap_rebind_entry_t *my_conn;
312    const char *bindDN = NULL;
313    const char *bindPW = NULL;
314
315    my_conn = apr_ldap_rebind_lookup(ld);
316
317    if ((my_conn) && (my_conn->bindDN != NULL)) {
318        bindDN = my_conn->bindDN;
319        bindPW = my_conn->bindPW;
320    }
321
322    return (ldap_bind_s(ld, bindDN, bindPW, LDAP_AUTH_SIMPLE));
323}
324
325static int apr_ldap_rebind_set_callback(LDAP *ld)
326{
327#if defined(LDAP_SET_REBIND_PROC_THREE)
328    ldap_set_rebind_proc(ld, LDAP_rebindproc, NULL);
329#else
330    ldap_set_rebind_proc(ld, LDAP_rebindproc);
331#endif
332    return APR_SUCCESS;
333}
334
335#elif APR_HAS_NOVELL_LDAPSDK
336
337/* LDAP_rebindproc() openLDAP V3 style
338 * ON ENTRY:
339 *     ld       Pointer to an LDAP control structure. (input only)
340 *     url      Unused in this routine
341 *     request  Unused in this routine
342 *     msgid    Unused in this routine
343 */
344static int LDAP_rebindproc(LDAP *ld, LDAP_CONST char *url, int request, ber_int_t msgid)
345{
346
347    apr_ldap_rebind_entry_t *my_conn;
348    const char *bindDN = NULL;
349    const char *bindPW = NULL;
350
351    my_conn = apr_ldap_rebind_lookup(ld);
352
353    if ((my_conn) && (my_conn->bindDN != NULL)) {
354        bindDN = my_conn->bindDN;
355        bindPW = my_conn->bindPW;
356    }
357
358    return (ldap_bind_s(ld, bindDN, bindPW, LDAP_AUTH_SIMPLE));
359}
360
361static int apr_ldap_rebind_set_callback(LDAP *ld)
362{
363    ldap_set_rebind_proc(ld, LDAP_rebindproc);
364    return APR_SUCCESS;
365}
366
367#else         /* Implementation not recognised */
368
369static int apr_ldap_rebind_set_callback(LDAP *ld)
370{
371    return APR_ENOTIMPL;
372}
373
374#endif
375
376
377#endif       /* APR_HAS_LDAP */
378