diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/lib/libldap5/sources/ldap/common/sasl.c | |
download | illumos-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.c | 974 |
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 |