1 /*
2 * CDDL HEADER START
3 *
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.
7 *
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.
12 *
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]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
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>
33
34 #define SEARCH_TIMEOUT_SECS 120
35 #define NSLDAPI_SM_BUF 128
36
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);
52
53 static int nsldapi_sasl_close( LDAP *ld, Sockbuf *sb );
54 static void destroy_sasliobuf(Sockbuf *sb);
55
56 /*
57 * SASL Dependent routines
58 *
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.
71 */
72
nsldapi_sasl_fail()73 static int nsldapi_sasl_fail()
74 {
75 return( SASL_FAIL );
76 }
77
78 /*
79 * Global SASL Init data
80 */
81
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 }
91 };
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 */
95
nsldapi_sasl_init(void)96 int nsldapi_sasl_init( void )
97 {
98 int saslrc;
99
100 mutex_lock(&sasl_mutex);
101 if ( nsldapi_sasl_inited ) {
102 mutex_unlock(&sasl_mutex);
103 return( 0 );
104 }
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 );
111 }
112 }
113 mutex_unlock(&sasl_mutex);
114 return( -1 );
115 }
116
117 /*
118 * SASL encryption routines
119 */
120
121 /*
122 * Get the 4 octet header [size] for a sasl encrypted buffer.
123 * See RFC222 [section 3].
124 */
125
126 static int
nsldapi_sasl_pktlen(char * buf,int maxbufsize)127 nsldapi_sasl_pktlen( char *buf, int maxbufsize )
128 {
129 int size;
130
131 #if defined( _WINDOWS ) || defined( _WIN32 )
132 size = ntohl(*(long *)buf);
133 #else
134 size = ntohl(*(uint32_t *)buf);
135 #endif
136
137 if ( size < 0 || size > maxbufsize ) {
138 return (-1 );
139 }
140
141 return( size + 4 ); /* include the first 4 bytes */
142 }
143
144 static int
nsldapi_sasl_read(int s,void * buf,int len,struct lextiof_socket_private * arg)145 nsldapi_sasl_read( int s, void *buf, int len,
146 struct lextiof_socket_private *arg)
147 {
148 Sockbuf *sb = (Sockbuf *)arg;
149 LDAP *ld;
150 const char *dbuf;
151 char *cp;
152 int ret;
153 unsigned dlen, blen;
154
155 if (sb == NULL) {
156 return( -1 );
157 }
158
159 ld = (LDAP *)sb->sb_sasl_prld;
160 if (ld == NULL) {
161 return( -1 );
162 }
163
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;
174 }
175 return( ret );
176 }
177
178 /* buffer is empty - fill it */
179 cp = sb->sb_sasl_ibuf;
180 dlen = 0;
181
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 );
190 }
191 #ifdef EINTR
192 if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
193 continue;
194 #endif
195 if ( ret <= 0 )
196 return( ret );
197
198 cp += ret;
199 dlen += ret;
200 }
201
202 blen = 4;
203
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 );
208 }
209 dlen = ret - dlen;
210
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 );
219 }
220
221 #ifdef EINTR
222 if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
223 continue;
224 #endif
225 if ( ret <= 0 )
226 return( ret );
227
228 cp += ret;
229 blen += ret;
230 dlen -= ret;
231 }
232
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 );
243 }
244
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;
249
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;
258 }
259 return( ret );
260 }
261
262 static int
nsldapi_sasl_write(int s,const void * buf,int len,struct lextiof_socket_private * arg)263 nsldapi_sasl_write( int s, const void *buf, int len,
264 struct lextiof_socket_private *arg)
265 {
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;
271
272 if (sb == NULL) {
273 return( -1 );
274 }
275
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 );
281 }
282
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 );
290 }
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);
300 }
301 if ( ret < 0 )
302 return( ret );
303 optr += ret;
304 olen -= ret;
305 }
306 len -= clen;
307 cbuf += clen;
308 tlen += clen;
309 }
310 return( tlen );
311 }
312
313 static int
nsldapi_sasl_poll(LDAP_X_PollFD fds[],int nfds,int timeout,struct lextiof_session_private * arg)314 nsldapi_sasl_poll(
315 LDAP_X_PollFD fds[], int nfds, int timeout,
316 struct lextiof_session_private *arg )
317 {
318 Sockbuf *sb = (Sockbuf *)arg;
319 LDAP *ld;
320 int i;
321
322 if (sb == NULL) {
323 return( -1 );
324 }
325 ld = (LDAP *)sb->sb_sasl_prld;
326 if (ld == NULL) {
327 return( -1 );
328 }
329
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;
337 }
338
339 }
340 }
341 return ( ld->ld_sasl_io_fns.lextiof_poll( fds, nfds, timeout,
342 (void *)ld->ld_sasl_io_fns.lextiof_session_arg) );
343 }
344
345 /* no encryption indirect routines */
346
347 static int
nsldapi_sasl_ne_read(int s,void * buf,int len,struct lextiof_socket_private * arg)348 nsldapi_sasl_ne_read( int s, void *buf, int len,
349 struct lextiof_socket_private *arg)
350 {
351 Sockbuf *sb = (Sockbuf *)arg;
352
353 if (sb == NULL) {
354 return( -1 );
355 }
356
357 return( sb->sb_sasl_fns.lbextiofn_read( s, buf, len,
358 sb->sb_sasl_fns.lbextiofn_socket_arg) );
359 }
360
361 static int
nsldapi_sasl_ne_write(int s,const void * buf,int len,struct lextiof_socket_private * arg)362 nsldapi_sasl_ne_write( int s, const void *buf, int len,
363 struct lextiof_socket_private *arg)
364 {
365 Sockbuf *sb = (Sockbuf *)arg;
366
367 if (sb == NULL) {
368 return( -1 );
369 }
370
371 return( sb->sb_sasl_fns.lbextiofn_write( s, buf, len,
372 sb->sb_sasl_fns.lbextiofn_socket_arg) );
373 }
374
375 static int
nsldapi_sasl_close_socket(int s,struct lextiof_socket_private * arg)376 nsldapi_sasl_close_socket(int s, struct lextiof_socket_private *arg )
377 {
378 Sockbuf *sb = (Sockbuf *)arg;
379 LDAP *ld;
380
381 if (sb == NULL) {
382 return( -1 );
383 }
384 ld = (LDAP *)sb->sb_sasl_prld;
385 if (ld == NULL) {
386 return( -1 );
387 }
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);
393
394 /* undo SASL */
395 nsldapi_sasl_close( ld, sb );
396
397 return ( ld->ld_sasl_io_fns.lextiof_close( s,
398 (struct lextiof_socket_private *)
399 sb->sb_sasl_fns.lbextiofn_socket_arg ) );
400 }
401
402 /*
403 * install encryption routines if security has been negotiated
404 */
405 static int
nsldapi_sasl_install(LDAP * ld,Sockbuf * sb,void * ctx_arg,sasl_ssf_t * ssf)406 nsldapi_sasl_install( LDAP *ld, Sockbuf *sb, void *ctx_arg, sasl_ssf_t *ssf)
407 {
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;
414
415 if (ssf && *ssf) {
416 encrypt = 1;
417 }
418 if ( sb == NULL ) {
419 return( LDAP_LOCAL_ERROR );
420 }
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 );
426
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 );
437 }
438 if ((sb->sb_sasl_ibuf = NSLDAPI_MALLOC(bufsiz)) == NULL) {
439 return( LDAP_LOCAL_ERROR );
440 }
441 sb->sb_sasl_iptr = NULL;
442 sb->sb_sasl_bfsz = bufsiz;
443 sb->sb_sasl_ilen = 0;
444 }
445
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 );
456 }
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 );
464 }
465
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;
482 }
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 );
496 }
497 sb->sb_sasl_prld = (void *)ld;
498 }
499
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 );
514 }
515 }
516
517 return( LDAP_SUCCESS );
518 }
519
520 static int
nsldapi_sasl_cvterrno(LDAP * ld,int err,char * msg)521 nsldapi_sasl_cvterrno( LDAP *ld, int err, char *msg )
522 {
523 int rc = LDAP_LOCAL_ERROR;
524
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;
562 }
563
564 LDAP_SET_LDERRNO( ld, rc, NULL, msg );
565 return( rc );
566 }
567
568 int
nsldapi_sasl_open(LDAP * ld)569 nsldapi_sasl_open(LDAP *ld)
570 {
571 Sockbuf *sb;
572 char * host;
573 int saslrc;
574 sasl_conn_t *ctx = NULL;
575
576 if (ld == NULL) {
577 return( LDAP_LOCAL_ERROR );
578 }
579
580 if (ld->ld_defconn == NULL) {
581 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
582 return( LDAP_LOCAL_ERROR );
583 }
584 sb = ld->ld_defconn->lconn_sb;
585 host = ld->ld_defhost;
586
587 if ( sb == NULL || host == NULL ) {
588 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
589 return( LDAP_LOCAL_ERROR );
590 }
591
592 if (sb->sb_sasl_ctx) {
593 sasl_dispose(&sb->sb_sasl_ctx);
594 sb->sb_sasl_ctx = NULL;
595 }
596
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 );
603 }
604
605 saslrc = _sasl_client_new(gctx, "ldap", host,
606 NULL, NULL, /* iplocal ipremote strings currently unused */
607 NULL, 0, &ctx );
608
609 if ( saslrc != SASL_OK ) {
610 mutex_unlock(&sasl_mutex);
611 sasl_dispose(&ctx);
612 return( nsldapi_sasl_cvterrno( ld, saslrc, NULL ) );
613 }
614
615 sb->sb_sasl_ctx = (void *)ctx;
616 mutex_unlock(&sasl_mutex);
617
618 return( LDAP_SUCCESS );
619 }
620
621 static void
destroy_sasliobuf(Sockbuf * sb)622 destroy_sasliobuf(Sockbuf *sb)
623 {
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;
630 }
631 }
632
633 static int
nsldapi_sasl_close(LDAP * ld,Sockbuf * sb)634 nsldapi_sasl_close( LDAP *ld, Sockbuf *sb )
635 {
636 sasl_conn_t *ctx = (sasl_conn_t *)sb->sb_sasl_ctx;
637
638 destroy_sasliobuf(sb);
639
640 if( ctx != NULL ) {
641 sasl_dispose( &ctx );
642 sb->sb_sasl_ctx = NULL;
643 }
644 return( LDAP_SUCCESS );
645 }
646
647 static int
nsldapi_sasl_do_bind(LDAP * ld,const char * dn,const char * mechs,unsigned flags,LDAP_SASL_INTERACT_PROC * callback,void * defaults,LDAPControl ** sctrl,LDAPControl ** cctrl)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 )
652 {
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;
662
663 if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) {
664 LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
665 return( LDAP_NOT_SUPPORTED );
666 }
667
668 /* shouldn't happen */
669 if (callback == NULL) {
670 return( LDAP_LOCAL_ERROR );
671 }
672
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 ) );
678 }
679 }
680
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 );
685 }
686
687 /* expect context to be initialized when connection is open */
688 ctx = (sasl_conn_t *)ld->ld_defconn->lconn_sb->sb_sasl_ctx;
689
690 if( ctx == NULL ) {
691 LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
692 return( LDAP_LOCAL_ERROR );
693 }
694
695 /* (re)set security properties */
696 sasl_setprop( ctx, SASL_SEC_PROPS, &ld->ld_sasl_secprops );
697
698 ccred.bv_val = NULL;
699 ccred.bv_len = 0;
700
701 LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n",
702 (mech ? mech : ""), 0, 0 );
703
704 do {
705 saslrc = sasl_client_start( ctx,
706 mechs,
707 &prompts,
708 (const char **)&ccred.bv_val,
709 &credlen,
710 &mech );
711
712 LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of client start for SASL/%s authentication\n",
713 stepnum, (mech ? mech : ""), 0 );
714 stepnum++;
715
716 if( saslrc == SASL_INTERACT &&
717 (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) {
718 break;
719 }
720 } while ( saslrc == SASL_INTERACT );
721
722 ccred.bv_len = credlen;
723
724 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
725 return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
726 }
727
728 stepnum = 1;
729
730 do {
731 struct berval *scred;
732 int clientstepnum = 1;
733
734 scred = NULL;
735
736 LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of bind for SASL/%s authentication\n",
737 stepnum, (mech ? mech : ""), 0 );
738 stepnum++;
739
740 /* notify server of a sasl bind step */
741 rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
742 sctrl, cctrl, &scred);
743
744 if ( ccred.bv_val != NULL ) {
745 ccred.bv_val = NULL;
746 }
747
748 if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
749 ber_bvfree( scred );
750 return( rc );
751 }
752
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 );
772 }
773 }
774 break;
775 }
776
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 );
788
789 if( saslrc == SASL_INTERACT &&
790 (callback)(ld, flags, defaults, prompts)
791 != LDAP_SUCCESS ) {
792 break;
793 }
794 } while ( saslrc == SASL_INTERACT );
795
796 ccred.bv_len = credlen;
797 ber_bvfree( scred );
798
799 if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
800 return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
801 }
802 } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
803
804 if ( rc != LDAP_SUCCESS ) {
805 return( rc );
806 }
807
808 if ( saslrc != SASL_OK ) {
809 return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
810 }
811
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);
815 }
816
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 );
823 }
824 rc = nsldapi_sasl_install(ld, ld->ld_conns->lconn_sb, ctx, ssf);
825 }
826
827 return( rc );
828 }
829
830 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
831 /*
832 * Get available SASL Mechanisms supported by the server
833 */
834
835 static int
nsldapi_get_sasl_mechs(LDAP * ld,char ** pmech)836 nsldapi_get_sasl_mechs ( LDAP *ld, char **pmech )
837 {
838 char *attr[] = { "supportedSASLMechanisms", NULL };
839 char **values, **v, *mech, *m;
840 LDAPMessage *res, *e;
841 struct timeval timeout;
842 int slen, rc;
843
844 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
845 return( LDAP_PARAM_ERROR );
846 }
847
848 timeout.tv_sec = SEARCH_TIMEOUT_SECS;
849 timeout.tv_usec = 0;
850
851 rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE,
852 "objectclass=*", attr, 0, &timeout, &res );
853
854 if ( rc != LDAP_SUCCESS ) {
855 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
856 }
857
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 );
863 }
864 return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
865 }
866
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 );
872 }
873
874 slen = 0;
875 for(v = values; *v != NULL; v++ ) {
876 slen += strlen(*v) + 1;
877 }
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 );
883 }
884 m = mech;
885 for(v = values; *v; v++) {
886 if (v != values) {
887 *m++ = ' ';
888 }
889 slen = strlen(*v);
890 strncpy(m, *v, slen);
891 m += slen;
892 }
893 *m = '\0';
894
895 ldap_value_free( values );
896 ldap_msgfree( res );
897
898 *pmech = mech;
899
900 return( LDAP_SUCCESS );
901 }
902 #endif
903
nsldapi_sasl_secprops(const char * in,sasl_security_properties_t * secprops)904 int nsldapi_sasl_secprops(
905 const char *in,
906 sasl_security_properties_t *secprops )
907 {
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;
919
920 if (in == NULL) {
921 return LDAP_PARAM_ERROR;
922 }
923 inp = nsldapi_strdup(in);
924 if (inp == NULL) {
925 return LDAP_PARAM_ERROR;
926 }
927 props = ldap_str2charray( inp, "," );
928 NSLDAPI_FREE( inp );
929
930 if( props == NULL || secprops == NULL ) {
931 return LDAP_PARAM_ERROR;
932 }
933
934 for( i=0; props[i]; i++ ) {
935 if( strcasecmp(props[i], "none") == 0 ) {
936 got_sflags++;
937
938 } else if( strcasecmp(props[i], "noactive") == 0 ) {
939 got_sflags++;
940 sflags |= SASL_SEC_NOACTIVE;
941
942 } else if( strcasecmp(props[i], "noanonymous") == 0 ) {
943 got_sflags++;
944 sflags |= SASL_SEC_NOANONYMOUS;
945
946 } else if( strcasecmp(props[i], "nodict") == 0 ) {
947 got_sflags++;
948 sflags |= SASL_SEC_NODICTIONARY;
949
950 } else if( strcasecmp(props[i], "noplain") == 0 ) {
951 got_sflags++;
952 sflags |= SASL_SEC_NOPLAINTEXT;
953
954 } else if( strcasecmp(props[i], "forwardsec") == 0 ) {
955 got_sflags++;
956 sflags |= SASL_SEC_FORWARD_SECRECY;
957
958 } else if( strcasecmp(props[i], "passcred") == 0 ) {
959 got_sflags++;
960 sflags |= SASL_SEC_PASS_CREDENTIALS;
961
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;
969 }
970
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;
978 }
979
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 );
989 }
990 } else {
991 return( LDAP_NOT_SUPPORTED );
992 }
993 } else {
994 return( LDAP_NOT_SUPPORTED );
995 }
996 }
997
998 if(got_sflags) {
999 secprops->security_flags = sflags;
1000 }
1001 if(got_min_ssf) {
1002 secprops->min_ssf = min_ssf;
1003 }
1004 if(got_max_ssf) {
1005 secprops->max_ssf = max_ssf;
1006 }
1007 if(got_maxbufsize) {
1008 secprops->maxbufsize = maxbufsize;
1009 }
1010
1011 ldap_charray_free( props );
1012 return( LDAP_SUCCESS );
1013 }
1014
1015 /*
1016 * SASL Authentication Interface: ldap_sasl_interactive_bind_s
1017 *
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.
1023 *
1024 * Currently inpose the following restrictions:
1025 * A mech list must be provided, only LDAP_SASL_INTERACTIVE
1026 * mode is supported
1027 */
1028 int
1029 LDAP_CALL
ldap_sasl_interactive_bind_s(LDAP * ld,const char * dn,const char * saslMechanism,LDAPControl ** sctrl,LDAPControl ** cctrl,unsigned flags,LDAP_SASL_INTERACT_PROC * callback,void * defaults)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 )
1034 {
1035 #ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
1036 char *smechs;
1037 #endif
1038 int rc;
1039
1040 LDAPDebug(LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0);
1041
1042 if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
1043 return( LDAP_PARAM_ERROR );
1044 }
1045
1046 if (flags != LDAP_SASL_INTERACTIVE || callback == NULL) {
1047 return( LDAP_PARAM_ERROR );
1048 }
1049
1050 LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK );
1051
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 );
1058 }
1059 saslMechanism = smechs;
1060 #else
1061 LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1062 return( LDAP_PARAM_ERROR );
1063 #endif
1064 }
1065
1066 /* initialize SASL library */
1067 if ( nsldapi_sasl_init() < 0 ) {
1068 return( LDAP_PARAM_ERROR );
1069 }
1070
1071 rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism,
1072 flags, callback, defaults, sctrl, cctrl);
1073
1074 LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1075 return( rc );
1076 }
1077
1078 #endif
1079