1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3 *
4 * Copyright 1998-2021 The OpenLDAP Foundation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted only as authorized by the OpenLDAP
9 * Public License.
10 *
11 * A copy of this license is available in the file LICENSE in the
12 * top-level directory of the distribution or, alternatively, at
13 * <http://www.OpenLDAP.org/license.html>.
14 */
15
16 #include "portable.h"
17
18 #include "ldap-int.h"
19
20 #ifdef HAVE_CYRUS_SASL
21
22 #include <stdio.h>
23
24 #include <ac/socket.h>
25 #include <ac/stdlib.h>
26 #include <ac/string.h>
27 #include <ac/time.h>
28 #include <ac/errno.h>
29 #include <ac/ctype.h>
30 #include <ac/unistd.h>
31
32 #ifdef HAVE_LIMITS_H
33 #include <limits.h>
34 #endif
35
36 #ifndef INT_MAX
37 #define INT_MAX 2147483647 /* 32 bit signed max */
38 #endif
39
40 #if !defined(HOST_NAME_MAX) && defined(_POSIX_HOST_NAME_MAX)
41 #define HOST_NAME_MAX _POSIX_HOST_NAME_MAX
42 #endif
43
44 #ifdef HAVE_SASL_SASL_H
45 #include <sasl/sasl.h>
46 #else
47 #include <sasl.h>
48 #endif
49
50 #if SASL_VERSION_MAJOR >= 2
51 #define SASL_CONST const
52 #else
53 #define SASL_CONST
54 #endif
55
56 /*
57 * Various Cyrus SASL related stuff.
58 */
59
60 static const sasl_callback_t client_callbacks[] = {
61 #ifdef SASL_CB_GETREALM
62 { SASL_CB_GETREALM, NULL, NULL },
63 #endif
64 { SASL_CB_USER, NULL, NULL },
65 { SASL_CB_AUTHNAME, NULL, NULL },
66 { SASL_CB_PASS, NULL, NULL },
67 { SASL_CB_ECHOPROMPT, NULL, NULL },
68 { SASL_CB_NOECHOPROMPT, NULL, NULL },
69 { SASL_CB_LIST_END, NULL, NULL }
70 };
71
72 /*
73 * ldap_int_initialize is responsible for calling this only once.
74 */
ldap_int_sasl_init(void)75 int ldap_int_sasl_init( void )
76 {
77 #ifdef HAVE_SASL_VERSION
78 /* stringify the version number, sasl.h doesn't do it for us */
79 #define VSTR0(maj, min, pat) #maj "." #min "." #pat
80 #define VSTR(maj, min, pat) VSTR0(maj, min, pat)
81 #define SASL_VERSION_STRING VSTR(SASL_VERSION_MAJOR, SASL_VERSION_MINOR, \
82 SASL_VERSION_STEP)
83 { int rc;
84 sasl_version( NULL, &rc );
85 if ( ((rc >> 16) != ((SASL_VERSION_MAJOR << 8)|SASL_VERSION_MINOR)) ||
86 (rc & 0xffff) < SASL_VERSION_STEP) {
87 char version[sizeof("xxx.xxx.xxxxx")];
88 sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff,
89 rc & 0xffff );
90
91 Debug( LDAP_DEBUG_ANY,
92 "ldap_int_sasl_init: SASL library version mismatch:"
93 " expected " SASL_VERSION_STRING ","
94 " got %s\n", version, 0, 0 );
95 return -1;
96 }
97 }
98 #endif
99
100 /* SASL 2 takes care of its own memory completely internally */
101 #if SASL_VERSION_MAJOR < 2 && !defined(CSRIMALLOC)
102 sasl_set_alloc(
103 ber_memalloc,
104 ber_memcalloc,
105 ber_memrealloc,
106 ber_memfree );
107 #endif /* CSRIMALLOC */
108
109 #ifdef LDAP_R_COMPILE
110 sasl_set_mutex(
111 ldap_pvt_sasl_mutex_new,
112 ldap_pvt_sasl_mutex_lock,
113 ldap_pvt_sasl_mutex_unlock,
114 ldap_pvt_sasl_mutex_dispose );
115 #endif
116
117 if ( sasl_client_init( NULL ) == SASL_OK ) {
118 return 0;
119 }
120
121 #if SASL_VERSION_MAJOR < 2
122 /* A no-op to make sure we link with Cyrus 1.5 */
123 sasl_client_auth( NULL, NULL, NULL, 0, NULL, NULL );
124 #endif
125 return -1;
126 }
127
128 static void
sb_sasl_cyrus_init(struct sb_sasl_generic_data * p,ber_len_t * min_send,ber_len_t * max_send,ber_len_t * max_recv)129 sb_sasl_cyrus_init(
130 struct sb_sasl_generic_data *p,
131 ber_len_t *min_send,
132 ber_len_t *max_send,
133 ber_len_t *max_recv)
134 {
135 sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
136 ber_len_t maxbuf;
137
138 sasl_getprop( sasl_context, SASL_MAXOUTBUF,
139 (SASL_CONST void **)(char *) &maxbuf );
140
141 *min_send = SASL_MIN_BUFF_SIZE;
142 *max_send = maxbuf;
143 *max_recv = SASL_MAX_BUFF_SIZE;
144 }
145
146 static ber_int_t
sb_sasl_cyrus_encode(struct sb_sasl_generic_data * p,unsigned char * buf,ber_len_t len,Sockbuf_Buf * dst)147 sb_sasl_cyrus_encode(
148 struct sb_sasl_generic_data *p,
149 unsigned char *buf,
150 ber_len_t len,
151 Sockbuf_Buf *dst)
152 {
153 sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
154 ber_int_t ret;
155 unsigned tmpsize = dst->buf_size;
156
157 ret = sasl_encode( sasl_context, (char *)buf, len,
158 (SASL_CONST char **)&dst->buf_base,
159 &tmpsize );
160
161 dst->buf_size = tmpsize;
162 dst->buf_end = dst->buf_size;
163
164 if ( ret != SASL_OK ) {
165 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
166 "sb_sasl_cyrus_encode: failed to encode packet: %s\n",
167 sasl_errstring( ret, NULL, NULL ) );
168 return -1;
169 }
170
171 return 0;
172 }
173
174 static ber_int_t
sb_sasl_cyrus_decode(struct sb_sasl_generic_data * p,const Sockbuf_Buf * src,Sockbuf_Buf * dst)175 sb_sasl_cyrus_decode(
176 struct sb_sasl_generic_data *p,
177 const Sockbuf_Buf *src,
178 Sockbuf_Buf *dst)
179 {
180 sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private;
181 ber_int_t ret;
182 unsigned tmpsize = dst->buf_size;
183
184 ret = sasl_decode( sasl_context,
185 src->buf_base, src->buf_end,
186 (SASL_CONST char **)&dst->buf_base,
187 (unsigned *)&tmpsize );
188
189
190 dst->buf_size = tmpsize;
191 dst->buf_end = dst->buf_size;
192
193 if ( ret != SASL_OK ) {
194 ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug,
195 "sb_sasl_cyrus_decode: failed to decode packet: %s\n",
196 sasl_errstring( ret, NULL, NULL ) );
197 return -1;
198 }
199
200 return 0;
201 }
202
203 static void
sb_sasl_cyrus_reset_buf(struct sb_sasl_generic_data * p,Sockbuf_Buf * buf)204 sb_sasl_cyrus_reset_buf(
205 struct sb_sasl_generic_data *p,
206 Sockbuf_Buf *buf)
207 {
208 #if SASL_VERSION_MAJOR >= 2
209 ber_pvt_sb_buf_init( buf );
210 #else
211 ber_pvt_sb_buf_destroy( buf );
212 #endif
213 }
214
215 static void
sb_sasl_cyrus_fini(struct sb_sasl_generic_data * p)216 sb_sasl_cyrus_fini(
217 struct sb_sasl_generic_data *p)
218 {
219 #if SASL_VERSION_MAJOR >= 2
220 /*
221 * SASLv2 encode/decode buffers are managed by
222 * libsasl2. Ensure they are not freed by liblber.
223 */
224 p->buf_in.buf_base = NULL;
225 p->buf_out.buf_base = NULL;
226 #endif
227 }
228
229 static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = {
230 sb_sasl_cyrus_init,
231 sb_sasl_cyrus_encode,
232 sb_sasl_cyrus_decode,
233 sb_sasl_cyrus_reset_buf,
234 sb_sasl_cyrus_fini
235 };
236
ldap_pvt_sasl_install(Sockbuf * sb,void * ctx_arg)237 int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg )
238 {
239 struct sb_sasl_generic_install install_arg;
240
241 install_arg.ops = &sb_sasl_cyrus_ops;
242 install_arg.ops_private = ctx_arg;
243
244 return ldap_pvt_sasl_generic_install( sb, &install_arg );
245 }
246
ldap_pvt_sasl_remove(Sockbuf * sb)247 void ldap_pvt_sasl_remove( Sockbuf *sb )
248 {
249 ldap_pvt_sasl_generic_remove( sb );
250 }
251
252 static int
sasl_err2ldap(int saslerr)253 sasl_err2ldap( int saslerr )
254 {
255 int rc;
256
257 /* map SASL errors to LDAP API errors returned by:
258 * sasl_client_new()
259 * SASL_OK, SASL_NOMECH, SASL_NOMEM
260 * sasl_client_start()
261 * SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT
262 * sasl_client_step()
263 * SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV
264 */
265
266 switch (saslerr) {
267 case SASL_CONTINUE:
268 rc = LDAP_MORE_RESULTS_TO_RETURN;
269 break;
270 case SASL_INTERACT:
271 rc = LDAP_LOCAL_ERROR;
272 break;
273 case SASL_OK:
274 rc = LDAP_SUCCESS;
275 break;
276 case SASL_NOMEM:
277 rc = LDAP_NO_MEMORY;
278 break;
279 case SASL_NOMECH:
280 rc = LDAP_AUTH_UNKNOWN;
281 break;
282 case SASL_BADPROT:
283 rc = LDAP_DECODING_ERROR;
284 break;
285 case SASL_BADSERV:
286 rc = LDAP_AUTH_UNKNOWN;
287 break;
288
289 /* other codes */
290 case SASL_BADAUTH:
291 rc = LDAP_AUTH_UNKNOWN;
292 break;
293 case SASL_NOAUTHZ:
294 rc = LDAP_PARAM_ERROR;
295 break;
296 case SASL_FAIL:
297 rc = LDAP_LOCAL_ERROR;
298 break;
299 case SASL_TOOWEAK:
300 case SASL_ENCRYPT:
301 rc = LDAP_AUTH_UNKNOWN;
302 break;
303 default:
304 rc = LDAP_LOCAL_ERROR;
305 break;
306 }
307
308 assert( rc == LDAP_SUCCESS || LDAP_API_ERROR( rc ) );
309 return rc;
310 }
311
312 int
ldap_int_sasl_open(LDAP * ld,LDAPConn * lc,const char * host)313 ldap_int_sasl_open(
314 LDAP *ld,
315 LDAPConn *lc,
316 const char * host )
317 {
318 int rc;
319 sasl_conn_t *ctx;
320
321 assert( lc->lconn_sasl_authctx == NULL );
322
323 if ( host == NULL ) {
324 ld->ld_errno = LDAP_LOCAL_ERROR;
325 return ld->ld_errno;
326 }
327
328 #if SASL_VERSION_MAJOR >= 2
329 rc = sasl_client_new( "ldap", host, NULL, NULL,
330 client_callbacks, 0, &ctx );
331 #else
332 rc = sasl_client_new( "ldap", host, client_callbacks,
333 SASL_SECURITY_LAYER, &ctx );
334 #endif
335
336 if ( rc != SASL_OK ) {
337 ld->ld_errno = sasl_err2ldap( rc );
338 return ld->ld_errno;
339 }
340
341 Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n",
342 host, 0, 0 );
343
344 lc->lconn_sasl_authctx = ctx;
345
346 return LDAP_SUCCESS;
347 }
348
ldap_int_sasl_close(LDAP * ld,LDAPConn * lc)349 int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
350 {
351 sasl_conn_t *ctx = lc->lconn_sasl_authctx;
352
353 if( ctx != NULL ) {
354 sasl_dispose( &ctx );
355 if ( lc->lconn_sasl_sockctx &&
356 lc->lconn_sasl_authctx != lc->lconn_sasl_sockctx ) {
357 ctx = lc->lconn_sasl_sockctx;
358 sasl_dispose( &ctx );
359 }
360 lc->lconn_sasl_sockctx = NULL;
361 lc->lconn_sasl_authctx = NULL;
362 }
363
364 return LDAP_SUCCESS;
365 }
366
367 int
ldap_int_sasl_bind(LDAP * ld,const char * dn,const char * mechs,LDAPControl ** sctrls,LDAPControl ** cctrls,unsigned flags,LDAP_SASL_INTERACT_PROC * interact,void * defaults,LDAPMessage * result,const char ** rmech,int * msgid)368 ldap_int_sasl_bind(
369 LDAP *ld,
370 const char *dn,
371 const char *mechs,
372 LDAPControl **sctrls,
373 LDAPControl **cctrls,
374 unsigned flags,
375 LDAP_SASL_INTERACT_PROC *interact,
376 void *defaults,
377 LDAPMessage *result,
378 const char **rmech,
379 int *msgid )
380 {
381 const char *mech;
382 sasl_ssf_t *ssf;
383 sasl_conn_t *ctx;
384 sasl_interact_t *prompts = NULL;
385 struct berval ccred = BER_BVNULL;
386 int saslrc, rc;
387 unsigned credlen;
388 #if !defined(_WIN32)
389 char my_hostname[HOST_NAME_MAX + 1];
390 #endif
391 int free_saslhost = 0;
392
393 Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n",
394 mechs ? mechs : "<null>", 0, 0 );
395
396 /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */
397 if (ld->ld_version < LDAP_VERSION3) {
398 ld->ld_errno = LDAP_NOT_SUPPORTED;
399 return ld->ld_errno;
400 }
401
402 /* Starting a Bind */
403 if ( !result ) {
404 const char *pmech = NULL;
405 sasl_conn_t *oldctx;
406 ber_socket_t sd;
407 void *ssl;
408
409 rc = 0;
410 LDAP_MUTEX_LOCK( &ld->ld_conn_mutex );
411 ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
412
413 if ( sd == AC_SOCKET_INVALID || !ld->ld_defconn ) {
414 /* not connected yet */
415
416 rc = ldap_open_defconn( ld );
417
418 if ( rc == 0 ) {
419 ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb,
420 LBER_SB_OPT_GET_FD, &sd );
421
422 if( sd == AC_SOCKET_INVALID ) {
423 ld->ld_errno = LDAP_LOCAL_ERROR;
424 rc = ld->ld_errno;
425 }
426 }
427 }
428 if ( rc == 0 && ld->ld_defconn &&
429 ld->ld_defconn->lconn_status == LDAP_CONNST_CONNECTING ) {
430 rc = ldap_int_check_async_open( ld, sd );
431 }
432 LDAP_MUTEX_UNLOCK( &ld->ld_conn_mutex );
433 if( rc != 0 ) return ld->ld_errno;
434
435 oldctx = ld->ld_defconn->lconn_sasl_authctx;
436
437 /* If we already have an authentication context, clear it out */
438 if( oldctx ) {
439 if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
440 sasl_dispose( &oldctx );
441 }
442 ld->ld_defconn->lconn_sasl_authctx = NULL;
443 }
444
445 {
446 char *saslhost;
447 int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options,
448 LDAP_BOOL_SASL_NOCANON );
449
450 /* If we don't need to canonicalize just use the host
451 * from the LDAP URI.
452 * Always use the result of gethostname() for LDAPI.
453 * Skip for Windows which doesn't support LDAPI.
454 */
455 #if !defined(_WIN32)
456 if (ld->ld_defconn->lconn_server->lud_scheme != NULL &&
457 strcmp("ldapi", ld->ld_defconn->lconn_server->lud_scheme) == 0) {
458 rc = gethostname(my_hostname, HOST_NAME_MAX + 1);
459 if (rc == 0) {
460 saslhost = my_hostname;
461 } else {
462 saslhost = "localhost";
463 }
464 } else
465 #endif
466 if ( nocanon )
467 saslhost = ld->ld_defconn->lconn_server->lud_host;
468 else {
469 saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb,
470 "localhost" );
471 free_saslhost = 1;
472 }
473 rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost );
474 if ( free_saslhost )
475 LDAP_FREE( saslhost );
476 }
477
478 if ( rc != LDAP_SUCCESS ) return rc;
479
480 ctx = ld->ld_defconn->lconn_sasl_authctx;
481
482 #ifdef HAVE_TLS
483 /* Check for TLS */
484 ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
485 if ( ssl ) {
486 struct berval authid = BER_BVNULL;
487 ber_len_t fac;
488
489 fac = ldap_pvt_tls_get_strength( ssl );
490 /* failure is OK, we just can't use SASL EXTERNAL */
491 (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
492
493 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
494 LDAP_FREE( authid.bv_val );
495 }
496 #endif
497
498 #if !defined(_WIN32)
499 /* Check for local */
500 if ( ldap_pvt_url_scheme2proto(
501 ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC )
502 {
503 char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295,"
504 "cn=peercred,cn=external,cn=auth")];
505 sprintf( authid, "gidNumber=%u+uidNumber=%u,"
506 "cn=peercred,cn=external,cn=auth",
507 getegid(), geteuid() );
508 (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
509 LDAP_PVT_SASL_LOCAL_SSF );
510 }
511 #endif
512
513 /* (re)set security properties */
514 sasl_setprop( ctx, SASL_SEC_PROPS,
515 &ld->ld_options.ldo_sasl_secprops );
516
517 mech = NULL;
518
519 do {
520 saslrc = sasl_client_start( ctx,
521 mechs,
522 #if SASL_VERSION_MAJOR < 2
523 NULL,
524 #endif
525 &prompts,
526 (SASL_CONST char **)&ccred.bv_val,
527 &credlen,
528 &mech );
529
530 if( pmech == NULL && mech != NULL ) {
531 pmech = mech;
532 *rmech = mech;
533
534 if( flags != LDAP_SASL_QUIET ) {
535 fprintf(stderr,
536 "SASL/%s authentication started\n",
537 pmech );
538 }
539 }
540
541 if( saslrc == SASL_INTERACT ) {
542 int res;
543 if( !interact ) break;
544 res = (interact)( ld, flags, defaults, prompts );
545
546 if( res != LDAP_SUCCESS ) break;
547 }
548 } while ( saslrc == SASL_INTERACT );
549 rc = LDAP_SASL_BIND_IN_PROGRESS;
550
551 } else {
552 /* continuing an in-progress Bind */
553 struct berval *scred = NULL;
554
555 ctx = ld->ld_defconn->lconn_sasl_authctx;
556
557 rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 );
558 if ( rc != LDAP_SUCCESS ) {
559 if ( scred )
560 ber_bvfree( scred );
561 goto done;
562 }
563
564 rc = ldap_result2error( ld, result, 0 );
565 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
566 if( scred ) {
567 /* and server provided us with data? */
568 Debug( LDAP_DEBUG_TRACE,
569 "ldap_int_sasl_bind: rc=%d len=%ld\n",
570 rc, scred ? (long) scred->bv_len : -1L, 0 );
571 ber_bvfree( scred );
572 scred = NULL;
573 }
574 goto done;
575 }
576
577 mech = *rmech;
578 if ( rc == LDAP_SUCCESS && mech == NULL ) {
579 if ( scred )
580 ber_bvfree( scred );
581 goto success;
582 }
583
584 do {
585 if( ! scred ) {
586 /* no data! */
587 Debug( LDAP_DEBUG_TRACE,
588 "ldap_int_sasl_bind: no data in step!\n",
589 0, 0, 0 );
590 }
591
592 saslrc = sasl_client_step( ctx,
593 (scred == NULL) ? NULL : scred->bv_val,
594 (scred == NULL) ? 0 : scred->bv_len,
595 &prompts,
596 (SASL_CONST char **)&ccred.bv_val,
597 &credlen );
598
599 Debug( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n",
600 saslrc, 0, 0 );
601
602 if( saslrc == SASL_INTERACT ) {
603 int res;
604 if( !interact ) break;
605 res = (interact)( ld, flags, defaults, prompts );
606 if( res != LDAP_SUCCESS ) break;
607 }
608 } while ( saslrc == SASL_INTERACT );
609
610 ber_bvfree( scred );
611 }
612
613 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
614 rc = ld->ld_errno = sasl_err2ldap( saslrc );
615 #if SASL_VERSION_MAJOR >= 2
616 if ( ld->ld_error ) {
617 LDAP_FREE( ld->ld_error );
618 }
619 ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) );
620 #endif
621 goto done;
622 }
623
624 if ( saslrc == SASL_OK )
625 *rmech = NULL;
626
627 ccred.bv_len = credlen;
628
629 if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) {
630 rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid );
631
632 if ( ccred.bv_val != NULL ) {
633 #if SASL_VERSION_MAJOR < 2
634 LDAP_FREE( ccred.bv_val );
635 #endif
636 ccred.bv_val = NULL;
637 }
638 if ( rc == LDAP_SUCCESS )
639 rc = LDAP_SASL_BIND_IN_PROGRESS;
640 goto done;
641 }
642
643 success:
644 /* Conversation was completed successfully by now */
645 if( flags != LDAP_SASL_QUIET ) {
646 char *data;
647 saslrc = sasl_getprop( ctx, SASL_USERNAME,
648 (SASL_CONST void **)(char *) &data );
649 if( saslrc == SASL_OK && data && *data ) {
650 fprintf( stderr, "SASL username: %s\n", data );
651 }
652
653 #if SASL_VERSION_MAJOR < 2
654 saslrc = sasl_getprop( ctx, SASL_REALM,
655 (SASL_CONST void **) &data );
656 if( saslrc == SASL_OK && data && *data ) {
657 fprintf( stderr, "SASL realm: %s\n", data );
658 }
659 #endif
660 }
661
662 ssf = NULL;
663 saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf );
664 if( saslrc == SASL_OK ) {
665 if( flags != LDAP_SASL_QUIET ) {
666 fprintf( stderr, "SASL SSF: %lu\n",
667 (unsigned long) *ssf );
668 }
669
670 if( ssf && *ssf ) {
671 if ( ld->ld_defconn->lconn_sasl_sockctx ) {
672 sasl_conn_t *oldctx = ld->ld_defconn->lconn_sasl_sockctx;
673 sasl_dispose( &oldctx );
674 ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb );
675 }
676 ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx );
677 ld->ld_defconn->lconn_sasl_sockctx = ctx;
678
679 if( flags != LDAP_SASL_QUIET ) {
680 fprintf( stderr, "SASL data security layer installed.\n" );
681 }
682 }
683 }
684 ld->ld_defconn->lconn_sasl_authctx = ctx;
685
686 done:
687 return rc;
688 }
689
690 int
ldap_int_sasl_external(LDAP * ld,LDAPConn * conn,const char * authid,ber_len_t ssf)691 ldap_int_sasl_external(
692 LDAP *ld,
693 LDAPConn *conn,
694 const char * authid,
695 ber_len_t ssf )
696 {
697 int sc;
698 sasl_conn_t *ctx;
699 #if SASL_VERSION_MAJOR < 2
700 sasl_external_properties_t extprops;
701 #else
702 sasl_ssf_t sasl_ssf = ssf;
703 #endif
704
705 ctx = conn->lconn_sasl_authctx;
706
707 if ( ctx == NULL ) {
708 return LDAP_LOCAL_ERROR;
709 }
710
711 #if SASL_VERSION_MAJOR >= 2
712 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf );
713 if ( sc == SASL_OK )
714 sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid );
715 #else
716 memset( &extprops, '\0', sizeof(extprops) );
717 extprops.ssf = ssf;
718 extprops.auth_id = (char *) authid;
719
720 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
721 (void *) &extprops );
722 #endif
723
724 if ( sc != SASL_OK ) {
725 return LDAP_LOCAL_ERROR;
726 }
727
728 return LDAP_SUCCESS;
729 }
730
731
732 #define GOT_MINSSF 1
733 #define GOT_MAXSSF 2
734 #define GOT_MAXBUF 4
735
736 static struct {
737 struct berval key;
738 int sflag;
739 int ival;
740 int idef;
741 } sprops[] = {
742 { BER_BVC("none"), 0, 0, 0 },
743 { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 },
744 { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 },
745 { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 },
746 { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 },
747 { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 },
748 { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 },
749 { BER_BVC("minssf="), 0, GOT_MINSSF, 0 },
750 { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX },
751 { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 },
752 { BER_BVNULL, 0, 0, 0 }
753 };
754
ldap_pvt_sasl_secprops_unparse(sasl_security_properties_t * secprops,struct berval * out)755 void ldap_pvt_sasl_secprops_unparse(
756 sasl_security_properties_t *secprops,
757 struct berval *out )
758 {
759 int i, l = 0;
760 int comma;
761 char *ptr;
762
763 if ( secprops == NULL || out == NULL ) {
764 return;
765 }
766
767 comma = 0;
768 for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
769 if ( sprops[i].ival ) {
770 int v = 0;
771
772 switch( sprops[i].ival ) {
773 case GOT_MINSSF: v = secprops->min_ssf; break;
774 case GOT_MAXSSF: v = secprops->max_ssf; break;
775 case GOT_MAXBUF: v = secprops->maxbufsize; break;
776 }
777 /* It is the default, ignore it */
778 if ( v == sprops[i].idef ) continue;
779
780 l += sprops[i].key.bv_len + 24;
781 } else if ( sprops[i].sflag ) {
782 if ( sprops[i].sflag & secprops->security_flags ) {
783 l += sprops[i].key.bv_len;
784 }
785 } else if ( secprops->security_flags == 0 ) {
786 l += sprops[i].key.bv_len;
787 }
788 if ( comma ) l++;
789 comma = 1;
790 }
791 l++;
792
793 out->bv_val = LDAP_MALLOC( l );
794 if ( out->bv_val == NULL ) {
795 out->bv_len = 0;
796 return;
797 }
798
799 ptr = out->bv_val;
800 comma = 0;
801 for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) {
802 if ( sprops[i].ival ) {
803 int v = 0;
804
805 switch( sprops[i].ival ) {
806 case GOT_MINSSF: v = secprops->min_ssf; break;
807 case GOT_MAXSSF: v = secprops->max_ssf; break;
808 case GOT_MAXBUF: v = secprops->maxbufsize; break;
809 }
810 /* It is the default, ignore it */
811 if ( v == sprops[i].idef ) continue;
812
813 if ( comma ) *ptr++ = ',';
814 ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v );
815 comma = 1;
816 } else if ( sprops[i].sflag ) {
817 if ( sprops[i].sflag & secprops->security_flags ) {
818 if ( comma ) *ptr++ = ',';
819 ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
820 comma = 1;
821 }
822 } else if ( secprops->security_flags == 0 ) {
823 if ( comma ) *ptr++ = ',';
824 ptr += sprintf(ptr, "%s", sprops[i].key.bv_val );
825 comma = 1;
826 }
827 }
828 out->bv_len = ptr - out->bv_val;
829 }
830
ldap_pvt_sasl_secprops(const char * in,sasl_security_properties_t * secprops)831 int ldap_pvt_sasl_secprops(
832 const char *in,
833 sasl_security_properties_t *secprops )
834 {
835 unsigned i, j, l;
836 char **props;
837 unsigned sflags = 0;
838 int got_sflags = 0;
839 sasl_ssf_t max_ssf = 0;
840 int got_max_ssf = 0;
841 sasl_ssf_t min_ssf = 0;
842 int got_min_ssf = 0;
843 unsigned maxbufsize = 0;
844 int got_maxbufsize = 0;
845
846 if( secprops == NULL ) {
847 return LDAP_PARAM_ERROR;
848 }
849 props = ldap_str2charray( in, "," );
850 if( props == NULL ) {
851 return LDAP_PARAM_ERROR;
852 }
853
854 for( i=0; props[i]; i++ ) {
855 l = strlen( props[i] );
856 for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) {
857 if ( l < sprops[j].key.bv_len ) continue;
858 if ( strncasecmp( props[i], sprops[j].key.bv_val,
859 sprops[j].key.bv_len )) continue;
860 if ( sprops[j].ival ) {
861 unsigned v;
862 char *next = NULL;
863 if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] ))
864 continue;
865 v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 );
866 if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue;
867 switch( sprops[j].ival ) {
868 case GOT_MINSSF:
869 min_ssf = v; got_min_ssf++; break;
870 case GOT_MAXSSF:
871 max_ssf = v; got_max_ssf++; break;
872 case GOT_MAXBUF:
873 maxbufsize = v; got_maxbufsize++; break;
874 }
875 } else {
876 if ( props[i][sprops[j].key.bv_len] ) continue;
877 if ( sprops[j].sflag )
878 sflags |= sprops[j].sflag;
879 else
880 sflags = 0;
881 got_sflags++;
882 }
883 break;
884 }
885 if ( BER_BVISNULL( &sprops[j].key )) {
886 ldap_charray_free( props );
887 return LDAP_NOT_SUPPORTED;
888 }
889 }
890
891 if(got_sflags) {
892 secprops->security_flags = sflags;
893 }
894 if(got_min_ssf) {
895 secprops->min_ssf = min_ssf;
896 }
897 if(got_max_ssf) {
898 secprops->max_ssf = max_ssf;
899 }
900 if(got_maxbufsize) {
901 secprops->maxbufsize = maxbufsize;
902 }
903
904 ldap_charray_free( props );
905 return LDAP_SUCCESS;
906 }
907
908 int
ldap_int_sasl_config(struct ldapoptions * lo,int option,const char * arg)909 ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg )
910 {
911 int rc;
912
913 switch( option ) {
914 case LDAP_OPT_X_SASL_SECPROPS:
915 rc = ldap_pvt_sasl_secprops( arg, &lo->ldo_sasl_secprops );
916 if( rc == LDAP_SUCCESS ) return 0;
917 }
918
919 return -1;
920 }
921
922 int
ldap_int_sasl_get_option(LDAP * ld,int option,void * arg)923 ldap_int_sasl_get_option( LDAP *ld, int option, void *arg )
924 {
925 if ( option == LDAP_OPT_X_SASL_MECHLIST ) {
926 *(char ***)arg = (char **)sasl_global_listmech();
927 return 0;
928 }
929
930 if ( ld == NULL )
931 return -1;
932
933 switch ( option ) {
934 case LDAP_OPT_X_SASL_MECH: {
935 *(char **)arg = ld->ld_options.ldo_def_sasl_mech
936 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_mech ) : NULL;
937 } break;
938 case LDAP_OPT_X_SASL_REALM: {
939 *(char **)arg = ld->ld_options.ldo_def_sasl_realm
940 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_realm ) : NULL;
941 } break;
942 case LDAP_OPT_X_SASL_AUTHCID: {
943 *(char **)arg = ld->ld_options.ldo_def_sasl_authcid
944 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authcid ) : NULL;
945 } break;
946 case LDAP_OPT_X_SASL_AUTHZID: {
947 *(char **)arg = ld->ld_options.ldo_def_sasl_authzid
948 ? LDAP_STRDUP( ld->ld_options.ldo_def_sasl_authzid ) : NULL;
949 } break;
950
951 case LDAP_OPT_X_SASL_SSF: {
952 int sc;
953 sasl_ssf_t *ssf;
954 sasl_conn_t *ctx;
955
956 if( ld->ld_defconn == NULL ) {
957 return -1;
958 }
959
960 ctx = ld->ld_defconn->lconn_sasl_sockctx;
961
962 if ( ctx == NULL ) {
963 return -1;
964 }
965
966 sc = sasl_getprop( ctx, SASL_SSF,
967 (SASL_CONST void **)(char *) &ssf );
968
969 if ( sc != SASL_OK ) {
970 return -1;
971 }
972
973 *(ber_len_t *)arg = *ssf;
974 } break;
975
976 case LDAP_OPT_X_SASL_SSF_EXTERNAL:
977 /* this option is write only */
978 return -1;
979
980 case LDAP_OPT_X_SASL_SSF_MIN:
981 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.min_ssf;
982 break;
983 case LDAP_OPT_X_SASL_SSF_MAX:
984 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.max_ssf;
985 break;
986 case LDAP_OPT_X_SASL_MAXBUFSIZE:
987 *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize;
988 break;
989 case LDAP_OPT_X_SASL_NOCANON:
990 *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
991 break;
992
993 case LDAP_OPT_X_SASL_USERNAME: {
994 int sc;
995 char *username;
996 sasl_conn_t *ctx;
997
998 if( ld->ld_defconn == NULL ) {
999 return -1;
1000 }
1001
1002 ctx = ld->ld_defconn->lconn_sasl_authctx;
1003
1004 if ( ctx == NULL ) {
1005 return -1;
1006 }
1007
1008 sc = sasl_getprop( ctx, SASL_USERNAME,
1009 (SASL_CONST void **)(char **) &username );
1010
1011 if ( sc != SASL_OK ) {
1012 return -1;
1013 }
1014
1015 *(char **)arg = username ? LDAP_STRDUP( username ) : NULL;
1016 } break;
1017
1018 case LDAP_OPT_X_SASL_SECPROPS:
1019 /* this option is write only */
1020 return -1;
1021
1022 #ifdef SASL_GSS_CREDS
1023 case LDAP_OPT_X_SASL_GSS_CREDS: {
1024 sasl_conn_t *ctx;
1025 int sc;
1026
1027 if ( ld->ld_defconn == NULL )
1028 return -1;
1029
1030 ctx = ld->ld_defconn->lconn_sasl_authctx;
1031 if ( ctx == NULL )
1032 return -1;
1033
1034 sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg );
1035 if ( sc != SASL_OK )
1036 return -1;
1037 }
1038 break;
1039 #endif
1040
1041 default:
1042 return -1;
1043 }
1044 return 0;
1045 }
1046
1047 int
ldap_int_sasl_set_option(LDAP * ld,int option,void * arg)1048 ldap_int_sasl_set_option( LDAP *ld, int option, void *arg )
1049 {
1050 if ( ld == NULL )
1051 return -1;
1052
1053 if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON )
1054 return -1;
1055
1056 switch ( option ) {
1057 case LDAP_OPT_X_SASL_SSF:
1058 case LDAP_OPT_X_SASL_USERNAME:
1059 /* This option is read-only */
1060 return -1;
1061
1062 case LDAP_OPT_X_SASL_SSF_EXTERNAL: {
1063 int sc;
1064 #if SASL_VERSION_MAJOR < 2
1065 sasl_external_properties_t extprops;
1066 #else
1067 sasl_ssf_t sasl_ssf;
1068 #endif
1069 sasl_conn_t *ctx;
1070
1071 if( ld->ld_defconn == NULL ) {
1072 return -1;
1073 }
1074
1075 ctx = ld->ld_defconn->lconn_sasl_authctx;
1076
1077 if ( ctx == NULL ) {
1078 return -1;
1079 }
1080
1081 #if SASL_VERSION_MAJOR >= 2
1082 sasl_ssf = * (ber_len_t *)arg;
1083 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf);
1084 #else
1085 memset(&extprops, 0L, sizeof(extprops));
1086
1087 extprops.ssf = * (ber_len_t *) arg;
1088
1089 sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL,
1090 (void *) &extprops );
1091 #endif
1092
1093 if ( sc != SASL_OK ) {
1094 return -1;
1095 }
1096 } break;
1097
1098 case LDAP_OPT_X_SASL_SSF_MIN:
1099 ld->ld_options.ldo_sasl_secprops.min_ssf = *(ber_len_t *)arg;
1100 break;
1101 case LDAP_OPT_X_SASL_SSF_MAX:
1102 ld->ld_options.ldo_sasl_secprops.max_ssf = *(ber_len_t *)arg;
1103 break;
1104 case LDAP_OPT_X_SASL_MAXBUFSIZE:
1105 ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg;
1106 break;
1107 case LDAP_OPT_X_SASL_NOCANON:
1108 if ( arg == LDAP_OPT_OFF ) {
1109 LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
1110 } else {
1111 LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON );
1112 }
1113 break;
1114
1115 case LDAP_OPT_X_SASL_SECPROPS: {
1116 int sc;
1117 sc = ldap_pvt_sasl_secprops( (char *) arg,
1118 &ld->ld_options.ldo_sasl_secprops );
1119
1120 return sc == LDAP_SUCCESS ? 0 : -1;
1121 }
1122
1123 #ifdef SASL_GSS_CREDS
1124 case LDAP_OPT_X_SASL_GSS_CREDS: {
1125 sasl_conn_t *ctx;
1126 int sc;
1127
1128 if ( ld->ld_defconn == NULL )
1129 return -1;
1130
1131 ctx = ld->ld_defconn->lconn_sasl_authctx;
1132 if ( ctx == NULL )
1133 return -1;
1134
1135 sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg );
1136 if ( sc != SASL_OK )
1137 return -1;
1138 }
1139 break;
1140 #endif
1141
1142 default:
1143 return -1;
1144 }
1145 return 0;
1146 }
1147
1148 #ifdef LDAP_R_COMPILE
1149 #define LDAP_DEBUG_R_SASL
ldap_pvt_sasl_mutex_new(void)1150 void *ldap_pvt_sasl_mutex_new(void)
1151 {
1152 ldap_pvt_thread_mutex_t *mutex;
1153
1154 mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1,
1155 sizeof(ldap_pvt_thread_mutex_t) );
1156
1157 if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) {
1158 return mutex;
1159 }
1160 LDAP_FREE( mutex );
1161 #ifndef LDAP_DEBUG_R_SASL
1162 assert( 0 );
1163 #endif /* !LDAP_DEBUG_R_SASL */
1164 return NULL;
1165 }
1166
ldap_pvt_sasl_mutex_lock(void * mutex)1167 int ldap_pvt_sasl_mutex_lock(void *mutex)
1168 {
1169 #ifdef LDAP_DEBUG_R_SASL
1170 if ( mutex == NULL ) {
1171 return SASL_OK;
1172 }
1173 #else /* !LDAP_DEBUG_R_SASL */
1174 assert( mutex != NULL );
1175 #endif /* !LDAP_DEBUG_R_SASL */
1176 return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex )
1177 ? SASL_FAIL : SASL_OK;
1178 }
1179
ldap_pvt_sasl_mutex_unlock(void * mutex)1180 int ldap_pvt_sasl_mutex_unlock(void *mutex)
1181 {
1182 #ifdef LDAP_DEBUG_R_SASL
1183 if ( mutex == NULL ) {
1184 return SASL_OK;
1185 }
1186 #else /* !LDAP_DEBUG_R_SASL */
1187 assert( mutex != NULL );
1188 #endif /* !LDAP_DEBUG_R_SASL */
1189 return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex )
1190 ? SASL_FAIL : SASL_OK;
1191 }
1192
ldap_pvt_sasl_mutex_dispose(void * mutex)1193 void ldap_pvt_sasl_mutex_dispose(void *mutex)
1194 {
1195 #ifdef LDAP_DEBUG_R_SASL
1196 if ( mutex == NULL ) {
1197 return;
1198 }
1199 #else /* !LDAP_DEBUG_R_SASL */
1200 assert( mutex != NULL );
1201 #endif /* !LDAP_DEBUG_R_SASL */
1202 (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex );
1203 LDAP_FREE( mutex );
1204 }
1205 #endif
1206
1207 #else
ldap_int_sasl_init(void)1208 int ldap_int_sasl_init( void )
1209 { return LDAP_SUCCESS; }
1210
ldap_int_sasl_close(LDAP * ld,LDAPConn * lc)1211 int ldap_int_sasl_close( LDAP *ld, LDAPConn *lc )
1212 { return LDAP_SUCCESS; }
1213
1214 int
ldap_int_sasl_bind(LDAP * ld,const char * dn,const char * mechs,LDAPControl ** sctrls,LDAPControl ** cctrls,unsigned flags,LDAP_SASL_INTERACT_PROC * interact,void * defaults,LDAPMessage * result,const char ** rmech,int * msgid)1215 ldap_int_sasl_bind(
1216 LDAP *ld,
1217 const char *dn,
1218 const char *mechs,
1219 LDAPControl **sctrls,
1220 LDAPControl **cctrls,
1221 unsigned flags,
1222 LDAP_SASL_INTERACT_PROC *interact,
1223 void *defaults,
1224 LDAPMessage *result,
1225 const char **rmech,
1226 int *msgid )
1227 { return LDAP_NOT_SUPPORTED; }
1228
1229 int
ldap_int_sasl_external(LDAP * ld,LDAPConn * conn,const char * authid,ber_len_t ssf)1230 ldap_int_sasl_external(
1231 LDAP *ld,
1232 LDAPConn *conn,
1233 const char * authid,
1234 ber_len_t ssf )
1235 { return LDAP_SUCCESS; }
1236
1237 #endif /* HAVE_CYRUS_SASL */
1238