Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CONC-648] Do not trust error packets received from the server prior to TLS handshake completion #223

Merged
merged 3 commits into from
Dec 21, 2023

Conversation

dlenski
Copy link
Contributor

@dlenski dlenski commented Jun 12, 2023

MariaDB Connector/C does not distinguish application-layer error
packets
that it receives prior to TLS
handshake completion from those that it receives immediately after.

(A trivially modified server built from
dlenski/mariadb-server@demonstration_of_CONC-648_vulnerability
can easily be used to demonstrate this.)

Pre-TLS error packet received from this trivially modified server. This packet
should NOT be trusted to actually originate from the server:

$ mariadb --ssl --ssl-verify-server-cert -uUsername -pVerySecretPassword -h CONC-648.vuln.demo.server.com
ERROR 1815 (HY000): Internal error: Client will accept this error as genuine even if running with --ssl --ssl-verify-server-cert, and even though this error is sent in plaintext

Post-(TLS handshake) error packet received from a normal MariaDB server upon
an attempt to connect with incorrect credentials. This error packet CAN be
trusted to actually originate from the server, assuming transitive trust in
the TLS protocol implementation and PKI-based certificate validation:

$ mariadb --ssl --ssl-verify-server-cert -uUsername -pWrongPassword -h $NORMAL_MARIADB10.6.14_SERVER
ERROR 1045 (28000): Access denied for user 'Username'@'A.B.C.D' (using password: YES)

This client behavior opens up MariaDB Connector/C clients to an extremely
straightforward downgrade attack.

An on-path or pervasive attacker can inject errors into MariaDB
client→server connections that are intended to be protected by TLS, and the
client has no clear mechanism to distinguish such errors from errors that
actually come from the server.

An attacker could easily use this to DOS a client, or even influence its
behavior. For example, consider a client application which is configured…

  1. To use TLS with server certificate validation
    (--ssl --ssl-verify-server-cert), and
  2. To wait for a back-off period and then retry connection attempts if the server
    responds with ER_CON_COUNT_ERROR ("Too many connections") from the
    server, and
  3. To give up and shut down if its connection attempts fail with
    ER_ACCESS_DENIED_ERROR ("Access denied for user"), on the assumption
    that this is due to an incorrect or expired password, and cannot be
    resolved without human intervention.

An attacker could completely disable the retry mechanism of this application
by intercepting connection attempts and replying with
ER_ACCESS_DENIED_ERROR packets.

This patch modifies MariaDB Connector/C so that if the client is configured
to use TLS, error packets received prior to the completion of the TLS
handshake are untrusted, and are changed to a generic CR_CONNECTION_ERROR.

$ mariadb --ssl --ssl-verify-server-cert -uUsername -pVerySecretPassword -h CONC-648.vuln.demo.server.com
ERROR 2002 (HY000): Received error packet before completion of TLS handshake. Suppressing its details so that the client cannot vary its behavior based on this UNTRUSTED input.

@dlenski
Copy link
Contributor Author

dlenski commented Jun 12, 2023

This commit message contains a tcpdump log of a MariaDB client communicating with a trivially-modified server which which unconditionally replies with a pre-(TLS handshake) application-layer error packet, which the client then displays without distinguishing its untrusted origin.

$ mariadb --version
mariadb  Ver 15.1 Distrib 10.6.14-MariaDB, for debian-linux-gnu (x86_64) using readline 5.2

$ mariadb --ssl --sl-verify-server-cert CONC-648.vuln.demo.server.com
ERROR 1815 (HY000): Internal error: Client will accept this error as genuine even if running with --ssl --ssl-verify-server-cert, and even though this error is sent in plaintext PRIOR TO TLS HANDSHAKE.
$ sudo tcpdump -n -X -i lo tcp port 3306
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
15:24:46.182853 IP 127.0.0.1.40234 > 127.0.0.1.3306: Flags [S], seq 1546762979, win 65495, options [mss 65495,sackOK,TS val 113496632 ecr 0,nop,wscale 7], length 0
        0x0000:  4500 003c 2b03 4000 8006 d1b6 7f00 0001  E..<+.@.........
        0x0010:  7f00 0001 9d2a 0cea 5c31 bae3 0000 0000  .....*..\1......
        0x0020:  a002 ffd7 fe30 0000 0204 ffd7 0402 080a  .....0..........
        0x0030:  06c3 d238 0000 0000 0103 0307            ...8........
