Skip to main content

oracledb_protocol/thin/
auth.rs

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 build_auth_phase_two_payload(
27    user: &str,
28    encrypted: &crate::crypto::EncryptedPassword,
29    driver_name: &str,
30    version_num: u32,
31    connect_string: &str,
32) -> Result<Vec<u8>> {
33    build_auth_phase_two_payload_with_seq(
34        user,
35        encrypted,
36        driver_name,
37        version_num,
38        connect_string,
39        1,
40    )
41}
42
43pub fn build_auth_phase_two_payload_with_seq(
44    user: &str,
45    encrypted: &crate::crypto::EncryptedPassword,
46    driver_name: &str,
47    version_num: u32,
48    connect_string: &str,
49    seq_num: u8,
50) -> Result<Vec<u8>> {
51    build_auth_phase_two_payload_with_context_with_seq(
52        user,
53        encrypted,
54        driver_name,
55        version_num,
56        connect_string,
57        seq_num,
58        &[],
59    )
60}
61
62pub fn build_auth_phase_two_payload_with_context_with_seq(
63    user: &str,
64    encrypted: &crate::crypto::EncryptedPassword,
65    driver_name: &str,
66    version_num: u32,
67    connect_string: &str,
68    seq_num: u8,
69    app_context: &[(String, String, String)],
70) -> Result<Vec<u8>> {
71    build_auth_phase_two_payload_with_proxy_with_seq(
72        user,
73        encrypted,
74        driver_name,
75        version_num,
76        connect_string,
77        seq_num,
78        app_context,
79        None,
80    )
81}
82
83/// Phase-two auth payload with optional proxy authentication: the reference
84/// writes `PROXY_CLIENT_NAME` as the first key/value pair when the connect
85/// user is of the form `user[proxy_user]` (messages/auth.pyx).
86#[allow(clippy::too_many_arguments)]
87pub fn build_auth_phase_two_payload_with_proxy_with_seq(
88    user: &str,
89    encrypted: &crate::crypto::EncryptedPassword,
90    driver_name: &str,
91    version_num: u32,
92    connect_string: &str,
93    seq_num: u8,
94    app_context: &[(String, String, String)],
95    proxy_user: Option<&str>,
96) -> Result<Vec<u8>> {
97    let mut writer = TtcWriter::new();
98    writer.write_function_code_with_seq(TNS_FUNC_AUTH_PHASE_TWO, seq_num);
99    writer.write_ub8(0);
100    let mut num_pairs = 6u32;
101    if encrypted.speedy_key.is_some() {
102        num_pairs += 1;
103    }
104    if proxy_user.is_some() {
105        num_pairs += 1;
106    }
107    if !connect_string.is_empty() {
108        num_pairs += 1;
109    }
110    let app_context_pairs =
111        app_context
112            .len()
113            .checked_mul(3)
114            .ok_or(ProtocolError::InvalidPacketLength {
115                length: app_context.len(),
116                minimum: 0,
117            })?;
118    num_pairs +=
119        u32::try_from(app_context_pairs).map_err(|_| ProtocolError::InvalidPacketLength {
120            length: app_context.len(),
121            minimum: 0,
122        })?;
123    write_auth_header(
124        &mut writer,
125        user,
126        TNS_AUTH_MODE_LOGON | TNS_AUTH_MODE_WITH_PASSWORD,
127        num_pairs,
128    )?;
129    if let Some(proxy_user) = proxy_user {
130        write_key_value(&mut writer, "PROXY_CLIENT_NAME", proxy_user, 0)?;
131    }
132    write_key_value(&mut writer, "AUTH_SESSKEY", &encrypted.session_key, 1)?;
133    if let Some(speedy_key) = &encrypted.speedy_key {
134        write_key_value(&mut writer, "AUTH_PBKDF2_SPEEDY_KEY", speedy_key, 0)?;
135    }
136    write_key_value(&mut writer, "AUTH_PASSWORD", &encrypted.password, 0)?;
137    write_key_value(&mut writer, "SESSION_CLIENT_CHARSET", "873", 0)?;
138    write_key_value(&mut writer, "SESSION_CLIENT_DRIVER_NAME", driver_name, 0)?;
139    write_key_value(
140        &mut writer,
141        "SESSION_CLIENT_VERSION",
142        &version_num.to_string(),
143        0,
144    )?;
145    write_key_value(
146        &mut writer,
147        "AUTH_ALTER_SESSION",
148        "ALTER SESSION SET TIME_ZONE='+00:00'\0",
149        1,
150    )?;
151    for (namespace, name, value) in app_context {
152        write_key_value(&mut writer, "AUTH_APPCTX_NSPACE\0", namespace, 0)?;
153        write_key_value(&mut writer, "AUTH_APPCTX_ATTR\0", name, 0)?;
154        write_key_value(&mut writer, "AUTH_APPCTX_VALUE\0", value, 0)?;
155    }
156    if !connect_string.is_empty() {
157        write_key_value(&mut writer, "AUTH_CONNECT_STRING", connect_string, 0)?;
158    }
159    Ok(writer.into_bytes())
160}
161
162/// Change-password payload: an AUTH_PHASE_TWO message carrying only the
163/// combo-key-encrypted old/new passwords (reference
164/// connection.pyx `_create_change_password_message` + messages/auth.pyx
165/// `_write_message`: auth mode WITH_PASSWORD|CHANGE_PASSWORD, two pairs).
166pub fn build_change_password_payload_with_seq(
167    user: &str,
168    encoded_password: &str,
169    encoded_newpassword: &str,
170    seq_num: u8,
171) -> Result<Vec<u8>> {
172    let mut writer = TtcWriter::new();
173    writer.write_function_code_with_seq(TNS_FUNC_AUTH_PHASE_TWO, seq_num);
174    writer.write_ub8(0);
175    write_auth_header(
176        &mut writer,
177        user,
178        TNS_AUTH_MODE_WITH_PASSWORD | TNS_AUTH_MODE_CHANGE_PASSWORD,
179        2,
180    )?;
181    write_key_value(&mut writer, "AUTH_PASSWORD", encoded_password, 0)?;
182    write_key_value(&mut writer, "AUTH_NEWPASSWORD", encoded_newpassword, 0)?;
183    Ok(writer.into_bytes())
184}
185
186pub fn parse_auth_response(payload: &[u8]) -> Result<AuthResponse> {
187    let mut reader = TtcReader::new(payload);
188    let mut response = AuthResponse::default();
189    while reader.remaining() > 0 {
190        let message_type = reader.read_u8()?;
191        match message_type {
192            TNS_MSG_TYPE_PROTOCOL => {
193                if let Some(capabilities) = skip_protocol_message(&mut reader)? {
194                    response.capabilities = Some(capabilities);
195                }
196            }
197            TNS_MSG_TYPE_DATA_TYPES => skip_data_types_response(&mut reader)?,
198            TNS_MSG_TYPE_PARAMETER => {
199                let mut parsed = parse_return_parameters(&mut reader)?;
200                response.session_data.append(&mut parsed.session_data);
201                if parsed.verifier_type.is_some() {
202                    response.verifier_type = parsed.verifier_type;
203                }
204            }
205            TNS_MSG_TYPE_STATUS => {
206                let _call_status = reader.read_ub4()?;
207                let _seq = reader.read_ub2()?;
208            }
209            TNS_MSG_TYPE_SERVER_SIDE_PIGGYBACK => {
210                let _ = skip_server_side_piggyback(&mut reader)?;
211            }
212            TNS_MSG_TYPE_END_OF_RESPONSE => break,
213            TNS_MSG_TYPE_ERROR => {
214                if let Some(message) = parse_server_error(&mut reader, 13)? {
215                    return Err(ProtocolError::ServerError(message));
216                }
217            }
218            _ => {
219                return Err(ProtocolError::UnknownMessageType {
220                    message_type,
221                    position: reader.position().saturating_sub(1),
222                })
223            }
224        }
225    }
226    Ok(response)
227}
228
229pub(crate) fn write_auth_header(
230    writer: &mut TtcWriter,
231    user: &str,
232    auth_mode: u32,
233    num_pairs: u32,
234) -> Result<()> {
235    let user_bytes = user.as_bytes();
236    writer.write_u8(u8::from(!user_bytes.is_empty()));
237    writer.write_ub4(u32::try_from(user_bytes.len()).map_err(|_| {
238        ProtocolError::InvalidPacketLength {
239            length: user_bytes.len(),
240            minimum: 0,
241        }
242    })?);
243    writer.write_ub4(auth_mode);
244    writer.write_u8(1);
245    writer.write_ub4(num_pairs);
246    writer.write_u8(1);
247    writer.write_u8(1);
248    if !user_bytes.is_empty() {
249        writer.write_bytes_with_length(user_bytes)?;
250    }
251    Ok(())
252}
253
254pub(crate) fn write_key_value(
255    writer: &mut TtcWriter,
256    key: &str,
257    value: &str,
258    flags: u32,
259) -> Result<()> {
260    writer.write_str_two_lengths(key)?;
261    writer.write_str_two_lengths(value)?;
262    writer.write_ub4(flags);
263    Ok(())
264}
265
266pub(crate) fn parse_return_parameters(reader: &mut TtcReader<'_>) -> Result<AuthResponse> {
267    let num_params = reader.read_ub2()?;
268    let mut response = AuthResponse::default();
269    for _ in 0..num_params {
270        let key = reader
271            .read_string_with_length()?
272            .ok_or(ProtocolError::TtcDecode("missing auth response key"))?;
273        let value = reader.read_string_with_length()?.unwrap_or_default();
274        if key == "AUTH_VFR_DATA" {
275            response.verifier_type = Some(reader.read_ub4()?);
276        } else {
277            let _flags = reader.read_ub4()?;
278        }
279        response.session_data.insert(key, value);
280    }
281    Ok(response)
282}