RFC 4256

Generic Message Exchange Authentication for the Secure Shell Protocol (SSH)

πŸ“… published January 2006, from ietf

RFC 4256: Generic Message Exchange Authentication for the Secure Shell Protocol (SSH) πŸŽ€

Okay bestie, this one is from the Network Working Group 🧚 It's a Standards Track doc dated January 2006, written by F. Cusack (savecore.net) and M. Forssen (AppGate Network Security AB) πŸ’…


Status of This Memo πŸ“œ

This doc specifies an Internet standards track protocol for the Internet community, and it's asking for discussion and suggestions for improvements 🫢 If you want the standardization state and status of this protocol, check the current edition of the "Internet Official Protocol Standards" (STD 1). Distribution of this memo is unlimited ✨

Copyright (C) The Internet Society (2006).

Abstract 🌸

SSH is the protocol for secure remote login and other secure network services over an insecure network, so it's kind of a big deal πŸ‘‘ This doc describes a general purpose authentication method for SSH. It's built for interactive authentication, where the auth data gets typed in on a keyboard (or some equivalent alphanumeric input device) ⌨️ The major goal is to let the SSH client support a whole class of authentication mechanisms without needing to know the specifics of each actual one. Iconic, honestly πŸ¦‹

1. Introduction ✨

Okay so the SSH authentication protocol [SSH-USERAUTH] is a general-purpose user authentication protocol πŸ’– It's meant to run on top of the SSH transport layer protocol [SSH-TRANS]. The authentication protocol assumes that the protocols underneath it already provide integrity and confidentiality protection πŸ”

This doc describes a general purpose authentication method for the SSH authentication protocol. It's a great fit for interactive authentication methods that don't need any special software support on the client side. Instead, all the auth data gets entered through the keyboard ⌨️ The big goal is for the SSH client to have little or no knowledge of the specifics of the underlying authentication mechanism(s) the SSH server uses. The best part? The server can pick or change those mechanisms whenever it wants without anyone having to update client code 🧚

The name for this authentication method is "keyboard-interactive" πŸŽ€

This doc should be read only after you've read the SSH architecture document [SSH-ARCH] and the SSH authentication document [SSH-USERAUTH]. It uses terminology and notation from both of those freely, without explaining them again πŸ“š

This doc also covers some of the client's interaction with the user while it's collecting the auth info. Honestly that's a little outside the scope of a protocol spec, but it's included anyway, because some parts of the protocol were specifically designed around user interface issues. Leaving that info out could lead to incompatible or awkward implementations πŸ™ˆ

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC-2119] πŸ’…

2. Rationale πŸ“

Right now, the auth methods defined for SSH are tightly coupled with the underlying authentication mechanism. That makes it hard to add new mechanisms, because every client has to be updated to support the new one 😭 With the generic method defined here, clients won't need code changes to support new authentication mechanisms. And if a separate authentication layer is used, such as [PAM], then the server may not need any code changes either ✨

This is a huge perk over other methods, like the "password" method (defined in [SSH-USERAUTH]). New (presumably stronger) methods can be added "at will", and system security can be transparently enhanced πŸ’ͺπŸ”₯

Challenge-response and One Time Password mechanisms are also easily supported with this auth method πŸ¦‹

Not gonna lie though, this method is limited to authentication mechanisms that don't require any special code on the client, like hardware drivers or password mangling πŸ’€

3. Protocol Exchanges πŸ’œ

The client starts the authentication with an SSH_MSG_USERAUTH_REQUEST message. Then the server asks the client for auth info with an SSH_MSG_USERAUTH_INFO_REQUEST message. The client collects the info from the user and responds with an SSM_MSG_USERAUTH_INFO_RESPONSE message. The server MUST NOT send another SSH_MSG_USERAUTH_INFO_REQUEST before it has received the answer from the client πŸ™…β€β™€οΈ

3.1. Initial Exchange 🌈

The authentication starts with the client sending this packet:

byte      SSH_MSG_USERAUTH_REQUEST
string    user name (ISO-10646 UTF-8, as defined in [RFC-3629])
string    service name (US-ASCII)
string    "keyboard-interactive" (US-ASCII)
string    language tag (as defined in [RFC-3066])
string    submethods (ISO-10646 UTF-8)

The language tag is deprecated, so it SHOULD be the empty string πŸ™ƒ It may be removed in a future revision of this specification. Instead, the server SHOULD pick the language based on the tags communicated during key exchange [SSH-TRANS] 🌍

If the language tag isn't the empty string, the server SHOULD use that specified language for any messages sent to the client as part of this protocol. The language tag SHOULD NOT be used for language selection for messages outside of this protocol. If the server doesn't support the requested language, the language to be used is implementation-dependent πŸ€·β€β™€οΈ

