]> git.sur5r.net Git - openldap/blob - servers/slapd/sasl.c
Latest changes from HEAD.
[openldap] / servers / slapd / sasl.c
1 /* $OpenLDAP$ */
2 /*
3  * Copyright 1998-2002 The OpenLDAP Foundation, All Rights Reserved.
4  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
5  */
6
7 #include "portable.h"
8
9 #include <stdio.h>
10 #include <ac/stdlib.h>
11 #include <ac/string.h>
12
13 #include <lber.h>
14 #include <ldap_log.h>
15
16 #include "slap.h"
17
18 #ifdef HAVE_CYRUS_SASL
19 #include <limits.h>
20
21 #ifdef HAVE_SASL_SASL_H
22 #include <sasl/sasl.h>
23 #else
24 #include <sasl.h>
25 #endif
26
27 #if SASL_VERSION_MAJOR >= 2
28 #include <lutil.h>
29 #define SASL_CONST const
30 #else
31 #define SASL_CONST
32 #endif
33
34 #include <ldap_pvt.h>
35
36 #ifdef SLAPD_SPASSWD
37 #include <lutil.h>
38 #endif
39
40 /* Flags for telling slap_sasl_getdn() what type of identity is being passed */
41 #define FLAG_GETDN_AUTHCID 2
42 #define FLAG_GETDN_AUTHZID 4
43
44 static sasl_security_properties_t sasl_secprops;
45
46 static int
47 slap_sasl_log(
48         void *context,
49         int priority,
50         const char *message) 
51 {
52         Connection *conn = context;
53         int level;
54         const char * label;
55
56         if ( message == NULL ) {
57                 return SASL_BADPARAM;
58         }
59
60         switch (priority) {
61 #if SASL_VERSION_MAJOR >= 2
62         case SASL_LOG_NONE:
63                 level = LDAP_DEBUG_NONE;
64                 label = "None";
65                 break;
66         case SASL_LOG_ERR:
67                 level = LDAP_DEBUG_ANY;
68                 label = "Error";
69                 break;
70         case SASL_LOG_FAIL:
71                 level = LDAP_DEBUG_ANY;
72                 label = "Failure";
73                 break;
74         case SASL_LOG_WARN:
75                 level = LDAP_DEBUG_TRACE;
76                 label = "Warning";
77                 break;
78         case SASL_LOG_NOTE:
79                 level = LDAP_DEBUG_TRACE;
80                 label = "Notice";
81                 break;
82         case SASL_LOG_DEBUG:
83                 level = LDAP_DEBUG_TRACE;
84                 label = "Debug";
85                 break;
86         case SASL_LOG_TRACE:
87                 level = LDAP_DEBUG_TRACE;
88                 label = "Trace";
89                 break;
90         case SASL_LOG_PASS:
91                 level = LDAP_DEBUG_TRACE;
92                 label = "Password Trace";
93                 break;
94 #else
95         case SASL_LOG_ERR:
96                 level = LDAP_DEBUG_ANY;
97                 label = "Error";
98                 break;
99         case SASL_LOG_WARNING:
100                 level = LDAP_DEBUG_TRACE;
101                 label = "Warning";
102                 break;
103         case SASL_LOG_INFO:
104                 level = LDAP_DEBUG_TRACE;
105                 label = "Info";
106                 break;
107 #endif
108         default:
109                 return SASL_BADPARAM;
110         }
111
112 #ifdef NEW_LOGGING
113         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
114                 "SASL [conn=%ld] %s: %s\n",
115                 conn ? conn->c_connid : -1,
116                 label, message ));
117 #else
118         Debug( level, "SASL [conn=%ld] %s: %s\n",
119                 conn ? conn->c_connid: -1,
120                 label, message );
121 #endif
122
123
124         return SASL_OK;
125 }
126
127
128 /* Take any sort of identity string and return a DN with the "dn:" prefix. The
129    string returned in *dn is in its own allocated memory, and must be free'd 
130    by the calling process.
131    -Mark Adamson, Carnegie Mellon
132
133    The "dn:" prefix is no longer used anywhere inside slapd. It is only used
134    on strings passed in directly from SASL.
135    -Howard Chu, Symas Corp.
136 */
137
138 #define SET_DN  1
139 #define SET_U   2
140
141 static struct berval ext_bv = { sizeof("EXTERNAL")-1, "EXTERNAL" };
142
143 int slap_sasl_getdn( Connection *conn, char *id, int len,
144         char *user_realm, struct berval *dn, int flags )
145 {
146         char *c1;
147         int rc, is_dn = 0, do_norm = 1;
148         sasl_conn_t *ctx;
149         struct berval dn2;
150
151 #ifdef NEW_LOGGING
152         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
153                 "slap_sasl_getdn: conn %d id=%s\n",
154                 conn ? conn->c_connid : -1,
155                 id ? (*id ? id : "<empty>") : "NULL" ));
156 #else
157         Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: id=%s\n", 
158       id?(*id?id:"<empty>"):"NULL",0,0 );
159 #endif
160
161         dn->bv_val = NULL;
162         dn->bv_len = 0;
163
164         /* Blatantly anonymous ID */
165         if( id &&
166                 ( id[sizeof( "anonymous" )-1] == '\0'
167                         || id[sizeof( "anonymous" )-1] == '@' ) &&
168                 !strncasecmp( id, "anonymous", sizeof( "anonymous" )-1) ) {
169                 return( LDAP_SUCCESS );
170         }
171         ctx = conn->c_sasl_context;
172         if ( len == 0 ) len = strlen( id );
173
174         /* An authcID needs to be converted to authzID form */
175         if( flags & FLAG_GETDN_AUTHCID ) {
176                 if( conn->c_is_tls && conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len
177                         && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) ) {
178                         /* X.509 DN is already normalized */
179                         do_norm = 0;
180                         is_dn = SET_DN;
181
182                 } else {
183                         /* convert to u:<username> form */
184                         is_dn = SET_U;
185                 }
186                 ber_str2bv( id, len, 1, dn );
187         }
188         if( !is_dn ) {
189                 if( !strncasecmp( id, "u:", sizeof("u:")-1 )) {
190                         is_dn = SET_U;
191                         ber_str2bv( id+2, len-2, 1, dn );
192                 } else if ( !strncasecmp( id, "dn:", sizeof("dn:")-1) ) {
193                         is_dn = SET_DN;
194                         ber_str2bv( id+3, len-3, 1, dn );
195                 }
196         }
197
198         /* An authzID must be properly prefixed */
199         if( (flags & FLAG_GETDN_AUTHZID) && !is_dn ) {
200                 free( dn->bv_val );
201                 dn->bv_val = NULL;
202                 dn->bv_len = 0;
203                 return( LDAP_INAPPROPRIATE_AUTH );
204         }
205
206         /* Username strings */
207         if( is_dn == SET_U ) {
208                 char *p;
209                 len = dn->bv_len + sizeof("uid=")-1 + sizeof(",cn=auth")-1;
210
211                 if( user_realm && *user_realm ) {
212                         len += strlen( user_realm ) + sizeof(",cn=")-1;
213                 }
214
215                 if( conn->c_sasl_bind_mech.bv_len ) {
216                         len += conn->c_sasl_bind_mech.bv_len + sizeof(",cn=")-1;
217                 }
218
219                 /* Build the new dn */
220                 c1 = dn->bv_val;
221                 dn->bv_val = ch_malloc( len+1 );
222                 p = slap_strcopy( dn->bv_val, "uid=" );
223                 p = slap_strcopy( p, c1 );
224                 ch_free( c1 );
225
226                 if( user_realm && *user_realm ) {
227                         p = slap_strcopy( p, ",cn=" );
228                         p = slap_strcopy( p, user_realm );
229                 }
230                 if( conn->c_sasl_bind_mech.bv_len ) {
231                         p = slap_strcopy( p, ",cn=" );
232                         p = slap_strcopy( p, conn->c_sasl_bind_mech.bv_val );
233                 }
234                 p = slap_strcopy( p, ",cn=auth" );
235                 dn->bv_len = p - dn->bv_val;
236                 is_dn = SET_DN;
237
238 #ifdef NEW_LOGGING
239                 LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
240                         "slap_sasl_getdn: u:id converted to %s.\n", dn->bv_val ));
241 #else
242                 Debug( LDAP_DEBUG_TRACE, "getdn: u:id converted to %s\n", dn->bv_val,0,0 );
243 #endif
244         }
245
246         /* DN strings that are a cn=auth identity to run through regexp */
247         if( is_dn == SET_DN )
248         {
249                 slap_sasl2dn( dn, &dn2 );
250                 if( dn2.bv_val ) {
251                         ch_free( dn->bv_val );
252                         *dn = dn2;
253                         do_norm = 0;    /* slap_sasl2dn normalizes */
254 #ifdef NEW_LOGGING
255                         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
256                                 "slap_sasl_getdn: dn:id converted to %s.\n", dn->bv_val ));
257 #else
258                         Debug( LDAP_DEBUG_TRACE, "getdn: dn:id converted to %s\n",
259                                 dn->bv_val, 0, 0 );
260 #endif
261                 }
262         }
263
264         if ( do_norm ) {
265                 rc = dnNormalize2( NULL, dn, &dn2 );
266                 free(dn->bv_val);
267                 if ( rc != LDAP_SUCCESS ) {
268                         *dn = slap_empty_bv;
269                         return rc;
270                 }
271                 *dn = dn2;
272         }
273
274         return( LDAP_SUCCESS );
275 }
276
277 #if SASL_VERSION_MAJOR >= 2
278 static int
279 slap_sasl_checkpass(
280         sasl_conn_t *sconn,
281         void *context,
282         const char *username,
283         const char *pass,
284         unsigned passlen,
285         struct propctx *propctx)
286 {
287         Connection *conn = (Connection *)context;
288         struct berval dn, cred;
289         int rc;
290         BerVarray vals, bv;
291
292         cred.bv_val = (char *)pass;
293         cred.bv_len = passlen;
294
295         /* SASL will fallback to its own mechanisms if we don't
296          * find an answer here.
297          */
298
299         rc = slap_sasl_getdn( conn, (char *)username, 0, NULL, &dn,
300                 FLAG_GETDN_AUTHCID );
301         if ( rc != LDAP_SUCCESS ) {
302                 sasl_seterror( sconn, 0, ldap_err2string( rc ) );
303                 return SASL_NOUSER;
304         }
305
306         if ( dn.bv_len == 0 ) {
307                 sasl_seterror( sconn, 0,
308                         "No password is associated with the Root DSE" );
309                 if ( dn.bv_val != NULL ) {
310                         ch_free( dn.bv_val );
311                 }
312                 return SASL_NOUSER;
313         }
314
315         rc = backend_attribute( NULL, NULL, NULL, NULL, &dn,
316                 slap_schema.si_ad_userPassword, &vals);
317         if ( rc != LDAP_SUCCESS ) {
318                 ch_free( dn.bv_val );
319                 sasl_seterror( sconn, 0, ldap_err2string( rc ) );
320                 return SASL_NOVERIFY;
321         }
322
323         rc = SASL_NOVERIFY;
324
325         if ( vals != NULL ) {
326                 for ( bv = vals; bv->bv_val != NULL; bv++ ) {
327                         if ( !lutil_passwd( bv, &cred, NULL ) ) {
328                                 rc = SASL_OK;
329                                 break;
330                         }
331                 }
332                 ber_bvarray_free( vals );
333         }
334
335         if ( rc != SASL_OK ) {
336                 sasl_seterror( sconn, 0,
337                         ldap_err2string( LDAP_INVALID_CREDENTIALS ) );
338         }
339
340         ch_free( dn.bv_val );
341
342         return rc;
343 }
344
345 #if 0   /* CANON isn't for what you think it does. */
346 static int
347 slap_sasl_canonicalize(
348         sasl_conn_t *sconn,
349         void *context,
350         const char *in,
351         unsigned inlen,
352         unsigned flags,
353         const char *user_realm,
354         char *out,
355         unsigned out_max,
356         unsigned *out_len)
357 {
358         Connection *conn = (Connection *)context;
359         struct berval dn;
360         int rc;
361
362         *out_len = 0;
363
364 #ifdef NEW_LOGGING
365         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
366                 "slap_sasl_canonicalize: conn %d %s=\"%s\"\n",
367                         conn ? conn->c_connid : -1,
368                         (flags == SASL_CU_AUTHID) ? "authcid" : "authzid",
369                         in ? in : "<empty>" ));
370 #else
371         Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: "
372                 "%s=\"%s\"\n",
373                         conn ? conn->c_connid : -1,
374                         (flags == SASL_CU_AUTHID) ? "authcid" : "authzid",
375                         in ? in : "<empty>" );
376 #endif
377
378         rc = slap_sasl_getdn( conn, (char *)in, inlen, (char *)user_realm, &dn,
379                 (flags == SASL_CU_AUTHID) ? FLAG_GETDN_AUTHCID : FLAG_GETDN_AUTHZID );
380         if ( rc != LDAP_SUCCESS ) {
381                 sasl_seterror( sconn, 0, ldap_err2string( rc ) );
382                 return SASL_NOAUTHZ;
383         }               
384
385         if ( out_max < dn.bv_len ) {
386                 return SASL_BUFOVER;
387         }
388
389         AC_MEMCPY( out, dn.bv_val, dn.bv_len );
390         out[dn.bv_len] = '\0';
391
392         *out_len = dn.bv_len;
393
394         ch_free( dn.bv_val );
395
396 #ifdef NEW_LOGGING
397         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
398                 "slap_sasl_canonicalize: conn %d %s=\"%s\"\n",
399                         conn ? conn->c_connid : -1,
400                         (flags == SASL_CU_AUTHID) ? "authcDN" : "authzDN",
401                         out ));
402 #else
403         Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: "
404                 "%s=\"%s\"\n",
405                         conn ? conn->c_connid : -1,
406                         (flags == SASL_CU_AUTHID) ? "authcDN" : "authzDN",
407                         out );
408 #endif
409
410         return SASL_OK;
411 }
412 #endif
413
414 #define CANON_BUF_SIZE  256     /* from saslint.h */
415
416 static int
417 slap_sasl_authorize(
418         sasl_conn_t *sconn,
419         void *context,
420         char *requested_user,
421         unsigned rlen,
422         char *auth_identity,
423         unsigned alen,
424         const char *def_realm,
425         unsigned urlen,
426         struct propctx *propctx)
427 {
428         Connection *conn = (Connection *)context;
429         struct berval authcDN, authzDN;
430         char *realm;
431         int rc, equal = 1, ext = 0;
432
433 #ifdef NEW_LOGGING
434         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
435                 "slap_sasl_authorize: conn %d authcid=\"%s\" authzid=\"%s\"\n",
436                         conn ? conn->c_connid : -1, auth_identity, requested_user));
437 #else
438         Debug( LDAP_DEBUG_ARGS, "SASL Authorize [conn=%ld]: "
439                 "authcid=\"%s\" authzid=\"%s\"\n",
440                 conn ? conn->c_connid : -1, auth_identity, requested_user );
441 #endif
442
443         if ( requested_user )
444                 equal = !strcmp( auth_identity, requested_user );
445
446         /* If using SASL-EXTERNAL, don't modify the ID in any way */
447         if ( conn->c_is_tls && conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len
448                 && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) ) {
449                 ext = 1;
450                 realm = NULL;
451         } else {
452         /* Else look for an embedded realm in the name */
453                 realm = strchr( auth_identity, '@' );
454                 if ( realm ) *realm++ = '\0';
455         }
456
457         rc = slap_sasl_getdn( conn, auth_identity, alen, realm ? realm : (char *)def_realm,
458                 &authcDN, FLAG_GETDN_AUTHCID );
459         if ( realm )
460                 realm[-1] = '@';
461
462         if ( rc != LDAP_SUCCESS ) {
463                 sasl_seterror( sconn, 0, ldap_err2string( rc ) );
464                 return SASL_NOAUTHZ;
465         }               
466
467         if ( equal ) {
468                 if ( authcDN.bv_len > CANON_BUF_SIZE ) {
469                         free( authcDN.bv_val );
470                         return SASL_BUFOVER;
471                 }
472                 AC_MEMCPY( requested_user, authcDN.bv_val, authcDN.bv_len );
473
474                 return SASL_OK;
475         }
476
477         if ( ext ) {
478                 realm = NULL;
479         } else {
480                 realm = strchr( requested_user, '@' );
481                 if ( realm ) *realm++ = '\0';
482         }
483
484         rc = slap_sasl_getdn( conn, requested_user, rlen, realm ? realm : (char *)def_realm,
485                 &authzDN, FLAG_GETDN_AUTHZID );
486         if ( realm )
487                 realm[-1] = '@';
488
489         if ( rc != LDAP_SUCCESS ) {
490                 free( authcDN.bv_val );
491                 sasl_seterror( sconn, 0, ldap_err2string( rc ) );
492                 return SASL_NOAUTHZ;
493         }
494
495         if (authzDN.bv_len > CANON_BUF_SIZE) {
496                 free( authcDN.bv_val );
497                 free( authzDN.bv_val );
498                 return SASL_BUFOVER;
499         }
500
501         rc = slap_sasl_authorized( &authcDN, &authzDN );
502         free( authcDN.bv_val );
503         if ( rc != LDAP_SUCCESS ) {
504 #ifdef NEW_LOGGING
505                 LDAP_LOG(( "sasl", LDAP_LEVEL_INFO,
506                            "slap_sasl_authorize: conn %ld  authorization disallowed (%d)\n",
507                            (long)(conn ? conn->c_connid : -1), rc ));
508 #else
509                 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
510                         " authorization disallowed (%d)\n",
511                         (long) (conn ? conn->c_connid : -1), rc, 0 );
512 #endif
513
514                 sasl_seterror( sconn, 0, "not authorized" );
515                 free( authzDN.bv_val );
516                 return SASL_NOAUTHZ;
517         }
518         AC_MEMCPY( requested_user, authzDN.bv_val, authzDN.bv_len );
519         free( authzDN.bv_val );
520
521 #ifdef NEW_LOGGING
522         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
523                    "slap_sasl_authorize: conn %d authorization allowed\n",
524                    (long)(conn ? conn->c_connid : -1 ) ));
525 #else
526         Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
527                 " authorization allowed\n",
528                 (long) (conn ? conn->c_connid : -1), 0, 0 );
529 #endif
530
531         return SASL_OK;
532
533 #else
534 static int
535 slap_sasl_authorize(
536         void *context,
537         char *authcid,
538         char *authzid,
539         const char **user,
540         const char **errstr)
541 {
542         struct berval authcDN, authzDN;
543         int rc, ext = 0;
544         Connection *conn = context;
545         char *realm, *xrealm;
546
547         *user = NULL;
548
549 #ifdef NEW_LOGGING
550         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
551                    "slap_sasl_authorize: conn %d         authcid=\"%s\" authzid=\"%s\"\n",
552                    conn ? conn->c_connid : -1,
553                    authcid ? authcid : "<empty>",
554                    authzid ? authzid : "<empty>" ));
555 #else
556         Debug( LDAP_DEBUG_ARGS, "SASL Authorize [conn=%ld]: "
557                 "authcid=\"%s\" authzid=\"%s\"\n",
558                 (long) (conn ? conn->c_connid : -1),
559                 authcid ? authcid : "<empty>",
560                 authzid ? authzid : "<empty>" );
561 #endif
562
563         /* Figure out how much data we have for the dn */
564         rc = sasl_getprop( conn->c_sasl_context, SASL_REALM, (void **)&realm );
565         if( rc != SASL_OK && rc != SASL_NOTDONE ) {
566 #ifdef NEW_LOGGING
567                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
568                         "slap_sasl_authorize: getprop(REALM) failed.\n" ));
569 #else
570                 Debug(LDAP_DEBUG_TRACE,
571                         "authorize: getprop(REALM) failed!\n", 0,0,0);
572 #endif
573                 *errstr = "Could not extract realm";
574                 return SASL_NOAUTHZ;
575         }
576
577         /* Convert the identities to DN's. If no authzid was given, client will
578            be bound as the DN matching their username */
579         if ( conn->c_is_tls && conn->c_sasl_bind_mech.bv_len == ext_bv.bv_len
580                 && ( strcasecmp( ext_bv.bv_val, conn->c_sasl_bind_mech.bv_val ) == 0 ) ) {
581                 ext = 1;
582                 xrealm = NULL;
583         } else {
584                 xrealm = strchr( authcid, '@' );
585                 if ( xrealm ) *xrealm++ = '\0';
586         }
587         rc = slap_sasl_getdn( conn, (char *)authcid, 0, xrealm ? xrealm : realm, &authcDN, FLAG_GETDN_AUTHCID );
588         if ( xrealm ) xrealm[-1] = '@';
589         if( rc != LDAP_SUCCESS ) {
590                 *errstr = ldap_err2string( rc );
591                 return SASL_NOAUTHZ;
592         }
593         if( ( authzid == NULL ) || !strcmp( authcid,authzid ) ) {
594 #ifdef NEW_LOGGING
595                 LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
596                            "slap_sasl_authorize: conn %d  Using authcDN=%s\n",
597                            conn ? conn->c_connid : -1, authcDN.bv_val ));
598 #else
599                 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
600                  "Using authcDN=%s\n", (long) (conn ? conn->c_connid : -1), authcDN.bv_val,0 );
601 #endif
602
603                 *user = authcDN.bv_val;
604                 *errstr = NULL;
605                 return SASL_OK;
606         }
607         if ( ext ) {
608                 xrealm = NULL;
609         } else {
610                 xrealm = strchr( authzid, '@' );
611                 if ( xrealm ) *xrealm++ = '\0';
612         }
613         rc = slap_sasl_getdn( conn, (char *)authzid, 0, xrealm ? xrealm : realm, &authzDN, FLAG_GETDN_AUTHZID );
614         if ( xrealm ) xrealm[-1] = '@';
615         if( rc != LDAP_SUCCESS ) {
616                 ch_free( authcDN.bv_val );
617                 *errstr = ldap_err2string( rc );
618                 return SASL_NOAUTHZ;
619         }
620
621         rc = slap_sasl_authorized( &authcDN, &authzDN );
622         if( rc ) {
623 #ifdef NEW_LOGGING
624                 LDAP_LOG(( "sasl", LDAP_LEVEL_INFO,
625                            "slap_sasl_authorize: conn %ld  authorization disallowed (%d)\n",
626                            (long)(conn ? conn->c_connid : -1), rc ));
627 #else
628                 Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
629                         " authorization disallowed (%d)\n",
630                         (long) (conn ? conn->c_connid : -1), rc, 0 );
631 #endif
632
633                 *errstr = "not authorized";
634                 ch_free( authcDN.bv_val );
635                 ch_free( authzDN.bv_val );
636                 return SASL_NOAUTHZ;
637         }
638
639 #ifdef NEW_LOGGING
640         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
641                    "slap_sasl_authorize: conn %d authorization allowed\n",
642                    (long)(conn ? conn->c_connid : -1 ) ));
643 #else
644         Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
645                 " authorization allowed\n",
646                 (long) (conn ? conn->c_connid : -1), 0, 0 );
647 #endif
648
649
650         ch_free( authcDN.bv_val );
651         *user = authzDN.bv_val;
652         *errstr = NULL;
653         return SASL_OK;
654 }
655 #endif /* SASL_VERSION_MAJOR >= 2 */
656
657 static int
658 slap_sasl_err2ldap( int saslerr )
659 {
660         int rc;
661
662         switch (saslerr) {
663                 case SASL_CONTINUE:
664                         rc = LDAP_SASL_BIND_IN_PROGRESS;
665                         break;
666                 case SASL_FAIL:
667                         rc = LDAP_OTHER;
668                         break;
669                 case SASL_NOMEM:
670                         rc = LDAP_OTHER;
671                         break;
672                 case SASL_NOMECH:
673                         rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
674                         break;
675                 case SASL_BADAUTH:
676                         rc = LDAP_INVALID_CREDENTIALS;
677                         break;
678                 case SASL_NOAUTHZ:
679                         rc = LDAP_INSUFFICIENT_ACCESS;
680                         break;
681                 case SASL_TOOWEAK:
682                 case SASL_ENCRYPT:
683                         rc = LDAP_INAPPROPRIATE_AUTH;
684                         break;
685                 default:
686                         rc = LDAP_OTHER;
687                         break;
688         }
689
690         return rc;
691 }
692 #endif
693
694
695 int slap_sasl_init( void )
696 {
697 #ifdef HAVE_CYRUS_SASL
698         int rc;
699         static sasl_callback_t server_callbacks[] = {
700                 { SASL_CB_LOG, &slap_sasl_log, NULL },
701                 { SASL_CB_LIST_END, NULL, NULL }
702         };
703
704         sasl_set_alloc(
705                 ch_malloc,
706                 ch_calloc,
707                 ch_realloc,
708                 ch_free ); 
709
710         sasl_set_mutex(
711                 ldap_pvt_sasl_mutex_new,
712                 ldap_pvt_sasl_mutex_lock,
713                 ldap_pvt_sasl_mutex_unlock,
714                 ldap_pvt_sasl_mutex_dispose );
715
716         /* should provide callbacks for logging */
717         /* server name should be configurable */
718         rc = sasl_server_init( server_callbacks, "slapd" );
719
720         if( rc != SASL_OK ) {
721 #ifdef NEW_LOGGING
722                 LDAP_LOG(( "sasl", LDAP_LEVEL_INFO,
723                            "slap_sasl_init: init failed.\n" ));
724 #else
725                 Debug( LDAP_DEBUG_ANY, "sasl_server_init failed\n",
726                         0, 0, 0 );
727 #endif
728
729                 return -1;
730         }
731
732 #ifdef NEW_LOGGING
733         LDAP_LOG(( "sasl", LDAP_LEVEL_INFO,
734                    "slap_sasl_init: initialized!\n"));
735 #else
736         Debug( LDAP_DEBUG_TRACE, "slap_sasl_init: initialized!\n",
737                 0, 0, 0 );
738 #endif
739
740
741         /* default security properties */
742         memset( &sasl_secprops, '\0', sizeof(sasl_secprops) );
743         sasl_secprops.max_ssf = INT_MAX;
744         sasl_secprops.maxbufsize = 65536;
745         sasl_secprops.security_flags = SASL_SEC_NOPLAINTEXT|SASL_SEC_NOANONYMOUS;
746 #endif
747
748         return 0;
749 }
750
751 int slap_sasl_destroy( void )
752 {
753 #ifdef HAVE_CYRUS_SASL
754         sasl_done();
755 #endif
756         free( global_host );
757         global_host = NULL;
758
759         return 0;
760 }
761
762 int slap_sasl_open( Connection *conn )
763 {
764         int cb, sc = LDAP_SUCCESS;
765 #if SASL_VERSION_MAJOR >= 2
766         char *ipremoteport = NULL, *iplocalport = NULL;
767 #endif
768
769 #ifdef HAVE_CYRUS_SASL
770         sasl_conn_t *ctx = NULL;
771         sasl_callback_t *session_callbacks;
772
773         assert( conn->c_sasl_context == NULL );
774         assert( conn->c_sasl_extra == NULL );
775
776         conn->c_sasl_layers = 0;
777
778         session_callbacks =
779 #if SASL_VERSION_MAJOR >= 2
780                 ch_calloc( 5, sizeof(sasl_callback_t));
781 #else
782                 ch_calloc( 3, sizeof(sasl_callback_t));
783 #endif
784         conn->c_sasl_extra = session_callbacks;
785
786         session_callbacks[cb=0].id = SASL_CB_LOG;
787         session_callbacks[cb].proc = &slap_sasl_log;
788         session_callbacks[cb++].context = conn;
789
790         session_callbacks[cb].id = SASL_CB_PROXY_POLICY;
791         session_callbacks[cb].proc = &slap_sasl_authorize;
792         session_callbacks[cb++].context = conn;
793
794 #if SASL_VERSION_MAJOR >= 2
795 #if 0   /* CANON isn't for what you think it does. */
796         session_callbacks[cb].id = SASL_CB_CANON_USER;
797         session_callbacks[cb].proc = &slap_sasl_canonicalize;
798         session_callbacks[cb++].context = conn;
799 #endif
800
801         /* XXXX: this should be conditional */
802         session_callbacks[cb].id = SASL_CB_SERVER_USERDB_CHECKPASS;
803         session_callbacks[cb].proc = &slap_sasl_checkpass;
804         session_callbacks[cb++].context = conn;
805 #endif
806
807         session_callbacks[cb].id = SASL_CB_LIST_END;
808         session_callbacks[cb].proc = NULL;
809         session_callbacks[cb++].context = NULL;
810
811         if( global_host == NULL ) {
812                 global_host = ldap_pvt_get_fqdn( NULL );
813         }
814
815         /* create new SASL context */
816 #if SASL_VERSION_MAJOR >= 2
817         if ( conn->c_sock_name.bv_len != 0 &&
818              strncmp( conn->c_sock_name.bv_val, "IP=", 3 ) == 0) {
819                 char *p;
820
821                 iplocalport = ch_strdup( conn->c_sock_name.bv_val + 3 );
822                 /* Convert IPv6 addresses to address;port syntax. */
823                 p = strrchr( iplocalport, ' ' );
824                 /* Convert IPv4 addresses to address;port syntax. */
825                 if ( p == NULL ) p = strchr( iplocalport, ':' );
826                 if ( p != NULL ) {
827                         *p = ';';
828                 }
829         }
830         if ( conn->c_peer_name.bv_len != 0 &&
831              strncmp( conn->c_peer_name.bv_val, "IP=", 3 ) == 0) {
832                 char *p;
833
834                 ipremoteport = ch_strdup( conn->c_peer_name.bv_val + 3 );
835                 /* Convert IPv6 addresses to address;port syntax. */
836                 p = strrchr( ipremoteport, ' ' );
837                 /* Convert IPv4 addresses to address;port syntax. */
838                 if ( p == NULL ) p = strchr( ipremoteport, ':' );
839                 if ( p != NULL ) {
840                         *p = ';';
841                 }
842         }
843         sc = sasl_server_new( "ldap", global_host, global_realm,
844                 iplocalport, ipremoteport, session_callbacks, 0, &ctx );
845         if ( iplocalport != NULL ) {
846                 ch_free( iplocalport );
847         }
848         if ( ipremoteport != NULL ) {
849                 ch_free( ipremoteport );
850         }
851 #else
852         sc = sasl_server_new( "ldap", global_host, global_realm,
853                 session_callbacks, SASL_SECURITY_LAYER, &ctx );
854 #endif
855
856         if( sc != SASL_OK ) {
857 #ifdef NEW_LOGGING
858                 LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
859                            "slap_sasl_open: sasl_server_new failed: %d\n", sc ));
860 #else
861                 Debug( LDAP_DEBUG_ANY, "sasl_server_new failed: %d\n",
862                         sc, 0, 0 );
863 #endif
864
865                 return -1;
866         }
867
868         conn->c_sasl_context = ctx;
869
870         if( sc == SASL_OK ) {
871                 sc = sasl_setprop( ctx,
872                         SASL_SEC_PROPS, &sasl_secprops );
873
874                 if( sc != SASL_OK ) {
875 #ifdef NEW_LOGGING
876                         LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
877                                    "slap_sasl_open: sasl_setprop failed: %d \n", sc ));
878 #else
879                         Debug( LDAP_DEBUG_ANY, "sasl_setprop failed: %d\n",
880                                 sc, 0, 0 );
881 #endif
882
883                         slap_sasl_close( conn );
884                         return -1;
885                 }
886         }
887
888         sc = slap_sasl_err2ldap( sc );
889 #endif
890         return sc;
891 }
892
893 int slap_sasl_external(
894         Connection *conn,
895         slap_ssf_t ssf,
896         const char *auth_id )
897 {
898 #if SASL_VERSION_MAJOR >= 2
899         int sc;
900         sasl_conn_t *ctx = conn->c_sasl_context;
901
902         if ( ctx == NULL ) {
903                 return LDAP_UNAVAILABLE;
904         }
905
906         sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf );
907
908         if ( sc != SASL_OK ) {
909                 return LDAP_OTHER;
910         }
911
912         sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, auth_id );
913
914         if ( sc != SASL_OK ) {
915                 return LDAP_OTHER;
916         }
917
918 #elif defined(HAVE_CYRUS_SASL)
919         int sc;
920         sasl_conn_t *ctx = conn->c_sasl_context;
921         sasl_external_properties_t extprops;
922
923         if ( ctx == NULL ) {
924                 return LDAP_UNAVAILABLE;
925         }
926
927         memset( &extprops, '\0', sizeof(extprops) );
928         extprops.ssf = ssf;
929         extprops.auth_id = (char *) auth_id;
930
931         sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
932                 (void *) &extprops );
933
934         if ( sc != SASL_OK ) {
935                 return LDAP_OTHER;
936         }
937 #endif
938
939         return LDAP_SUCCESS;
940 }
941
942 int slap_sasl_reset( Connection *conn )
943 {
944 #ifdef HAVE_CYRUS_SASL
945         sasl_conn_t *ctx = conn->c_sasl_context;
946
947         if( ctx != NULL ) {
948         }
949 #endif
950         /* must return "anonymous" */
951         return LDAP_SUCCESS;
952 }
953
954 char ** slap_sasl_mechs( Connection *conn )
955 {
956         char **mechs = NULL;
957
958 #ifdef HAVE_CYRUS_SASL
959         sasl_conn_t *ctx = conn->c_sasl_context;
960
961         if( ctx != NULL ) {
962                 int sc;
963                 SASL_CONST char *mechstr;
964
965                 sc = sasl_listmech( ctx,
966                         NULL, NULL, ",", NULL,
967                         &mechstr, NULL, NULL );
968
969                 if( sc != SASL_OK ) {
970 #ifdef NEW_LOGGING
971                         LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
972                                 "slap_sasl_mechs: sasl_listmech failed: %d\n", sc ));
973 #else
974                         Debug( LDAP_DEBUG_ANY, "slap_sasl_listmech failed: %d\n",
975                                 sc, 0, 0 );
976 #endif
977
978                         return NULL;
979                 }
980
981                 mechs = str2charray( mechstr, "," );
982
983 #if SASL_VERSION_MAJOR < 2
984                 ch_free( mechstr );
985 #endif
986         }
987 #endif
988
989         return mechs;
990 }
991
992 int slap_sasl_close( Connection *conn )
993 {
994 #ifdef HAVE_CYRUS_SASL
995         sasl_conn_t *ctx = conn->c_sasl_context;
996
997         if( ctx != NULL ) {
998                 sasl_dispose( &ctx );
999         }
1000
1001         conn->c_sasl_context = NULL;
1002
1003         free( conn->c_sasl_extra );
1004         conn->c_sasl_extra = NULL;
1005 #endif
1006
1007         return LDAP_SUCCESS;
1008 }
1009
1010 int slap_sasl_bind(
1011     Connection          *conn,
1012     Operation           *op,  
1013     struct berval       *dn,  
1014     struct berval       *ndn,
1015     struct berval       *cred,
1016         struct berval                   *edn,
1017         slap_ssf_t              *ssfp )
1018 {
1019         int rc = 1;
1020
1021 #ifdef HAVE_CYRUS_SASL
1022         sasl_conn_t *ctx = conn->c_sasl_context;
1023         struct berval response;
1024         unsigned reslen = 0;
1025         const char *errstr = NULL;
1026         int sc;
1027
1028 #ifdef NEW_LOGGING
1029         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
1030                 "sasl_bind: conn %ld dn=\"%s\" mech=%s datalen=%ld\n",
1031                 conn->c_connid,
1032                 dn->bv_len ? dn->bv_val : "",
1033                 conn->c_sasl_bind_in_progress ? "<continuing>" : conn->c_sasl_bind_mech.bv_val,
1034                 cred ? cred->bv_len : 0 ));
1035 #else
1036         Debug(LDAP_DEBUG_ARGS,
1037                 "==> sasl_bind: dn=\"%s\" mech=%s datalen=%ld\n",
1038                 dn->bv_len ? dn->bv_val : "",
1039                 conn->c_sasl_bind_in_progress ? "<continuing>":conn->c_sasl_bind_mech.bv_val,
1040                 cred ? cred->bv_len : 0 );
1041 #endif
1042
1043
1044         if( ctx == NULL ) {
1045                 send_ldap_result( conn, op, LDAP_UNAVAILABLE,
1046                         NULL, "SASL unavailable on this session", NULL, NULL );
1047                 return rc;
1048         }
1049
1050 #if SASL_VERSION_MAJOR >= 2
1051 #define START( ctx, mech, cred, clen, resp, rlen, err ) \
1052         sasl_server_start( ctx, mech, cred, clen, resp, rlen )
1053 #define STEP( ctx, cred, clen, resp, rlen, err ) \
1054         sasl_server_step( ctx, cred, clen, resp, rlen )
1055 #else
1056 #define START( ctx, mech, cred, clen, resp, rlen, err ) \
1057         sasl_server_start( ctx, mech, cred, clen, resp, rlen, err )
1058 #define STEP( ctx, cred, clen, resp, rlen, err ) \
1059         sasl_server_step( ctx, cred, clen, resp, rlen, err )
1060 #endif
1061
1062         if ( !conn->c_sasl_bind_in_progress ) {
1063                 sc = START( ctx,
1064                         conn->c_sasl_bind_mech.bv_val,
1065                         cred->bv_len ? cred->bv_val : "",
1066                         cred->bv_len,
1067                         (SASL_CONST char **)&response.bv_val, &reslen, &errstr );
1068
1069         } else {
1070                 sc = STEP( ctx,
1071                         cred->bv_val, cred->bv_len,
1072                         (SASL_CONST char **)&response.bv_val, &reslen, &errstr );
1073         }
1074
1075         response.bv_len = reslen;
1076
1077         if ( sc == SASL_OK ) {
1078                 char *username = NULL;
1079
1080                 sc = sasl_getprop( ctx,
1081                         SASL_USERNAME, (SASL_CONST void **)&username );
1082
1083                 if ( sc != SASL_OK ) {
1084 #ifdef NEW_LOGGING
1085                         LDAP_LOG(( "sasl", LDAP_LEVEL_ERR,
1086                                 "slap_sasl_bind: getprop(USERNAME) failed: %d\n", sc ));
1087 #else
1088                         Debug(LDAP_DEBUG_TRACE,
1089                                 "slap_sasl_bind: getprop(USERNAME) failed!\n",
1090                                 0, 0, 0);
1091 #endif
1092
1093
1094                         send_ldap_result( conn, op, rc = slap_sasl_err2ldap( sc ),
1095                                 NULL, "no SASL username", NULL, NULL );
1096
1097                 } else {
1098                         sasl_ssf_t *ssf = NULL;
1099
1100                         rc = LDAP_SUCCESS;
1101                         ber_str2bv( username, 0, 1, edn );
1102
1103                         (void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
1104                         *ssfp = ssf ? *ssf : 0;
1105
1106                         if( *ssfp ) {
1107                                 ldap_pvt_thread_mutex_lock( &conn->c_mutex );
1108                                 conn->c_sasl_layers++;
1109                                 ldap_pvt_thread_mutex_unlock( &conn->c_mutex );
1110                         }
1111
1112                         send_ldap_sasl( conn, op, rc,
1113                                 NULL, NULL, NULL, NULL,
1114                                 response.bv_len ? &response : NULL );
1115                 }
1116
1117         } else if ( sc == SASL_CONTINUE ) {
1118                 send_ldap_sasl( conn, op, rc = LDAP_SASL_BIND_IN_PROGRESS,
1119                         NULL, NULL, NULL, NULL, &response );
1120
1121         } else {
1122 #if SASL_VERSION_MAJOR >= 2
1123                 errstr = sasl_errdetail( ctx );
1124 #endif
1125                 send_ldap_result( conn, op, rc = slap_sasl_err2ldap( sc ),
1126                         NULL, errstr, NULL, NULL );
1127         }
1128
1129 #if SASL_VERSION_MAJOR < 2
1130         if( response.bv_len ) {
1131                 ch_free( response.bv_val );
1132         }
1133 #endif
1134
1135 #ifdef NEW_LOGGING
1136         LDAP_LOG(( "sasl", LDAP_LEVEL_ENTRY,
1137                 "slap_sasl_bind: rc=%d\n", rc ));
1138 #else
1139         Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: rc=%d\n", rc, 0, 0);
1140 #endif
1141
1142
1143 #else
1144         send_ldap_result( conn, op, rc = LDAP_UNAVAILABLE,
1145                 NULL, "SASL not supported", NULL, NULL );
1146 #endif
1147
1148         return rc;
1149 }
1150
1151 char* slap_sasl_secprops( const char *in )
1152 {
1153 #ifdef HAVE_CYRUS_SASL
1154         int rc = ldap_pvt_sasl_secprops( in, &sasl_secprops );
1155
1156         return rc == LDAP_SUCCESS ? NULL : "Invalid security properties";
1157 #else
1158         return "SASL not supported";
1159 #endif
1160 }