1#![forbid(unsafe_code)]
2
3use super::*;
4
5pub fn append_auth_phase_one(
6 out: &mut Vec<u8>,
7 user: &str,
8 program: &str,
9 machine: &str,
10 osuser: &str,
11 terminal: &str,
12 pid: u32,
13) -> Result<()> {
14 let mut writer = TtcWriter::new();
15 writer.write_function_code(TNS_FUNC_AUTH_PHASE_ONE);
16 write_auth_header(&mut writer, user, TNS_AUTH_MODE_LOGON, 5)?;
17 write_key_value(&mut writer, "AUTH_TERMINAL", terminal, 0)?;
18 write_key_value(&mut writer, "AUTH_PROGRAM_NM", program, 0)?;
19 write_key_value(&mut writer, "AUTH_MACHINE", machine, 0)?;
20 write_key_value(&mut writer, "AUTH_PID", &pid.to_string(), 0)?;
21 write_key_value(&mut writer, "AUTH_SID", osuser, 0)?;
22 out.extend_from_slice(&writer.into_bytes());
23 Ok(())
24}
25
26pub fn append_auth_phase_two_token(
35 out: &mut Vec<u8>,
36 user: &str,
37 token: &str,
38 driver_name: &str,
39 version_num: u32,
40 connect_string: &str,
41) -> Result<()> {
42 let mut writer = TtcWriter::new();
43 writer.write_function_code(TNS_FUNC_AUTH_PHASE_TWO);
44 let mut num_pairs = 5u32;
46 if !connect_string.is_empty() {
47 num_pairs += 1;
48 }
49 write_auth_header(&mut writer, user, TNS_AUTH_MODE_LOGON, num_pairs)?;
50 write_key_value(&mut writer, "AUTH_TOKEN", token, 0)?;
51 write_key_value(&mut writer, "SESSION_CLIENT_CHARSET", "873", 0)?;
52 write_key_value(&mut writer, "SESSION_CLIENT_DRIVER_NAME", driver_name, 0)?;
53 write_key_value(
54 &mut writer,
55 "SESSION_CLIENT_VERSION",
56 &version_num.to_string(),
57 0,
58 )?;
59 write_key_value(
60 &mut writer,
61 "AUTH_ALTER_SESSION",
62 "ALTER SESSION SET TIME_ZONE='+00:00'\0",
63 1,
64 )?;
65 if !connect_string.is_empty() {
66 write_key_value(&mut writer, "AUTH_CONNECT_STRING", connect_string, 0)?;
67 }
68 out.extend_from_slice(&writer.into_bytes());
69 Ok(())
70}
71
72pub fn build_auth_phase_two_payload(
73 user: &str,
74 encrypted: &crate::crypto::EncryptedPassword,
75 driver_name: &str,
76 version_num: u32,
77 connect_string: &str,
78) -> Result<Vec<u8>> {
79 build_auth_phase_two_payload_with_seq(
80 user,
81 encrypted,
82 driver_name,
83 version_num,
84 connect_string,
85 1,
86 )
87}
88
89pub fn build_auth_phase_two_payload_with_seq(
90 user: &str,
91 encrypted: &crate::crypto::EncryptedPassword,
92 driver_name: &str,
93 version_num: u32,
94 connect_string: &str,
95 seq_num: u8,
96) -> Result<Vec<u8>> {
97 build_auth_phase_two_payload_with_context_with_seq(
98 user,
99 encrypted,
100 driver_name,
101 version_num,
102 connect_string,
103 seq_num,
104 &[],
105 )
106}
107
108pub fn build_auth_phase_two_payload_with_context_with_seq(
109 user: &str,
110 encrypted: &crate::crypto::EncryptedPassword,
111 driver_name: &str,
112 version_num: u32,
113 connect_string: &str,
114 seq_num: u8,
115 app_context: &[(String, String, String)],
116) -> Result<Vec<u8>> {
117 build_auth_phase_two_payload_with_proxy_with_seq(
118 user,
119 encrypted,
120 driver_name,
121 version_num,
122 connect_string,
123 seq_num,
124 app_context,
125 None,
126 None,
127 )
128}
129
130#[allow(clippy::too_many_arguments)]
134pub fn build_auth_phase_two_payload_with_proxy_with_seq(
135 user: &str,
136 encrypted: &crate::crypto::EncryptedPassword,
137 driver_name: &str,
138 version_num: u32,
139 connect_string: &str,
140 seq_num: u8,
141 app_context: &[(String, String, String)],
142 proxy_user: Option<&str>,
143 edition: Option<&str>,
144) -> Result<Vec<u8>> {
145 let mut writer = TtcWriter::new();
146 writer.write_function_code_with_seq(TNS_FUNC_AUTH_PHASE_TWO, seq_num);
147 writer.write_ub8(0);
148 let mut num_pairs = 6u32;
149 if encrypted.speedy_key.is_some() {
150 num_pairs += 1;
151 }
152 if proxy_user.is_some() {
153 num_pairs += 1;
154 }
155 if !connect_string.is_empty() {
156 num_pairs += 1;
157 }
158 if edition.is_some() {
159 num_pairs += 1;
160 }
161 let app_context_pairs =
162 app_context
163 .len()
164 .checked_mul(3)
165 .ok_or(ProtocolError::InvalidPacketLength {
166 length: app_context.len(),
167 minimum: 0,
168 })?;
169 num_pairs +=
170 u32::try_from(app_context_pairs).map_err(|_| ProtocolError::InvalidPacketLength {
171 length: app_context.len(),
172 minimum: 0,
173 })?;
174 write_auth_header(
175 &mut writer,
176 user,
177 TNS_AUTH_MODE_LOGON | TNS_AUTH_MODE_WITH_PASSWORD,
178 num_pairs,
179 )?;
180 if let Some(proxy_user) = proxy_user {
181 write_key_value(&mut writer, "PROXY_CLIENT_NAME", proxy_user, 0)?;
182 }
183 write_key_value(&mut writer, "AUTH_SESSKEY", &encrypted.session_key, 1)?;
184 if let Some(speedy_key) = &encrypted.speedy_key {
185 write_key_value(&mut writer, "AUTH_PBKDF2_SPEEDY_KEY", speedy_key, 0)?;
186 }
187 write_key_value(&mut writer, "AUTH_PASSWORD", &encrypted.password, 0)?;
188 write_key_value(&mut writer, "SESSION_CLIENT_CHARSET", "873", 0)?;
189 write_key_value(&mut writer, "SESSION_CLIENT_DRIVER_NAME", driver_name, 0)?;
190 write_key_value(
191 &mut writer,
192 "SESSION_CLIENT_VERSION",
193 &version_num.to_string(),
194 0,
195 )?;
196 write_key_value(
197 &mut writer,
198 "AUTH_ALTER_SESSION",
199 "ALTER SESSION SET TIME_ZONE='+00:00'\0",
200 1,
201 )?;
202 if let Some(edition) = edition {
206 write_key_value(&mut writer, "AUTH_ORA_EDITION", edition, 0)?;
207 }
208 for (namespace, name, value) in app_context {
209 write_key_value(&mut writer, "AUTH_APPCTX_NSPACE\0", namespace, 0)?;
210 write_key_value(&mut writer, "AUTH_APPCTX_ATTR\0", name, 0)?;
211 write_key_value(&mut writer, "AUTH_APPCTX_VALUE\0", value, 0)?;
212 }
213 if !connect_string.is_empty() {
214 write_key_value(&mut writer, "AUTH_CONNECT_STRING", connect_string, 0)?;
215 }
216 Ok(writer.into_bytes())
217}
218
219pub fn build_change_password_payload_with_seq(
224 user: &str,
225 encoded_password: &str,
226 encoded_newpassword: &str,
227 seq_num: u8,
228) -> Result<Vec<u8>> {
229 let mut writer = TtcWriter::new();
230 writer.write_function_code_with_seq(TNS_FUNC_AUTH_PHASE_TWO, seq_num);
231 writer.write_ub8(0);
232 write_auth_header(
233 &mut writer,
234 user,
235 TNS_AUTH_MODE_WITH_PASSWORD | TNS_AUTH_MODE_CHANGE_PASSWORD,
236 2,
237 )?;
238 write_key_value(&mut writer, "AUTH_PASSWORD", encoded_password, 0)?;
239 write_key_value(&mut writer, "AUTH_NEWPASSWORD", encoded_newpassword, 0)?;
240 Ok(writer.into_bytes())
241}
242
243pub fn parse_auth_response(payload: &[u8]) -> Result<AuthResponse> {
244 let mut reader = TtcReader::new(payload);
245 let mut response = AuthResponse::default();
246 while reader.remaining() > 0 {
247 let message_type = reader.read_u8()?;
248 match message_type {
249 TNS_MSG_TYPE_PROTOCOL => {
250 if let Some(capabilities) = skip_protocol_message(&mut reader)? {
251 response.capabilities = Some(capabilities);
252 }
253 }
254 TNS_MSG_TYPE_DATA_TYPES => skip_data_types_response(&mut reader)?,
255 TNS_MSG_TYPE_PARAMETER => {
256 let mut parsed = parse_return_parameters(&mut reader)?;
257 response.session_data.append(&mut parsed.session_data);
258 if parsed.verifier_type.is_some() {
259 response.verifier_type = parsed.verifier_type;
260 }
261 }
262 TNS_MSG_TYPE_STATUS => {
263 let _call_status = reader.read_ub4()?;
264 let _seq = reader.read_ub2()?;
265 }
266 TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
267 let _ = skip_server_side_piggyback(&mut reader)?;
268 }
269 TNS_MSG_TYPE_END_OF_RESPONSE => break,
270 TNS_MSG_TYPE_ERROR => {
271 if let Some(message) = parse_server_error(&mut reader, 13)? {
272 return Err(ProtocolError::ServerError(message));
273 }
274 }
275 _ => {
276 return Err(ProtocolError::UnknownMessageType {
277 message_type,
278 position: reader.position().saturating_sub(1),
279 })
280 }
281 }
282 }
283 Ok(response)
284}
285
286pub(crate) fn write_auth_header(
287 writer: &mut TtcWriter,
288 user: &str,
289 auth_mode: u32,
290 num_pairs: u32,
291) -> Result<()> {
292 let user_bytes = user.as_bytes();
293 writer.write_u8(u8::from(!user_bytes.is_empty()));
294 writer.write_ub4(u32::try_from(user_bytes.len()).map_err(|_| {
295 ProtocolError::InvalidPacketLength {
296 length: user_bytes.len(),
297 minimum: 0,
298 }
299 })?);
300 writer.write_ub4(auth_mode);
301 writer.write_u8(1);
302 writer.write_ub4(num_pairs);
303 writer.write_u8(1);
304 writer.write_u8(1);
305 if !user_bytes.is_empty() {
306 writer.write_bytes_with_length(user_bytes)?;
307 }
308 Ok(())
309}
310
311pub(crate) fn write_key_value(
312 writer: &mut TtcWriter,
313 key: &str,
314 value: &str,
315 flags: u32,
316) -> Result<()> {
317 writer.write_str_two_lengths(key)?;
318 writer.write_str_two_lengths(value)?;
319 writer.write_ub4(flags);
320 Ok(())
321}
322
323pub(crate) fn parse_return_parameters(reader: &mut TtcReader<'_>) -> Result<AuthResponse> {
324 let num_params = reader.read_ub2()?;
325 let mut response = AuthResponse::default();
326 for _ in 0..num_params {
327 let key = reader
328 .read_string_with_length()?
329 .ok_or(ProtocolError::TtcDecode("missing auth response key"))?;
330 let value = reader.read_string_with_length()?.unwrap_or_default();
331 if key == "AUTH_VFR_DATA" {
332 response.verifier_type = Some(reader.read_ub4()?);
333 } else {
334 let _flags = reader.read_ub4()?;
335 }
336 response.session_data.insert(key, value);
337 }
338 Ok(response)
339}
340
341#[cfg(test)]
342mod token_auth_tests {
343 use super::*;
344
345 fn read_ub4(bytes: &[u8], pos: &mut usize) -> u32 {
347 let len = bytes[*pos] as usize;
348 *pos += 1;
349 let mut value = 0u32;
350 for _ in 0..len {
351 value = (value << 8) | u32::from(bytes[*pos]);
352 *pos += 1;
353 }
354 value
355 }
356
357 fn contains(haystack: &[u8], needle: &[u8]) -> bool {
358 haystack.windows(needle.len()).any(|w| w == needle)
359 }
360
361 #[test]
366 fn token_message_carries_auth_token_not_password() {
367 let mut out = Vec::new();
368 append_auth_phase_two_token(
369 &mut out,
370 "scott",
371 "HEADER.PAYLOAD.SIG",
372 "drv",
373 300_000_000,
374 "cs",
375 )
376 .unwrap();
377
378 assert_eq!(out[0], TNS_MSG_TYPE_FUNCTION);
380 assert_eq!(out[1], TNS_FUNC_AUTH_PHASE_TWO);
381 assert_eq!(out[3], 1, "user is present");
383 let mut pos = 4;
384 assert_eq!(read_ub4(&out, &mut pos), 5, "user length = len(\"scott\")");
385 assert_eq!(
386 read_ub4(&out, &mut pos),
387 TNS_AUTH_MODE_LOGON,
388 "token auth uses LOGON only — never the WITH_PASSWORD bit"
389 );
390 assert_eq!(out[pos], 1); pos += 1;
392 assert_eq!(
393 read_ub4(&out, &mut pos),
394 6,
395 "AUTH_TOKEN + 4 session pairs + AUTH_CONNECT_STRING"
396 );
397
398 assert!(contains(&out, b"AUTH_TOKEN"));
399 assert!(
400 contains(&out, b"HEADER.PAYLOAD.SIG"),
401 "the token value is sent"
402 );
403 assert!(contains(&out, b"AUTH_CONNECT_STRING"));
404 assert!(
405 !contains(&out, b"AUTH_PASSWORD") && !contains(&out, b"AUTH_SESSKEY"),
406 "token auth must not send any password material"
407 );
408 }
409
410 #[test]
413 fn token_message_pair_count_without_connect_string() {
414 let mut out = Vec::new();
415 append_auth_phase_two_token(&mut out, "u", "tok", "drv", 1, "").unwrap();
416 let mut pos = 4;
417 let _user_len = read_ub4(&out, &mut pos);
418 let _auth_mode = read_ub4(&out, &mut pos);
419 pos += 1; assert_eq!(read_ub4(&out, &mut pos), 5, "AUTH_TOKEN + 4 session pairs");
421 assert!(!contains(&out, b"AUTH_CONNECT_STRING"));
422 }
423}