1N/A/*
1N/A * CDDL HEADER START
1N/A *
1N/A * The contents of this file are subject to the terms of the
1N/A * Common Development and Distribution License (the "License").
1N/A * You may not use this file except in compliance with the License.
1N/A *
1N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
1N/A * or http://www.opensolaris.org/os/licensing.
1N/A * See the License for the specific language governing permissions
1N/A * and limitations under the License.
1N/A *
1N/A * When distributing Covered Code, include this CDDL HEADER in each
1N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
1N/A * If applicable, add the following below this CDDL HEADER, with the
1N/A * fields enclosed by brackets "[]" replaced with your own identifying
1N/A * information: Portions Copyright [yyyy] [name of copyright owner]
1N/A *
1N/A * CDDL HEADER END
1N/A */
1N/A/*
1N/A * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
1N/A * Use is subject to license terms.
1N/A */
1N/A
1N/A#ifdef LDAP_SASLIO_HOOKS
1N/A#include <assert.h>
1N/A#include "ldap-int.h"
1N/A#include "../ber/lber-int.h"
1N/A#include <sasl/sasl.h>
1N/A#include <thread.h>
1N/A#include <synch.h>
1N/A
1N/A#define SEARCH_TIMEOUT_SECS 120
1N/A#define NSLDAPI_SM_BUF 128
1N/A
1N/Aextern void *sasl_create_context(void);
1N/Aextern void sasl_free_context(void *ctx);
1N/Aextern int _sasl_client_init(void *ctx, const sasl_callback_t *callbacks);
1N/Aextern int _sasl_client_new(void *ctx, const char *service,
1N/A const char *serverFQDN, const char *iplocalport,
1N/A const char *ipremoteport,
1N/A const sasl_callback_t *prompt_supp,
1N/A unsigned flags, sasl_conn_t **pconn);
1N/Aextern int _sasl_server_init(void *ctx,
1N/A const sasl_callback_t *callbacks, const char *appname);
1N/Aextern int _sasl_server_new(void *ctx, const char *service,
1N/A const char *serverFQDN, const char *user_realm,
1N/A const char *iplocalport, const char *ipremoteport,
1N/A const sasl_callback_t *callbacks,
1N/A unsigned flags, sasl_conn_t **pconn);
1N/A
1N/Astatic int nsldapi_sasl_close( LDAP *ld, Sockbuf *sb );
1N/Astatic void destroy_sasliobuf(Sockbuf *sb);
1N/A
1N/A/*
1N/A * SASL Dependent routines
1N/A *
1N/A * SASL security and integrity options are supported through the
1N/A * use of the extended I/O functionality. Because the extended
1N/A * I/O functions may already be in use prior to enabling encryption,
1N/A * when SASL encryption is enabled, these routine interpose themselves
1N/A * over the existng extended I/O routines and add an additional level
1N/A * of indirection.
1N/A * IE: Before SASL: client->libldap->lber->extio
1N/A * After SASL: client->libldap->lber->saslio->extio
1N/A * Any extio functions are still used for the raw i/O [IE prldap]
1N/A * but SASL will decrypt before passing to lber.
1N/A * SASL cannot decrypt a stream so full packets must be read
1N/A * before proceeding.
1N/A */
1N/A
1N/Astatic int nsldapi_sasl_fail()
1N/A{
1N/A return( SASL_FAIL );
1N/A}
1N/A
1N/A/*
1N/A * Global SASL Init data
1N/A */
1N/A
1N/Astatic sasl_callback_t client_callbacks[] = {
1N/A { SASL_CB_GETOPT, nsldapi_sasl_fail, NULL },
1N/A { SASL_CB_GETREALM, NULL, NULL },
1N/A { SASL_CB_USER, NULL, NULL },
1N/A { SASL_CB_AUTHNAME, NULL, NULL },
1N/A { SASL_CB_PASS, NULL, NULL },
1N/A { SASL_CB_ECHOPROMPT, NULL, NULL },
1N/A { SASL_CB_NOECHOPROMPT, NULL, NULL },
1N/A { SASL_CB_LIST_END, NULL, NULL }
1N/A};
1N/Astatic mutex_t sasl_mutex = DEFAULTMUTEX;
1N/Astatic int nsldapi_sasl_inited = 0;
1N/Astatic void *gctx; /* intentially not freed - avoid libsasl re-inits */
1N/A
1N/Aint nsldapi_sasl_init( void )
1N/A{
1N/A int saslrc;
1N/A
1N/A mutex_lock(&sasl_mutex);
1N/A if ( nsldapi_sasl_inited ) {
1N/A mutex_unlock(&sasl_mutex);
1N/A return( 0 );
1N/A }
1N/A if ((gctx = (void *)sasl_create_context()) != NULL) {
1N/A saslrc = _sasl_client_init(gctx, client_callbacks);
1N/A if (saslrc == SASL_OK ) {
1N/A nsldapi_sasl_inited = 1;
1N/A mutex_unlock(&sasl_mutex);
1N/A return( 0 );
1N/A }
1N/A }
1N/A mutex_unlock(&sasl_mutex);
1N/A return( -1 );
1N/A}
1N/A
1N/A/*
1N/A * SASL encryption routines
1N/A */
1N/A
1N/A/*
1N/A * Get the 4 octet header [size] for a sasl encrypted buffer.
1N/A * See RFC222 [section 3].
1N/A */
1N/A
1N/Astatic int
1N/Ansldapi_sasl_pktlen( char *buf, int maxbufsize )
1N/A{
1N/A int size;
1N/A
1N/A#if defined( _WINDOWS ) || defined( _WIN32 )
1N/A size = ntohl(*(long *)buf);
1N/A#else
1N/A size = ntohl(*(uint32_t *)buf);
1N/A#endif
1N/A
1N/A if ( size < 0 || size > maxbufsize ) {
1N/A return (-1 );
1N/A }
1N/A
1N/A return( size + 4 ); /* include the first 4 bytes */
1N/A}
1N/A
1N/Astatic int
1N/Ansldapi_sasl_read( int s, void *buf, int len,
1N/A struct lextiof_socket_private *arg)
1N/A{
1N/A Sockbuf *sb = (Sockbuf *)arg;
1N/A LDAP *ld;
1N/A const char *dbuf;
1N/A char *cp;
1N/A int ret;
1N/A unsigned dlen, blen;
1N/A
1N/A if (sb == NULL) {
1N/A return( -1 );
1N/A }
1N/A
1N/A ld = (LDAP *)sb->sb_sasl_prld;
1N/A if (ld == NULL) {
1N/A return( -1 );
1N/A }
1N/A
1N/A /* Is there anything left in the existing buffer? */
1N/A if ((ret = sb->sb_sasl_ilen) > 0) {
1N/A ret = (ret > len ? len : ret);
1N/A SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret );
1N/A if (ret == sb->sb_sasl_ilen) {
1N/A sb->sb_sasl_ilen = 0;
1N/A sb->sb_sasl_iptr = NULL;
1N/A } else {
1N/A sb->sb_sasl_ilen -= ret;
1N/A sb->sb_sasl_iptr += ret;
1N/A }
1N/A return( ret );
1N/A }
1N/A
1N/A /* buffer is empty - fill it */
1N/A cp = sb->sb_sasl_ibuf;
1N/A dlen = 0;
1N/A
1N/A /* Read the length of the packet */
1N/A while ( dlen < 4 ) {
1N/A if (sb->sb_sasl_fns.lbextiofn_read != NULL) {
1N/A ret = sb->sb_sasl_fns.lbextiofn_read(
1N/A s, cp, 4 - dlen,
1N/A sb->sb_sasl_fns.lbextiofn_socket_arg);
1N/A } else {
1N/A ret = read( sb->sb_sd, cp, 4 - dlen );
1N/A }
1N/A#ifdef EINTR
1N/A if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
1N/A continue;
1N/A#endif
1N/A if ( ret <= 0 )
1N/A return( ret );
1N/A
1N/A cp += ret;
1N/A dlen += ret;
1N/A }
1N/A
1N/A blen = 4;
1N/A
1N/A ret = nsldapi_sasl_pktlen( sb->sb_sasl_ibuf, sb->sb_sasl_bfsz );
1N/A if (ret < 0) {
1N/A LDAP_SET_ERRNO(ld, EIO);
1N/A return( -1 );
1N/A }
1N/A dlen = ret - dlen;
1N/A
1N/A /* read the rest of the encrypted packet */
1N/A while ( dlen > 0 ) {
1N/A if (sb->sb_sasl_fns.lbextiofn_read != NULL) {
1N/A ret = sb->sb_sasl_fns.lbextiofn_read(
1N/A s, cp, dlen,
1N/A sb->sb_sasl_fns.lbextiofn_socket_arg);
1N/A } else {
1N/A ret = read( sb->sb_sd, cp, dlen );
1N/A }
1N/A
1N/A#ifdef EINTR
1N/A if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
1N/A continue;
1N/A#endif
1N/A if ( ret <= 0 )
1N/A return( ret );
1N/A
1N/A cp += ret;
1N/A blen += ret;
1N/A dlen -= ret;
1N/A }
1N/A
1N/A /* Decode the packet */
1N/A ret = sasl_decode( sb->sb_sasl_ctx,
1N/A sb->sb_sasl_ibuf, blen,
1N/A &dbuf, &dlen);
1N/A if ( ret != SASL_OK ) {
1N/A /* sb_sasl_read: failed to decode packet, drop it, error */
1N/A sb->sb_sasl_iptr = NULL;
1N/A sb->sb_sasl_ilen = 0;
1N/A LDAP_SET_ERRNO(ld, EIO);
1N/A return( -1 );
1N/A }
1N/A
1N/A /* copy decrypted packet to the input buffer */
1N/A SAFEMEMCPY( sb->sb_sasl_ibuf, dbuf, dlen );
1N/A sb->sb_sasl_iptr = sb->sb_sasl_ibuf;
1N/A sb->sb_sasl_ilen = dlen;
1N/A
1N/A ret = (dlen > (unsigned) len ? len : dlen);
1N/A SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret );
1N/A if (ret == sb->sb_sasl_ilen) {
1N/A sb->sb_sasl_ilen = 0;
1N/A sb->sb_sasl_iptr = NULL;
1N/A } else {
1N/A sb->sb_sasl_ilen -= ret;
1N/A sb->sb_sasl_iptr += ret;
1N/A }
1N/A return( ret );
1N/A}
1N/A
1N/Astatic int
1N/Ansldapi_sasl_write( int s, const void *buf, int len,
1N/A struct lextiof_socket_private *arg)
1N/A{
1N/A Sockbuf *sb = (Sockbuf *)arg;
1N/A int ret = 0;
1N/A const char *obuf, *optr, *cbuf = (const char *)buf;
1N/A unsigned olen, clen, tlen = 0;
1N/A unsigned *maxbuf;
1N/A
1N/A if (sb == NULL) {
1N/A return( -1 );
1N/A }
1N/A
1N/A ret = sasl_getprop(sb->sb_sasl_ctx, SASL_MAXOUTBUF,
1N/A (const void **)&maxbuf);
1N/A if ( ret != SASL_OK ) {
1N/A /* just a sanity check, should never happen */
1N/A return( -1 );
1N/A }
1N/A
1N/A while (len > 0) {
1N/A clen = (len > *maxbuf) ? *maxbuf : len;
1N/A /* encode the next packet. */
1N/A ret = sasl_encode( sb->sb_sasl_ctx, cbuf, clen, &obuf, &olen);
1N/A if ( ret != SASL_OK ) {
1N/A /* XXX Log error? "sb_sasl_write: failed to encode packet..." */
1N/A return( -1 );
1N/A }
1N/A /* Write everything now, buffer is only good until next sasl_encode */
1N/A optr = obuf;
1N/A while (olen > 0) {
1N/A if (sb->sb_sasl_fns.lbextiofn_write != NULL) {
1N/A ret = sb->sb_sasl_fns.lbextiofn_write(
1N/A s, optr, olen,
1N/A sb->sb_sasl_fns.lbextiofn_socket_arg);
1N/A } else {
1N/A ret = write( sb->sb_sd, optr, olen);
1N/A }
1N/A if ( ret < 0 )
1N/A return( ret );
1N/A optr += ret;
1N/A olen -= ret;
1N/A }
1N/A len -= clen;
1N/A cbuf += clen;
1N/A tlen += clen;
1N/A }
1N/A return( tlen );
1N/A}
1N/A
1N/Astatic int
1N/Ansldapi_sasl_poll(
1N/A LDAP_X_PollFD fds[], int nfds, int timeout,
1N/A struct lextiof_session_private *arg )
1N/A{
1N/A Sockbuf *sb = (Sockbuf *)arg;
1N/A LDAP *ld;
1N/A int i;
1N/A
1N/A if (sb == NULL) {
1N/A return( -1 );
1N/A }
1N/A ld = (LDAP *)sb->sb_sasl_prld;
1N/A if (ld == NULL) {
1N/A return( -1 );
1N/A }
1N/A
1N/A if (fds && nfds > 0) {
1N/A for(i = 0; i < nfds; i++) {
1N/A if (fds[i].lpoll_socketarg ==
1N/A (struct lextiof_socket_private *)sb) {
1N/A fds[i].lpoll_socketarg =
1N/A (struct lextiof_socket_private *)
1N/A sb->sb_sasl_fns.lbextiofn_socket_arg;
1N/A }
1N/A
1N/A }
1N/A }
1N/A return ( ld->ld_sasl_io_fns.lextiof_poll( fds, nfds, timeout,
1N/A (void *)ld->ld_sasl_io_fns.lextiof_session_arg) );
1N/A}
1N/A
1N/A/* no encryption indirect routines */
1N/A
1N/Astatic int
1N/Ansldapi_sasl_ne_read( int s, void *buf, int len,
1N/A struct lextiof_socket_private *arg)
1N/A{
1N/A Sockbuf *sb = (Sockbuf *)arg;
1N/A
1N/A if (sb == NULL) {
1N/A return( -1 );
1N/A }
1N/A
1N/A return( sb->sb_sasl_fns.lbextiofn_read( s, buf, len,
1N/A sb->sb_sasl_fns.lbextiofn_socket_arg) );
1N/A}
1N/A
1N/Astatic int
1N/Ansldapi_sasl_ne_write( int s, const void *buf, int len,
1N/A struct lextiof_socket_private *arg)
1N/A{
1N/A Sockbuf *sb = (Sockbuf *)arg;
1N/A
1N/A if (sb == NULL) {
1N/A return( -1 );
1N/A }
1N/A
1N/A return( sb->sb_sasl_fns.lbextiofn_write( s, buf, len,
1N/A sb->sb_sasl_fns.lbextiofn_socket_arg) );
1N/A}
1N/A
1N/Astatic int
1N/Ansldapi_sasl_close_socket(int s, struct lextiof_socket_private *arg )
1N/A{
1N/A Sockbuf *sb = (Sockbuf *)arg;
1N/A LDAP *ld;
1N/A
1N/A if (sb == NULL) {
1N/A return( -1 );
1N/A }
1N/A ld = (LDAP *)sb->sb_sasl_prld;
1N/A if (ld == NULL) {
1N/A return( -1 );
1N/A }
1N/A /* undo function pointer interposing */
1N/A ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &ld->ld_sasl_io_fns );
1N/A ber_sockbuf_set_option( sb,
1N/A LBER_SOCKBUF_OPT_EXT_IO_FNS,
1N/A (void *)&sb->sb_sasl_fns);
1N/A
1N/A /* undo SASL */
1N/A nsldapi_sasl_close( ld, sb );
1N/A
1N/A return ( ld->ld_sasl_io_fns.lextiof_close( s,
1N/A (struct lextiof_socket_private *)
1N/A sb->sb_sasl_fns.lbextiofn_socket_arg ) );
1N/A}
1N/A
1N/A/*
1N/A * install encryption routines if security has been negotiated
1N/A */
1N/Astatic int
1N/Ansldapi_sasl_install( LDAP *ld, Sockbuf *sb, void *ctx_arg, sasl_ssf_t *ssf)
1N/A{
1N/A struct lber_x_ext_io_fns fns;
1N/A struct ldap_x_ext_io_fns iofns;
1N/A sasl_security_properties_t *secprops;
1N/A int rc, value;
1N/A int bufsiz;
1N/A int encrypt = 0;
1N/A
1N/A if (ssf && *ssf) {
1N/A encrypt = 1;
1N/A }
1N/A if ( sb == NULL ) {
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A rc = ber_sockbuf_get_option( sb,
1N/A LBER_SOCKBUF_OPT_TO_FILE_ONLY,
1N/A (void *) &value);
1N/A if (rc != 0 || value != 0)
1N/A return( LDAP_LOCAL_ERROR );
1N/A
1N/A if (encrypt) {
1N/A /* initialize input buffer - use MAX SIZE to avoid reallocs */
1N/A sb->sb_sasl_ctx = (sasl_conn_t *)ctx_arg;
1N/A rc = sasl_getprop( sb->sb_sasl_ctx, SASL_SEC_PROPS,
1N/A (const void **)&secprops );
1N/A if (rc != SASL_OK)
1N/A return( LDAP_LOCAL_ERROR );
1N/A bufsiz = secprops->maxbufsize;
1N/A if (bufsiz <= 0) {
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A if ((sb->sb_sasl_ibuf = NSLDAPI_MALLOC(bufsiz)) == NULL) {
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A sb->sb_sasl_iptr = NULL;
1N/A sb->sb_sasl_bfsz = bufsiz;
1N/A sb->sb_sasl_ilen = 0;
1N/A }
1N/A
1N/A /* Reset Session then Socket Args */
1N/A /* Get old values */
1N/A (void) memset( &sb->sb_sasl_fns, 0, LBER_X_EXTIO_FNS_SIZE);
1N/A sb->sb_sasl_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
1N/A rc = ber_sockbuf_get_option( sb,
1N/A LBER_SOCKBUF_OPT_EXT_IO_FNS,
1N/A (void *)&sb->sb_sasl_fns);
1N/A if (rc != 0) {
1N/A destroy_sasliobuf(sb);
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A memset( &ld->ld_sasl_io_fns, 0, sizeof(iofns));
1N/A ld->ld_sasl_io_fns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
1N/A rc = ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
1N/A &ld->ld_sasl_io_fns );
1N/A if (rc != 0 ) {
1N/A destroy_sasliobuf(sb);
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A
1N/A /* Set new values */
1N/A if ( ld->ld_sasl_io_fns.lextiof_read != NULL ||
1N/A ld->ld_sasl_io_fns.lextiof_write != NULL ||
1N/A ld->ld_sasl_io_fns.lextiof_poll != NULL ||
1N/A ld->ld_sasl_io_fns.lextiof_connect != NULL ||
1N/A ld->ld_sasl_io_fns.lextiof_close != NULL ) {
1N/A memset( &iofns, 0, sizeof(iofns));
1N/A iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
1N/A if (encrypt) {
1N/A iofns.lextiof_read = nsldapi_sasl_read;
1N/A iofns.lextiof_write = nsldapi_sasl_write;
1N/A iofns.lextiof_poll = nsldapi_sasl_poll;
1N/A } else {
1N/A iofns.lextiof_read = nsldapi_sasl_ne_read;
1N/A iofns.lextiof_write = nsldapi_sasl_ne_write;
1N/A iofns.lextiof_poll = nsldapi_sasl_poll;
1N/A }
1N/A iofns.lextiof_connect = ld->ld_sasl_io_fns.lextiof_connect;
1N/A iofns.lextiof_close = nsldapi_sasl_close_socket;
1N/A iofns.lextiof_newhandle = ld->ld_sasl_io_fns.lextiof_newhandle;
1N/A iofns.lextiof_disposehandle =
1N/A ld->ld_sasl_io_fns.lextiof_disposehandle;
1N/A iofns.lextiof_session_arg =
1N/A (void *) sb;
1N/A /* ld->ld_sasl_io_fns.lextiof_session_arg; */
1N/A rc = ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
1N/A &iofns );
1N/A if (rc != 0 ) {
1N/A destroy_sasliobuf(sb);
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A sb->sb_sasl_prld = (void *)ld;
1N/A }
1N/A
1N/A if (encrypt) {
1N/A (void) memset( &fns, 0, LBER_X_EXTIO_FNS_SIZE);
1N/A fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
1N/A fns.lbextiofn_read = nsldapi_sasl_read;
1N/A fns.lbextiofn_write = nsldapi_sasl_write;
1N/A fns.lbextiofn_socket_arg =
1N/A (void *) sb;
1N/A /* (void *)sb->sb_sasl_fns.lbextiofn_socket_arg; */
1N/A rc = ber_sockbuf_set_option( sb,
1N/A LBER_SOCKBUF_OPT_EXT_IO_FNS,
1N/A (void *)&fns);
1N/A if (rc != 0) {
1N/A destroy_sasliobuf(sb);
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A }
1N/A
1N/A return( LDAP_SUCCESS );
1N/A}
1N/A
1N/Astatic int
1N/Ansldapi_sasl_cvterrno( LDAP *ld, int err, char *msg )
1N/A{
1N/A int rc = LDAP_LOCAL_ERROR;
1N/A
1N/A switch (err) {
1N/A case SASL_OK:
1N/A rc = LDAP_SUCCESS;
1N/A break;
1N/A case SASL_NOMECH:
1N/A rc = LDAP_AUTH_UNKNOWN;
1N/A break;
1N/A case SASL_BADSERV:
1N/A rc = LDAP_CONNECT_ERROR;
1N/A break;
1N/A case SASL_DISABLED:
1N/A case SASL_ENCRYPT:
1N/A case SASL_EXPIRED:
1N/A case SASL_NOUSERPASS:
1N/A case SASL_NOVERIFY:
1N/A case SASL_PWLOCK:
1N/A case SASL_TOOWEAK:
1N/A case SASL_UNAVAIL:
1N/A case SASL_WEAKPASS:
1N/A rc = LDAP_INAPPROPRIATE_AUTH;
1N/A break;
1N/A case SASL_BADAUTH:
1N/A case SASL_NOAUTHZ:
1N/A rc = LDAP_INVALID_CREDENTIALS;
1N/A break;
1N/A case SASL_NOMEM:
1N/A rc = LDAP_NO_MEMORY;
1N/A break;
1N/A case SASL_NOUSER:
1N/A rc = LDAP_NO_SUCH_OBJECT;
1N/A break;
1N/A case SASL_CONTINUE:
1N/A case SASL_FAIL:
1N/A case SASL_INTERACT:
1N/A default:
1N/A rc = LDAP_LOCAL_ERROR;
1N/A break;
1N/A }
1N/A
1N/A LDAP_SET_LDERRNO( ld, rc, NULL, msg );
1N/A return( rc );
1N/A}
1N/A
1N/Aint
1N/Ansldapi_sasl_open(LDAP *ld)
1N/A{
1N/A Sockbuf *sb;
1N/A char * host;
1N/A int saslrc;
1N/A sasl_conn_t *ctx = NULL;
1N/A
1N/A if (ld == NULL) {
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A
1N/A if (ld->ld_defconn == NULL) {
1N/A LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A sb = ld->ld_defconn->lconn_sb;
1N/A host = ld->ld_defhost;
1N/A
1N/A if ( sb == NULL || host == NULL ) {
1N/A LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A
1N/A if (sb->sb_sasl_ctx) {
1N/A sasl_dispose(&sb->sb_sasl_ctx);
1N/A sb->sb_sasl_ctx = NULL;
1N/A }
1N/A
1N/A /* SASL is not properly initialized */
1N/A mutex_lock(&sasl_mutex);
1N/A if (gctx == NULL) {
1N/A LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
1N/A mutex_unlock(&sasl_mutex);
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A
1N/A saslrc = _sasl_client_new(gctx, "ldap", host,
1N/A NULL, NULL, /* iplocal ipremote strings currently unused */
1N/A NULL, 0, &ctx );
1N/A
1N/A if ( saslrc != SASL_OK ) {
1N/A mutex_unlock(&sasl_mutex);
1N/A sasl_dispose(&ctx);
1N/A return( nsldapi_sasl_cvterrno( ld, saslrc, NULL ) );
1N/A }
1N/A
1N/A sb->sb_sasl_ctx = (void *)ctx;
1N/A mutex_unlock(&sasl_mutex);
1N/A
1N/A return( LDAP_SUCCESS );
1N/A}
1N/A
1N/Astatic void
1N/Adestroy_sasliobuf(Sockbuf *sb)
1N/A{
1N/A if (sb != NULL && sb->sb_sasl_ibuf != NULL) {
1N/A NSLDAPI_FREE(sb->sb_sasl_ibuf);
1N/A sb->sb_sasl_ibuf = NULL;
1N/A sb->sb_sasl_iptr = NULL;
1N/A sb->sb_sasl_bfsz = 0;
1N/A sb->sb_sasl_ilen = 0;
1N/A }
1N/A}
1N/A
1N/Astatic int
1N/Ansldapi_sasl_close( LDAP *ld, Sockbuf *sb )
1N/A{
1N/A sasl_conn_t *ctx = (sasl_conn_t *)sb->sb_sasl_ctx;
1N/A
1N/A destroy_sasliobuf(sb);
1N/A
1N/A if( ctx != NULL ) {
1N/A sasl_dispose( &ctx );
1N/A sb->sb_sasl_ctx = NULL;
1N/A }
1N/A return( LDAP_SUCCESS );
1N/A}
1N/A
1N/Astatic int
1N/Ansldapi_sasl_do_bind( LDAP *ld, const char *dn,
1N/A const char *mechs, unsigned flags,
1N/A LDAP_SASL_INTERACT_PROC *callback, void *defaults,
1N/A LDAPControl **sctrl, LDAPControl **cctrl )
1N/A{
1N/A sasl_interact_t *prompts = NULL;
1N/A sasl_conn_t *ctx;
1N/A sasl_ssf_t *ssf = NULL;
1N/A const char *mech = NULL;
1N/A int saslrc, rc;
1N/A struct berval ccred;
1N/A unsigned credlen;
1N/A int stepnum = 1;
1N/A char *sasl_username = NULL;
1N/A
1N/A if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) {
1N/A LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
1N/A return( LDAP_NOT_SUPPORTED );
1N/A }
1N/A
1N/A /* shouldn't happen */
1N/A if (callback == NULL) {
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A
1N/A if ( ld->ld_defconn == NULL ||
1N/A ld->ld_defconn->lconn_status != LDAP_CONNST_CONNECTED) {
1N/A rc = nsldapi_open_ldap_defconn( ld );
1N/A if( rc < 0 ) {
1N/A return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
1N/A }
1N/A }
1N/A
1N/A /* should have a valid ld connection - now create sasl connection */
1N/A if ((rc = nsldapi_sasl_open(ld)) != LDAP_SUCCESS) {
1N/A LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
1N/A return( rc );
1N/A }
1N/A
1N/A /* expect context to be initialized when connection is open */
1N/A ctx = (sasl_conn_t *)ld->ld_defconn->lconn_sb->sb_sasl_ctx;
1N/A
1N/A if( ctx == NULL ) {
1N/A LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A
1N/A /* (re)set security properties */
1N/A sasl_setprop( ctx, SASL_SEC_PROPS, &ld->ld_sasl_secprops );
1N/A
1N/A ccred.bv_val = NULL;
1N/A ccred.bv_len = 0;
1N/A
1N/A LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n",
1N/A (mech ? mech : ""), 0, 0 );
1N/A
1N/A do {
1N/A saslrc = sasl_client_start( ctx,
1N/A mechs,
1N/A &prompts,
1N/A (const char **)&ccred.bv_val,
1N/A &credlen,
1N/A &mech );
1N/A
1N/A LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of client start for SASL/%s authentication\n",
1N/A stepnum, (mech ? mech : ""), 0 );
1N/A stepnum++;
1N/A
1N/A if( saslrc == SASL_INTERACT &&
1N/A (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) {
1N/A break;
1N/A }
1N/A } while ( saslrc == SASL_INTERACT );
1N/A
1N/A ccred.bv_len = credlen;
1N/A
1N/A if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
1N/A return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
1N/A }
1N/A
1N/A stepnum = 1;
1N/A
1N/A do {
1N/A struct berval *scred;
1N/A int clientstepnum = 1;
1N/A
1N/A scred = NULL;
1N/A
1N/A LDAPDebug(LDAP_DEBUG_TRACE, "Doing step %d of bind for SASL/%s authentication\n",
1N/A stepnum, (mech ? mech : ""), 0 );
1N/A stepnum++;
1N/A
1N/A /* notify server of a sasl bind step */
1N/A rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
1N/A sctrl, cctrl, &scred);
1N/A
1N/A if ( ccred.bv_val != NULL ) {
1N/A ccred.bv_val = NULL;
1N/A }
1N/A
1N/A if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
1N/A ber_bvfree( scred );
1N/A return( rc );
1N/A }
1N/A
1N/A if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
1N/A /* we're done, no need to step */
1N/A if( scred ) {
1N/A if (scred->bv_len == 0 ) { /* MS AD sends back empty screds */
1N/A LDAPDebug(LDAP_DEBUG_ANY,
1N/A "SASL BIND complete - ignoring empty credential response\n",
1N/A 0, 0, 0);
1N/A ber_bvfree( scred );
1N/A } else {
1N/A /* but server provided us with data! */
1N/A LDAPDebug(LDAP_DEBUG_TRACE,
1N/A "SASL BIND complete but invalid because server responded with credentials - length [%u]\n",
1N/A scred->bv_len, 0, 0);
1N/A ber_bvfree( scred );
1N/A LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR,
1N/A NULL, nsldapi_strdup( dgettext(TEXT_DOMAIN,
1N/A "Error during SASL handshake - "
1N/A "invalid server credential response") ));
1N/A return( LDAP_LOCAL_ERROR );
1N/A }
1N/A }
1N/A break;
1N/A }
1N/A
1N/A /* perform the next step of the sasl bind */
1N/A do {
1N/A LDAPDebug(LDAP_DEBUG_TRACE, "Doing client step %d of bind step %d for SASL/%s authentication\n",
1N/A clientstepnum, stepnum, (mech ? mech : "") );
1N/A clientstepnum++;
1N/A saslrc = sasl_client_step( ctx,
1N/A (scred == NULL) ? NULL : scred->bv_val,
1N/A (scred == NULL) ? 0 : scred->bv_len,
1N/A &prompts,
1N/A (const char **)&ccred.bv_val,
1N/A &credlen );
1N/A
1N/A if( saslrc == SASL_INTERACT &&
1N/A (callback)(ld, flags, defaults, prompts)
1N/A != LDAP_SUCCESS ) {
1N/A break;
1N/A }
1N/A } while ( saslrc == SASL_INTERACT );
1N/A
1N/A ccred.bv_len = credlen;
1N/A ber_bvfree( scred );
1N/A
1N/A if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
1N/A return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
1N/A }
1N/A } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
1N/A
1N/A if ( rc != LDAP_SUCCESS ) {
1N/A return( rc );
1N/A }
1N/A
1N/A if ( saslrc != SASL_OK ) {
1N/A return( nsldapi_sasl_cvterrno( ld, saslrc, nsldapi_strdup( sasl_errdetail( ctx ) ) ) );
1N/A }
1N/A
1N/A saslrc = sasl_getprop( ctx, SASL_USERNAME, (const void **) &sasl_username );
1N/A if ( (saslrc == SASL_OK) && sasl_username ) {
1N/A LDAPDebug(LDAP_DEBUG_TRACE, "SASL identity: %s\n", sasl_username, 0, 0);
1N/A }
1N/A
1N/A saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf );
1N/A if( saslrc == SASL_OK ) {
1N/A if( ssf && *ssf ) {
1N/A LDAPDebug(LDAP_DEBUG_TRACE,
1N/A "SASL install encryption, for SSF: %lu\n",
1N/A (unsigned long) *ssf, 0, 0 );
1N/A }
1N/A rc = nsldapi_sasl_install(ld, ld->ld_conns->lconn_sb, ctx, ssf);
1N/A }
1N/A
1N/A return( rc );
1N/A}
1N/A
1N/A#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
1N/A/*
1N/A * Get available SASL Mechanisms supported by the server
1N/A */
1N/A
1N/Astatic int
1N/Ansldapi_get_sasl_mechs ( LDAP *ld, char **pmech )
1N/A{
1N/A char *attr[] = { "supportedSASLMechanisms", NULL };
1N/A char **values, **v, *mech, *m;
1N/A LDAPMessage *res, *e;
1N/A struct timeval timeout;
1N/A int slen, rc;
1N/A
1N/A if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
1N/A return( LDAP_PARAM_ERROR );
1N/A }
1N/A
1N/A timeout.tv_sec = SEARCH_TIMEOUT_SECS;
1N/A timeout.tv_usec = 0;
1N/A
1N/A rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE,
1N/A "objectclass=*", attr, 0, &timeout, &res );
1N/A
1N/A if ( rc != LDAP_SUCCESS ) {
1N/A return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
1N/A }
1N/A
1N/A e = ldap_first_entry( ld, res );
1N/A if ( e == NULL ) {
1N/A ldap_msgfree( res );
1N/A if ( ld->ld_errno == LDAP_SUCCESS ) {
1N/A LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_OBJECT, NULL, NULL );
1N/A }
1N/A return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
1N/A }
1N/A
1N/A values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
1N/A if ( values == NULL ) {
1N/A ldap_msgfree( res );
1N/A LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL );
1N/A return( LDAP_NO_SUCH_ATTRIBUTE );
1N/A }
1N/A
1N/A slen = 0;
1N/A for(v = values; *v != NULL; v++ ) {
1N/A slen += strlen(*v) + 1;
1N/A }
1N/A if ( (mech = NSLDAPI_CALLOC(1, slen)) == NULL) {
1N/A ldap_value_free( values );
1N/A ldap_msgfree( res );
1N/A LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
1N/A return( LDAP_NO_MEMORY );
1N/A }
1N/A m = mech;
1N/A for(v = values; *v; v++) {
1N/A if (v != values) {
1N/A *m++ = ' ';
1N/A }
1N/A slen = strlen(*v);
1N/A strncpy(m, *v, slen);
1N/A m += slen;
1N/A }
1N/A *m = '\0';
1N/A
1N/A ldap_value_free( values );
1N/A ldap_msgfree( res );
1N/A
1N/A *pmech = mech;
1N/A
1N/A return( LDAP_SUCCESS );
1N/A}
1N/A#endif
1N/A
1N/Aint nsldapi_sasl_secprops(
1N/A const char *in,
1N/A sasl_security_properties_t *secprops )
1N/A{
1N/A int i;
1N/A char **props = NULL;
1N/A char *inp;
1N/A unsigned sflags = 0;
1N/A sasl_ssf_t max_ssf = 0;
1N/A sasl_ssf_t min_ssf = 0;
1N/A unsigned maxbufsize = 0;
1N/A int got_sflags = 0;
1N/A int got_max_ssf = 0;
1N/A int got_min_ssf = 0;
1N/A int got_maxbufsize = 0;
1N/A
1N/A if (in == NULL) {
1N/A return LDAP_PARAM_ERROR;
1N/A }
1N/A inp = nsldapi_strdup(in);
1N/A if (inp == NULL) {
1N/A return LDAP_PARAM_ERROR;
1N/A }
1N/A props = ldap_str2charray( inp, "," );
1N/A NSLDAPI_FREE( inp );
1N/A
1N/A if( props == NULL || secprops == NULL ) {
1N/A return LDAP_PARAM_ERROR;
1N/A }
1N/A
1N/A for( i=0; props[i]; i++ ) {
1N/A if( strcasecmp(props[i], "none") == 0 ) {
1N/A got_sflags++;
1N/A
1N/A } else if( strcasecmp(props[i], "noactive") == 0 ) {
1N/A got_sflags++;
1N/A sflags |= SASL_SEC_NOACTIVE;
1N/A
1N/A } else if( strcasecmp(props[i], "noanonymous") == 0 ) {
1N/A got_sflags++;
1N/A sflags |= SASL_SEC_NOANONYMOUS;
1N/A
1N/A } else if( strcasecmp(props[i], "nodict") == 0 ) {
1N/A got_sflags++;
1N/A sflags |= SASL_SEC_NODICTIONARY;
1N/A
1N/A } else if( strcasecmp(props[i], "noplain") == 0 ) {
1N/A got_sflags++;
1N/A sflags |= SASL_SEC_NOPLAINTEXT;
1N/A
1N/A } else if( strcasecmp(props[i], "forwardsec") == 0 ) {
1N/A got_sflags++;
1N/A sflags |= SASL_SEC_FORWARD_SECRECY;
1N/A
1N/A } else if( strcasecmp(props[i], "passcred") == 0 ) {
1N/A got_sflags++;
1N/A sflags |= SASL_SEC_PASS_CREDENTIALS;
1N/A
1N/A } else if( strncasecmp(props[i],
1N/A "minssf=", sizeof("minssf")) == 0 ) {
1N/A if( isdigit( props[i][sizeof("minssf")] ) ) {
1N/A got_min_ssf++;
1N/A min_ssf = atoi( &props[i][sizeof("minssf")] );
1N/A } else {
1N/A return LDAP_NOT_SUPPORTED;
1N/A }
1N/A
1N/A } else if( strncasecmp(props[i],
1N/A "maxssf=", sizeof("maxssf")) == 0 ) {
1N/A if( isdigit( props[i][sizeof("maxssf")] ) ) {
1N/A got_max_ssf++;
1N/A max_ssf = atoi( &props[i][sizeof("maxssf")] );
1N/A } else {
1N/A return LDAP_NOT_SUPPORTED;
1N/A }
1N/A
1N/A } else if( strncasecmp(props[i],
1N/A "maxbufsize=", sizeof("maxbufsize")) == 0 ) {
1N/A if( isdigit( props[i][sizeof("maxbufsize")] ) ) {
1N/A got_maxbufsize++;
1N/A maxbufsize = atoi( &props[i][sizeof("maxbufsize")] );
1N/A if( maxbufsize &&
1N/A (( maxbufsize < SASL_MIN_BUFF_SIZE )
1N/A || (maxbufsize > SASL_MAX_BUFF_SIZE ))) {
1N/A return( LDAP_PARAM_ERROR );
1N/A }
1N/A } else {
1N/A return( LDAP_NOT_SUPPORTED );
1N/A }
1N/A } else {
1N/A return( LDAP_NOT_SUPPORTED );
1N/A }
1N/A }
1N/A
1N/A if(got_sflags) {
1N/A secprops->security_flags = sflags;
1N/A }
1N/A if(got_min_ssf) {
1N/A secprops->min_ssf = min_ssf;
1N/A }
1N/A if(got_max_ssf) {
1N/A secprops->max_ssf = max_ssf;
1N/A }
1N/A if(got_maxbufsize) {
1N/A secprops->maxbufsize = maxbufsize;
1N/A }
1N/A
1N/A ldap_charray_free( props );
1N/A return( LDAP_SUCCESS );
1N/A}
1N/A
1N/A/*
1N/A * SASL Authentication Interface: ldap_sasl_interactive_bind_s
1N/A *
1N/A * This routine takes a DN, SASL mech list, and a SASL callback
1N/A * and performs the necessary sequencing to complete a SASL bind
1N/A * to the LDAP connection ld. The user provided callback can
1N/A * use an optionally provided set of default values to complete
1N/A * any necessary interactions.
1N/A *
1N/A * Currently inpose the following restrictions:
1N/A * A mech list must be provided, only LDAP_SASL_INTERACTIVE
1N/A * mode is supported
1N/A */
1N/Aint
1N/ALDAP_CALL
1N/Aldap_sasl_interactive_bind_s( LDAP *ld, const char *dn,
1N/A const char *saslMechanism,
1N/A LDAPControl **sctrl, LDAPControl **cctrl, unsigned flags,
1N/A LDAP_SASL_INTERACT_PROC *callback, void *defaults )
1N/A{
1N/A#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
1N/A char *smechs;
1N/A#endif
1N/A int rc;
1N/A
1N/A LDAPDebug(LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0);
1N/A
1N/A if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
1N/A return( LDAP_PARAM_ERROR );
1N/A }
1N/A
1N/A if (flags != LDAP_SASL_INTERACTIVE || callback == NULL) {
1N/A return( LDAP_PARAM_ERROR );
1N/A }
1N/A
1N/A LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK );
1N/A
1N/A if( saslMechanism == NULL || *saslMechanism == '\0' ) {
1N/A#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
1N/A rc = nsldapi_get_sasl_mechs( ld, &smechs );
1N/A if( rc != LDAP_SUCCESS ) {
1N/A LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1N/A return( rc );
1N/A }
1N/A saslMechanism = smechs;
1N/A#else
1N/A LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1N/A return( LDAP_PARAM_ERROR );
1N/A#endif
1N/A }
1N/A
1N/A /* initialize SASL library */
1N/A if ( nsldapi_sasl_init() < 0 ) {
1N/A return( LDAP_PARAM_ERROR );
1N/A }
1N/A
1N/A rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism,
1N/A flags, callback, defaults, sctrl, cctrl);
1N/A
1N/A LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
1N/A return( rc );
1N/A}
1N/A
1N/A#endif