The submethods field exists so the user can give a hint about which actual methods they want to use πŸŽ€ It's a comma-separated list of authentication submethods (software or hardware) that the user prefers. If the client knows the submethods the user prefers, probably through a configuration setting, it MAY use the submethods field to pass that along to the server. Otherwise, it MUST send the empty string 🫢

The actual names of the submethods are something the user and the server need to agree on 🀝

How the server interprets the submethods field is implementation-dependent 🧚

One possible strategy for the server is this: unless the user can use multiple different submethods, the server ignores this field. If the user can authenticate with one of several different submethods, the server should treat the submethods field as a hint for which submethod the user wants to use this time πŸ‘‘

Note that when this message is sent to the server, the client has NOT prompted the user for a password yet. So that info is NOT included with this initial message (unlike the "password" method) πŸ™ˆ

The server MUST reply with an SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or SSH_MSG_USERAUTH_INFO_REQUEST message πŸ’…

The server SHOULD NOT reply with the SSH_MSG_USERAUTH_FAILURE message if the failure is based on the user name or service name. Instead, it SHOULD send SSH_MSG_USERAUTH_INFO_REQUEST message(s), which look just like the ones that would've been sent if authentication should proceed. Then it sends the failure message (after a suitable delay, as described below) 😈 The goal is to make it impossible to find valid usernames by comparing the results when authenticating as different users. Lowkey genius 🧠

