1 /*        $NetBSD: sasl.c,v 1.3 2021/08/14 16:14:58 christos Exp $    */
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1998-2021 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 
18 #include <sys/cdefs.h>
19 __RCSID("$NetBSD: sasl.c,v 1.3 2021/08/14 16:14:58 christos Exp $");
20 
21 #include "portable.h"
22 
23 #include <stdio.h>
24 #ifdef HAVE_LIMITS_H
25 #include <limits.h>
26 #endif
27 
28 #include <ac/stdlib.h>
29 #include <ac/string.h>
30 
31 #include <lber.h>
32 #include <ldap_log.h>
33 
34 #include "slap.h"
35 
36 #include <rewrite.h>
37 
38 #ifdef HAVE_CYRUS_SASL
39 # ifdef HAVE_SASL_SASL_H
40 #  include <sasl/sasl.h>
41 #  include <sasl/saslplug.h>
42 # else
43 #  include <sasl.h>
44 #  include <saslplug.h>
45 # endif
46 
47 # define  SASL_CONST const
48 
49 #define SASL_VERSION_FULL     ((SASL_VERSION_MAJOR << 16) |\
50           (SASL_VERSION_MINOR << 8) | SASL_VERSION_STEP)
51 
52 #if SASL_VERSION_FULL >= 0x020119 /* 2.1.25 */
53 typedef sasl_callback_ft slap_sasl_cb_ft;
54 #else
55 typedef int (*slap_sasl_cb_ft)();
56 #endif
57 
58 static sasl_security_properties_t sasl_secprops;
59 #elif defined( SLAP_BUILTIN_SASL )
60 /*
61  * built-in SASL implementation
62  *        only supports EXTERNAL
63  */
64 typedef struct sasl_ctx {
65           slap_ssf_t sc_external_ssf;
66           struct berval sc_external_id;
67 } SASL_CTX;
68 
69 #endif
70 
71 #include <lutil.h>
72 
73 static struct berval ext_bv = BER_BVC( "EXTERNAL" );
74 
75 char *slap_sasl_auxprops;
76 
77 #ifdef HAVE_CYRUS_SASL
78 
79 /* Just use our internal auxprop by default */
80 static int
slap_sasl_getopt(void * context,const char * plugin_name,const char * option,const char ** result,unsigned * len)81 slap_sasl_getopt(
82           void *context,
83           const char *plugin_name,
84           const char *option,
85           const char **result,
86           unsigned *len)
87 {
88           if ( strcmp( option, "auxprop_plugin" )) {
89                     return SASL_FAIL;
90           }
91           if ( slap_sasl_auxprops )
92                     *result = slap_sasl_auxprops;
93           else
94                     *result = "slapd";
95           return SASL_OK;
96 }
97 
98 int
slap_sasl_log(void * context,int priority,const char * message)99 slap_sasl_log(
100           void *context,
101           int priority,
102           const char *message)
103 {
104           Connection *conn = context;
105           int level;
106           const char * label;
107 
108           if ( message == NULL ) {
109                     return SASL_BADPARAM;
110           }
111 
112           switch (priority) {
113           case SASL_LOG_NONE:
114                     level = LDAP_DEBUG_NONE;
115                     label = "None";
116                     break;
117           case SASL_LOG_ERR:
118                     level = LDAP_DEBUG_ANY;
119                     label = "Error";
120                     break;
121           case SASL_LOG_FAIL:
122                     level = LDAP_DEBUG_ANY;
123                     label = "Failure";
124                     break;
125           case SASL_LOG_WARN:
126                     level = LDAP_DEBUG_TRACE;
127                     label = "Warning";
128                     break;
129           case SASL_LOG_NOTE:
130                     level = LDAP_DEBUG_TRACE;
131                     label = "Notice";
132                     break;
133           case SASL_LOG_DEBUG:
134                     level = LDAP_DEBUG_TRACE;
135                     label = "Debug";
136                     break;
137           case SASL_LOG_TRACE:
138                     level = LDAP_DEBUG_TRACE;
139                     label = "Trace";
140                     break;
141           case SASL_LOG_PASS:
142                     level = LDAP_DEBUG_TRACE;
143                     label = "Password Trace";
144                     break;
145           default:
146                     return SASL_BADPARAM;
147           }
148 
149           Debug( level, "SASL [conn=%ld] %s: %s\n",
150                     conn ? (long) conn->c_connid: -1L,
151                     label, message );
152 
153 
154           return SASL_OK;
155 }
156 
157 static const char *slap_propnames[] = {
158           "*slapConn", "*slapAuthcDNlen", "*slapAuthcDN",
159           "*slapAuthzDNlen", "*slapAuthzDN", NULL };
160 
161 #ifdef SLAP_AUXPROP_DONTUSECOPY
162 int slap_dontUseCopy_ignore;
163 BerVarray slap_dontUseCopy_propnames;
164 #endif /* SLAP_AUXPROP_DONTUSECOPY */
165 
166 static Filter generic_filter = { LDAP_FILTER_PRESENT, { 0 }, NULL };
167 static struct berval generic_filterstr = BER_BVC("(objectclass=*)");
168 
169 #define   SLAP_SASL_PROP_CONN 0
170 #define   SLAP_SASL_PROP_AUTHCLEN       1
171 #define   SLAP_SASL_PROP_AUTHC          2
172 #define   SLAP_SASL_PROP_AUTHZLEN       3
173 #define   SLAP_SASL_PROP_AUTHZ          4
174 #define   SLAP_SASL_PROP_COUNT          5         /* Number of properties we used */
175 
176 typedef struct lookup_info {
177           int flags;
178           const struct propval *list;
179           sasl_server_params_t *sparams;
180 } lookup_info;
181 
182 static slap_response sasl_ap_lookup;
183 
184 static struct berval sc_cleartext = BER_BVC("{CLEARTEXT}");
185 
186 static int
sasl_ap_lookup(Operation * op,SlapReply * rs)187 sasl_ap_lookup( Operation *op, SlapReply *rs )
188 {
189           BerVarray bv;
190           AttributeDescription *ad;
191           Attribute *a;
192           const char *text;
193           int rc, i;
194           lookup_info *sl = (lookup_info *)op->o_callback->sc_private;
195 
196           /* return the actual error code,
197            * to allow caller to handle specific errors
198            */
199           if (rs->sr_type != REP_SEARCH) return rs->sr_err;
200 
201           for( i = 0; sl->list[i].name; i++ ) {
202                     const char *name = sl->list[i].name;
203 
204                     if ( name[0] == '*' ) {
205                               if ( sl->flags & SASL_AUXPROP_AUTHZID ) continue;
206                               /* Skip our private properties */
207                               if ( !strcmp( name, slap_propnames[0] )) {
208                                         i += SLAP_SASL_PROP_COUNT - 1;
209                                         continue;
210                               }
211                               name++;
212                     } else if ( !(sl->flags & SASL_AUXPROP_AUTHZID ) )
213                               continue;
214 
215                     if ( sl->list[i].values ) {
216                               if ( !(sl->flags & SASL_AUXPROP_OVERRIDE) ) continue;
217                     }
218                     ad = NULL;
219                     rc = slap_str2ad( name, &ad, &text );
220                     if ( rc != LDAP_SUCCESS ) {
221                               Debug( LDAP_DEBUG_TRACE,
222                                         "slap_ap_lookup: str2ad(%s): %s\n", name, text );
223                               continue;
224                     }
225 
226                     /* If it's the rootdn and a rootpw was present, we already set
227                      * it so don't override it here.
228                      */
229                     if ( ad == slap_schema.si_ad_userPassword && sl->list[i].values &&
230                               be_isroot_dn( op->o_bd, &op->o_req_ndn ))
231                               continue;
232 
233                     a = attr_find( rs->sr_entry->e_attrs, ad );
234                     if ( !a ) continue;
235                     if ( ! access_allowed( op, rs->sr_entry, ad, NULL, ACL_AUTH, NULL ) ) {
236                               continue;
237                     }
238                     if ( sl->list[i].values && ( sl->flags & SASL_AUXPROP_OVERRIDE ) ) {
239                               sl->sparams->utils->prop_erase( sl->sparams->propctx,
240                               sl->list[i].name );
241                     }
242                     for ( bv = a->a_vals; bv->bv_val; bv++ ) {
243                               /* ITS#3846 don't give hashed passwords to SASL */
244                               if ( ad == slap_schema.si_ad_userPassword &&
245                                         bv->bv_val[0] == '{' /*}*/ )
246                               {
247                                         if ( lutil_passwd_scheme( bv->bv_val ) ) {
248                                                   /* If it's not a recognized scheme, just assume it's
249                                                    * a cleartext password that happened to include brackets.
250                                                    *
251                                                    * If it's a recognized scheme, skip this value, unless the
252                                                    * scheme is {CLEARTEXT}. In that case, skip over the
253                                                    * scheme name and use the remainder. If there is nothing
254                                                    * past the scheme name, skip this value.
255                                                    */
256 #ifdef SLAPD_CLEARTEXT
257                                                   if ( !strncasecmp( bv->bv_val, sc_cleartext.bv_val,
258                                                             sc_cleartext.bv_len )) {
259                                                             struct berval cbv;
260                                                             cbv.bv_len = bv->bv_len - sc_cleartext.bv_len;
261                                                             if ( cbv.bv_len > 0 ) {
262                                                                       cbv.bv_val = bv->bv_val + sc_cleartext.bv_len;
263                                                                       sl->sparams->utils->prop_set( sl->sparams->propctx,
264                                                                                 sl->list[i].name, cbv.bv_val, cbv.bv_len );
265                                                             }
266                                                   }
267 #endif
268                                                   continue;
269                                         }
270                               }
271                               sl->sparams->utils->prop_set( sl->sparams->propctx,
272                                         sl->list[i].name, bv->bv_val, bv->bv_len );
273                     }
274           }
275           return LDAP_SUCCESS;
276 }
277 
278 #if SASL_VERSION_FULL >= 0x020118
279 static int
280 #else
281 static void
282 #endif
slap_auxprop_lookup(void * glob_context,sasl_server_params_t * sparams,unsigned flags,const char * user,unsigned ulen)283 slap_auxprop_lookup(
284           void *glob_context,
285           sasl_server_params_t *sparams,
286           unsigned flags,
287           const char *user,
288           unsigned ulen)
289 {
290           OperationBuffer opbuf = {{ NULL }};
291           Operation *op = (Operation *)&opbuf;
292           int i, doit = 0;
293           Connection *conn = NULL;
294           lookup_info sl;
295           int rc = LDAP_SUCCESS;
296 #ifdef SLAP_AUXPROP_DONTUSECOPY
297           int dontUseCopy = 0;
298           BackendDB *dontUseCopy_bd = NULL;
299 #endif /* SLAP_AUXPROP_DONTUSECOPY */
300 
301           sl.list = sparams->utils->prop_get( sparams->propctx );
302           sl.sparams = sparams;
303           sl.flags = flags;
304 
305           /* Find our DN and conn first */
306           for( i = 0; sl.list[i].name; i++ ) {
307                     if ( sl.list[i].name[0] == '*' ) {
308                               if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) {
309                                         if ( sl.list[i].values && sl.list[i].values[0] )
310                                                   AC_MEMCPY( &conn, sl.list[i].values[0], sizeof( conn ) );
311                                         continue;
312                               }
313                               if ( flags & SASL_AUXPROP_AUTHZID ) {
314                                         if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZLEN] )) {
315                                                   if ( sl.list[i].values && sl.list[i].values[0] )
316                                                             AC_MEMCPY( &op->o_req_ndn.bv_len, sl.list[i].values[0],
317                                                                       sizeof( op->o_req_ndn.bv_len ) );
318                                         } else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHZ] )) {
319                                                   if ( sl.list[i].values )
320                                                             op->o_req_ndn.bv_val = (char *)sl.list[i].values[0];
321                                                   break;
322                                         }
323                               }
324 
325                               if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) {
326                                         if ( sl.list[i].values && sl.list[i].values[0] )
327                                                   AC_MEMCPY( &op->o_req_ndn.bv_len, sl.list[i].values[0],
328                                                             sizeof( op->o_req_ndn.bv_len ) );
329                               } else if ( !strcmp( sl.list[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) {
330                                         if ( sl.list[i].values ) {
331                                                   op->o_req_ndn.bv_val = (char *)sl.list[i].values[0];
332                                                   if ( !(flags & SASL_AUXPROP_AUTHZID) )
333                                                             break;
334                                         }
335                               }
336 #ifdef SLAP_AUXPROP_DONTUSECOPY
337                               if ( slap_dontUseCopy_propnames != NULL ) {
338                                         int j;
339                                         struct berval bv;
340                                         ber_str2bv( &sl.list[i].name[1], 0, 1, &bv );
341                                         for ( j = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ j ]); j++ ) {
342                                                   if ( bvmatch( &bv, &slap_dontUseCopy_propnames[ j ] ) ) {
343                                                             dontUseCopy = 1;
344                                                             break;
345                                                   }
346                                         }
347                               }
348 #endif /* SLAP_AUXPROP_DONTUSECOPY */
349                     }
350           }
351 
352           /* we don't know anything about this, ignore it */
353           if ( !conn ) {
354                     rc = LDAP_SUCCESS;
355                     goto done;
356           }
357 
358           /* Now see what else needs to be fetched */
359           for( i = 0; sl.list[i].name; i++ ) {
360                     const char *name = sl.list[i].name;
361 
362                     if ( name[0] == '*' ) {
363                               if ( flags & SASL_AUXPROP_AUTHZID ) continue;
364                               /* Skip our private properties */
365                               if ( !strcmp( name, slap_propnames[0] )) {
366                                         i += SLAP_SASL_PROP_COUNT - 1;
367                                         continue;
368                               }
369                               name++;
370                     } else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
371                               continue;
372 
373                     if ( sl.list[i].values ) {
374                               if ( !(flags & SASL_AUXPROP_OVERRIDE) ) continue;
375                     }
376                     doit = 1;
377                     break;
378           }
379 
380           if (doit) {
381                     slap_callback cb = { NULL, sasl_ap_lookup, NULL, NULL };
382 
383                     cb.sc_private = &sl;
384 
385                     op->o_bd = select_backend( &op->o_req_ndn, 1 );
386 
387                     if ( op->o_bd ) {
388                               /* For rootdn, see if we can use the rootpw */
389                               if ( be_isroot_dn( op->o_bd, &op->o_req_ndn ) &&
390                                         !BER_BVISEMPTY( &op->o_bd->be_rootpw )) {
391                                         struct berval cbv = BER_BVNULL;
392 
393                                         /* If there's a recognized scheme, see if it's CLEARTEXT */
394                                         if ( lutil_passwd_scheme( op->o_bd->be_rootpw.bv_val )) {
395                                                   if ( !strncasecmp( op->o_bd->be_rootpw.bv_val,
396                                                             sc_cleartext.bv_val, sc_cleartext.bv_len )) {
397 
398                                                             /* If it's CLEARTEXT, skip past scheme spec */
399                                                             cbv.bv_len = op->o_bd->be_rootpw.bv_len -
400                                                                       sc_cleartext.bv_len;
401                                                             if ( cbv.bv_len ) {
402                                                                       cbv.bv_val = op->o_bd->be_rootpw.bv_val +
403                                                                                 sc_cleartext.bv_len;
404                                                             }
405                                                   }
406                                         /* No scheme, use the whole value */
407                                         } else {
408                                                   cbv = op->o_bd->be_rootpw;
409                                         }
410                                         if ( !BER_BVISEMPTY( &cbv )) {
411                                                   for( i = 0; sl.list[i].name; i++ ) {
412                                                             const char *name = sl.list[i].name;
413 
414                                                             if ( name[0] == '*' ) {
415                                                                       if ( flags & SASL_AUXPROP_AUTHZID ) continue;
416                                                                                 name++;
417                                                             } else if ( !(flags & SASL_AUXPROP_AUTHZID ) )
418                                                                       continue;
419 
420                                                             if ( !strcasecmp(name,"userPassword") ) {
421                                                                       sl.sparams->utils->prop_set( sl.sparams->propctx,
422                                                                                 sl.list[i].name, cbv.bv_val, cbv.bv_len );
423                                                                       break;
424                                                             }
425                                                   }
426                                         }
427                               }
428 
429 #ifdef SLAP_AUXPROP_DONTUSECOPY
430                               if ( SLAP_SHADOW( op->o_bd ) && dontUseCopy ) {
431                                         dontUseCopy_bd = op->o_bd;
432                                         op->o_bd = frontendDB;
433                               }
434 
435 retry_dontUseCopy:;
436 #endif /* SLAP_AUXPROP_DONTUSECOPY */
437 
438                               if ( op->o_bd->be_search ) {
439                                         SlapReply rs = {REP_RESULT};
440 #ifdef SLAP_AUXPROP_DONTUSECOPY
441                                         LDAPControl **save_ctrls = NULL, c;
442                                         int save_dontUseCopy;
443 #endif /* SLAP_AUXPROP_DONTUSECOPY */
444 
445                                         op->o_hdr = conn->c_sasl_bindop->o_hdr;
446                                         op->o_controls = opbuf.ob_controls;
447                                         op->o_tag = LDAP_REQ_SEARCH;
448                                         op->o_dn = conn->c_ndn;
449                                         op->o_ndn = conn->c_ndn;
450                                         op->o_callback = &cb;
451                                         slap_op_time( &op->o_time, &op->o_tincr );
452                                         op->o_do_not_cache = 1;
453                                         op->o_is_auth_check = 1;
454                                         op->o_req_dn = op->o_req_ndn;
455                                         op->ors_scope = LDAP_SCOPE_BASE;
456                                         op->ors_deref = LDAP_DEREF_NEVER;
457                                         op->ors_tlimit = SLAP_NO_LIMIT;
458                                         op->ors_slimit = 1;
459                                         op->ors_filter = &generic_filter;
460                                         op->ors_filterstr = generic_filterstr;
461                                         op->o_authz = conn->c_authz;
462                                         /* FIXME: we want all attributes, right? */
463                                         op->ors_attrs = NULL;
464 
465 #ifdef SLAP_AUXPROP_DONTUSECOPY
466                                         if ( dontUseCopy ) {
467                                                   save_dontUseCopy = op->o_dontUseCopy;
468                                                   if ( !op->o_dontUseCopy ) {
469                                                             int cnt = 0;
470                                                             save_ctrls = op->o_ctrls;
471                                                             if ( op->o_ctrls ) {
472                                                                       for ( ; op->o_ctrls[ cnt ]; cnt++ )
473                                                                                 ;
474                                                             }
475                                                             op->o_ctrls = op->o_tmpcalloc( sizeof(LDAPControl *), cnt + 2, op->o_tmpmemctx );
476                                                             if ( cnt ) {
477                                                                       for ( cnt = 0; save_ctrls[ cnt ]; cnt++ ) {
478                                                                                 op->o_ctrls[ cnt ] = save_ctrls[ cnt ];
479                                                                       }
480                                                             }
481                                                             c.ldctl_oid = LDAP_CONTROL_DONTUSECOPY;
482                                                             c.ldctl_iscritical = 1;
483                                                             BER_BVZERO( &c.ldctl_value );
484                                                             op->o_ctrls[ cnt ] = &c;
485                                                   }
486                                                   op->o_dontUseCopy = SLAP_CONTROL_CRITICAL;
487                                         }
488 #endif /* SLAP_AUXPROP_DONTUSECOPY */
489 
490                                         rc = op->o_bd->be_search( op, &rs );
491 
492 #ifdef SLAP_AUXPROP_DONTUSECOPY
493                                         if ( dontUseCopy ) {
494                                                   if ( save_ctrls != op->o_ctrls ) {
495                                                             op->o_tmpfree( op->o_ctrls, op->o_tmpmemctx );
496                                                             op->o_ctrls = save_ctrls;
497                                                             op->o_dontUseCopy = save_dontUseCopy;
498                                                   }
499 
500                                                   if ( rs.sr_err == LDAP_UNAVAILABLE && slap_dontUseCopy_ignore )
501                                                   {
502                                                             op->o_bd = dontUseCopy_bd;
503                                                             dontUseCopy = 0;
504                                                             goto retry_dontUseCopy;
505                                                   }
506                                         }
507 #endif /* SLAP_AUXPROP_DONTUSECOPY */
508                               }
509                     }
510           }
511 done:;
512 #if SASL_VERSION_FULL >= 0x020118
513           return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK;
514 #endif
515 }
516 
517 #if SASL_VERSION_FULL >= 0x020110
518 static int
slap_auxprop_store(void * glob_context,sasl_server_params_t * sparams,struct propctx * prctx,const char * user,unsigned ulen)519 slap_auxprop_store(
520           void *glob_context,
521           sasl_server_params_t *sparams,
522           struct propctx *prctx,
523           const char *user,
524           unsigned ulen)
525 {
526           Operation op = {0};
527           Opheader oph;
528           int rc, i;
529           unsigned j;
530           Connection *conn = NULL;
531           const struct propval *pr;
532           Modifications *modlist = NULL, **modtail = &modlist, *mod;
533           slap_callback cb = { NULL, slap_null_cb, NULL, NULL };
534           char textbuf[SLAP_TEXT_BUFLEN];
535           const char *text;
536           size_t textlen = sizeof(textbuf);
537 #ifdef SLAP_AUXPROP_DONTUSECOPY
538           int dontUseCopy = 0;
539           BackendDB *dontUseCopy_bd = NULL;
540 #endif /* SLAP_AUXPROP_DONTUSECOPY */
541 
542           /* just checking if we are enabled */
543           if (!prctx) return SASL_OK;
544 
545           if (!sparams || !user) return SASL_BADPARAM;
546 
547           pr = sparams->utils->prop_get( sparams->propctx );
548 
549           /* Find our DN and conn first */
550           for( i = 0; pr[i].name; i++ ) {
551                     if ( pr[i].name[0] == '*' ) {
552                               if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_CONN] ) ) {
553                                         if ( pr[i].values && pr[i].values[0] )
554                                                   AC_MEMCPY( &conn, pr[i].values[0], sizeof( conn ) );
555                                         continue;
556                               }
557                               if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHCLEN] )) {
558                                         if ( pr[i].values && pr[i].values[0] )
559                                                   AC_MEMCPY( &op.o_req_ndn.bv_len, pr[i].values[0],
560                                                             sizeof( op.o_req_ndn.bv_len ) );
561                               } else if ( !strcmp( pr[i].name, slap_propnames[SLAP_SASL_PROP_AUTHC] ) ) {
562                                         if ( pr[i].values )
563                                                   op.o_req_ndn.bv_val = (char *)pr[i].values[0];
564                               }
565 #ifdef SLAP_AUXPROP_DONTUSECOPY
566                               if ( slap_dontUseCopy_propnames != NULL ) {
567                                         struct berval bv;
568                                         ber_str2bv( &pr[i].name[1], 0, 1, &bv );
569                                         for ( j = 0; !BER_BVISNULL( &slap_dontUseCopy_propnames[ j ] ); j++ ) {
570                                                   if ( bvmatch( &bv, &slap_dontUseCopy_propnames[ j ] ) ) {
571                                                             dontUseCopy = 1;
572                                                             break;
573                                                   }
574                                         }
575                               }
576 #endif /* SLAP_AUXPROP_DONTUSECOPY */
577                     }
578           }
579           if (!conn || !op.o_req_ndn.bv_val) return SASL_BADPARAM;
580 
581           op.o_bd = select_backend( &op.o_req_ndn, 1 );
582 
583           if ( !op.o_bd || !op.o_bd->be_modify ) return SASL_FAIL;
584 
585 #ifdef SLAP_AUXPROP_DONTUSECOPY
586           if ( SLAP_SHADOW( op.o_bd ) && dontUseCopy ) {
587                     dontUseCopy_bd = op.o_bd;
588                     op.o_bd = frontendDB;
589                     op.o_dontUseCopy = SLAP_CONTROL_CRITICAL;
590           }
591 #endif /* SLAP_AUXPROP_DONTUSECOPY */
592 
593           pr = sparams->utils->prop_get( prctx );
594           if (!pr) return SASL_BADPARAM;
595 
596           for (i=0; pr[i].name; i++);
597           if (!i) return SASL_BADPARAM;
598 
599           for (i=0; pr[i].name; i++) {
600                     mod = (Modifications *)ch_malloc( sizeof(Modifications) );
601                     mod->sml_op = LDAP_MOD_REPLACE;
602                     mod->sml_flags = 0;
603                     ber_str2bv( pr[i].name, 0, 0, &mod->sml_type );
604                     mod->sml_numvals = pr[i].nvalues;
605                     mod->sml_values = (struct berval *)ch_malloc( (pr[i].nvalues + 1) *
606                               sizeof(struct berval));
607                     for (j=0; j<pr[i].nvalues; j++) {
608                               ber_str2bv( pr[i].values[j], 0, 1, &mod->sml_values[j]);
609                     }
610                     BER_BVZERO( &mod->sml_values[j] );
611                     mod->sml_nvalues = NULL;
612                     mod->sml_desc = NULL;
613                     *modtail = mod;
614                     modtail = &mod->sml_next;
615           }
616           *modtail = NULL;
617 
618           rc = slap_mods_check( &op, modlist, &text, textbuf, textlen, NULL );
619 
620           if ( rc == LDAP_SUCCESS ) {
621                     rc = slap_mods_no_user_mod_check( &op, modlist,
622                               &text, textbuf, textlen );
623 
624                     if ( rc == LDAP_SUCCESS ) {
625                               if ( conn->c_sasl_bindop ) {
626                                         op.o_hdr = conn->c_sasl_bindop->o_hdr;
627                               } else {
628                                         op.o_hdr = &oph;
629                                         memset( &oph, 0, sizeof(oph) );
630                                         operation_fake_init( conn, &op, ldap_pvt_thread_pool_context(), 0 );
631                               }
632                               op.o_tag = LDAP_REQ_MODIFY;
633                               op.o_ndn = op.o_req_ndn;
634                               op.o_callback = &cb;
635                               slap_op_time( &op.o_time, &op.o_tincr );
636                               op.o_do_not_cache = 1;
637                               op.o_is_auth_check = 1;
638                               op.o_req_dn = op.o_req_ndn;
639                               op.orm_modlist = modlist;
640 
641                               for (;;) {
642                                         SlapReply rs = {REP_RESULT};
643                                         rc = op.o_bd->be_modify( &op, &rs );
644 
645 #ifdef SLAP_AUXPROP_DONTUSECOPY
646                                         if ( dontUseCopy &&
647                                                   rs.sr_err == LDAP_UNAVAILABLE &&
648                                                   slap_dontUseCopy_ignore )
649                                         {
650                                                   op.o_bd = dontUseCopy_bd;
651                                                   op.o_dontUseCopy = SLAP_CONTROL_NONE;
652                                                   dontUseCopy = 0;
653                                                   continue;
654                                         }
655 #endif /* SLAP_AUXPROP_DONTUSECOPY */
656                                         break;
657                               }
658                     }
659           }
660           slap_mods_free( modlist, 1 );
661           return rc != LDAP_SUCCESS ? SASL_FAIL : SASL_OK;
662 }
663 #endif /* SASL_VERSION_FULL >= 2.1.16 */
664 
665 static sasl_auxprop_plug_t slap_auxprop_plugin = {
666           0,        /* Features */
667           0,        /* spare */
668           NULL,     /* glob_context */
669           NULL,     /* auxprop_free */
670           slap_auxprop_lookup,
671           "slapd",  /* name */
672 #if SASL_VERSION_FULL >= 0x020110
673           slap_auxprop_store  /* the declaration of this member changed
674                                          * in cyrus SASL from 2.1.15 to 2.1.16 */
675 #else
676           NULL
677 #endif
678 };
679 
680 static int
slap_auxprop_init(const sasl_utils_t * utils,int max_version,int * out_version,sasl_auxprop_plug_t ** plug,const char * plugname)681 slap_auxprop_init(
682           const sasl_utils_t *utils,
683           int max_version,
684           int *out_version,
685           sasl_auxprop_plug_t **plug,
686           const char *plugname)
687 {
688           if ( !out_version || !plug ) return SASL_BADPARAM;
689 
690           if ( max_version < SASL_AUXPROP_PLUG_VERSION ) return SASL_BADVERS;
691 
692           *out_version = SASL_AUXPROP_PLUG_VERSION;
693           *plug = &slap_auxprop_plugin;
694           return SASL_OK;
695 }
696 
697 /* Convert a SASL authcid or authzid into a DN. Store the DN in an
698  * auxiliary property, so that we can refer to it in sasl_authorize
699  * without interfering with anything else. Also, the SASL username
700  * buffer is constrained to 256 characters, and our DNs could be
701  * much longer (SLAP_LDAPDN_MAXLEN, currently set to 8192)
702  */
703 static int
slap_sasl_canonicalize(sasl_conn_t * sconn,void * context,const char * in,unsigned inlen,unsigned flags,const char * user_realm,char * out,unsigned out_max,unsigned * out_len)704 slap_sasl_canonicalize(
705           sasl_conn_t *sconn,
706           void *context,
707           const char *in,
708           unsigned inlen,
709           unsigned flags,
710           const char *user_realm,
711           char *out,
712           unsigned out_max,
713           unsigned *out_len)
714 {
715           Connection *conn = (Connection *)context;
716           struct propctx *props = sasl_auxprop_getctx( sconn );
717           struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } };
718           struct berval dn;
719           int rc, which;
720           const char *names[2];
721           struct berval       bvin;
722 
723           *out_len = 0;
724 
725           Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n",
726                     conn ? (long) conn->c_connid : -1L,
727                     (flags & SASL_CU_AUTHID) ? "authcid" : "authzid",
728                     in ? in : "<empty>");
729 
730           /* If name is too big, just truncate. We don't care, we're
731            * using DNs, not the usernames.
732            */
733           if ( inlen > out_max )
734                     inlen = out_max-1;
735 
736           /* This is a Simple Bind using SPASSWD. That means the in-directory
737            * userPassword of the Binding user already points at SASL, so it
738            * cannot be used to actually satisfy a password comparison. Just
739            * ignore it, some other mech will process it.
740            */
741           if ( !conn->c_sasl_bindop ||
742                     conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) goto done;
743 
744           /* See if we need to add request, can only do it once */
745           prop_getnames( props, slap_propnames, auxvals );
746           if ( !auxvals[0].name )
747                     prop_request( props, slap_propnames );
748 
749           if ( flags & SASL_CU_AUTHID )
750                     which = SLAP_SASL_PROP_AUTHCLEN;
751           else
752                     which = SLAP_SASL_PROP_AUTHZLEN;
753 
754           /* Need to store the Connection for auxprop_lookup */
755           if ( !auxvals[SLAP_SASL_PROP_CONN].values ) {
756                     names[0] = slap_propnames[SLAP_SASL_PROP_CONN];
757                     names[1] = NULL;
758                     prop_set( props, names[0], (char *)&conn, sizeof( conn ) );
759           }
760 
761           /* Already been here? */
762           if ( auxvals[which].values )
763                     goto done;
764 
765           /* Normally we require an authzID to have a u: or dn: prefix.
766            * However, SASL frequently gives us an authzID that is just
767            * an exact copy of the authcID, without a prefix. We need to
768            * detect and allow this condition. If SASL calls canonicalize
769            * with SASL_CU_AUTHID|SASL_CU_AUTHZID this is a no-brainer.
770            * But if it's broken into two calls, we need to remember the
771            * authcID so that we can compare the authzID later. We store
772            * the authcID temporarily in conn->c_sasl_dn. We necessarily
773            * finish Canonicalizing before Authorizing, so there is no
774            * conflict with slap_sasl_authorize's use of this temp var.
775            *
776            * The SASL EXTERNAL mech is backwards from all the other mechs,
777            * it does authzID before the authcID. If we see that authzID
778            * has already been done, don't do anything special with authcID.
779            */
780           if ( flags == SASL_CU_AUTHID && !auxvals[SLAP_SASL_PROP_AUTHZ].values ) {
781                     conn->c_sasl_dn.bv_val = (char *) in;
782                     conn->c_sasl_dn.bv_len = 0;
783           } else if ( flags == SASL_CU_AUTHZID && conn->c_sasl_dn.bv_val ) {
784                     rc = strcmp( in, conn->c_sasl_dn.bv_val );
785                     conn->c_sasl_dn.bv_val = NULL;
786                     /* They were equal, no work needed */
787                     if ( !rc ) goto done;
788           }
789 
790           bvin.bv_val = (char *)in;
791           bvin.bv_len = inlen;
792           rc = slap_sasl_getdn( conn, NULL, &bvin, (char *)user_realm, &dn,
793                     (flags & SASL_CU_AUTHID) ? SLAP_GETDN_AUTHCID : SLAP_GETDN_AUTHZID );
794           if ( rc != LDAP_SUCCESS ) {
795                     sasl_seterror( sconn, 0, ldap_err2string( rc ) );
796                     return SASL_NOAUTHZ;
797           }
798 
799           names[0] = slap_propnames[which];
800           names[1] = NULL;
801           prop_set( props, names[0], (char *)&dn.bv_len, sizeof( dn.bv_len ) );
802 
803           which++;
804           names[0] = slap_propnames[which];
805           prop_set( props, names[0], dn.bv_val, dn.bv_len );
806 
807           Debug( LDAP_DEBUG_ARGS, "SASL Canonicalize [conn=%ld]: %s=\"%s\"\n",
808                     conn ? (long) conn->c_connid : -1L, names[0]+1,
809                     dn.bv_val ? dn.bv_val : "<EMPTY>" );
810 
811           /* Not needed any more, SASL has copied it */
812           if ( conn && conn->c_sasl_bindop )
813                     conn->c_sasl_bindop->o_tmpfree( dn.bv_val, conn->c_sasl_bindop->o_tmpmemctx );
814 
815 done:
816           AC_MEMCPY( out, in, inlen );
817           out[inlen] = '\0';
818 
819           *out_len = inlen;
820 
821           return SASL_OK;
822 }
823 
824 static int
slap_sasl_authorize(sasl_conn_t * sconn,void * context,char * requested_user,unsigned rlen,char * auth_identity,unsigned alen,const char * def_realm,unsigned urlen,struct propctx * props)825 slap_sasl_authorize(
826           sasl_conn_t *sconn,
827           void *context,
828           char *requested_user,
829           unsigned rlen,
830           char *auth_identity,
831           unsigned alen,
832           const char *def_realm,
833           unsigned urlen,
834           struct propctx *props)
835 {
836           Connection *conn = (Connection *)context;
837           /* actually:
838            *        (SLAP_SASL_PROP_COUNT - 1)    because we skip "conn",
839            *        + 1                                     for NULL termination?
840            */
841           struct propval auxvals[ SLAP_SASL_PROP_COUNT ] = { { 0 } };
842           struct berval authcDN, authzDN = BER_BVNULL;
843           int rc;
844 
845           /* Simple Binds don't support proxy authorization, ignore it */
846           if ( !conn->c_sasl_bindop ||
847                     conn->c_sasl_bindop->orb_method != LDAP_AUTH_SASL ) return SASL_OK;
848 
849           Debug( LDAP_DEBUG_ARGS, "SASL proxy authorize [conn=%ld]: "
850                     "authcid=\"%s\" authzid=\"%s\"\n",
851                     conn ? (long) conn->c_connid : -1L, auth_identity, requested_user );
852           if ( conn->c_sasl_dn.bv_val ) {
853                     BER_BVZERO( &conn->c_sasl_dn );
854           }
855 
856           /* Skip SLAP_SASL_PROP_CONN */
857           prop_getnames( props, slap_propnames+1, auxvals );
858 
859           /* Should not happen */
860           if ( !auxvals[0].values ) {
861                     sasl_seterror( sconn, 0, "invalid authcid" );
862                     return SASL_NOAUTHZ;
863           }
864 
865           AC_MEMCPY( &authcDN.bv_len, auxvals[0].values[0], sizeof(authcDN.bv_len) );
866           authcDN.bv_val = auxvals[1].values ? (char *)auxvals[1].values[0] : NULL;
867           conn->c_sasl_dn = authcDN;
868 
869           /* Nothing to do if no authzID was given */
870           if ( !auxvals[2].name || !auxvals[2].values ) {
871                     goto ok;
872           }
873 
874           AC_MEMCPY( &authzDN.bv_len, auxvals[2].values[0], sizeof(authzDN.bv_len) );
875           authzDN.bv_val = auxvals[3].values ? (char *)auxvals[3].values[0] : NULL;
876 
877           rc = slap_sasl_authorized( conn->c_sasl_bindop, &authcDN, &authzDN );
878           if ( rc != LDAP_SUCCESS ) {
879                     Debug( LDAP_DEBUG_TRACE, "SASL Proxy Authorize [conn=%ld]: "
880                               "proxy authorization disallowed (%d)\n",
881                               conn ? (long) conn->c_connid : -1L, rc );
882 
883                     sasl_seterror( sconn, 0, "not authorized" );
884                     return SASL_NOAUTHZ;
885           }
886 
887           /* FIXME: we need yet another dup because slap_sasl_getdn()
888            * is using the bind operation slab */
889           ber_dupbv( &conn->c_sasl_authz_dn, &authzDN );
890 
891 ok:
892           if (conn->c_sasl_bindop) {
893                     Debug( LDAP_DEBUG_STATS,
894                               "%s BIND authcid=\"%s\" authzid=\"%s\"\n",
895                               conn->c_sasl_bindop->o_log_prefix,
896                               auth_identity, requested_user );
897           }
898 
899           Debug( LDAP_DEBUG_TRACE, "SASL Authorize [conn=%ld]: "
900                     " proxy authorization allowed authzDN=\"%s\"\n",
901                     conn ? (long) conn->c_connid : -1L,
902                     authzDN.bv_val ? authzDN.bv_val : "" );
903           return SASL_OK;
904 }
905 
906 static int
slap_sasl_err2ldap(int saslerr)907 slap_sasl_err2ldap( int saslerr )
908 {
909           int rc;
910 
911           /* map SASL errors to LDAP resultCode returned by:
912            *        sasl_server_new()
913            *                  SASL_OK, SASL_NOMEM
914            *        sasl_server_step()
915            *                  SASL_OK, SASL_CONTINUE, SASL_TRANS, SASL_BADPARAM, SASL_BADPROT,
916            *      ...
917            *        sasl_server_start()
918            *      + SASL_NOMECH
919            *        sasl_setprop()
920            *                  SASL_OK, SASL_BADPARAM
921            */
922 
923           switch (saslerr) {
924                     case SASL_OK:
925                               rc = LDAP_SUCCESS;
926                               break;
927                     case SASL_CONTINUE:
928                               rc = LDAP_SASL_BIND_IN_PROGRESS;
929                               break;
930                     case SASL_FAIL:
931                     case SASL_NOMEM:
932                               rc = LDAP_OTHER;
933                               break;
934                     case SASL_NOMECH:
935                               rc = LDAP_AUTH_METHOD_NOT_SUPPORTED;
936                               break;
937                     case SASL_BADAUTH:
938                     case SASL_NOUSER:
939                     case SASL_TRANS:
940                     case SASL_EXPIRED:
941                               rc = LDAP_INVALID_CREDENTIALS;
942                               break;
943                     case SASL_NOAUTHZ:
944                               rc = LDAP_INSUFFICIENT_ACCESS;
945                               break;
946                     case SASL_TOOWEAK:
947                     case SASL_ENCRYPT:
948                               rc = LDAP_INAPPROPRIATE_AUTH;
949                               break;
950                     case SASL_UNAVAIL:
951                     case SASL_TRYAGAIN:
952                               rc = LDAP_UNAVAILABLE;
953                               break;
954                     case SASL_DISABLED:
955                               rc = LDAP_UNWILLING_TO_PERFORM;
956                               break;
957                     default:
958                               rc = LDAP_OTHER;
959                               break;
960           }
961 
962           return rc;
963 }
964 
965 #ifdef SLAPD_SPASSWD
966 
967 static struct berval sasl_pwscheme = BER_BVC("{SASL}");
968 
chk_sasl(const struct berval * sc,const struct berval * passwd,const struct berval * cred,const char ** text)969 static int chk_sasl(
970           const struct berval *sc,
971           const struct berval * passwd,
972           const struct berval * cred,
973           const char **text )
974 {
975           unsigned int i;
976           int rtn;
977           void *ctx, *sconn = NULL;
978 
979           for( i=0; i<cred->bv_len; i++) {
980                     if(cred->bv_val[i] == '\0') {
981                               return LUTIL_PASSWD_ERR;      /* NUL character in password */
982                     }
983           }
984 
985           if( cred->bv_val[i] != '\0' ) {
986                     return LUTIL_PASSWD_ERR;      /* cred must behave like a string */
987           }
988 
989           for( i=0; i<passwd->bv_len; i++) {
990                     if(passwd->bv_val[i] == '\0') {
991                               return LUTIL_PASSWD_ERR;      /* NUL character in password */
992                     }
993           }
994 
995           if( passwd->bv_val[i] != '\0' ) {
996                     return LUTIL_PASSWD_ERR;      /* passwd must behave like a string */
997           }
998 
999           rtn = LUTIL_PASSWD_ERR;
1000 
1001           ctx = ldap_pvt_thread_pool_context();
1002           ldap_pvt_thread_pool_getkey( ctx, (void *)slap_sasl_bind, &sconn, NULL );
1003 
1004           if( sconn != NULL ) {
1005                     int sc;
1006                     sc = sasl_checkpass( sconn,
1007                               passwd->bv_val, passwd->bv_len,
1008                               cred->bv_val, cred->bv_len );
1009                     rtn = ( sc != SASL_OK ) ? LUTIL_PASSWD_ERR : LUTIL_PASSWD_OK;
1010           }
1011 
1012           return rtn;
1013 }
1014 #endif /* SLAPD_SPASSWD */
1015 
1016 #endif /* HAVE_CYRUS_SASL */
1017 
1018 typedef struct slapd_map_data {
1019           struct berval base;
1020           struct berval filter;
1021           AttributeName attrs[2];
1022           int scope;
1023 } slapd_map_data;
1024 
1025 static void *
slapd_rw_config(const char * fname,int lineno,int argc,char ** argv)1026 slapd_rw_config( const char *fname, int lineno, int argc, char **argv )
1027 {
1028           slapd_map_data *ret = NULL;
1029           LDAPURLDesc *lud = NULL;
1030           char *uri;
1031           AttributeDescription *ad = NULL;
1032           int rc, flen = 0;
1033           struct berval dn, ndn;
1034 
1035           if ( argc != 1 ) {
1036                     Debug( LDAP_DEBUG_ANY,
1037                               "[%s:%d] slapd map needs URI\n",
1038                               fname, lineno );
1039         return NULL;
1040           }
1041 
1042           uri = argv[0];
1043           if ( strncasecmp( uri, "uri=", STRLENOF( "uri=" ) ) == 0 ) {
1044                     uri += STRLENOF( "uri=" );
1045           }
1046 
1047           if ( ldap_url_parse( uri, &lud ) != LDAP_URL_SUCCESS ) {
1048                     Debug( LDAP_DEBUG_ANY,
1049                               "[%s:%d] illegal URI '%s'\n",
1050                               fname, lineno, uri );
1051         return NULL;
1052           }
1053 
1054           if ( strcasecmp( lud->lud_scheme, "ldap" )) {
1055                     Debug( LDAP_DEBUG_ANY,
1056                               "[%s:%d] illegal URI scheme '%s'\n",
1057                               fname, lineno, lud->lud_scheme );
1058                     goto done;
1059           }
1060 
1061           if (( lud->lud_host && lud->lud_host[0] ) || lud->lud_exts
1062                     || !lud->lud_dn ) {
1063                     Debug( LDAP_DEBUG_ANY,
1064                               "[%s:%d] illegal URI '%s'\n",
1065                               fname, lineno, uri );
1066                     goto done;
1067           }
1068 
1069           if ( lud->lud_attrs ) {
1070                     if ( lud->lud_attrs[1] ) {
1071                               Debug( LDAP_DEBUG_ANY,
1072                                         "[%s:%d] only one attribute allowed in URI\n",
1073                                         fname, lineno );
1074                               goto done;
1075                     }
1076                     if ( strcasecmp( lud->lud_attrs[0], "dn" ) &&
1077                               strcasecmp( lud->lud_attrs[0], "entryDN" )) {
1078                               const char *text;
1079                               rc = slap_str2ad( lud->lud_attrs[0], &ad, &text );
1080                               if ( rc )
1081                                         goto done;
1082                     }
1083           }
1084           ber_str2bv( lud->lud_dn, 0, 0, &dn );
1085           if ( dnNormalize( 0, NULL, NULL, &dn, &ndn, NULL ))
1086                     goto done;
1087 
1088           if ( lud->lud_filter ) {
1089                     flen = strlen( lud->lud_filter ) + 1;
1090           }
1091           ret = ch_malloc( sizeof( slapd_map_data ) + flen );
1092           ret->base = ndn;
1093           if ( flen ) {
1094                     ret->filter.bv_val = (char *)(ret+1);
1095                     ret->filter.bv_len = flen - 1;
1096                     strcpy( ret->filter.bv_val, lud->lud_filter );
1097           } else {
1098                     BER_BVZERO( &ret->filter );
1099           }
1100           ret->scope = lud->lud_scope;
1101           if ( ad ) {
1102                     ret->attrs[0].an_name = ad->ad_cname;
1103           } else {
1104                     BER_BVZERO( &ret->attrs[0].an_name );
1105           }
1106           ret->attrs[0].an_desc = ad;
1107           BER_BVZERO( &ret->attrs[1].an_name );
1108 done:
1109           ldap_free_urldesc( lud );
1110           return ret;
1111 }
1112 
1113 struct slapd_rw_info {
1114           slapd_map_data *si_data;
1115           struct berval si_val;
1116 };
1117 
1118 static int
slapd_rw_cb(Operation * op,SlapReply * rs)1119 slapd_rw_cb( Operation *op, SlapReply *rs )
1120 {
1121           if ( rs->sr_type == REP_SEARCH ) {
1122                     struct slapd_rw_info *si = op->o_callback->sc_private;
1123 
1124                     if ( si->si_data->attrs[0].an_desc ) {
1125                               Attribute *a;
1126 
1127                               a = attr_find( rs->sr_entry->e_attrs,
1128                                         si->si_data->attrs[0].an_desc );
1129                               if ( a ) {
1130                                         ber_dupbv( &si->si_val, a->a_vals );
1131                               }
1132                     } else {
1133                               ber_dupbv( &si->si_val, &rs->sr_entry->e_name );
1134                     }
1135           }
1136           return LDAP_SUCCESS;
1137 }
1138 
1139 static int
slapd_rw_apply(void * private,const char * filter,struct berval * val)1140 slapd_rw_apply( void *private, const char *filter, struct berval *val )
1141 {
1142           slapd_map_data *sl = private;
1143           slap_callback cb = { NULL };
1144           Connection conn = {0};
1145           OperationBuffer opbuf;
1146           Operation *op;
1147           void *thrctx;
1148           SlapReply rs = {REP_RESULT};
1149           struct slapd_rw_info si;
1150           char *ptr;
1151           int rc;
1152 
1153           thrctx = ldap_pvt_thread_pool_context();
1154           connection_fake_init2( &conn, &opbuf, thrctx, 0 );
1155           op = &opbuf.ob_op;
1156 
1157           op->o_tag = LDAP_REQ_SEARCH;
1158           op->o_req_dn = op->o_req_ndn = sl->base;
1159           op->o_bd = select_backend( &op->o_req_ndn, 1 );
1160           if ( !op->o_bd ) {
1161                     return REWRITE_ERR;
1162           }
1163           si.si_data = sl;
1164           BER_BVZERO( &si.si_val );
1165           op->ors_scope = sl->scope;
1166           op->ors_deref = LDAP_DEREF_NEVER;
1167           op->ors_slimit = 1;
1168           op->ors_tlimit = SLAP_NO_LIMIT;
1169           if ( sl->attrs[0].an_desc ) {
1170                     op->ors_attrs = sl->attrs;
1171           } else {
1172                     op->ors_attrs = slap_anlist_no_attrs;
1173           }
1174           if ( filter ) {
1175                     rc = strlen( filter );
1176           } else {
1177                     rc = 0;
1178           }
1179           rc += sl->filter.bv_len;
1180           ptr = op->ors_filterstr.bv_val = op->o_tmpalloc( rc + 1, op->o_tmpmemctx );
1181           if ( sl->filter.bv_len ) {
1182                     ptr = lutil_strcopy( ptr, sl->filter.bv_val );
1183           } else {
1184                     *ptr = '\0';
1185           }
1186           if ( filter ) {
1187                     strcpy( ptr, filter );
1188           }
1189           op->ors_filter = str2filter_x( op, op->ors_filterstr.bv_val );
1190           if ( !op->ors_filter ) {
1191                     op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1192                     return REWRITE_ERR;
1193           }
1194 
1195           op->ors_attrsonly = 0;
1196           op->o_dn = op->o_bd->be_rootdn;
1197           op->o_ndn = op->o_bd->be_rootndn;
1198           op->o_do_not_cache = 1;
1199 
1200           cb.sc_response = slapd_rw_cb;
1201           cb.sc_private = &si;
1202           op->o_callback = &cb;
1203 
1204           rc = op->o_bd->be_search( op, &rs );
1205           if ( rc == LDAP_SUCCESS && !BER_BVISNULL( &si.si_val )) {
1206                     *val = si.si_val;
1207                     rc = REWRITE_SUCCESS;
1208           } else {
1209                     if ( !BER_BVISNULL( &si.si_val )) {
1210                               ch_free( si.si_val.bv_val );
1211                     }
1212                     rc = REWRITE_ERR;
1213           }
1214           filter_free_x( op, op->ors_filter, 1 );
1215           op->o_tmpfree( op->ors_filterstr.bv_val, op->o_tmpmemctx );
1216           return rc;
1217 }
1218 
1219 static int
slapd_rw_destroy(void * private)1220 slapd_rw_destroy( void *private )
1221 {
1222           slapd_map_data *md = private;
1223 
1224           assert( private != NULL );
1225 
1226           ch_free( md->base.bv_val );
1227           ch_free( md );
1228 
1229           return 0;
1230 }
1231 
1232 static const rewrite_mapper slapd_mapper = {
1233           "slapd",
1234           slapd_rw_config,
1235           slapd_rw_apply,
1236           slapd_rw_destroy
1237 };
1238 
slap_sasl_init(void)1239 int slap_sasl_init( void )
1240 {
1241 #ifdef HAVE_CYRUS_SASL
1242           int rc;
1243           static sasl_callback_t server_callbacks[] = {
1244                     { SASL_CB_LOG, (slap_sasl_cb_ft)&slap_sasl_log, NULL },
1245                     { SASL_CB_GETOPT, (slap_sasl_cb_ft)&slap_sasl_getopt, NULL },
1246                     { SASL_CB_LIST_END, NULL, NULL }
1247           };
1248 #endif
1249 
1250           rewrite_mapper_register( &slapd_mapper );
1251 
1252 #ifdef HAVE_CYRUS_SASL
1253 #ifdef HAVE_SASL_VERSION
1254           /* stringify the version number, sasl.h doesn't do it for us */
1255 #define   VSTR0(maj, min, pat)          #maj "." #min "." #pat
1256 #define   VSTR(maj, min, pat) VSTR0(maj, min, pat)
1257 #define   SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \
1258                                         SASL_VERSION_STEP)
1259 
1260           sasl_version( NULL, &rc );
1261           if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) ||
1262                     (rc & 0xffff) < SASL_VERSION_STEP)
1263           {
1264                     char version[sizeof("xxx.xxx.xxxxx")];
1265                     sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff,
1266                               rc & 0xffff );
1267                     Debug( LDAP_DEBUG_ANY, "slap_sasl_init: SASL library version mismatch:"
1268                               " expected %s, got %s\n",
1269                               SASL_VERSION_STRING, version );
1270                     return -1;
1271           }
1272 #endif
1273 
1274           sasl_set_mutex(
1275                     ldap_pvt_sasl_mutex_new,
1276                     ldap_pvt_sasl_mutex_lock,
1277                     ldap_pvt_sasl_mutex_unlock,
1278                     ldap_pvt_sasl_mutex_dispose );
1279 
1280           generic_filter.f_desc = slap_schema.si_ad_objectClass;
1281 
1282           rc = sasl_auxprop_add_plugin( "slapd", slap_auxprop_init );
1283           if( rc != SASL_OK ) {
1284                     Debug( LDAP_DEBUG_ANY, "slap_sasl_init: auxprop add plugin failed\n" );
1285                     return -1;
1286           }
1287 
1288           /* should provide callbacks for logging */
1289           /* server name should be configurable */
1290           rc = sasl_server_init( server_callbacks, "slapd" );
1291 
1292           if( rc != SASL_OK ) {
1293                     Debug( LDAP_DEBUG_ANY, "slap_sasl_init: server init failed\n" );
1294 
1295                     return -1;
1296           }
1297 
1298 #ifdef SLAPD_SPASSWD
1299           lutil_passwd_add( &sasl_pwscheme, chk_sasl, NULL );
1300 #endif
1301 
1302           Debug( LDAP_DEBUG_TRACE, "slap_sasl_init: initialized!\n" );
1303 
1304           /* default security properties */
1305           memset( &sasl_secprops, '\0', sizeof(sasl_secprops) );
1306           sasl_secprops.max_ssf = INT_MAX;
1307           sasl_secprops.maxbufsize = 65536;
1308           sasl_secprops.security_flags = SASL_SEC_NOPLAINTEXT|SASL_SEC_NOANONYMOUS;
1309 #endif
1310 
1311           return 0;
1312 }
1313 
slap_sasl_destroy(void)1314 int slap_sasl_destroy( void )
1315 {
1316 #ifdef HAVE_CYRUS_SASL
1317           sasl_done();
1318 
1319 #ifdef SLAP_AUXPROP_DONTUSECOPY
1320           if ( slap_dontUseCopy_propnames ) {
1321                     ber_bvarray_free( slap_dontUseCopy_propnames );
1322                     slap_dontUseCopy_propnames = NULL;
1323           }
1324 #endif /* SLAP_AUXPROP_DONTUSECOPY */
1325 #endif
1326           free( sasl_host );
1327           sasl_host = NULL;
1328           free( sasl_cbinding );
1329           sasl_cbinding = NULL;
1330 
1331           return 0;
1332 }
1333 
1334 static char *
slap_sasl_peer2ipport(struct berval * peer)1335 slap_sasl_peer2ipport( struct berval *peer )
1336 {
1337           int                 isv6 = 0;
1338           char                *ipport, *p,
1339                               *addr = &peer->bv_val[ STRLENOF( "IP=" ) ];
1340           ber_len_t plen = peer->bv_len - STRLENOF( "IP=" );
1341 
1342           /* IPv6? */
1343           if ( addr[0] == '[' ) {
1344                     isv6 = 1;
1345                     plen--;
1346           }
1347           ipport = ch_strdup( &addr[isv6] );
1348 
1349           /* Convert IPv6/IPv4 addresses to address;port syntax. */
1350           p = strrchr( ipport, ':' );
1351           if ( p != NULL ) {
1352                     *p = ';';
1353                     if ( isv6 ) {
1354                               assert( p[-1] == ']' );
1355                               AC_MEMCPY( &p[-1], p, plen - ( p - ipport ) + 1 );
1356                     }
1357 
1358           } else if ( isv6 ) {
1359                     /* trim ']' */
1360                     plen--;
1361                     assert( addr[plen] == ']' );
1362                     addr[plen] = '\0';
1363           }
1364 
1365           return ipport;
1366 }
1367 
slap_sasl_open(Connection * conn,int reopen)1368 int slap_sasl_open( Connection *conn, int reopen )
1369 {
1370           int sc = LDAP_SUCCESS;
1371 #ifdef HAVE_CYRUS_SASL
1372           int cb;
1373 
1374           sasl_conn_t *ctx = NULL;
1375           sasl_callback_t *session_callbacks;
1376           char *ipremoteport = NULL, *iplocalport = NULL;
1377 
1378           assert( conn->c_sasl_authctx == NULL );
1379 
1380           if ( !reopen ) {
1381                     assert( conn->c_sasl_extra == NULL );
1382 
1383                     session_callbacks =
1384                               SLAP_CALLOC( 5, sizeof(sasl_callback_t));
1385                     if( session_callbacks == NULL ) {
1386                               Debug( LDAP_DEBUG_ANY,
1387                                         "slap_sasl_open: SLAP_MALLOC failed" );
1388                               return -1;
1389                     }
1390                     conn->c_sasl_extra = session_callbacks;
1391 
1392                     session_callbacks[cb=0].id = SASL_CB_LOG;
1393                     session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_log;
1394                     session_callbacks[cb++].context = conn;
1395 
1396                     session_callbacks[cb].id = SASL_CB_PROXY_POLICY;
1397                     session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_authorize;
1398                     session_callbacks[cb++].context = conn;
1399 
1400                     session_callbacks[cb].id = SASL_CB_CANON_USER;
1401                     session_callbacks[cb].proc = (slap_sasl_cb_ft)&slap_sasl_canonicalize;
1402                     session_callbacks[cb++].context = conn;
1403 
1404                     session_callbacks[cb].id = SASL_CB_LIST_END;
1405                     session_callbacks[cb].proc = NULL;
1406                     session_callbacks[cb++].context = NULL;
1407           } else {
1408                     session_callbacks = conn->c_sasl_extra;
1409           }
1410 
1411           conn->c_sasl_layers = 0;
1412 
1413           /* create new SASL context */
1414           if ( conn->c_sock_name.bv_len != 0 &&
1415                     strncmp( conn->c_sock_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 )
1416           {
1417                     iplocalport = slap_sasl_peer2ipport( &conn->c_sock_name );
1418           }
1419 
1420           if ( conn->c_peer_name.bv_len != 0 &&
1421                     strncmp( conn->c_peer_name.bv_val, "IP=", STRLENOF( "IP=" ) ) == 0 )
1422           {
1423                     ipremoteport = slap_sasl_peer2ipport( &conn->c_peer_name );
1424           }
1425 
1426           sc = sasl_server_new( "ldap", sasl_host, global_realm,
1427                     iplocalport, ipremoteport, session_callbacks, SASL_SUCCESS_DATA, &ctx );
1428           if ( iplocalport != NULL ) {
1429                     ch_free( iplocalport );
1430           }
1431           if ( ipremoteport != NULL ) {
1432                     ch_free( ipremoteport );
1433           }
1434 
1435           if( sc != SASL_OK ) {
1436                     Debug( LDAP_DEBUG_ANY, "sasl_server_new failed: %d\n",
1437                               sc );
1438 
1439                     return -1;
1440           }
1441 
1442           conn->c_sasl_authctx = ctx;
1443 
1444           if( sc == SASL_OK ) {
1445                     sc = sasl_setprop( ctx,
1446                               SASL_SEC_PROPS, &sasl_secprops );
1447 
1448                     if( sc != SASL_OK ) {
1449                               Debug( LDAP_DEBUG_ANY, "sasl_setprop failed: %d\n",
1450                                         sc );
1451 
1452                               slap_sasl_close( conn );
1453                               return -1;
1454                     }
1455           }
1456 
1457           sc = slap_sasl_err2ldap( sc );
1458 
1459 #elif defined(SLAP_BUILTIN_SASL)
1460           /* built-in SASL implementation */
1461           SASL_CTX *ctx = (SASL_CTX *) SLAP_MALLOC(sizeof(SASL_CTX));
1462           if( ctx == NULL ) return -1;
1463 
1464           ctx->sc_external_ssf = 0;
1465           BER_BVZERO( &ctx->sc_external_id );
1466 
1467           conn->c_sasl_authctx = ctx;
1468 #endif
1469 
1470           return sc;
1471 }
1472 
slap_sasl_external(Connection * conn,slap_ssf_t ssf,struct berval * auth_id)1473 int slap_sasl_external(
1474           Connection *conn,
1475           slap_ssf_t ssf,
1476           struct berval *auth_id )
1477 {
1478 #ifdef HAVE_CYRUS_SASL
1479           int sc;
1480           sasl_conn_t *ctx = conn->c_sasl_authctx;
1481           sasl_ssf_t sasl_ssf = ssf;
1482 
1483           if ( ctx == NULL ) {
1484                     return LDAP_UNAVAILABLE;
1485           }
1486 
1487           sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
1488 
1489           if ( sc != SASL_OK ) {
1490                     return LDAP_OTHER;
1491           }
1492 
1493           sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL,
1494                     auth_id ? auth_id->bv_val : NULL );
1495 
1496           if ( sc != SASL_OK ) {
1497                     return LDAP_OTHER;
1498           }
1499 #elif defined(SLAP_BUILTIN_SASL)
1500           /* built-in SASL implementation */
1501           SASL_CTX *ctx = conn->c_sasl_authctx;
1502           if ( ctx == NULL ) return LDAP_UNAVAILABLE;
1503 
1504           ctx->sc_external_ssf = ssf;
1505           if( auth_id ) {
1506                     ctx->sc_external_id = *auth_id;
1507                     BER_BVZERO( auth_id );
1508           } else {
1509                     BER_BVZERO( &ctx->sc_external_id );
1510           }
1511 #endif
1512 
1513           return LDAP_SUCCESS;
1514 }
1515 
slap_sasl_cbinding(Connection * conn,void * ssl)1516 int slap_sasl_cbinding( Connection *conn, void *ssl )
1517 {
1518 #ifdef SASL_CHANNEL_BINDING
1519           void *cb;
1520           int i;
1521 
1522           if ( sasl_cbinding == NULL )
1523                     return LDAP_SUCCESS;
1524 
1525           i = ldap_pvt_sasl_cbinding_parse( sasl_cbinding );
1526           if ( i < 0 )
1527                     return LDAP_SUCCESS;
1528 
1529           cb = ldap_pvt_sasl_cbinding( ssl, i, 1 );
1530           if ( cb != NULL ) {
1531                     sasl_setprop( conn->c_sasl_authctx, SASL_CHANNEL_BINDING, cb );
1532                     conn->c_sasl_cbind = cb;
1533           }
1534 #endif
1535           return LDAP_SUCCESS;
1536 }
1537 
slap_sasl_reset(Connection * conn)1538 int slap_sasl_reset( Connection *conn )
1539 {
1540           return LDAP_SUCCESS;
1541 }
1542 
slap_sasl_mechs(Connection * conn)1543 char ** slap_sasl_mechs( Connection *conn )
1544 {
1545           char **mechs = NULL;
1546 
1547 #ifdef HAVE_CYRUS_SASL
1548           sasl_conn_t *ctx = conn->c_sasl_authctx;
1549 
1550           if( ctx == NULL ) ctx = conn->c_sasl_sockctx;
1551 
1552           if( ctx != NULL ) {
1553                     int sc;
1554                     SASL_CONST char *mechstr;
1555 
1556                     sc = sasl_listmech( ctx,
1557                               NULL, NULL, ",", NULL,
1558                               &mechstr, NULL, NULL );
1559 
1560                     if( sc != SASL_OK ) {
1561                               Debug( LDAP_DEBUG_ANY, "slap_sasl_listmech failed: %d\n",
1562                                         sc );
1563 
1564                               return NULL;
1565                     }
1566 
1567                     mechs = ldap_str2charray( mechstr, "," );
1568           }
1569 #elif defined(SLAP_BUILTIN_SASL)
1570           /* builtin SASL implementation */
1571           SASL_CTX *ctx = conn->c_sasl_authctx;
1572           if ( ctx != NULL && ctx->sc_external_id.bv_val ) {
1573                     /* should check ssf */
1574                     mechs = ldap_str2charray( "EXTERNAL", "," );
1575           }
1576 #endif
1577 
1578           return mechs;
1579 }
1580 
slap_sasl_close(Connection * conn)1581 int slap_sasl_close( Connection *conn )
1582 {
1583 #ifdef HAVE_CYRUS_SASL
1584           sasl_conn_t *ctx = conn->c_sasl_authctx;
1585 
1586           if( ctx != NULL ) {
1587                     sasl_dispose( &ctx );
1588           }
1589           if ( conn->c_sasl_sockctx &&
1590                     conn->c_sasl_authctx != conn->c_sasl_sockctx )
1591           {
1592                     ctx = conn->c_sasl_sockctx;
1593                     sasl_dispose( &ctx );
1594           }
1595 
1596           conn->c_sasl_authctx = NULL;
1597           conn->c_sasl_sockctx = NULL;
1598           conn->c_sasl_done = 0;
1599 
1600           free( conn->c_sasl_extra );
1601           conn->c_sasl_extra = NULL;
1602 
1603           free( conn->c_sasl_cbind );
1604           conn->c_sasl_cbind = NULL;
1605 
1606 #elif defined(SLAP_BUILTIN_SASL)
1607           SASL_CTX *ctx = conn->c_sasl_authctx;
1608           if( ctx ) {
1609                     if( ctx->sc_external_id.bv_val ) {
1610                               free( ctx->sc_external_id.bv_val );
1611                               BER_BVZERO( &ctx->sc_external_id );
1612                     }
1613                     free( ctx );
1614                     conn->c_sasl_authctx = NULL;
1615           }
1616 #endif
1617 
1618           return LDAP_SUCCESS;
1619 }
1620 
slap_sasl_bind(Operation * op,SlapReply * rs)1621 int slap_sasl_bind( Operation *op, SlapReply *rs )
1622 {
1623 #ifdef HAVE_CYRUS_SASL
1624           sasl_conn_t *ctx = op->o_conn->c_sasl_authctx;
1625           struct berval response;
1626           unsigned reslen = 0;
1627           int sc;
1628 
1629           Debug(LDAP_DEBUG_ARGS,
1630                     "==> sasl_bind: dn=\"%s\" mech=%s datalen=%ld\n",
1631                     op->o_req_dn.bv_len ? op->o_req_dn.bv_val : "",
1632                     op->o_conn->c_sasl_bind_in_progress ? "<continuing>" :
1633                     op->o_conn->c_sasl_bind_mech.bv_val,
1634                     op->orb_cred.bv_len );
1635 
1636           if( ctx == NULL ) {
1637                     send_ldap_error( op, rs, LDAP_UNAVAILABLE,
1638                               "SASL unavailable on this session" );
1639                     return rs->sr_err;
1640           }
1641 
1642 #define   START( ctx, mech, cred, clen, resp, rlen, err ) \
1643           sasl_server_start( ctx, mech, cred, clen, resp, rlen )
1644 #define   STEP( ctx, cred, clen, resp, rlen, err ) \
1645           sasl_server_step( ctx, cred, clen, resp, rlen )
1646 
1647           if ( !op->o_conn->c_sasl_bind_in_progress ) {
1648                     /* If we already authenticated once, must use a new context */
1649                     if ( op->o_conn->c_sasl_done ) {
1650                               sasl_ssf_t ssf = 0;
1651                               sasl_ssf_t *ssfp = NULL;
1652                               const char *authid = NULL;
1653 
1654                               sasl_getprop( ctx, SASL_SSF_EXTERNAL, (void *)&ssfp );
1655                               if ( ssfp ) ssf = *ssfp;
1656 
1657                               sasl_getprop( ctx, SASL_AUTH_EXTERNAL, (void *)&authid );
1658                               if ( authid ) authid = ch_strdup( authid );
1659 
1660                               if ( ctx != op->o_conn->c_sasl_sockctx ) {
1661                                         sasl_dispose( &ctx );
1662                               }
1663                               op->o_conn->c_sasl_authctx = NULL;
1664 
1665                               slap_sasl_open( op->o_conn, 1 );
1666                               ctx = op->o_conn->c_sasl_authctx;
1667                               sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf );
1668                               if ( authid ) {
1669                                         sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
1670                                         ch_free( (char *)authid );
1671                               }
1672                     }
1673                     sc = START( ctx,
1674                               op->o_conn->c_sasl_bind_mech.bv_val,
1675                               op->orb_cred.bv_val, op->orb_cred.bv_len,
1676                               (SASL_CONST char **)&response.bv_val, &reslen, &rs->sr_text );
1677 
1678           } else {
1679                     sc = STEP( ctx,
1680                               op->orb_cred.bv_val, op->orb_cred.bv_len,
1681                               (SASL_CONST char **)&response.bv_val, &reslen, &rs->sr_text );
1682           }
1683 
1684           response.bv_len = reslen;
1685 
1686           if ( sc == SASL_OK ) {
1687                     sasl_ssf_t *ssf = NULL;
1688 
1689                     ber_dupbv_x( &op->orb_edn, &op->o_conn->c_sasl_dn, op->o_tmpmemctx );
1690                     BER_BVZERO( &op->o_conn->c_sasl_dn );
1691                     op->o_conn->c_sasl_done = 1;
1692 
1693                     rs->sr_err = LDAP_SUCCESS;
1694 
1695                     (void) sasl_getprop( ctx, SASL_SSF, (void *)&ssf );
1696                     op->orb_ssf = ssf ? *ssf : 0;
1697 
1698                     ctx = NULL;
1699                     if( op->orb_ssf ) {
1700                               ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
1701                               op->o_conn->c_sasl_layers++;
1702 
1703                               /* If there's an old layer, set sockctx to NULL to
1704                                * tell connection_read() to wait for us to finish.
1705                                * Otherwise there is a race condition: we have to
1706                                * send the Bind response using the old security
1707                                * context and then remove it before reading any
1708                                * new messages.
1709                                */
1710                               if ( op->o_conn->c_sasl_sockctx ) {
1711                                         ctx = op->o_conn->c_sasl_sockctx;
1712                                         op->o_conn->c_sasl_sockctx = NULL;
1713                               } else {
1714                                         op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx;
1715                               }
1716                               ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
1717                     }
1718 
1719                     /* Must send response using old security layer */
1720                     rs->sr_sasldata = (response.bv_len ? &response : NULL);
1721                     send_ldap_sasl( op, rs );
1722 
1723                     /* Now dispose of the old security layer.
1724                      */
1725                     if ( ctx ) {
1726                               ldap_pvt_thread_mutex_lock( &op->o_conn->c_mutex );
1727                               ldap_pvt_sasl_remove( op->o_conn->c_sb );
1728                               op->o_conn->c_sasl_sockctx = op->o_conn->c_sasl_authctx;
1729                               ldap_pvt_thread_mutex_unlock( &op->o_conn->c_mutex );
1730                               sasl_dispose( &ctx );
1731                     }
1732           } else if ( sc == SASL_CONTINUE ) {
1733                     rs->sr_err = LDAP_SASL_BIND_IN_PROGRESS,
1734                     rs->sr_text = sasl_errdetail( ctx );
1735                     rs->sr_sasldata = &response;
1736                     send_ldap_sasl( op, rs );
1737 
1738           } else {
1739                     BER_BVZERO( &op->o_conn->c_sasl_dn );
1740                     rs->sr_text = sasl_errdetail( ctx );
1741                     rs->sr_err = slap_sasl_err2ldap( sc ),
1742                     send_ldap_result( op, rs );
1743           }
1744 
1745           Debug(LDAP_DEBUG_TRACE, "<== slap_sasl_bind: rc=%d\n", rs->sr_err );
1746 
1747 #elif defined(SLAP_BUILTIN_SASL)
1748           /* built-in SASL implementation */
1749           SASL_CTX *ctx = op->o_conn->c_sasl_authctx;
1750 
1751           if ( ctx == NULL ) {
1752                     send_ldap_error( op, rs, LDAP_OTHER,
1753                               "Internal SASL Error" );
1754 
1755           } else if ( bvmatch( &ext_bv, &op->o_conn->c_sasl_bind_mech ) ) {
1756                     /* EXTERNAL */
1757 
1758                     if( op->orb_cred.bv_len ) {
1759                               rs->sr_text = "proxy authorization not supported";
1760                               rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1761                               send_ldap_result( op, rs );
1762 
1763                     } else {
1764                               op->orb_edn = ctx->sc_external_id;
1765                               rs->sr_err = LDAP_SUCCESS;
1766                               rs->sr_sasldata = NULL;
1767                               send_ldap_sasl( op, rs );
1768                     }
1769 
1770           } else {
1771                     send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED,
1772                               "requested SASL mechanism not supported" );
1773           }
1774 #else
1775           send_ldap_error( op, rs, LDAP_AUTH_METHOD_NOT_SUPPORTED,
1776                     "SASL not supported" );
1777 #endif
1778 
1779           return rs->sr_err;
1780 }
1781 
slap_sasl_secprops(const char * in)1782 char* slap_sasl_secprops( const char *in )
1783 {
1784 #ifdef HAVE_CYRUS_SASL
1785           int rc = ldap_pvt_sasl_secprops( in, &sasl_secprops );
1786 
1787           return rc == LDAP_SUCCESS ? NULL : "Invalid security properties";
1788 #else
1789           return "SASL not supported";
1790 #endif
1791 }
1792 
slap_sasl_secprops_unparse(struct berval * bv)1793 void slap_sasl_secprops_unparse( struct berval *bv )
1794 {
1795 #ifdef HAVE_CYRUS_SASL
1796           ldap_pvt_sasl_secprops_unparse( &sasl_secprops, bv );
1797 #endif
1798 }
1799 
1800 #ifdef HAVE_CYRUS_SASL
1801 int
slap_sasl_setpass(Operation * op,SlapReply * rs)1802 slap_sasl_setpass( Operation *op, SlapReply *rs )
1803 {
1804           struct berval id = BER_BVNULL;          /* needs to come from connection */
1805           struct berval new = BER_BVNULL;
1806           struct berval old = BER_BVNULL;
1807 
1808           assert( ber_bvcmp( &slap_EXOP_MODIFY_PASSWD, &op->ore_reqoid ) == 0 );
1809 
1810           rs->sr_err = sasl_getprop( op->o_conn->c_sasl_authctx, SASL_USERNAME,
1811                     (SASL_CONST void **)(char *)&id.bv_val );
1812 
1813           if( rs->sr_err != SASL_OK ) {
1814                     rs->sr_text = "unable to retrieve SASL username";
1815                     rs->sr_err = LDAP_OTHER;
1816                     goto done;
1817           }
1818 
1819           Debug( LDAP_DEBUG_ARGS, "==> slap_sasl_setpass: \"%s\"\n",
1820                     id.bv_val ? id.bv_val : "" );
1821 
1822           rs->sr_err = slap_passwd_parse( op->ore_reqdata,
1823                     NULL, &old, &new, &rs->sr_text );
1824 
1825           if( rs->sr_err != LDAP_SUCCESS ) {
1826                     goto done;
1827           }
1828 
1829           if( new.bv_len == 0 ) {
1830                     slap_passwd_generate(&new);
1831 
1832                     if( new.bv_len == 0 ) {
1833                               rs->sr_text = "password generation failed.";
1834                               rs->sr_err = LDAP_OTHER;
1835                               goto done;
1836                     }
1837 
1838                     rs->sr_rspdata = slap_passwd_return( &new );
1839           }
1840 
1841           rs->sr_err = sasl_setpass( op->o_conn->c_sasl_authctx, id.bv_val,
1842                     new.bv_val, new.bv_len, old.bv_val, old.bv_len, 0 );
1843           if( rs->sr_err != SASL_OK ) {
1844                     rs->sr_text = sasl_errdetail( op->o_conn->c_sasl_authctx );
1845           }
1846           switch(rs->sr_err) {
1847                     case SASL_OK:
1848                               rs->sr_err = LDAP_SUCCESS;
1849                               break;
1850 
1851                     case SASL_NOCHANGE:
1852                     case SASL_NOMECH:
1853                     case SASL_DISABLED:
1854                     case SASL_PWLOCK:
1855                     case SASL_FAIL:
1856                     case SASL_BADPARAM:
1857                     default:
1858                               rs->sr_err = LDAP_OTHER;
1859           }
1860 
1861 done:
1862           return rs->sr_err;
1863 }
1864 #endif /* HAVE_CYRUS_SASL */
1865 
1866 /* Take any sort of identity string and return a DN with the "dn:" prefix. The
1867  * string returned in *dn is in its own allocated memory, and must be free'd
1868  * by the calling process.  -Mark Adamson, Carnegie Mellon
1869  *
1870  * The "dn:" prefix is no longer used anywhere inside slapd. It is only used
1871  * on strings passed in directly from SASL.  -Howard Chu, Symas Corp.
1872  */
1873 
1874 #define SET_NONE    0
1875 #define   SET_DN              1
1876 #define   SET_U               2
1877 
slap_sasl_getdn(Connection * conn,Operation * op,struct berval * id,char * user_realm,struct berval * dn,int flags)1878 int slap_sasl_getdn( Connection *conn, Operation *op, struct berval *id,
1879           char *user_realm, struct berval *dn, int flags )
1880 {
1881           int rc, is_dn = SET_NONE, do_norm = 1;
1882           struct berval dn2, *mech;
1883 
1884           assert( conn != NULL );
1885           assert( id != NULL );
1886 
1887           Debug( LDAP_DEBUG_ARGS, "slap_sasl_getdn: conn %lu id=%s [len=%lu]\n",
1888                     conn->c_connid,
1889                     BER_BVISNULL( id ) ? "NULL" : ( BER_BVISEMPTY( id ) ? "<empty>" : id->bv_val ),
1890                     BER_BVISNULL( id ) ? 0 : ( BER_BVISEMPTY( id ) ? 0 :
1891                                                (unsigned long) id->bv_len ) );
1892 
1893           if ( !op ) {
1894                     op = conn->c_sasl_bindop;
1895           }
1896           assert( op != NULL );
1897 
1898           BER_BVZERO( dn );
1899 
1900           if ( !BER_BVISNULL( id ) ) {
1901                     /* Blatantly anonymous ID */
1902                     static struct berval bv_anonymous = BER_BVC( "anonymous" );
1903 
1904                     if ( ber_bvstrcasecmp( id, &bv_anonymous ) == 0 ) {
1905                               return( LDAP_SUCCESS );
1906                     }
1907 
1908           } else {
1909                     /* FIXME: if empty, should we stop? */
1910                     BER_BVSTR( id, "" );
1911           }
1912 
1913           if ( !BER_BVISEMPTY( &conn->c_sasl_bind_mech ) ) {
1914                     mech = &conn->c_sasl_bind_mech;
1915           } else {
1916                     mech = &conn->c_authmech;
1917           }
1918 
1919           /* An authcID needs to be converted to authzID form. Set the
1920            * values directly into *dn; they will be normalized later. (and
1921            * normalizing always makes a new copy.) An ID from a TLS certificate
1922            * is already normalized, so copy it and skip normalization.
1923            */
1924           if( flags & SLAP_GETDN_AUTHCID ) {
1925                     if( bvmatch( mech, &ext_bv )) {
1926                               /* EXTERNAL DNs are already normalized */
1927                               assert( !BER_BVISNULL( id ) );
1928 
1929                               do_norm = 0;
1930                               is_dn = SET_DN;
1931                               ber_dupbv_x( dn, id, op->o_tmpmemctx );
1932 
1933                     } else {
1934                               /* convert to u:<username> form */
1935                               is_dn = SET_U;
1936                               *dn = *id;
1937                     }
1938           }
1939 
1940           if( is_dn == SET_NONE ) {
1941                     if( !strncasecmp( id->bv_val, "u:", STRLENOF( "u:" ) ) ) {
1942                               is_dn = SET_U;
1943                               dn->bv_val = id->bv_val + STRLENOF( "u:" );
1944                               dn->bv_len = id->bv_len - STRLENOF( "u:" );
1945 
1946                     } else if ( !strncasecmp( id->bv_val, "dn:", STRLENOF( "dn:" ) ) ) {
1947                               is_dn = SET_DN;
1948                               dn->bv_val = id->bv_val + STRLENOF( "dn:" );
1949                               dn->bv_len = id->bv_len - STRLENOF( "dn:" );
1950                     }
1951           }
1952 
1953           /* No other possibilities from here */
1954           if( is_dn == SET_NONE ) {
1955                     BER_BVZERO( dn );
1956                     return( LDAP_INAPPROPRIATE_AUTH );
1957           }
1958 
1959           /* Username strings */
1960           if( is_dn == SET_U ) {
1961                     /* ITS#3419: values may need escape */
1962                     LDAPRDN             DN[ 5 ];
1963                     LDAPAVA   *RDNs[ 4 ][ 2 ];
1964                     LDAPAVA   AVAs[ 4 ];
1965                     int                 irdn;
1966 
1967                     irdn = 0;
1968                     DN[ irdn ] = RDNs[ irdn ];
1969                     RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
1970                     AVAs[ irdn ].la_attr = slap_schema.si_ad_uid->ad_cname;
1971                     AVAs[ irdn ].la_value = *dn;
1972                     AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
1973                     AVAs[ irdn ].la_private = NULL;
1974                     RDNs[ irdn ][ 1 ] = NULL;
1975 
1976                     if ( user_realm && *user_realm ) {
1977                               irdn++;
1978                               DN[ irdn ] = RDNs[ irdn ];
1979                               RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
1980                               AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
1981                               ber_str2bv( user_realm, 0, 0, &AVAs[ irdn ].la_value );
1982                               AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
1983                               AVAs[ irdn ].la_private = NULL;
1984                               RDNs[ irdn ][ 1 ] = NULL;
1985                     }
1986 
1987                     if ( !BER_BVISNULL( mech ) ) {
1988                               irdn++;
1989                               DN[ irdn ] = RDNs[ irdn ];
1990                               RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
1991                               AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
1992                               AVAs[ irdn ].la_value = *mech;
1993                               AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
1994                               AVAs[ irdn ].la_private = NULL;
1995                               RDNs[ irdn ][ 1 ] = NULL;
1996                     }
1997 
1998                     irdn++;
1999                     DN[ irdn ] = RDNs[ irdn ];
2000                     RDNs[ irdn ][ 0 ] = &AVAs[ irdn ];
2001                     AVAs[ irdn ].la_attr = slap_schema.si_ad_cn->ad_cname;
2002                     BER_BVSTR( &AVAs[ irdn ].la_value, "auth" );
2003                     AVAs[ irdn ].la_flags = LDAP_AVA_NULL;
2004                     AVAs[ irdn ].la_private = NULL;
2005                     RDNs[ irdn ][ 1 ] = NULL;
2006 
2007                     irdn++;
2008                     DN[ irdn ] = NULL;
2009 
2010                     rc = ldap_dn2bv_x( DN, dn, LDAP_DN_FORMAT_LDAPV3,
2011                                         op->o_tmpmemctx );
2012                     if ( rc != LDAP_SUCCESS ) {
2013                               BER_BVZERO( dn );
2014                               return rc;
2015                     }
2016 
2017                     Debug( LDAP_DEBUG_TRACE,
2018                               "slap_sasl_getdn: u:id converted to %s\n",
2019                               dn->bv_val );
2020 
2021           } else {
2022 
2023                     /* Dup the DN in any case, so we don't risk
2024                      * leaks or dangling pointers later,
2025                      * and the DN value is '\0' terminated */
2026                     ber_dupbv_x( &dn2, dn, op->o_tmpmemctx );
2027                     dn->bv_val = dn2.bv_val;
2028           }
2029 
2030           /* All strings are in DN form now. Normalize if needed. */
2031           if ( do_norm ) {
2032                     rc = dnNormalize( 0, NULL, NULL, dn, &dn2, op->o_tmpmemctx );
2033 
2034                     /* User DNs were constructed above and must be freed now */
2035                     slap_sl_free( dn->bv_val, op->o_tmpmemctx );
2036 
2037                     if ( rc != LDAP_SUCCESS ) {
2038                               BER_BVZERO( dn );
2039                               return rc;
2040                     }
2041                     *dn = dn2;
2042           }
2043 
2044           /* Run thru regexp */
2045           slap_sasl2dn( op, dn, &dn2, flags );
2046           if( !BER_BVISNULL( &dn2 ) ) {
2047                     slap_sl_free( dn->bv_val, op->o_tmpmemctx );
2048                     *dn = dn2;
2049                     Debug( LDAP_DEBUG_TRACE,
2050                               "slap_sasl_getdn: dn:id converted to %s\n",
2051                               dn->bv_val );
2052           }
2053 
2054           return( LDAP_SUCCESS );
2055 }
2056