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