]> git.sur5r.net Git - openldap/blob - servers/slapd/bind.c
a285c0328b301c691668b989e210b27235a15421
[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 "slap.h"
28
29 int
30 do_bind(
31     Connection  *conn,
32     Operation   *op
33 )
34 {
35         BerElement      *ber = op->o_ber;
36         ber_int_t               version;
37         ber_tag_t method;
38         char            *mech;
39         char            *dn;
40         char *ndn;
41         ber_tag_t       tag;
42         int                     rc = LDAP_SUCCESS;
43         struct berval   cred;
44         Backend         *be;
45
46         Debug( LDAP_DEBUG_TRACE, "do_bind\n", 0, 0, 0 );
47
48         dn = NULL;
49         ndn = NULL;
50         mech = NULL;
51         cred.bv_val = NULL;
52
53         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
54
55         /* Force to connection to "anonymous" until bind succeeds.
56          * This may need to be relocated or done on a case by case basis
57          * to handle certain SASL mechanisms.
58          */
59
60         if ( conn->c_cdn != NULL ) {
61                 free( conn->c_cdn );
62                 conn->c_cdn = NULL;
63         }
64
65         if ( conn->c_dn != NULL ) {
66                 free( conn->c_dn );
67                 conn->c_dn = NULL;
68         }
69
70         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
71
72         if ( op->o_dn != NULL ) {
73                 free( op->o_dn );
74                 op->o_dn = ch_strdup( "" );
75         }
76
77         if ( op->o_ndn != NULL ) {
78                 free( op->o_ndn );
79                 op->o_ndn = ch_strdup( "" );
80         }
81
82         /*
83          * Parse the bind request.  It looks like this:
84          *
85          *      BindRequest ::= SEQUENCE {
86          *              version         INTEGER,                 -- version
87          *              name            DistinguishedName,       -- dn
88          *              authentication  CHOICE {
89          *                      simple          [0] OCTET STRING -- passwd
90          *                      krbv42ldap      [1] OCTET STRING
91          *                      krbv42dsa       [2] OCTET STRING
92          *                      SASL            [3] SaslCredentials
93          *              }
94          *      }
95          *
96          *      SaslCredentials ::= SEQUENCE {
97      *          mechanism           LDAPString,
98      *          credentials         OCTET STRING OPTIONAL
99          *      }
100          */
101
102         tag = ber_scanf( ber, "{iat" /*}*/, &version, &dn, &method );
103
104         if ( tag == LBER_ERROR ) {
105                 Debug( LDAP_DEBUG_ANY, "bind: ber_scanf failed\n", 0, 0, 0 );
106                 send_ldap_disconnect( conn, op,
107                         LDAP_PROTOCOL_ERROR, "decoding error" );
108                 rc = -1;
109                 goto cleanup;
110         }
111
112         if ( dn_normalize( dn ) == NULL ) {
113                 Debug( LDAP_DEBUG_ANY, "bind: invalid dn (%s)\n", dn, 0, 0 );
114                 send_ldap_result( conn, op, rc = LDAP_INVALID_DN_SYNTAX, NULL,
115                     "invalid DN", NULL, NULL );
116                 goto cleanup;
117         }
118
119         ndn = ch_strdup( dn );
120         ldap_pvt_str2upper( ndn );
121
122         op->o_protocol = version;
123
124         if( method != LDAP_AUTH_SASL ) {
125                 tag = ber_scanf( ber, /*{*/ "o}", &cred );
126
127         } else {
128                 tag = ber_scanf( ber, "{a" /*}*/, &mech );
129
130                 if ( tag != LBER_ERROR ) {
131                         ber_len_t len;
132                         tag = ber_peek_tag( ber, &len );
133
134                         if ( tag == LDAP_TAG_LDAPCRED ) { 
135                                 tag = ber_scanf( ber, "o", &cred );
136                         }
137
138                         if ( tag != LBER_ERROR ) {
139                                 tag = ber_scanf( ber, /*{{*/ "}}" );
140                         }
141                 }
142         }
143
144         if ( tag == LBER_ERROR ) {
145                 send_ldap_disconnect( conn, op,
146                         LDAP_PROTOCOL_ERROR,
147                 "decoding error" );
148                 rc = -1;
149                 goto cleanup;
150         }
151
152         if( (rc = get_ctrls( conn, op, 1 )) != LDAP_SUCCESS ) {
153                 Debug( LDAP_DEBUG_ANY, "do_bind: get_ctrls failed\n", 0, 0, 0 );
154                 goto cleanup;
155         } 
156
157         if( method == LDAP_AUTH_SASL ) {
158                 Debug( LDAP_DEBUG_TRACE, "do_sasl_bind: dn (%s) mech %s\n",
159                         dn, mech, NULL );
160         } else {
161                 Debug( LDAP_DEBUG_TRACE, "do_bind: version=%ld dn=\"%s\" method=%ld\n",
162                         (unsigned long) version, dn, (unsigned long) method );
163         }
164
165         Statslog( LDAP_DEBUG_STATS, "conn=%ld op=%d BIND dn=\"%s\" method=%ld\n",
166             op->o_connid, op->o_opid, ndn, (unsigned long) method, 0 );
167
168         if ( version < LDAP_VERSION_MIN || version > LDAP_VERSION_MAX ) {
169                 Debug( LDAP_DEBUG_ANY, "do_bind: unknown version=%ld\n",
170                         (unsigned long) version, 0, 0 );
171                 send_ldap_result( conn, op, rc = LDAP_PROTOCOL_ERROR,
172                         NULL, "version not supported", NULL, NULL );
173                 goto cleanup;
174         }
175
176         if ( method == LDAP_AUTH_SASL ) {
177                 if ( version < LDAP_VERSION3 ) {
178                         Debug( LDAP_DEBUG_ANY, "do_bind: sasl with LDAPv%ld\n",
179                                 (unsigned long) version, 0, 0 );
180                         send_ldap_disconnect( conn, op,
181                                 LDAP_PROTOCOL_ERROR, "sasl bind requires LDAPv3" );
182                         rc = -1;
183                         goto cleanup;
184                 }
185
186                 if( mech == NULL || *mech == '\0' ) {
187                         Debug( LDAP_DEBUG_ANY,
188                                 "do_bind: no sasl mechanism provided\n",
189                                 0, 0, 0 );
190                         send_ldap_result( conn, op, rc = LDAP_AUTH_METHOD_NOT_SUPPORTED,
191                                 NULL, "no sasl mechanism provided", NULL, NULL );
192                         goto cleanup;
193                 }
194
195                 if( !charray_inlist( supportedSASLMechanisms, mech ) ) {
196                         Debug( LDAP_DEBUG_ANY,
197                                 "do_bind: sasl mechanism=\"%s\" not supported.\n",
198                                 mech, 0, 0 );
199                         send_ldap_result( conn, op, rc = LDAP_AUTH_METHOD_NOT_SUPPORTED,
200                                 NULL, "sasl mechanism not supported", NULL, NULL );
201                         goto cleanup;
202                 }
203
204                 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
205
206                 if ( conn->c_authmech != NULL ) {
207                         assert( conn->c_bind_in_progress );
208
209                         if((strcmp(conn->c_authmech, mech) != 0)) {
210                                 /* mechanism changed, cancel in progress bind */
211                                 conn->c_bind_in_progress = 0;
212                                 if( conn->c_authstate != NULL ) {
213                                         free(conn->c_authstate);
214                                         conn->c_authstate = NULL;
215                                 }
216                                 free(conn->c_authmech);
217                                 conn->c_authmech = NULL;
218                         }
219
220 #ifdef LDAP_DEBUG
221                 } else {
222                         assert( !conn->c_bind_in_progress );
223                         assert( conn->c_authmech == NULL );
224                         assert( conn->c_authstate == NULL );
225 #endif
226                 }
227
228         } else {
229                 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
230
231                 if ( conn->c_authmech != NULL ) {
232                         assert( conn->c_bind_in_progress );
233
234                         /* cancel in progress bind */
235                         conn->c_bind_in_progress = 0;
236
237                         if( conn->c_authstate != NULL ) {
238                                 free(conn->c_authstate);
239                                 conn->c_authstate = NULL;
240                         }
241                         free(conn->c_authmech);
242                         conn->c_authmech = NULL;
243                 }
244         }
245
246         conn->c_protocol = version;
247         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
248
249         /* accept null binds */
250         if ( ndn == NULL || *ndn == '\0' ) {
251                 /*
252                  * we already forced connection to "anonymous", we just
253                  * need to send success
254                  */
255                 send_ldap_result( conn, op, LDAP_SUCCESS,
256                         NULL, NULL, NULL, NULL );
257                 goto cleanup;
258         }
259
260         /*
261          * We could be serving multiple database backends.  Select the
262          * appropriate one, or send a referral to our "referral server"
263          * if we don't hold it.
264          */
265
266         if ( (be = select_backend( ndn )) == NULL ) {
267                 if ( cred.bv_len == 0 ) {
268                         send_ldap_result( conn, op, LDAP_SUCCESS,
269                                 NULL, NULL, NULL, NULL );
270
271                 } else if ( default_referral ) {
272                         send_ldap_result( conn, op, rc = LDAP_REFERRAL,
273                                 NULL, NULL, default_referral, NULL );
274
275                 } else {
276                         send_ldap_result( conn, op, rc = LDAP_INVALID_CREDENTIALS,
277                                 NULL, NULL, NULL, NULL );
278                 }
279
280                 goto cleanup;
281         }
282
283         if ( be->be_bind ) {
284                 /* alias suffix */
285                 char *edn;
286
287                 /* deref suffix alias if appropriate */
288                 ndn = suffix_alias( be, ndn );
289
290                 if ( (*be->be_bind)( be, conn, op, dn, ndn, method, mech, &cred, &edn ) == 0 ) {
291                         ldap_pvt_thread_mutex_lock( &conn->c_mutex );
292
293                         conn->c_cdn = dn;
294                         dn = NULL;
295
296                         if(edn != NULL) {
297                                 conn->c_dn = edn;
298                         } else {
299                                 conn->c_dn = ndn;
300                                 ndn = NULL;
301                         }
302
303                         Debug( LDAP_DEBUG_TRACE, "do_bind: bound \"%s\" to \"%s\"\n",
304                         conn->c_cdn, conn->c_dn, method );
305
306                         ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
307
308                         /* send this here to avoid a race condition */
309                         send_ldap_result( conn, op, LDAP_SUCCESS,
310                                 NULL, NULL, NULL, NULL );
311
312                 } else if (edn != NULL) {
313                         free( edn );
314                 }
315
316         } else {
317                 send_ldap_result( conn, op, rc = LDAP_UNWILLING_TO_PERFORM,
318                         NULL, "Function not implemented", NULL, NULL );
319         }
320
321 cleanup:
322         if( dn != NULL ) {
323                 free( dn );
324         }
325         if( ndn != NULL ) {
326                 free( ndn );
327         }
328         if ( mech != NULL ) {
329                 free( mech );
330         }
331         if ( cred.bv_val != NULL ) {
332                 free( cred.bv_val );
333         }
334
335         return rc;
336 }