RFC 2228

FTP Security Extensions

πŸ“… published October 1997, from ietf

FTP Security Extensions πŸŽ€

bestie, okay so this is RFC 2228 πŸ’– It's the FTP Security Extensions spec, written by M. Horowitz (Cygnus Solutions) and S. Lunt (Bellcore) in October 1997. It's a Standards Track doc, and it updates RFC 959, the original FTP spec from 1985. Think of it as the glow-up for FTP πŸ’…βœ¨

Status of this Memo πŸ“œ

This is an Internet standards track protocol for the Internet community, and the authors are asking for discussion and suggestions for improvements 🫢 For 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 (1997). All Rights Reserved. πŸ¦‹

Abstract πŸ”₯

Okay so this doc extends the FTP spec, STD 9, which is RFC 959, "FILE TRANSFER PROTOCOL (FTP)" (October 1985) 🧚 The goal is strong authentication, integrity, and confidentiality on both the control and data channels. It gets there by adding new optional commands, replies, and file transfer encodings, and it's giving main character energy ⭐

The new optional commands are:

  • AUTH (Authentication/Security Mechanism),
  • ADAT (Authentication/Security Data),
  • PROT (Data Channel Protection Level),
  • PBSZ (Protection Buffer Size),
  • CCC (Clear Command Channel),
  • MIC (Integrity Protected Command),
  • CONF (Confidentiality Protected Command), and
  • ENC (Privacy Protected Command).

There's also a brand new class of reply types, 6yz, for protected replies 🌈

None of the above commands are required to be implemented, but they have interdependencies, and those dependencies are written down alongside each command πŸ’œ Also, this spec is compatible with STD 9, RFC 959, so it doesn't break the old stuff πŸ“

1. Introduction ✨

The File Transfer Protocol (FTP) defined in STD 9 and RFC 959 is the one that's out there on the Internet right now, and it sends usernames and passwords in cleartext to log clients in (through the USER and PASS commands) πŸ™ˆ Except for "anonymous" FTP archives, that's a real security risk, because anyone monitoring local or wide-area networks can steal passwords πŸ’€ It either hands attackers your password, or it keeps servers that can't or won't accept those risks from giving people access to their files. Not ideal, bestie.

Authenticating users securely is only part of the problem. You also need to authenticate servers, protect sensitive data, and verify its integrity 😭 An attacker might read valuable data just by watching the network, or actively delete or modify transferred data to corrupt it. An active attacker can also start spurious file transfers to and from a site of their choice, and invoke other commands on the server. Right now FTP has no way to encrypt or verify the authenticity of commands, replies, or transferred data. And these security services matter even for anonymous file access πŸ‘‘

Current practice for sending files securely is generally one of these:

  1. via FTP of files pre-encrypted under keys which are manually distributed,
  2. via electronic mail containing an encoding of a file encrypted under keys which are manually distributed,
  3. via a PEM message, or
  4. via the rcp command enhanced to use Kerberos.

None of these could be considered even a de facto standard, and none are truly interactive πŸ₯² So there's a need to transfer files securely using FTP, in a way that's supported consistently within the FTP protocol and that takes advantage of existing security infrastructure and technology. If these security services are going to be introduced in an interoperable way, the FTP spec needs extensions πŸ”₯

Here's the tea: the FTP control connection follows the Telnet protocol, and Telnet has defined an authentication and encryption option [TELNET-SEC]. But [RFC-1123] explicitly forbids the use of Telnet option negotiation over the control connection (other than Synch and IP) πŸ™…β€β™€οΈ Also, the Telnet authentication and encryption option doesn't offer integrity protection on its own (without confidentiality), and it doesn't cover protecting the data channel πŸ’…

2. FTP Security Overview πŸ¦‹

At the highest level, the FTP security extensions aim to provide an abstract mechanism for authenticating and/or authorizing connections, and for integrity and/or confidentiality protection of commands, replies, and data transfers πŸ’–

Authentication, in FTP security terms, is establishing a client's identity and/or a server's identity in a secure way, usually with cryptographic techniques πŸ” Basic FTP doesn't have a concept of authentication at all, so this is new territory.

Authorization is the process of validating a user for login. The basic authorization process uses the USER, PASS, and ACCT commands. With the FTP security extensions, authentication set up through a security mechanism can also be used to make the authorization decision ✨

Without the security extensions, client authentication, the way people normally mean the word, just never happens πŸ™ˆ FTP authorization is done with a password, sent over the network in the clear as the argument to the PASS command. Whoever has that password is assumed to be allowed to transfer files as the user named in the USER command, but nobody ever securely establishes who the client actually is. Iconic behavior, not in a good way πŸ’€

An FTP security interaction starts when the client tells the server which security mechanism it wants to use, with the AUTH command. The server will either accept that mechanism, reject it, or, if it doesn't implement the security extensions, reject the command completely πŸŽ€ The client can try multiple security mechanisms until it asks for one the server accepts, so there's a rudimentary form of negotiation going on. (If you want more complex negotiation, you can implement it as a security mechanism.) The server's reply says whether the client has to respond with additional data for the security mechanism to interpret 🧚 If none is needed, that usually means the mechanism reads the password (given by the PASS command) differently, like with a token or a one-time password system.

