summaryrefslogtreecommitdiff
path: root/usr/src/lib/libldap5/sources/ldap/common/sasl.c
diff options
context:
space:
mode:
authorstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
committerstevel@tonic-gate <none@none>2005-06-14 00:00:00 -0700
commit7c478bd95313f5f23a4c958a745db2134aa03244 (patch)
treec871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libldap5/sources/ldap/common/sasl.c
downloadillumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz
OpenSolaris Launch
Diffstat (limited to 'usr/src/lib/libldap5/sources/ldap/common/sasl.c')
-rw-r--r--usr/src/lib/libldap5/sources/ldap/common/sasl.c974
1 files changed, 974 insertions, 0 deletions
diff --git a/usr/src/lib/libldap5/sources/ldap/common/sasl.c b/usr/src/lib/libldap5/sources/ldap/common/sasl.c
new file mode 100644
index 0000000000..8dba6d6562
--- /dev/null
+++ b/usr/src/lib/libldap5/sources/ldap/common/sasl.c
@@ -0,0 +1,974 @@
+/*
+ * Copyright 2003 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifdef LDAP_SASLIO_HOOKS
+#include <assert.h>
+#include "ldap-int.h"
+#include "../ber/lber-int.h"
+#include <sasl/sasl.h>
+#include <thread.h>
+#include <synch.h>
+
+#define SEARCH_TIMEOUT_SECS 120
+#define NSLDAPI_SM_BUF 128
+
+extern void *sasl_create_context(void);
+extern void sasl_free_context(void *ctx);
+extern int _sasl_client_init(void *ctx, const sasl_callback_t *callbacks);
+extern int _sasl_client_new(void *ctx, const char *service,
+ const char *serverFQDN, const char *iplocalport,
+ const char *ipremoteport,
+ const sasl_callback_t *prompt_supp,
+ unsigned flags, sasl_conn_t **pconn);
+extern int _sasl_server_init(void *ctx,
+ const sasl_callback_t *callbacks, const char *appname);
+extern int _sasl_server_new(void *ctx, const char *service,
+ const char *serverFQDN, const char *user_realm,
+ const char *iplocalport, const char *ipremoteport,
+ const sasl_callback_t *callbacks,
+ unsigned flags, sasl_conn_t **pconn);
+
+static int nsldapi_sasl_close( LDAP *ld, Sockbuf *sb );
+
+/*
+ * SASL Dependent routines
+ *
+ * SASL security and integrity options are supported through the
+ * use of the extended I/O functionality. Because the extended
+ * I/O functions may already be in use prior to enabling encryption,
+ * when SASL encryption is enabled, these routine interpose themselves
+ * over the existng extended I/O routines and add an additional level
+ * of indirection.
+ * IE: Before SASL: client->libldap->lber->extio
+ * After SASL: client->libldap->lber->saslio->extio
+ * Any extio functions are still used for the raw i/O [IE prldap]
+ * but SASL will decrypt before passing to lber.
+ * SASL cannot decrypt a stream so full packets must be read
+ * before proceeding.
+ */
+
+static int nsldapi_sasl_fail()
+{
+ return( SASL_FAIL );
+}
+
+/*
+ * Global SASL Init data
+ */
+
+static sasl_callback_t client_callbacks[] = {
+ { SASL_CB_GETOPT, nsldapi_sasl_fail, NULL },
+ { SASL_CB_GETREALM, NULL, NULL },
+ { SASL_CB_USER, NULL, NULL },
+ { SASL_CB_AUTHNAME, NULL, NULL },
+ { SASL_CB_PASS, NULL, NULL },
+ { SASL_CB_ECHOPROMPT, NULL, NULL },
+ { SASL_CB_NOECHOPROMPT, NULL, NULL },
+ { SASL_CB_LIST_END, NULL, NULL }
+};
+static mutex_t sasl_mutex = DEFAULTMUTEX;
+static int nsldapi_sasl_inited = 0;
+static void *gctx; /* intentially not freed - avoid libsasl re-inits */
+
+int nsldapi_sasl_init( void )
+{
+ int saslrc;
+
+ mutex_lock(&sasl_mutex);
+ if ( nsldapi_sasl_inited ) {
+ mutex_unlock(&sasl_mutex);
+ return( 0 );
+ }
+ if ((gctx = (void *)sasl_create_context()) != NULL) {
+ saslrc = _sasl_client_init(gctx, client_callbacks);
+ if (saslrc == SASL_OK ) {
+ nsldapi_sasl_inited = 1;
+ mutex_unlock(&sasl_mutex);
+ return( 0 );
+ }
+ }
+ mutex_unlock(&sasl_mutex);
+ return( -1 );
+}
+
+/*
+ * SASL encryption routines
+ */
+
+/*
+ * Get the 4 octet header [size] for a sasl encrypted buffer.
+ * See RFC222 [section 3].
+ */
+
+static int
+nsldapi_sasl_pktlen( char *buf, int maxbufsize )
+{
+ int size;
+
+ size = ntohl(*(long *)buf);
+
+ if ( size < 0 || size > maxbufsize ) {
+ return (-1 );
+ }
+
+ return( size + 4 ); /* include the first 4 bytes */
+}
+
+static int
+nsldapi_sasl_read( int s, void *buf, int len,
+ struct lextiof_socket_private *arg)
+{
+ Sockbuf *sb = (Sockbuf *)arg;
+ LDAP *ld;
+ const char *dbuf;
+ char *cp;
+ int ret;
+ unsigned dlen, blen;
+
+ if (sb == NULL) {
+ return( -1 );
+ }
+
+ ld = (LDAP *)sb->sb_sasl_prld;
+ if (ld == NULL) {
+ return( -1 );
+ }
+
+ /* Is there anything left in the existing buffer? */
+ if ((ret = sb->sb_sasl_ilen) > 0) {
+ ret = (ret > len ? len : ret);
+ SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret );
+ if (ret == sb->sb_sasl_ilen) {
+ sb->sb_sasl_ilen = 0;
+ sb->sb_sasl_iptr = NULL;
+ } else {
+ sb->sb_sasl_ilen -= ret;
+ sb->sb_sasl_iptr += ret;
+ }
+ return( ret );
+ }
+
+ /* buffer is empty - fill it */
+ cp = sb->sb_sasl_ibuf;
+ dlen = 0;
+
+ /* Read the length of the packet */
+ while ( dlen < 4 ) {
+ if (sb->sb_sasl_fns.lbextiofn_read != NULL) {
+ ret = sb->sb_sasl_fns.lbextiofn_read(
+ s, cp, 4 - dlen,
+ sb->sb_sasl_fns.lbextiofn_socket_arg);
+ } else {
+ ret = read( sb->sb_sd, cp, 4 - dlen );
+ }
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
+ continue;
+#endif
+ if ( ret <= 0 )
+ return( ret );
+
+ cp += ret;
+ dlen += ret;
+ }
+
+ blen = 4;
+
+ ret = nsldapi_sasl_pktlen( sb->sb_sasl_ibuf, sb->sb_sasl_bfsz );
+ if (ret < 0) {
+ LDAP_SET_ERRNO(ld, EIO);
+ return( -1 );
+ }
+ dlen = ret - dlen;
+
+ /* read the rest of the encrypted packet */
+ while ( dlen > 0 ) {
+ if (sb->sb_sasl_fns.lbextiofn_read != NULL) {
+ ret = sb->sb_sasl_fns.lbextiofn_read(
+ s, cp, dlen,
+ sb->sb_sasl_fns.lbextiofn_socket_arg);
+ } else {
+ ret = read( sb->sb_sd, cp, dlen );
+ }
+
+#ifdef EINTR
+ if ( ( ret < 0 ) && ( LDAP_GET_ERRNO(ld) == EINTR ) )
+ continue;
+#endif
+ if ( ret <= 0 )
+ return( ret );
+
+ cp += ret;
+ blen += ret;
+ dlen -= ret;
+ }
+
+ /* Decode the packet */
+ ret = sasl_decode( sb->sb_sasl_ctx,
+ sb->sb_sasl_ibuf, blen,
+ &dbuf, &dlen);
+ if ( ret != SASL_OK ) {
+ /* sb_sasl_read: failed to decode packet, drop it, error */
+ sb->sb_sasl_iptr = NULL;
+ sb->sb_sasl_ilen = 0;
+ LDAP_SET_ERRNO(ld, EIO);
+ return( -1 );
+ }
+
+ /* copy decrypted packet to the input buffer */
+ SAFEMEMCPY( sb->sb_sasl_ibuf, dbuf, dlen );
+ sb->sb_sasl_iptr = sb->sb_sasl_ibuf;
+ sb->sb_sasl_ilen = dlen;
+
+ ret = (dlen > (unsigned) len ? len : dlen);
+ SAFEMEMCPY( buf, sb->sb_sasl_iptr, ret );
+ if (ret == sb->sb_sasl_ilen) {
+ sb->sb_sasl_ilen = 0;
+ sb->sb_sasl_iptr = NULL;
+ } else {
+ sb->sb_sasl_ilen -= ret;
+ sb->sb_sasl_iptr += ret;
+ }
+ return( ret );
+}
+
+static int
+nsldapi_sasl_write( int s, const void *buf, int len,
+ struct lextiof_socket_private *arg)
+{
+ Sockbuf *sb = (Sockbuf *)arg;
+ int ret;
+ const char *obuf, *optr;
+ unsigned olen;
+
+ if (sb == NULL) {
+ return( -1 );
+ }
+
+ /* encode the next packet. */
+ ret = sasl_encode( sb->sb_sasl_ctx, buf, (unsigned)len, &obuf, &olen);
+ if ( ret != SASL_OK ) {
+ /* XXX Log error? "sb_sasl_write: failed to encode packet..." */
+ return( -1 );
+ }
+
+ /* Write everything now, buffer is only good until next sasl_encode */
+ optr = obuf;
+ while (olen > 0) {
+ if (sb->sb_sasl_fns.lbextiofn_write != NULL) {
+ ret = sb->sb_sasl_fns.lbextiofn_write(
+ s, optr, olen,
+ sb->sb_sasl_fns.lbextiofn_socket_arg);
+ } else {
+ ret = write( sb->sb_sd, optr, olen);
+ }
+ if ( ret < 0 )
+ return( ret );
+ optr += ret;
+ olen -= ret;
+ }
+ return( len );
+}
+
+static int
+nsldapi_sasl_poll(
+ LDAP_X_PollFD fds[], int nfds, int timeout,
+ struct lextiof_session_private *arg )
+{
+ Sockbuf *sb = (Sockbuf *)arg;
+ LDAP *ld;
+ int i;
+
+ if (sb == NULL) {
+ return( -1 );
+ }
+ ld = (LDAP *)sb->sb_sasl_prld;
+ if (ld == NULL) {
+ return( -1 );
+ }
+
+ if (fds && nfds > 0) {
+ for(i = 0; i < nfds; i++) {
+ if (fds[i].lpoll_socketarg ==
+ (struct lextiof_socket_private *)sb) {
+ fds[i].lpoll_socketarg =
+ (struct lextiof_socket_private *)
+ sb->sb_sasl_fns.lbextiofn_socket_arg;
+ }
+
+ }
+ }
+ return ( ld->ld_sasl_io_fns.lextiof_poll( fds, nfds, timeout,
+ (void *)ld->ld_sasl_io_fns.lextiof_session_arg) );
+}
+
+/* no encryption indirect routines */
+
+static int
+nsldapi_sasl_ne_read( int s, void *buf, int len,
+ struct lextiof_socket_private *arg)
+{
+ Sockbuf *sb = (Sockbuf *)arg;
+
+ if (sb == NULL) {
+ return( -1 );
+ }
+
+ return( sb->sb_sasl_fns.lbextiofn_read( s, buf, len,
+ sb->sb_sasl_fns.lbextiofn_socket_arg) );
+}
+
+static int
+nsldapi_sasl_ne_write( int s, const void *buf, int len,
+ struct lextiof_socket_private *arg)
+{
+ Sockbuf *sb = (Sockbuf *)arg;
+
+ if (sb == NULL) {
+ return( -1 );
+ }
+
+ return( sb->sb_sasl_fns.lbextiofn_write( s, buf, len,
+ sb->sb_sasl_fns.lbextiofn_socket_arg) );
+}
+
+static int
+nsldapi_sasl_close_socket(int s, struct lextiof_socket_private *arg )
+{
+ Sockbuf *sb = (Sockbuf *)arg;
+ LDAP *ld;
+
+ if (sb == NULL) {
+ return( -1 );
+ }
+ ld = (LDAP *)sb->sb_sasl_prld;
+ if (ld == NULL) {
+ return( -1 );
+ }
+ /* undo function pointer interposing */
+ ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS, &ld->ld_sasl_io_fns );
+ ber_sockbuf_set_option( sb,
+ LBER_SOCKBUF_OPT_EXT_IO_FNS,
+ (void *)&sb->sb_sasl_fns);
+
+ /* undo SASL */
+ nsldapi_sasl_close( ld, sb );
+
+ return ( ld->ld_sasl_io_fns.lextiof_close( s,
+ (struct lextiof_socket_private *)
+ sb->sb_sasl_fns.lbextiofn_socket_arg ) );
+}
+
+/*
+ * install encryption routines if security has been negotiated
+ */
+static int
+nsldapi_sasl_install( LDAP *ld, Sockbuf *sb, void *ctx_arg, sasl_ssf_t *ssf)
+{
+ struct lber_x_ext_io_fns fns;
+ struct ldap_x_ext_io_fns iofns;
+ sasl_security_properties_t *secprops;
+ int rc, value;
+ int bufsiz;
+ int encrypt = 0;
+
+ if (ssf && *ssf) {
+ encrypt = 1;
+ }
+ rc = ber_sockbuf_get_option( sb,
+ LBER_SOCKBUF_OPT_TO_FILE_ONLY,
+ (void *) &value);
+ if (rc != 0 || value != 0)
+ return( LDAP_LOCAL_ERROR );
+
+ if (encrypt) {
+ /* initialize input buffer - use MAX SIZE to avoid reallocs */
+ sb->sb_sasl_ctx = (sasl_conn_t *)ctx_arg;
+ rc = sasl_getprop( sb->sb_sasl_ctx, SASL_SEC_PROPS,
+ (const void **)&secprops );
+ if (rc != SASL_OK)
+ return( LDAP_LOCAL_ERROR );
+ bufsiz = secprops->maxbufsize;
+ if (bufsiz <= 0) {
+ return( LDAP_LOCAL_ERROR );
+ }
+ if ((sb->sb_sasl_ibuf = NSLDAPI_MALLOC(bufsiz)) == NULL) {
+ return( LDAP_LOCAL_ERROR );
+ }
+ sb->sb_sasl_iptr = NULL;
+ sb->sb_sasl_bfsz = bufsiz;
+ sb->sb_sasl_ilen = 0;
+ }
+
+ /* Reset Session then Socket Args */
+ /* Get old values */
+ (void) memset( &sb->sb_sasl_fns, 0, LBER_X_EXTIO_FNS_SIZE);
+ sb->sb_sasl_fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
+ rc = ber_sockbuf_get_option( sb,
+ LBER_SOCKBUF_OPT_EXT_IO_FNS,
+ (void *)&sb->sb_sasl_fns);
+ memset( &ld->ld_sasl_io_fns, 0, sizeof(iofns));
+ ld->ld_sasl_io_fns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
+ rc = ldap_get_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
+ &ld->ld_sasl_io_fns );
+ if (rc != 0 )
+ return( LDAP_LOCAL_ERROR );
+
+ /* Set new values */
+ if ( ld->ld_sasl_io_fns.lextiof_read != NULL ||
+ ld->ld_sasl_io_fns.lextiof_write != NULL ||
+ ld->ld_sasl_io_fns.lextiof_poll != NULL ||
+ ld->ld_sasl_io_fns.lextiof_connect != NULL ||
+ ld->ld_sasl_io_fns.lextiof_close != NULL ) {
+ memset( &iofns, 0, sizeof(iofns));
+ iofns.lextiof_size = LDAP_X_EXTIO_FNS_SIZE;
+ if (encrypt) {
+ iofns.lextiof_read = nsldapi_sasl_read;
+ iofns.lextiof_write = nsldapi_sasl_write;
+ iofns.lextiof_poll = nsldapi_sasl_poll;
+ } else {
+ iofns.lextiof_read = nsldapi_sasl_ne_read;
+ iofns.lextiof_write = nsldapi_sasl_ne_write;
+ iofns.lextiof_poll = nsldapi_sasl_poll;
+ }
+ iofns.lextiof_connect = ld->ld_sasl_io_fns.lextiof_connect;
+ iofns.lextiof_close = nsldapi_sasl_close_socket;
+ iofns.lextiof_newhandle = ld->ld_sasl_io_fns.lextiof_newhandle;
+ iofns.lextiof_disposehandle =
+ ld->ld_sasl_io_fns.lextiof_disposehandle;
+ iofns.lextiof_session_arg =
+ (void *) sb;
+ /* ld->ld_sasl_io_fns.lextiof_session_arg; */
+ rc = ldap_set_option( ld, LDAP_X_OPT_EXTIO_FN_PTRS,
+ &iofns );
+ if (rc != 0 )
+ return( LDAP_LOCAL_ERROR );
+ sb->sb_sasl_prld = (void *)ld;
+ }
+
+ if (encrypt) {
+ (void) memset( &fns, 0, LBER_X_EXTIO_FNS_SIZE);
+ fns.lbextiofn_size = LBER_X_EXTIO_FNS_SIZE;
+ fns.lbextiofn_read = nsldapi_sasl_read;
+ fns.lbextiofn_write = nsldapi_sasl_write;
+ fns.lbextiofn_socket_arg =
+ (void *) sb;
+ /* (void *)sb->sb_sasl_fns.lbextiofn_socket_arg; */
+ rc = ber_sockbuf_set_option( sb,
+ LBER_SOCKBUF_OPT_EXT_IO_FNS,
+ (void *)&fns);
+ if (rc != 0)
+ return( LDAP_LOCAL_ERROR );
+ }
+
+ return( LDAP_SUCCESS );
+}
+
+static int
+nsldapi_sasl_cvterrno( LDAP *ld, int err )
+{
+ int rc = LDAP_LOCAL_ERROR;
+
+ switch (err) {
+ case SASL_OK:
+ rc = LDAP_SUCCESS;
+ break;
+ case SASL_NOMECH:
+ rc = LDAP_AUTH_UNKNOWN;
+ break;
+ case SASL_BADSERV:
+ rc = LDAP_CONNECT_ERROR;
+ break;
+ case SASL_DISABLED:
+ case SASL_ENCRYPT:
+ case SASL_EXPIRED:
+ case SASL_NOUSERPASS:
+ case SASL_NOVERIFY:
+ case SASL_PWLOCK:
+ case SASL_TOOWEAK:
+ case SASL_UNAVAIL:
+ case SASL_WEAKPASS:
+ rc = LDAP_INAPPROPRIATE_AUTH;
+ break;
+ case SASL_BADAUTH:
+ case SASL_NOAUTHZ:
+ rc = LDAP_INVALID_CREDENTIALS;
+ break;
+ case SASL_NOMEM:
+ rc = LDAP_NO_MEMORY;
+ break;
+ case SASL_NOUSER:
+ rc = LDAP_NO_SUCH_OBJECT;
+ break;
+ case SASL_CONTINUE:
+ case SASL_FAIL:
+ case SASL_INTERACT:
+ default:
+ rc = LDAP_LOCAL_ERROR;
+ break;
+ }
+
+ LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
+ return( rc );
+}
+
+int
+nsldapi_sasl_open(LDAP *ld)
+{
+ Sockbuf *sb;
+ char * host;
+ int saslrc;
+ sasl_conn_t *ctx;
+
+ if (ld == NULL) {
+ return( LDAP_LOCAL_ERROR );
+ }
+
+ if (ld->ld_defconn == NULL) {
+ LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
+ return( LDAP_LOCAL_ERROR );
+ }
+ sb = ld->ld_defconn->lconn_sb;
+ host = ld->ld_defhost;
+
+ if ( sb == NULL || host == NULL ) {
+ LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
+ return( LDAP_LOCAL_ERROR );
+ }
+
+ /* SASL is not properly initialized */
+ mutex_lock(&sasl_mutex);
+ if (gctx == NULL) {
+ LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
+ mutex_unlock(&sasl_mutex);
+ return( LDAP_LOCAL_ERROR );
+ }
+
+ saslrc = _sasl_client_new(gctx, "ldap", host,
+ NULL, NULL, /* iplocal ipremote strings currently unused */
+ NULL, 0, &ctx );
+
+ if ( saslrc != SASL_OK ) {
+ mutex_unlock(&sasl_mutex);
+ return( nsldapi_sasl_cvterrno( ld, saslrc ) );
+ }
+
+ sb->sb_sasl_ctx = (void *)ctx;
+ mutex_unlock(&sasl_mutex);
+
+ return( LDAP_SUCCESS );
+}
+
+static int
+nsldapi_sasl_close( LDAP *ld, Sockbuf *sb )
+{
+ sasl_conn_t *ctx = (sasl_conn_t *)sb->sb_sasl_ctx;
+
+ if( ctx != NULL ) {
+ sasl_dispose( &ctx );
+ sb->sb_sasl_ctx = NULL;
+ }
+ return( LDAP_SUCCESS );
+}
+
+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 )
+{
+ sasl_interact_t *prompts = NULL;
+ sasl_conn_t *ctx;
+ sasl_ssf_t *ssf = NULL;
+ const char *mech = NULL;
+ int saslrc, rc;
+ struct berval ccred;
+ unsigned credlen;
+
+ if (NSLDAPI_LDAP_VERSION( ld ) < LDAP_VERSION3) {
+ LDAP_SET_LDERRNO( ld, LDAP_NOT_SUPPORTED, NULL, NULL );
+ return( LDAP_NOT_SUPPORTED );
+ }
+
+ /* shouldn't happen */
+ if (callback == NULL) {
+ return( LDAP_LOCAL_ERROR );
+ }
+
+ if ( ld->ld_defconn == NULL ||
+ ld->ld_defconn->lconn_status != LDAP_CONNST_CONNECTED) {
+ rc = nsldapi_open_ldap_defconn( ld );
+ if( rc < 0 ) {
+ return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
+ }
+ }
+
+ /* should have a valid ld connection - now create sasl connection */
+ if ((rc = nsldapi_sasl_open(ld)) != LDAP_SUCCESS) {
+ LDAP_SET_LDERRNO( ld, rc, NULL, NULL );
+ return( rc );
+ }
+
+ /* expect context to be initialized when connection is open */
+ ctx = (sasl_conn_t *)ld->ld_defconn->lconn_sb->sb_sasl_ctx;
+
+ if( ctx == NULL ) {
+ LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR, NULL, NULL );
+ return( LDAP_LOCAL_ERROR );
+ }
+
+ /* (re)set security properties */
+ sasl_setprop( ctx, SASL_SEC_PROPS, &ld->ld_sasl_secprops );
+
+ ccred.bv_val = NULL;
+ ccred.bv_len = 0;
+
+ do {
+ saslrc = sasl_client_start( ctx,
+ mechs,
+ &prompts,
+ (const char **)&ccred.bv_val,
+ &credlen,
+ &mech );
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "Starting SASL/%s authentication\n",
+ (mech ? mech : ""), 0, 0 );
+
+ if( saslrc == SASL_INTERACT &&
+ (callback)(ld, flags, defaults, prompts) != LDAP_SUCCESS ) {
+ break;
+ }
+ } while ( saslrc == SASL_INTERACT );
+
+ ccred.bv_len = credlen;
+
+ if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
+ return( nsldapi_sasl_cvterrno( ld, saslrc ) );
+ }
+
+ do {
+ struct berval *scred;
+
+ scred = NULL;
+
+ /* notify server of a sasl bind step */
+ rc = ldap_sasl_bind_s(ld, dn, mech, &ccred,
+ sctrl, cctrl, &scred);
+
+ if ( ccred.bv_val != NULL ) {
+ ccred.bv_val = NULL;
+ }
+
+ if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
+ if( scred && scred->bv_len ) {
+ /* and server provided us with data? */
+ ber_bvfree( scred );
+ }
+ return( rc );
+ }
+
+ if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
+ /* we're done, no need to step */
+ if( scred && scred->bv_len ) {
+ /* but server provided us with data! */
+ ber_bvfree( scred );
+ LDAP_SET_LDERRNO( ld, LDAP_LOCAL_ERROR,
+ NULL, NULL );
+ return( LDAP_LOCAL_ERROR );
+ }
+ break;
+ }
+
+ /* perform the next step of the sasl bind */
+ do {
+ saslrc = sasl_client_step( ctx,
+ (scred == NULL) ? NULL : scred->bv_val,
+ (scred == NULL) ? 0 : scred->bv_len,
+ &prompts,
+ (const char **)&ccred.bv_val,
+ &credlen );
+
+ if( saslrc == SASL_INTERACT &&
+ (callback)(ld, flags, defaults, prompts)
+ != LDAP_SUCCESS ) {
+ break;
+ }
+ } while ( saslrc == SASL_INTERACT );
+
+ ccred.bv_len = credlen;
+ ber_bvfree( scred );
+
+ if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) {
+ return( nsldapi_sasl_cvterrno( ld, saslrc ) );
+ }
+ } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( rc );
+ }
+
+ if ( saslrc != SASL_OK ) {
+ return( nsldapi_sasl_cvterrno( ld, saslrc ) );
+ }
+
+ saslrc = sasl_getprop( ctx, SASL_SSF, (const void **) &ssf );
+ if( saslrc == SASL_OK ) {
+ rc = nsldapi_sasl_install(ld, ld->ld_conns->lconn_sb, ctx, ssf);
+ }
+
+ return( rc );
+}
+
+#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
+/*
+ * Get available SASL Mechanisms supported by the server
+ */
+
+static int
+nsldapi_get_sasl_mechs ( LDAP *ld, char **pmech )
+{
+ char *attr[] = { "supportedSASLMechanisms", NULL };
+ char **values, **v, *mech, *m;
+ LDAPMessage *res, *e;
+ struct timeval timeout;
+ int slen, rc;
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ timeout.tv_sec = SEARCH_TIMEOUT_SECS;
+ timeout.tv_usec = 0;
+
+ rc = ldap_search_st( ld, "", LDAP_SCOPE_BASE,
+ "objectclass=*", attr, 0, &timeout, &res );
+
+ if ( rc != LDAP_SUCCESS ) {
+ return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
+ }
+
+ e = ldap_first_entry( ld, res );
+ if ( e == NULL ) {
+ ldap_msgfree( res );
+ if ( ld->ld_errno == LDAP_SUCCESS ) {
+ LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_OBJECT, NULL, NULL );
+ }
+ return( LDAP_GET_LDERRNO( ld, NULL, NULL ) );
+ }
+
+ values = ldap_get_values( ld, e, "supportedSASLMechanisms" );
+ if ( values == NULL ) {
+ ldap_msgfree( res );
+ LDAP_SET_LDERRNO( ld, LDAP_NO_SUCH_ATTRIBUTE, NULL, NULL );
+ return( LDAP_NO_SUCH_ATTRIBUTE );
+ }
+
+ slen = 0;
+ for(v = values; *v != NULL; v++ ) {
+ slen += strlen(*v) + 1;
+ }
+ if ( (mech = NSLDAPI_CALLOC(1, slen)) == NULL) {
+ ldap_value_free( values );
+ ldap_msgfree( res );
+ LDAP_SET_LDERRNO( ld, LDAP_NO_MEMORY, NULL, NULL );
+ return( LDAP_NO_MEMORY );
+ }
+ m = mech;
+ for(v = values; *v; v++) {
+ if (v != values) {
+ *m++ = ' ';
+ }
+ slen = strlen(*v);
+ strncpy(m, *v, slen);
+ m += slen;
+ }
+ *m = '\0';
+
+ ldap_value_free( values );
+ ldap_msgfree( res );
+
+ *pmech = mech;
+
+ return( LDAP_SUCCESS );
+}
+#endif
+
+int nsldapi_sasl_secprops(
+ const char *in,
+ sasl_security_properties_t *secprops )
+{
+ int i;
+ char **props = NULL;
+ char *inp;
+ unsigned sflags = 0;
+ sasl_ssf_t max_ssf = 0;
+ sasl_ssf_t min_ssf = 0;
+ unsigned maxbufsize = 0;
+ int got_sflags = 0;
+ int got_max_ssf = 0;
+ int got_min_ssf = 0;
+ int got_maxbufsize = 0;
+
+ if (in == NULL) {
+ return LDAP_PARAM_ERROR;
+ }
+ inp = nsldapi_strdup(in);
+ if (inp == NULL) {
+ return LDAP_PARAM_ERROR;
+ }
+ props = ldap_str2charray( inp, "," );
+ NSLDAPI_FREE( inp );
+
+ if( props == NULL || secprops == NULL ) {
+ return LDAP_PARAM_ERROR;
+ }
+
+ for( i=0; props[i]; i++ ) {
+ if( strcasecmp(props[i], "none") == 0 ) {
+ got_sflags++;
+
+ } else if( strcasecmp(props[i], "noactive") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_NOACTIVE;
+
+ } else if( strcasecmp(props[i], "noanonymous") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_NOANONYMOUS;
+
+ } else if( strcasecmp(props[i], "nodict") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_NODICTIONARY;
+
+ } else if( strcasecmp(props[i], "noplain") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_NOPLAINTEXT;
+
+ } else if( strcasecmp(props[i], "forwardsec") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_FORWARD_SECRECY;
+
+ } else if( strcasecmp(props[i], "passcred") == 0 ) {
+ got_sflags++;
+ sflags |= SASL_SEC_PASS_CREDENTIALS;
+
+ } else if( strncasecmp(props[i],
+ "minssf=", sizeof("minssf")) == 0 ) {
+ if( isdigit( props[i][sizeof("minssf")] ) ) {
+ got_min_ssf++;
+ min_ssf = atoi( &props[i][sizeof("minssf")] );
+ } else {
+ return LDAP_NOT_SUPPORTED;
+ }
+
+ } else if( strncasecmp(props[i],
+ "maxssf=", sizeof("maxssf")) == 0 ) {
+ if( isdigit( props[i][sizeof("maxssf")] ) ) {
+ got_max_ssf++;
+ max_ssf = atoi( &props[i][sizeof("maxssf")] );
+ } else {
+ return LDAP_NOT_SUPPORTED;
+ }
+
+ } else if( strncasecmp(props[i],
+ "maxbufsize=", sizeof("maxbufsize")) == 0 ) {
+ if( isdigit( props[i][sizeof("maxbufsize")] ) ) {
+ got_maxbufsize++;
+ maxbufsize = atoi( &props[i][sizeof("maxbufsize")] );
+ if( maxbufsize &&
+ (( maxbufsize < SASL_MIN_BUFF_SIZE )
+ || (maxbufsize > SASL_MAX_BUFF_SIZE ))) {
+ return( LDAP_PARAM_ERROR );
+ }
+ } else {
+ return( LDAP_NOT_SUPPORTED );
+ }
+ } else {
+ return( LDAP_NOT_SUPPORTED );
+ }
+ }
+
+ if(got_sflags) {
+ secprops->security_flags = sflags;
+ }
+ if(got_min_ssf) {
+ secprops->min_ssf = min_ssf;
+ }
+ if(got_max_ssf) {
+ secprops->max_ssf = max_ssf;
+ }
+ if(got_maxbufsize) {
+ secprops->maxbufsize = maxbufsize;
+ }
+
+ ldap_charray_free( props );
+ return( LDAP_SUCCESS );
+}
+
+/*
+ * SASL Authentication Interface: ldap_sasl_interactive_bind_s
+ *
+ * This routine takes a DN, SASL mech list, and a SASL callback
+ * and performs the necessary sequencing to complete a SASL bind
+ * to the LDAP connection ld. The user provided callback can
+ * use an optionally provided set of default values to complete
+ * any necessary interactions.
+ *
+ * Currently inpose the following restrictions:
+ * A mech list must be provided, only LDAP_SASL_INTERACTIVE
+ * mode is supported
+ */
+int
+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 )
+{
+#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
+ char *smechs;
+#endif
+ int rc;
+
+ LDAPDebug(LDAP_DEBUG_TRACE, "ldap_sasl_interactive_bind_s\n", 0, 0, 0);
+
+ if ( !NSLDAPI_VALID_LDAP_POINTER( ld )) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ if (flags != LDAP_SASL_INTERACTIVE || callback == NULL) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ LDAP_MUTEX_LOCK(ld, LDAP_SASL_LOCK );
+
+ if( saslMechanism == NULL || *saslMechanism == '\0' ) {
+#ifdef LDAP_SASLIO_GET_MECHS_FROM_SERVER
+ rc = nsldapi_get_sasl_mechs( ld, &smechs );
+ if( rc != LDAP_SUCCESS ) {
+ LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
+ return( rc );
+ }
+ saslMechanism = smechs;
+#else
+ LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
+ return( LDAP_PARAM_ERROR );
+#endif
+ }
+
+ /* initialize SASL library */
+ if ( nsldapi_sasl_init() < 0 ) {
+ return( LDAP_PARAM_ERROR );
+ }
+
+ rc = nsldapi_sasl_do_bind( ld, dn, saslMechanism,
+ flags, callback, defaults, sctrl, cctrl);
+
+ LDAP_MUTEX_UNLOCK(ld, LDAP_SASL_LOCK );
+ return( rc );
+}
+
+#endif