RFC 4254

The Secure Shell (SSH) Connection Protocol

πŸ“… published January 2006, from ietf

RFC 4254: The Secure Shell (SSH) Connection Protocol πŸ¦‹

Bestie, this is the one that makes SSH actually work! πŸ’– It's from January 2006, it's a Standards Track doc, and it was written by T. Ylonen (SSH Communications Security Corp) and C. Lonvick, Ed. (Cisco Systems, Inc.). It doesn't obsolete or update any other RFCs, so it's a clean, standalone slay. ✨


Status of This Memo πŸ“œ

Okay, so this is a standards track protocol doc for the Internet community. It wants people to talk about it and suggest improvements 🫢. If you want to know where it actually stands, check the current edition of "Internet Official Protocol Standards" (STD 1). Also, distribution is unlimited, so share away 🌸.

Copyright (C) The Internet Society (2006). πŸ’…

Abstract 🧚

Secure Shell (SSH) is a protocol for secure remote login and other secure network services over an insecure network. πŸ”

This doc is all about the SSH Connection Protocol, and honestly it's giving multitasking queen πŸ‘‘. It handles interactive login sessions, remote command execution, forwarded TCP/IP connections, and forwarded X11 connections. ALL of those channels get multiplexed into one single encrypted tunnel. Iconic. 😭

The Connection Protocol was built to run on top of the SSH transport layer and user authentication protocols. Main character energy πŸŽ€.

Table of Contents πŸ—‚οΈ

1. Introduction ....................................................2
2. Contributors ....................................................3
3. Conventions Used in This Document ...............................3
4. Global Requests .................................................4
5. Channel Mechanism ...............................................5
   5.1. Opening a Channel ..........................................5
   5.2. Data Transfer ..............................................7
   5.3. Closing a Channel ..........................................9
   5.4. Channel-Specific Requests ..................................9
6. Interactive Sessions ...........................................10
   6.1. Opening a Session .........................................10
   6.2. Requesting a Pseudo-Terminal ..............................11
   6.3. X11 Forwarding ............................................11
        6.3.1. Requesting X11 Forwarding ..........................11
        6.3.2. X11 Channels .......................................12
   6.4. Environment Variable Passing ..............................12
   6.5. Starting a Shell or a Command .............................13
   6.6. Session Data Transfer .....................................14
   6.7. Window Dimension Change Message ...........................14
   6.8. Local Flow Control ........................................14
   6.9. Signals ...................................................15
   6.10. Returning Exit Status ....................................15
7. TCP/IP Port Forwarding .........................................16
   7.1. Requesting Port Forwarding ................................16
   7.2. TCP/IP Forwarding Channels ................................18
8. Encoding of Terminal Modes .....................................19
9. Summary of Message Numbers .....................................21
10. IANA Considerations ...........................................21
11. Security Considerations .......................................21
12. References ....................................................22
   12.1. Normative References .....................................22
   12.2. Informative References ...................................22
Authors' Addresses ................................................23
Trademark Notice ..................................................23

1. Introduction πŸŽ€

Okay, the big picture: the SSH Connection Protocol runs on top of the SSH transport layer and user authentication protocols ([SSH-TRANS] and [SSH-USERAUTH]). It gives you interactive login sessions, remote command execution, forwarded TCP/IP connections, and forwarded X11 connections. Slay. πŸ’–

The service name for this protocol is "ssh-connection" ✨.

Quick heads up though: you should only read this doc after you've read the SSH architecture document [SSH-ARCH] πŸ“š. This one uses the architecture doc's terminology and notation all over the place without explaining it again, so no skipping, bestie πŸ™ˆ.

2. Contributors 🫢

The major original contributors were Tatu Ylonen, Tero Kivinen, Timo J. Rinne, and Sami Lehtinen (all SSH Communications Security Corp), plus Markku-Juhani O. Saarinen (University of Jyvaskyla). Darren Moffat was the original editor of this whole set of documents and also made very substantial contributions. Legend behavior πŸ‘‘.

Loads of other people helped develop this doc over the years, including Mats Andersson, Ben Harris, Bill Sommerfeld, Brent McClure, Niels Moller, Damien Miller, Derek Fawcus, Frank Cusack, Heikki Nousiainen, Jakob Schlyter, Jeff Van Dyke, Jeffrey Altman, Jeffrey Hutzelman, Jon Bright, Joseph Galbraith, Ken Hornstein, Markus Friedl, Martin Forssen, Nicolas Williams, Niels Provos, Perry Metzger, Peter Gutmann, Simon Josefsson, Simon Tatham, Wei Dai, Denis Bider, der Mouse, and Tadayoshi Kohno. Being listed doesn't mean they endorse the doc, just that they helped build it 🌈.

3. Conventions Used in This Document πŸ“–

All SSH protocol docs shall use the keywords "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" to describe requirements. These keywords get interpreted the way [RFC2119] says ✨.

