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