sasl.c revision 0c71ee58938636c3d3ad26d13891ca5d84934f03
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2009 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#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;
#if defined( _WINDOWS ) || defined( _WIN32 )
size = ntohl(*(long *)buf);
#else
size = ntohl(*(uint32_t *)buf);
#endif
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 void
destroy_sasliobuf(Sockbuf *sb)
{
if (sb != NULL && sb->sb_sasl_ibuf != NULL) {
NSLDAPI_FREE(sb->sb_sasl_ibuf);
sb->sb_sasl_ibuf = NULL;
}
}
static int
nsldapi_sasl_close( LDAP *ld, Sockbuf *sb )
{
sasl_conn_t *ctx = (sasl_conn_t *)sb->sb_sasl_ctx;
destroy_sasliobuf(sb);
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