15:24:46.182917 IP 127.0.0.1.3306 > 127.0.0.1.40234: Flags [S.], seq 3238764927, ack 1546762980, win 65483, options [mss 65495,sackOK,TS val 113496632 ecr 113496632,nop,wscale 7], length 0
        0x0000:  4500 003c 0000 4000 8006 fcb9 7f00 0001  E..<..@.........
        0x0010:  7f00 0001 0cea 9d2a c10b a17f 5c31 bae4  .......*....\1..
        0x0020:  a012 ffcb fe30 0000 0204 ffd7 0402 080a  .....0..........
        0x0030:  06c3 d238 06c3 d238 0103 0307            ...8...8....
15:24:46.182957 IP 127.0.0.1.40234 > 127.0.0.1.3306: Flags [.], ack 1, win 512, options [nop,nop,TS val 113496632 ecr 113496632], length 0
        0x0000:  4500 0034 2b04 4000 8006 d1bd 7f00 0001  E..4+.@.........
        0x0010:  7f00 0001 9d2a 0cea 5c31 bae4 c10b a180  .....*..\1......
        0x0020:  8010 0200 fe28 0000 0101 080a 06c3 d238  .....(.........8
        0x0030:  06c3 d238                                ...8
15:24:46.185305 IP 127.0.0.1.3306 > 127.0.0.1.40234: Flags [P.], seq 1:189, ack 1, win 512, options [nop,nop,TS val 113496635 ecr 113496632], length 188
        0x0000:  4508 00f0 622c 4000 8006 99d1 7f00 0001  E...b,@.........
        0x0010:  7f00 0001 0cea 9d2a c10b a180 5c31 bae4  .......*....\1..
        0x0020:  8018 0200 fee4 0000 0101 080a 06c3 d23b  ...............;
        0x0030:  06c3 d238 b800 0000 ff17 0749 6e74 6572  ...8.......Inter
        0x0040:  6e61 6c20 6572 726f 723a 2043 6c69 656e  nal.error:.Clien
        0x0050:  7420 7769 6c6c 2061 6363 6570 7420 7468  t.will.accept.th
        0x0060:  6973 2065 7272 6f72 2061 7320 6765 6e75  is.error.as.genu
        0x0070:  696e 6520 6576 656e 2069 6620 7275 6e6e  ine.even.if.runn
        0x0080:  696e 6720 7769 7468 202d 2d73 736c 202d  ing.with.--ssl.-
        0x0090:  2d73 736c 2d76 6572 6966 792d 7365 7276  -ssl-verify-serv
        0x00a0:  6572 2d63 6572 742c 2061 6e64 2065 7665  er-cert,.and.eve
        0x00b0:  6e20 7468 6f75 6768 2074 6869 7320 6572  n.though.this.er
        0x00c0:  726f 7220 6973 2073 656e 7420 696e 2070  ror.is.sent.in.p
        0x00d0:  6c61 696e 7465 7874 2050 5249 4f52 2054  laintext.PRIOR.T
        0x00e0:  4f20 544c 5320 4841 4e44 5348 414b 452e  O.TLS.HANDSHAKE.
15:24:46.185343 IP 127.0.0.1.40234 > 127.0.0.1.3306: Flags [.], ack 189, win 511, options [nop,nop,TS val 113496635 ecr 113496635], length 0
        0x0000:  4508 0034 2b05 4000 8006 d1b4 7f00 0001  E..4+.@.........
        0x0010:  7f00 0001 9d2a 0cea 5c31 bae4 c10b a23c  .....*..\1.....<
        0x0020:  8010 01ff fe28 0000 0101 080a 06c3 d23b  .....(.........;
        0x0030:  06c3 d23b                                ...;
15:24:46.185432 IP 127.0.0.1.40234 > 127.0.0.1.3306: Flags [F.], seq 1, ack 189, win 512, options [nop,nop,TS val 113496635 ecr 113496635], length 0
        0x0000:  4508 0034 2b06 4000 8006 d1b3 7f00 0001  E..4+.@.........
        0x0010:  7f00 0001 9d2a 0cea 5c31 bae4 c10b a23c  .....*..\1.....<
        0x0020:  8011 0200 fe28 0000 0101 080a 06c3 d23b  .....(.........;
        0x0030:  06c3 d23b                                ...;
15:24:46.185569 IP 127.0.0.1.3306 > 127.0.0.1.40234: Flags [F.], seq 189, ack 2, win 512, options [nop,nop,TS val 113496635 ecr 113496635], length 0
        0x0000:  4508 0034 622d 4000 8006 9a8c 7f00 0001  E..4b-@.........
        0x0010:  7f00 0001 0cea 9d2a c10b a23c 5c31 bae5  .......*...<\1..
        0x0020:  8011 0200 fe28 0000 0101 080a 06c3 d23b  .....(.........;
        0x0030:  06c3 d23b                                ...;

@dlenski dlenski force-pushed the fix_CONC-648_vulnerability branch 2 times, most recently from 7400409 to abad64b Compare June 12, 2023 23:46
@dlenski dlenski changed the title [CONC-648] Do not trust error packets received prior to TLS handshake completion [CONC-648] Do not trust error packets received from the server prior to TLS handshake completion Jun 20, 2023
@vuvova vuvova self-assigned this Jul 6, 2023
@vuvova vuvova self-requested a review July 6, 2023 15:54
libmariadb/mariadb_lib.c Outdated Show resolved Hide resolved
@dlenski dlenski force-pushed the fix_CONC-648_vulnerability branch from abad64b to 777e956 Compare July 6, 2023 20:57
@vuvova vuvova requested a review from 9EOR9 July 6, 2023 21:52
@vuvova
Copy link
Member

vuvova commented Jul 6, 2023

I think this is ok. Rewriting the error to CR_CONNECTION_ERROR will prevent any automatic reaction by the client. But the user, the human, will be able to understand what happened.

@9E0R9, what do you think?

@vuvova
Copy link
Member

vuvova commented Oct 21, 2023

I was just going to merge, but wanted to check the code in the context first. Few lines below there's

  /* Check if server sends an error */
  if (mysql->protocol_version == 0XFF)
  {
    net_get_error(end, pkt_length - 1, net->last_error, sizeof(net->last_error),
      &net->last_errno, net->sqlstate);
    /* fix for bug #26426 */
    if (net->last_errno == 1040)
      memcpy(net->sqlstate, "08004", SQLSTATE_LENGTH);
    goto error;
  }

does it need to be fixed similarly?

@dlenski
Copy link
Contributor Author

dlenski commented Dec 5, 2023

@vuvova wrote:

I was just going to merge, but wanted to check the code in the context first. Few lines below there's

does it need to be fixed similarly?

You're referring specifically to these lines, right?

/* Check if server sends an error */
if (mysql->protocol_version == 0XFF)
{
net_get_error(end, pkt_length - 1, net->last_error, sizeof(net->last_error),
&net->last_errno, net->sqlstate);
/* fix for bug #26426 */
if (net->last_errno == 1040)
memcpy(net->sqlstate, "08004", SQLSTATE_LENGTH);
goto error;
}

That is completely redundant dead code, and has been forever (not as a result of this PR):

  1. That redundant code checks whether the first byte of the packet received by ma_net_safe_read is 0xff.
  2. … but if the first byte of the packet received by ma_net_safe_read were 0xff, then ma_net_safe_read would have returned packet_error, and we would have taken the earlier branch which this PR is modifying.

In general, there is a great deal of redundant code and confusing naming* in mthd_my_real_connect.


* For example…
Why does char *end refer to the first byte of the packet??
Why is the 0xff byte indicating an error here referred to as "protocol version" despite that naming appearing nowhere in its documentation??
Etc.

dlenski added a commit to dlenski/mariadb-connector-c that referenced this pull request Dec 5, 2023
@dlenski
Copy link
Contributor Author

dlenski commented Dec 5, 2023

@vuvova, I added 8529511 to document the section of seemingly-unreachable code.

@rusher
Copy link
Collaborator

rusher commented Dec 6, 2023

updated missing possible Error packet in documentation https://mariadb.com/kb/en/connection/
Server after having established a socket can send an ERR_Packet. For example when reaching https://mariadb.com/kb/en/server-system-variables/#max_connections

@vuvova
Copy link
Member

vuvova commented Dec 13, 2023

Right, I agree, it's dead code. Instead of a comment, I'd rather remove it and add an assert instead.

Also, what if the connection is actually dropped by the server? Not an error message, but network error or something. It'd be less confusing to get CR_SERVER_LOST instead of your new "The authenticity of the following error cannot be verified". Because it actually can, can it not?

Meaning, I can apply this PR, but I'd prefer to put your if (mysql->options.use_ssl) after if (mysql->net.last_errno == CR_SERVER_LOST), not before. And I'd replace the dead code with an assert.

Would that be ok for you? I'll of course keep the attribution.

Or would you prefer to do these changes yourself?

Or do you disagree with this approach completely?

@dlenski
Copy link
Contributor Author

dlenski commented Dec 13, 2023

@vuvova wrote:

Right, I agree, it's dead code. Instead of a comment, I'd rather remove it and add an assert instead.

As you can see from 8529511, I did both.

I added a MYSQL_UNREACHABLE_ASSERT, and I added the explanatory comment so that if the assertion is ever triggered, whoever is debugging it will have an idea of why it was added.

Meaning, I can apply this PR, but I'd prefer to put your if (mysql->options.use_ssl) after if (mysql->net.last_errno == CR_SERVER_LOST), not before.

I had very intentionally chosen this order. Two (related) reasons:

  1. If the order is reversed then it will still be possible for a malicious actor to spoof a CR_SERVER_LOST by injecting it as a pre-TLS error packet. That's precisely the issue that this PR is designed to address. As I wrote in CONC-648:

    it is unsafe for the error to be relayed to the client in a form where the client could vary its behavior based on the contents of the error response.

  2. If the client library actually wants to signal a CR_SERVER_LOST error based on a lower-layer failure (e.g. network-layer outage), then it can and should do so. It simply should not be willing to do so based on interpreting an improperly-trusted packet allegedly sent by the server. Also from CONC-648:

    it's not that the client needs to hide the details of any error which it encounters before the TLS handshake. It's just that the client should hide the details of an application-level error which it appears to have received from the server before the TLS handshake (because that error could actually be from a MITM attacker).

@vuvova
Copy link
Member

vuvova commented Dec 13, 2023

As you can see from 8529511, I did both.

yes, I know you did both. I'm not a great fan of having dead code laying around, so I would prefer only the assert.

If the order is reversed then it will still be possible for a malicious actor to spoof a CR_SERVER_LOST by injecting it as a pre-TLS error packet

I checked the code again. May be I'm missing something, but I don't see how CR_SERVER_LOST can be a result of any data sent by the server. It looks like CR_SERVER_LOST can only be caused by a network error.

@dlenski
Copy link
Contributor Author

dlenski commented Dec 13, 2023

As you can see from 8529511, I did both.

yes, I know you did both. I'm not a great fan of having dead code laying around, so I would prefer only the assert.

What does "only the assert" mean?

  1. If you don't want dead code laying around, and are 100% confident that these lines are utterly unreachable, then we should just delete the unreachable lines entirely:

    /* Check if server sends an error */
    if (mysql->protocol_version == 0XFF)
    {
    net_get_error(end, pkt_length - 1, net->last_error, sizeof(net->last_error),
    &net->last_errno, net->sqlstate);
    /* fix for bug #26426 */
    if (net->last_errno == 1040)
    memcpy(net->sqlstate, "08004", SQLSTATE_LENGTH);
    goto error;
    }

  2. If you are (like me), only about 99.99% confident that these lines are unreachable, then it makes sense to leave the assert there in order to catch and diagnose whatever exceptionally tricky code path can lead to the presumed-unreachable code.

    And if the code path leading to an assertion is that obscure, then it surely makes sense to have a comment clarifying how it got there.

I checked the code again. May be I'm missing something, but I don't see how CR_SERVER_LOST can be a result of any data sent by the server.

The fact that a MITM can inject a pre-TLS error packet, which appears to have been sent by the server, is precisely the point of CONC-648.

You can test this yourself by building a trivially-modified server (dlenski/mariadb-server@demonstration_of_CONC-648_vulnerability), and you'll see that you can inject CR_SERVER_LOST errors
just as easily as any other.

If I modify the order of the if in the way that you've requested, it will remain possible to inject pre-TLS CR_SERVER_LOST errors which will be reported to the client in an indistinguishable way.

@vuvova
Copy link
Member

vuvova commented Dec 13, 2023

By "only the assert" I meant something like

@@ -1523,20 +1523,10 @@ MYSQL *mthd_my_real_connect(MYSQL *mysql, const char
 
   /* Check if version of protocol matches current one */
 
   mysql->protocol_version= end[0];
   end++;
 
+  assert(mysql->protocol_version != 0xFF);
-  /* Check if server sends an error */
-  if (mysql->protocol_version == 0XFF)
-  {
-    net_get_error(end, pkt_length - 1, net->last_error, sizeof(net->last_error>
-      &net->last_errno, net->sqlstate);
-    /* fix for bug #26426 */
-    if (net->last_errno == 1040)
-      memcpy(net->sqlstate, "08004", SQLSTATE_LENGTH);
-    goto error;
-  }
-
   if (mysql->protocol_version <  PROTOCOL_VERSION)
   {
     net->last_errno= CR_VERSION_ERROR;

but when I was writing it I reconsidered. end is signed char, it can never be 255, protocol_version is unsigned int. When net->read_pos[0] is 255, end will be -1, and protocol_version will be 0xFFFFFFFF, it cannot possibly be 0xFF whether ma_net_safe_read() filters this value out or not.

So, now I'd say — let's just remove that if(), no comments, no asserts, it's too broken.


If I modify the order of the if in the way that you've requested, it will remain possible to inject pre-TLS CR_SERVER_LOST errors which will be reported to the client in an indistinguishable way.

Hmm, indeed. I don't think the client should accept client-side errors from the server at all, pre-TLS or not. Something like

@@ -235,6 +235,13 @@ ma_net_safe_read(MYSQL *mysql)
         }
         goto restart;
       }
+      if (last_errno >= 2000 && last_errno < 3000 ||
+          last_errno >= 5000 && last_errno < 6000)
+      {
+        my_set_error(mysql, CR_MALFORMED_PACKET, SQLSTATE_UNKNOWN, 0);
+      }
+      else
+      {
         net->last_errno= last_errno;
         if (pos[0]== '#')
         {
@@ -248,6 +255,7 @@ ma_net_safe_read(MYSQL *mysql)
         ma_strmake(net->last_error,(char*) pos,
                 min(len,sizeof(net->last_error)-1));
+      }
     }
     else
     {
       my_set_error(mysql, CR_UNKNOWN_ERROR, SQLSTATE_UNKNOWN, 0);

Per @vuvova in
mariadb-corporation#223 (comment):

> I don't think the client should accept client-side errors from the server
> at all.

If the server sends an error packet with error codes in the ranges
`CR_{MIN,MAX}_ERROR` (codes [2000, 2999]) or `CER_{MIN,MAX}_ERROR` (codes
[5000, 5999]), we will replace these with `CR_MALFORMED_PACKET`, rather than
propagating them to the client user.
… completion

MariaDB Connector/C does not distinguish [application-layer error
packets](https://mariadb.com/kb/en/err_packet) that it receives prior to TLS
handshake completion from those that it receives immediately after.

(A trivially modified server built from
dlenski/mariadb-server@demonstration_of_CONC-648_vulnerability
can easily be used to demonstrate this.)

Pre-TLS error packet received from this trivially modified server. This packet
should NOT be trusted to actually originate from the server:

    $ mariadb --ssl --ssl-verify-server-cert -uUsername -pVerySecretPassword -h CONC-648.vuln.demo.server.com
    ERROR 1815 (HY000): Internal error: Client will accept this error as genuine even if running with --ssl --ssl-verify-server-cert, and even though this error is sent in plaintext PRIOR TO TLS HANDSHAKE.

Post-(TLS handshake) error packet received from a normal MariaDB server upon
an attempt to connect with incorrect credentials.  This error packet CAN be
trusted to actually originate from the server, assuming transitive trust in
the TLS protocol implementation and PKI-based certificate validation:

    $ mariadb --ssl --ssl-verify-server-cert -uUsername -pWrongPassword -h $NORMAL_MARIADB10.6.14_SERVER
    ERROR 1045 (28000): Access denied for user 'Username'@'A.B.C.D' (using password: YES)

This client behavior opens up MariaDB Connector/C clients to an extremely
straightforward [downgrade attack](https://en.wikipedia.org/wiki/Downgrade_attack).

An on-path or pervasive attacker can inject errors into MariaDB
client→server connections that are intended to be protected by TLS, and the
client has no clear mechanism to distinguish such errors from errors that
actually come from the server.

An attacker could easily use this to DOS a client, or even influence its
behavior.  For example, consider a client application which is configured…

1. To use TLS with server certificate validation
   (`--ssl --ssl-verify-server-cert`), and
2. To wait for a back-off period and then *retry* connection attempts if the server
   responds with `ER_CON_COUNT_ERROR` ("Too many connections") from the
   server, and
3. To give up and shut down if its connection attempts fail with
   `ER_ACCESS_DENIED_ERROR` ("Access denied for user"), on the assumption
   that this is due to an incorrect or expired password, and cannot be
   resolved without human intervention.

An attacker could completely disable the retry mechanism of this application
by intercepting connection attempts and replying with
`ER_ACCESS_DENIED_ERROR` packets.

This patch modifies MariaDB Connector/C so that if the client is configured
to use TLS, error packets received prior to the completion of the TLS
handshake are untrusted, and are changed to a generic `CR_CONNECTION_ERROR`.

    $ mariadb --ssl --ssl-verify-server-cert -uUsername -pVerySecretPassword -h CONC-648.vuln.demo.server.com
    ERROR 2002 (HY000): Received error packet before completion of TLS handshake. The authenticity of the following error cannot be verified:
    1815 - Internal error: Client will accept this error as genuine even if running with --ssl --ssl-verify-server-cert, and even though this error is sent in plaintext PRIOR TO TLS HANDSHAKE.

All new code of the whole pull request, including one or several files
that are either new files or modified ones, are contributed under the
BSD-new license. I am contributing on behalf of my employer Amazon Web
Services, Inc.
Based on Sergei Golubchik's question about this code section in
mariadb-corporation#223 (comment),
eventually culminating in the conclusion that it's literally impossible to
reach this code section based on the types and signedess of the variables
involved:
mariadb-corporation#223 (comment)

All new code of the whole pull request, including one or several files
that are either new files or modified ones, are contributed under the
BSD-new license. I am contributing on behalf of my employer Amazon Web
Services, Inc.
@dlenski
Copy link
Contributor Author

dlenski commented Dec 13, 2023

@vuvova wrote:

So, now I'd say — let's just removed that if(), no comments, no asserts, it's too broken.

Great catch that it's literally impossible by the variable types alone. 🙌

Done, removed entirely in ce7fc39.

If I modify the order of the if in the way that you've requested, it will remain possible to inject pre-TLS CR_SERVER_LOST errors which will be reported to the client in an indistinguishable way.

Hmm, indeed. I don't think the client should accept client-side errors from the server at all, pre-TLS or not. Something like

Great, that makes things much simpler!

  • I've prepended a commit f5f8032?w=1, to do exactly as you suggested and reject client-only error codes sent by the server.
  • I've modified the "main" commit (now 8279430) to reorder the if/else if as requested.

@dlenski dlenski requested a review from vuvova December 13, 2023 23:34
Copy link
Member

@vuvova vuvova left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great, thanks!

@@ -241,18 +241,30 @@ ma_net_safe_read(MYSQL *mysql)
}
goto restart;
}
net->last_errno= last_errno;
if (pos[0]== '#')
if (last_errno >= CR_MIN_ERROR && last_errno <= CR_MAX_ERROR ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:
if (IS_MYSQL_ERROR(last_errno) || IS_MARIADB_ERROR(last_errno)) ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to fix travis builds, otherwise the code doesn't compile with -Werror. But I don't want to drag this any longer, I'll merge and fix afterwards with a separate commit

Copy link
Contributor Author

@dlenski dlenski Dec 21, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about:
if (IS_MYSQL_ERROR(last_errno) || IS_MARIADB_ERROR(last_errno)) ?

Makes sense to simplify the code, but per the definition of the IS_MYSQL_ERROR macro, this would not be 100% equivalent.

  1. Current version rejects error codes [2000, 2999] + [5000, 5999].
  2. @9EOR9's proposed replacement would reject only error codes [2000, 2061] + [5000, 5023].

Because these error codes are client-side-only, they should be equivalent as long as no one forgets to update CR_MYSQL_LAST_ERROR and CR_MARIADB_LAST_ERROR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider modifying the IS_MYSQL_ERROR and IS_MARIADB_ERROR macros so that they include the entire client-side error ranges, rather than depending on CR_MYSQL_LAST_ERROR and CR_MARIADB_LAST_ERROR being updated.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That wouldn't hurt, but I'll leave it to @9EOR9. It doesn't matter much whether we test the full possible range or the actual range only, All error codes within the range we test can be considered safe, not injected by MitM, but generated here in the client library. All codes outside of that range are unsafe (before SSL). So as long as the code never trusts error message numbers outside of the tested range, it should be good.

@vuvova vuvova merged commit bd87353 into mariadb-corporation:3.3 Dec 21, 2023
1 check failed
vuvova pushed a commit that referenced this pull request Dec 21, 2023
Per @vuvova in
#223 (comment):

> I don't think the client should accept client-side errors from the server
> at all.

If the server sends an error packet with error codes in the ranges
`CR_{MIN,MAX}_ERROR` (codes [2000, 2999]) or `CER_{MIN,MAX}_ERROR` (codes
[5000, 5999]), we will replace these with `CR_MALFORMED_PACKET`, rather than
propagating them to the client user.
@dlenski dlenski deleted the fix_CONC-648_vulnerability branch December 21, 2023 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants