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