Skip to content

Commit

Permalink
Add support for SASL bind (#62)
Browse files Browse the repository at this point in the history
* Add support for SASL bind

We introduce a ldap_sasl() method in Ldap and LdapMulti with
an optionnal array of SASL options. A module that subclasses
Ldap or Ldapmulti can use it instead of simple login().

This requires SASL bind support in Symfony, which has been
merged in the 7.3 branch. SimpleSAMLphp uses Symfony 7.2.
How should this ne handled? I can backport the patches
for Symfony 7.2, but do we have a way to fold them in
the simpleSAMLphp package?

* Check for Symfony's saslBind and whoami method presence

* Fix QA-issues

* Rename method

* Rename method

* Fix QA

* Remove whitespace from end of line

* Remove whitespace from end of line

* Fix long line

* Fix: Allow optional sasl_args parameter

---------

Co-authored-by: Tim van Dijen <[email protected]>
  • Loading branch information
manu0401 and tvdijen authored Oct 31, 2024
1 parent 14c06d3 commit b4b1fbd
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 10 deletions.
34 changes: 31 additions & 3 deletions src/Auth/Source/Ldap.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 loginSasl(string $username, #[\SensitiveParameter]string $password, array $sasl_args = []): array
{
if (preg_match('/^\s*$/', $password)) {
// The empty string is considered an anonymous bind to Symfony
Expand Down Expand Up @@ -128,7 +129,22 @@ protected function login(string $username, #[\SensitiveParameter]string $passwor
}

/* Verify the credentials */
$this->connector->bind($dn, $password);
if (empty($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);
Expand All @@ -145,6 +161,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->loginSasl($username, $password);
}


/**
* Attempt to find a user's attributes given its username.
Expand Down
35 changes: 28 additions & 7 deletions src/Auth/Source/LdapMulti.php
Original file line number Diff line number Diff line change
Expand Up @@ -105,14 +105,20 @@ 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 loginSasl(
string $username,
#[\SensitiveParameter]string $password,
string $organization,
?array $sasl_args,
): array {
if ($this->includeOrgInUsername) {
$username = $username . '@' . $organization;
}
Expand All @@ -128,15 +134,30 @@ 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
{
return $this->login($username, $password);
public function loginOverload(
string $username,
#[\SensitiveParameter]string $password,
?array $sasl_args,
): array {
return $this->loginSasl($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->loginSasl($username, $password, $organization);
}

/**
* Retrieve list of organizations.
Expand Down
40 changes: 40 additions & 0 deletions src/Connector/Ldap.php
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,46 @@ 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 {
if (!method_exists($this->connection, 'saslBind')) {
throw new Error\Error("SASL not implemented");
}

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
{
if (!method_exists($this->connection, 'whoami')) {
throw new Error\Error("SASL not implemented");
}

return $this->connection->whoami();
}

/**
* @inheritDoc
Expand Down
33 changes: 33 additions & 0 deletions src/ConnectorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down

0 comments on commit b4b1fbd

Please sign in to comment.