]> git.sur5r.net Git - openldap/blobdiff - libraries/libldap/sasl.c
Merge remote branch 'origin/mdb.master'
[openldap] / libraries / libldap / sasl.c
index 1764eabe121c6b4511550195148ee4ae2373a7da..fc995bee449dee8acf63378e509fc3bedc3222bc 100644 (file)
@@ -1,7 +1,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 1998-2008 The OpenLDAP Foundation.
+ * Copyright 1998-2012 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -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,13 +418,16 @@ 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( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL )
-       ldap_pvt_thread_mutex_lock( &ldap_int_sasl_mutex );
+#if defined( HAVE_CYRUS_SASL )
+       LDAP_MUTEX_LOCK( &ldap_int_sasl_mutex );
 #endif
 #ifdef LDAP_CONNECTIONLESS
        if( LDAP_IS_UDP(ld) ) {
@@ -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,36 +451,86 @@ 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( LDAP_R_COMPILE ) && defined( HAVE_CYRUS_SASL )
-       ldap_pvt_thread_mutex_unlock( &ldap_int_sasl_mutex );
+#if defined( HAVE_CYRUS_SASL )
+       LDAP_MUTEX_UNLOCK( &ldap_int_sasl_mutex );
 #endif
        if ( smechs ) LDAP_FREE( smechs );
 
        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
@@ -482,12 +539,6 @@ done:
 #include <sasl.h>
 #endif
 
-#if SASL_VERSION_MAJOR >= 2
-#define SASL_CONST const
-#else
-#define SASL_CONST
-#endif
-
 #endif /* HAVE_CYRUS_SASL */
 
 static int
@@ -509,6 +560,7 @@ sb_sasl_generic_setup( Sockbuf_IO_Desc *sbiod, void *arg )
        p->ops = i->ops;
        p->ops_private = i->ops_private;
        p->sbiod = sbiod;
+       p->flags = 0;
        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 );
@@ -684,13 +736,14 @@ sb_sasl_generic_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
 {
        struct sb_sasl_generic_data     *p;
        int                             ret;
+       ber_len_t                       len2;
 
        assert( sbiod != NULL );
        assert( SOCKBUF_VALID( sbiod->sbiod_sb ) );
 
        p = (struct sb_sasl_generic_data *)sbiod->sbiod_pvt;
 
-       /* Are there anything left in the buffer? */
+       /* Is 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;
@@ -702,14 +755,22 @@ sb_sasl_generic_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
                }
        }
 
-       /* now encode the next packet. */
-       p->ops->reset_buf( p, &p->buf_out );
+       len2 = p->max_send - 100;       /* For safety margin */
+       len2 = len > len2 ? len2 : len;
 
-       if ( len > p->max_send - 100 ) {
-               len = p->max_send - 100;        /* For safety margin */
+       /* If we're just retrying a partial write, tell the
+        * caller it's done. Let them call again if there's
+        * still more left to write.
+        */
+       if ( p->flags & LDAP_PVT_SASL_PARTIAL_WRITE ) {
+               p->flags ^= LDAP_PVT_SASL_PARTIAL_WRITE;
+               return len2;
        }
 
-       ret = p->ops->encode( p, buf, len, &p->buf_out );
+       /* now encode the next packet. */
+       p->ops->reset_buf( p, &p->buf_out );
+
+       ret = p->ops->encode( p, buf, len2, &p->buf_out );
 
        if ( ret != 0 ) {
                ber_log_printf( LDAP_DEBUG_ANY, sbiod->sbiod_sb->sb_debug,
@@ -720,10 +781,24 @@ sb_sasl_generic_write( Sockbuf_IO_Desc *sbiod, void *buf, ber_len_t len)
 
        ret = ber_pvt_sb_do_write( sbiod, &p->buf_out );
 
+       if ( ret < 0 ) {
+               /* error? */
+               int err = sock_errno();
+               /* caller can retry this */
+               if ( err == EAGAIN || err == EWOULDBLOCK || err == EINTR )
+                       p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE;
+               return ret;
+       } else if ( p->buf_out.buf_ptr != p->buf_out.buf_end ) {
+               /* partial write? pretend nothing got written */
+               p->flags |= LDAP_PVT_SASL_PARTIAL_WRITE;
+               sock_errset(EAGAIN);
+               len2 = -1;
+       }
+
        /* return number of bytes encoded, not written, to ensure
         * no byte is encoded twice (even if only sent once).
         */
-       return len;
+       return len2;
 }
 
 static int