1use smb_server_proto::types::{FileTime, Status};
4use smb_server_proto_smb1::consts::{self, caps};
5use smb_server_proto_smb1::header::RespBody;
6use smb_server_proto_smb1::negotiate;
7use smb_server_proto_smb1::session_setup as ss;
8use smb_server_proto_smb1::tree_connect as tc;
9
10use crate::auth::authenticate_ntlmssp;
11use crate::dispatch::{IoCtx, IoContext, ReqView};
12use crate::state::{next_tid, next_uid, Session};
13
14fn crate_ext_sec() -> u32 {
15 smb_server_proto_smb1::consts::caps::CAP_EXTENDED_SECURITY
16}
17
18pub fn our_caps() -> u32 {
20 caps::CAP_UNICODE
21 | caps::CAP_LARGE_FILES
22 | caps::CAP_NT_SMBS
23 | caps::CAP_STATUS32
24 | caps::CAP_LEVEL2_OPLOCKS
25 | caps::CAP_LOCK_AND_READ
26 | caps::CAP_NT_FIND
27 | caps::CAP_INFOLEVEL_PASSTHRU
28 | caps::CAP_LARGE_READX
29 | caps::CAP_LARGE_WRITEX
30}
31
32pub fn negotiate(
34 io: &IoCtx<'_>,
35 req: &ReqView,
36 bodies: &mut Vec<RespBody>,
37) -> Result<Status, Status> {
38 let parsed = negotiate::NegotiateReq::parse(req.data);
39 let idx = parsed.select().ok_or(Status::INVALID_PARAMETER)?;
40 let body = RespBody::new(
43 consts::COM_NEGOTIATE,
44 negotiate::build_params(idx as u16, our_caps() | crate_ext_sec(), FileTime::now()),
45 io.server.guid.to_vec(),
46 );
47 *bodies = vec![body];
48 Ok(Status::SUCCESS)
49}
50
51#[cfg_attr(dylint_lib = "no_magic_numbers", allow(no_magic_numbers))] pub fn setup(
54 io: &mut IoContext,
55 req: &ReqView,
56 bodies: &mut Vec<RespBody>,
57) -> Result<Status, Status> {
58 let conn = &mut io.conn;
59 let wct = req.wct;
60 let ext = wct == 12;
62 let (blob, _legacy) = if ext {
63 let e = ss::SessionSetupExtReq::parse(req.words, req.data)
64 .map_err(|_| Status::LOGON_FAILURE)?;
65 (e.blob, None)
66 } else {
67 let l = ss::SessionSetupLegacyReq::parse(
68 req.words,
69 req.data,
70 req.unicode(),
71 req.bc_off_abs + 2,
72 )
73 .map_err(|_| Status::LOGON_FAILURE)?;
74 (l.nt_resp.clone(), Some(l))
75 };
76
77 let parsed_nt = blob.clone();
78 let inner = smb_server_auth::ntlm::unwrap_blob(&parsed_nt).unwrap_or(&[]);
79 match smb_server_auth::ntlm::msg_type(inner) {
80 Some(smb_server_auth::ntlm::MSG_TYPE1) | Some(smb_server_auth::ntlm::MSG_TYPE2) | None => {
81 let uid = if conn.uid != 0 { conn.uid } else { next_uid() };
83 conn.uid = uid;
84 conn.auth_pending = true;
85 conn.spnego = smb_server_auth::ntlm::is_spnego(&parsed_nt);
86
87 let t2 = smb_server_auth::ntlm::build_type2(
88 &conn.challenge,
89 &io.server.domain,
90 &io.server.server_name,
91 );
92 let out_blob = if conn.spnego {
93 smb_server_auth::ntlm::wrap_negtoken_targ(&t2)
94 } else {
95 t2.clone()
96 };
97 let (params, bytes) = ss::build_session_setup_response(0, &out_blob);
98 let mut b = RespBody::new(consts::COM_SESSION_SETUP_ANDX, params, bytes);
99 b.uid_override = Some(uid);
100 *bodies = vec![b];
101 Ok(Status::MORE_PROCESSING_REQUIRED)
102 }
103 Some(smb_server_auth::ntlm::MSG_TYPE3) => {
104 let t3 = smb_server_auth::ntlm::parse_type3(inner).ok_or(Status::LOGON_FAILURE)?;
106 let _ = &_legacy;
107 let out =
108 authenticate_ntlmssp(&io.server.users, io.server.allow_guest, &conn.challenge, &t3);
109 if !out.ok {
110 return Err(Status::LOGON_FAILURE);
111 }
112 let uid = if conn.uid != 0 { conn.uid } else { next_uid() };
113 conn.uid = uid;
114 conn.auth_pending = false;
115 conn.session = Some(Session {
116 user: out.user.clone(),
117 guest: out.guest,
118 trees: Vec::new(),
119 });
120
121 let action: u16 = if out.guest { 0x0001 } else { 0x0000 };
122 let fin = if conn.spnego {
123 smb_server_auth::ntlm::wrap_accept_complete()
124 } else {
125 Vec::new()
126 };
127 let (params, mut bytes) = ss::build_session_setup_response(action, &fin);
128 bytes.truncate(fin.len());
131 let mut b = RespBody::new(consts::COM_SESSION_SETUP_ANDX, params, bytes);
132 b.uid_override = Some(uid);
133 *bodies = vec![b];
134 Ok(Status::SUCCESS)
135 }
136 _ => Err(Status::LOGON_FAILURE),
137 }
138}
139
140#[cfg_attr(dylint_lib = "no_magic_numbers", allow(no_magic_numbers))] pub fn tree_connect(
143 io: &mut IoContext,
144 req: &ReqView,
145 bodies: &mut Vec<RespBody>,
146) -> Result<Status, Status> {
147 let tc = tc::TreeConnectReq::parse(
148 req.words,
149 req.data,
150 req.unicode(),
151 req.bc_off_abs + 2,
152 )
153 .map_err(|_| Status::INVALID_PARAMETER)?;
154
155 let name = tc
156 .path
157 .rsplit(['\\', '/'])
158 .find(|s| !s.is_empty())
159 .unwrap_or("")
160 .to_lowercase();
161 let ipc = name == "ipc$" || tc.service.starts_with("IPC");
162 if !ipc && !io.server.shares.contains_key(&name) {
163 return Err(Status::BAD_NETWORK_NAME);
164 }
165 let tid = next_tid();
166 io.conn.trees.insert(tid, name.clone());
167 if let Some(sess) = io.conn.session.as_mut() {
168 sess.trees.push(tid);
169 }
170
171 let svc = if ipc { "IPC" } else { "A:" };
172 let fs = if ipc { "" } else { "NTFS" };
173 let (params, mut bytes) = tc::build_response(0x0001, svc, fs, req.unicode(), 43);
174 if fs.is_empty() && unicode_tail_ok(&bytes) {
175 bytes.truncate(bytes.len().saturating_sub(if req.unicode() { 2 } else { 1 }));
176 }
177 let mut b = RespBody::new(consts::COM_TREE_CONNECT_ANDX, params, bytes);
178 b.tid_override = Some(tid);
179 *bodies = vec![b];
180 Ok(Status::SUCCESS)
181}
182
183fn unicode_tail_ok(_bytes: &[u8]) -> bool {
184 true
185}
186
187#[cfg_attr(dylint_lib = "no_magic_numbers", allow(no_magic_numbers))] pub fn echo(req: &ReqView, bodies: &mut Vec<RespBody>) -> Result<Status, Status> {
190 let count = if req.words.len() >= 2 {
191 u16::from_le_bytes([req.words[0], req.words[1]]).max(1)
192 } else {
193 1
194 };
195 bodies.clear();
196 for seq in 0..count {
197 bodies.push(ss_echo_body(seq, req.data));
198 }
199 Ok(Status::SUCCESS)
200}
201
202#[cfg_attr(dylint_lib = "no_magic_numbers", allow(no_magic_numbers))] fn ss_echo_body(seq: u16, data: &[u8]) -> RespBody {
204 let mut params = Vec::with_capacity(4);
205 params.extend_from_slice(&seq.to_le_bytes());
206 params.extend_from_slice(&0u16.to_le_bytes());
207 RespBody::new(consts::COM_ECHO, params, data.to_vec())
208}