X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=libraries%2Flibldap%2Fcyrus.c;h=0f635b2aad676daa7d3fcc221d7849f542aeeabb;hb=a5cad3f18a4b8fbbbb1bfc5db9d00022d9ebbfc4;hp=3585f7538de0ce8864c77ebbbd57bd64fec8dec4;hpb=3fb0648079b3027a0d9a652739534a89ee65f2fe;p=openldap diff --git a/libraries/libldap/cyrus.c b/libraries/libldap/cyrus.c index 3585f7538d..0f635b2aad 100644 --- a/libraries/libldap/cyrus.c +++ b/libraries/libldap/cyrus.c @@ -1,7 +1,7 @@ /* $OpenLDAP$ */ /* This work is part of OpenLDAP Software . * - * Copyright 1998-2004 The OpenLDAP Foundation. + * Copyright 1998-2010 The OpenLDAP Foundation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,10 +25,22 @@ #include #include +#ifdef HAVE_LIMITS_H +#include +#endif + #include "ldap-int.h" #ifdef HAVE_CYRUS_SASL +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifndef INT_MAX +#define INT_MAX 2147483647 /* 32 bit signed max */ +#endif + #ifdef LDAP_R_COMPILE ldap_pvt_thread_mutex_t ldap_int_sasl_mutex; #endif @@ -80,17 +92,10 @@ int ldap_int_sasl_init( void ) sprintf( version, "%u.%d.%d", (unsigned)rc >> 24, (rc >> 16) & 0xff, rc & 0xffff ); -#ifdef NEW_LOGGING - LDAP_LOG( TRANSPORT, INFO, - "ldap_int_sasl_init: SASL library version mismatch:" - " expected " SASL_VERSION_STRING "," - " got %s\n", version, 0, 0 ); -#else Debug( LDAP_DEBUG_ANY, "ldap_int_sasl_init: SASL library version mismatch:" " expected " SASL_VERSION_STRING "," " got %s\n", version, 0, 0 ); -#endif return -1; } } @@ -128,324 +133,128 @@ int ldap_int_sasl_init( void ) return -1; } -/* - * SASL encryption support for LBER Sockbufs - */ - -struct sb_sasl_data { - sasl_conn_t *sasl_context; - unsigned *sasl_maxbuf; - Sockbuf_Buf sec_buf_in; - Sockbuf_Buf buf_in; - Sockbuf_Buf buf_out; -}; - -static int -sb_sasl_setup( Sockbuf_IO_Desc *sbiod, void *arg ) +static void +sb_sasl_cyrus_init( + struct sb_sasl_generic_data *p, + ber_len_t *min_send, + ber_len_t *max_send, + ber_len_t *max_recv) { - struct sb_sasl_data *p; + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_len_t maxbuf; - assert( sbiod != NULL ); - - p = LBER_MALLOC( sizeof( *p ) ); - if ( p == NULL ) - return -1; - p->sasl_context = (sasl_conn_t *)arg; - ber_pvt_sb_buf_init( &p->sec_buf_in ); - ber_pvt_sb_buf_init( &p->buf_in ); - ber_pvt_sb_buf_init( &p->buf_out ); - if ( ber_pvt_sb_grow_buffer( &p->sec_buf_in, SASL_MIN_BUFF_SIZE ) < 0 ) { - LBER_FREE( p ); - errno = ENOMEM; - return -1; - } - sasl_getprop( p->sasl_context, SASL_MAXOUTBUF, - (SASL_CONST void **) &p->sasl_maxbuf ); - - sbiod->sbiod_pvt = p; + sasl_getprop( sasl_context, SASL_MAXOUTBUF, + (SASL_CONST void **)(char *) &maxbuf ); - return 0; + *min_send = SASL_MIN_BUFF_SIZE; + *max_send = maxbuf; + *max_recv = SASL_MAX_BUFF_SIZE; } -static int -sb_sasl_remove( Sockbuf_IO_Desc *sbiod ) +static ber_int_t +sb_sasl_cyrus_encode( + struct sb_sasl_generic_data *p, + unsigned char *buf, + ber_len_t len, + Sockbuf_Buf *dst) { - struct sb_sasl_data *p; - - assert( sbiod != NULL ); - - p = (struct sb_sasl_data *)sbiod->sbiod_pvt; -#if SASL_VERSION_MAJOR >= 2 - /* - * SASLv2 encode/decode buffers are managed by - * libsasl2. Ensure they are not freed by liblber. - */ - p->buf_in.buf_base = NULL; - p->buf_out.buf_base = NULL; -#endif - ber_pvt_sb_buf_destroy( &p->sec_buf_in ); - ber_pvt_sb_buf_destroy( &p->buf_in ); - ber_pvt_sb_buf_destroy( &p->buf_out ); - LBER_FREE( p ); - sbiod->sbiod_pvt = NULL; - return 0; -} + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_int_t ret; + unsigned tmpsize = dst->buf_size; -static ber_len_t -sb_sasl_pkt_length( const unsigned char *buf, int debuglevel ) -{ - ber_len_t size; + ret = sasl_encode( sasl_context, (char *)buf, len, + (SASL_CONST char **)&dst->buf_base, + &tmpsize ); - assert( buf != NULL ); + dst->buf_size = tmpsize; + dst->buf_end = dst->buf_size; - size = buf[0] << 24 - | buf[1] << 16 - | buf[2] << 8 - | buf[3]; - - if ( size > SASL_MAX_BUFF_SIZE ) { - /* somebody is trying to mess me up. */ - ber_log_printf( LDAP_DEBUG_ANY, debuglevel, - "sb_sasl_pkt_length: received illegal packet length " - "of %lu bytes\n", (unsigned long)size ); - size = 16; /* this should lead to an error. */ + if ( ret != SASL_OK ) { + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_cyrus_encode: failed to encode packet: %s\n", + sasl_errstring( ret, NULL, NULL ) ); + return -1; } - return size + 4; /* include the size !!! */ -} - -/* Drop a processed packet from the input buffer */ -static void -sb_sasl_drop_packet ( Sockbuf_Buf *sec_buf_in, int debuglevel ) -{ - ber_slen_t len; - - len = sec_buf_in->buf_ptr - sec_buf_in->buf_end; - if ( len > 0 ) - AC_MEMCPY( sec_buf_in->buf_base, sec_buf_in->buf_base + - sec_buf_in->buf_end, len ); - - if ( len >= 4 ) { - sec_buf_in->buf_end = sb_sasl_pkt_length( - (unsigned char *) sec_buf_in->buf_base, debuglevel); - } - else { - sec_buf_in->buf_end = 0; - } - sec_buf_in->buf_ptr = len; + return 0; } -static ber_slen_t -sb_sasl_read( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +static ber_int_t +sb_sasl_cyrus_decode( + struct sb_sasl_generic_data *p, + const Sockbuf_Buf *src, + Sockbuf_Buf *dst) { - struct sb_sasl_data *p; - ber_slen_t ret, bufptr; - - assert( sbiod != NULL ); - assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); - - p = (struct sb_sasl_data *)sbiod->sbiod_pvt; - - /* Are there anything left in the buffer? */ - ret = ber_pvt_sb_copy_out( &p->buf_in, buf, len ); - bufptr = ret; - len -= ret; + sasl_conn_t *sasl_context = (sasl_conn_t *)p->ops_private; + ber_int_t ret; + unsigned tmpsize = dst->buf_size; - if ( len == 0 ) - return bufptr; + ret = sasl_decode( sasl_context, + src->buf_base, src->buf_end, + (SASL_CONST char **)&dst->buf_base, + (unsigned *)&tmpsize ); -#if SASL_VERSION_MAJOR >= 2 - ber_pvt_sb_buf_init( &p->buf_in ); -#else - ber_pvt_sb_buf_destroy( &p->buf_in ); -#endif - /* Read the length of the packet */ - while ( p->sec_buf_in.buf_ptr < 4 ) { - ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base + - p->sec_buf_in.buf_ptr, - 4 - p->sec_buf_in.buf_ptr ); -#ifdef EINTR - if ( ( ret < 0 ) && ( errno == EINTR ) ) - continue; -#endif - if ( ret <= 0 ) - return bufptr ? bufptr : ret; - - p->sec_buf_in.buf_ptr += ret; - } - - /* The new packet always starts at p->sec_buf_in.buf_base */ - ret = sb_sasl_pkt_length( (unsigned char *) p->sec_buf_in.buf_base, - sbiod->sbiod_sb->sb_debug ); - - /* Grow the packet buffer if neccessary */ - if ( ( p->sec_buf_in.buf_size < (ber_len_t) ret ) && - ber_pvt_sb_grow_buffer( &p->sec_buf_in, ret ) < 0 ) - { - errno = ENOMEM; - return -1; - } - p->sec_buf_in.buf_end = ret; - - /* Did we read the whole encrypted packet? */ - while ( p->sec_buf_in.buf_ptr < p->sec_buf_in.buf_end ) { - /* No, we have got only a part of it */ - ret = p->sec_buf_in.buf_end - p->sec_buf_in.buf_ptr; - - ret = LBER_SBIOD_READ_NEXT( sbiod, p->sec_buf_in.buf_base + - p->sec_buf_in.buf_ptr, ret ); -#ifdef EINTR - if ( ( ret < 0 ) && ( errno == EINTR ) ) - continue; -#endif - if ( ret <= 0 ) - return bufptr ? bufptr : ret; - - p->sec_buf_in.buf_ptr += ret; - } - - /* Decode the packet */ - { - unsigned tmpsize = p->buf_in.buf_end; - ret = sasl_decode( p->sasl_context, p->sec_buf_in.buf_base, - p->sec_buf_in.buf_end, - (SASL_CONST char **)&p->buf_in.buf_base, - (unsigned *)&tmpsize ); - p->buf_in.buf_end = tmpsize; - } - - /* Drop the packet from the input buffer */ - sb_sasl_drop_packet( &p->sec_buf_in, sbiod->sbiod_sb->sb_debug ); + dst->buf_size = tmpsize; + dst->buf_end = dst->buf_size; if ( ret != SASL_OK ) { - ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug, - "sb_sasl_read: failed to decode packet: %s\n", - sasl_errstring( ret, NULL, NULL ) ); - errno = EIO; + ber_log_printf( LDAP_DEBUG_ANY, p->sbiod->sbiod_sb->sb_debug, + "sb_sasl_cyrus_decode: failed to decode packet: %s\n", + sasl_errstring( ret, NULL, NULL ) ); return -1; } - - p->buf_in.buf_size = p->buf_in.buf_end; - bufptr += ber_pvt_sb_copy_out( &p->buf_in, (char*) buf + bufptr, len ); - - return bufptr; + return 0; } -static ber_slen_t -sb_sasl_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len) +static void +sb_sasl_cyrus_reset_buf( + struct sb_sasl_generic_data *p, + Sockbuf_Buf *buf) { - struct sb_sasl_data *p; - int ret; - - assert( sbiod != NULL ); - assert( SOCKBUF_VALID( sbiod->sbiod_sb ) ); - - p = (struct sb_sasl_data *)sbiod->sbiod_pvt; - - /* Are there anything left in the buffer? */ - if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) { - ret = ber_pvt_sb_do_write( sbiod, &p->buf_out ); - if ( ret < 0 ) - return ret; - /* Still have something left?? */ - if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) { - errno = EAGAIN; - return 0; - } - } - - /* now encode the next packet. */ #if SASL_VERSION_MAJOR >= 2 - ber_pvt_sb_buf_init( &p->buf_out ); + ber_pvt_sb_buf_init( buf ); #else - ber_pvt_sb_buf_destroy( &p->buf_out ); + ber_pvt_sb_buf_destroy( buf ); #endif - if ( len > *p->sasl_maxbuf - 100 ) { - len = *p->sasl_maxbuf - 100; /* For safety margin */ - } - - { - unsigned tmpsize = p->buf_out.buf_size; - ret = sasl_encode( p->sasl_context, buf, len, - (SASL_CONST char **)&p->buf_out.buf_base, - &tmpsize ); - p->buf_out.buf_size = tmpsize; - } - - if ( ret != SASL_OK ) { - ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug, - "sb_sasl_write: failed to encode packet: %s\n", - sasl_errstring( ret, NULL, NULL ) ); - return -1; - } - p->buf_out.buf_end = p->buf_out.buf_size; - - ret = ber_pvt_sb_do_write( sbiod, &p->buf_out ); - if ( ret <= 0 ) { - return ret; - } - return len; } -static int -sb_sasl_ctrl( Sockbuf_IO_Desc *sbiod, int opt, void *arg ) +static void +sb_sasl_cyrus_fini( + struct sb_sasl_generic_data *p) { - struct sb_sasl_data *p; - - p = (struct sb_sasl_data *)sbiod->sbiod_pvt; - - if ( opt == LBER_SB_OPT_DATA_READY ) { - if ( p->buf_in.buf_ptr != p->buf_in.buf_end ) - return 1; - } - - return LBER_SBIOD_CTRL_NEXT( sbiod, opt, arg ); +#if SASL_VERSION_MAJOR >= 2 + /* + * SASLv2 encode/decode buffers are managed by + * libsasl2. Ensure they are not freed by liblber. + */ + p->buf_in.buf_base = NULL; + p->buf_out.buf_base = NULL; +#endif } -Sockbuf_IO ldap_pvt_sockbuf_io_sasl = { - sb_sasl_setup, /* sbi_setup */ - sb_sasl_remove, /* sbi_remove */ - sb_sasl_ctrl, /* sbi_ctrl */ - sb_sasl_read, /* sbi_read */ - sb_sasl_write, /* sbi_write */ - NULL /* sbi_close */ -}; +static const struct sb_sasl_generic_ops sb_sasl_cyrus_ops = { + sb_sasl_cyrus_init, + sb_sasl_cyrus_encode, + sb_sasl_cyrus_decode, + sb_sasl_cyrus_reset_buf, + sb_sasl_cyrus_fini + }; int ldap_pvt_sasl_install( Sockbuf *sb, void *ctx_arg ) { -#ifdef NEW_LOGGING - LDAP_LOG ( TRANSPORT, ENTRY, "ldap_pvt_sasl_install\n", 0, 0, 0 ); -#else - Debug( LDAP_DEBUG_TRACE, "ldap_pvt_sasl_install\n", - 0, 0, 0 ); -#endif + struct sb_sasl_generic_install install_arg; - /* don't install the stuff unless security has been negotiated */ + install_arg.ops = &sb_sasl_cyrus_ops; + install_arg.ops_private = ctx_arg; - if ( !ber_sockbuf_ctrl( sb, LBER_SB_OPT_HAS_IO, - &ldap_pvt_sockbuf_io_sasl ) ) - { -#ifdef LDAP_DEBUG - ber_sockbuf_add_io( sb, &ber_sockbuf_io_debug, - LBER_SBIOD_LEVEL_APPLICATION, (void *)"sasl_" ); -#endif - ber_sockbuf_add_io( sb, &ldap_pvt_sockbuf_io_sasl, - LBER_SBIOD_LEVEL_APPLICATION, ctx_arg ); - } - - return LDAP_SUCCESS; + return ldap_pvt_sasl_generic_install( sb, &install_arg ); } void ldap_pvt_sasl_remove( Sockbuf *sb ) { - ber_sockbuf_remove_io( sb, &ldap_pvt_sockbuf_io_sasl, - LBER_SBIOD_LEVEL_APPLICATION ); -#ifdef LDAP_DEBUG - ber_sockbuf_remove_io( sb, &ber_sockbuf_io_debug, - LBER_SBIOD_LEVEL_APPLICATION ); -#endif + ldap_pvt_sasl_generic_remove( sb ); } static int @@ -453,6 +262,15 @@ sasl_err2ldap( int saslerr ) { int rc; + /* map SASL errors to LDAP API errors returned by: + * sasl_client_new() + * SASL_OK, SASL_NOMECH, SASL_NOMEM + * sasl_client_start() + * SASL_OK, SASL_NOMECH, SASL_NOMEM, SASL_INTERACT + * sasl_client_step() + * SASL_OK, SASL_INTERACT, SASL_BADPROT, SASL_BADSERV + */ + switch (saslerr) { case SASL_CONTINUE: rc = LDAP_MORE_RESULTS_TO_RETURN; @@ -463,21 +281,29 @@ sasl_err2ldap( int saslerr ) case SASL_OK: rc = LDAP_SUCCESS; break; - case SASL_FAIL: - rc = LDAP_LOCAL_ERROR; - break; case SASL_NOMEM: rc = LDAP_NO_MEMORY; break; case SASL_NOMECH: rc = LDAP_AUTH_UNKNOWN; break; + case SASL_BADPROT: + rc = LDAP_DECODING_ERROR; + break; + case SASL_BADSERV: + rc = LDAP_AUTH_UNKNOWN; + break; + + /* other codes */ case SASL_BADAUTH: rc = LDAP_AUTH_UNKNOWN; break; case SASL_NOAUTHZ: rc = LDAP_PARAM_ERROR; break; + case SASL_FAIL: + rc = LDAP_LOCAL_ERROR; + break; case SASL_TOOWEAK: case SASL_ENCRYPT: rc = LDAP_AUTH_UNKNOWN; @@ -525,13 +351,8 @@ ldap_int_sasl_open( return ld->ld_errno; } -#ifdef NEW_LOGGING - LDAP_LOG ( TRANSPORT, DETAIL1, "ldap_int_sasl_open: host=%s\n", - host, 0, 0 ); -#else Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_open: host=%s\n", host, 0, 0 ); -#endif lc->lconn_sasl_authctx = ctx; @@ -579,13 +400,8 @@ ldap_int_sasl_bind( ber_socket_t sd; void *ssl; -#ifdef NEW_LOGGING - LDAP_LOG ( TRANSPORT, ARGS, "ldap_int_sasl_bind: %s\n", - mechs ? mechs : "", 0, 0 ); -#else Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n", mechs ? mechs : "", 0, 0 ); -#endif /* do a quick !LDAPv3 check... ldap_sasl_bind will do the rest. */ if (ld->ld_version < LDAP_VERSION3) { @@ -593,22 +409,31 @@ ldap_int_sasl_bind( return ld->ld_errno; } + rc = 0; +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_lock( &ld->ld_req_mutex ); +#endif ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); if ( sd == AC_SOCKET_INVALID ) { /* not connected yet */ - int rc; rc = ldap_open_defconn( ld ); - if( rc < 0 ) return ld->ld_errno; - ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd ); + if ( rc == 0 ) { + ber_sockbuf_ctrl( ld->ld_defconn->lconn_sb, + LBER_SB_OPT_GET_FD, &sd ); - if( sd == AC_SOCKET_INVALID ) { - ld->ld_errno = LDAP_LOCAL_ERROR; - return ld->ld_errno; + if( sd == AC_SOCKET_INVALID ) { + ld->ld_errno = LDAP_LOCAL_ERROR; + rc = ld->ld_errno; + } } } +#ifdef LDAP_R_COMPILE + ldap_pvt_thread_mutex_unlock( &ld->ld_req_mutex ); +#endif + if( rc != 0 ) return ld->ld_errno; oldctx = ld->ld_defconn->lconn_sasl_authctx; @@ -620,17 +445,31 @@ ldap_int_sasl_bind( ld->ld_defconn->lconn_sasl_authctx = NULL; } - { char *saslhost = ldap_host_connected_to( ld->ld_sb, "localhost" ); - rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost ); - LDAP_FREE( saslhost ); + { + char *saslhost; + int nocanon = (int)LDAP_BOOL_GET( &ld->ld_options, + LDAP_BOOL_SASL_NOCANON ); + + /* If we don't need to canonicalize just use the host + * from the LDAP URI. + */ + if ( nocanon ) + saslhost = ld->ld_defconn->lconn_server->lud_host; + else + saslhost = ldap_host_connected_to( ld->ld_defconn->lconn_sb, + "localhost" ); + rc = ldap_int_sasl_open( ld, ld->ld_defconn, saslhost ); + if ( !nocanon ) + LDAP_FREE( saslhost ); } if ( rc != LDAP_SUCCESS ) return rc; ctx = ld->ld_defconn->lconn_sasl_authctx; +#ifdef HAVE_TLS /* Check for TLS */ - ssl = ldap_pvt_tls_sb_ctx( ld->ld_sb ); + ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb ); if ( ssl ) { struct berval authid = BER_BVNULL; ber_len_t fac; @@ -642,16 +481,20 @@ ldap_int_sasl_bind( (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac ); LDAP_FREE( authid.bv_val ); } +#endif #if !defined(_WIN32) /* Check for local */ - if ( ldap_pvt_url_scheme2proto( ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) { - char authid[sizeof("uidNumber=4294967295+gidNumber=4294967295," + if ( ldap_pvt_url_scheme2proto( + ld->ld_defconn->lconn_server->lud_scheme ) == LDAP_PROTO_IPC ) + { + char authid[sizeof("gidNumber=4294967295+uidNumber=4294967295," "cn=peercred,cn=external,cn=auth")]; - sprintf( authid, "uidNumber=%d+gidNumber=%d," + sprintf( authid, "gidNumber=%u+uidNumber=%u," "cn=peercred,cn=external,cn=auth", - (int) geteuid(), (int) getegid() ); - (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, LDAP_PVT_SASL_LOCAL_SSF ); + getegid(), geteuid() ); + (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid, + LDAP_PVT_SASL_LOCAL_SSF ); } #endif @@ -697,6 +540,9 @@ ldap_int_sasl_bind( if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { rc = ld->ld_errno = sasl_err2ldap( saslrc ); #if SASL_VERSION_MAJOR >= 2 + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); #endif goto done; @@ -708,7 +554,8 @@ ldap_int_sasl_bind( scred = NULL; - rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls, &scred ); + rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls, + &scred ); if ( ccred.bv_val != NULL ) { #if SASL_VERSION_MAJOR < 2 @@ -718,18 +565,13 @@ ldap_int_sasl_bind( } if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) { - if( scred && scred->bv_len ) { + if( scred ) { /* and server provided us with data? */ -#ifdef NEW_LOGGING - LDAP_LOG ( TRANSPORT, DETAIL1, - "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n", - rc, saslrc, scred->bv_len ); -#else Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n", - rc, saslrc, scred->bv_len ); -#endif + rc, saslrc, scred ? (long) scred->bv_len : -1L ); ber_bvfree( scred ); + scred = NULL; } rc = ld->ld_errno; goto done; @@ -737,25 +579,45 @@ ldap_int_sasl_bind( 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! */ -#ifdef NEW_LOGGING - LDAP_LOG ( TRANSPORT, DETAIL1, - "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n", - rc, saslrc, scred->bv_len ); -#else - Debug( LDAP_DEBUG_TRACE, - "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n", - rc, saslrc, scred->bv_len ); + if( scred ) { + /* but we got additional data? */ +#define KLUDGE_FOR_MSAD +#ifdef KLUDGE_FOR_MSAD + /* + * MSAD provides empty additional data in violation of LDAP + * technical specifications. As no existing SASL mechanism + * allows empty data with an outcome message, just ignore it + * for now. Hopefully MS will fix their bug before someone + * defines a mechanism with possibly empty additional data. + */ + if( scred->bv_len == 0 ) { + Debug( LDAP_DEBUG_ANY, + "ldap_int_sasl_bind: ignoring " + " bogus empty data provided with SASL outcome message.\n", + rc, saslrc, scred->bv_len ); + ber_bvfree( scred ); + } else #endif - ber_bvfree( scred ); - rc = ld->ld_errno = LDAP_LOCAL_ERROR; - goto done; + { + Debug( LDAP_DEBUG_TRACE, + "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n", + rc, saslrc, scred->bv_len ); + rc = ld->ld_errno = LDAP_LOCAL_ERROR; + ber_bvfree( scred ); + goto done; + } } break; } do { + if( ! scred ) { + /* no data! */ + Debug( LDAP_DEBUG_TRACE, + "ldap_int_sasl_bind: no data in step!\n", + 0, 0, 0 ); + } + saslrc = sasl_client_step( ctx, (scred == NULL) ? NULL : scred->bv_val, (scred == NULL) ? 0 : scred->bv_len, @@ -763,13 +625,8 @@ ldap_int_sasl_bind( (SASL_CONST char **)&ccred.bv_val, &credlen ); -#ifdef NEW_LOGGING - LDAP_LOG ( TRANSPORT, DETAIL1, - "ldap_int_sasl_bind: sasl_client_step: %d\n", saslrc,0,0 ); -#else Debug( LDAP_DEBUG_TRACE, "sasl_client_step: %d\n", saslrc, 0, 0 ); -#endif if( saslrc == SASL_INTERACT ) { int res; @@ -785,6 +642,9 @@ ldap_int_sasl_bind( if ( (saslrc != SASL_OK) && (saslrc != SASL_CONTINUE) ) { ld->ld_errno = sasl_err2ldap( saslrc ); #if SASL_VERSION_MAJOR >= 2 + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); #endif rc = ld->ld_errno; @@ -796,6 +656,9 @@ ldap_int_sasl_bind( if ( saslrc != SASL_OK ) { #if SASL_VERSION_MAJOR >= 2 + if ( ld->ld_error ) { + LDAP_FREE( ld->ld_error ); + } ld->ld_error = LDAP_STRDUP( sasl_errdetail( ctx ) ); #endif rc = ld->ld_errno = sasl_err2ldap( saslrc ); @@ -803,20 +666,22 @@ ldap_int_sasl_bind( } if( flags != LDAP_SASL_QUIET ) { - saslrc = sasl_getprop( ctx, SASL_USERNAME, (SASL_CONST void **) &data ); + saslrc = sasl_getprop( ctx, SASL_USERNAME, + (SASL_CONST void **)(char *) &data ); if( saslrc == SASL_OK && data && *data ) { fprintf( stderr, "SASL username: %s\n", data ); } #if SASL_VERSION_MAJOR < 2 - saslrc = sasl_getprop( ctx, SASL_REALM, (SASL_CONST void **) &data ); + saslrc = sasl_getprop( ctx, SASL_REALM, + (SASL_CONST void **) &data ); if( saslrc == SASL_OK && data && *data ) { fprintf( stderr, "SASL realm: %s\n", data ); } #endif } - saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **) &ssf ); + saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf ); if( saslrc == SASL_OK ) { if( flags != LDAP_SASL_QUIET ) { fprintf( stderr, "SASL SSF: %lu\n", @@ -824,16 +689,17 @@ ldap_int_sasl_bind( } if( ssf && *ssf ) { - if( flags != LDAP_SASL_QUIET ) { - fprintf( stderr, "SASL installing layers\n" ); - } if ( ld->ld_defconn->lconn_sasl_sockctx ) { oldctx = ld->ld_defconn->lconn_sasl_sockctx; sasl_dispose( &oldctx ); - ldap_pvt_sasl_remove( ld->ld_sb ); + ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb ); } - ldap_pvt_sasl_install( ld->ld_conns->lconn_sb, ctx ); + ldap_pvt_sasl_install( ld->ld_defconn->lconn_sb, ctx ); ld->ld_defconn->lconn_sasl_sockctx = ctx; + + if( flags != LDAP_SASL_QUIET ) { + fprintf( stderr, "SASL data security layer installed.\n" ); + } } } ld->ld_defconn->lconn_sasl_authctx = ctx; @@ -853,6 +719,8 @@ ldap_int_sasl_external( sasl_conn_t *ctx; #if SASL_VERSION_MAJOR < 2 sasl_external_properties_t extprops; +#else + sasl_ssf_t sasl_ssf = ssf; #endif ctx = conn->lconn_sasl_authctx; @@ -862,7 +730,7 @@ ldap_int_sasl_external( } #if SASL_VERSION_MAJOR >= 2 - sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &ssf ); + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf ); if ( sc == SASL_OK ) sc = sasl_setprop( ctx, SASL_AUTH_EXTERNAL, authid ); #else @@ -882,12 +750,111 @@ ldap_int_sasl_external( } +#define GOT_MINSSF 1 +#define GOT_MAXSSF 2 +#define GOT_MAXBUF 4 + +static struct { + struct berval key; + int sflag; + int ival; + int idef; +} sprops[] = { + { BER_BVC("none"), 0, 0, 0 }, + { BER_BVC("nodict"), SASL_SEC_NODICTIONARY, 0, 0 }, + { BER_BVC("noplain"), SASL_SEC_NOPLAINTEXT, 0, 0 }, + { BER_BVC("noactive"), SASL_SEC_NOACTIVE, 0, 0 }, + { BER_BVC("passcred"), SASL_SEC_PASS_CREDENTIALS, 0, 0 }, + { BER_BVC("forwardsec"), SASL_SEC_FORWARD_SECRECY, 0, 0 }, + { BER_BVC("noanonymous"), SASL_SEC_NOANONYMOUS, 0, 0 }, + { BER_BVC("minssf="), 0, GOT_MINSSF, 0 }, + { BER_BVC("maxssf="), 0, GOT_MAXSSF, INT_MAX }, + { BER_BVC("maxbufsize="), 0, GOT_MAXBUF, 65536 }, + { BER_BVNULL, 0, 0, 0 } +}; + +void ldap_pvt_sasl_secprops_unparse( + sasl_security_properties_t *secprops, + struct berval *out ) +{ + int i, l = 0; + int comma; + char *ptr; + + if ( secprops == NULL || out == NULL ) { + return; + } + + comma = 0; + for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { + if ( sprops[i].ival ) { + int v = 0; + + switch( sprops[i].ival ) { + case GOT_MINSSF: v = secprops->min_ssf; break; + case GOT_MAXSSF: v = secprops->max_ssf; break; + case GOT_MAXBUF: v = secprops->maxbufsize; break; + } + /* It is the default, ignore it */ + if ( v == sprops[i].idef ) continue; + + l += sprops[i].key.bv_len + 24; + } else if ( sprops[i].sflag ) { + if ( sprops[i].sflag & secprops->security_flags ) { + l += sprops[i].key.bv_len; + } + } else if ( secprops->security_flags == 0 ) { + l += sprops[i].key.bv_len; + } + if ( comma ) l++; + comma = 1; + } + l++; + + out->bv_val = LDAP_MALLOC( l ); + if ( out->bv_val == NULL ) { + out->bv_len = 0; + return; + } + + ptr = out->bv_val; + comma = 0; + for ( i=0; !BER_BVISNULL( &sprops[i].key ); i++ ) { + if ( sprops[i].ival ) { + int v = 0; + + switch( sprops[i].ival ) { + case GOT_MINSSF: v = secprops->min_ssf; break; + case GOT_MAXSSF: v = secprops->max_ssf; break; + case GOT_MAXBUF: v = secprops->maxbufsize; break; + } + /* It is the default, ignore it */ + if ( v == sprops[i].idef ) continue; + + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s%d", sprops[i].key.bv_val, v ); + comma = 1; + } else if ( sprops[i].sflag ) { + if ( sprops[i].sflag & secprops->security_flags ) { + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); + comma = 1; + } + } else if ( secprops->security_flags == 0 ) { + if ( comma ) *ptr++ = ','; + ptr += sprintf(ptr, "%s", sprops[i].key.bv_val ); + comma = 1; + } + } + out->bv_len = ptr - out->bv_val; +} + int ldap_pvt_sasl_secprops( const char *in, sasl_security_properties_t *secprops ) { - int i; - char **props = ldap_str2charray( in, "," ); + unsigned i, j, l; + char **props; unsigned sflags = 0; int got_sflags = 0; sasl_ssf_t max_ssf = 0; @@ -897,76 +864,47 @@ int ldap_pvt_sasl_secprops( unsigned maxbufsize = 0; int got_maxbufsize = 0; - if( props == NULL || secprops == NULL ) { + if( secprops == NULL ) { + return LDAP_PARAM_ERROR; + } + props = ldap_str2charray( in, "," ); + if( props == NULL ) { return LDAP_PARAM_ERROR; } for( i=0; props[i]; i++ ) { - if( !strcasecmp(props[i], "none") ) { - got_sflags++; - - } else if( !strcasecmp(props[i], "noplain") ) { - got_sflags++; - sflags |= SASL_SEC_NOPLAINTEXT; - - } else if( !strcasecmp(props[i], "noactive") ) { - got_sflags++; - sflags |= SASL_SEC_NOACTIVE; - - } else if( !strcasecmp(props[i], "nodict") ) { - got_sflags++; - sflags |= SASL_SEC_NODICTIONARY; - - } else if( !strcasecmp(props[i], "forwardsec") ) { - got_sflags++; - sflags |= SASL_SEC_FORWARD_SECRECY; - - } else if( !strcasecmp(props[i], "noanonymous")) { - got_sflags++; - sflags |= SASL_SEC_NOANONYMOUS; - - } else if( !strcasecmp(props[i], "passcred") ) { - got_sflags++; - sflags |= SASL_SEC_PASS_CREDENTIALS; - - } else if( !strncasecmp(props[i], - "minssf=", sizeof("minssf")) ) - { - if( isdigit( (unsigned char) 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")) ) - { - if( isdigit( (unsigned char) 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")) ) - { - if( isdigit( (unsigned char) props[i][sizeof("maxbufsize")] ) ) { - got_maxbufsize++; - maxbufsize = atoi( &props[i][sizeof("maxbufsize")] ); + l = strlen( props[i] ); + for ( j=0; !BER_BVISNULL( &sprops[j].key ); j++ ) { + if ( l < sprops[j].key.bv_len ) continue; + if ( strncasecmp( props[i], sprops[j].key.bv_val, + sprops[j].key.bv_len )) continue; + if ( sprops[j].ival ) { + unsigned v; + char *next = NULL; + if ( !isdigit( (unsigned char)props[i][sprops[j].key.bv_len] )) + continue; + v = strtoul( &props[i][sprops[j].key.bv_len], &next, 10 ); + if ( next == &props[i][sprops[j].key.bv_len] || next[0] != '\0' ) continue; + switch( sprops[j].ival ) { + case GOT_MINSSF: + min_ssf = v; got_min_ssf++; break; + case GOT_MAXSSF: + max_ssf = v; got_max_ssf++; break; + case GOT_MAXBUF: + maxbufsize = v; got_maxbufsize++; break; + } } else { - return LDAP_NOT_SUPPORTED; + if ( props[i][sprops[j].key.bv_len] ) continue; + if ( sprops[j].sflag ) + sflags |= sprops[j].sflag; + else + sflags = 0; + got_sflags++; } - - if( maxbufsize && (( maxbufsize < SASL_MIN_BUFF_SIZE ) - || (maxbufsize > SASL_MAX_BUFF_SIZE ))) - { - /* bad maxbufsize */ - return LDAP_PARAM_ERROR; - } - - } else { + break; + } + if ( BER_BVISNULL( &sprops[j].key )) { + ldap_charray_free( props ); return LDAP_NOT_SUPPORTED; } } @@ -1005,6 +943,13 @@ ldap_int_sasl_config( struct ldapoptions *lo, int option, const char *arg ) int ldap_int_sasl_get_option( LDAP *ld, int option, void *arg ) { + if ( option == LDAP_OPT_X_SASL_MECHLIST ) { + if ( ldap_int_sasl_init() ) + return -1; + *(char ***)arg = (char **)sasl_global_listmech(); + return 0; + } + if ( ld == NULL ) return -1; @@ -1042,7 +987,7 @@ ldap_int_sasl_get_option( LDAP *ld, int option, void *arg ) } sc = sasl_getprop( ctx, SASL_SSF, - (SASL_CONST void **) &ssf ); + (SASL_CONST void **)(char *) &ssf ); if ( sc != SASL_OK ) { return -1; @@ -1064,11 +1009,58 @@ ldap_int_sasl_get_option( LDAP *ld, int option, void *arg ) case LDAP_OPT_X_SASL_MAXBUFSIZE: *(ber_len_t *)arg = ld->ld_options.ldo_sasl_secprops.maxbufsize; break; + case LDAP_OPT_X_SASL_NOCANON: + *(int *)arg = (int) LDAP_BOOL_GET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + break; + + case LDAP_OPT_X_SASL_USERNAME: { + int sc; + char *username; + sasl_conn_t *ctx; + + if( ld->ld_defconn == NULL ) { + return -1; + } + + ctx = ld->ld_defconn->lconn_sasl_authctx; + + if ( ctx == NULL ) { + return -1; + } + + sc = sasl_getprop( ctx, SASL_USERNAME, + (SASL_CONST void **)(char **) &username ); + + if ( sc != SASL_OK ) { + return -1; + } + + *(char **)arg = username ? LDAP_STRDUP( username ) : NULL; + } break; case LDAP_OPT_X_SASL_SECPROPS: /* this option is write only */ return -1; +#ifdef SASL_GSS_CREDS + case LDAP_OPT_X_SASL_GSS_CREDS: { + sasl_conn_t *ctx; + int sc; + + if ( ld->ld_defconn == NULL ) + return -1; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + if ( ctx == NULL ) + return -1; + + sc = sasl_getprop( ctx, SASL_GSS_CREDS, arg ); + if ( sc != SASL_OK ) + return -1; + } + break; +#endif + default: return -1; } @@ -1081,8 +1073,12 @@ ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) if ( ld == NULL ) return -1; + if ( arg == NULL && option != LDAP_OPT_X_SASL_NOCANON ) + return -1; + switch ( option ) { case LDAP_OPT_X_SASL_SSF: + case LDAP_OPT_X_SASL_USERNAME: /* This option is read-only */ return -1; @@ -1090,6 +1086,8 @@ ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) int sc; #if SASL_VERSION_MAJOR < 2 sasl_external_properties_t extprops; +#else + sasl_ssf_t sasl_ssf; #endif sasl_conn_t *ctx; @@ -1104,7 +1102,8 @@ ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) } #if SASL_VERSION_MAJOR >= 2 - sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, arg); + sasl_ssf = * (ber_len_t *)arg; + sc = sasl_setprop( ctx, SASL_SSF_EXTERNAL, &sasl_ssf); #else memset(&extprops, 0L, sizeof(extprops)); @@ -1128,6 +1127,13 @@ ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) case LDAP_OPT_X_SASL_MAXBUFSIZE: ld->ld_options.ldo_sasl_secprops.maxbufsize = *(ber_len_t *)arg; break; + case LDAP_OPT_X_SASL_NOCANON: + if ( arg == LDAP_OPT_OFF ) { + LDAP_BOOL_CLR(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + } else { + LDAP_BOOL_SET(&ld->ld_options, LDAP_BOOL_SASL_NOCANON ); + } + break; case LDAP_OPT_X_SASL_SECPROPS: { int sc; @@ -1137,6 +1143,25 @@ ldap_int_sasl_set_option( LDAP *ld, int option, void *arg ) return sc == LDAP_SUCCESS ? 0 : -1; } +#ifdef SASL_GSS_CREDS + case LDAP_OPT_X_SASL_GSS_CREDS: { + sasl_conn_t *ctx; + int sc; + + if ( ld->ld_defconn == NULL ) + return -1; + + ctx = ld->ld_defconn->lconn_sasl_authctx; + if ( ctx == NULL ) + return -1; + + sc = sasl_setprop( ctx, SASL_GSS_CREDS, arg ); + if ( sc != SASL_OK ) + return -1; + } + break; +#endif + default: return -1; } @@ -1149,7 +1174,7 @@ void *ldap_pvt_sasl_mutex_new(void) { ldap_pvt_thread_mutex_t *mutex; - mutex = (ldap_pvt_thread_mutex_t *) LDAP_MALLOC( + mutex = (ldap_pvt_thread_mutex_t *) LDAP_CALLOC( 1, sizeof(ldap_pvt_thread_mutex_t) ); if ( ldap_pvt_thread_mutex_init( mutex ) == 0 ) { @@ -1168,7 +1193,7 @@ int ldap_pvt_sasl_mutex_lock(void *mutex) return SASL_OK; } #else /* !LDAP_DEBUG_R_SASL */ - assert( mutex ); + assert( mutex != NULL ); #endif /* !LDAP_DEBUG_R_SASL */ return ldap_pvt_thread_mutex_lock( (ldap_pvt_thread_mutex_t *)mutex ) ? SASL_FAIL : SASL_OK; @@ -1181,7 +1206,7 @@ int ldap_pvt_sasl_mutex_unlock(void *mutex) return SASL_OK; } #else /* !LDAP_DEBUG_R_SASL */ - assert( mutex ); + assert( mutex != NULL ); #endif /* !LDAP_DEBUG_R_SASL */ return ldap_pvt_thread_mutex_unlock( (ldap_pvt_thread_mutex_t *)mutex ) ? SASL_FAIL : SASL_OK; @@ -1194,7 +1219,7 @@ void ldap_pvt_sasl_mutex_dispose(void *mutex) return; } #else /* !LDAP_DEBUG_R_SASL */ - assert( mutex ); + assert( mutex != NULL ); #endif /* !LDAP_DEBUG_R_SASL */ (void) ldap_pvt_thread_mutex_destroy( (ldap_pvt_thread_mutex_t *)mutex ); LDAP_FREE( mutex );