If the server needs more security information, the client and server enter a security data exchange 🀝 The client sends an ADAT command with the first block of security data. The server's reply says whether the data exchange is complete, whether there was an error, or whether more data is needed. The server's reply can optionally include security data for the client to interpret. If more data is needed, the client sends another ADAT command with the next block and waits for the server's reply. This can go on as many times as necessary. Once the exchange completes, the client and server have established a security association 🌸 That association may include authentication (client, server, or mutual) and keying information for integrity and/or confidentiality, depending on the mechanism in use.

The term "security data" was chosen carefully 🧐 The point of the security data exchange is to establish a security association between the client and server, and that association might not include any authentication at all. For example, a Diffie-Hellman exchange establishes a secret key, but no authentication takes place. If an FTP server has an RSA key pair but the client doesn't, the client can authenticate the server, but the server can't authenticate the client 🫠

Once a security association exists, authentication that's part of it can be used instead of, or in addition to, the standard username/password exchange for authorizing a user to connect to the server. A username given by the USER command is always required to specify the identity to be used on the server πŸ’œ

To stop an attacker from inserting or deleting commands on the control stream, if the security association supports integrity, the server and client must use integrity protection on the control stream, unless the client first sends a CCC command to turn off that requirement πŸ›‘οΈ Integrity protection is done with the MIC and ENC commands, and the 63z reply codes. The CCC command and its reply must be sent with integrity protection. Commands and replies may be sent without integrity (that is, in the clear or with confidentiality only) only if no security association is established, if the negotiated security association doesn't support integrity, or if the CCC command has succeeded πŸ”₯

Once the client and server have negotiated an acceptable buffer size, using the PBSZ command, for encapsulating protected data over the data channel, the security mechanism can also be used to protect data channel transfers 🌈

Policy isn't specified by this document πŸ™Œ Client and server implementations can choose to restrict which operations they allow, depending on the security association that exists. For example, a server might require that a client authorize through a security mechanism instead of a password, require a one-time password from a token, require at least integrity protection on the command channel, or require that certain files only be transmitted encrypted. An anonymous FTP client might refuse to do file transfers without integrity protection, so it can make sure downloaded files are valid πŸ“

No particular set of functionality is required, except for the dependencies described in the next section πŸ’… That means none of authentication, integrity, or confidentiality is required of an implementation, although a mechanism that does none of them isn't much use. For example, it's fine for a mechanism to implement only integrity protection, one-way authentication and/or encryption, encryption without any authentication or integrity protection, or any other subset of functionality, if policy or technical considerations make that desirable. Of course, one peer might require stronger protection, as a matter of policy, than the other can provide, which means perfect interoperability isn't guaranteed 😭

3. New FTP Commands πŸ’–

The following commands are optional, but they depend on each other. They're extensions to the FTP Access Control Commands 🧚

The reply codes in here are generally RECOMMENDED, not strictly required πŸ’…βœ¨ The idea is that codes for the full range of success and failure modes should exist, but servers get to keep info from the client on a need-to-know basis πŸ™ˆ A server might have a security mechanism but a policy against using it. In that case it SHOULD reply with 534, but it MAY reply with 504 if it doesn't want to reveal that the disallowed mechanism is supported 🫣 If it goes with a different code than the recommended one, it SHOULD try to pick one that only differs in the last digit πŸ”’ In all cases the server MUST use a code that's documented as returnable from the command it received, and that code MUST start with the same digit as the recommended code for that situation πŸ‘‘

AUTHENTICATION/SECURITY MECHANISM (AUTH)

The argument is a Telnet string naming a supported mechanism πŸŽ€ It's case-insensitive, so the casing doesn't matter πŸ’– Values MUST be registered with the IANA, except values starting with X-, which are reserved for local use 🧚

If the server doesn't recognize the AUTH command, it MUST respond with 500 😭 This is meant to cover the huge base of non-security-aware ftp servers, which answer 500 to any command they don't know. If the server recognizes AUTH but doesn't implement the security extensions, it SHOULD respond with 502 🌸

If the server doesn't understand the named security mechanism, it SHOULD respond with 504 πŸ€·β€β™€οΈ

If the server isn't willing to accept the named mechanism, it SHOULD respond with 534 🚫

If the server isn't able to accept the named mechanism, like when a required resource is unavailable, it SHOULD respond with 431 πŸ₯Ί

If the server is willing to accept the named mechanism but needs security data, it MUST respond with 334 πŸ™Œ

If the server is willing to accept the named mechanism and doesn't need any security data, it MUST respond with 234 ✨

If the server responds with 334, it MAY include security data as described in the next section 🫢

Some servers let AUTH be reissued to set up new authentication. A successful AUTH wipes out any state tied to earlier FTP Security commands 🧹 In that case the server MUST also require the user to reauthorize, meaning reissue some or all of USER, PASS, and ACCT (see section 4 for an explanation of "authorize" in this context) πŸ”

AUTHENTICATION/SECURITY DATA (ADAT)

The argument is a Telnet string representing base 64 encoded security data (see Section 9, "Base 64 Encoding") πŸ“¦ If a success reply code comes back, the server MAY also put a string of the form "ADAT=base64data" in the text part of the reply if it wants to send security data back to the client πŸ’Œ

