]> git.sur5r.net Git - openldap/blob - servers/slapd/bind.c
294c851ce7388a37ef908002d70f3d226f570e5f
[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-2000 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            *saslmech;
41         char            *dn;
42         char *ndn;
43         ber_tag_t       tag;
44         int                     rc = LDAP_SUCCESS;
45         const char      *text;
46         struct berval   cred;
47         Backend         *be;
48
49         Debug( LDAP_DEBUG_TRACE, "do_bind\n", 0, 0, 0 );
50
51         dn = NULL;
52         ndn = NULL;
53         mech = NULL;
54         cred.bv_val = NULL;
55
56         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
57
58         /*
59          * Force to connection to "anonymous" until bind succeeds.
60          */
61
62         if ( conn->c_cdn != NULL ) {
63                 free( conn->c_cdn );
64                 conn->c_cdn = NULL;
65         }
66
67         if ( conn->c_dn != NULL ) {
68                 free( conn->c_dn );
69                 conn->c_dn = NULL;
70         }
71
72         conn->c_authc_backend = NULL;
73         conn->c_authz_backend = NULL;
74
75         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
76
77         if ( op->o_dn != NULL ) {
78                 free( op->o_dn );
79                 op->o_dn = ch_strdup( "" );
80         }
81
82         if ( op->o_ndn != NULL ) {
83                 free( op->o_ndn );
84                 op->o_ndn = ch_strdup( "" );
85         }
86
87         /*
88          * Parse the bind request.  It looks like this:
89          *
90          *      BindRequest ::= SEQUENCE {
91          *              version         INTEGER,                 -- version
92          *              name            DistinguishedName,       -- dn
93          *              authentication  CHOICE {
94          *                      simple          [0] OCTET STRING -- passwd
95          *                      krbv42ldap      [1] OCTET STRING
96          *                      krbv42dsa       [2] OCTET STRING
97          *                      SASL            [3] SaslCredentials
98          *              }
99          *      }
100          *
101          *      SaslCredentials ::= SEQUENCE {
102      *          mechanism           LDAPString,
103      *          credentials         OCTET STRING OPTIONAL
104          *      }
105          */
106
107         tag = ber_scanf( ber, "{iat" /*}*/, &version, &dn, &method );
108
109         if ( tag == LBER_ERROR ) {
110                 Debug( LDAP_DEBUG_ANY, "bind: ber_scanf failed\n", 0, 0, 0 );
111                 send_ldap_disconnect( conn, op,
112                         LDAP_PROTOCOL_ERROR, "decoding error" );
113                 rc = -1;
114                 goto cleanup;
115         }
116
117         op->o_protocol = version;
118
119         if( method != LDAP_AUTH_SASL ) {
120                 tag = ber_scanf( ber, /*{*/ "o}", &cred );
121
122         } else {
123                 tag = ber_scanf( ber, "{a" /*}*/, &mech );
124
125                 if ( tag != LBER_ERROR ) {
126                         ber_len_t len;
127                         tag = ber_peek_tag( ber, &len );
128
129                         if ( tag == LDAP_TAG_LDAPCRED ) { 
130                                 tag = ber_scanf( ber, "o", &cred );
131                         }
132
133                         if ( tag != LBER_ERROR ) {
134                                 tag = ber_scanf( ber, /*{{*/ "}}" );
135                         }
136                 }
137         }
138
139         if ( tag == LBER_ERROR ) {
140                 send_ldap_disconnect( conn, op,
141                         LDAP_PROTOCOL_ERROR,
142                 "decoding error" );
143                 rc = SLAPD_DISCONNECT;
144                 goto cleanup;
145         }
146
147         if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
148                 Debug( LDAP_DEBUG_ANY, "do_bind: get_ctrls failed\n", 0, 0, 0 );
149                 goto cleanup;
150         } 
151
152         ndn = ch_strdup( dn );
153
154         if ( dn_normalize( ndn ) == NULL ) {
155                 Debug( LDAP_DEBUG_ANY, "bind: invalid dn (%s)\n", dn, 0, 0 );
156                 send_ldap_result( conn, op, rc = LDAP_INVALID_DN_SYNTAX, NULL,
157                     "invalid DN", NULL, NULL );
158                 goto cleanup;
159         }
160
161         if( method == LDAP_AUTH_SASL ) {
162                 Debug( LDAP_DEBUG_TRACE, "do_sasl_bind: dn (%s) mech %s\n",
163                         dn, mech, NULL );
164         } else {
165                 Debug( LDAP_DEBUG_TRACE, "do_bind: version=%ld dn=\"%s\" method=%ld\n",
166                         (unsigned long) version, dn, (unsigned long) method );
167         }
168
169         Statslog( LDAP_DEBUG_STATS, "conn=%ld op=%d BIND dn=\"%s\" method=%ld\n",
170             op->o_connid, op->o_opid, ndn, (unsigned long) method, 0 );
171
172         if ( version < LDAP_VERSION_MIN || version > LDAP_VERSION_MAX ) {
173                 Debug( LDAP_DEBUG_ANY, "do_bind: unknown version=%ld\n",
174                         (unsigned long) version, 0, 0 );
175                 send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR,
176                         NULL, "requested protocol version not supported", NULL, NULL );
177                 goto cleanup;
178         }
179
180         /* we set connection version regardless of whether bind succeeds
181          * or not.
182          */
183         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
184         conn->c_protocol = version;
185         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
186
187         if ( method == LDAP_AUTH_SASL ) {
188                 char *edn;
189                 slap_ssf_t ssf = 0;
190
191                 if ( version < LDAP_VERSION3 ) {
192                         Debug( LDAP_DEBUG_ANY, "do_bind: sasl with LDAPv%ld\n",
193                                 (unsigned long) version, 0, 0 );
194                         send_ldap_disconnect( conn, op,
195                                 LDAP_PROTOCOL_ERROR, "SASL bind requires LDAPv3" );
196                         rc = SLAPD_DISCONNECT;
197                         goto cleanup;
198                 }
199
200                 if( mech == NULL || *mech == '\0' ) {
201                         Debug( LDAP_DEBUG_ANY,
202                                 "do_bind: no sasl mechanism provided\n",
203                                 0, 0, 0 );
204                         send_ldap_result( conn, op, rc = LDAP_AUTH_METHOD_NOT_SUPPORTED,
205                                 NULL, "no SASL mechanism provided", NULL, NULL );
206                         goto cleanup;
207                 }
208
209                 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
210
211                 if ( conn->c_sasl_bind_mech != NULL ) {
212                         /* SASL bind is in progress */
213                         saslmech = NULL;
214
215                         if((strcmp(conn->c_sasl_bind_mech, mech) != 0)) {
216                                 /* mechanism changed */
217                                 slap_sasl_reset(conn);
218                         }
219
220                         free( conn->c_sasl_bind_mech );
221                         conn->c_sasl_bind_mech = NULL;
222
223 #ifdef LDAP_DEBUG
224                 } else {
225                         /* SASL bind is NOT in progress */
226                         saslmech = mech;
227                         assert( conn->c_sasl_bind_mech == NULL );
228 #endif
229                 }
230
231                 ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
232
233                 edn = NULL;
234                 rc = slap_sasl_bind( conn, op, dn, ndn, saslmech, &cred,
235                         &edn, &ssf );
236
237                 if( rc == LDAP_SUCCESS ) {
238                         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
239                         conn->c_dn = edn;
240                         conn->c_authmech = mech;
241                         if( ssf ) conn->c_sasl_layers++;
242                         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
243
244                 } else if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) {
245                         conn->c_sasl_bind_mech = mech;
246                 }
247
248                 mech = NULL;
249
250                 goto cleanup;
251
252         } else {
253                 /* Not SASL, cancel any in-progress bind */
254                 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
255
256                 if ( conn->c_sasl_bind_mech != NULL ) {
257                         assert( conn->c_sasl_bind_in_progress );
258
259                         free(conn->c_sasl_bind_mech);
260                         conn->c_sasl_bind_mech = NULL;
261
262                 } else {
263                         assert( !conn->c_sasl_bind_in_progress );
264                 }
265
266                 slap_sasl_reset( conn );
267                 ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
268         }
269
270         /* accept "anonymous" binds */
271         if ( cred.bv_len == 0 || ndn == NULL || *ndn == '\0' ) {
272                 /*
273                  * we already forced connection to "anonymous",
274                  * just need to send success
275                  */
276                 send_ldap_result( conn, op, LDAP_SUCCESS,
277                         NULL, NULL, NULL, NULL );
278                 Debug( LDAP_DEBUG_TRACE, "do_bind: v%d anonymous bind\n",
279                         version, 0, 0 );
280                 goto cleanup;
281         }
282
283         /*
284          * We could be serving multiple database backends.  Select the
285          * appropriate one, or send a referral to our "referral server"
286          * if we don't hold it.
287          */
288
289         if ( (be = select_backend( ndn )) == NULL ) {
290                 if ( default_referral ) {
291                         send_ldap_result( conn, op, rc = LDAP_REFERRAL,
292                                 NULL, NULL, default_referral, NULL );
293
294                 } else {
295                         /* noSuchObject is not allowed to be returned by bind */
296                         send_ldap_result( conn, op, rc = LDAP_INVALID_CREDENTIALS,
297                                 NULL, NULL, NULL, NULL );
298                 }
299
300                 goto cleanup;
301         }
302
303         conn->c_authz_backend = be;
304
305         /* make sure this backend recongizes critical controls */
306         rc = backend_check_controls( be, conn, op, &text ) ;
307
308         if( rc != LDAP_SUCCESS ) {
309                 send_ldap_result( conn, op, rc,
310                         NULL, text, NULL, NULL );
311                 goto cleanup;
312         }
313
314         if ( be->be_bind ) {
315                 int ret;
316                 /* alias suffix */
317                 char *edn = NULL;
318
319                 /* deref suffix alias if appropriate */
320                 ndn = suffix_alias( be, ndn );
321
322                 ret = (*be->be_bind)( be, conn, op, dn, ndn,
323                         method, &cred, &edn );
324
325                 if ( ret == 0 ) {
326                         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
327
328                         conn->c_cdn = dn;
329                         dn = NULL;
330
331                         if(edn != NULL) {
332                                 conn->c_dn = edn;
333                         } else {
334                                 conn->c_dn = ndn;
335                                 ndn = NULL;
336                         }
337
338                         Debug( LDAP_DEBUG_TRACE, "do_bind: v%d bind: \"%s\" to \"%s\"\n",
339                         version, conn->c_cdn, conn->c_dn );
340
341                         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
342
343                         /* send this here to avoid a race condition */
344                         send_ldap_result( conn, op, LDAP_SUCCESS,
345                                 NULL, NULL, NULL, NULL );
346
347                 } else if (edn != NULL) {
348                         free( edn );
349                 }
350
351         } else {
352                 send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
353                         NULL, "operation not supported within namingContext", NULL, NULL );
354         }
355
356 cleanup:
357         if( rc != LDAP_SASL_BIND_IN_PROGRESS ) {
358                 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
359
360                 /* dispose of mech */
361                 free( conn->c_sasl_bind_mech );
362                 conn->c_sasl_bind_mech = NULL;
363
364                 ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
365         }
366
367         if( dn != NULL ) {
368                 free( dn );
369         }
370         if( ndn != NULL ) {
371                 free( ndn );
372         }
373         if ( mech != NULL ) {
374                 free( mech );
375         }
376         if ( cred.bv_val != NULL ) {
377                 free( cred.bv_val );
378         }
379
380         return rc;
381 }