Skip to content

Commit e614b26

Browse files
committed
add mysql_native_password auth support
1 parent 45b5b8a commit e614b26

File tree

5 files changed

+364
-23
lines changed

5 files changed

+364
-23
lines changed

examples/serve_auth.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
//! After running this, you should be able to run:
2+
//!
3+
//! ```console
4+
//! $ echo "SELECT * FROM foo" | mysql -h 127.0.0.1 --table
5+
//! $
6+
//! ```
7+
8+
extern crate msql_srv;
9+
extern crate mysql;
10+
extern crate mysql_common as myc;
11+
12+
use msql_srv::*;
13+
use std::io;
14+
use std::net;
15+
use std::thread;
16+
17+
struct Backend;
18+
impl<W: io::Write> MysqlShim<W> for Backend {
19+
type Error = io::Error;
20+
21+
fn on_prepare(&mut self, _: &str, info: StatementMetaWriter<W>) -> io::Result<()> {
22+
info.reply(42, &[], &[])
23+
}
24+
fn on_execute(
25+
&mut self,
26+
_: u32,
27+
_: msql_srv::ParamParser,
28+
results: QueryResultWriter<W>,
29+
) -> io::Result<()> {
30+
results.completed(0, 0)
31+
}
32+
fn on_close(&mut self, _: u32) {}
33+
34+
fn on_query(&mut self, sql: &str, results: QueryResultWriter<W>) -> io::Result<()> {
35+
println!("execute sql {:?}", sql);
36+
results.start(&[])?.finish()
37+
}
38+
39+
/// authenticate method for the specified plugin
40+
fn authenticate(
41+
&self,
42+
auth_plugin: &str,
43+
username: &[u8],
44+
salt: &[u8],
45+
auth_data: &[u8],
46+
) -> bool {
47+
println!(
48+
"auth_plugin, {:?}, user: {:?} , salt: {:?}, auth_data:{:?}",
49+
auth_plugin, username, salt, auth_data
50+
);
51+
52+
username == "default".as_bytes()
53+
}
54+
55+
fn on_init(&mut self, _: &str, _: InitWriter<'_, W>) -> Result<(), Self::Error> {
56+
Ok(())
57+
}
58+
59+
fn version(&self) -> &str {
60+
// 5.1.10 because that's what Ruby's ActiveRecord requires
61+
"5.1.10-alpha-msql-proxy"
62+
}
63+
64+
fn connect_id(&self) -> u32 {
65+
u32::from_le_bytes([0x08, 0x00, 0x00, 0x00])
66+
}
67+
68+
fn default_auth_plugin(&self) -> &str {
69+
"mysql_native_password"
70+
}
71+
72+
fn auth_plugin_for_username(&self, _user: &[u8]) -> &str {
73+
"mysql_native_password"
74+
}
75+
76+
fn salt(&self) -> [u8; 20] {
77+
let bs = ";X,po_k}>o6^Wz!/kM}N".as_bytes();
78+
let mut scramble: [u8; 20] = [0; 20];
79+
for i in 0..20 {
80+
scramble[i] = bs[i];
81+
if scramble[i] == b'\0' || scramble[i] == b'$' {
82+
scramble[i] = scramble[i] + 1;
83+
}
84+
}
85+
scramble
86+
}
87+
}
88+
89+
fn main() {
90+
let mut threads = Vec::new();
91+
let listener = net::TcpListener::bind("127.0.0.1:3306").unwrap();
92+
93+
while let Ok((s, _)) = listener.accept() {
94+
println!("{:?}", "got one socket");
95+
threads.push(thread::spawn(move || {
96+
MysqlIntermediary::run_on_tcp(Backend, s).unwrap();
97+
}));
98+
}
99+
100+
for t in threads {
101+
t.join().unwrap();
102+
}
103+
}
104+
105+
#[test]
106+
fn it_works() {
107+
let c: u8 = b'\0';
108+
let d: u8 = 0 as u8;
109+
let e: u8 = 0x00;
110+
111+
assert_eq!(c, d);
112+
assert_eq!(e, d);
113+
}

examples/serve_one.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ fn main() {
4141
let listener = net::TcpListener::bind("127.0.0.1:3306").unwrap();
4242

4343
while let Ok((s, _)) = listener.accept() {
44+
println!("{:?}", "got one socket");
4445
threads.push(thread::spawn(move || {
4546
MysqlIntermediary::run_on_tcp(Backend, s).unwrap();
4647
}));

src/commands.rs

Lines changed: 97 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,90 @@
11
use crate::myc::constants::{CapabilityFlags, Command as CommandByte};
22

33
#[derive(Debug)]
4-
pub struct ClientHandshake<'a> {
4+
pub struct ClientHandshake {
55
capabilities: CapabilityFlags,
66
maxps: u32,
77
collation: u16,
8-
username: &'a [u8],
8+
pub(crate) db: Option<Vec<u8>>,
9+
pub(crate) username: Vec<u8>,
10+
pub(crate) auth_response: Vec<u8>,
11+
pub(crate) auth_plugin: Vec<u8>,
912
}
1013

1114
#[allow(clippy::branches_sharing_code)]
12-
pub fn client_handshake(i: &[u8]) -> nom::IResult<&[u8], ClientHandshake<'_>> {
15+
pub fn client_handshake(i: &[u8]) -> nom::IResult<&[u8], ClientHandshake> {
1316
// mysql handshake protocol documentation
1417
// https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_connection_phase_packets_protocol_handshake_response.html
1518

1619
let (i, cap) = nom::number::complete::le_u16(i)?;
1720

18-
if CapabilityFlags::from_bits_truncate(cap as u32).contains(CapabilityFlags::CLIENT_PROTOCOL_41)
19-
{
21+
let mut capabilities = CapabilityFlags::from_bits_truncate(cap as u32);
22+
if capabilities.contains(CapabilityFlags::CLIENT_PROTOCOL_41) {
2023
// HandshakeResponse41
2124
let (i, cap2) = nom::number::complete::le_u16(i)?;
2225
let cap = (cap2 as u32) << 16 | cap as u32;
2326

27+
capabilities = CapabilityFlags::from_bits_truncate(cap as u32);
28+
2429
let (i, maxps) = nom::number::complete::le_u32(i)?;
2530
let (i, collation) = nom::bytes::complete::take(1u8)(i)?;
31+
2632
let (i, _) = nom::bytes::complete::take(23u8)(i)?;
33+
2734
let (i, username) = nom::bytes::complete::take_until(&b"\0"[..])(i)?;
2835
let (i, _) = nom::bytes::complete::tag(b"\0")(i)?;
2936

37+
println!("iii {:?}", i);
38+
let (i, auth_response) =
39+
if capabilities.contains(CapabilityFlags::CLIENT_PLUGIN_AUTH_LENENC_CLIENT_DATA) {
40+
println!("iii0 {:?}", i);
41+
42+
let (i, size) = read_length_encoded_number(i)?;
43+
nom::bytes::complete::take(size)(i)?
44+
} else if capabilities.contains(CapabilityFlags::CLIENT_SECURE_CONNECTION) {
45+
println!("iii1 {:?}", i);
46+
47+
let (i, size) = nom::number::complete::le_u8(i)?;
48+
nom::bytes::complete::take(size)(i)?
49+
} else {
50+
println!("iii2 {:?}", i);
51+
52+
nom::bytes::complete::take_until(&b"\0"[..])(i)?
53+
};
54+
55+
println!("iii3 {:?}", i);
56+
57+
let (i, db) =
58+
if capabilities.contains(CapabilityFlags::CLIENT_CONNECT_WITH_DB) && !i.is_empty() {
59+
let (i, db) = nom::bytes::complete::take_until(&b"\0"[..])(i)?;
60+
let (i, _) = nom::bytes::complete::tag(b"\0")(i)?;
61+
(i, Some(db))
62+
} else {
63+
(i, None)
64+
};
65+
66+
let (i, auth_plugin) =
67+
if capabilities.contains(CapabilityFlags::CLIENT_PLUGIN_AUTH) && !i.is_empty() {
68+
println!("iii55 {:?}", i);
69+
70+
let (i, auth_plugin) = nom::bytes::complete::take_until(&b"\0"[..])(i)?;
71+
72+
let (i, _) = nom::bytes::complete::tag(b"\0")(i)?;
73+
(i, auth_plugin)
74+
} else {
75+
(i, &b""[..])
76+
};
77+
3078
Ok((
3179
i,
3280
ClientHandshake {
3381
capabilities: CapabilityFlags::from_bits_truncate(cap),
3482
maxps,
3583
collation: u16::from(collation[0]),
36-
username,
84+
username: username.to_vec(),
85+
db: db.map(|c| c.to_vec()),
86+
auth_response: auth_response.to_vec(),
87+
auth_plugin: auth_plugin.to_vec(),
3788
},
3889
))
3990
} else {
@@ -42,19 +93,51 @@ pub fn client_handshake(i: &[u8]) -> nom::IResult<&[u8], ClientHandshake<'_>> {
4293
let (i, maxps2) = nom::number::complete::le_u8(i)?;
4394
let maxps = (maxps2 as u32) << 16 | maxps1 as u32;
4495
let (i, username) = nom::bytes::complete::take_until(&b"\0"[..])(i)?;
96+
let (i, _) = nom::bytes::complete::tag(b"\0")(i)?;
97+
98+
let (i, auth_response, db) =
99+
if capabilities.contains(CapabilityFlags::CLIENT_CONNECT_WITH_DB) {
100+
let (i, auth_response) = nom::bytes::complete::tag(b"\0")(i)?;
101+
let (i, _) = nom::bytes::complete::tag(b"\0")(i)?;
102+
103+
let (i, db) = nom::bytes::complete::tag(b"\0")(i)?;
104+
let (i, _) = nom::bytes::complete::tag(b"\0")(i)?;
105+
106+
(i, auth_response, Some(db))
107+
} else {
108+
(&b""[..], i, None)
109+
};
45110

46111
Ok((
47112
i,
48113
ClientHandshake {
49114
capabilities: CapabilityFlags::from_bits_truncate(cap as u32),
50115
maxps,
51116
collation: 0,
52-
username,
117+
username: username.to_vec(),
118+
db: db.map(|c| c.to_vec()),
119+
auth_response: auth_response.to_vec(),
120+
auth_plugin: vec![],
53121
},
54122
))
55123
}
56124
}
57125

126+
fn read_length_encoded_number(i: &[u8]) -> nom::IResult<&[u8], u64> {
127+
let (i, b) = nom::number::complete::le_u8(i)?;
128+
let size: usize = match b {
129+
0xfb => return Ok((i, 0)),
130+
0xfc => 2,
131+
0xfd => 3,
132+
0xfe => 8,
133+
_ => return Ok((i, b as u64)),
134+
};
135+
let mut bytes = [0u8; 8];
136+
let (i, b) = nom::bytes::complete::take(size)(i)?;
137+
bytes[..size].copy_from_slice(b);
138+
Ok((i, u64::from_le_bytes(bytes)))
139+
}
140+
58141
#[derive(Debug, PartialEq, Eq)]
59142
pub enum Command<'a> {
60143
Query(&'a [u8]),
@@ -143,10 +226,14 @@ mod tests {
143226
#[test]
144227
fn it_parses_handshake() {
145228
let data = &[
146-
0x25, 0x00, 0x00, 0x01, 0x85, 0xa6, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x01, 0x21, 0x00,
229+
0x4f, 0x00, 0x00, 0x01, 0x85, 0xa6, 0xff, 0x09, 0x00, 0x00, 0x00, 0x01, 0x21, 0x00,
147230
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
148-
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x6f, 0x6e, 0x00, 0x00,
231+
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6a, 0x6f, 0x6e, 0x00, 0x14, 0xf7,
232+
0xd1, 0x6c, 0xe9, 0x0d, 0x2f, 0x34, 0xb0, 0x2f, 0xd8, 0x1d, 0x18, 0xc7, 0xa4, 0xe8,
233+
0x98, 0x97, 0x67, 0xeb, 0xad, 0x6d, 0x79, 0x73, 0x71, 0x6c, 0x5f, 0x6e, 0x61, 0x74,
234+
0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6f, 0x72, 0x64, 0x00,
149235
];
236+
150237
let r = Cursor::new(&data[..]);
151238
let mut pr = PacketReader::new(r);
152239
let (_, p) = pr.next().unwrap().unwrap();
@@ -161,7 +248,7 @@ mod tests {
161248
assert!(!handshake
162249
.capabilities
163250
.contains(CapabilityFlags::CLIENT_CONNECT_WITH_DB));
164-
assert!(!handshake
251+
assert!(handshake
165252
.capabilities
166253
.contains(CapabilityFlags::CLIENT_DEPRECATE_EOF));
167254
assert_eq!(handshake.collation, UTF8_GENERAL_CI);

0 commit comments

Comments
 (0)