Both cases carry data that's specific to the security mechanism named in the previous AUTH command 🧩 The ADAT command and its replies let client and server run an arbitrary security protocol, so the exchange MUST include enough info for both peers to know which optional features are available πŸ•΅οΈβ€β™€οΈ For example, if the client doesn't support data encryption, the server MUST be told, so it knows not to send encrypted command channel replies πŸ™…β€β™€οΈ It is strongly RECOMMENDED that the security mechanism provide sequencing on the command channel, so commands can't be deleted, reordered, or replayed πŸ”

The ADAT command MUST be preceded by a successful AUTH command πŸ›ŽοΈ It can't be issued once a security data exchange completes, whether successfully or not, unless an AUTH command came first to reset the security state πŸ”„

If the server hasn't received an AUTH command yet, or a prior security data exchange completed but the security state hasn't been reset with AUTH, it SHOULD respond with 503 😬

If the server can't base 64 decode the argument, it SHOULD respond with 501 πŸ™ƒ

If the server rejects the security data (say, a checksum fails), it SHOULD respond with 535 πŸ’”

If the server accepts the security data and needs more, it SHOULD respond with 335 ⏳

If the server accepts the security data and doesn't need anything else (so the exchange has completed successfully), it MUST respond with 235 πŸŽ‰

If the server responds with 235 or 335, it MAY include security data in the text part of the reply, as described above πŸ’¬

If the ADAT command returns an error, the security data exchange fails, and the client MUST reset its internal security state 🫠 If the client gets out of sync with the server (for example, the server sends a 234 reply to an AUTH command but the client still has more data to send), then the client MUST reset the server's security state too πŸ”§

PROTECTION BUFFER SIZE (PBSZ)

The argument is a decimal integer for the maximum size, in bytes, of the encoded data blocks sent or received during a file transfer πŸ“ This number SHALL be no greater than what can be represented in a 32-bit unsigned integer πŸ”’

This command lets the FTP client and server negotiate a maximum protected buffer size for the connection 🀝 There is no default size, so the client MUST issue a PBSZ command before it can issue the first PROT command 🧱

The PBSZ command MUST be preceded by a successful security data exchange πŸ—οΈ

If the server can't parse the argument, or the number won't fit in 32 bits, it SHOULD respond with 501 πŸ™ˆ

If the server hasn't completed a security data exchange with the client, it SHOULD respond with 503 β›”

Otherwise, the server MUST reply with 200 βœ… If the client's size is too large for the server, the server MUST use a string of the form "PBSZ=number" in the text part of the reply to show a smaller buffer size πŸ“‰ If both buffer sizes are specified, the client and the server MUST use the smaller of the two 🫰

DATA CHANNEL PROTECTION LEVEL (PROT)

The argument is a single Telnet character code for the data channel protection level πŸ›‘οΈ

This command tells the server what kind of data channel protection the client and server will be using πŸ¦‹ The following codes are assigned:

   C - Clear
   S - Safe
   E - Confidential
   P - Private

The default level, if none is specified, is Clear 🫧 Clear means the data channel carries the raw file transfer data, with no security applied πŸͺž Safe means the data is integrity protected 🧷 Confidential means the data is confidentiality protected 🀫 Private means the data is both integrity and confidentiality protected πŸ’œ

It's reasonable for a security mechanism not to provide every protection level πŸ€·β€β™€οΈ It's also reasonable for a mechanism to give more protection at a level than was asked for (for instance, a mechanism might provide Confidential protection but include integrity protection in that encoding, because of API or other considerations) 🎁

The PROT command MUST be preceded by a successful protection buffer size negotiation 🧡

If the server doesn't understand the specified protection level, it SHOULD respond with 504 πŸ˜΅β€πŸ’«

If the current security mechanism doesn't support the specified protection level, the server SHOULD respond with 536 πŸ™…

If the server hasn't completed a protection buffer size negotiation with the client, it SHOULD respond with 503 🚧

The PROT command will be rejected, and the server SHOULD reply 503, if no previous PBSZ command was issued πŸ™ƒ

If the server isn't willing to accept the specified protection level, it SHOULD respond with 534 😀

If the server isn't able to accept the specified protection level, like when a required resource is unavailable, it SHOULD respond with 431 πŸ₯€

Otherwise, the server MUST reply with 200 to say the specified protection level is accepted πŸŽ€

CLEAR COMMAND CHANNEL (CCC)

This command doesn't take an argument 🫢

Sometimes it's desirable to use a security mechanism to authenticate and/or authorize the client and server without doing any integrity checking on later commands 🧐 This might happen in an environment where IP security is already in place, making sure the hosts are authenticated and the TCP streams can't be tampered with, but user authentication is still wanted 🏰

If unprotected commands are allowed on any connection, an attacker could insert a command into the control stream, and the server would have no way to know it was invalid 😱 To prevent that, once a security data exchange completes successfully, and if the mechanism supports integrity, then integrity MUST be used (through the MIC or ENC command, with a 631 or 632 reply) until the CCC command is issued to allow non-integrity protected control channel messages πŸ”’ The CCC command itself MUST be integrity protected πŸ›‘οΈ

Once the CCC command completes successfully, if a command isn't protected, then its reply MUST also be unprotected 🚨 This is there so it works with clients that don't support protection after CCC has been issued 🀝

This command MUST be preceded by a successful security data exchange πŸ—οΈ

