]> git.sur5r.net Git - openldap/blob - servers/slapd/bind.c
711dfb0be5b398952cb39835742b06ee91517c80
[openldap] / servers / slapd / bind.c
1 /* bind.c - decode an ldap bind operation and pass it to a backend db */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-1999 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 /*
9  * Copyright (c) 1995 Regents of the University of Michigan.
10  * All rights reserved.
11  *
12  * Redistribution and use in source and binary forms are permitted
13  * provided that this notice is preserved and that due credit is given
14  * to the University of Michigan at Ann Arbor. The name of the University
15  * may not be used to endorse or promote products derived from this
16  * software without specific prior written permission. This software
17  * is provided ``as is'' without express or implied warranty.
18  */
19
20 #include "portable.h"
21
22 #include <stdio.h>
23
24 #include <ac/string.h>
25 #include <ac/socket.h>
26
27 #include "ldap_pvt.h"
28 #include "slap.h"
29
30 int
31 do_bind(
32     Connection  *conn,
33     Operation   *op
34 )
35 {
36         BerElement      *ber = op->o_ber;
37         ber_int_t               version;
38         ber_tag_t method;
39         char            *mech;
40         char            *dn;
41         char *ndn;
42         ber_tag_t       tag;
43         int                     rc = LDAP_SUCCESS;
44         char    *text;
45         struct berval   cred;
46         Backend         *be;
47
48         Debug( LDAP_DEBUG_TRACE, "do_bind\n", 0, 0, 0 );
49
50         dn = NULL;
51         ndn = NULL;
52         mech = NULL;
53         cred.bv_val = NULL;
54
55         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
56
57         /*
58          * Force to connection to "anonymous" until bind succeeds.
59          */
60
61         if ( conn->c_cdn != NULL ) {
62                 free( conn->c_cdn );
63                 conn->c_cdn = NULL;
64         }
65
66         if ( conn->c_dn != NULL ) {
67                 free( conn->c_dn );
68                 conn->c_dn = NULL;
69         }
70
71         conn->c_authc_backend = NULL;
72         conn->c_authz_backend = NULL;
73
74         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
75
76         if ( op->o_dn != NULL ) {
77                 free( op->o_dn );
78                 op->o_dn = ch_strdup( "" );
79         }
80
81         if ( op->o_ndn != NULL ) {
82                 free( op->o_ndn );
83                 op->o_ndn = ch_strdup( "" );
84         }
85
86         /*
87          * Parse the bind request.  It looks like this:
88          *
89          *      BindRequest ::= SEQUENCE {
90          *              version         INTEGER,                 -- version
91          *              name            DistinguishedName,       -- dn
92          *              authentication  CHOICE {
93          *                      simple          [0] OCTET STRING -- passwd
94          *                      krbv42ldap      [1] OCTET STRING
95          *                      krbv42dsa       [2] OCTET STRING
96          *                      SASL            [3] SaslCredentials
97          *              }
98          *      }
99          *
100          *      SaslCredentials ::= SEQUENCE {
101      *          mechanism           LDAPString,
102      *          credentials         OCTET STRING OPTIONAL
103          *      }
104          */
105
106         tag = ber_scanf( ber, "{iat" /*}*/, &version, &dn, &method );
107
108         if ( tag == LBER_ERROR ) {
109                 Debug( LDAP_DEBUG_ANY, "bind: ber_scanf failed\n", 0, 0, 0 );
110                 send_ldap_disconnect( conn, op,
111                         LDAP_PROTOCOL_ERROR, "decoding error" );
112                 rc = -1;
113                 goto cleanup;
114         }
115
116         op->o_protocol = version;
117
118         if( method != LDAP_AUTH_SASL ) {
119                 tag = ber_scanf( ber, /*{*/ "o}", &cred );
120
121         } else {
122                 tag = ber_scanf( ber, "{a" /*}*/, &mech );
123
124                 if ( tag != LBER_ERROR ) {
125                         ber_len_t len;
126                         tag = ber_peek_tag( ber, &len );
127
128                         if ( tag == LDAP_TAG_LDAPCRED ) { 
129                                 tag = ber_scanf( ber, "o", &cred );
130                         }
131
132                         if ( tag != LBER_ERROR ) {
133                                 tag = ber_scanf( ber, /*{{*/ "}}" );
134                         }
135                 }
136         }
137
138         if ( tag == LBER_ERROR ) {
139                 send_ldap_disconnect( conn, op,
140                         LDAP_PROTOCOL_ERROR,
141                 "decoding error" );
142                 rc = SLAPD_DISCONNECT;
143                 goto cleanup;
144         }
145
146         if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
147                 Debug( LDAP_DEBUG_ANY, "do_bind: get_ctrls failed\n", 0, 0, 0 );
148                 goto cleanup;
149         } 
150
151         ndn = ch_strdup( dn );
152
153         if ( dn_normalize( ndn ) == NULL ) {
154                 Debug( LDAP_DEBUG_ANY, "bind: invalid dn (%s)\n", dn, 0, 0 );
155                 send_ldap_result( conn, op, rc = LDAP_INVALID_DN_SYNTAX, NULL,
156                     "invalid DN", NULL, NULL );
157                 goto cleanup;
158         }
159
160         if( method == LDAP_AUTH_SASL ) {
161                 Debug( LDAP_DEBUG_TRACE, "do_sasl_bind: dn (%s) mech %s\n",
162                         dn, mech, NULL );
163         } else {
164                 Debug( LDAP_DEBUG_TRACE, "do_bind: version=%ld dn=\"%s\" method=%ld\n",
165                         (unsigned long) version, dn, (unsigned long) method );
166         }
167
168         Statslog( LDAP_DEBUG_STATS, "conn=%ld op=%d BIND dn=\"%s\" method=%ld\n",
169             op->o_connid, op->o_opid, ndn, (unsigned long) method, 0 );
170
171         if ( version < LDAP_VERSION_MIN || version > LDAP_VERSION_MAX ) {
172                 Debug( LDAP_DEBUG_ANY, "do_bind: unknown version=%ld\n",
173                         (unsigned long) version, 0, 0 );
174                 send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR,
175                         NULL, "requested protocol version not supported", NULL, NULL );
176                 goto cleanup;
177         }
178
179         /* we set connection version regardless of whether bind succeeds
180          * or not.
181          */
182         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
183         conn->c_protocol = version;
184         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
185
186         if ( method == LDAP_AUTH_SASL ) {
187                 char *edn;
188
189                 if ( version < LDAP_VERSION3 ) {
190                         Debug( LDAP_DEBUG_ANY, "do_bind: sasl with LDAPv%ld\n",
191                                 (unsigned long) version, 0, 0 );
192                         send_ldap_disconnect( conn, op,
193                                 LDAP_PROTOCOL_ERROR, "SASL bind requires LDAPv3" );
194                         rc = SLAPD_DISCONNECT;
195                         goto cleanup;
196                 }
197
198                 if( mech == NULL || *mech == '\0' ) {
199                         Debug( LDAP_DEBUG_ANY,
200                                 "do_bind: no sasl mechanism provided\n",
201                                 0, 0, 0 );
202                         send_ldap_result( conn, op, rc = LDAP_AUTH_METHOD_NOT_SUPPORTED,
203                                 NULL, "no SASL mechanism provided", NULL, NULL );
204                         goto cleanup;
205                 }
206
207                 if( !charray_inlist( supportedSASLMechanisms, mech ) ) {
208                         Debug( LDAP_DEBUG_ANY,
209                                 "do_bind: sasl mechanism=\"%s\" not supported.\n",
210                                 mech, 0, 0 );
211                         send_ldap_result( conn, op, rc = LDAP_AUTH_METHOD_NOT_SUPPORTED,
212                                 NULL, "SASL mechanism not supported", NULL, NULL );
213                         goto cleanup;
214                 }
215
216                 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
217
218                 if ( conn->c_sasl_bind_mech != NULL ) {
219                         if((strcmp(conn->c_sasl_bind_mech, mech) != 0)) {
220                                 /* mechanism changed, cancel in progress bind */
221 #ifdef HAVE_CYRUS_SASL
222                                 sasl_dispose(&conn->c_sasl_bind_context);
223                                 conn->c_sasl_bind_context = NULL;
224 #endif
225                         }
226                         free( conn->c_sasl_bind_mech );
227                         conn->c_sasl_bind_mech = NULL;
228
229 #ifdef LDAP_DEBUG
230 #ifdef HAVE_CYRUS_SASL
231                 } else {
232                         assert( conn->c_sasl_bind_context == NULL );
233 #endif
234 #endif
235                 }
236
237                 ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
238
239                 edn = NULL;
240                 rc = sasl_bind( conn, op, dn, ndn, mech, &cred, &edn );
241
242                 if( rc == LDAP_SUCCESS && edn != NULL ) {
243                         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
244 #ifdef HAVE_CYRUS_SASL
245                         assert( conn->c_sasl_bind_context == NULL );
246 #endif
247                         conn->c_dn = edn;
248                         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
249
250                 } else if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) {
251 #ifdef HAVE_CYRUS_SASL
252                         assert( conn->c_sasl_bind_context != NULL );
253 #endif
254                         conn->c_sasl_bind_mech = mech;
255                         mech = NULL;
256
257 #ifdef HAVE_CYRUS_SASL
258                 } else {
259                         assert( conn->c_sasl_bind_context != NULL );
260 #endif
261                 }
262
263                 goto cleanup;
264
265         } else {
266                 /* Not SASL, cancel any in-progress bind */
267                 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
268
269                 if ( conn->c_sasl_bind_mech != NULL ) {
270                         assert( conn->c_sasl_bind_in_progress );
271
272                         free(conn->c_sasl_bind_mech);
273                         conn->c_sasl_bind_mech = NULL;
274
275 #ifdef HAVE_CYRUS_SASL
276                         assert( conn->c_sasl_bind_context != NULL );
277                         sasl_dispose(&conn->c_sasl_bind_context);
278                         conn->c_sasl_bind_context = NULL;
279 #endif
280                 } else {
281                         assert( !conn->c_sasl_bind_in_progress );
282 #ifdef HAVE_CYRUS_SASL
283                         assert( conn->c_sasl_bind_context == NULL );
284 #endif
285                 }
286
287                 ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
288         }
289
290         /* accept "anonymous" binds */
291         if ( cred.bv_len == 0 || ndn == NULL || *ndn == '\0' ) {
292                 /*
293                  * we already forced connection to "anonymous",
294                  * just need to send success
295                  */
296                 send_ldap_result( conn, op, LDAP_SUCCESS,
297                         NULL, NULL, NULL, NULL );
298                 Debug( LDAP_DEBUG_TRACE, "do_bind: v%d anonymous bind\n",
299                         version, 0, 0 );
300                 goto cleanup;
301         }
302
303         /*
304          * We could be serving multiple database backends.  Select the
305          * appropriate one, or send a referral to our "referral server"
306          * if we don't hold it.
307          */
308
309         if ( (be = select_backend( ndn )) == NULL ) {
310                 if ( default_referral ) {
311                         send_ldap_result( conn, op, rc = LDAP_REFERRAL,
312                                 NULL, NULL, default_referral, NULL );
313
314                 } else {
315                         /* noSuchObject is not allowed to be returned by bind */
316                         send_ldap_result( conn, op, rc = LDAP_INVALID_CREDENTIALS,
317                                 NULL, NULL, NULL, NULL );
318                 }
319
320                 goto cleanup;
321         }
322
323         conn->c_authz_backend = be;
324
325         /* make sure this backend recongizes critical controls */
326         rc = backend_check_controls( be, conn, op, &text ) ;
327
328         if( rc != LDAP_SUCCESS ) {
329                 send_ldap_result( conn, op, rc,
330                         NULL, text, NULL, NULL );
331                 goto cleanup;
332         }
333
334         if ( be->be_bind ) {
335                 int ret;
336                 /* alias suffix */
337                 char *edn = NULL;
338
339                 /* deref suffix alias if appropriate */
340                 ndn = suffix_alias( be, ndn );
341
342                 ret = (*be->be_bind)( be, conn, op, dn, ndn,
343                         method, mech, &cred, &edn );
344
345                 if ( ret == 0 ) {
346                         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
347
348                         conn->c_cdn = dn;
349                         dn = NULL;
350
351                         if(edn != NULL) {
352                                 conn->c_dn = edn;
353                         } else {
354                                 conn->c_dn = ndn;
355                                 ndn = NULL;
356                         }
357
358                         Debug( LDAP_DEBUG_TRACE, "do_bind: v%d bind: \"%s\" to \"%s\"\n",
359                         version, conn->c_cdn, conn->c_dn );
360
361                         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
362
363                         /* send this here to avoid a race condition */
364                         send_ldap_result( conn, op, LDAP_SUCCESS,
365                                 NULL, NULL, NULL, NULL );
366
367                 } else if (edn != NULL) {
368                         free( edn );
369                 }
370
371         } else {
372                 send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
373                         NULL, "bind function not implemented", NULL, NULL );
374         }
375
376 cleanup:
377         if( dn != NULL ) {
378                 free( dn );
379         }
380         if( ndn != NULL ) {
381                 free( ndn );
382         }
383         if ( mech != NULL ) {
384                 free( mech );
385         }
386         if ( cred.bv_val != NULL ) {
387                 free( cred.bv_val );
388         }
389
390         return rc;
391 }