Commit 09cef093800ce953e71eeab94490096852b48a7e
1 parent
4e566b9c
Exists in
main
Não bloqueia buscas/bind quando STARTTLS falha no AD
STARTTLS virou best-effort: se o servidor não aceitar negociar na porta 389 (connect error visto em produção), o resto do sistema continua funcionando normalmente via bind simples -- só a troca de senha (unicodePwd) exige canal criptografado e agora falha com uma mensagem clara nesse caso específico, em vez de derrubar toda consulta ao AD. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Showing
1 changed file
with
21 additions
and
9 deletions
Show diff stats
src/Ldap.php
| ... | ... | @@ -8,6 +8,7 @@ final class Ldap |
| 8 | 8 | { |
| 9 | 9 | /** @var resource|\LDAP\Connection */ |
| 10 | 10 | private $connect; |
| 11 | + private bool $encrypted; | |
| 11 | 12 | |
| 12 | 13 | public function __construct() |
| 13 | 14 | { |
| ... | ... | @@ -20,19 +21,22 @@ final class Ldap |
| 20 | 21 | ldap_set_option($this->connect, LDAP_OPT_REFERRALS, 0); |
| 21 | 22 | ldap_set_option($this->connect, LDAP_OPT_NETWORK_TIMEOUT, 5); |
| 22 | 23 | |
| 24 | + $this->encrypted = str_starts_with(strtolower($server), 'ldaps://'); | |
| 25 | + | |
| 23 | 26 | // O AD recusa operações sensíveis (troca de senha via unicodePwd) em canal |
| 24 | - // não criptografado ("Server is unwilling to perform"). Se o servidor não | |
| 25 | - // for ldaps://, negocia STARTTLS antes de qualquer bind. | |
| 26 | - if (!str_starts_with(strtolower($server), 'ldaps://')) { | |
| 27 | - if (!@ldap_start_tls($this->connect)) { | |
| 28 | - throw new LdapException( | |
| 29 | - 'Falha ao negociar STARTTLS com o LDAP: ' . ldap_error($this->connect) . | |
| 30 | - '. Troque ldap.server para ldaps:// no config/local.php se o servidor não suportar STARTTLS.' | |
| 31 | - ); | |
| 32 | - } | |
| 27 | + // não criptografado. Tenta STARTTLS só como bônus: se o servidor não suportar | |
| 28 | + // (nem todo AD aceita na 389), segue sem criptografia -- buscas/bind continuam | |
| 29 | + // funcionando normalmente, só a troca de senha vai exigir isso explicitamente. | |
| 30 | + if (!$this->encrypted && @ldap_start_tls($this->connect)) { | |
| 31 | + $this->encrypted = true; | |
| 33 | 32 | } |
| 34 | 33 | } |
| 35 | 34 | |
| 35 | + public function isEncrypted(): bool | |
| 36 | + { | |
| 37 | + return $this->encrypted; | |
| 38 | + } | |
| 39 | + | |
| 36 | 40 | /** |
| 37 | 41 | * Tenta autenticar o usuário com a própria senha dele (bind direto). |
| 38 | 42 | * Retorna true/false, nunca lança por credencial errada. |
| ... | ... | @@ -118,6 +122,14 @@ final class Ldap |
| 118 | 122 | |
| 119 | 123 | public function modify(string $dn, array $attributes): void |
| 120 | 124 | { |
| 125 | + if (array_key_exists('unicodePwd', $attributes) && !$this->encrypted) { | |
| 126 | + throw new LdapException( | |
| 127 | + 'Não foi possível negociar um canal seguro (STARTTLS) com o LDAP, e o Active Directory ' . | |
| 128 | + 'exige isso para trocar senha. Peça ao time de AD para liberar STARTTLS na porta 389, ' . | |
| 129 | + 'ou troque ldap.server para ldaps://... (porta 636) no config/local.php.' | |
| 130 | + ); | |
| 131 | + } | |
| 132 | + | |
| 121 | 133 | $ok = @ldap_mod_replace($this->connect, $dn, $attributes); |
| 122 | 134 | if (!$ok) { |
| 123 | 135 | throw new LdapException('Erro ao modificar LDAP: ' . ldap_error($this->connect)); | ... | ... |