If the command isn't integrity-protected, the server MUST respond with 533 πŸ™…β€β™€οΈ

If the server isn't willing to turn off the integrity requirement, it SHOULD respond with 534 πŸ™„

Otherwise, the server MUST reply with 200 to indicate that unprotected commands and replies may now be used on the command channel πŸ₯³


INTEGRITY PROTECTED COMMAND (MIC) and

CONFIDENTIALITY PROTECTED COMMAND (CONF) and

PRIVACY PROTECTED COMMAND (ENC)

The argument field of MIC is a Telnet string made of a base 64 encoded "safe" message, produced by a security mechanism specific message integrity procedure 🧁 The argument field of CONF is a Telnet string made of a base 64 encoded "confidential" message, produced by a security mechanism specific confidentiality procedure 🍬 The argument field of ENC is a Telnet string made of a base 64 encoded "private" message, produced by a security mechanism specific message integrity and confidentiality procedure 🍭

The server will decode and/or verify the encoded message πŸ”

This command MUST be preceded by a successful security data exchange πŸ’…

A server MAY require that the first command after a successful security data exchange be CCC, and not implement the protection commands at all. In that case, the server SHOULD respond with a 502 reply code. πŸ™ˆ

If the server can't base 64 decode the argument, it SHOULD respond with a 501 reply code. πŸ’€

If the server hasn't completed a security data exchange with the client yet, it SHOULD respond with a 503 reply code. ✨

If the server has completed a security data exchange with the client using a mechanism that supports integrity, and it needs a CCC command because of policy or implementation limitations, it SHOULD respond with a 503 reply code. The specificity is iconic 😭

If the server rejects the command because the current security mechanism doesn't support it, the server SHOULD respond with reply code 537. 🫢

If the server rejects the command (a failed checksum, for instance), it SHOULD respond with reply code 535. πŸ”₯

If the server isn't willing to accept the command (like if privacy is REQUIRED by policy, or if a CONF command shows up before a CCC command), it SHOULD respond with reply code 533. Boundaries, bestie πŸ‘‘

Otherwise, the command gets treated as an FTP command. An end-of-line code doesn't have to be included, but if one is included, it MUST be a Telnet end-of-line code, not a local end-of-line code. πŸ¦‹

The server MAY require that, under some or all circumstances, all commands be protected. In that case, it SHOULD make a 533 reply to commands other than MIC, CONF, and ENC. πŸŽ€


4. Login Authorization πŸ”

Okay so the security data exchange can, among other things, establish the identity of the client in a secure way to the server. πŸ’– That identity MAY be used as one input to the login authorization process. Main character energy πŸ‘‘

When it comes to the FTP login commands (AUTH, PASS, ACCT), the server MAY choose to change the sequence of commands and replies specified by RFC 959, as follows. There are also some brand new replies available. ✨

If the server is willing to let the user named by the USER command log in based on the identity established by the security data exchange, it SHOULD respond with reply code 232. 🌸

If the security mechanism requires a challenge/response password, it SHOULD respond to the USER command with reply code 336. The text part of the reply SHOULD contain the challenge. Honestly this is so important 🧚 The client MUST display the challenge to the user before prompting for the password in this case. This is especially relevant to more sophisticated clients or graphical user interfaces that have dialog boxes or other modal input. Those clients SHOULD be careful not to prompt for the password before the username has been sent to the server, in case the user needs the challenge from the 336 reply to build a valid password. 😭🫢


5. New FTP Replies πŸŽ€

The new reply codes come in two classes. The first class is new replies made necessary by the new FTP Security commands. The second class is a new reply type that marks protected replies. Two categories, zero drama πŸ’…

5.1. New individual reply codes

Here's the lineup πŸ“

   232 User logged in, authorized by security data exchange.
   234 Security data exchange complete.
   235 [ADAT=base64data]
         ; This reply indicates that the security data exchange
         ; completed successfully.  The square brackets are not
         ; to be included in the reply, but indicate that
         ; security data in the reply is OPTIONAL.

   334 [ADAT=base64data]
         ; This reply indicates that the requested security mechanism
         ; is ok, and includes security data to be used by the client
         ; to construct the next command.  The square brackets are not
         ; to be included in the reply, but indicate that security data
         ; in the reply is OPTIONAL.
   335 [ADAT=base64data]
         ; This reply indicates that the security data is
         ; acceptable, and more is required to complete the
         ; security data exchange.  The square brackets
         ; are not to be included in the reply, but indicate
         ; that security data in the reply is OPTIONAL.

   336 Username okay, need password.  Challenge is "...."
         ; The exact representation of the challenge SHOULD be chosen
         ; by the mechanism to be sensible to the human user of the
         ; system.

   431 Need some unavailable resource to process security.

   533 Command protection level denied for policy reasons.
   534 Request denied for policy reasons.
   535 Failed security check (hash, sequence, etc).
   536 Requested PROT level not supported by mechanism.
   537 Command protection level not supported by security mechanism.

5.2. Protected replies.

One new reply type is introduced. πŸ¦‹

   6yz   Protected reply

There are three reply codes of this type. The first, reply code 631, indicates an integrity protected reply. The second, reply code 632, indicates a confidentiality and integrity protected reply. The third, reply code 633, indicates a confidentiality protected reply. πŸ”

