8322 nl: misleading-indentation
[unleashed/tickless.git] / usr / src / lib / libldap5 / sources / ldap / common / sasl.c
blob3f05b2dbcac4619cf9afe408f05ef63ae5b6c110
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #ifdef LDAP_SASLIO_HOOKS
27 #include <assert.h>
28 #include "ldap-int.h"
29 #include "../ber/lber-int.h"
30 #include <sasl/sasl.h>
31 #include <thread.h>
32 #include <synch.h>
34 #define SEARCH_TIMEOUT_SECS 120
35 #define NSLDAPI_SM_BUF 128
37 extern void *sasl_create_context(void);
38 extern void sasl_free_context(void *ctx);
39 extern int _sasl_client_init(void *ctx, const sasl_callback_t *callbacks);
40 extern int _sasl_client_new(void *ctx, const char *service,
41 const char *serverFQDN, const char *iplocalport,
42 const char *ipremoteport,
43 const sasl_callback_t *prompt_supp,
44 unsigned flags, sasl_conn_t **pconn);
45 extern int _sasl_server_init(void *ctx,
46 const sasl_callback_t *callbacks, const char *appname);
47 extern int _sasl_server_new(void *ctx, const char *service,
48 const char *serverFQDN, const char *user_realm,
49 const char *iplocalport, const char *ipremoteport,
50 const sasl_callback_t *callbacks,
51 unsigned flags, sasl_conn_t **pconn);
53 static int nsldapi_sasl_close( LDAP *ld, Sockbuf *sb );
54 static void destroy_sasliobuf(Sockbuf *sb);
57 * SASL Dependent routines
59 * SASL security and integrity options are supported through the
60 * use of the extended I/O functionality. Because the extended
61 * I/O functions may already be in use prior to enabling encryption,
62 * when SASL encryption is enabled, these routine interpose themselves
63 * over the existng extended I/O routines and add an additional level
64 * of indirection.
65 * IE: Before SASL: client->libldap->lber->extio
66 * After SASL: client->libldap->lber->saslio->extio
67 * Any extio functions are still used for the raw i/O [IE prldap]
68 * but SASL will decrypt before passing to lber.
69 * SASL cannot decrypt a stream so full packets must be read
70 * before proceeding.
73 static int nsldapi_sasl_fail()
75 return( SASL_FAIL );
79 * Global SASL Init data
82 static sasl_callback_t client_callbacks[] = {
83 { SASL_CB_GETOPT, nsldapi_sasl_fail, NULL },
84 { SASL_CB_GETREALM, NULL, NULL },
85 { SASL_CB_USER, NULL, NULL },
86 { SASL_CB_AUTHNAME, NULL, NULL },
87 { SASL_CB_PASS, NULL, NULL },
88 { SASL_CB_ECHOPROMPT, NULL, NULL },
89 { SASL_CB_NOECHOPROMPT, NULL, NULL },
90 { SASL_CB_LIST_END, NULL, NULL }
92 static mutex_t sasl_mutex = DEFAULTMUTEX;
93 static int nsldapi_sasl_inited = 0;
94 static void *gctx; /* intentially not freed - avoid libsasl re-inits */
96 int nsldapi_sasl_init( void )
98 int saslrc;
100 mutex_lock(&sasl_mutex);
101 if ( nsldapi_sasl_inited ) {
102 mutex_unlock(&sasl_mutex);
103 return( 0 );
105 if ((gctx = (void *)sasl_create_context()) != NULL) {
106 saslrc = _sasl_client_init(gctx, client_callbacks);
107 if (saslrc == SASL_OK ) {
108 nsldapi_sasl_inited = 1;
109 mutex_unlock(&sasl_mutex);
110 return( 0 );
113 mutex_unlock(&sasl_mutex);
114 return( -1 );
118 * SASL encryption routines
122 * Get the 4 octet header [size] for a sasl encrypted buffer.
123 * See RFC222 [section 3].
126 static int
127 nsldapi_sasl_pktlen( char *buf, int maxbufsize )
129 int size;
131 #if defined( _WINDOWS ) || defined( _WIN32 )
132 size = ntohl(*(long *)buf);
133 #else
134 size = ntohl(*(uint32_t *)buf);
135 #endif
137 if ( size < 0 || size > maxbufsize ) {
138 return (-1 );
141 return( size + 4 ); /* include the first 4 bytes */
144 static int
145 nsldapi_sasl_read( int s, void *buf, int len,
146 struct lextiof_socket_private *arg)
148 Sockbuf *sb = (Sockbuf *)arg;
149 LDAP *ld;
150 const char *dbuf;
151 char *cp;
152 int ret;
153 unsigned dlen, blen;
155 if (sb == NULL) {
156 return( -1 );
159 ld = (LDAP *)sb->sb_sasl_prld;
160 if (ld == NULL) {
161 return( -1 );
164 /* Is there anything left in the existing buffer? */
165 if ((ret = sb->sb_sasl_ilen) > 0) {
166 ret = (ret > len ? len : ret);
167 SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret );
168 if (ret == sb->sb_sasl_ilen) {
169 sb->sb_sasl_ilen = 0;
170 sb->sb_sasl_iptr = NULL;
171 } else {
172 sb->sb_sasl_ilen -= ret;
173 sb->sb_sasl_iptr += ret;
175 return( ret );
178 /* buffer is empty - fill it */
179 cp = sb->sb_sasl_ibuf;
180 dlen = 0;
182 /* Read the length of the packet */
183 while ( dlen < 4 ) {
184 if (sb->sb_sasl_fns.lbextiofn_read != NULL) {
185 ret = sb->sb_sasl_fns.lbextiofn_read(
186 s, cp, 4 - dlen,
187 sb->sb_sasl_fns.lbextiofn_socket_arg);
188 } else {
189 ret = read( sb->sb_sd, cp, 4 - dlen );
191 #ifdef EINTR
192 if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
193 continue;
194 #endif
195 if ( ret <= 0 )
196 return( ret );
198 cp += ret;
199 dlen += ret;
202 blen = 4;
204 ret = nsldapi_sasl_pktlen( sb->sb_sasl_ibuf, sb->sb_sasl_bfsz );
205 if (ret < 0) {
206 LDAP_SET_ERRNO(ld, EIO);
207 return( -1 );
209 dlen = ret - dlen;
211 /* read the rest of the encrypted packet */
212 while ( dlen > 0 ) {
213 if (sb->sb_sasl_fns.lbextiofn_read != NULL) {
214 ret = sb->sb_sasl_fns.lbextiofn_read(
215 s, cp, dlen,
216 sb->sb_sasl_fns.lbextiofn_socket_arg);
217 } else {
218 ret = read( sb->sb_sd, cp, dlen );
221 #ifdef EINTR
222 if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
223 continue;
224 #endif
225 if ( ret <= 0 )
226 return( ret );
228 cp += ret;
229 blen += ret;
230 dlen -= ret;
233 /* Decode the packet */
234 ret = sasl_decode( sb->sb_sasl_ctx,
235 sb->sb_sasl_ibuf, blen,
236 &dbuf, &dlen);
237 if ( ret != SASL_OK ) {
238 /* sb_sasl_read: failed to decode packet, drop it, error */
239 sb->sb_sasl_iptr = NULL;
240 sb->sb_sasl_ilen = 0;
241 LDAP_SET_ERRNO(ld, EIO);
242 return( -1 );
245 /* copy decrypted packet to the input buffer */
246 SAFEMEMCPY( sb->sb_sasl_ibuf, dbuf, dlen );
247 sb->sb_sasl_iptr = sb->sb_sasl_ibuf;
248 sb->sb_sasl_ilen = dlen;
250 ret = (dlen > (unsigned) len ? len : dlen);
251 SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret );
252 if (ret == sb->sb_sasl_ilen) {
253 sb->sb_sasl_ilen = 0;
254 sb->sb_sasl_iptr = NULL;
255 } else {
256 sb->sb_sasl_ilen -= ret;
257 sb->sb_sasl_iptr += ret;
259 return( ret );
262 static int
263 nsldapi_sasl_write( int s, const void *buf, int len,
264 struct lextiof_socket_private *arg)
266 Sockbuf *sb = (Sockbuf *)arg;
267 int ret = 0;
268 const char *obuf, *optr, *cbuf = (const char *)buf;
269 unsigned olen, clen, tlen = 0;
270 unsigned *maxbuf;
272 if (sb == NULL) {
273 return( -1 );
276 ret = sasl_getprop(sb->sb_sasl_ctx, SASL_MAXOUTBUF,
277 (const void **)&maxbuf);
278 if ( ret != SASL_OK ) {
279 /* just a sanity check, should never happen */
280 return( -1 );
283 while (len > 0) {
284 clen = (len > *maxbuf) ? *maxbuf : len;
285 /* encode the next packet. */
286 ret = sasl_encode( sb->sb_sasl_ctx, cbuf, clen, &obuf, &olen);
287 if ( ret != SASL_OK ) {
288 /* XXX Log error? "sb_sasl_write: failed to encode packet..." */
289 return( -1 );
291 /* Write everything now, buffer is only good until next sasl_encode */
292 optr = obuf;
293 while (olen > 0) {
294 if (sb->sb_sasl_fns.lbextiofn_write != NULL) {
295 ret = sb->sb_sasl_fns.lbextiofn_write(
296 s, optr, olen,
297 sb->sb_sasl_fns.lbextiofn_socket_arg);
298 } else {
299 ret = write( sb->sb_sd, optr, olen);
301 if ( ret < 0 )
302 return( ret );
303 optr += ret;
304 olen -= ret;
306 len -= clen;
307 cbuf += clen;
308 tlen += clen;
310 return( tlen );
313 static int
314 nsldapi_sasl_poll(
315 LDAP_X_PollFD fds[], int nfds, int timeout,
316 struct lextiof_session_private *arg )
318 Sockbuf *sb = (Sockbuf *)arg;
319 LDAP *ld;
320 int i;
322 if (sb == NULL) {
323 return( -1 );
325 ld = (LDAP *)sb->sb_sasl_prld;
326 if (ld == NULL) {
327 return( -1 );
330 if (fds && nfds > 0) {
331 for(i = 0; i < nfds; i++) {
332 if (fds[i].lpoll_socketarg ==
333 (struct lextiof_socket_private *)sb) {
334 fds[i].lpoll_socketarg =
335 (struct lextiof_socket_private *)
336 sb->sb_sasl_fns.lbextiofn_socket_arg;
341 return ( ld->ld_sasl_io_fns.lextiof_poll( fds, nfds, timeout,
342 (void *)ld->ld_sasl_io_fns.lextiof_session_arg) );
345 /* no encryption indirect routines */
347 static int
348 nsldapi_sasl_ne_read( int s, void *buf, int len,
349 struct lextiof_socket_private *arg)
351 Sockbuf *sb = (Sockbuf *)arg;
353 if (sb == NULL) {
354 return( -1 );
357 return( sb->sb_sasl_fns.lbextiofn_read( s, buf, len,
358 sb->sb_sasl_fns.lbextiofn_socket_arg) );
361 static int
362 nsldapi_sasl_ne_write( int s, const void *buf, int len,
363 struct lextiof_socket_private *arg)
365 Sockbuf *sb = (Sockbuf *)arg;
367 if (sb == NULL) {
368 return( -1 );
371 return( sb->sb_sasl_fns.lbextiofn_write( s, buf, len,
372 sb->sb_sasl_fns.lbextiofn_socket_arg) );
375 static int
376 nsldapi_sasl_close_socket(int s, struct lextiof_socket_private *arg )
378 Sockbuf *sb = (Sockbuf *)arg;
379 LDAP *ld;
381 if (sb == NULL) {
382 return( -1 );
384 ld = (LDAP *)sb->sb_sasl_prld;
385 if (ld == NULL) {
386 return( -1 );
388 /* undo function pointer interposing */
389 ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &ld->ld_sasl_io_fns );
390 ber_sockbuf_set_option( sb,
391 LBER_SOCKBUF_OPT_EXT_IO_FNS,
392 (void *)&sb->sb_sasl_fns);
394 /* undo SASL */
395 nsldapi_sasl_close( ld, sb );
397 return ( ld->ld_sasl_io_fns.lextiof_close( s,
398 (struct lextiof_socket_private *)
399 sb->sb_sasl_fns.lbextiofn_socket_arg ) );
403 * install encryption routines if security has been negotiated
405 static int
406 nsldapi_sasl_install( LDAP *ld, Sockbuf *sb, void *ctx_arg, sasl_ssf_t *ssf)
408 struct lber_x_ext_io_fns fns;
409 struct ldap_x_ext_io_fns iofns;
410 sasl_security_properties_t *secprops;
411 int rc, value;
412 int bufsiz;
413 int encrypt = 0;
415 if (ssf && *ssf) {
416 encrypt = 1;
418 if ( sb == NULL ) {
419 return( LDAP_LOCAL_ERROR );
421 rc = ber_sockbuf_get_option( sb,
422 LBER_SOCKBUF_OPT_TO_FILE_ONLY,
423 (void *) &value);
424 if (rc != 0 || value != 0)
425 return( LDAP_LOCAL_ERROR );
427 if (encrypt) {
428 /* initialize input buffer - use MAX SIZE to avoid reallocs */
429 sb->sb_sasl_ctx = (sasl_conn_t *)ctx_arg;
430 rc = sasl_getprop( sb->sb_sasl_ctx, SASL_SEC_PROPS,
431 (const void **)&secprops );
432 if (rc != SASL_OK)
433 return( LDAP_LOCAL_ERROR );
434 bufsiz = secprops->maxbufsize;
435 if (bufsiz <= 0) {
436 return( LDAP_LOCAL_ERROR );
438 if ((sb->sb_sasl_ibuf = NSLDAPI_MALLOC(bufsiz)) == NULL) {
439 return( LDAP_LOCAL_ERROR );
441 sb->sb_sasl_iptr = NULL;
442 sb->sb_sasl_bfsz = bufsiz;
443 sb->sb_sasl_ilen = 0;
446 /* Reset Session then Socket Args */
447 /* Get old values */
448 (void) memset( &sb->sb_sasl_fns, 0, LBER_X_EXTIO_FNS_SIZE);
449 sb->sb_sasl_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
450 rc = ber_sockbuf_get_option( sb,
451 LBER_SOCKBUF_OPT_EXT_IO_FNS,
452 (void *)&sb->sb_sasl_fns);
453 if (rc != 0) {
454 destroy_sasliobuf(sb);
455 return( LDAP_LOCAL_ERROR );
457 memset( &ld->ld_sasl_io_fns, 0, sizeof(iofns));
458 ld->ld_sasl_io_fns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
459 rc = ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
460 &ld->ld_sasl_io_fns );
461 if (rc != 0 ) {
462 destroy_sasliobuf(sb);
463 return( LDAP_LOCAL_ERROR );
466 /* Set new values */
467 if ( ld->ld_sasl_io_fns.lextiof_read != NULL ||
468 ld->ld_sasl_io_fns.lextiof_write != NULL ||
469 ld->ld_sasl_io_fns.lextiof_poll != NULL ||
470 ld->ld_sasl_io_fns.lextiof_connect != NULL ||
471 ld->ld_sasl_io_fns.lextiof_close != NULL ) {
472 memset( &iofns, 0, sizeof(iofns));
473 iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
474 if (encrypt) {
475 iofns.lextiof_read = nsldapi_sasl_read;
476 iofns.lextiof_write = nsldapi_sasl_write;
477 iofns.lextiof_poll = nsldapi_sasl_poll;
478 } else {
479 iofns.lextiof_read = nsldapi_sasl_ne_read;
480 iofns.lextiof_write = nsldapi_sasl_ne_write;
481 iofns.lextiof_poll = nsldapi_sasl_poll;
483 iofns.lextiof_connect = ld->ld_sasl_io_fns.lextiof_connect;
484 iofns.lextiof_close = nsldapi_sasl_close_socket;
485 iofns.lextiof_newhandle = ld->ld_sasl_io_fns.lextiof_newhandle;
486 iofns.lextiof_disposehandle =
487 ld->ld_sasl_io_fns.lextiof_disposehandle;
488 iofns.lextiof_session_arg =
489 (void *) sb;
490 /* ld->ld_sasl_io_fns.lextiof_session_arg; */
491 rc = ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
492 &iofns );
493 if (rc != 0 ) {
494 destroy_sasliobuf(sb);
495 return( LDAP_LOCAL_ERROR );
497 sb->sb_sasl_prld = (void *)ld;
500 if (encrypt) {
501 (void) memset( &fns, 0, LBER_X_EXTIO_FNS_SIZE);
502 fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
503 fns.lbextiofn_read = nsldapi_sasl_read;
504 fns.lbextiofn_write = nsldapi_sasl_write;
505 fns.lbextiofn_socket_arg =
506 (void *) sb;
507 /* (void *)sb->sb_sasl_fns.lbextiofn_socket_arg; */
508 rc = ber_sockbuf_set_option( sb,
509 LBER_SOCKBUF_OPT_EXT_IO_FNS,
510 (void *)&fns);
511 if (rc != 0) {
512 destroy_sasliobuf(sb);
513 return( LDAP_LOCAL_ERROR );
517 return( LDAP_SUCCESS );
520 static int
521 nsldapi_sasl_cvterrno( LDAP *ld, int err, char *msg )
523 int rc = LDAP_LOCAL_ERROR;
525 switch (err) {
526 case SASL_OK:
527 rc = LDAP_SUCCESS;
528 break;
529 case SASL_NOMECH:
530 rc = LDAP_AUTH_UNKNOWN;
531 break;
532 case SASL_BADSERV:
533 rc = LDAP_CONNECT_ERROR;
534 break;
535 case SASL_DISABLED:
536 case SASL_ENCRYPT:
537 case SASL_EXPIRED:
538 case SASL_NOUSERPASS:
539 case SASL_NOVERIFY:
540 case SASL_PWLOCK:
541 case SASL_TOOWEAK:
542 case SASL_UNAVAIL:
543 case SASL_WEAKPASS:
544 rc = LDAP_INAPPROPRIATE_AUTH;
545 break;
546 case SASL_BADAUTH:
547 case SASL_NOAUTHZ:
548 rc = LDAP_INVALID_CREDENTIALS;
549 break;
550 case SASL_NOMEM:
551 rc = LDAP_NO_MEMORY;
552 break;
553 case SASL_NOUSER:
554 rc = LDAP_NO_SUCH_OBJECT;
555 break;
556 case SASL_CONTINUE:
557 case SASL_FAIL:
558 case SASL_INTERACT:
559 default:
560 rc = LDAP_LOCAL_ERROR;
561 break;
564 LDAP_SET_LDERRNO( ld, rc, NULL, msg );
565 return( rc );
569 nsldapi_sasl_open(LDAP *ld)
571 Sockbuf *sb;
572 char * host;
573 int saslrc;
574 sasl_conn_t *ctx = NULL;
576 if (ld == NULL) {
577 return( LDAP_LOCAL_ERROR );
580 if (ld->ld_defconn == NULL) {
581 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
582 return( LDAP_LOCAL_ERROR );
584 sb = ld->ld_defconn->lconn_sb;
585 host = ld->ld_defhost;
587 if ( sb == NULL || host == NULL ) {
588 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
589 return( LDAP_LOCAL_ERROR );
592 if (sb->sb_sasl_ctx) {
593 sasl_dispose(&sb->sb_sasl_ctx);
594 sb->sb_sasl_ctx = NULL;
597 /* SASL is not properly initialized */
598 mutex_lock(&sasl_mutex);
599 if (gctx == NULL) {
600 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
601 mutex_unlock(&sasl_mutex);
602 return( LDAP_LOCAL_ERROR );
605 saslrc = _sasl_client_new(gctx, "ldap", host,
606 NULL, NULL, /* iplocal ipremote strings currently unused */
607 NULL, 0, &ctx );
609 if ( saslrc != SASL_OK ) {
610 mutex_unlock(&sasl_mutex);
611 sasl_dispose(&ctx);
612 return( nsldapi_sasl_cvterrno( ld, saslrc, NULL ) );
615 sb->sb_sasl_ctx = (void *)ctx;
616 mutex_unlock(&sasl_mutex);
618 return( LDAP_SUCCESS );
621 static void
622 destroy_sasliobuf(Sockbuf *sb)
624 if (sb != NULL && sb->sb_sasl_ibuf != NULL) {
625 NSLDAPI_FREE(sb->sb_sasl_ibuf);
626 sb->sb_sasl_ibuf = NULL;
627 sb->sb_sasl_iptr = NULL;
628 sb->sb_sasl_bfsz = 0;
629 sb->sb_sasl_ilen = 0;
633 static int
634 nsldapi_sasl_close( LDAP *ld, Sockbuf *sb )
636 sasl_conn_t *ctx = (sasl_conn_t *)sb->sb_sasl_ctx;
638 destroy_sasliobuf(sb);
640 if( ctx != NULL ) {
641 sasl_dispose( &ctx );
642 sb->sb_sasl_ctx = NULL;
644 return( LDAP_SUCCESS );
647 static int
648 nsldapi_sasl_do_bind( LDAP *ld, const char *dn,
649 const char *mechs, unsigned flags,
650 LDAP_SASL_INTERACT_PROC *callback, void *defaults,
651 LDAPControl **sctrl, LDAPControl **cctrl )
653 sasl_interact_t *prompts = NULL;
654 sasl_conn_t *ctx;
655 sasl_ssf_t *ssf = NULL;
656 const char *mech = NULL;
657 int saslrc, rc;
658 struct berval ccred;
659 unsigned credlen;
660 int stepnum = 1;
661 char *sasl_username = NULL;
663 if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) {
664 LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
665 return( LDAP_NOT_SUPPORTED );
668 /* shouldn't happen */
669 if (callback == NULL) {
670 return( LDAP_LOCAL_ERROR );
673 if ( ld->ld_defconn == NULL ||
674 ld->ld_defconn->lconn_status != LDAP_CONNST_CONNECTED) {
675 rc = nsldapi_open_ldap_defconn( ld );
676 if( rc < 0 ) {
677 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
681 /* should have a valid ld connection - now create sasl connection */
682 if ((rc = nsldapi_sasl_open(ld)) != LDAP_SUCCESS) {
683 LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
684 return( rc );
687 /* expect context to be initialized when connection is open */
688 ctx = (sasl_conn_t *)ld->ld_defconn->lconn_sb->sb_sasl_ctx;
690 if( ctx == NULL ) {
691 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
692 return( LDAP_LOCAL_ERROR );
695 /* (re)set security properties */
696 sasl_setprop( ctx, SASL_SEC_PROPS, &ld->ld_sasl_secprops );
698 ccred.bv_val = NULL;
699 ccred.bv_len = 0;
701 LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n",
702 (mech ? mech : ""), 0, 0 );
704 do {
705 saslrc = sasl_client_start( ctx,
706 mechs,
707 &prompts,
708 (const char **)&ccred.bv_val,
709 &credlen,
710 &mech );
712 LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of client start for SASL/%s authentication\n",
713 stepnum, (mech ? mech : ""), 0 );
714 stepnum++;
716 if( saslrc == SASL_INTERACT &&
717 (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) {
718 break;
720 } while ( saslrc == SASL_INTERACT );
722 ccred.bv_len = credlen;
724 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
725 return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
728 stepnum = 1;
730 do {
731 struct berval *scred;
732 int clientstepnum = 1;
734 scred = NULL;
736 LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of bind for SASL/%s authentication\n",
737 stepnum, (mech ? mech : ""), 0 );
738 stepnum++;
740 /* notify server of a sasl bind step */
741 rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
742 sctrl, cctrl, &scred);
744 if ( ccred.bv_val != NULL ) {
745 ccred.bv_val = NULL;
748 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
749 ber_bvfree( scred );
750 return( rc );
753 if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
754 /* we're done, no need to step */
755 if( scred ) {
756 if (scred->bv_len == 0 ) { /* MS AD sends back empty screds */
757 LDAPDebug(LDAP_DEBUG_ANY,
758 "SASL BIND complete - ignoring empty credential response\n",
759 0, 0, 0);
760 ber_bvfree( scred );
761 } else {
762 /* but server provided us with data! */
763 LDAPDebug(LDAP_DEBUG_TRACE,
764 "SASL BIND complete but invalid because server responded with credentials - length [%u]\n",
765 scred->bv_len, 0, 0);
766 ber_bvfree( scred );
767 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR,
768 NULL, nsldapi_strdup( dgettext(TEXT_DOMAIN,
769 "Error during SASL handshake - "
770 "invalid server credential response") ));
771 return( LDAP_LOCAL_ERROR );
774 break;
777 /* perform the next step of the sasl bind */
778 do {
779 LDAPDebug(LDAP_DEBUG_TRACE, "Doing client step %d of bind step %d for SASL/%s authentication\n",
780 clientstepnum, stepnum, (mech ? mech : "") );
781 clientstepnum++;
782 saslrc = sasl_client_step( ctx,
783 (scred == NULL) ? NULL : scred->bv_val,
784 (scred == NULL) ? 0 : scred->bv_len,
785 &prompts,
786 (const char **)&ccred.bv_val,
787 &credlen );
789 if( saslrc == SASL_INTERACT &&
790 (callback)(ld, flags, defaults, prompts)
791 != LDAP_SUCCESS ) {
792 break;
794 } while ( saslrc == SASL_INTERACT );
796 ccred.bv_len = credlen;
797 ber_bvfree( scred );
799 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
800 return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
802 } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
804 if ( rc != LDAP_SUCCESS ) {
805 return( rc );
808 if ( saslrc != SASL_OK ) {
809 return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
812 saslrc = sasl_getprop( ctx, SASL_USERNAME, (const void **) &sasl_username );
813 if ( (saslrc == SASL_OK) && sasl_username ) {
814 LDAPDebug(LDAP_DEBUG_TRACE, "SASL identity: %s\n", sasl_username, 0, 0);
817 saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf );
818 if( saslrc == SASL_OK ) {
819 if( ssf && *ssf ) {
820 LDAPDebug(LDAP_DEBUG_TRACE,
821 "SASL install encryption, for SSF: %lu\n",
822 (unsigned long) *ssf, 0, 0 );
824 rc = nsldapi_sasl_install(ld, ld->ld_conns->lconn_sb, ctx, ssf);
827 return( rc );
830 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
832 * Get available SASL Mechanisms supported by the server
835 static int
836 nsldapi_get_sasl_mechs ( LDAP *ld, char **pmech )
838 char *attr[] = { "supportedSASLMechanisms", NULL };
839 char **values, **v, *mech, *m;
840 LDAPMessage *res, *e;
841 struct timeval timeout;
842 int slen, rc;
844 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
845 return( LDAP_PARAM_ERROR );
848 timeout.tv_sec = SEARCH_TIMEOUT_SECS;
849 timeout.tv_usec = 0;
851 rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE,
852 "objectclass=*", attr, 0, &timeout, &res );
854 if ( rc != LDAP_SUCCESS ) {
855 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
858 e = ldap_first_entry( ld, res );
859 if ( e == NULL ) {
860 ldap_msgfree( res );
861 if ( ld->ld_errno == LDAP_SUCCESS ) {
862 LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_OBJECT, NULL, NULL );
864 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
867 values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
868 if ( values == NULL ) {
869 ldap_msgfree( res );
870 LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL );
871 return( LDAP_NO_SUCH_ATTRIBUTE );
874 slen = 0;
875 for(v = values; *v != NULL; v++ ) {
876 slen += strlen(*v) + 1;
878 if ( (mech = NSLDAPI_CALLOC(1, slen)) == NULL) {
879 ldap_value_free( values );
880 ldap_msgfree( res );
881 LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
882 return( LDAP_NO_MEMORY );
884 m = mech;
885 for(v = values; *v; v++) {
886 if (v != values) {
887 *m++ = ' ';
889 slen = strlen(*v);
890 strncpy(m, *v, slen);
891 m += slen;
893 *m = '\0';
895 ldap_value_free( values );
896 ldap_msgfree( res );
898 *pmech = mech;
900 return( LDAP_SUCCESS );
902 #endif
904 int nsldapi_sasl_secprops(
905 const char *in,
906 sasl_security_properties_t *secprops )
908 int i;
909 char **props = NULL;
910 char *inp;
911 unsigned sflags = 0;
912 sasl_ssf_t max_ssf = 0;
913 sasl_ssf_t min_ssf = 0;
914 unsigned maxbufsize = 0;
915 int got_sflags = 0;
916 int got_max_ssf = 0;
917 int got_min_ssf = 0;
918 int got_maxbufsize = 0;
920 if (in == NULL) {
921 return LDAP_PARAM_ERROR;
923 inp = nsldapi_strdup(in);
924 if (inp == NULL) {
925 return LDAP_PARAM_ERROR;
927 props = ldap_str2charray( inp, "," );
928 NSLDAPI_FREE( inp );
930 if( props == NULL || secprops == NULL ) {
931 return LDAP_PARAM_ERROR;
934 for( i=0; props[i]; i++ ) {
935 if( strcasecmp(props[i], "none") == 0 ) {
936 got_sflags++;
938 } else if( strcasecmp(props[i], "noactive") == 0 ) {
939 got_sflags++;
940 sflags |= SASL_SEC_NOACTIVE;
942 } else if( strcasecmp(props[i], "noanonymous") == 0 ) {
943 got_sflags++;
944 sflags |= SASL_SEC_NOANONYMOUS;
946 } else if( strcasecmp(props[i], "nodict") == 0 ) {
947 got_sflags++;
948 sflags |= SASL_SEC_NODICTIONARY;
950 } else if( strcasecmp(props[i], "noplain") == 0 ) {
951 got_sflags++;
952 sflags |= SASL_SEC_NOPLAINTEXT;
954 } else if( strcasecmp(props[i], "forwardsec") == 0 ) {
955 got_sflags++;
956 sflags |= SASL_SEC_FORWARD_SECRECY;
958 } else if( strcasecmp(props[i], "passcred") == 0 ) {
959 got_sflags++;
960 sflags |= SASL_SEC_PASS_CREDENTIALS;
962 } else if( strncasecmp(props[i],
963 "minssf=", sizeof("minssf")) == 0 ) {
964 if( isdigit( props[i][sizeof("minssf")] ) ) {
965 got_min_ssf++;
966 min_ssf = atoi( &props[i][sizeof("minssf")] );
967 } else {
968 return LDAP_NOT_SUPPORTED;
971 } else if( strncasecmp(props[i],
972 "maxssf=", sizeof("maxssf")) == 0 ) {
973 if( isdigit( props[i][sizeof("maxssf")] ) ) {
974 got_max_ssf++;
975 max_ssf = atoi( &props[i][sizeof("maxssf")] );
976 } else {
977 return LDAP_NOT_SUPPORTED;
980 } else if( strncasecmp(props[i],
981 "maxbufsize=", sizeof("maxbufsize")) == 0 ) {
982 if( isdigit( props[i][sizeof("maxbufsize")] ) ) {
983 got_maxbufsize++;
984 maxbufsize = atoi( &props[i][sizeof("maxbufsize")] );
985 if( maxbufsize &&
986 (( maxbufsize < SASL_MIN_BUFF_SIZE )
987 || (maxbufsize > SASL_MAX_BUFF_SIZE ))) {
988 return( LDAP_PARAM_ERROR );
990 } else {
991 return( LDAP_NOT_SUPPORTED );
993 } else {
994 return( LDAP_NOT_SUPPORTED );
998 if(got_sflags) {
999 secprops->security_flags = sflags;
1001 if(got_min_ssf) {
1002 secprops->min_ssf = min_ssf;
1004 if(got_max_ssf) {
1005 secprops->max_ssf = max_ssf;
1007 if(got_maxbufsize) {
1008 secprops->maxbufsize = maxbufsize;
1011 ldap_charray_free( props );
1012 return( LDAP_SUCCESS );
1016 * SASL Authentication Interface: ldap_sasl_interactive_bind_s
1018 * This routine takes a DN, SASL mech list, and a SASL callback
1019 * and performs the necessary sequencing to complete a SASL bind
1020 * to the LDAP connection ld. The user provided callback can
1021 * use an optionally provided set of default values to complete
1022 * any necessary interactions.
1024 * Currently inpose the following restrictions:
1025 * A mech list must be provided, only LDAP_SASL_INTERACTIVE
1026 * mode is supported
1029 LDAP_CALL
1030 ldap_sasl_interactive_bind_s( LDAP *ld, const char *dn,
1031 const char *saslMechanism,
1032 LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags,
1033 LDAP_SASL_INTERACT_PROC *callback, void *defaults )
1035 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
1036 char *smechs;
1037 #endif
1038 int rc;
1040 LDAPDebug(LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0);
1042 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
1043 return( LDAP_PARAM_ERROR );
1046 if (flags != LDAP_SASL_INTERACTIVE || callback == NULL) {
1047 return( LDAP_PARAM_ERROR );
1050 LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK );
1052 if( saslMechanism == NULL || *saslMechanism == '\0' ) {
1053 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
1054 rc = nsldapi_get_sasl_mechs( ld, &smechs );
1055 if( rc != LDAP_SUCCESS ) {
1056 LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1057 return( rc );
1059 saslMechanism = smechs;
1060 #else
1061 LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1062 return( LDAP_PARAM_ERROR );
1063 #endif
1066 /* initialize SASL library */
1067 if ( nsldapi_sasl_init() < 0 ) {
1068 return( LDAP_PARAM_ERROR );
1071 rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism,
1072 flags, callback, defaults, sctrl, cctrl);
1074 LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1075 return( rc );
1078 #endif