When the words "PRIVATE USE", "HIERARCHICAL ALLOCATION", "FIRST COME FIRST SERVED", "EXPERT REVIEW", "SPECIFICATION REQUIRED", "IESG APPROVAL", "IETF CONSENSUS", and "STANDARDS ACTION" show up while talking about namespace allocation, they mean what [RFC2434] says they mean πŸ“.

Protocol fields and their possible values are defined across this whole set of docs. The fields themselves get defined in the message definitions. For example, SSH_MSG_CHANNEL_DATA looks like this:

   byte      SSH_MSG_CHANNEL_DATA
   uint32    recipient channel
   string    data

Here's the vibe check for naming: whenever fields get referenced, they're in single quotes, like 'data'. Whenever values for those fields get referenced, they're in double quotes. So using the example above, possible values for 'data' are "foo" and "bar" πŸ’œ.

4. Global Requests 🌍

Some requests change the state of the remote end globally, no matter which channels are involved. Like asking to start TCP/IP forwarding on a specific port πŸ”₯. Note that both the client and server MAY send global requests at any time, and the receiver MUST respond appropriately. πŸ™Œ Every one of these requests uses this format:

   byte      SSH_MSG_GLOBAL_REQUEST
   string    request name in US-ASCII only
   boolean   want reply
   ....      request-specific data follows

The 'request name' follows the DNS extensibility naming convention from [SSH-ARCH] 🧚.

If 'want reply' is TRUE, the recipient responds to this message with either SSH_MSG_REQUEST_SUCCESS or SSH_MSG_REQUEST_FAILURE:

   byte      SSH_MSG_REQUEST_SUCCESS
   ....     response specific data

Usually, the 'response specific data' is non-existent. πŸ’€

If the recipient doesn't recognize or support the request, it just replies with SSH_MSG_REQUEST_FAILURE:

   byte      SSH_MSG_REQUEST_FAILURE

Reply messages generally don't include request type identifiers πŸ™ˆ. So how does the sender know which reply goes with which request? It is REQUIRED that replies to SSH_MSG_GLOBAL_REQUESTS MUST be sent in the same order as the corresponding request messages. For channel requests, replies that relate to the same channel MUST also come back in the right order. Channel requests for distinct channels MAY be replied to out-of-order, though. Ordering is everything, periodt πŸ’….

5. Channel Mechanism πŸ¦‹

All terminal sessions, forwarded connections, and so on are channels ✨. Either side may open a channel. Multiple channels get multiplexed into a single connection, so it's giving efficiency πŸ”₯.

Channels are identified by numbers at each end. The number referring to a channel may be different on each side, so don't assume they match πŸ™ƒ. Requests to open a channel contain the sender's channel number. Any other channel-related messages contain the recipient's channel number for the channel πŸŽ€.

Channels are flow-controlled. No data may be sent to a channel until a message is received to indicate that window space is available. Basically, don't yeet data at a channel before it says it has room 🫢.

That's the first chunk, bestie! 🌈 Part 2 covers 5.1 onward, so stay tuned πŸ’–.

5.1. Opening a Channel

Okay so when either side wants to open a brand new channel πŸ¦‹, it grabs a local number for it and sends this message to the other side, with its local channel number and initial window size tucked inside πŸŽ€:

byte      SSH_MSG_CHANNEL_OPEN
string    channel type in US-ASCII only
uint32    sender channel
uint32    initial window size
uint32    maximum packet size
....      channel type specific data follows

The 'channel type' is basically a name 🏷️ (check [SSH-ARCH] and [SSH-NUMBERS] for the full deets), and it can be extended in similar ways. 'sender channel' is the sender's own local ID for the channel. 'initial window size' tells the other side how many bytes of channel data they can send to the sender of this message before the window needs adjusting. 'maximum packet size' is the biggest single data packet that can be sent to the sender. For example, you might want smaller packets for interactive connections so you get snappier responses on slow links 🐒

Then the other side decides if it can open the channel πŸ€” and replies with either SSH_MSG_CHANNEL_OPEN_CONFIRMATION or SSH_MSG_CHANNEL_OPEN_FAILURE:

byte      SSH_MSG_CHANNEL_OPEN_CONFIRMATION
uint32    recipient channel
uint32    sender channel
uint32    initial window size
uint32    maximum packet size
....      channel type specific data follows

'recipient channel' is the channel number from the original open request, and 'sender channel' is the channel number the other side allocated ✨

byte      SSH_MSG_CHANNEL_OPEN_FAILURE
uint32    recipient channel
uint32    reason code
string    description in ISO-10646 UTF-8 encoding [RFC3629]
string    language tag [RFC3066]

If the recipient of the SSH_MSG_CHANNEL_OPEN message doesn't support the specified 'channel type', it just responds with SSH_MSG_CHANNEL_OPEN_FAILURE πŸ’… The client MAY show the 'description' string to the user. If it does, the client software should take the precautions discussed in [SSH-ARCH] 🫢

The SSH_MSG_CHANNEL_OPEN_FAILURE 'reason code' values are listed in this table. Heads up: they're written in decimal so they're easier to read, but they're actually uint32 values πŸ€“

         Symbolic name                           reason code
         -------------                           -----------
        SSH_OPEN_ADMINISTRATIVELY_PROHIBITED          1
        SSH_OPEN_CONNECT_FAILED                       2
        SSH_OPEN_UNKNOWN_CHANNEL_TYPE                 3
        SSH_OPEN_RESOURCE_SHORTAGE                    4