The text part of a 631 reply is a Telnet string made up of a base 64 encoded "safe" message, produced by a security mechanism specific message integrity procedure. The text part of a 632 reply is a Telnet string made up of a base 64 encoded "private" message, produced by a security mechanism specific message confidentiality and integrity procedure. The text part of a 633 reply is a Telnet string made up of a base 64 encoded "confidential" message, produced by a security mechanism specific message confidentiality procedure. ✨

The client will decode and verify the encoded reply. How failures decoding or verifying replies get handled is implementation-specific. An end-of-line code need not be included, but if one is included, it MUST be a Telnet end-of-line code, not a local end-of-line code. 🌈

A protected reply MAY only be sent if a security data exchange has succeeded. πŸ’œ

The 63z reply MAY be a multiline reply. In that case, the plaintext reply MUST be broken up into a number of fragments. Each fragment MUST be protected, then base 64 encoded in order into a separate line of the multiline reply. There need not be any correspondence between the line breaks in the plaintext reply and the encoded reply. Telnet end-of-line codes MUST appear in the plaintext of the encoded reply, except for the final end-of-line code, which is OPTIONAL. 🧚

The multiline reply MUST be formatted more strictly than the continuation specification in RFC 959. Specifically, each line before the last MUST be formed by the reply code, followed immediately by a hyphen, followed by a base 64 encoded fragment of the reply. πŸ’…

For example, if the plaintext reply is:

   123-First line
   Second line
     234 A line beginning with numbers
   123 The last line