The server MAY reply with an SSH_MSG_USERAUTH_SUCCESS message if no authentication is required for the user in question. However, a better approach, for the reasons discussed above, might be to reply with an SSH_MSG_USERAUTH_INFO_REQUEST message and ignore (don't validate) the response πŸ¦‹

3.2. Information Requests πŸ’–

Requests are generated from the server using the SSH_MSG_USERAUTH_INFO_REQUEST message πŸ“¨

The server may send as many requests as needed to authenticate the client, and the client MUST be prepared to handle multiple exchanges 🌸 However, the server MUST NOT ever have more than one SSH_MSG_USERAUTH_INFO_REQUEST message outstanding. So it can't send another request before the client has answered πŸ™…β€β™€οΈ

The SSH_MSG_USERAUTH_INFO_REQUEST message is defined as follows:

byte      SSH_MSG_USERAUTH_INFO_REQUEST
string    name (ISO-10646 UTF-8)
string    instruction (ISO-10646 UTF-8)
string    language tag (as defined in [RFC-3066])
int       num-prompts
string    prompt[1] (ISO-10646 UTF-8)
boolean   echo[1]
...
string    prompt[num-prompts] (ISO-10646 UTF-8)
boolean   echo[num-prompts]

The language tag is deprecated, so it SHOULD be the empty string πŸ™ƒ It may be removed in a future revision of this specification. Instead, the server SHOULD pick the language used based on the tags communicated during key exchange [SSH-TRANS] 🌍

If the language tag isn't the empty string, the server SHOULD use that specified language for any messages sent to the client as part of this protocol. The language tag SHOULD NOT be used for language selection for messages outside of this protocol. If the server doesn't support the requested language, the language to be used is implementation-dependent πŸ€·β€β™€οΈ

The server SHOULD keep in mind that some clients may not be able to properly display a long name or prompt field (see next section), so it should limit the lengths of those fields if possible πŸŽ€ For example, instead of an instruction field of "Enter Password" and a prompt field of "Password for user23@host.domain: ", a better choice might be an instruction field of "Password authentication for user23@host.domain" and a prompt field of "Password: ". This method is expected to typically be backended by [PAM], so those kinds of choices wouldn't be possible 🧚

The name and instruction fields MAY be empty strings, and the client MUST be prepared to handle that correctly 🫢 The prompt field(s) MUST NOT be empty strings πŸ’…

The num-prompts field may be 0. If it is, there won't be any prompt/echo fields in the message, but the client SHOULD still display the name and instruction fields (as described below) ✨

3.3. User Interface ✨

okay so bestie, when the server sends a request message, the client SHOULD prompt the user like this πŸ’…

a command line interface (CLI) client SHOULD print the name and instruction (if non-empty), adding newlines πŸŽ€ then, for each prompt in turn, the client SHOULD display the prompt and read the user input. easy peasy 🌸

a graphical user interface (GUI) client has SO many choices for how to prompt the user πŸ¦‹ one option is to use the name field (maybe with the app's name in front) as the title of a dialog window where the prompts show up. the instruction field becomes a text message in there, and the prompts become labels for text entry fields. all fields SHOULD be presented to the user πŸ’– for example, an implementation SHOULD NOT toss the name field just because its windows don't have titles πŸ™ˆ it SHOULD find another way to show that info. and if prompts show up in a dialog window, the client SHOULD NOT give each prompt its own separate window 🫢

now the length stuff, which is very important πŸ‘‘ all clients MUST properly handle an instruction field with embedded newlines ✨ they SHOULD also be able to display at least 30 characters for the name and prompts 🌈 if the server sends names or prompts longer than 30 characters, the client MAY truncate them to whatever length it can show πŸ’œ but if it does truncate anything, there MUST be an obvious indication that truncation happened, no cap 😭 and the instruction field SHOULD NOT be truncated πŸ’€

clients SHOULD use control character filtering, as discussed in [SSH-ARCH], to avoid attacks that sneak terminal control characters into the fields being displayed πŸ”₯ that one is giving security main character energy πŸ‘‘

for each prompt, the matching echo field tells you whether the user's typing should be echoed as they type 🧚 clients SHOULD correctly echo or mask user input for each prompt independently of the other prompts in the request message πŸ¦‹ if a client doesn't honor the echo field for whatever reason, it MUST err on the side of masking input πŸ” a GUI client might like a checkbox to toggle echo/mask, iykyk πŸ“ clients SHOULD NOT add any extra characters to the prompt, like ": " (colon-space), because the server is responsible for supplying all the text the user sees πŸ’… and clients MUST also accept empty responses from the user and pass them on as empty strings 🌸

3.4. Information Responses ✨

after the client gets the requested info from the user, it MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message πŸ’–

here's the layout of that message, bestie πŸŽ€

byte      SSH_MSG_USERAUTH_INFO_RESPONSE
int       num-responses
string    response[1] (ISO-10646 UTF-8)
...
string    response[num-responses] (ISO-10646 UTF-8)

the responses are encoded in ISO-10646 UTF-8 πŸ’œ it's up to the server how it interprets and validates them πŸ¦‹ however, if the client reads the responses in some other encoding (like ISO 8859-1), it MUST convert them to ISO-10646 UTF-8 before sending 🌈

from an internationalization standpoint, the goal is that if a user types responses, authentication still works no matter what OS or client software they're on 🌍 getting there takes normalization 🧠 systems that support non-ASCII passwords SHOULD always normalize passwords and usernames whenever they're added to the database, or compared (with or without hashing) to existing entries 🫢 SSH implementations that both store passwords and compare them SHOULD use [SASLPREP] for normalization πŸ’…

if the num-responses field doesn't match the num-prompts field in the request message, the server MUST send a failure message πŸ™…β€β™€οΈ

if the server sends a 0' num-prompts field in the request message, the client MUST send a response message with a 0' num-responses field to finish the exchange 🌸

the responses MUST be ordered the same way the prompts were ordered πŸŽ€ so response[n] MUST be the answer to prompt[n] ✨

after receiving the response, the server MUST send either an SSH_MSG_USERAUTH_SUCCESS, an SSH_MSG_USERAUTH_FAILURE, or another SSH_MSG_USERAUTH_INFO_REQUEST message πŸ‘‘

if the server fails to authenticate the user (through the underlying authentication mechanism(s)), it SHOULD NOT send another request message to try to get new authentication data πŸ™…β€β™€οΈ instead, it SHOULD send a failure message πŸ’€ the only time the server should send multiple request messages is if additional authentication data is needed (like when there are multiple underlying authentication mechanisms that must be used to authenticate the user) πŸ”—

if the server intends to respond with a failure message, it MAY delay for an implementation-dependent time before sending it to the client ⏳ implementations will probably make that delay configurable, and a suggested default is 2 seconds πŸ¦‹


4. Authentication Examples 🧚

okay here are two example exchanges between a client and server πŸ’– the first is challenge/response with a handheld token, which is an authentication that isn't possible any other way πŸ”

C:   byte      SSH_MSG_USERAUTH_REQUEST
C:   string    "user23"
C:   string    "ssh-userauth"
C:   string    "keyboard-interactive"
C:   string    ""
C:   string    ""

S:   byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:   string    "CRYPTOCard Authentication"
S:   string    "The challenge is '14315716'"
S:   string    "en-US"
S:   int       1
S:   string    "Response: "
S:   boolean   TRUE

[Client prompts user for password]

C:   byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:   int       1
C:   string    "6d757575"

S:   byte      SSH_MSG_USERAUTH_SUCCESS

the second example is standard password authentication, but here the user's password is expired 😭

C:   byte      SSH_MSG_USERAUTH_REQUEST
C:   string    "user23"
C:   string    "ssh-userauth"
C:   string    "keyboard-interactive"
C:   string    "en-US"
C:   string    ""

S:   byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:   string    "Password Authentication"
S:   string    ""
S:   string    "en-US"
S:   int       1
S:   string    "Password: "
S:   boolean   FALSE

[Client prompts user for password]

C:   byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:   int       1
C:   string    "password"

S:   byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:   string    "Password Expired"
S:   string    "Your password has expired."
S:   string    "en-US"
S:   int       2
S:   string    "Enter new password: "
S:   boolean   FALSE
S:   string    "Enter it again: "
S:   boolean   FALSE

[Client prompts user for new password]

C:   byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:   int       2
C:   string    "newpass"
C:   string    "newpass"

S:   byte      SSH_MSG_USERAUTH_INFO_REQUEST
S:   string    "Password changed"
S:   string    "Password successfully changed for user23."
S:   string    "en-US"
S:   int       0
[Client displays message to user]

C:   byte      SSH_MSG_USERAUTH_INFO_RESPONSE
C:   int       0

S:   byte      SSH_MSG_USERAUTH_SUCCESS

5. IANA Considerations πŸ—‚οΈ

the userauth type "keyboard-interactive" is used for this authentication method πŸ”‘

the following method-specific constants are used with this authentication method πŸ’œ

SSH_MSG_USERAUTH_INFO_REQUEST           60
SSH_MSG_USERAUTH_INFO_RESPONSE          61

6. Security Considerations 🚨

the authentication protocol and this authentication method depend on the security of the underlying SSH transport layer πŸ” without the confidentiality that transport provides, any authentication data passed with this method can be intercepted 😱

the number of client-server exchanges needed to finish an authentication with this method can vary πŸ‘€ an observer might learn something valuable just by counting that number. for example, an observer may guess that a user's password has expired, and with more watching, figure out the password lifetime set by a site's expiration policy πŸ’€

7. References πŸ“š

7.1. Normative References πŸ’…

[RFC-2119]      Bradner, S., "Key words for use in RFCs to Indicate
                Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC-3629]      Yergeau, F., "UTF-8, a transformation format of ISO
                10646", STD 63, RFC 3629, November 2003.

[RFC-3066]      Alvestrand, H., "Tags for the Identification of
                Languages", BCP 47, RFC 3066, January 2001.

[SSH-ARCH]      Ylonen, T. and C. Lonvick, Ed., "The Secure Shell
                (SSH) Protocol Architecture", RFC 4251, January 2006.

[SSH-USERAUTH]  Ylonen, T. and C. Lonvick, Ed., "The Secure Shell
                (SSH) Authentication Protocol", RFC 4252, January
                2006.

[SSH-TRANS]     Ylonen, T. and C. Lonvick, Ed., "The Secure Shell
                (SSH) Transport Layer Protocol", RFC 4253, January
                2006.

[SASLPREP]      Zeilenga, K., "SASLprep: Stringprep Profile for User
                Names and Passwords", RFC 4013, February 2005.

7.2. Informative References 🌷

[PAM]           Samar, V., Schemers, R., "Unified Login With
                Pluggable Authentication Modules (PAM)", OSF RFC
                86.0, October 1995.

Authors' Addresses πŸ’Œ

Frank Cusack
savecore.net

EMail: frank@savecore.net


Martin Forssen
AppGate Network Security AB
Otterhallegatan 2
SE-411 18 Gothenburg
SWEDEN

EMail: maf@appgate.com

Copyright (C) The Internet Society (2006).

This document is subject to the rights, licenses and restrictions contained in BCP 78, and except as set forth therein, the authors retain all their rights.

This document and the information contained herein are provided on an "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. πŸ’–

Intellectual Property ✨

okay so the IETF is basically saying "we are NOT picking a side on whether any patents or IP rights apply to the tech in this doc" πŸ™…β€β™€οΈ and it's not claiming it went hunting for those rights either. if you want the rules on rights in RFC docs, check out BCP 78 and BCP 79 πŸ“š

if you want to see IPR disclosures that were filed with the IETF Secretariat, or any licensing assurances, or what happened when someone tried to get a general license or permission to use proprietary rights for implementing or using this spec, that all lives in the IETF online IPR repository at http://www.ietf.org/ipr πŸ”

and the IETF really wants to hear from you 🫢 if you know about any copyrights, patents, patent applications, or other proprietary rights that might cover tech you'd need to implement this standard, send it to ietf-ipr@ietf.org πŸ’Œ


Acknowledgement 🌸

the RFC Editor function gets its funding from the IETF Administrative Support Activity (IASA) πŸ’– so shoutout to them for keeping the lights on ✨

πŸ“¦ where this page comes from