Requests for new SSH_MSG_CHANNEL_OPEN 'reason code' values (and their 'description' text) in the range of 0x00000005 to 0xFDFFFFFF MUST be done through the IETF CONSENSUS method, as described in [RFC2434]. The IANA won't assign Channel Connection Failure 'reason code' values in the range of 0xFE000000 to 0xFFFFFFFF. Those values are left for PRIVATE USE, as described in [RFC2434] πŸ”’

Even though the IANA has no control over 0xFE000000 to 0xFFFFFFFF, this range gets split into two parts with the following rules 🍰

  • The range of 0xFE000000 to 0xFEFFFFFF is for use with locally assigned channels. For example, if a channel is proposed with a 'channel type' of "example_session@example.com" but fails, the response will contain either a 'reason code' assigned by the IANA (from the list above, in the range of 0x00000001 to 0xFDFFFFFF) or a locally assigned value in the range of 0xFE000000 to 0xFEFFFFFF. Naturally, if the server doesn't understand the proposed 'channel type', even a locally defined one, then the 'reason code' MUST be 0x00000003, as described above, if a 'reason code' is sent. If the server does understand the 'channel type' but the channel still fails to open, the server SHOULD respond with a locally assigned 'reason code' value that matches the proposed, local 'channel type'. It's assumed that practitioners will try the IANA assigned 'reason code' values first and then document their locally assigned ones πŸ“
  • There are no restrictions or suggestions for the range starting with 0xFF. No interoperability is expected for anything used in this range. Basically, it's for experimenting πŸ§ͺ

5.2. Data Transfer

The window size tells you how many bytes the other party can send before it has to wait for the window to be adjusted πŸͺŸ Both parties use this message to adjust the window:

byte      SSH_MSG_CHANNEL_WINDOW_ADJUST
uint32    recipient channel
uint32    bytes to add

After receiving this message, the recipient MAY send the given number of bytes more than it was previously allowed to send, and the window size goes up. Implementations MUST correctly handle window sizes of up to 2^32 - 1 bytes. The window MUST NOT be increased above 2^32 - 1 bytes πŸ“ˆ

Data transfer happens with messages of this type πŸ“¨

byte      SSH_MSG_CHANNEL_DATA
uint32    recipient channel
string    data

The maximum amount of data allowed is set by the maximum packet size for the channel and the current window size, whichever one is smaller πŸ‘€ The window size goes down by the amount of data sent. Both parties MAY ignore any extra data sent after the allowed window is empty πŸ™…

Implementations are expected to have some limit on the SSH transport layer packet size (any limit for received packets MUST be 32768 bytes or larger, as described in [SSH-TRANS]). The implementation of the SSH connection layer:

  • MUST NOT advertise a maximum packet size that would result in transport packets larger than its transport layer is willing to receive.
  • MUST NOT generate data packets larger than its transport layer is willing to send, even if the remote end would be willing to accept very large packets.

Some channels can carry several types of data too 🎭 An example is stderr data from interactive sessions. That kind of data can go in SSH_MSG_CHANNEL_EXTENDED_DATA messages, where a separate integer says what type of data it is. The available types and what they mean depend on the channel type 🧩

byte      SSH_MSG_CHANNEL_EXTENDED_DATA
uint32    recipient channel
uint32    data_type_code
string    data

Extended data uses the same window as ordinary data πŸͺŸ

Right now, only this type is defined. Heads up again: the value for 'data_type_code' is written in decimal for readability, but it's actually a uint32 value πŸ€“

           Symbolic name                  data_type_code
           -------------                  --------------
         SSH_EXTENDED_DATA_STDERR               1

'data_type_code' values for extended channel data transfer MUST be assigned sequentially. Requests for new 'data_type_code' values and their associated 'data' strings, in the range of 0x00000002 to 0xFDFFFFFF, MUST be done through the IETF CONSENSUS method as described in [RFC2434]. The IANA won't assign 'data_type_code' values in the range of 0xFE000000 to 0xFFFFFFFF. Those values are left for PRIVATE USE, as described in [RFC2434]. The actual instructions for the IANA are in [SSH-NUMBERS] πŸ“‹


5.3. Closing a Channel

When a party is done sending data to a channel, it SHOULD send SSH_MSG_CHANNEL_EOF πŸŒ™

byte      SSH_MSG_CHANNEL_EOF
uint32    recipient channel

No explicit response is sent back for this one πŸ€·β€β™€οΈ The application may send EOF to whatever is at the other end of the channel. The channel stays open after this message though, so more data can still come through in the other direction. This message doesn't use up any window space and can be sent even when no window space is left πŸͺ„

