SSH-Verbindungstest: 'password' vor 'keyboard-interactive' bevorzugen
Bei einigen Switches/APs (u.a. ArubaOS-Varianten), die Server-seitig sowohl 'password' als auch 'keyboard-interactive' anbieten, fuehrte keyboard-interactive zu einem stillen Fehlschlag ganz ohne sichtbaren Prompt im Browser-Terminal (RFC 4256 erlaubt eine erste Runde ganz ohne Prompts -- fuer den Nutzer unsichtbar -- gefolgt von sofortigem Scheitern statt einer zweiten Runde mit dem eigentlichen Passwort- Prompt). Das erklaerte exakt das gemeldete Symptom: kein Prompt, direkt 'Zugangsdaten falsch'. 'password' wird jetzt bevorzugt, wenn der Server es ueberhaupt anbietet (Ende-zu-Ende gegen einen echten Host verifiziert: Host-Key-Bestaetigung -> Passwort-Prompt -> erfolgreicher Login, keine Regression). Die keyboard-interactive-Handler-Funktion wurde zusaetzlich gehaertet: title/instructions werden jetzt mit angezeigt (manche Geraete legen den Hinweistext dort statt in prompt_list ab) und leere prompt_text-Strings bekommen einen Passwort-Fallback-Text, falls keyboard-interactive doch der einzig angebotene Weg ist.
This commit is contained in:
+32
-12
@@ -5886,24 +5886,44 @@ def _authenticate_interactive(ws, transport, username):
|
||||
if transport.is_authenticated():
|
||||
return True
|
||||
|
||||
if "keyboard-interactive" in allowed:
|
||||
def handler(title, instructions, prompt_list):
|
||||
responses = []
|
||||
for prompt_text, echo in prompt_list:
|
||||
ws.send(f"\r\n{prompt_text}")
|
||||
responses.append(_terminal_read_line(ws, echo=bool(echo)) or "")
|
||||
return responses
|
||||
try:
|
||||
transport.auth_interactive(username, handler)
|
||||
except paramiko.AuthenticationException:
|
||||
pass
|
||||
elif "password" in allowed:
|
||||
# "password" bewusst VOR "keyboard-interactive" versuchen, auch wenn der
|
||||
# Server beides anbietet: viele Switches/APs (u.a. diverse ArubaOS-
|
||||
# Varianten) bieten zwar "keyboard-interactive" an, deren Server-seitige
|
||||
# Umsetzung ist aber teils nicht robust — z. B. eine erste Runde ganz
|
||||
# ohne Prompts (laut RFC 4256 gültig, aber unsichtbar für den Nutzer),
|
||||
# gefolgt von sofortigem Scheitern statt einer zweiten Runde mit dem
|
||||
# eigentlichen Passwort-Prompt. Das erklärt exakt das beobachtete
|
||||
# Symptom: im Terminal erscheint NIE ein Prompt, nur direkt "Zugangs-
|
||||
# daten falsch". "password" ist der einfachere, zuverlässiger
|
||||
# unterstützte Pfad (Ende-zu-Ende gegen einen echten Host verifiziert)
|
||||
# und wird deshalb bevorzugt, wenn der Server ihn überhaupt anbietet.
|
||||
if "password" in allowed:
|
||||
ws.send("\r\nPasswort: ")
|
||||
pw = _terminal_read_line(ws, echo=False)
|
||||
try:
|
||||
transport.auth_password(username, pw or "")
|
||||
except paramiko.AuthenticationException:
|
||||
pass
|
||||
elif "keyboard-interactive" in allowed:
|
||||
def handler(title, instructions, prompt_list):
|
||||
# title/instructions zusätzlich anzeigen: manche Geräte legen
|
||||
# den eigentlichen Hinweistext ("Password:") dort statt in
|
||||
# prompt_list ab. Leere prompt_text-Strings (ebenfalls bei
|
||||
# manchen Geräten üblich) bekommen einen sinnvollen Fallback,
|
||||
# damit dem Nutzer nie eine Eingabeaufforderung ganz ohne
|
||||
# sichtbaren Text präsentiert wird.
|
||||
for text in (title, instructions):
|
||||
if text:
|
||||
ws.send(f"\r\n{text}")
|
||||
responses = []
|
||||
for prompt_text, echo in prompt_list:
|
||||
ws.send(f"\r\n{prompt_text or 'Passwort: '}")
|
||||
responses.append(_terminal_read_line(ws, echo=bool(echo)) or "")
|
||||
return responses
|
||||
try:
|
||||
transport.auth_interactive(username, handler)
|
||||
except paramiko.AuthenticationException:
|
||||
pass
|
||||
else:
|
||||
ws.send(f"\r\n\x1b[31mServer bietet keine unterstützte Auth-Methode an ({', '.join(allowed) or 'keine'}).\x1b[0m\r\n")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user