Re: [PATCH] Remove hardcoded static string length

From: Jeffrey E Altman
Date: Tue May 30 2023 - 10:40:00 EST


On 5/29/2023 9:32 AM, David Laight wrote:
From: Jeffrey E Altman
Sent: 27 May 2023 16:09

On 5/25/2023 11:37 AM, Kenny Ho wrote:
On Thu, May 25, 2023 at 11:04 AM David Laight<David.Laight@xxxxxxxxxx> wrote:
"The standard formulation seems to be: <project> <version> built
<yyyy>-<mm>-<dd>"
Which I don't recall the string actually matching?
Also the people who like reproducible builds don't like __DATE__.
That's correct, it was not matching even when it was introduced. I am
simply taking that as people caring about the content and not simply
making rxrpc_version_string == UTS_RELEASE. The current format is:

"linux-" UTS_RELEASE " AF_RXRPC"

Kenny
The RX_PACKET_TYPE_VERSION query is issued by the "rxdebug <host> <port>
-version" command which prints the received string to stdout.   It has
also been used some implementations to record the version of the peer.
Although it is required that a response to the RX_PACKET_TYPE_VERSION
query be issued, there is no requirement that the returned string
contain anything beyond a single NUL octet.
Does that mean that the zero-padding/truncation to 65 bytes is bogus?

Its bogus.  The original code implemented in 1988 was very sloppy.  Over the years some of the sloppiness was misinterpreted as well thought out design decisions.

The CMU/Transarc/IBM rxdebug allocates a char[64] for the received version c-string.   The original version wrote a 65 octets of a static productVersion c-string that was defined far away from the code that wrote it to the network.   Over time, the productVersion c-string was changed from a fixed length c-string to a build time generated c-string that had variable length.  It could be shorter than 65-octets or longer (up to 2000 octets). However, the Rx code that wrote the version data to the wire continued to write 65-octets regardless of the size of the productVersion c-string.   This was noticed after IBM AFS 3.6 was forked to form OpenAFS because the version strings became shorter.

OpenAFS 1.2 began the practice of constructing the responseData as follows:

1. Allocate a fixed size char[66] on the stack which I will call 'responseData'

2. bzero responseData

3. copy the productVersion into responseData as follows
    snprintf(responseData, sizeof(responseData), "%s", productVersion)

4. write 65 octets from responseData to the wire

This change (OpenAFS commit 902055cc97a8dd26a26af55778c0b3843de3cc70) addressed the problem of productVersion being shorter than 65-octets by writing a padded response but it did nothing to ensure that the response written to the network was NUL terminated if strlen(productVersion) >= 65.

The author of this commit also wrote the rx-spec.txt document that David Howells used as his source.

Its all bogus.  There is absolutely no requirement that a NUL padded buffer be sent.   The response can be a valid c-string of any length with the only constraint being that the resulting udp packet should not be too large to deliver.

Additionally is the response supposed to the '\0' terminated?
The existing code doesn't guarantee that at all.

The IBM/Transarc/CMU derived RX stacks issue process the Get Version request as follows:

1. Allocate a versionBuffer on the stack to store the returned string.  In the original CMU/Transarc/IBM Rx stack this was char[64].

2. Issue RX_PACKET_TYPE_VERSION query which consists of a 28 octet Rx header with no data.

3. Read RX_PACKET_TYPE_VERSION response into a 1500 octet buffer and remember bytesRead

3a. If  bytesRead is less than 28 octets, its too small to be a Rx header, ignore it.

3b. If the Rx header contents do not match the query, discard it.

3c. The data response begins at &buffer[28] and will be (bytesRead - 28) octets.   Copy MIN(sizeof(versionBuffer), (bytesRead - 28)) octets of the responseData to the versionBuffer.

4. NUL terminate the versionBuffer.  Note: prior to OpenAFS 1.6.23 this step was not performed which is why the sender should always NUL terminate the transmitted version data but this is a bug in the receiver and it is not required that the RX_PACKET_TYPE_VERSION response data be limited to a 64 octet c-string.

5. Do something with the contents of the versionBuffer such as
    printf("AFS version: %s\n", versionBuffer);


Although it is convenient to be able to remotely identify the version of
an Rx implementation, there are good reasons why this information should
not be exposed to an anonymous requester:

1. Linux AF_RXRPC is part of the kernel.  As such, returning
UTS_RELEASE identifies to potential attackers the explicit kernel
version, architecture and perhaps distro.  As this query can be
issued anonymously, this provides an information disclosure that can
be used to target known vulnerabilities in the kernel.
I guess it could even be used as a probe to find more/interesting
systems to attack once inside the firewall.
Exactly.
2. The RX_PACKET_TYPE_VERSION reply is larger than the query by the
number of octets in the version data.  As the query is received via
udp with no reachability test, it means that the
RX_PACKET_TYPE_VERSION query/response can be used to perform an 3.3x
amplification attack: 28 octets in and potentially 93 octets out.

With my security hat on I would suggest that either AF_RXRPC return a
single NUL octet or the c-string "AF_RXRPC" and nothing more.
Is there any point including "AF_RXRPC"?
It is almost certainly implied by the message format.

There is no required message format.   IBM AFS could be built such that the resulting version c-string was

  "@(#)CML not accessible: No version information"

which was shorter than 65 octets.

Or the exact text from the standard - which might be:
"version string - to be supplied by O.E.M."
(I've seen hardware versions with strings like the above that
exactly match the datasheet....)

The rx-spec.txt was an attempt by an individual circa 2001 to document the RxRPC protocol from reading the OpenAFS source code. The document did not undergo peer review and the author did not have the benefit of access to the development history or experience developing/maintaining/extending an Rx implementation.   It was an excellent first effort but it should not be considered gospel.

Limiting the version to (eg) 6.2 would give a hint to the
capabilities/bugs without giving away all the relative addresses
in something like a RHEL kernel.

I would be fine with something like "Linux 6.2".

Jeffrey Altman


Attachment: smime.p7s
Description: S/MIME Cryptographic Signature