Skip to content

Commit b5c2781

Browse files
committed
Fix handshake bug when SSL is enabled
This fixes a bug where the username was not being populated in the `ClientHandshake` struct when SSL is enabled. When the client requests SSL (and so CapabilityFlags::CLIENT_SSL is set) the protocol expects to stop the handshake before the username is sent by the client, switch to SSL and then restart the handshake (this time including SSL) but over SSL instead of plain text. This bug was introduced because the username was _always_ not being read from the data sent by the server when SSL is enabled, instead of skipping it the first time, but reading it as expected when restarting the handshake over SSL. This change fixes the bug, by passing in extra information to the client handshake function. Additional tests are also added to cover this logic.
1 parent 35225bb commit b5c2781

File tree

2 files changed

+67
-5
lines changed

2 files changed

+67
-5
lines changed

src/commands.rs

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ pub struct ClientHandshake<'a> {
99
username: Option<&'a [u8]>,
1010
}
1111

12-
pub fn client_handshake(i: &[u8]) -> nom::IResult<&[u8], ClientHandshake<'_>> {
12+
pub fn client_handshake(i: &[u8], after_tls: bool) -> nom::IResult<&[u8], ClientHandshake<'_>> {
1313
// mysql handshake protocol documentation
1414
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_response.html
1515

@@ -27,7 +27,7 @@ pub fn client_handshake(i: &[u8]) -> nom::IResult<&[u8], ClientHandshake<'_>> {
2727
let (i, collation) = nom::bytes::complete::take(1u8)(i)?;
2828
let (i, _) = nom::bytes::complete::take(23u8)(i)?;
2929

30-
let (i, username) = if !capabilities.contains(CapabilityFlags::CLIENT_SSL) {
30+
let (i, username) = if after_tls || !capabilities.contains(CapabilityFlags::CLIENT_SSL) {
3131
let (i, user) = nom::bytes::complete::take_until(&b"\0"[..])(i)?;
3232
let (i, _) = nom::bytes::complete::tag(b"\0")(i)?;
3333
(i, Some(user))
@@ -159,7 +159,7 @@ mod tests {
159159
let r = Cursor::new(data);
160160
let mut pr = PacketConn::new(r);
161161
let (_, p) = pr.next().unwrap().unwrap();
162-
let (_, handshake) = client_handshake(&p).unwrap();
162+
let (_, handshake) = client_handshake(&p, false).unwrap();
163163
println!("{:?}", handshake);
164164
assert!(handshake
165165
.capabilities
@@ -178,6 +178,68 @@ mod tests {
178178
assert_eq!(handshake.maxps, 16777216);
179179
}
180180

181+
#[test]
182+
fn it_parses_handshake_with_ssl_enabled() {
183+
let data = [
184+
0x25, 0x00, 0x00, 0x01, 0x85, 0xae, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x01, 0x21, 0x00,
185+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
186+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x6f, 0x6e, 0x00, 0x00, 0x05,
187+
]
188+
.to_vec();
189+
let r = Cursor::new(data);
190+
let mut pr = PacketConn::new(r);
191+
let (_, p) = pr.next().unwrap().unwrap();
192+
let (_, handshake) = client_handshake(&p, false).unwrap();
193+
println!("{:?}", handshake);
194+
assert!(handshake
195+
.capabilities
196+
.contains(CapabilityFlags::CLIENT_LONG_PASSWORD));
197+
assert!(handshake
198+
.capabilities
199+
.contains(CapabilityFlags::CLIENT_MULTI_RESULTS));
200+
assert!(!handshake
201+
.capabilities
202+
.contains(CapabilityFlags::CLIENT_CONNECT_WITH_DB));
203+
assert!(!handshake
204+
.capabilities
205+
.contains(CapabilityFlags::CLIENT_DEPRECATE_EOF));
206+
assert!(handshake.capabilities.contains(CapabilityFlags::CLIENT_SSL));
207+
assert_eq!(handshake.collation, UTF8_GENERAL_CI);
208+
assert_eq!(handshake.username, None);
209+
assert_eq!(handshake.maxps, 16777216);
210+
}
211+
212+
#[test]
213+
fn it_parses_handshake_after_ssl() {
214+
let data = [
215+
0x25, 0x00, 0x00, 0x01, 0x85, 0xae, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x01, 0x21, 0x00,
216+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
217+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x6f, 0x6e, 0x00, 0x00, 0x05,
218+
]
219+
.to_vec();
220+
let r = Cursor::new(data);
221+
let mut pr = PacketConn::new(r);
222+
let (_, p) = pr.next().unwrap().unwrap();
223+
let (_, handshake) = client_handshake(&p, true).unwrap();
224+
println!("{:?}", handshake);
225+
assert!(handshake
226+
.capabilities
227+
.contains(CapabilityFlags::CLIENT_LONG_PASSWORD));
228+
assert!(handshake
229+
.capabilities
230+
.contains(CapabilityFlags::CLIENT_MULTI_RESULTS));
231+
assert!(!handshake
232+
.capabilities
233+
.contains(CapabilityFlags::CLIENT_CONNECT_WITH_DB));
234+
assert!(!handshake
235+
.capabilities
236+
.contains(CapabilityFlags::CLIENT_DEPRECATE_EOF));
237+
assert!(handshake.capabilities.contains(CapabilityFlags::CLIENT_SSL));
238+
assert_eq!(handshake.collation, UTF8_GENERAL_CI);
239+
assert_eq!(handshake.username.unwrap(), &b"jon"[..]);
240+
assert_eq!(handshake.maxps, 16777216);
241+
}
242+
181243
#[test]
182244
fn it_parses_request() {
183245
let data = [

src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ impl<B: MysqlShim<RW>, RW: Read + Write> MysqlIntermediary<B, RW> {
272272
"peer terminated connection",
273273
)
274274
})?;
275-
let handshake = commands::client_handshake(&handshake)
275+
let handshake = commands::client_handshake(&handshake, false)
276276
.map_err(|e| match e {
277277
nom::Err::Incomplete(_) => io::Error::new(
278278
io::ErrorKind::UnexpectedEof,
@@ -323,7 +323,7 @@ impl<B: MysqlShim<RW>, RW: Read + Write> MysqlIntermediary<B, RW> {
323323
"peer terminated connection",
324324
)
325325
})?;
326-
let _handshake = commands::client_handshake(&handshake)
326+
let _handshake = commands::client_handshake(&handshake, true)
327327
.map_err(|e| match e {
328328
nom::Err::Incomplete(_) => io::Error::new(
329329
io::ErrorKind::UnexpectedEof,

0 commit comments

Comments
 (0)