When either party wants to end the channel, it sends SSH_MSG_CHANNEL_CLOSE. When a party receives this message, it MUST send back an SSH_MSG_CHANNEL_CLOSE unless it has already sent one for that channel. The channel counts as closed for a party once it has both sent and received SSH_MSG_CHANNEL_CLOSE, and then that party may reuse the channel number ♻️ A party MAY send SSH_MSG_CHANNEL_CLOSE without having sent or received SSH_MSG_CHANNEL_EOF πŸ’«

byte      SSH_MSG_CHANNEL_CLOSE
uint32    recipient channel

This message doesn't use up window space and can be sent even if no window space is left πŸ™Œ

It is RECOMMENDED that all data sent before this message gets delivered to the actual destination, if possible πŸ“¬


5.4. Channel-Specific Requests

Lots of 'channel type' values have extensions that only apply to that particular channel type πŸ’œ A classic example is asking for a pty (pseudo terminal) for an interactive session.

All channel-specific requests use this format:

byte      SSH_MSG_CHANNEL_REQUEST
uint32    recipient channel
string    request type in US-ASCII characters only
boolean   want reply
....      type-specific data follows

If 'want reply' is FALSE, no response is sent for the request. Otherwise, the recipient replies with either SSH_MSG_CHANNEL_SUCCESS, SSH_MSG_CHANNEL_FAILURE, or request-specific continuation messages. If the request isn't recognized or isn't supported for the channel, SSH_MSG_CHANNEL_FAILURE comes back 😬

This message doesn't use up window space and can be sent even if no window space is left 🎈 The values of 'request type' are local to each channel type. The client is allowed to send more messages without waiting for the response to the request πŸƒβ€β™€οΈ

'request type' names follow the DNS extensibility naming convention outlined in [SSH-ARCH] and [SSH-NUMBERS] 🌐

byte      SSH_MSG_CHANNEL_SUCCESS
uint32    recipient channel


byte      SSH_MSG_CHANNEL_FAILURE
uint32    recipient channel

These messages don't use up window space and can be sent even if no window space is left 🫧


6. Interactive Sessions ✨

A session is a remote execution of a program πŸ–₯️ The program can be a shell, an application, a system command, or some built-in subsystem. It may or may not have a tty, and it may or may not involve X11 forwarding. Multiple sessions can be active at the same time, which is very main character energy πŸ‘‘

6.1. Opening a Session

A session starts when you send this message πŸš€

byte      SSH_MSG_CHANNEL_OPEN
string    "session"
uint32    sender channel
uint32    initial window size
uint32    maximum packet size

Client implementations SHOULD reject any session channel open requests. This makes it harder for a corrupt server to attack the client πŸ›‘οΈπŸ’–

6.2. Requesting a Pseudo-Terminal ✨

okay so a pseudo-terminal (pty) can be set up for the session by sending this message πŸ’»

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "pty-req"
  boolean   want_reply
  string    TERM environment variable value (e.g., vt100)
  uint32    terminal width, characters (e.g., 80)
  uint32    terminal height, rows (e.g., 24)
  uint32    terminal width, pixels (e.g., 640)
  uint32    terminal height, pixels (e.g., 480)
  string    encoded terminal modes

