diff --git a/src/Auth/Source/Ldap.php b/src/Auth/Source/Ldap.php index 5d14e5cbc..463c8b8e9 100644 --- a/src/Auth/Source/Ldap.php +++ b/src/Auth/Source/Ldap.php @@ -67,13 +67,14 @@ public function __construct(array $info, array $config) /** - * Attempt to log in using the given username and password. + * Attempt to log in using SASL and the given username and password. * * @param string $username The username the user wrote. * @param string $password The password the user wrote. + * @param array|null $sasl_args SASL options * @return array Associative array with the users attributes. */ - protected function login(string $username, #[\SensitiveParameter]string $password): array + protected function login_sasl(string $username, #[\SensitiveParameter]string $password, ?array $sasl_args): array { if (preg_match('/^\s*$/', $password)) { // The empty string is considered an anonymous bind to Symfony @@ -128,7 +129,14 @@ protected function login(string $username, #[\SensitiveParameter]string $passwor } /* Verify the credentials */ - $this->connector->bind($dn, $password); + if (isset($sasl_args)) { + Assert::isArray($sasl_args); + + $this->connector->saslBind($dn, $password, $sasl_args['mech'], $sasl_args['realm'], $sasl_args['authc_id'], $sasl_args['authz_id'], $sasl_args['props']); + $dn = $this->connector->whoami(); + } else { + $this->connector->bind($dn, $password); + } /* If the credentials were correct, rebind using a privileged account to read attributes */ $readUsername = $this->ldapConfig->getOptionalString('priv.username', null); @@ -145,6 +153,18 @@ protected function login(string $username, #[\SensitiveParameter]string $passwor return $this->processAttributes(/** @scrutinizer-ignore-type */$entry); } + /** + * Attempt to log in using the given username and password. + * + * @param string $username The username the user wrote. + * @param string $password The password the user wrote. + * @return array Associative array with the users attributes. + */ + protected function login(string $username, #[\SensitiveParameter]string $password): array + { + return $this->login_sasl($username, $password); + } + /** * Attempt to find a user's attributes given its username. diff --git a/src/Auth/Source/LdapMulti.php b/src/Auth/Source/LdapMulti.php index ff006a278..c47e77204 100644 --- a/src/Auth/Source/LdapMulti.php +++ b/src/Auth/Source/LdapMulti.php @@ -105,13 +105,15 @@ public function __construct(array $info, array $config) /** - * Attempt to log in using the given username and password. + * Attempt to log in using SASL and the given username and password. * * @param string $username The username the user wrote. * @param string $password The password the user wrote. + * @param string $organizaion The organization the user chose. + * @param array|null $sasl_args SASL options * @return array Associative array with the users attributes. */ - protected function login(string $username, #[\SensitiveParameter]string $password, string $organization): array + protected function login_sasl(string $username, #[\SensitiveParameter]string $password, string $organization, ?array $sasl_args): array { if ($this->includeOrgInUsername) { $username = $username . '@' . $organization; @@ -128,15 +130,27 @@ protected function login(string $username, #[\SensitiveParameter]string $passwor $ldap = new class (['AuthId' => $authsource], $sourceConfig->toArray()) extends Ldap { - public function loginOverload(string $username, #[\SensitiveParameter]string $password): array + public function loginOverload(string $username, #[\SensitiveParameter]string $password, ?array $sasl_args): array { - return $this->login($username, $password); + return $this->login_sasl($username, $password, $sasl_args); } }; - return $ldap->loginOverload($username, $password); + return $ldap->loginOverload($username, $password, $sasl_args); } + /** + * Attempt to log in using the given username and password. + * + * @param string $username The username the user wrote. + * @param string $password The password the user wrote. + * @param string $organizaion The organization the user chose. + * @return array Associative array with the users attributes. + */ + protected function login(string $username, #[\SensitiveParameter]string $password, string $organization): array + { + return $this->login_sasl($username, $password, $organization); + } /** * Retrieve list of organizations. diff --git a/src/Connector/Ldap.php b/src/Connector/Ldap.php index a5e9208c8..475e42a72 100644 --- a/src/Connector/Ldap.php +++ b/src/Connector/Ldap.php @@ -109,6 +109,32 @@ public function bind(?string $username, #[\SensitiveParameter]?string $password) } } + /** + * @inheritDoc + */ + public function saslBind(?string $username, #[\SensitiveParameter]?string $password, ?string $mech, ?string $realm, ?string $authcId, ?string $authzId, ?string $props): void + + { + try { + $this->connection->saslBind($username, strval($password), $mech, $realm, $authcId, $authzId, $props); + } catch (InvalidCredentialsException $e) { + throw new Error\Error($this->resolveBindError($e)); + } + + if ($username === null) { + Logger::debug("LDAP bind(): Anonymous bind succesful."); + } else { + Logger::debug(sprintf("LDAP bind(): Bind successful for DN '%s'.", $username)); + } + } + + /** + * @inheritDoc + */ + public function whoami(): string + { + return $this->connection->whoami(); + } /** * @inheritDoc diff --git a/src/ConnectorInterface.php b/src/ConnectorInterface.php index fe6ef93b5..fdb0f686a 100644 --- a/src/ConnectorInterface.php +++ b/src/ConnectorInterface.php @@ -28,6 +28,39 @@ public function bind( ): void; + /** + * Bind to an LDAP-server using SASL + * + * @param string|null $username + * @param string|null $password Null for passwordless logon + * @param string|null $mech + * @param string|null $realm + * @param string|null $authcId + * @param string|null $authzId + * @param string|null $props + * @return void + * + * @throws \SimpleSAML\Error\Exception if none of the LDAP-servers could be contacted + */ + public function saslBind( + ?string $username, + ?string $password, + ?string $mech, + ?string $realm, + ?string $authcId, + ?string $authzId, + ?string $props + ): void; + + + /** + * Return the authenticated DN + * + * @return string + */ + public function whoami(): string; + + /** * Search the LDAP-directory for a specific object *