then the resulting protected reply could be any of the following (the first example has a line break only to fit within the margins):

  631 base64(protect("123-First line\r\nSecond line\r\n  234 A line
  631-base64(protect("123-First line\r\n"))
  631-base64(protect("Second line\r\n"))
  631-base64(protect("  234 A line beginning with numbers\r\n"))
  631 base64(protect("123 The last line"))

  631-base64(protect("123-First line\r\nSecond line\r\n  234 A line b"))
  631 base64(protect("eginning with numbers\r\n123 The last line\r\n"))

6. Data Channel Encapsulation 🌸

When data transfers are protected between the client and server (in either direction), certain transformations and encapsulations MUST be performed so that the recipient can properly decode the transmitted file. Think of it as wrapping the gift so it arrives intact 🎁

The sender MUST apply all protection services after the transformations associated with the representation type, file structure, and transfer mode have been performed. For the purposes of protection, the data sent over the data channel is to be treated as a byte stream. πŸ¦‹

When a data transfer is performed in an authenticated manner, the authentication checks are performed on individual blocks of the file, rather than on the file as a whole. Consequently, it's possible for insertion attacks to insert blocks into the data stream (i.e., replays) that authenticate correctly, but result in a corrupted file going undetected by the receiver. This is the part that's giving me chills πŸ’€ To guard against such attacks, the specific security mechanism employed SHOULD include mechanisms to protect against them. Many GSS-API mechanisms usable with the specification in Appendix I, and the Kerberos mechanism in Appendix II, do so. πŸ”₯

The sender MUST take the input byte stream and break it up into blocks, such that each block, when encoded using a security mechanism specific procedure, is no larger than the buffer size negotiated by the client with the PBSZ command. Each block MUST be encoded, then transmitted with the length of the encoded block prepended as a four byte unsigned integer, most significant byte first. πŸŽ€

When the end of the file is reached, the sender MUST encode a block of zero bytes, and send this final block to the recipient before closing the data connection. 🫢

The recipient will read the four byte length, read a block of data that many bytes long, then decode and verify this block with a security mechanism specific procedure. This MUST be repeated until a block encoding a buffer of zero bytes is received. That signals the end of the encoded byte stream. 🌈

Any transformations associated with the representation type, file structure, and transfer mode are to be performed by the recipient on the byte stream resulting from the above process. ✨

When using block transfer mode, the sender's (cleartext) buffer size is independent of the block size. πŸ“

The server will reply 534 to a STOR, STOU, RETR, LIST, NLST, or APPE command if the current protection level is not at the level dictated by the server's security requirements for the particular file transfer. πŸ™ˆ

If any data protection services fail at any time during data transfer at the server end (including an attempt to send a buffer size greater than the negotiated maximum), the server will send a 535 reply to the data transfer command (either STOR, STOU, RETR, LIST, NLST, or APPE). Eek, that's a lot of moving parts 😭

7. Potential policy considerations ✨

okay so there are no restrictions on what client and server policy can look like πŸŽ€ but there are a few recommendations that an implementation should implement, bestie πŸ’…

  • once a security data exchange has happened, a server SHOULD require all commands to be protected (with integrity and/or confidentiality) πŸ”’, and it SHOULD protect all replies 🫢. replies should use the same level of protection as the command that produced them πŸ¦‹. this includes replies that indicate failure of the MIC, CONF, and ENC commands 😭. so here's the tea: it is not meaningful to require that AUTH and ADAT be protected πŸ™ˆ, but it IS meaningful and useful to require that PROT and PBSZ be protected πŸ’–. and the use of CCC is NOT RECOMMENDED πŸ’€, but it's defined so implementations that want that functionality can still work together 🌈
  • a client SHOULD encrypt the PASS command whenever possible πŸ”. it's reasonable for the server to refuse a non-encrypted PASS command if the server knows encryption is available 🧚
  • even though no security commands are required to be implemented, it is RECOMMENDED that an implementation provide all commands that can be implemented, given the mechanisms supported and the policy considerations of the site (export controls, for instance) πŸ‘‘

8. Declarative specifications πŸ“œ

ngl these sections are modelled after sections 5.3 and 5.4 of RFC 959 πŸ“, which describe the same information, except for the standard FTP commands and replies 🌸

8.1. FTP Security commands and arguments πŸŽ€

AUTH <SP> <mechanism-name> <CRLF>
ADAT <SP> <base64data> <CRLF>
PROT <SP> <prot-code> <CRLF>
PBSZ <SP> <decimal-integer> <CRLF>
MIC <SP> <base64data> <CRLF>
CONF <SP> <base64data> <CRLF>
ENC <SP> <base64data> <CRLF>

<mechanism-name> ::= <string>
<base64data> ::= <string>
        ; must be formatted as described in section 9
<prot-code> ::= C | S | E | P
<decimal-integer> ::= any decimal integer from 1 to (2^32)-1

8.2. Command-Reply sequences πŸ¦‹

Security Association Setup
   AUTH
      234
      334
      502, 504, 534, 431
      500, 501, 421
   ADAT
      235
      335
      503, 501, 535
      500, 501, 421
Data protection negotiation commands
   PBSZ
      200
      503
      500, 501, 421, 530
   PROT
      200
      504, 536, 503, 534, 431
      500, 501, 421, 530
Command channel protection commands
   MIC
      535, 533
      500, 501, 421
   CONF
      535, 533
      500, 501, 421
   ENC
      535, 533
      500, 501, 421
Security-Enhanced login commands (only new replies listed)
   USER
      232
      336
Data channel commands (only new replies listed)
   STOR
      534, 535
   STOU
      534, 535
   RETR
      534, 535

   LIST
      534, 535
   NLST
      534, 535
   APPE
      534, 535

In addition to these reply codes, any security command can return 500, 501, 502, 533, or 421. Any ftp command can return a reply code encapsulated in a 631, 632, or 633 reply once a security data exchange has completed successfully 🫢.


9. State Diagrams πŸ—ΊοΈ

this section has a state diagram that shows the flow of authentication and authorization in a security enhanced FTP implementation 🌈. the rectangular blocks are states where the client MUST issue a command πŸ’…, and the diamond blocks are states where the server MUST issue a response πŸ‘‘.

      ,------------------,  USER
   __\| Unauthenticated  |_________\
  |  /| (new connection) |         /|
  |   `------------------'          |
  |            |                    |
  |            | AUTH               |
  |            V                    |
  |           / \                   |
  | 4yz,5yz  /   \   234            |
  |<--------<     >------------->.  |
  |          \   /               |  |
  |           \_/                |  |
  |            |                 |  |
  |            | 334             |  |
  |            V                 |  |
  |  ,--------------------,      |  |
  |  | Need Security Data |<--.  |  |
  |  `--------------------'   |  |  |
  |            |              |  |  |
  |            | ADAT         |  |  |
  |            V              |  |  |
  |           / \             |  |  |
  | 4yz,5yz  /   \   335      |  |  |
  `<--------<     >-----------'  |  |
             \   /               |  |
              \_/                |  |
               |                 |  |
               | 235             |  |
               V                 |  |
       ,---------------.         |  |
  ,--->| Authenticated |<--------'  |  After the client and server
  |    `---------------'            |  have completed authenti-
  |            |                    |  cation, command must be
  |            | USER               |  integrity-protected if
  |            |                    |  integrity is available.  The
  |            |<-------------------'  CCC command may be issued to
  |            V                       relax this restriction.

  |           / \
  | 4yz,5yz  /   \   2yz
  |<--------<     >------------->.
  |          \   /               |
  |           \_/                |
  |            |                 |
  |            | 3yz             |
  |            V                 |
  |    ,---------------.         |
  |    | Need Password |         |
  |    `---------------'         |
  |            |                 |
  |            | PASS            |
  |            V                 |
  |           / \                |
  | 4yz,5yz  /   \   2yz         |
  |<--------<     >------------->|
  |          \   /               |
  |           \_/                |
  |            |                 |
  |            | 3yz             |
  |            V                 |
  |    ,--------------.          |
  |    | Need Account |          |
  |    `--------------'          |
  |            |                 |
  |            | ACCT            |
  |            V                 |
  |           / \                |
  | 4yz,5yz  /   \   2yz         |
  `<--------<     >------------->|
             \   /               |
              \_/                |
               |                 |
               | 3yz             |
               V                 |
         ,-------------.         |
         | Authorized  |/________|
         | (Logged in) |\
         `-------------'

10. Base 64 Encoding 🧁

base 64 encoding is the same as the Printable Encoding described in Section 4.3.2.4 of [RFC-1421] πŸ’œ, except that line breaks MUST NOT be included πŸ™…β€β™€οΈ. this encoding is defined as follows πŸ“.

proceeding from left to right, the bit string that comes out of the mechanism specific protection routine gets encoded into characters that are universally representable at all sites 🌍, though not necessarily with the same bit patterns (e.g., although the character "E" is represented in an ASCII-based system as hexadecimal 45 and as hexadecimal C5 in an EBCDIC-based system, the local significance of the two representations is equivalent) πŸ€“.

a 64-character subset of International Alphabet IA5 is used, which lets you represent 6 bits per printable character πŸ”’. (the proposed subset of characters is represented identically in IA5 and ASCII.) the character "=" signifies a special processing function used for padding within the printable encoding procedure πŸŽ€.

the encoding process represents 24-bit groups of input bits as output strings of 4 encoded characters πŸ’…. proceeding from left to right across a 24-bit input group output from the security mechanism specific message protection procedure, each 6-bit group is used as an index into an array of 64 printable characters, namely "[A-Z][a-z][0-9]+/" πŸ”€. the character referenced by the index is placed in the output string. these characters are selected so they're universally representable, and the set excludes characters with particular significance to Telnet (e.g., "<CR>", "<LF>", IAC) πŸ™ˆ.

special processing is performed if fewer than 24 bits are available in an input group at the end of a message 🧩. a full encoding quantum is always completed at the end of a message. when fewer than 24 input bits are available in an input group, zero bits are added (on the right) to form an integral number of 6-bit groups. output character positions which are not required to represent actual input data are set to the character "=". since all canonically encoded output is an integral number of octets, only the following cases can arise: (1) the final quantum of encoding input is an integral multiple of 24 bits; here, the final unit of encoded output will be an integral multiple of 4 characters with no "=" padding, (2) the final quantum of encoding input is exactly 8 bits; here, the final unit of encoded output will be two characters followed by two "=" padding characters, or (3) the final quantum of encoding input is exactly 16 bits; here, the final unit of encoded output will be three characters followed by one "=" padding character πŸ“.

implementors MUST keep in mind that the base 64 encodings in ADAT, MIC, CONF, and ENC commands, and in 63z replies, may be arbitrarily long πŸ“. thus, the entire line MUST be read before it can be processed πŸ“–. several successive reads on the control channel may be necessary πŸ”. it is not appropriate for a server to reject a command containing a base 64 encoding simply because it is too long (assuming that the decoding is otherwise well formed in the context in which it was sent) πŸ™…β€β™€οΈ.

case MUST NOT be ignored when reading commands and replies containing base 64 encodings πŸ” .

11. Security Considerations 🚨

this entire document deals with security considerations related to the File Transfer Protocol. this is the serious part, bestie, so read carefully πŸ˜­πŸ”’

third party file transfers cannot be secured using these extensions πŸ’€, since a security context cannot be established between two servers using these facilities (no control connection exists between servers over which to pass ADAT tokens) πŸ™ˆ. further work in this area is deferred 🫢.

12. Acknowledgements πŸ’–

i would like to thank the members of the CAT WG 🌸, as well as all participants in discussions on the "cat-ietf@mit.edu" mailing list πŸ“¬, for their contributions to this document. i would especially like to thank Sam Sjogren, John Linn, Ted Ts'o, Jordan Brown, Michael Kogut, Derrick Brashear, John Gardiner Myers, Denis Pinkas, and Karri Balk for their contributions to this work πŸ‘‘. of course, without Steve Lunt, the author of the first six revisions of this document, it would not exist at all πŸ¦‹.

13. References πŸ“š

[TELNET-SEC] Borman, D., "Telnet Authentication and Encryption
   Option", Work in Progress.

[RFC-1123] Braden, R., "Requirements for Internet Hosts --
   Application and Support", STD 3, RFC 1123, October 1989.

[RFC-1421] Linn, J., "Privacy Enhancement for Internet Electronic
   Mail: Part I: Message Encryption and Authentication Procedures",
   RFC 1421, February 1993.

14. Author's Address πŸ’Œ

Marc Horowitz
Cygnus Solutions
955 Massachusetts Avenue
Cambridge, MA 02139

Phone: +1 617 354 7688
EMail: marc@cygnus.com

Appendix I: Specification under the GSSAPI ✨

okay so new security mechanisms are way more useful if they're built as GSSAPI mechanisms instead of FTP security mechanisms πŸ’… existing ftp implementations can pick them up with little or no code changes, and other protocols built on the GSSAPI, like IMAP, can use the mechanism too without the designers doing any extra spec or implementation work πŸ”₯

The security mechanism name (for the AUTH command) for all GSSAPI mechanisms is GSSAPI. If the server supports a security mechanism that uses the GSSAPI, it MUST respond with a 334 reply code, which tells the client that an ADAT command is expected next πŸŽ€

The client MUST begin the authentication exchange by calling GSS_Init_Sec_Context, passing in 0 for input_context_handle (initially), and a targ_name equal to output_name from GSS_Import_Name, called with input_name_type of Host-Based Service and input_name_string of "ftp@hostname". Here "hostname" is the fully qualified host name of the server, all in lower case πŸ“ (Failing that, the client MAY try again with input_name_string of "host@hostname".) The output_token MUST then be base 64 encoded and sent to the server as the argument to an ADAT command. If GSS_Init_Sec_Context returns GSS_S_CONTINUE_NEEDED, the client MUST expect a token back in the reply to the ADAT command. That token MUST then get passed into another call to GSS_Init_Sec_Context. In this case, if GSS_Init_Sec_Context returns no output_token, the reply code from the server for the previous ADAT command MUST have been 235. If GSS_Init_Sec_Context returns GSS_S_COMPLETE, no more tokens are coming from the server, and the client MUST consider the server authenticated 🫢

The server MUST base 64 decode the argument to the ADAT command and pass the resulting token to GSS_Accept_Sec_Context as input_token. It sets acceptor_cred_handle to NULL (for "use default credentials") and uses 0 for input_context_handle (initially). If an output_token comes back, it MUST be base 64 encoded and returned to the client by including "ADAT=base64string" in the text of the reply. If GSS_Accept_Sec_Context returns GSS_S_COMPLETE, the reply code MUST be 235, and the server MUST consider the client authenticated πŸ‘‘ If GSS_Accept_Sec_Context returns GSS_S_CONTINUE_NEEDED, the reply code MUST be 335. Otherwise the reply code SHOULD be 535, and the text of the reply SHOULD contain a descriptive error message πŸ™ˆ

The chan_bindings input to GSS_Init_Sec_Context and GSS_Accept_Sec_Context SHOULD use the client internet address and server internet address as the initiator and acceptor addresses, respectively. The address type for both SHOULD be GSS_C_AF_INET. No application data SHOULD be specified 🧚

Since GSSAPI supports anonymous peers to security contexts, the client's authentication of the server might not actually establish an identity. Lowkey a plot twist πŸ‘€

The procedure for MIC commands, 631 replies, and Safe file transfers is:

GSS_Wrap for the sender, with conf_flag == FALSE

GSS_Unwrap for the receiver

The procedure for ENC commands, 632 replies, and Private file transfers is:

GSS_Wrap for the sender, with conf_flag == TRUE
GSS_Unwrap for the receiver

CONF commands and 633 replies are not supported πŸ’€

Both the client and the server SHOULD inspect the value of conf_avail to find out whether the peer supports confidentiality services πŸ”

When the security state gets reset (when AUTH is received a second time, or when REIN is received), this SHOULD be done by calling the GSS_Delete_sec_context function 🧹


Appendix II: Specification under Kerberos version 4 πŸ¦‹

The security mechanism name (for the AUTH command) for Kerberos Version 4 is KERBEROS_V4. If the server supports KERBEROS_V4, it MUST respond with a 334 reply code, indicating that an ADAT command is expected next 🌸

The client MUST retrieve a ticket for the Kerberos principal "ftp.hostname@realm" by calling krb_mk_req(3). It uses a principal name of "ftp", an instance equal to the first part of the canonical host name of the server with all letters in lower case (as returned by krb_get_phost(3)), the server's realm name (as returned by krb_realmofhost(3)), and an arbitrary checksum. The ticket MUST then be base 64 encoded and sent as the argument to an ADAT command ✨

If the "ftp" principal name is not a registered principal in the Kerberos database, the client MAY fall back on the "rcmd" principal name (same instance and realm). However, servers MUST accept only one or the other of these principal names, and MUST NOT be willing to accept either. Generally, if the server has a key for the "ftp" principal in its srvtab, then that principal only MUST be used. Otherwise, the "rcmd" principal only MUST be used πŸŽ€

The server MUST base 64 decode the argument to the ADAT command and pass the result to krb_rd_req(3). The server MUST add one to the checksum from the authenticator, convert the result to network byte order (most significant byte first), sign it using krb_mk_safe(3), and base 64 encode the result. Upon success, the server MUST reply to the client with a 235 code and include "ADAT=base64string" in the text of the reply. Upon failure, the server SHOULD reply 535 πŸ™…β€β™€οΈ

Upon receipt of the 235 reply from the server, the client MUST parse the text of the reply for the base 64 encoded data, decode it, convert it from network byte order, and pass the result to krb_rd_safe(3). The client MUST consider the server authenticated if the resulting checksum equals one plus the value previously sent 🫢

The procedure for MIC commands, 631 replies, and Safe file transfers is:

krb_mk_safe(3) for the sender
krb_rd_safe(3) for the receiver

The procedure for ENC commands, 632 replies, and Private file transfers is:

krb_mk_priv(3) for the sender
krb_rd_priv(3) for the receiver

CONF commands and 633 replies are not supported πŸ’€

Note that this specification for KERBEROS_V4 has no provision for negotiating alternate means for integrity and confidentiality routines. Also, the ADAT exchange does not convey whether the peer supports confidentiality services πŸ™ˆ

In order to stay within the allowed PBSZ, implementors MUST take note that a cleartext buffer will grow by 31 bytes when processed by krb_mk_safe(3), and will grow by 26 bytes when processed by krb_mk_priv(3). Don't skip this one, bestie 🚨

Copyright (C) The Internet Society (1997). All Rights Reserved.

This document and translations of it may be copied and furnished to others, and derivative works that comment on or otherwise explain it or assist in its implmentation may be prepared, copied, published andand distributed, in whole or in part, without restriction of any kind, provided that the above copyright notice and this paragraph are included on all such copies and derivative works. However, this document itself may not be modified in any way, such as by removing the copyright notice or references to the Internet Society or other Internet organizations, except as needed for the purpose of developing Internet standards in which case the procedures for copyrights defined in the Internet Standards process must be followed, or as required to translate it into languages other than English.

The limited permissions granted above are perpetual and will not be revoked by the Internet Society or its successors or assigns.

This document and the information contained herein is provided on an "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING TASK FORCE DISCLAIMS 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. 🌈

πŸ“¦ where this page comes from