]> git.sur5r.net Git - openldap/commitdiff
Add ldap_sasl_interactive_bind()
authorQuanah Gibson-Mount <quanah@openldap.org>
Thu, 6 Jan 2011 19:18:16 +0000 (19:18 +0000)
committerQuanah Gibson-Mount <quanah@openldap.org>
Thu, 6 Jan 2011 19:18:16 +0000 (19:18 +0000)
doc/man/man3/ldap_bind.3
include/ldap.h
libraries/libldap/cyrus.c
libraries/libldap/ldap-int.h
libraries/libldap/sasl.c

index 98ffef60069458cf3a912a8dba8edc8911948e15..009f3697dc6f6cbdea1b960113a0451bd67aa133 100644 (file)
@@ -49,6 +49,15 @@ OpenLDAP LDAP (libldap, \-lldap)
 .BI "void *" defaults ");"
 .RE
 .LP
+.BI "int ldap_sasl_interactive_bind(LDAP *" ld ", const char *" dn ","
+.RS
+.BI "const char *" mechs ","
+.BI "LDAPControl *" sctrls "[], LDAPControl *" cctrls "[],"
+.BI "unsigned " flags ", LDAP_SASL_INTERACT_PROC *" interact ","
+.BI "void *" defaults ", LDAPMessage *" result ","
+.BI "const char **" rmechp ", int *" msgidp ");"
+.RE
+.LP
 .BI "int (LDAP_SASL_INTERACT_PROC)(LDAP *" ld ", unsigned " flags ", void *" defaults ", void *" sasl_interact ");"
 .LP
 .BI "int ldap_unbind(LDAP *" ld ");"
@@ -84,7 +93,7 @@ either simple or SASL authentication.
 .LP
 .B SASL
 (Simple Authentication and Security Layer)
-that can negotiate one of many different kinds of authentication.
+can negotiate one of many different kinds of authentication.
 Both synchronous and asynchronous versions of each variant of the bind
 call are provided.  All routines
 take \fIld\fP as their first parameter, as returned from
@@ -210,6 +219,27 @@ SASL_CB_LIST_END
 indicates the end of the array of prompts
 .LP
 See the Cyrus SASL documentation for more details.
+.LP
+Applications which need to manage connections asynchronously may use
+.BR ldap_sasl_interactive_bind ()
+instead of the synchronous version. The parameters are the same as
+for the synchronous function, with three additional parameters.
+The actual SASL mechanism that was used, and the message ID for use
+with
+.BR ldap_result ()
+will be returned in rmechp and msgidp, respectively.
+The value in rmechp must not be modified by the caller and must be
+passed back on each subsequent call. The message obtained from
+.BR ldap_result ()
+must be passed in the result parameter.
+This parameter must be NULL when initiating a new Bind. The caller
+must free the result message after each call using
+.BR ldap_msgfree ().
+The
+.BR ldap_sasl_interactive_bind ()
+function returns an LDAP result code. If the code is
+LDAP_SASL_BIND_IN_PROGRESS then the Bind is not complete yet, and
+this function must be called again with the next result from the server.
 .SH REBINDING
 .LP
 The
