diff --git a/lib/SimpleSAML/Auth/LDAP.php b/lib/SimpleSAML/Auth/LDAP.php
index 2dcda1676fb2511792a6ef7c23a894547ec224ce..db78e2b11a9a8336fa5704b70664c16d70d965ec 100644
--- a/lib/SimpleSAML/Auth/LDAP.php
+++ b/lib/SimpleSAML/Auth/LDAP.php
@@ -29,6 +29,11 @@ class SimpleSAML_Auth_LDAP {
 	 */
 	private $ldap = null;
 
+	/**
+	 * LDAP user: authz_id if SASL is in use, binding dn otherwise
+	 */
+	private $authz_id = null;
+
 	/**
 	 * Timeout value, in seconds.
 	 *
@@ -294,25 +299,57 @@ class SimpleSAML_Auth_LDAP {
 	 * The DN used.
 	 * @param string $password
 	 * The password used.
+	 * @param array $sasl_args
+	 * Array of SASL options for SASL bind
 	 * @return bool
-	 * Returns TRUE if successful, FALSE if LDAP_INVALID_CREDENTIALS.
+	 * Returns TRUE if successful, FALSE if
+	 * LDAP_INVALID_CREDENTIALS, LDAP_X_PROXY_AUTHZ_FAILURE,
+	 * LDAP_INAPPROPRIATE_AUTH, LDAP_INSUFFICIENT_ACCESS
 	 * @throws SimpleSAML_Error_Exception on other errors
 	 */
-	public function bind($dn, $password) {
+	public function bind($dn, $password, array $sasl_args = NULL) {
+		$authz_id = null;
 
-		// Bind, with error handling.
-		if (@ldap_bind($this->ldap, $dn, $password)) {
+		if ($sasl_args != NULL) {
+			if (!function_exists(ldap_sasl_bind)) {
+				$ex_msg = 'Library - missing SASL support';
+				throw $this->makeException($ex_msg);
+			}
+
+			// SASL Bind, with error handling.
+			$authz_id = $sasl_args['authz_id'];
+			$error = @ldap_sasl_bind($this->ldap, $dn, $password,
+						 $sasl_args['mech'],
+						 $sasl_args['realm'],
+						 $sasl_args['authc_id'],
+						 $sasl_args['authz_id'],
+						 $sasl_args['props']);
+		} else {
+			// Simple Bind, with error handling.
+			$authz_id = $dn;
+			$error = @ldap_bind($this->ldap, $dn, $password);
+		}
 
+		if ($error === TRUE) {
 			// Good.
+			$this->authz_id = $authz_id;
 			SimpleSAML_Logger::debug('Library - LDAP bind(): Bind successful with DN \'' . $dn . '\'');
 			return TRUE;
 
 		}
 
-		/* Handle invalid DN/password.
-		 * LDAP_INVALID_CREDENTIALS */
-		if (ldap_errno($this->ldap) === 49) {
+		/* Handle errors
+		 * LDAP_INVALID_CREDENTIALS
+		 * LDAP_INSUFFICIENT_ACCESS */
+		switch(ldap_errno($this->ldap)) {
+		case 47:	/* LDAP_X_PROXY_AUTHZ_FAILURE */
+		case 48:	/* LDAP_INAPPROPRIATE_AUTH */
+		case 49:	/* LDAP_INVALID_CREDENTIALS */
+		case 50:	/* LDAP_INSUFFICIENT_ACCESS */
 			return FALSE;
+			break;
+		default;
+			break;
 		}
 
 		// Bad.
@@ -503,6 +540,47 @@ class SimpleSAML_Auth_LDAP {
 		return $string;
 	}
 
+	/**
+	 * Convert SASL authz_id into a DN
+	 */
+	private  function authzid_to_dn($searchBase, $searchAttributes, $authz_id) {
+		if (preg_match("/^dn:/", $authz_id))
+			return preg_replace("/^dn:/", "", $authz_id);
+
+		if (preg_match("/^u:/", $authz_id))
+			return $this->searchfordn($searchBase, $searchAttributes,
+			                          preg_replace("/^u:/", "", $authz_id));
+
+		return $authz_id;
+	}
+
+	/**
+	 * ldap_exop_whoami accessor, if available. Use requested authz_id
+	 * otherwise.
+	 *
+	 * ldap_exop_whoami is not yet included in PHP. For reference, the
+	 * feature request: http://bugs.php.net/bug.php?id=42060
+	 * And the patch against lastest PHP release:
+	 * http://cvsweb.netbsd.org/bsdweb.cgi/pkgsrc/databases/php-ldap/files/ldap-ctrl-exop.patch
+	 */
+	public  function whoami($searchBase, $searchAttributes) {
+		$authz_id = '';
+
+		if (function_exists('ldap_exop_whoami')) {
+			if (ldap_exop_whoami($this->ldap, $authz_id) !== true)
+			    throw $this->getLDAPException('LDAP whoami exop failure');
+		} else {
+			$authz_id = $this->authz_id;
+		}
+
+		$dn = $this->authzid_to_dn($searchBase, $searchAttributes, $authz_id);
+
+		if (!isset($dn) || ($dn == ''))
+			throw $this->getLDAPException('Cannot figure userID');
+
+		return $dn;
+	}
+
 }
 
 ?>
\ No newline at end of file
diff --git a/modules/ldap/lib/Auth/Source/LDAP.php b/modules/ldap/lib/Auth/Source/LDAP.php
index f49ff333cd75cb343b74314556adf9f1b4b381a5..a2c41a31b02f5c52530cda62cb832b7a73b88c4d 100644
--- a/modules/ldap/lib/Auth/Source/LDAP.php
+++ b/modules/ldap/lib/Auth/Source/LDAP.php
@@ -42,13 +42,14 @@ class sspmod_ldap_Auth_Source_LDAP extends sspmod_core_Auth_UserPassBase {
 	 *
 	 * @param string $username  The username the user wrote.
 	 * @param string $password  The password the user wrote.
+	 * param array $sasl_arg  Associative array of SASL options
 	 * @return array  Associative array with the users attributes.
 	 */
-	protected function login($username, $password) {
+	protected function login($username, $password, array $sasl_args = NULL) {
 		assert('is_string($username)');
 		assert('is_string($password)');
 
-		return $this->ldapConfig->login($username, $password);
+		return $this->ldapConfig->login($username, $password, $sasl_args);
 	}
 
 }
diff --git a/modules/ldap/lib/ConfigHelper.php b/modules/ldap/lib/ConfigHelper.php
index 8f1dd5634224e97dbcc9e9bb77bee8ce1c24a4d5..7fc63ba2f1989fdc7a96f83f999ddfe89be81018 100644
--- a/modules/ldap/lib/ConfigHelper.php
+++ b/modules/ldap/lib/ConfigHelper.php
@@ -160,9 +160,10 @@ class sspmod_ldap_ConfigHelper {
 	 *
 	 * @param string $username  The username the user wrote.
 	 * @param string $password  The password the user wrote.
+	 * @param arrray $sasl_args  Array of SASL options for LDAP bind.
 	 * @return array  Associative array with the users attributes.
 	 */
-	public function login($username, $password) {
+	public function login($username, $password, array $sasl_args = NULL) {
 		assert('is_string($username)');
 		assert('is_string($password)');
 
@@ -186,11 +187,15 @@ class sspmod_ldap_ConfigHelper {
 			}
 		}
 
-		if (!$ldap->bind($dn, $password)) {
+		if (!$ldap->bind($dn, $password, $sasl_args)) {
 			SimpleSAML_Logger::info($this->location . ': '. $username . ' failed to authenticate. DN=' . $dn);
 			throw new SimpleSAML_Error_Error('WRONGUSERPASS');
 		}
 
+		/* In case of SASL bind, authenticated and authorized DN may differ */
+		if (isset($sasl_args))
+			$dn = $ldap->whoami($this->searchBase, $this->searchAttributes);
+
 		/* Are privs needed to get the attributes? */
 		if ($this->privRead) {
 			/* Yes, rebind with privs */