Commit 09cef093800ce953e71eeab94490096852b48a7e

Authored by Willen Borges Coelho
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));
... ...