]> git.sur5r.net Git - openldap/commitdiff
Add ldap_sasl_interactive_bind()
authorHoward Chu <hyc@openldap.org>
Thu, 14 Oct 2010 01:29:32 +0000 (01:29 +0000)
committerHoward Chu <hyc@openldap.org>
Thu, 14 Oct 2010 01:29:32 +0000 (01:29 +0000)
include/ldap.h
libraries/libldap/cyrus.c
libraries/libldap/ldap-int.h
libraries/libldap/sasl.c

index 5c05d58b406527c00c67c44a6845c43726e07c09..fa6e4176ecc50b248b68314c42fd12c27c795a59 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 9bd5938dc400d23c4fc716dbb44eeda648b40bcf..a223d4d4191a4b3a5f55f4dd3b4a829bdd1c57e8 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;
+       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,203 +408,161 @@ 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;
+               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;
 
-                       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_INTERACT ) {
+                               int res;
+                               if( !interact ) break;
+                               res = (interact)( ld, flags, defaults, prompts );
 
-       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( res != LDAP_SUCCESS ) break;
+                       }
+                       *rmech = mech;
+               } while ( saslrc == SASL_INTERACT );
 
-       do {
-               struct berval *scred;
-               unsigned credlen;
+       } else {
+               /* continuing an in-progress Bind */
+               struct berval *scred = NULL;
 
                scred = NULL;
+               rc = ldap_parse_sasl_bind_result( ld, result, &scred, 0 );
+               if ( rc != LDAP_SUCCESS )
+                       goto done;
 
-               rc = ldap_sasl_bind_s( ld, dn, mech, &ccred, sctrls, cctrls,
-                       &scred );
-
-               if ( ccred.bv_val != NULL ) {
-#if SASL_VERSION_MAJOR < 2
-                       LDAP_FREE( ccred.bv_val );
-#endif
-                       ccred.bv_val = NULL;
-               }
-
+               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;
-               }
-
+               ctx = ld->ld_defconn->lconn_sasl_authctx;
+               mech = *rmech;
                do {
                        if( ! scred ) {
                                /* no data! */
@@ -632,36 +589,42 @@ 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;
        }
 
+       ccred.bv_len = credlen;
+
+       /* Always send a request on first Bind; only send subsequent if
+        * saslrc == SASL_CONTINUE
+        */
+       if ( !result || saslrc == SASL_CONTINUE ) {
+               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;
+       }
+
+       /* 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 +640,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 +650,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 70ecb63d821ca448f6df56c582a6249f8bd0a9e2..e4c760aea025328dd68bc7009b43e3f716c4a12c 100644 (file)
@@ -685,7 +685,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 8238c4cf20b59f66bfbf574ed3bf9b6c074b15ec..61ae706b1066852e362f3039db2018493c5a7625 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;
@@ -460,10 +467,10 @@ ldap_sasl_interactive_bind_s(
                        "ldap_sasl_interactive_bind_s: 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 +481,51 @@ 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 );
+
+               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