index 43b6106444c7028f57d87adbae63082d9e14a46c..85d4fbecca71f1b393a4ba00e205797ef6d7af76 100644 (file)
@@ -1186,6 +1186,26 @@ ldap_sasl_bind LDAP_P((
 typedef int (LDAP_SASL_INTERACT_PROC) LDAP_P((
        LDAP *ld, unsigned flags, void* defaults, void *interact ));
 
+LDAP_F( int )
+ldap_sasl_interactive_bind LDAP_P((
+       LDAP *ld,
+       LDAP_CONST char *dn, /* usually NULL */
+       LDAP_CONST char *saslMechanism,
+       LDAPControl **serverControls,
+       LDAPControl **clientControls,
+
+       /* should be client controls */
+       unsigned flags,
+       LDAP_SASL_INTERACT_PROC *proc,
+       void *defaults,
+       
+       /* as obtained from ldap_result() */
+       LDAPMessage *result,
+
+       /* returned during bind processing */
+       const char **rmech,
+       int *msgid ));
+
 LDAP_F( int )
 ldap_sasl_interactive_bind_s LDAP_P((
        LDAP *ld,
index be6e02384479e9ea97664bd6c0d187049a5c1e5e..24417b9809208d1a150394bd706ab4ef070c663e 100644 (file)
@@ -386,19 +386,18 @@ ldap_int_sasl_bind(
        LDAPControl             **cctrls,
        unsigned                flags,
        LDAP_SASL_INTERACT_PROC *interact,
-       void * defaults )
+       void                    *defaults,
+       LDAPMessage             *result,
+       const char              **rmech,
+       int                             *msgid )
 {
-       char *data;
-       const char *mech = NULL;
-       const char *pmech = NULL;
-       int                     saslrc, rc;
-       sasl_ssf_t              *ssf = NULL;
-       sasl_conn_t     *ctx, *oldctx = NULL;
+       const char              *mech;
+       sasl_ssf_t              *ssf;
+       sasl_conn_t             *ctx;
        sasl_interact_t *prompts = NULL;
+       struct berval   ccred = BER_BVNULL;
+       int saslrc, rc;
        unsigned credlen;
-       struct berval ccred;
-       ber_socket_t            sd;
-       void    *ssl;
 
        Debug( LDAP_DEBUG_TRACE, "ldap_int_sasl_bind: %s\n",
                mechs ? mechs : "<null>", 0, 0 );
@@ -409,202 +408,162 @@ ldap_int_sasl_bind(
                return ld->ld_errno;
        }
 
-       rc = 0;
-       LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
-       ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
+       /* Starting a Bind */
+       if ( !result ) {
+               const char *pmech = NULL;
+               sasl_conn_t     *oldctx;
+               ber_socket_t            sd;
+               void    *ssl;
+
+               rc = 0;
+               LDAP_MUTEX_LOCK( &ld->ld_req_mutex );
+               ber_sockbuf_ctrl( ld->ld_sb, LBER_SB_OPT_GET_FD, &sd );
 
-       if ( sd == AC_SOCKET_INVALID ) {
-               /* not connected yet */
+               if ( sd == AC_SOCKET_INVALID ) {
+                       /* not connected yet */
 
-               rc = ldap_open_defconn( ld );
+                       rc = ldap_open_defconn( ld );
 
-               if ( rc == 0 ) {
-                       ber_sockbuf_ctrl( ld->ld_defconn->lconn_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;
-                               rc = ld->ld_errno;
+                               if( sd == AC_SOCKET_INVALID ) {
+                                       ld->ld_errno = LDAP_LOCAL_ERROR;
+                                       rc = ld->ld_errno;
+                               }
                        }
-               }
-       }   
-       LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
-       if( rc != 0 ) return ld->ld_errno;
+               }   
+               LDAP_MUTEX_UNLOCK( &ld->ld_req_mutex );
+               if( rc != 0 ) return ld->ld_errno;
 
-       oldctx = ld->ld_defconn->lconn_sasl_authctx;
+               oldctx = ld->ld_defconn->lconn_sasl_authctx;
 
-       /* If we already have an authentication context, clear it out */
-       if( oldctx ) {
-               if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
-                       sasl_dispose( &oldctx );
+               /* If we already have an authentication context, clear it out */
+               if( oldctx ) {
+                       if ( oldctx != ld->ld_defconn->lconn_sasl_sockctx ) {
+                               sasl_dispose( &oldctx );
+                       }
+                       ld->ld_defconn->lconn_sasl_authctx = NULL;
                }
-               ld->ld_defconn->lconn_sasl_authctx = NULL;
-       }
 
-       {
-               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 );
-       }
+               {
+                       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;
+               if ( rc != LDAP_SUCCESS ) return rc;
 
-       ctx = ld->ld_defconn->lconn_sasl_authctx;
+               ctx = ld->ld_defconn->lconn_sasl_authctx;
 
 #ifdef HAVE_TLS
-       /* Check for TLS */
-       ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
-       if ( ssl ) {
-               struct berval authid = BER_BVNULL;
-               ber_len_t fac;
-
-               fac = ldap_pvt_tls_get_strength( ssl );
-               /* failure is OK, we just can't use SASL EXTERNAL */
-               (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
-
-               (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid.bv_val, fac );
-               LDAP_FREE( authid.bv_val );
-       }
+               /* Check for TLS */
+               ssl = ldap_pvt_tls_sb_ctx( ld->ld_defconn->lconn_sb );
+               if ( ssl ) {
+                       struct berval authid = BER_BVNULL;
+                       ber_len_t fac;
+
+                       fac = ldap_pvt_tls_get_strength( ssl );
+                       /* failure is OK, we just can't use SASL EXTERNAL */
+                       (void) ldap_pvt_tls_get_my_dn( ssl, &authid, NULL, 0 );
+
+                       (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("gidNumber=4294967295+uidNumber=4294967295,"
-                       "cn=peercred,cn=external,cn=auth")];
-               sprintf( authid, "gidNumber=%u+uidNumber=%u,"
-                       "cn=peercred,cn=external,cn=auth",
-                       getegid(), geteuid() );
-               (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
-                       LDAP_PVT_SASL_LOCAL_SSF );
-       }
+               /* Check for local */
+               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, "gidNumber=%u+uidNumber=%u,"
+                               "cn=peercred,cn=external,cn=auth",
+                               getegid(), geteuid() );
+                       (void) ldap_int_sasl_external( ld, ld->ld_defconn, authid,
+                               LDAP_PVT_SASL_LOCAL_SSF );
+               }
 #endif
 
-       /* (re)set security properties */
-       sasl_setprop( ctx, SASL_SEC_PROPS,
-               &ld->ld_options.ldo_sasl_secprops );
+               /* (re)set security properties */
+               sasl_setprop( ctx, SASL_SEC_PROPS,
+                       &ld->ld_options.ldo_sasl_secprops );
 
-       ccred.bv_val = NULL;
-       ccred.bv_len = 0;
+               mech = NULL;
 
-       do {
-               saslrc = sasl_client_start( ctx,
-                       mechs,
+               do {
+                       saslrc = sasl_client_start( ctx,
+                               mechs,
 #if SASL_VERSION_MAJOR < 2
-                       NULL,
+                               NULL,
 #endif
-                       &prompts,
-                       (SASL_CONST char **)&ccred.bv_val,
-                       &credlen,
-                       &mech );
+                               &prompts,
+                               (SASL_CONST char **)&ccred.bv_val,
+                               &credlen,
+                               &mech );
 
-               if( pmech == NULL && mech != NULL ) {
-                       pmech = mech;
+                       if( pmech == NULL && mech != NULL ) {
+                               pmech = mech;
+                               *rmech = mech;
 
-                       if( flags != LDAP_SASL_QUIET ) {
-                               fprintf(stderr,
-                                       "SASL/%s authentication started\n",
-                                       pmech );
+                               if( flags != LDAP_SASL_QUIET ) {
+                                       fprintf(stderr,
+                                               "SASL/%s authentication started\n",
+                                               pmech );
+                               }
                        }
-               }
 
-               if( saslrc == SASL_INTERACT ) {
-                       int res;
-                       if( !interact ) break;
-                       res = (interact)( ld, flags, defaults, prompts );
-
-                       if( res != LDAP_SUCCESS ) break;
-               }
-       } while ( saslrc == SASL_INTERACT );
-
-       ccred.bv_len = credlen;
-
-       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;
-       }
+                       if( saslrc == SASL_INTERACT ) {
+                               int res;
+                               if( !interact ) break;
+                               res = (interact)( ld, flags, defaults, prompts );
 
-       do {
-               struct berval *scred;
-               unsigned credlen;
+                               if( res != LDAP_SUCCESS ) break;
+                       }
+               } while ( saslrc == SASL_INTERACT );
+               rc = LDAP_SASL_BIND_IN_PROGRESS;
 
-               scred = NULL;
+       } else {
+               /* continuing an in-progress Bind */
+               struct berval *scred = NULL;
 
-               rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls,
-                       &scred );
+               ctx = ld->ld_defconn->lconn_sasl_authctx;
 
-               if ( ccred.bv_val != NULL ) {
-#if SASL_VERSION_MAJOR < 2
-                       LDAP_FREE( ccred.bv_val );
-#endif
-                       ccred.bv_val = NULL;
-               }
+               rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 );
+               if ( rc != LDAP_SUCCESS )
+                       goto done;
 
+               rc = ldap_result2error( ld, result, 0 );
                if ( rc != LDAP_SUCCESS && rc != LDAP_SASL_BIND_IN_PROGRESS ) {
                        if( scred ) {
                                /* and server provided us with data? */
                                Debug( LDAP_DEBUG_TRACE,
-                                       "ldap_int_sasl_bind: rc=%d sasl=%d len=%ld\n",
-                                       rc, saslrc, scred ? (long) scred->bv_len : -1L );
+                                       "ldap_int_sasl_bind: rc=%d len=%ld\n",
+                                       rc, scred ? (long) scred->bv_len : -1L, 0 );
                                ber_bvfree( scred );
                                scred = NULL;
                        }
-                       rc = ld->ld_errno;
                        goto done;
                }
 
-               if( rc == LDAP_SUCCESS && saslrc == SASL_OK ) {
-                       /* we're done, no need to step */
-                       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
-                               {
-                                       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;
-               }
+               mech = *rmech;
+               if ( rc == LDAP_SUCCESS && mech == NULL )
+                       goto success;
 
                do {
                        if( ! scred ) {
@@ -632,36 +591,43 @@ ldap_int_sasl_bind(
                        }
                } while ( saslrc == SASL_INTERACT );
 
-               ccred.bv_len = credlen;
                ber_bvfree( scred );
+       }
 
-               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;
-                       goto done;
-               }
-       } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
-
-       if ( rc != LDAP_SUCCESS ) goto done;
-
-       if ( saslrc != SASL_OK ) {
+       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
-               rc = ld->ld_errno = sasl_err2ldap( saslrc );
                goto done;
        }
 
+       if ( saslrc == SASL_OK )
+               *rmech = NULL;
+
+       ccred.bv_len = credlen;
+
+       if ( rc == LDAP_SASL_BIND_IN_PROGRESS ) {
+               rc = ldap_sasl_bind( ld, dn, mech, &ccred, sctrls, cctrls, msgid );
+
+               if ( ccred.bv_val != NULL ) {
+#if SASL_VERSION_MAJOR < 2
+                       LDAP_FREE( ccred.bv_val );
+#endif
+                       ccred.bv_val = NULL;
+               }
+               if ( rc == LDAP_SUCCESS )
+                       rc = LDAP_SASL_BIND_IN_PROGRESS;
+               goto done;
+       }
+
+success:
+       /* Conversation was completed successfully by now */
        if( flags != LDAP_SASL_QUIET ) {
+               char *data;
                saslrc = sasl_getprop( ctx, SASL_USERNAME,
                        (SASL_CONST void **)(char *) &data );
                if( saslrc == SASL_OK && data && *data ) {
@@ -677,6 +643,7 @@ ldap_int_sasl_bind(
 #endif
        }
 
+       ssf = NULL;
        saslrc = sasl_getprop( ctx, SASL_SSF, (SASL_CONST void **)(char *) &ssf );
        if( saslrc == SASL_OK ) {
                if( flags != LDAP_SASL_QUIET ) {
@@ -686,7 +653,7 @@ ldap_int_sasl_bind(
 
                if( ssf && *ssf ) {
                        if ( ld->ld_defconn->lconn_sasl_sockctx ) {
-                               oldctx = ld->ld_defconn->lconn_sasl_sockctx;
+                               sasl_conn_t     *oldctx = ld->ld_defconn->lconn_sasl_sockctx;
                                sasl_dispose( &oldctx );
                                ldap_pvt_sasl_remove( ld->ld_defconn->lconn_sb );
                        }
index 8989610749d4b2ff75bfb8232b6c616b63f5f64b..984613605223121d1ed700c83f7b66db4ff9455d 100644 (file)
@@ -693,7 +693,10 @@ LDAP_F (int) ldap_int_sasl_bind LDAP_P((
        /* should be passed in client controls */
        unsigned flags,
        LDAP_SASL_INTERACT_PROC *interact,
-       void *defaults ));
+       void *defaults,
+       LDAPMessage *result,
+       const char **rmech,
+       int *msgid ));
 
 /* in schema.c */
 LDAP_F (char *) ldap_int_parse_numericoid LDAP_P((
index 2cf5f1bc160ab4bba6ec5eee71756a3baf035c5c..b1c12c5c29948216379cd04620f96ce2a2a6cc0a 100644 (file)
@@ -401,15 +401,16 @@ ldap_pvt_sasl_getmechs ( LDAP *ld, char **pmechlist )
 }
 
 /*
- * ldap_sasl_interactive_bind_s - interactive SASL authentication
+ * ldap_sasl_interactive_bind - interactive SASL authentication
  *
  * This routine uses interactive callbacks.
  *
  * LDAP_SUCCESS is returned upon success, the ldap error code
- * otherwise.
+ * otherwise. LDAP_SASL_BIND_IN_PROGRESS is returned if further
+ * calls are needed.
  */
 int
-ldap_sasl_interactive_bind_s(
+ldap_sasl_interactive_bind(
        LDAP *ld,
        LDAP_CONST char *dn, /* usually NULL */
        LDAP_CONST char *mechs,
@@ -417,10 +418,13 @@ ldap_sasl_interactive_bind_s(
        LDAPControl **clientControls,
        unsigned flags,
        LDAP_SASL_INTERACT_PROC *interact,
-       void *defaults )
+       void *defaults,
+       LDAPMessage *result,
+       const char **rmech,
+       int *msgid )
 {
-       int rc;
        char *smechs = NULL;
+       int rc;
 
 #if defined( HAVE_CYRUS_SASL )
        LDAP_MUTEX_LOCK( &ldap_int_sasl_mutex );
@@ -437,6 +441,9 @@ ldap_sasl_interactive_bind_s(
        } else
 #endif
 
+       /* First time */
+       if ( !result ) {
+
 #ifdef HAVE_CYRUS_SASL
        if( mechs == NULL || *mechs == '\0' ) {
                mechs = ld->ld_options.ldo_def_sasl_mech;
@@ -444,26 +451,29 @@ ldap_sasl_interactive_bind_s(
 #endif
                
        if( mechs == NULL || *mechs == '\0' ) {
+               /* FIXME: this needs to be asynchronous too;
+                * perhaps NULL should be disallowed for async usage?
+                */
                rc = ldap_pvt_sasl_getmechs( ld, &smechs );
                if( rc != LDAP_SUCCESS ) {
                        goto done;
                }
 
                Debug( LDAP_DEBUG_TRACE,
-                       "ldap_sasl_interactive_bind_s: server supports: %s\n",
+                       "ldap_sasl_interactive_bind: server supports: %s\n",
                        smechs, 0, 0 );
 
                mechs = smechs;
 
        } else {
                Debug( LDAP_DEBUG_TRACE,
-                       "ldap_sasl_interactive_bind_s: user selected: %s\n",
+                       "ldap_sasl_interactive_bind: user selected: %s\n",
                        mechs, 0, 0 );
        }
-
+       }
        rc = ldap_int_sasl_bind( ld, dn, mechs,
                serverControls, clientControls,
-               flags, interact, defaults );
+               flags, interact, defaults, result, rmech, msgid );
 
 done:
 #if defined( HAVE_CYRUS_SASL )
@@ -474,6 +484,53 @@ done:
        return rc;
 }
 
+/*
+ * ldap_sasl_interactive_bind_s - interactive SASL authentication
+ *
+ * This routine uses interactive callbacks.
+ *
+ * LDAP_SUCCESS is returned upon success, the ldap error code
+ * otherwise.
+ */
+int
+ldap_sasl_interactive_bind_s(
+       LDAP *ld,
+       LDAP_CONST char *dn, /* usually NULL */
+       LDAP_CONST char *mechs,
+       LDAPControl **serverControls,
+       LDAPControl **clientControls,
+       unsigned flags,
+       LDAP_SASL_INTERACT_PROC *interact,
+       void *defaults )
+{
+       const char *rmech = NULL;
+       LDAPMessage *result = NULL;
+       int rc, msgid;
+
+       do {
+               rc = ldap_sasl_interactive_bind( ld, dn, mechs,
+                       serverControls, clientControls,
+                       flags, interact, defaults, result, &rmech, &msgid );
+
+               ldap_msgfree( result );
+
+               if ( rc != LDAP_SASL_BIND_IN_PROGRESS )
+                       break;
+
+#ifdef LDAP_CONNECTIONLESS
+               if (LDAP_IS_UDP(ld)) {
+                       break;
+               }
+#endif
+
+               if ( ldap_result( ld, msgid, LDAP_MSG_ALL, NULL, &result ) == -1 || !result ) {
+                       return( ld->ld_errno ); /* ldap_result sets ld_errno */
+               }
+       } while ( rc == LDAP_SASL_BIND_IN_PROGRESS );
+
+       return rc;
+}
+
 #ifdef HAVE_CYRUS_SASL
 
 #ifdef HAVE_SASL_SASL_H