Skip to content

Client authentication

What OpenSSH can authenticate with, and why the list looks the way it does.

Six, counting none. The server’s method table (auth2.c:77) and the client’s (sshconnect2.c:387) agree:

MethodImplementationNotes
noneauth2-none.c:66Not real authentication — it probes the server’s method list, or succeeds outright if PermitEmptyPasswords applies
publickeyauth2-pubkey.c:876Plus the [email protected] variant (sshconnect2.c:1332)
passwordauth2-passwd.c:76Plaintext password inside the encrypted channel
keyboard-interactiveauth2-kbdint.c:68Generic challenge/response front-end
hostbasedauth2-hostbased.c:266The client host key signs, for host-to-host trust
gssapi-with-micauth2-gss.c:327Compiled in only under #ifdef GSSAPI — Kerberos and friends, portable OpenSSH only

Two things that look like separate methods but are not:

keyboard-interactive back-ends are pluggable devices, not methods: BSD Auth or PAM (auth2-chall.c:64). PAM is where LDAP, TOTP and RADIUS actually live.

publickey credential types all ride the one method — plain key files, certificates, FIDO/U2F security keys (sk-*), agent-held keys, and PKCS#11 tokens.

A server-driven prompt loop. Rather than defining what the user proves, it defines a way for the server to say “print this text, read a line, send it back” — for as many rounds as it likes. Standardised as RFC 4256, Generic Message Exchange Authentication.

The server sends SSH2_MSG_USERAUTH_INFO_REQUEST (message 60, ssh2.h:127) carrying a name, an instruction string, and N prompts, each with an echo flag:

name, instruction, language, num_prompts, { prompt[i], echo[i] }

The client is deliberately ignorant of what any of it means. input_userauth_info_req loops over the prompts, prefixes each with (user@host) so a malicious server cannot spoof local output, calls read_passphrase with RP_ECHO taken from the echo flag, and returns the strings in INFO_RESPONSE (61).

sequenceDiagram
    participant C as ssh
    participant S as sshd
    participant P as PAM / BSD Auth
    C->>S: USERAUTH_REQUEST "keyboard-interactive"
    S->>P: device->query()
    P-->>S: name, instruction, prompts, echo flags
    S->>C: INFO_REQUEST (msg 60)
    Note over C: render prompts, read answers
    C->>S: INFO_RESPONSE (msg 61)
    S->>P: device->respond()
    P-->>S: more prompts, or done
    S->>C: another INFO_REQUEST, or SUCCESS / FAILURE

password authentication (RFC 4252 §8) has exactly one shape: one hidden field called “password”, plus a single fixed “your password expired” branch. Real-world authentication does not fit inside that:

  • Challenge/response — S/Key, RSA SecurID, RADIUS challenge. The server must display a challenge before the user can answer.
  • Multi-step — password, then a token code, then perhaps a new-password confirmation, with the number of steps decided at runtime.
  • Server-supplied text — “Enter code for token #42”, “Password expires in 3 days”. num_prompts = 0 is legal, making it a pure display message.
  • Per-field echo — a username or token serial should be visible; a PIN should not.

The decisive property is that the client needs no knowledge of the mechanism. Deploy TOTP, RADIUS, or a new PAM module on the server and every existing SSH client already works. That is exactly PAM’s model — its conversation function maps one-to-one onto this exchange — which is why the whole method is built as a pluggable KbdintDevice with query/respond callbacks, PAM or BSD Auth plugged in at auth2-chall.c:64.

The IETF secsh draft dates from around 1999–2001; RFC 4256 was not published until January 2006, long after everyone had shipped it. OpenSSH’s implementation carries Copyright (c) 2001 Markus Friedl and Copyright (c) 2001 Per Allansson, which is the era it landed.

The tree keeps the fossil record in its configuration aliases. Both servconf.h:261 and readconf.c:224 still map these onto KbdInteractiveAuthentication:

AliasOrigin
ChallengeResponseAuthenticationThe pre-standardisation name; demoted to an alias in OpenSSH 8.7 (2021)
SKeyAuthenticationThe S/Key one-time-password scheme it was first built for
TISAuthentication (client only)Inherited from SSH1’s TIS Firewall Toolkit auth — the original “server asks an arbitrary question” method

AuthenticationMethods in sshd_config composes the list above into a multi-factor sequence:

AuthenticationMethods publickey,keyboard-interactive

That requires public key authentication followed by keyboard-interactive. Only methods that come next in one or more lists are offered at each stage, so the client cannot reorder them. Each completed stage returns a USERAUTH_FAILURE with the partial flag set rather than a success — see reading sshconnect2.c for how the client handles that, and server-side authentication for how the server consumes the lists to decide when you are done.

A keyboard-interactive stage can be pinned to one device:

AuthenticationMethods publickey,keyboard-interactive:bsdauth