the encoded terminal modes are explained in Section 8 🧚 zero dimension parameters MUST be ignored πŸ™ˆ the character and row sizes win over the pixel sizes (when they're nonzero) πŸ’… and pixel sizes are about the drawable area of the window, so no cap, that's the part people mix up

the dimension parameters are only informational, bestie πŸŽ€

the client SHOULD ignore pty requests 🫢


6.3. X11 Forwarding πŸ¦‹

6.3.1. Requesting X11 Forwarding

X11 forwarding can be asked for on a session by sending a SSH_MSG_CHANNEL_REQUEST message 🌸

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "x11-req"
  boolean   want reply
  boolean   single connection
  string    x11 authentication protocol
  string    x11 authentication cookie
  uint32    x11 screen number

it is RECOMMENDED that the x11 authentication cookie you send be a fake, random cookie 🎭 and that it gets checked and swapped for the real cookie when a connection request comes in πŸ”„ that's lowkey genius

X11 connection forwarding should stop when the session channel is closed ✨ but already opened forwardings should not be automatically closed when the session channel is closed, so they stick around 🌈

if single connection is TRUE, only a single connection should be forwarded πŸ’œ no more connections get forwarded after the first one, or after the session channel has been closed πŸ™…β€β™€οΈ

the x11 authentication protocol is the name of the X11 authentication method being used, like "MIT-MAGIC-COOKIE-1" πŸ“

the x11 authentication cookie MUST be hexadecimal encoded πŸ”₯

the X Protocol is documented in [SCHEIFLER] πŸ“š

6.3.2. X11 Channels

X11 channels get opened with a channel open request πŸšͺ the channels that come out of it are independent of the session, so closing the session channel does not close the forwarded X11 channels πŸ¦‹

  byte      SSH_MSG_CHANNEL_OPEN
  string    "x11"
  uint32    sender channel
  uint32    initial window size
  uint32    maximum packet size
  string    originator address (e.g., "192.168.7.38")
  uint32    originator port

the recipient should respond with SSH_MSG_CHANNEL_OPEN_CONFIRMATION or SSH_MSG_CHANNEL_OPEN_FAILURE πŸ’Œ

implementations MUST reject any X11 channel open requests if they have not requested X11 forwarding πŸ›‘ no exceptions, periodt πŸ’…


6.4. Environment Variable Passing 🌿

environment variables can be passed along to the shell or command that's going to start later 🌱 but ngl, setting env vars without any control inside a privileged process can be a real security hazard 😬 so it is recommended that implementations either keep a list of allowed variable names, or only set environment variables after the server process has dropped enough privileges πŸ”

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "env"
  boolean   want reply
  string    variable name
  string    variable value

6.5. Starting a Shell or a Command 🐚

once the session is set up, a program gets started on the remote end πŸš€ it can be a shell, an application program, or a subsystem with a host-independent name 🧩 only one of these requests can succeed per channel, so pick wisely bestie πŸ’–

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "shell"
  boolean   want reply

this one asks for the user's default shell (usually set in /etc/passwd on UNIX systems) to be started at the other end 🐚

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "exec"
  boolean   want reply
  string    command

this one asks the server to start running the given command πŸƒβ€β™€οΈ the command string may contain a path, and normal precautions MUST be taken to prevent the execution of unauthorized commands 🚨 this is giving security-first energy

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "subsystem"
  boolean   want reply
  string    subsystem name

this last form runs a predefined subsystem πŸ§šβ€β™€οΈ it's expected these will include a general file transfer mechanism, and maybe other features too πŸ“ implementations may also let you configure more mechanisms like that πŸ”§ because the user's shell is usually what runs the subsystem, it's advisable for the subsystem protocol to have a "magic cookie" at the start of the protocol transaction ✨ that way it can be told apart from random output generated by shell initialization scripts and so on πŸ™ˆ that spurious shell output may be filtered out either at the server or at the client 🧹

the server SHOULD NOT halt the execution of the protocol stack when starting a shell or a program πŸ™…β€β™€οΈ all input and output from these SHOULD be redirected to the channel or to the encrypted tunnel πŸ”’

it is RECOMMENDED that the reply to these messages be requested and checked πŸ’… and the client SHOULD ignore these messages 🫠

subsystem names follow the DNS extensibility naming convention outlined in [SSH-NUMBERS] 🌐


6.6. Session Data Transfer πŸ“¦

data transfer for a session is handled with SSH_MSG_CHANNEL_DATA and SSH_MSG_CHANNEL_EXTENDED_DATA packets, plus the window mechanism πŸͺŸ the extended data type SSH_EXTENDED_DATA_STDERR has been defined for stderr data πŸ”΄ very organized, very iconic πŸ‘‘


6.7. Window Dimension Change Message πŸ“

when the window (terminal) size changes on the client side, it MAY send a message to the other side to tell it the new dimensions πŸ“βœ¨

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "window-change"
  boolean   FALSE
  uint32    terminal width, columns
  uint32    terminal height, rows
  uint32    terminal width, pixels
  uint32    terminal height, pixels

a response SHOULD NOT be sent to this message 🀫


6.8. Local Flow Control πŸŽ›οΈ

on lots of systems, it's possible to tell whether a pseudo-terminal is using control-S/control-Q flow control πŸ•ΉοΈ when flow control is allowed, it's often super desirable to do the flow control at the client end, so responses to user requests speed up πŸŽοΈπŸ’¨ this is what the notification below is for πŸ“£ at first, the server is in charge of flow control (and yes, "client" still means the side that started the session, and "server" means the other side πŸ”)

the message below is how the server tells the client when it can or cannot do flow control (control-S/control-Q processing) πŸ™‹β€β™€οΈ if client can do is TRUE, the client is allowed to do flow control using control-S and control-Q βœ… the client MAY ignore this message πŸ’β€β™€οΈ

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "xon-xoff"
  boolean   FALSE
  boolean   client can do

no response is sent to this message 🀐


6.9. Signals ⚑

a signal can be delivered to the remote process or service using the following message πŸ’₯ some systems may not implement signals at all, and in that case they SHOULD ignore this message 🫑

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "signal"
  boolean   FALSE
  string    signal name (without the "SIG" prefix)

the signal name values get encoded the same way as described in the passage about SSH_MSG_CHANNEL_REQUEST messages using "exit-signal" in this section πŸ”


6.10. Returning Exit Status 🏁

when the command running at the other end finishes, this message can be sent to return the command's exit status πŸŽ‰ returning the status is RECOMMENDED πŸ’― no acknowledgement is sent for this message πŸ™Š and the channel needs to be closed with SSH_MSG_CHANNEL_CLOSE after this message πŸ”’

the client MAY ignore these messages πŸ’…

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "exit-status"
  boolean   FALSE
  uint32    exit_status

the remote command can also end violently because of a signal πŸ’€ this is how that gets indicated ⬇️ and a zero exit_status usually means the command finished successfully βœ…

  byte      SSH_MSG_CHANNEL_REQUEST
  uint32    recipient channel
  string    "exit-signal"
  boolean   FALSE
  string    signal name (without the "SIG" prefix)
  boolean   core dumped
  string    error message in ISO-10646 UTF-8 encoding
  string    language tag [RFC3066]

the signal name is one of the following (these come from [POSIX]) πŸ“œ

        ABRT
        ALRM
        FPE
        HUP
        ILL
        INT
        KILL
        PIPE
        QUIT
        SEGV
        TERM
        USR1
        USR2

additional signal name values MAY be sent in the format "sig-name@xyz", where "sig-name" and "xyz" can be anything a particular implementer wants (except the "@" sign) 🎨 however, it is suggested that if a configure script is used, any non-standard signal name values it finds get encoded as "SIG@xyz.config.guess" 🧠 where "SIG" is the signal name without the "SIG" prefix, and "xyz" is the host type, as determined by "config.guess" πŸ–₯️

⚠️ the error message is extra textual explanation of the error, and it may be several lines separated by CRLF (Carriage Return - Line Feed) pairs πŸ“ the client software MAY display this message to the user, and if it does, the client software should take the precautions discussed in [SSH-ARCH] πŸ”

7. TCP/IP Port Forwarding 🌈

7.1. Requesting Port Forwarding ✨

okay so, a party doesn't have to explicitly request forwardings from its own end going the other direction πŸ’… BUT if it wants connections to a port on the other side forwarded to the local side, it must explicitly request this. no skipping that step, bestie 🫢

byte      SSH_MSG_GLOBAL_REQUEST
string    "tcpip-forward"
boolean   want reply
string    address to bind (e.g., "0.0.0.0")
uint32    port number to bind

the 'address to bind' and 'port number to bind' fields say which IP address (or domain name) and port should accept connections for forwarding πŸ¦‹ some strings used for 'address to bind' have special-case semantics, and they're iconic:

  • "" means connections are to be accepted on all protocol families supported by the SSH implementation.
  • "0.0.0.0" means to listen on all IPv4 addresses.
  • "::" means to listen on all IPv6 addresses.
  • "localhost" means to listen on all protocol families supported by the SSH implementation on loopback addresses only ([RFC3330] and [RFC3513]).
  • "127.0.0.1" and "::1" indicate listening on the loopback interfaces for IPv4 and IPv6, respectively.

note that the client can still filter connections based on information passed in the open request πŸ‘€ not me thinking the client has the final say πŸ™ˆ

implementations should only allow forwarding privileged ports if the user has been authenticated as a privileged user πŸ”

Client implementations SHOULD reject these messages 🚫 they are normally only sent by the client, so it's a whole vibe check πŸ’€

if a client passes 0 as the port number to bind and has 'want reply' as TRUE, then the server allocates the next available unprivileged port number and replies with the following message πŸŽ€ otherwise, there is no response-specific data, just vibes ✨

byte     SSH_MSG_REQUEST_SUCCESS
uint32   port that was bound on the server

a port forwarding can be canceled with the following message 🌸 heads up, though: channel open requests may still be received until a reply to this message is received, so don't panic if one shows up mid-cancel 😭

byte      SSH_MSG_GLOBAL_REQUEST
string    "cancel-tcpip-forward"
boolean   want reply
string    address_to_bind (e.g., "127.0.0.1")
uint32    port number to bind

Client implementations SHOULD reject these messages too 🚫 same energy as before, they're normally only sent by the client 🌈


7.2. TCP/IP Forwarding Channels πŸ¦‹

when a connection comes to a port that has remote forwarding requested, a channel gets opened to forward the port to the other side 🧚 that's the whole magic trick ✨

byte      SSH_MSG_CHANNEL_OPEN
string    "forwarded-tcpip"
uint32    sender channel
uint32    initial window size
uint32    maximum packet size
string    address that was connected
uint32    port that was connected
string    originator IP address
uint32    originator port

Implementations MUST reject these messages unless they have previously requested a remote TCP/IP port forwarding with the given port number πŸ™…β€β™€οΈ no uninvited guests, periodt πŸ’…

when a connection comes to a locally forwarded TCP/IP port, the following packet is sent to the other side πŸŽ€ note that these messages MAY also be sent for ports where no forwarding has been explicitly requested πŸ‘€ the receiving side must decide whether to allow the forwarding, so it's got the final say πŸ‘‘

byte      SSH_MSG_CHANNEL_OPEN
string    "direct-tcpip"
uint32    sender channel
uint32    initial window size
uint32    maximum packet size
string    host to connect
uint32    port to connect
string    originator IP address
uint32    originator port

the 'host to connect' and 'port to connect' fields tell the recipient where to connect the channel, the TCP/IP host and port πŸ“ the 'host to connect' can be either a domain name or a numeric IP address, whichever's your type πŸ’–

the 'originator IP address' is the numeric IP address of the machine where the connection request came from, and the 'originator port' is the port on that host where the connection started 🌍

forwarded TCP/IP channels are totally independent of any sessions πŸ¦‹ closing a session channel doesn't in any way imply that forwarded connections should be closed, so don't assume they all go down together 🫢

Client implementations SHOULD reject direct TCP/IP open requests for security reasons πŸ”’ this one's a serious little rule, bestie 🚨


8. Encoding of Terminal Modes 🌈

all 'encoded terminal modes' (the ones passed in a pty request) get encoded into a byte stream ✨ the coding is meant to be portable across different environments, so it works everywhere 🧚 the stream is made of opcode-argument pairs, where the opcode is a byte value πŸŽ€ opcodes 1 to 159 have a single uint32 argument, but opcodes 160 to 255 aren't defined yet, and they cause parsing to stop (they should only be used after any other data πŸ’…). the stream ends with opcode TTY_OP_END (0x00) 🏁

the client SHOULD put any modes it knows about into the stream, and the server MAY ignore any modes it doesn't know about 🫢 this gives some degree of machine-independence, at least between systems that use a POSIX-like tty interface πŸ¦‹ the protocol can support other systems too, but the client may need to fill in reasonable values for a number of parameters so the server pty gets set to a reasonable mode 🌸 the server leaves all unspecified mode bits in their default values, and only some combinations actually make sense, so pick your combos wisely πŸ’€

the naming of the opcode values mostly follows the POSIX terminal mode flags ✨ the following opcode values have been defined πŸ‘‘ note that the values below are in decimal for readability, but they're actually byte values πŸ™ˆ

      opcode  mnemonic       description
      ------  --------       -----------
      0     TTY_OP_END  Indicates end of options.
      1     VINTR       Interrupt character; 255 if none.  Similarly
                         for the other characters.  Not all of these
                         characters are supported on all systems.
      2     VQUIT       The quit character (sends SIGQUIT signal on
                         POSIX systems).
      3     VERASE      Erase the character to left of the cursor.
      4     VKILL       Kill the current input line.
      5     VEOF        End-of-file character (sends EOF from the
                         terminal).
      6     VEOL        End-of-line character in addition to
                         carriage return and/or linefeed.
      7     VEOL2       Additional end-of-line character.
      8     VSTART      Continues paused output (normally
                         control-Q).
      9     VSTOP       Pauses output (normally control-S).
      10    VSUSP       Suspends the current program.
      11    VDSUSP      Another suspend character.

      12    VREPRINT    Reprints the current input line.
      13    VWERASE     Erases a word left of cursor.
      14    VLNEXT      Enter the next character typed literally,
                         even if it is a special character
      15    VFLUSH      Character to flush output.
      16    VSWTCH      Switch to a different shell layer.
      17    VSTATUS     Prints system status line (load, command,
                         pid, etc).
      18    VDISCARD    Toggles the flushing of terminal output.
      30    IGNPAR      The ignore parity flag.  The parameter
                         SHOULD be 0 if this flag is FALSE,
                         and 1 if it is TRUE.
      31    PARMRK      Mark parity and framing errors.
      32    INPCK       Enable checking of parity errors.
      33    ISTRIP      Strip 8th bit off characters.
      34    INLCR       Map NL into CR on input.
      35    IGNCR       Ignore CR on input.
      36    ICRNL       Map CR to NL on input.
      37    IUCLC       Translate uppercase characters to
                         lowercase.
      38    IXON        Enable output flow control.
      39    IXANY       Any char will restart after stop.
      40    IXOFF       Enable input flow control.
      41    IMAXBEL     Ring bell on input queue full.
      50    ISIG        Enable signals INTR, QUIT, [D]SUSP.
      51    ICANON      Canonicalize input lines.
      52    XCASE       Enable input and output of uppercase
                         characters by preceding their lowercase
                         equivalents with "\".
      53    ECHO        Enable echoing.
      54    ECHOE       Visually erase chars.
      55    ECHOK       Kill character discards current line.
      56    ECHONL      Echo NL even if ECHO is off.
      57    NOFLSH      Don't flush after interrupt.
      58    TOSTOP      Stop background jobs from output.
      59    IEXTEN      Enable extensions.
      60    ECHOCTL     Echo control characters as ^(Char).
      61    ECHOKE      Visual erase for line kill.
      62    PENDIN      Retype pending input.
      70    OPOST       Enable output processing.
      71    OLCUC       Convert lowercase to uppercase.
      72    ONLCR       Map NL to CR-NL.
      73    OCRNL       Translate carriage return to newline
                         (output).
      74    ONOCR       Translate newline to carriage
                         return-newline (output).
      75    ONLRET      Newline performs a carriage return
                         (output).

      90    CS7         7 bit mode.
      91    CS8         8 bit mode.
      92    PARENB      Parity enable.
      93    PARODD      Odd parity, else even.

      128 TTY_OP_ISPEED  Specifies the input baud rate in
                          bits per second.
      129 TTY_OP_OSPEED  Specifies the output baud rate in
                          bits per second.

9. Summary of Message Numbers πŸ”’

here's the quick cheat sheet of messages and their message numbers πŸ“‹ perfect for screenshotting πŸ“Έ

        SSH_MSG_GLOBAL_REQUEST                  80
        SSH_MSG_REQUEST_SUCCESS                 81
        SSH_MSG_REQUEST_FAILURE                 82
        SSH_MSG_CHANNEL_OPEN                    90
        SSH_MSG_CHANNEL_OPEN_CONFIRMATION       91
        SSH_MSG_CHANNEL_OPEN_FAILURE            92
        SSH_MSG_CHANNEL_WINDOW_ADJUST           93
        SSH_MSG_CHANNEL_DATA                    94
        SSH_MSG_CHANNEL_EXTENDED_DATA           95
        SSH_MSG_CHANNEL_EOF                     96
        SSH_MSG_CHANNEL_CLOSE                   97
        SSH_MSG_CHANNEL_REQUEST                 98
        SSH_MSG_CHANNEL_SUCCESS                 99
        SSH_MSG_CHANNEL_FAILURE                100

10. IANA Considerations πŸ“š

this document is part of a set 🧩 the IANA considerations for the SSH protocol, as defined in [SSH-ARCH], [SSH-TRANS], [SSH-USERAUTH], and this document, are detailed in [SSH-NUMBERS] πŸ’œ


11. Security Considerations πŸ”

this protocol is assumed to run on top of a secure, authenticated transport πŸ›‘οΈ user authentication and protection against network-level attacks are assumed to be provided by the underlying protocols, so the protocol itself isn't doing all the heavy lifting 😀

full security considerations for this protocol are provided in [SSH-ARCH] πŸ“– specific to this document, it is RECOMMENDED that implementations disable all the potentially dangerous features (e.g., agent forwarding, X11 forwarding, and TCP/IP forwarding) if the host key has changed without notice or explanation 🚨 bestie, that's a major red flag, so if the host key changes out of nowhere, shut the dangerous stuff off immediately πŸ’€πŸ”₯


12. References πŸ“Ž

12.1. Normative References ✨

okay bestie, these are the docs you NEED to have read before the rest of this makes sense πŸ“šπŸŽ€ each one is a whole RFC of its own, so grab a snack first πŸ“

  • [SSH-ARCH] Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH) Protocol Architecture", RFC 4251, January 2006.
  • [SSH-TRANS] Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH) Transport Layer Protocol", RFC 4253, January 2006.
  • [SSH-USERAUTH] Ylonen, T. and C. Lonvick, Ed., "The Secure Shell (SSH) Authentication Protocol", RFC 4252, January 2006.
  • [SSH-NUMBERS] Lehtinen, S. and C. Lonvick, Ed., "The Secure Shell (SSH) Protocol Assigned Numbers", RFC 4250, January 2006.
  • [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.
  • [RFC2434] Narten, T. and H. Alvestrand, "Guidelines for Writing an IANA Considerations Section in RFCs", BCP 26, RFC 2434, October 1998.
  • [RFC3066] Alvestrand, H., "Tags for the Identification of Languages", BCP 47, RFC 3066, January 2001.
  • [RFC3629] Yergeau, F., "UTF-8, a transformation format of ISO 10646", STD 63, RFC 3629, November 2003.

12.2. Informative References 🌈

these ones are extra reading, not required, but they're lowkey fascinating 🧚

  • [RFC3330] IANA, "Special-Use IPv4 Addresses", RFC 3330, September 2002.
  • [RFC3513] Hinden, R. and S. Deering, "Internet Protocol Version 6 (IPv6) Addressing Architecture", RFC 3513, April 2003.
  • [SCHEIFLER] Scheifler, R., "X Window System : The Complete Reference to Xlib, X Protocol, Icccm, Xlfd, 3rd edition.", Digital Press ISBN 1555580882, February 1992.
  • [POSIX] ISO/IEC, 9945-1., "Information technology -- Portable Operating System Interface (POSIX)-Part 1: System Application Program Interface (API) C Language", ANSI/IEE Std 1003.1, July 1996.

Authors' Addresses πŸ’Œ

here's where the authors live, so you can slide into their inboxes πŸ¦‹

Tatu Ylonen
SSH Communications Security Corp
Valimotie 17
00380 Helsinki
Finland

EMail: ylo@ssh.com


Chris Lonvick (editor)
Cisco Systems, Inc.
12515 Research Blvd.
Austin  78759
USA

EMail: clonvick@cisco.com

Trademark Notice πŸ’…

"ssh" is a registered trademark in the United States and/or other countries. Respect the brand, bestie 🫢

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 πŸ‘‘

the IETF takes no position on whether any IPR claims cover the tech in this doc, or whether licenses are available for it, and it hasn't independently gone hunting for any of that πŸ™ˆ rights info lives in BCP 78 and BCP 79.

copies of IPR disclosures sent to the IETF Secretariat, plus any assurances of licenses, or the results of attempts to get a general license for implementers or users, can be found in the IETF online IPR repository at http://www.ietf.org/ipr.

the IETF also invites anyone to point out copyrights, patents, patent applications, or other proprietary rights that might cover tech needed to implement this standard. send that info to the IETF at ietf-ipr@ietf.org πŸ“¬

Acknowledgement 🌸

funding for the RFC Editor function comes from the IETF Administrative Support Activity (IASA) πŸ’–

πŸ“¦ where this page comes from