]>
Commit | Line | Data |
---|---|---|
c3effa8b | 1 | /* |
cd68afe3 | 2 | Unix SMB/CIFS implementation. |
c3effa8b AT |
3 | process incoming packets - main loop |
4 | Copyright (C) Andrew Tridgell 1992-1998 | |
b91704d4 | 5 | Copyright (C) Volker Lendecke 2005-2007 |
8af7400d | 6 | |
c3effa8b AT |
7 | This program is free software; you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by | |
d824b98f | 9 | the Free Software Foundation; either version 3 of the License, or |
c3effa8b | 10 | (at your option) any later version. |
8af7400d | 11 | |
c3effa8b AT |
12 | This program is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
8af7400d | 16 | |
c3effa8b | 17 | You should have received a copy of the GNU General Public License |
5e54558c | 18 | along with this program. If not, see <http://www.gnu.org/licenses/>. |
c3effa8b AT |
19 | */ |
20 | ||
21 | #include "includes.h" | |
12476223 | 22 | #include "../lib/tsocket/tsocket.h" |
0e771263 | 23 | #include "system/filesys.h" |
8c24ebf3 | 24 | #include "smbd/smbd.h" |
3dde0cbb | 25 | #include "smbd/globals.h" |
965fec35 | 26 | #include "source3/smbd/smbXsrv_session.h" |
ca8afc66 | 27 | #include "smbd/smbXsrv_open.h" |
aff002e8 | 28 | #include "librpc/gen_ndr/netlogon.h" |
c7fe04ab | 29 | #include "../lib/async_req/async_sock.h" |
8e16d6db | 30 | #include "ctdbd_conn.h" |
b38d0542 | 31 | #include "../lib/util/select.h" |
2e8a85ec | 32 | #include "printing/queue_process.h" |
9758afd4 | 33 | #include "system/select.h" |
235f1485 | 34 | #include "passdb.h" |
af300a9f | 35 | #include "auth.h" |
b2af281e | 36 | #include "messages.h" |
af63c0b3 | 37 | #include "lib/messages_ctdb.h" |
165521e2 | 38 | #include "smbprofile.h" |
c233c214 | 39 | #include "rpc_server/spoolss/srv_spoolss_nt.h" |
27afb891 | 40 | #include "../lib/util/tevent_ntstatus.h" |
715933a3 SM |
41 | #include "../libcli/security/dom_sid.h" |
42 | #include "../libcli/security/security_token.h" | |
43 | #include "lib/id_cache.h" | |
258ce91f | 44 | #include "lib/util/sys_rw_data.h" |
f01af728 | 45 | #include "system/threads.h" |
74590c67 | 46 | #include "lib/pthreadpool/pthreadpool_tevent.h" |
d7cfb12b | 47 | #include "util_event.h" |
3eee4394 | 48 | #include "libcli/smb/smbXcli_base.h" |
303c4829 | 49 | #include "lib/util/time_basic.h" |
25043ebb | 50 | #include "source3/lib/substitute.h" |
62cc0bba | 51 | #include "lib/util/util_process.h" |
c3effa8b | 52 | |
6d84b24d SM |
53 | /* Internal message queue for deferred opens. */ |
54 | struct pending_message_list { | |
55 | struct pending_message_list *next, *prev; | |
56 | struct timeval request_time; /* When was this first issued? */ | |
57 | struct smbd_server_connection *sconn; | |
b87ec64c | 58 | struct smbXsrv_connection *xconn; |
ae1cb5ca | 59 | struct tevent_timer *te; |
6d84b24d SM |
60 | uint32_t seqnum; |
61 | bool encrypted; | |
62 | bool processed; | |
63 | DATA_BLOB buf; | |
0ead434b | 64 | struct deferred_open_record *open_rec; |
6d84b24d SM |
65 | }; |
66 | ||
3b2c9beb | 67 | static bool smb_splice_chain(uint8_t **poutbuf, const uint8_t *andx_buf); |
5a33e906 | 68 | |
6f792afe | 69 | void smbd_echo_init(struct smbXsrv_connection *xconn) |
8de85546 | 70 | { |
b0fe6c5c SM |
71 | xconn->smb1.echo_handler.trusted_fd = -1; |
72 | xconn->smb1.echo_handler.socket_lock_fd = -1; | |
f01af728 | 73 | #ifdef HAVE_ROBUST_MUTEXES |
b0fe6c5c | 74 | xconn->smb1.echo_handler.socket_mutex = NULL; |
f01af728 CS |
75 | #endif |
76 | } | |
77 | ||
b0fe6c5c | 78 | static bool smbd_echo_active(struct smbXsrv_connection *xconn) |
f01af728 | 79 | { |
b0fe6c5c | 80 | if (xconn->smb1.echo_handler.socket_lock_fd != -1) { |
f01af728 CS |
81 | return true; |
82 | } | |
83 | ||
84 | #ifdef HAVE_ROBUST_MUTEXES | |
b0fe6c5c | 85 | if (xconn->smb1.echo_handler.socket_mutex != NULL) { |
f01af728 CS |
86 | return true; |
87 | } | |
88 | #endif | |
cad0c004 | 89 | |
f01af728 CS |
90 | return false; |
91 | } | |
92 | ||
6905bb6c | 93 | static bool smbd_lock_socket_internal(struct smbXsrv_connection *xconn) |
f01af728 | 94 | { |
b0fe6c5c | 95 | if (!smbd_echo_active(xconn)) { |
cad0c004 VL |
96 | return true; |
97 | } | |
98 | ||
b0fe6c5c | 99 | xconn->smb1.echo_handler.ref_count++; |
6800fdbb | 100 | |
b0fe6c5c | 101 | if (xconn->smb1.echo_handler.ref_count > 1) { |
6800fdbb JA |
102 | return true; |
103 | } | |
104 | ||
c0288e06 | 105 | DEBUG(10,("pid[%d] wait for socket lock\n", (int)getpid())); |
cad0c004 | 106 | |
f01af728 | 107 | #ifdef HAVE_ROBUST_MUTEXES |
b0fe6c5c | 108 | if (xconn->smb1.echo_handler.socket_mutex != NULL) { |
f01af728 | 109 | int ret = EINTR; |
da00021a | 110 | |
f01af728 CS |
111 | while (ret == EINTR) { |
112 | ret = pthread_mutex_lock( | |
b0fe6c5c | 113 | xconn->smb1.echo_handler.socket_mutex); |
f01af728 CS |
114 | if (ret == 0) { |
115 | break; | |
116 | } | |
117 | } | |
118 | if (ret != 0) { | |
119 | DEBUG(1, ("pthread_mutex_lock failed: %s\n", | |
120 | strerror(ret))); | |
121 | return false; | |
122 | } | |
123 | } | |
124 | #endif | |
125 | ||
b0fe6c5c | 126 | if (xconn->smb1.echo_handler.socket_lock_fd != -1) { |
f01af728 CS |
127 | bool ok; |
128 | ||
129 | do { | |
130 | ok = fcntl_lock( | |
b0fe6c5c | 131 | xconn->smb1.echo_handler.socket_lock_fd, |
f01af728 CS |
132 | F_SETLKW, 0, 0, F_WRLCK); |
133 | } while (!ok && (errno == EINTR)); | |
134 | ||
135 | if (!ok) { | |
136 | DEBUG(1, ("fcntl_lock failed: %s\n", strerror(errno))); | |
137 | return false; | |
138 | } | |
cad0c004 VL |
139 | } |
140 | ||
7b0b1d6d | 141 | DEBUG(10,("pid[%d] got socket lock\n", (int)getpid())); |
cad0c004 | 142 | |
8de85546 SM |
143 | return true; |
144 | } | |
145 | ||
bb8e6d45 | 146 | void smbd_lock_socket(struct smbXsrv_connection *xconn) |
6800fdbb | 147 | { |
6905bb6c | 148 | if (!smbd_lock_socket_internal(xconn)) { |
6800fdbb JA |
149 | exit_server_cleanly("failed to lock socket"); |
150 | } | |
151 | } | |
152 | ||
6905bb6c | 153 | static bool smbd_unlock_socket_internal(struct smbXsrv_connection *xconn) |
8de85546 | 154 | { |
b0fe6c5c | 155 | if (!smbd_echo_active(xconn)) { |
cad0c004 VL |
156 | return true; |
157 | } | |
158 | ||
b0fe6c5c | 159 | xconn->smb1.echo_handler.ref_count--; |
6800fdbb | 160 | |
b0fe6c5c | 161 | if (xconn->smb1.echo_handler.ref_count > 0) { |
6800fdbb JA |
162 | return true; |
163 | } | |
164 | ||
f01af728 | 165 | #ifdef HAVE_ROBUST_MUTEXES |
b0fe6c5c | 166 | if (xconn->smb1.echo_handler.socket_mutex != NULL) { |
d40891a1 VL |
167 | int ret; |
168 | ret = pthread_mutex_unlock( | |
169 | xconn->smb1.echo_handler.socket_mutex); | |
f01af728 CS |
170 | if (ret != 0) { |
171 | DEBUG(1, ("pthread_mutex_unlock failed: %s\n", | |
172 | strerror(ret))); | |
173 | return false; | |
174 | } | |
175 | } | |
176 | #endif | |
177 | ||
b0fe6c5c | 178 | if (xconn->smb1.echo_handler.socket_lock_fd != -1) { |
f01af728 CS |
179 | bool ok; |
180 | ||
181 | do { | |
182 | ok = fcntl_lock( | |
b0fe6c5c | 183 | xconn->smb1.echo_handler.socket_lock_fd, |
f01af728 CS |
184 | F_SETLKW, 0, 0, F_UNLCK); |
185 | } while (!ok && (errno == EINTR)); | |
186 | ||
187 | if (!ok) { | |
188 | DEBUG(1, ("fcntl_lock failed: %s\n", strerror(errno))); | |
189 | return false; | |
190 | } | |
cad0c004 VL |
191 | } |
192 | ||
c0288e06 | 193 | DEBUG(10,("pid[%d] unlocked socket\n", (int)getpid())); |
cad0c004 | 194 | |
8de85546 SM |
195 | return true; |
196 | } | |
197 | ||
bb8e6d45 | 198 | void smbd_unlock_socket(struct smbXsrv_connection *xconn) |
6800fdbb | 199 | { |
6905bb6c | 200 | if (!smbd_unlock_socket_internal(xconn)) { |
6800fdbb JA |
201 | exit_server_cleanly("failed to unlock socket"); |
202 | } | |
203 | } | |
204 | ||
36441da4 JA |
205 | /* Accessor function for smb_read_error for smbd functions. */ |
206 | ||
9254bb4e JA |
207 | /**************************************************************************** |
208 | Send an smb to a fd. | |
209 | ****************************************************************************/ | |
210 | ||
12f1d94a VL |
211 | bool smb1_srv_send(struct smbXsrv_connection *xconn, |
212 | char *buffer, | |
213 | bool do_signing, | |
214 | uint32_t seqnum, | |
215 | bool do_encrypt) | |
9254bb4e | 216 | { |
54c51a66 | 217 | size_t len = 0; |
9254bb4e JA |
218 | ssize_t ret; |
219 | char *buf_out = buffer; | |
977aa660 | 220 | |
b05b4cab | 221 | if (!NT_STATUS_IS_OK(xconn->transport.status)) { |
52ccb40d SM |
222 | /* |
223 | * we're not supposed to do any io | |
224 | */ | |
225 | return true; | |
226 | } | |
227 | ||
bb8e6d45 | 228 | smbd_lock_socket(xconn); |
9254bb4e | 229 | |
c16c90a1 | 230 | if (do_signing) { |
2772c92e AS |
231 | NTSTATUS status; |
232 | ||
c16c90a1 | 233 | /* Sign the outgoing packet if required. */ |
fa9c48ae | 234 | status = smb1_srv_calculate_sign_mac(xconn, buf_out, seqnum); |
2772c92e AS |
235 | if (!NT_STATUS_IS_OK(status)) { |
236 | DBG_ERR("Failed to calculate signing mac: %s\n", | |
237 | nt_errstr(status)); | |
238 | return false; | |
239 | } | |
c16c90a1 | 240 | } |
9254bb4e JA |
241 | |
242 | if (do_encrypt) { | |
68fb12cb | 243 | NTSTATUS status = srv_encrypt_buffer(xconn, buffer, &buf_out); |
9254bb4e JA |
244 | if (!NT_STATUS_IS_OK(status)) { |
245 | DEBUG(0, ("send_smb: SMB encryption failed " | |
246 | "on outgoing packet! Error %s\n", | |
247 | nt_errstr(status) )); | |
cc983c9a | 248 | ret = -1; |
54c51a66 | 249 | goto out; |
9254bb4e JA |
250 | } |
251 | } | |
252 | ||
d5693d99 | 253 | len = smb_len_large(buf_out) + 4; |
9254bb4e | 254 | |
0ccffffe | 255 | ret = write_data(xconn->transport.sock, buf_out, len); |
c344ad30 | 256 | if (ret <= 0) { |
2fd53228 | 257 | int saved_errno = errno; |
40ae8b74 VL |
258 | /* |
259 | * Try and give an error message saying what | |
260 | * client failed. | |
261 | */ | |
51bc104c | 262 | DEBUG(1,("pid[%d] Error writing %d bytes to client %s. %d. (%s)\n", |
c0288e06 | 263 | (int)getpid(), (int)len, |
2fd53228 SM |
264 | smbXsrv_connection_dbg(xconn), |
265 | (int)ret, strerror(saved_errno))); | |
266 | errno = saved_errno; | |
40ae8b74 | 267 | |
245e3959 | 268 | srv_free_enc_buffer(xconn, buf_out); |
c344ad30 | 269 | goto out; |
9254bb4e JA |
270 | } |
271 | ||
245e3959 | 272 | srv_free_enc_buffer(xconn, buf_out); |
54c51a66 | 273 | out: |
bb8e6d45 | 274 | smbd_unlock_socket(xconn); |
852c9ac3 | 275 | return (ret > 0); |
9254bb4e JA |
276 | } |
277 | ||
695c4a7a JA |
278 | /* Socket functions for smbd packet processing. */ |
279 | ||
58fbb512 | 280 | static bool valid_packet_size(size_t len) |
695c4a7a JA |
281 | { |
282 | /* | |
283 | * A WRITEX with CAP_LARGE_WRITEX can be 64k worth of data plus 65 bytes | |
284 | * of header. Don't print the error if this fits.... JRA. | |
285 | */ | |
286 | ||
032621d5 | 287 | if (len > (LARGE_WRITEX_BUFFER_SIZE + LARGE_WRITEX_HDR_SIZE)) { |
695c4a7a JA |
288 | DEBUG(0,("Invalid packet length! (%lu bytes).\n", |
289 | (unsigned long)len)); | |
d36434f3 | 290 | return false; |
695c4a7a JA |
291 | } |
292 | return true; | |
293 | } | |
294 | ||
695c4a7a JA |
295 | /**************************************************************************** |
296 | Attempt a zerocopy writeX read. We know here that len > smb_size-4 | |
297 | ****************************************************************************/ | |
298 | ||
299 | /* | |
300 | * Unfortunately, earlier versions of smbclient/libsmbclient | |
301 | * don't send this "standard" writeX header. I've fixed this | |
302 | * for 3.2 but we'll use the old method with earlier versions. | |
303 | * Windows and CIFSFS at least use this standard size. Not | |
304 | * sure about MacOSX. | |
305 | */ | |
306 | ||
307 | #define STANDARD_WRITE_AND_X_HEADER_SIZE (smb_size - 4 + /* basic header */ \ | |
308 | (2*14) + /* word count (including bcc) */ \ | |
309 | 1 /* pad byte */) | |
310 | ||
250b2b64 VL |
311 | static NTSTATUS receive_smb_raw_talloc_partial_read(TALLOC_CTX *mem_ctx, |
312 | const char lenbuf[4], | |
e3ab0a05 | 313 | struct smbXsrv_connection *xconn, |
8b5d163d | 314 | int sock, |
fa0de395 | 315 | char **buffer, |
250b2b64 VL |
316 | unsigned int timeout, |
317 | size_t *p_unread, | |
318 | size_t *len_ret) | |
695c4a7a JA |
319 | { |
320 | /* Size of a WRITEX call (+4 byte len). */ | |
321 | char writeX_header[4 + STANDARD_WRITE_AND_X_HEADER_SIZE]; | |
322 | ssize_t len = smb_len_large(lenbuf); /* Could be a UNIX large writeX. */ | |
323 | ssize_t toread; | |
250b2b64 | 324 | NTSTATUS status; |
695c4a7a | 325 | |
227718cd | 326 | memcpy(writeX_header, lenbuf, 4); |
695c4a7a | 327 | |
43c766a1 | 328 | status = read_fd_with_timeout( |
8b5d163d | 329 | sock, writeX_header + 4, |
250b2b64 VL |
330 | STANDARD_WRITE_AND_X_HEADER_SIZE, |
331 | STANDARD_WRITE_AND_X_HEADER_SIZE, | |
332 | timeout, NULL); | |
695c4a7a | 333 | |
250b2b64 | 334 | if (!NT_STATUS_IS_OK(status)) { |
9671547d | 335 | DEBUG(0, ("read_fd_with_timeout failed for client %s read " |
a513086c | 336 | "error = %s.\n", |
93f7e625 | 337 | smbXsrv_connection_dbg(xconn), |
9671547d | 338 | nt_errstr(status))); |
250b2b64 | 339 | return status; |
695c4a7a JA |
340 | } |
341 | ||
342 | /* | |
343 | * Ok - now try and see if this is a possible | |
344 | * valid writeX call. | |
345 | */ | |
346 | ||
8c457da2 | 347 | if (is_valid_writeX_buffer(xconn, (uint8_t *)writeX_header)) { |
695c4a7a JA |
348 | /* |
349 | * If the data offset is beyond what | |
350 | * we've read, drain the extra bytes. | |
351 | */ | |
352 | uint16_t doff = SVAL(writeX_header,smb_vwv11); | |
353 | ssize_t newlen; | |
354 | ||
355 | if (doff > STANDARD_WRITE_AND_X_HEADER_SIZE) { | |
356 | size_t drain = doff - STANDARD_WRITE_AND_X_HEADER_SIZE; | |
8b5d163d | 357 | if (drain_socket(sock, drain) != drain) { |
695c4a7a JA |
358 | smb_panic("receive_smb_raw_talloc_partial_read:" |
359 | " failed to drain pending bytes"); | |
360 | } | |
361 | } else { | |
362 | doff = STANDARD_WRITE_AND_X_HEADER_SIZE; | |
363 | } | |
364 | ||
365 | /* Spoof down the length and null out the bcc. */ | |
366 | set_message_bcc(writeX_header, 0); | |
367 | newlen = smb_len(writeX_header); | |
368 | ||
369 | /* Copy the header we've written. */ | |
370 | ||
8d4a8389 | 371 | *buffer = (char *)talloc_memdup(mem_ctx, |
695c4a7a JA |
372 | writeX_header, |
373 | sizeof(writeX_header)); | |
374 | ||
375 | if (*buffer == NULL) { | |
376 | DEBUG(0, ("Could not allocate inbuf of length %d\n", | |
377 | (int)sizeof(writeX_header))); | |
250b2b64 | 378 | return NT_STATUS_NO_MEMORY; |
695c4a7a JA |
379 | } |
380 | ||
381 | /* Work out the remaining bytes. */ | |
382 | *p_unread = len - STANDARD_WRITE_AND_X_HEADER_SIZE; | |
250b2b64 VL |
383 | *len_ret = newlen + 4; |
384 | return NT_STATUS_OK; | |
695c4a7a JA |
385 | } |
386 | ||
387 | if (!valid_packet_size(len)) { | |
250b2b64 | 388 | return NT_STATUS_INVALID_PARAMETER; |
695c4a7a JA |
389 | } |
390 | ||
391 | /* | |
392 | * Not a valid writeX call. Just do the standard | |
393 | * talloc and return. | |
394 | */ | |
395 | ||
3d151376 | 396 | *buffer = talloc_array(mem_ctx, char, len+4); |
695c4a7a JA |
397 | |
398 | if (*buffer == NULL) { | |
399 | DEBUG(0, ("Could not allocate inbuf of length %d\n", | |
400 | (int)len+4)); | |
250b2b64 | 401 | return NT_STATUS_NO_MEMORY; |
695c4a7a JA |
402 | } |
403 | ||
404 | /* Copy in what we already read. */ | |
405 | memcpy(*buffer, | |
406 | writeX_header, | |
407 | 4 + STANDARD_WRITE_AND_X_HEADER_SIZE); | |
408 | toread = len - STANDARD_WRITE_AND_X_HEADER_SIZE; | |
409 | ||
410 | if(toread > 0) { | |
e604e137 | 411 | status = read_packet_remainder( |
8b5d163d | 412 | sock, |
fa0de395 | 413 | (*buffer) + 4 + STANDARD_WRITE_AND_X_HEADER_SIZE, |
e604e137 VL |
414 | timeout, toread); |
415 | ||
416 | if (!NT_STATUS_IS_OK(status)) { | |
8ca459e0 JA |
417 | DEBUG(10, ("receive_smb_raw_talloc_partial_read: %s\n", |
418 | nt_errstr(status))); | |
250b2b64 | 419 | return status; |
695c4a7a JA |
420 | } |
421 | } | |
422 | ||
250b2b64 VL |
423 | *len_ret = len + 4; |
424 | return NT_STATUS_OK; | |
695c4a7a JA |
425 | } |
426 | ||
a2db154c | 427 | static NTSTATUS receive_smb_raw_talloc(TALLOC_CTX *mem_ctx, |
e3ab0a05 | 428 | struct smbXsrv_connection *xconn, |
8b5d163d | 429 | int sock, |
9fe66ddd VL |
430 | char **buffer, unsigned int timeout, |
431 | size_t *p_unread, size_t *plen) | |
695c4a7a JA |
432 | { |
433 | char lenbuf[4]; | |
0afbfa42 | 434 | size_t len; |
695c4a7a | 435 | int min_recv_size = lp_min_receive_file_size(); |
0afbfa42 | 436 | NTSTATUS status; |
695c4a7a | 437 | |
695c4a7a JA |
438 | *p_unread = 0; |
439 | ||
8b5d163d | 440 | status = read_smb_length_return_keepalive(sock, lenbuf, timeout, |
a2db154c | 441 | &len); |
0afbfa42 | 442 | if (!NT_STATUS_IS_OK(status)) { |
9fe66ddd | 443 | return status; |
695c4a7a JA |
444 | } |
445 | ||
dea223ba TP |
446 | if (CVAL(lenbuf,0) == 0 && min_recv_size && |
447 | (smb_len_large(lenbuf) > /* Could be a UNIX large writeX. */ | |
448 | (min_recv_size + STANDARD_WRITE_AND_X_HEADER_SIZE)) && | |
e0ad956c | 449 | !smb1_srv_is_signing_active(xconn) && |
b0fe6c5c | 450 | xconn->smb1.echo_handler.trusted_fde == NULL) { |
695c4a7a | 451 | |
8ca459e0 | 452 | return receive_smb_raw_talloc_partial_read( |
e3ab0a05 | 453 | mem_ctx, lenbuf, xconn, sock, buffer, timeout, |
fa0de395 | 454 | p_unread, plen); |
695c4a7a JA |
455 | } |
456 | ||
457 | if (!valid_packet_size(len)) { | |
9fe66ddd | 458 | return NT_STATUS_INVALID_PARAMETER; |
695c4a7a JA |
459 | } |
460 | ||
461 | /* | |
462 | * The +4 here can't wrap, we've checked the length above already. | |
463 | */ | |
464 | ||
3d151376 | 465 | *buffer = talloc_array(mem_ctx, char, len+4); |
695c4a7a JA |
466 | |
467 | if (*buffer == NULL) { | |
468 | DEBUG(0, ("Could not allocate inbuf of length %d\n", | |
469 | (int)len+4)); | |
9fe66ddd | 470 | return NT_STATUS_NO_MEMORY; |
695c4a7a JA |
471 | } |
472 | ||
473 | memcpy(*buffer, lenbuf, sizeof(lenbuf)); | |
474 | ||
8b5d163d | 475 | status = read_packet_remainder(sock, (*buffer)+4, timeout, len); |
e604e137 | 476 | if (!NT_STATUS_IS_OK(status)) { |
9fe66ddd | 477 | return status; |
695c4a7a JA |
478 | } |
479 | ||
9fe66ddd VL |
480 | *plen = len + 4; |
481 | return NT_STATUS_OK; | |
695c4a7a JA |
482 | } |
483 | ||
7e55512a DM |
484 | NTSTATUS smb1_receive_talloc(TALLOC_CTX *mem_ctx, |
485 | struct smbXsrv_connection *xconn, | |
486 | int sock, | |
487 | char **buffer, unsigned int timeout, | |
488 | size_t *p_unread, bool *p_encrypted, | |
489 | size_t *p_len, | |
490 | uint32_t *seqnum, | |
491 | bool trusted_channel) | |
695c4a7a | 492 | { |
47666c93 | 493 | size_t len = 0; |
9fe66ddd | 494 | NTSTATUS status; |
695c4a7a | 495 | |
9254bb4e JA |
496 | *p_encrypted = false; |
497 | ||
e3ab0a05 | 498 | status = receive_smb_raw_talloc(mem_ctx, xconn, sock, buffer, timeout, |
63e08ef8 | 499 | p_unread, &len); |
9fe66ddd | 500 | if (!NT_STATUS_IS_OK(status)) { |
b99becd4 CA |
501 | DEBUG(NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)?5:1, |
502 | ("receive_smb_raw_talloc failed for client %s " | |
503 | "read error = %s.\n", | |
9fe66659 | 504 | smbXsrv_connection_dbg(xconn), |
b99becd4 | 505 | nt_errstr(status)) ); |
e514cd0a | 506 | return status; |
695c4a7a JA |
507 | } |
508 | ||
07605559 | 509 | if (is_encrypted_packet((uint8_t *)*buffer)) { |
245e3959 | 510 | status = srv_decrypt_buffer(xconn, *buffer); |
afc93255 JA |
511 | if (!NT_STATUS_IS_OK(status)) { |
512 | DEBUG(0, ("receive_smb_talloc: SMB decryption failed on " | |
513 | "incoming packet! Error %s\n", | |
514 | nt_errstr(status) )); | |
e514cd0a | 515 | return status; |
afc93255 | 516 | } |
9254bb4e | 517 | *p_encrypted = true; |
afc93255 JA |
518 | } |
519 | ||
695c4a7a | 520 | /* Check the incoming SMB signature. */ |
777fbb37 | 521 | if (!smb1_srv_check_sign_mac(xconn, *buffer, seqnum, trusted_channel)) { |
695c4a7a JA |
522 | DEBUG(0, ("receive_smb: SMB Signature verification failed on " |
523 | "incoming packet!\n")); | |
e514cd0a | 524 | return NT_STATUS_INVALID_NETWORK_RESPONSE; |
695c4a7a JA |
525 | } |
526 | ||
e514cd0a VL |
527 | *p_len = len; |
528 | return NT_STATUS_OK; | |
695c4a7a JA |
529 | } |
530 | ||
aab2fe02 | 531 | /**************************************************************************** |
e5a95132 GJC |
532 | Function to push a message onto the tail of a linked list of smb messages ready |
533 | for processing. | |
aab2fe02 JA |
534 | ****************************************************************************/ |
535 | ||
30191d1a | 536 | static bool push_queued_message(struct smb_request *req, |
54abd2aa GC |
537 | struct timeval request_time, |
538 | struct timeval end_time, | |
0ead434b | 539 | struct deferred_open_record *open_rec) |
aab2fe02 | 540 | { |
b578db69 | 541 | int msg_len = smb_len(req->inbuf) + 4; |
54abd2aa GC |
542 | struct pending_message_list *msg; |
543 | ||
ad0a07c5 | 544 | msg = talloc_zero(NULL, struct pending_message_list); |
aab2fe02 | 545 | |
1eff0523 JA |
546 | if(msg == NULL) { |
547 | DEBUG(0,("push_message: malloc fail (1)\n")); | |
548 | return False; | |
549 | } | |
8b2b7d1c | 550 | msg->sconn = req->sconn; |
b87ec64c | 551 | msg->xconn = req->xconn; |
aab2fe02 | 552 | |
b578db69 | 553 | msg->buf = data_blob_talloc(msg, req->inbuf, msg_len); |
2fc57c9a | 554 | if(msg->buf.data == NULL) { |
1eff0523 | 555 | DEBUG(0,("push_message: malloc fail (2)\n")); |
fb5362c0 | 556 | TALLOC_FREE(msg); |
1eff0523 JA |
557 | return False; |
558 | } | |
aab2fe02 | 559 | |
54abd2aa | 560 | msg->request_time = request_time; |
c16c90a1 | 561 | msg->seqnum = req->seqnum; |
9254bb4e | 562 | msg->encrypted = req->encrypted; |
8a6b90d4 | 563 | msg->processed = false; |
2fc57c9a | 564 | |
0ead434b VL |
565 | if (open_rec) { |
566 | msg->open_rec = talloc_move(msg, &open_rec); | |
2fc57c9a | 567 | } |
aab2fe02 | 568 | |
4e437616 | 569 | #if 0 |
16cfc724 SM |
570 | msg->te = tevent_add_timer(msg->sconn->ev_ctx, |
571 | msg, | |
572 | end_time, | |
573 | smbd_deferred_open_timer, | |
574 | msg); | |
aeb798c3 SM |
575 | if (!msg->te) { |
576 | DEBUG(0,("push_message: event_add_timed failed\n")); | |
577 | TALLOC_FREE(msg); | |
578 | return false; | |
579 | } | |
4e437616 | 580 | #endif |
aeb798c3 | 581 | |
476672b6 | 582 | DLIST_ADD_END(req->sconn->deferred_open_queue, msg); |
aab2fe02 | 583 | |
54abd2aa GC |
584 | DEBUG(10,("push_message: pushed message length %u on " |
585 | "deferred_open_queue\n", (unsigned int)msg_len)); | |
2fc57c9a | 586 | |
1eff0523 | 587 | return True; |
aab2fe02 JA |
588 | } |
589 | ||
2fc57c9a | 590 | /**************************************************************************** |
54abd2aa GC |
591 | Function to push a deferred open smb message onto a linked list of local smb |
592 | messages ready for processing. | |
593 | ****************************************************************************/ | |
594 | ||
5582077b DM |
595 | bool push_deferred_open_message_smb1(struct smb_request *req, |
596 | struct timeval timeout, | |
597 | struct file_id id, | |
598 | struct deferred_open_record *open_rec) | |
54abd2aa | 599 | { |
303c4829 | 600 | struct timeval_buf tvbuf; |
54abd2aa GC |
601 | struct timeval end_time; |
602 | ||
c3250149 | 603 | if (req->unread_bytes) { |
e15939b4 | 604 | DEBUG(0,("push_deferred_open_message_smb: logic error ! " |
c3250149 JA |
605 | "unread_bytes = %u\n", |
606 | (unsigned int)req->unread_bytes )); | |
e15939b4 | 607 | smb_panic("push_deferred_open_message_smb: " |
c3250149 JA |
608 | "logic error unread_bytes != 0" ); |
609 | } | |
610 | ||
bb81b9a7 | 611 | end_time = timeval_sum(&req->request_time, &timeout); |
54abd2aa | 612 | |
303c4829 VL |
613 | DBG_DEBUG("pushing message len %u mid %"PRIu64" timeout time [%s]\n", |
614 | (unsigned int) smb_len(req->inbuf)+4, | |
615 | req->mid, | |
616 | timeval_str_buf(&end_time, false, true, &tvbuf)); | |
54abd2aa | 617 | |
bb81b9a7 | 618 | return push_queued_message(req, req->request_time, end_time, open_rec); |
54abd2aa GC |
619 | } |
620 | ||
22dbd677 JA |
621 | /* |
622 | * Only allow 5 outstanding trans requests. We're allocating memory, so | |
623 | * prevent a DoS. | |
624 | */ | |
aab2fe02 | 625 | |
79842437 | 626 | NTSTATUS allow_new_trans(struct trans_state *list, uint64_t mid) |
c3effa8b | 627 | { |
22dbd677 JA |
628 | int count = 0; |
629 | for (; list != NULL; list = list->next) { | |
c3effa8b | 630 | |
22dbd677 JA |
631 | if (list->mid == mid) { |
632 | return NT_STATUS_INVALID_PARAMETER; | |
633 | } | |
634 | ||
635 | count += 1; | |
636 | } | |
637 | if (count > 5) { | |
638 | return NT_STATUS_INSUFFICIENT_RESOURCES; | |
639 | } | |
c3effa8b | 640 | |
22dbd677 | 641 | return NT_STATUS_OK; |
c3effa8b AT |
642 | } |
643 | ||
c3effa8b AT |
644 | /* |
645 | These flags determine some of the permissions required to do an operation | |
646 | ||
647 | Note that I don't set NEED_WRITE on some write operations because they | |
648 | are used by some brain-dead clients when printing, and I don't want to | |
649 | force write permissions on print services. | |
650 | */ | |
651 | #define AS_USER (1<<0) | |
ce61fb21 | 652 | #define NEED_WRITE (1<<1) /* Must be paired with AS_USER */ |
c3effa8b | 653 | #define TIME_INIT (1<<2) |
ce61fb21 JA |
654 | #define CAN_IPC (1<<3) /* Must be paired with AS_USER */ |
655 | #define AS_GUEST (1<<5) /* Must *NOT* be paired with AS_USER */ | |
54abd2aa | 656 | #define DO_CHDIR (1<<6) |
c3effa8b AT |
657 | |
658 | /* | |
659 | define a list of possible SMB messages and their corresponding | |
660 | functions. Any message that has a NULL function is unimplemented - | |
661 | please feel free to contribute implementations! | |
662 | */ | |
1eff0523 JA |
663 | static const struct smb_message_struct { |
664 | const char *name; | |
48d3a1d2 | 665 | void (*fn)(struct smb_request *req); |
1eff0523 JA |
666 | int flags; |
667 | } smb_messages[256] = { | |
918c3ebe | 668 | |
b578db69 VL |
669 | /* 0x00 */ { "SMBmkdir",reply_mkdir,AS_USER | NEED_WRITE}, |
670 | /* 0x01 */ { "SMBrmdir",reply_rmdir,AS_USER | NEED_WRITE}, | |
671 | /* 0x02 */ { "SMBopen",reply_open,AS_USER }, | |
672 | /* 0x03 */ { "SMBcreate",reply_mknew,AS_USER}, | |
673 | /* 0x04 */ { "SMBclose",reply_close,AS_USER | CAN_IPC }, | |
674 | /* 0x05 */ { "SMBflush",reply_flush,AS_USER}, | |
675 | /* 0x06 */ { "SMBunlink",reply_unlink,AS_USER | NEED_WRITE }, | |
676 | /* 0x07 */ { "SMBmv",reply_mv,AS_USER | NEED_WRITE }, | |
677 | /* 0x08 */ { "SMBgetatr",reply_getatr,AS_USER}, | |
678 | /* 0x09 */ { "SMBsetatr",reply_setatr,AS_USER | NEED_WRITE}, | |
679 | /* 0x0a */ { "SMBread",reply_read,AS_USER}, | |
680 | /* 0x0b */ { "SMBwrite",reply_write,AS_USER | CAN_IPC }, | |
681 | /* 0x0c */ { "SMBlock",reply_lock,AS_USER}, | |
682 | /* 0x0d */ { "SMBunlock",reply_unlock,AS_USER}, | |
683 | /* 0x0e */ { "SMBctemp",reply_ctemp,AS_USER }, | |
684 | /* 0x0f */ { "SMBmknew",reply_mknew,AS_USER}, | |
685 | /* 0x10 */ { "SMBcheckpath",reply_checkpath,AS_USER}, | |
686 | /* 0x11 */ { "SMBexit",reply_exit,DO_CHDIR}, | |
687 | /* 0x12 */ { "SMBlseek",reply_lseek,AS_USER}, | |
688 | /* 0x13 */ { "SMBlockread",reply_lockread,AS_USER}, | |
689 | /* 0x14 */ { "SMBwriteunlock",reply_writeunlock,AS_USER}, | |
690 | /* 0x15 */ { NULL, NULL, 0 }, | |
691 | /* 0x16 */ { NULL, NULL, 0 }, | |
692 | /* 0x17 */ { NULL, NULL, 0 }, | |
693 | /* 0x18 */ { NULL, NULL, 0 }, | |
694 | /* 0x19 */ { NULL, NULL, 0 }, | |
695 | /* 0x1a */ { "SMBreadbraw",reply_readbraw,AS_USER}, | |
696 | /* 0x1b */ { "SMBreadBmpx",reply_readbmpx,AS_USER}, | |
697 | /* 0x1c */ { "SMBreadBs",reply_readbs,AS_USER }, | |
698 | /* 0x1d */ { "SMBwritebraw",reply_writebraw,AS_USER}, | |
699 | /* 0x1e */ { "SMBwriteBmpx",reply_writebmpx,AS_USER}, | |
700 | /* 0x1f */ { "SMBwriteBs",reply_writebs,AS_USER}, | |
701 | /* 0x20 */ { "SMBwritec", NULL,0}, | |
702 | /* 0x21 */ { NULL, NULL, 0 }, | |
703 | /* 0x22 */ { "SMBsetattrE",reply_setattrE,AS_USER | NEED_WRITE }, | |
704 | /* 0x23 */ { "SMBgetattrE",reply_getattrE,AS_USER }, | |
705 | /* 0x24 */ { "SMBlockingX",reply_lockingX,AS_USER }, | |
706 | /* 0x25 */ { "SMBtrans",reply_trans,AS_USER | CAN_IPC }, | |
707 | /* 0x26 */ { "SMBtranss",reply_transs,AS_USER | CAN_IPC}, | |
708 | /* 0x27 */ { "SMBioctl",reply_ioctl,0}, | |
709 | /* 0x28 */ { "SMBioctls", NULL,AS_USER}, | |
710 | /* 0x29 */ { "SMBcopy",reply_copy,AS_USER | NEED_WRITE }, | |
711 | /* 0x2a */ { "SMBmove", NULL,AS_USER | NEED_WRITE }, | |
712 | /* 0x2b */ { "SMBecho",reply_echo,0}, | |
713 | /* 0x2c */ { "SMBwriteclose",reply_writeclose,AS_USER}, | |
714 | /* 0x2d */ { "SMBopenX",reply_open_and_X,AS_USER | CAN_IPC }, | |
715 | /* 0x2e */ { "SMBreadX",reply_read_and_X,AS_USER | CAN_IPC }, | |
716 | /* 0x2f */ { "SMBwriteX",reply_write_and_X,AS_USER | CAN_IPC }, | |
717 | /* 0x30 */ { NULL, NULL, 0 }, | |
718 | /* 0x31 */ { NULL, NULL, 0 }, | |
719 | /* 0x32 */ { "SMBtrans2",reply_trans2, AS_USER | CAN_IPC }, | |
7716ad68 | 720 | /* 0x33 */ { "SMBtranss2",reply_transs2, AS_USER | CAN_IPC }, |
b578db69 VL |
721 | /* 0x34 */ { "SMBfindclose",reply_findclose,AS_USER}, |
722 | /* 0x35 */ { "SMBfindnclose",reply_findnclose,AS_USER}, | |
723 | /* 0x36 */ { NULL, NULL, 0 }, | |
724 | /* 0x37 */ { NULL, NULL, 0 }, | |
725 | /* 0x38 */ { NULL, NULL, 0 }, | |
726 | /* 0x39 */ { NULL, NULL, 0 }, | |
727 | /* 0x3a */ { NULL, NULL, 0 }, | |
728 | /* 0x3b */ { NULL, NULL, 0 }, | |
729 | /* 0x3c */ { NULL, NULL, 0 }, | |
730 | /* 0x3d */ { NULL, NULL, 0 }, | |
731 | /* 0x3e */ { NULL, NULL, 0 }, | |
732 | /* 0x3f */ { NULL, NULL, 0 }, | |
733 | /* 0x40 */ { NULL, NULL, 0 }, | |
734 | /* 0x41 */ { NULL, NULL, 0 }, | |
735 | /* 0x42 */ { NULL, NULL, 0 }, | |
736 | /* 0x43 */ { NULL, NULL, 0 }, | |
737 | /* 0x44 */ { NULL, NULL, 0 }, | |
738 | /* 0x45 */ { NULL, NULL, 0 }, | |
739 | /* 0x46 */ { NULL, NULL, 0 }, | |
740 | /* 0x47 */ { NULL, NULL, 0 }, | |
741 | /* 0x48 */ { NULL, NULL, 0 }, | |
742 | /* 0x49 */ { NULL, NULL, 0 }, | |
743 | /* 0x4a */ { NULL, NULL, 0 }, | |
744 | /* 0x4b */ { NULL, NULL, 0 }, | |
745 | /* 0x4c */ { NULL, NULL, 0 }, | |
746 | /* 0x4d */ { NULL, NULL, 0 }, | |
747 | /* 0x4e */ { NULL, NULL, 0 }, | |
748 | /* 0x4f */ { NULL, NULL, 0 }, | |
749 | /* 0x50 */ { NULL, NULL, 0 }, | |
750 | /* 0x51 */ { NULL, NULL, 0 }, | |
751 | /* 0x52 */ { NULL, NULL, 0 }, | |
752 | /* 0x53 */ { NULL, NULL, 0 }, | |
753 | /* 0x54 */ { NULL, NULL, 0 }, | |
754 | /* 0x55 */ { NULL, NULL, 0 }, | |
755 | /* 0x56 */ { NULL, NULL, 0 }, | |
756 | /* 0x57 */ { NULL, NULL, 0 }, | |
757 | /* 0x58 */ { NULL, NULL, 0 }, | |
758 | /* 0x59 */ { NULL, NULL, 0 }, | |
759 | /* 0x5a */ { NULL, NULL, 0 }, | |
760 | /* 0x5b */ { NULL, NULL, 0 }, | |
761 | /* 0x5c */ { NULL, NULL, 0 }, | |
762 | /* 0x5d */ { NULL, NULL, 0 }, | |
763 | /* 0x5e */ { NULL, NULL, 0 }, | |
764 | /* 0x5f */ { NULL, NULL, 0 }, | |
765 | /* 0x60 */ { NULL, NULL, 0 }, | |
766 | /* 0x61 */ { NULL, NULL, 0 }, | |
767 | /* 0x62 */ { NULL, NULL, 0 }, | |
768 | /* 0x63 */ { NULL, NULL, 0 }, | |
769 | /* 0x64 */ { NULL, NULL, 0 }, | |
770 | /* 0x65 */ { NULL, NULL, 0 }, | |
771 | /* 0x66 */ { NULL, NULL, 0 }, | |
772 | /* 0x67 */ { NULL, NULL, 0 }, | |
773 | /* 0x68 */ { NULL, NULL, 0 }, | |
774 | /* 0x69 */ { NULL, NULL, 0 }, | |
775 | /* 0x6a */ { NULL, NULL, 0 }, | |
776 | /* 0x6b */ { NULL, NULL, 0 }, | |
777 | /* 0x6c */ { NULL, NULL, 0 }, | |
778 | /* 0x6d */ { NULL, NULL, 0 }, | |
779 | /* 0x6e */ { NULL, NULL, 0 }, | |
780 | /* 0x6f */ { NULL, NULL, 0 }, | |
781 | /* 0x70 */ { "SMBtcon",reply_tcon,0}, | |
782 | /* 0x71 */ { "SMBtdis",reply_tdis,DO_CHDIR}, | |
783 | /* 0x72 */ { "SMBnegprot",reply_negprot,0}, | |
784 | /* 0x73 */ { "SMBsesssetupX",reply_sesssetup_and_X,0}, | |
785 | /* 0x74 */ { "SMBulogoffX",reply_ulogoffX, 0}, /* ulogoff doesn't give a valid TID */ | |
786 | /* 0x75 */ { "SMBtconX",reply_tcon_and_X,0}, | |
787 | /* 0x76 */ { NULL, NULL, 0 }, | |
788 | /* 0x77 */ { NULL, NULL, 0 }, | |
789 | /* 0x78 */ { NULL, NULL, 0 }, | |
790 | /* 0x79 */ { NULL, NULL, 0 }, | |
791 | /* 0x7a */ { NULL, NULL, 0 }, | |
792 | /* 0x7b */ { NULL, NULL, 0 }, | |
793 | /* 0x7c */ { NULL, NULL, 0 }, | |
794 | /* 0x7d */ { NULL, NULL, 0 }, | |
795 | /* 0x7e */ { NULL, NULL, 0 }, | |
796 | /* 0x7f */ { NULL, NULL, 0 }, | |
797 | /* 0x80 */ { "SMBdskattr",reply_dskattr,AS_USER}, | |
798 | /* 0x81 */ { "SMBsearch",reply_search,AS_USER}, | |
799 | /* 0x82 */ { "SMBffirst",reply_search,AS_USER}, | |
800 | /* 0x83 */ { "SMBfunique",reply_search,AS_USER}, | |
801 | /* 0x84 */ { "SMBfclose",reply_fclose,AS_USER}, | |
802 | /* 0x85 */ { NULL, NULL, 0 }, | |
803 | /* 0x86 */ { NULL, NULL, 0 }, | |
804 | /* 0x87 */ { NULL, NULL, 0 }, | |
805 | /* 0x88 */ { NULL, NULL, 0 }, | |
806 | /* 0x89 */ { NULL, NULL, 0 }, | |
807 | /* 0x8a */ { NULL, NULL, 0 }, | |
808 | /* 0x8b */ { NULL, NULL, 0 }, | |
809 | /* 0x8c */ { NULL, NULL, 0 }, | |
810 | /* 0x8d */ { NULL, NULL, 0 }, | |
811 | /* 0x8e */ { NULL, NULL, 0 }, | |
812 | /* 0x8f */ { NULL, NULL, 0 }, | |
813 | /* 0x90 */ { NULL, NULL, 0 }, | |
814 | /* 0x91 */ { NULL, NULL, 0 }, | |
815 | /* 0x92 */ { NULL, NULL, 0 }, | |
816 | /* 0x93 */ { NULL, NULL, 0 }, | |
817 | /* 0x94 */ { NULL, NULL, 0 }, | |
818 | /* 0x95 */ { NULL, NULL, 0 }, | |
819 | /* 0x96 */ { NULL, NULL, 0 }, | |
820 | /* 0x97 */ { NULL, NULL, 0 }, | |
821 | /* 0x98 */ { NULL, NULL, 0 }, | |
822 | /* 0x99 */ { NULL, NULL, 0 }, | |
823 | /* 0x9a */ { NULL, NULL, 0 }, | |
824 | /* 0x9b */ { NULL, NULL, 0 }, | |
825 | /* 0x9c */ { NULL, NULL, 0 }, | |
826 | /* 0x9d */ { NULL, NULL, 0 }, | |
827 | /* 0x9e */ { NULL, NULL, 0 }, | |
828 | /* 0x9f */ { NULL, NULL, 0 }, | |
829 | /* 0xa0 */ { "SMBnttrans",reply_nttrans, AS_USER | CAN_IPC }, | |
830 | /* 0xa1 */ { "SMBnttranss",reply_nttranss, AS_USER | CAN_IPC }, | |
831 | /* 0xa2 */ { "SMBntcreateX",reply_ntcreate_and_X, AS_USER | CAN_IPC }, | |
832 | /* 0xa3 */ { NULL, NULL, 0 }, | |
833 | /* 0xa4 */ { "SMBntcancel",reply_ntcancel, 0 }, | |
834 | /* 0xa5 */ { "SMBntrename",reply_ntrename, AS_USER | NEED_WRITE }, | |
835 | /* 0xa6 */ { NULL, NULL, 0 }, | |
836 | /* 0xa7 */ { NULL, NULL, 0 }, | |
837 | /* 0xa8 */ { NULL, NULL, 0 }, | |
838 | /* 0xa9 */ { NULL, NULL, 0 }, | |
839 | /* 0xaa */ { NULL, NULL, 0 }, | |
840 | /* 0xab */ { NULL, NULL, 0 }, | |
841 | /* 0xac */ { NULL, NULL, 0 }, | |
842 | /* 0xad */ { NULL, NULL, 0 }, | |
843 | /* 0xae */ { NULL, NULL, 0 }, | |
844 | /* 0xaf */ { NULL, NULL, 0 }, | |
845 | /* 0xb0 */ { NULL, NULL, 0 }, | |
846 | /* 0xb1 */ { NULL, NULL, 0 }, | |
847 | /* 0xb2 */ { NULL, NULL, 0 }, | |
848 | /* 0xb3 */ { NULL, NULL, 0 }, | |
849 | /* 0xb4 */ { NULL, NULL, 0 }, | |
850 | /* 0xb5 */ { NULL, NULL, 0 }, | |
851 | /* 0xb6 */ { NULL, NULL, 0 }, | |
852 | /* 0xb7 */ { NULL, NULL, 0 }, | |
853 | /* 0xb8 */ { NULL, NULL, 0 }, | |
854 | /* 0xb9 */ { NULL, NULL, 0 }, | |
855 | /* 0xba */ { NULL, NULL, 0 }, | |
856 | /* 0xbb */ { NULL, NULL, 0 }, | |
857 | /* 0xbc */ { NULL, NULL, 0 }, | |
858 | /* 0xbd */ { NULL, NULL, 0 }, | |
859 | /* 0xbe */ { NULL, NULL, 0 }, | |
860 | /* 0xbf */ { NULL, NULL, 0 }, | |
861 | /* 0xc0 */ { "SMBsplopen",reply_printopen,AS_USER}, | |
862 | /* 0xc1 */ { "SMBsplwr",reply_printwrite,AS_USER}, | |
863 | /* 0xc2 */ { "SMBsplclose",reply_printclose,AS_USER}, | |
864 | /* 0xc3 */ { "SMBsplretq",reply_printqueue,AS_USER}, | |
865 | /* 0xc4 */ { NULL, NULL, 0 }, | |
866 | /* 0xc5 */ { NULL, NULL, 0 }, | |
867 | /* 0xc6 */ { NULL, NULL, 0 }, | |
868 | /* 0xc7 */ { NULL, NULL, 0 }, | |
869 | /* 0xc8 */ { NULL, NULL, 0 }, | |
870 | /* 0xc9 */ { NULL, NULL, 0 }, | |
871 | /* 0xca */ { NULL, NULL, 0 }, | |
872 | /* 0xcb */ { NULL, NULL, 0 }, | |
873 | /* 0xcc */ { NULL, NULL, 0 }, | |
874 | /* 0xcd */ { NULL, NULL, 0 }, | |
875 | /* 0xce */ { NULL, NULL, 0 }, | |
876 | /* 0xcf */ { NULL, NULL, 0 }, | |
877 | /* 0xd0 */ { "SMBsends",reply_sends,AS_GUEST}, | |
878 | /* 0xd1 */ { "SMBsendb", NULL,AS_GUEST}, | |
879 | /* 0xd2 */ { "SMBfwdname", NULL,AS_GUEST}, | |
880 | /* 0xd3 */ { "SMBcancelf", NULL,AS_GUEST}, | |
881 | /* 0xd4 */ { "SMBgetmac", NULL,AS_GUEST}, | |
882 | /* 0xd5 */ { "SMBsendstrt",reply_sendstrt,AS_GUEST}, | |
883 | /* 0xd6 */ { "SMBsendend",reply_sendend,AS_GUEST}, | |
884 | /* 0xd7 */ { "SMBsendtxt",reply_sendtxt,AS_GUEST}, | |
885 | /* 0xd8 */ { NULL, NULL, 0 }, | |
886 | /* 0xd9 */ { NULL, NULL, 0 }, | |
887 | /* 0xda */ { NULL, NULL, 0 }, | |
888 | /* 0xdb */ { NULL, NULL, 0 }, | |
889 | /* 0xdc */ { NULL, NULL, 0 }, | |
890 | /* 0xdd */ { NULL, NULL, 0 }, | |
891 | /* 0xde */ { NULL, NULL, 0 }, | |
892 | /* 0xdf */ { NULL, NULL, 0 }, | |
893 | /* 0xe0 */ { NULL, NULL, 0 }, | |
894 | /* 0xe1 */ { NULL, NULL, 0 }, | |
895 | /* 0xe2 */ { NULL, NULL, 0 }, | |
896 | /* 0xe3 */ { NULL, NULL, 0 }, | |
897 | /* 0xe4 */ { NULL, NULL, 0 }, | |
898 | /* 0xe5 */ { NULL, NULL, 0 }, | |
899 | /* 0xe6 */ { NULL, NULL, 0 }, | |
900 | /* 0xe7 */ { NULL, NULL, 0 }, | |
901 | /* 0xe8 */ { NULL, NULL, 0 }, | |
902 | /* 0xe9 */ { NULL, NULL, 0 }, | |
903 | /* 0xea */ { NULL, NULL, 0 }, | |
904 | /* 0xeb */ { NULL, NULL, 0 }, | |
905 | /* 0xec */ { NULL, NULL, 0 }, | |
906 | /* 0xed */ { NULL, NULL, 0 }, | |
907 | /* 0xee */ { NULL, NULL, 0 }, | |
908 | /* 0xef */ { NULL, NULL, 0 }, | |
909 | /* 0xf0 */ { NULL, NULL, 0 }, | |
910 | /* 0xf1 */ { NULL, NULL, 0 }, | |
911 | /* 0xf2 */ { NULL, NULL, 0 }, | |
912 | /* 0xf3 */ { NULL, NULL, 0 }, | |
913 | /* 0xf4 */ { NULL, NULL, 0 }, | |
914 | /* 0xf5 */ { NULL, NULL, 0 }, | |
915 | /* 0xf6 */ { NULL, NULL, 0 }, | |
916 | /* 0xf7 */ { NULL, NULL, 0 }, | |
917 | /* 0xf8 */ { NULL, NULL, 0 }, | |
918 | /* 0xf9 */ { NULL, NULL, 0 }, | |
919 | /* 0xfa */ { NULL, NULL, 0 }, | |
920 | /* 0xfb */ { NULL, NULL, 0 }, | |
921 | /* 0xfc */ { NULL, NULL, 0 }, | |
922 | /* 0xfd */ { NULL, NULL, 0 }, | |
923 | /* 0xfe */ { NULL, NULL, 0 }, | |
924 | /* 0xff */ { NULL, NULL, 0 } | |
918c3ebe JA |
925 | |
926 | }; | |
c3effa8b | 927 | |
cc6a4101 | 928 | |
712a30ed | 929 | /******************************************************************* |
a834a73e GC |
930 | Dump a packet to a file. |
931 | ********************************************************************/ | |
932 | ||
c99d2455 | 933 | static void smb_dump(const char *name, int type, const char *data) |
712a30ed | 934 | { |
c99d2455 | 935 | size_t len; |
712a30ed | 936 | int fd, i; |
d068bc64 JA |
937 | char *fname = NULL; |
938 | if (DEBUGLEVEL < 50) { | |
939 | return; | |
940 | } | |
712a30ed | 941 | |
c99d2455 | 942 | len = smb_len_tcp(data)+4; |
712a30ed | 943 | for (i=1;i<100;i++) { |
86a80cf4 JA |
944 | fname = talloc_asprintf(talloc_tos(), |
945 | "/tmp/%s.%d.%s", | |
946 | name, | |
947 | i, | |
948 | type ? "req" : "resp"); | |
949 | if (fname == NULL) { | |
d068bc64 JA |
950 | return; |
951 | } | |
712a30ed LL |
952 | fd = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0644); |
953 | if (fd != -1 || errno != EEXIST) break; | |
86a80cf4 | 954 | TALLOC_FREE(fname); |
712a30ed LL |
955 | } |
956 | if (fd != -1) { | |
e400bfce JA |
957 | ssize_t ret = write(fd, data, len); |
958 | if (ret != len) | |
959 | DEBUG(0,("smb_dump: problem: write returned %d\n", (int)ret )); | |
712a30ed | 960 | close(fd); |
9c8d23e5 | 961 | DEBUG(0,("created %s len %lu\n", fname, (unsigned long)len)); |
712a30ed | 962 | } |
86a80cf4 | 963 | TALLOC_FREE(fname); |
712a30ed LL |
964 | } |
965 | ||
b74bef8f RB |
966 | static void smb1srv_update_crypto_flags(struct smbXsrv_session *session, |
967 | struct smb_request *req, | |
968 | uint8_t type, | |
969 | bool *update_session_globalp, | |
970 | bool *update_tcon_globalp) | |
971 | { | |
972 | connection_struct *conn = req->conn; | |
973 | struct smbXsrv_tcon *tcon = conn ? conn->tcon : NULL; | |
974 | uint8_t encrypt_flag = SMBXSRV_PROCESSED_UNENCRYPTED_PACKET; | |
975 | uint8_t sign_flag = SMBXSRV_PROCESSED_UNSIGNED_PACKET; | |
976 | bool update_session = false; | |
977 | bool update_tcon = false; | |
978 | ||
979 | if (req->encrypted) { | |
980 | encrypt_flag = SMBXSRV_PROCESSED_ENCRYPTED_PACKET; | |
981 | } | |
982 | ||
e0ad956c | 983 | if (smb1_srv_is_signing_active(req->xconn)) { |
b74bef8f RB |
984 | sign_flag = SMBXSRV_PROCESSED_SIGNED_PACKET; |
985 | } else if ((type == SMBecho) || (type == SMBsesssetupX)) { | |
986 | /* | |
7077ae40 | 987 | * echo can be unsigned. Session setup except final |
b74bef8f RB |
988 | * session setup response too |
989 | */ | |
990 | sign_flag &= ~SMBXSRV_PROCESSED_UNSIGNED_PACKET; | |
991 | } | |
992 | ||
993 | update_session |= smbXsrv_set_crypto_flag( | |
994 | &session->global->encryption_flags, encrypt_flag); | |
995 | update_session |= smbXsrv_set_crypto_flag( | |
996 | &session->global->signing_flags, sign_flag); | |
997 | ||
998 | if (tcon) { | |
999 | update_tcon |= smbXsrv_set_crypto_flag( | |
1000 | &tcon->global->encryption_flags, encrypt_flag); | |
1001 | update_tcon |= smbXsrv_set_crypto_flag( | |
1002 | &tcon->global->signing_flags, sign_flag); | |
1003 | } | |
1004 | ||
1005 | if (update_session) { | |
1006 | session->global->channels[0].encryption_cipher = SMB_ENCRYPTION_GSSAPI; | |
1007 | } | |
1008 | ||
1009 | *update_session_globalp = update_session; | |
1010 | *update_tcon_globalp = update_tcon; | |
1011 | return; | |
1012 | } | |
1013 | ||
49fdf8f9 VL |
1014 | static void set_current_case_sensitive(connection_struct *conn, uint16_t flags) |
1015 | { | |
1016 | int snum; | |
1017 | enum remote_arch_types ra_type; | |
1018 | ||
1019 | SMB_ASSERT(conn != NULL); | |
80cd127b | 1020 | SMB_ASSERT(!conn_using_smb2(conn->sconn)); |
49fdf8f9 VL |
1021 | |
1022 | snum = SNUM(conn); | |
1023 | ||
49fdf8f9 VL |
1024 | /* |
1025 | * Obey the client case sensitivity requests - only for clients that | |
1026 | * support it. */ | |
1027 | switch (lp_case_sensitive(snum)) { | |
1028 | case Auto: | |
1029 | /* | |
7077ae40 | 1030 | * We need this ugliness due to DOS/Win9x clients that lie |
49fdf8f9 VL |
1031 | * about case insensitivity. */ |
1032 | ra_type = get_remote_arch(); | |
d4848111 | 1033 | if ((ra_type != RA_SAMBA) && (ra_type != RA_CIFSFS)) { |
49fdf8f9 VL |
1034 | /* |
1035 | * Client can't support per-packet case sensitive | |
1036 | * pathnames. */ | |
1037 | conn->case_sensitive = false; | |
1038 | } else { | |
1039 | conn->case_sensitive = | |
1040 | !(flags & FLAG_CASELESS_PATHNAMES); | |
1041 | } | |
1042 | break; | |
1043 | case True: | |
1044 | conn->case_sensitive = true; | |
1045 | break; | |
1046 | default: | |
1047 | conn->case_sensitive = false; | |
1048 | break; | |
1049 | } | |
49fdf8f9 VL |
1050 | } |
1051 | ||
c3effa8b | 1052 | /**************************************************************************** |
cc6a4101 VL |
1053 | Prepare everything for calling the actual request function, and potentially |
1054 | call the request function via the "new" interface. | |
1055 | ||
1056 | Return False if the "legacy" function needs to be called, everything is | |
1057 | prepared. | |
1058 | ||
1059 | Return True if we're done. | |
1060 | ||
1061 | I know this API sucks, but it is the one with the least code change I could | |
1062 | find. | |
c3effa8b | 1063 | ****************************************************************************/ |
a834a73e | 1064 | |
6abd9867 | 1065 | static connection_struct *switch_message(uint8_t type, struct smb_request *req) |
c3effa8b | 1066 | { |
b5c6964a RB |
1067 | const struct loadparm_substitution *lp_sub = |
1068 | loadparm_s3_global_substitution(); | |
941db29a | 1069 | int flags; |
02d9ba6e | 1070 | uint64_t session_tag; |
9254bb4e | 1071 | connection_struct *conn = NULL; |
4c9c53f5 | 1072 | struct smbXsrv_connection *xconn = req->xconn; |
e7700025 SM |
1073 | NTTIME now = timeval_to_nttime(&req->request_time); |
1074 | struct smbXsrv_session *session = NULL; | |
1075 | NTSTATUS status; | |
24f8e973 | 1076 | |
a834a73e | 1077 | errno = 0; |
0557c6cb | 1078 | |
dec0243c SM |
1079 | if (!xconn->smb1.negprot.done) { |
1080 | switch (type) { | |
1081 | /* | |
1082 | * Without a negprot the request must | |
1083 | * either be a negprot, or one of the | |
1084 | * evil old SMB mailslot messaging types. | |
1085 | */ | |
1086 | case SMBnegprot: | |
1087 | case SMBsendstrt: | |
1088 | case SMBsendend: | |
1089 | case SMBsendtxt: | |
1090 | break; | |
1091 | default: | |
1092 | exit_server_cleanly("The first request " | |
1093 | "should be a negprot"); | |
1094 | } | |
1095 | } | |
1096 | ||
48d3a1d2 | 1097 | if (smb_messages[type].fn == NULL) { |
a834a73e | 1098 | DEBUG(0,("Unknown message type %d!\n",type)); |
c99d2455 | 1099 | smb_dump("Unknown", 1, (const char *)req->inbuf); |
cc6a4101 | 1100 | reply_unknown_new(req, type); |
9254bb4e | 1101 | return NULL; |
941db29a | 1102 | } |
a834a73e | 1103 | |
941db29a | 1104 | flags = smb_messages[type].flags; |
a834a73e | 1105 | |
941db29a | 1106 | /* In share mode security we must ignore the vuid. */ |
d7bb9618 | 1107 | session_tag = req->vuid; |
9254bb4e | 1108 | conn = req->conn; |
918c3ebe | 1109 | |
8579dd4d | 1110 | DEBUG(3,("switch message %s (pid %d) conn 0x%lx\n", smb_fn_name(type), |
c0288e06 | 1111 | (int)getpid(), (unsigned long)conn)); |
941db29a | 1112 | |
c99d2455 | 1113 | smb_dump(smb_fn_name(type), 1, (const char *)req->inbuf); |
918c3ebe | 1114 | |
941db29a | 1115 | /* Ensure this value is replaced in the incoming packet. */ |
4f41be35 | 1116 | SSVAL(discard_const_p(uint8_t, req->inbuf),smb_uid,session_tag); |
918c3ebe | 1117 | |
941db29a VL |
1118 | /* |
1119 | * Ensure the correct username is in current_user_info. This is a | |
1120 | * really ugly bugfix for problems with multiple session_setup_and_X's | |
1121 | * being done and allowing %U and %G substitutions to work correctly. | |
1122 | * There is a reason this code is done here, don't move it unless you | |
8579dd4d VL |
1123 | * know what you're doing... :-). |
1124 | * JRA. | |
941db29a VL |
1125 | */ |
1126 | ||
e7700025 SM |
1127 | /* |
1128 | * lookup an existing session | |
1129 | * | |
1130 | * Note: for now we only check for NT_STATUS_NETWORK_SESSION_EXPIRED | |
1131 | * here, the main check is still in change_to_user() | |
1132 | */ | |
4c9c53f5 | 1133 | status = smb1srv_session_lookup(xconn, |
e7700025 SM |
1134 | session_tag, |
1135 | now, | |
1136 | &session); | |
1137 | if (NT_STATUS_EQUAL(status, NT_STATUS_NETWORK_SESSION_EXPIRED)) { | |
1138 | switch (type) { | |
1139 | case SMBsesssetupX: | |
1140 | status = NT_STATUS_OK; | |
1141 | break; | |
1142 | default: | |
1143 | DEBUG(1,("Error: session %llu is expired, mid=%llu.\n", | |
1144 | (unsigned long long)session_tag, | |
1145 | (unsigned long long)req->mid)); | |
1146 | reply_nterror(req, NT_STATUS_NETWORK_SESSION_EXPIRED); | |
1147 | return conn; | |
1148 | } | |
1149 | } | |
1150 | ||
a22b5038 RB |
1151 | if (session != NULL && |
1152 | session->global->auth_session_info != NULL && | |
1153 | !(flags & AS_USER)) | |
1154 | { | |
60dbaa49 SM |
1155 | /* |
1156 | * change_to_user() implies set_current_user_info() | |
1157 | * and chdir_connect_service(). | |
1158 | * | |
1159 | * So we only call set_current_user_info if | |
1160 | * we don't have AS_USER specified. | |
1161 | */ | |
a22b5038 RB |
1162 | set_current_user_info( |
1163 | session->global->auth_session_info->unix_info->sanitized_username, | |
1164 | session->global->auth_session_info->unix_info->unix_name, | |
1165 | session->global->auth_session_info->info->domain_name); | |
941db29a | 1166 | } |
d57e67f9 | 1167 | |
941db29a VL |
1168 | /* Does this call need to be run as the connected user? */ |
1169 | if (flags & AS_USER) { | |
fcda2645 | 1170 | |
941db29a VL |
1171 | /* Does this call need a valid tree connection? */ |
1172 | if (!conn) { | |
8579dd4d VL |
1173 | /* |
1174 | * Amazingly, the error code depends on the command | |
1175 | * (from Samba4). | |
1176 | */ | |
941db29a | 1177 | if (type == SMBntcreateX) { |
cc6a4101 | 1178 | reply_nterror(req, NT_STATUS_INVALID_HANDLE); |
941db29a | 1179 | } else { |
642101ac | 1180 | reply_nterror(req, NT_STATUS_NETWORK_NAME_DELETED); |
ce61fb21 | 1181 | } |
9254bb4e | 1182 | return NULL; |
941db29a | 1183 | } |
d57e67f9 | 1184 | |
ffe1918e SM |
1185 | set_current_case_sensitive(conn, SVAL(req->inbuf,smb_flg)); |
1186 | ||
60dbaa49 SM |
1187 | /* |
1188 | * change_to_user() implies set_current_user_info() | |
1189 | * and chdir_connect_service(). | |
1190 | */ | |
d836f4a7 | 1191 | if (!change_to_user_and_service(conn,session_tag)) { |
5e9aade5 | 1192 | DEBUG(0, ("Error: Could not change to user. Removing " |
79842437 JA |
1193 | "deferred open, mid=%llu.\n", |
1194 | (unsigned long long)req->mid)); | |
74deee3c | 1195 | reply_force_doserror(req, ERRSRV, ERRbaduid); |
9254bb4e | 1196 | return conn; |
941db29a | 1197 | } |
c3effa8b | 1198 | |
941db29a | 1199 | /* All NEED_WRITE and CAN_IPC flags must also have AS_USER. */ |
918c3ebe | 1200 | |
941db29a VL |
1201 | /* Does it need write permission? */ |
1202 | if ((flags & NEED_WRITE) && !CAN_WRITE(conn)) { | |
cc6a4101 | 1203 | reply_nterror(req, NT_STATUS_MEDIA_WRITE_PROTECTED); |
9254bb4e | 1204 | return conn; |
ce61fb21 | 1205 | } |
918c3ebe | 1206 | |
941db29a VL |
1207 | /* IPC services are limited */ |
1208 | if (IS_IPC(conn) && !(flags & CAN_IPC)) { | |
642101ac | 1209 | reply_nterror(req, NT_STATUS_ACCESS_DENIED); |
9254bb4e | 1210 | return conn; |
f60ad8de | 1211 | } |
5ef67759 SM |
1212 | } else if (flags & AS_GUEST) { |
1213 | /* | |
1214 | * Does this protocol need to be run as guest? (Only archane | |
1215 | * messenger service requests have this...) | |
1216 | */ | |
1217 | if (!change_to_guest()) { | |
1218 | reply_nterror(req, NT_STATUS_ACCESS_DENIED); | |
1219 | return conn; | |
1220 | } | |
941db29a VL |
1221 | } else { |
1222 | /* This call needs to be run as root */ | |
1223 | change_to_root_user(); | |
1224 | } | |
918c3ebe | 1225 | |
941db29a VL |
1226 | /* load service specific parameters */ |
1227 | if (conn) { | |
9254bb4e JA |
1228 | if (req->encrypted) { |
1229 | conn->encrypted_tid = true; | |
1230 | /* encrypted required from now on. */ | |
e5d4e8df | 1231 | conn->encrypt_level = SMB_SIGNING_REQUIRED; |
9254bb4e | 1232 | } else if (ENCRYPTION_REQUIRED(conn)) { |
7808a259 | 1233 | if (req->cmd != SMBtrans2 && req->cmd != SMBtranss2) { |
9f1dfd8f SM |
1234 | DEBUG(1,("service[%s] requires encryption" |
1235 | "%s ACCESS_DENIED. mid=%llu\n", | |
b5c6964a | 1236 | lp_servicename(talloc_tos(), lp_sub, SNUM(conn)), |
9f1dfd8f SM |
1237 | smb_fn_name(type), |
1238 | (unsigned long long)req->mid)); | |
1239 | reply_nterror(req, NT_STATUS_ACCESS_DENIED); | |
9254bb4e JA |
1240 | return conn; |
1241 | } | |
1242 | } | |
1243 | ||
4a97448c SM |
1244 | if (flags & DO_CHDIR) { |
1245 | bool ok; | |
1246 | ||
1247 | ok = chdir_current_service(conn); | |
1248 | if (!ok) { | |
1249 | reply_nterror(req, NT_STATUS_ACCESS_DENIED); | |
1250 | return conn; | |
1251 | } | |
afce2b24 | 1252 | } |
941db29a VL |
1253 | conn->num_smb_operations++; |
1254 | } | |
918c3ebe | 1255 | |
b74bef8f RB |
1256 | /* |
1257 | * Update encryption and signing state tracking flags that are | |
1258 | * used by smbstatus to display signing and encryption status. | |
1259 | */ | |
1260 | if (session != NULL) { | |
1261 | bool update_session_global = false; | |
1262 | bool update_tcon_global = false; | |
1263 | ||
aa27bcef RB |
1264 | req->session = session; |
1265 | ||
b74bef8f RB |
1266 | smb1srv_update_crypto_flags(session, req, type, |
1267 | &update_session_global, | |
1268 | &update_tcon_global); | |
1269 | ||
1270 | if (update_session_global) { | |
1271 | status = smbXsrv_session_update(session); | |
1272 | if (!NT_STATUS_IS_OK(status)) { | |
1273 | reply_nterror(req, NT_STATUS_UNSUCCESSFUL); | |
1274 | return conn; | |
1275 | } | |
1276 | } | |
1277 | ||
1278 | if (update_tcon_global) { | |
1279 | status = smbXsrv_tcon_update(req->conn->tcon); | |
1280 | if (!NT_STATUS_IS_OK(status)) { | |
1281 | reply_nterror(req, NT_STATUS_UNSUCCESSFUL); | |
1282 | return conn; | |
1283 | } | |
1284 | } | |
1285 | } | |
1286 | ||
48d3a1d2 | 1287 | smb_messages[type].fn(req); |
9254bb4e | 1288 | return req->conn; |
e9ea36e4 AT |
1289 | } |
1290 | ||
e9ea36e4 | 1291 | /**************************************************************************** |
a834a73e | 1292 | Construct a reply to the incoming packet. |
e9ea36e4 | 1293 | ****************************************************************************/ |
a834a73e | 1294 | |
6f792afe | 1295 | void construct_reply(struct smbXsrv_connection *xconn, |
15503566 VL |
1296 | char *inbuf, |
1297 | int size, | |
1298 | size_t unread_bytes, | |
1299 | uint32_t seqnum, | |
1300 | bool encrypted) | |
e9ea36e4 | 1301 | { |
d83ecf5b | 1302 | struct smbd_server_connection *sconn = xconn->client->sconn; |
cc6a4101 | 1303 | struct smb_request *req; |
e9ea36e4 | 1304 | |
929e1d99 | 1305 | if (!(req = talloc(talloc_tos(), struct smb_request))) { |
cc6a4101 VL |
1306 | smb_panic("could not allocate smb_request"); |
1307 | } | |
54c51a66 | 1308 | |
0de91444 | 1309 | if (!init_smb1_request(req, sconn, xconn, (uint8_t *)inbuf, unread_bytes, |
c2533f94 | 1310 | encrypted, seqnum)) { |
b7898148 VL |
1311 | exit_server_cleanly("Invalid SMB request"); |
1312 | } | |
1313 | ||
b8125663 | 1314 | req->inbuf = (uint8_t *)talloc_move(req, &inbuf); |
e9ea36e4 | 1315 | |
95bd7928 | 1316 | req->conn = switch_message(req->cmd, req); |
e9ea36e4 | 1317 | |
b578db69 | 1318 | if (req->outbuf == NULL) { |
95bd7928 SM |
1319 | /* |
1320 | * Request has suspended itself, will come | |
1321 | * back here. | |
1322 | */ | |
b578db69 | 1323 | return; |
cc6a4101 | 1324 | } |
b578db69 VL |
1325 | if (CVAL(req->outbuf,0) == 0) { |
1326 | show_msg((char *)req->outbuf); | |
1327 | } | |
95bd7928 | 1328 | smb_request_done(req); |
e9ea36e4 AT |
1329 | } |
1330 | ||
7408736d | 1331 | static void construct_reply_chain(struct smbXsrv_connection *xconn, |
625056af VL |
1332 | char *inbuf, |
1333 | int size, | |
1334 | uint32_t seqnum, | |
1335 | bool encrypted) | |
3b2c9beb | 1336 | { |
3b2c9beb | 1337 | struct smb_request **reqs = NULL; |
8f93068c VL |
1338 | struct smb_request *req; |
1339 | unsigned num_reqs; | |
3b2c9beb VL |
1340 | bool ok; |
1341 | ||
5fe76a54 | 1342 | ok = smb1_parse_chain(xconn, (uint8_t *)inbuf, xconn, encrypted, |
3b2c9beb VL |
1343 | seqnum, &reqs, &num_reqs); |
1344 | if (!ok) { | |
8f93068c VL |
1345 | char errbuf[smb_size]; |
1346 | error_packet(errbuf, 0, 0, NT_STATUS_INVALID_PARAMETER, | |
1347 | __LINE__, __FILE__); | |
12f1d94a | 1348 | if (!smb1_srv_send(xconn, errbuf, true, seqnum, encrypted)) { |
8f93068c | 1349 | exit_server_cleanly("construct_reply_chain: " |
a48bf243 | 1350 | "smb1_srv_send failed."); |
8f93068c VL |
1351 | } |
1352 | return; | |
3b2c9beb VL |
1353 | } |
1354 | ||
8f93068c VL |
1355 | req = reqs[0]; |
1356 | req->inbuf = (uint8_t *)talloc_move(reqs, &inbuf); | |
3b2c9beb | 1357 | |
8f93068c | 1358 | req->conn = switch_message(req->cmd, req); |
3b2c9beb | 1359 | |
8f93068c VL |
1360 | if (req->outbuf == NULL) { |
1361 | /* | |
1362 | * Request has suspended itself, will come | |
1363 | * back here. | |
1364 | */ | |
1365 | return; | |
1366 | } | |
1367 | smb_request_done(req); | |
1368 | } | |
3b2c9beb | 1369 | |
8f93068c VL |
1370 | /* |
1371 | * To be called from an async SMB handler that is potentially chained | |
1372 | * when it is finished for shipping. | |
1373 | */ | |
1374 | ||
1375 | void smb_request_done(struct smb_request *req) | |
1376 | { | |
1377 | struct smb_request **reqs = NULL; | |
1378 | struct smb_request *first_req; | |
1379 | size_t i, num_reqs, next_index; | |
1380 | NTSTATUS status; | |
1381 | ||
1382 | if (req->chain == NULL) { | |
1383 | first_req = req; | |
1384 | goto shipit; | |
3b2c9beb VL |
1385 | } |
1386 | ||
8f93068c VL |
1387 | reqs = req->chain; |
1388 | num_reqs = talloc_array_length(reqs); | |
3b2c9beb | 1389 | |
8f93068c VL |
1390 | for (i=0; i<num_reqs; i++) { |
1391 | if (reqs[i] == req) { | |
1392 | break; | |
1393 | } | |
1394 | } | |
1395 | if (i == num_reqs) { | |
3b2c9beb | 1396 | /* |
8f93068c | 1397 | * Invalid chain, should not happen |
3b2c9beb | 1398 | */ |
8f93068c VL |
1399 | status = NT_STATUS_INTERNAL_ERROR; |
1400 | goto error; | |
1401 | } | |
1402 | next_index = i+1; | |
1403 | ||
1404 | while ((next_index < num_reqs) && (IVAL(req->outbuf, smb_rcls) == 0)) { | |
1405 | struct smb_request *next = reqs[next_index]; | |
faa8edcc SM |
1406 | struct smbXsrv_tcon *tcon; |
1407 | NTTIME now = timeval_to_nttime(&req->request_time); | |
8f93068c VL |
1408 | |
1409 | next->vuid = SVAL(req->outbuf, smb_uid); | |
1410 | next->tid = SVAL(req->outbuf, smb_tid); | |
deda0438 | 1411 | status = smb1srv_tcon_lookup(req->xconn, next->tid, |
faa8edcc | 1412 | now, &tcon); |
deda0438 | 1413 | |
faa8edcc | 1414 | if (NT_STATUS_IS_OK(status)) { |
deda0438 | 1415 | next->conn = tcon->compat; |
faa8edcc | 1416 | } else { |
deda0438 | 1417 | next->conn = NULL; |
faa8edcc | 1418 | } |
8f93068c | 1419 | next->chain_fsp = req->chain_fsp; |
fc5e5845 | 1420 | next->inbuf = req->inbuf; |
8f93068c VL |
1421 | |
1422 | req = next; | |
1423 | req->conn = switch_message(req->cmd, req); | |
1424 | ||
1425 | if (req->outbuf == NULL) { | |
1426 | /* | |
1427 | * Request has suspended itself, will come | |
1428 | * back here. | |
1429 | */ | |
1430 | return; | |
1431 | } | |
1432 | next_index += 1; | |
3b2c9beb VL |
1433 | } |
1434 | ||
8f93068c VL |
1435 | first_req = reqs[0]; |
1436 | ||
3b2c9beb | 1437 | for (i=1; i<next_index; i++) { |
8f93068c | 1438 | bool ok; |
3b2c9beb | 1439 | |
8f93068c | 1440 | ok = smb_splice_chain(&first_req->outbuf, reqs[i]->outbuf); |
3b2c9beb VL |
1441 | if (!ok) { |
1442 | status = NT_STATUS_INTERNAL_ERROR; | |
1443 | goto error; | |
1444 | } | |
3b2c9beb VL |
1445 | } |
1446 | ||
8f93068c VL |
1447 | SSVAL(first_req->outbuf, smb_uid, SVAL(req->outbuf, smb_uid)); |
1448 | SSVAL(first_req->outbuf, smb_tid, SVAL(req->outbuf, smb_tid)); | |
3b2c9beb VL |
1449 | |
1450 | /* | |
1451 | * This scary statement intends to set the | |
1452 | * FLAGS2_32_BIT_ERROR_CODES flg2 field in first_req->outbuf | |
1453 | * to the value last_req->outbuf carries | |
1454 | */ | |
1455 | SSVAL(first_req->outbuf, smb_flg2, | |
1456 | (SVAL(first_req->outbuf, smb_flg2) & ~FLAGS2_32_BIT_ERROR_CODES) | |
8f93068c | 1457 | |(SVAL(req->outbuf, smb_flg2) & FLAGS2_32_BIT_ERROR_CODES)); |
3b2c9beb VL |
1458 | |
1459 | /* | |
1460 | * Transfer the error codes from the subrequest to the main one | |
1461 | */ | |
8f93068c VL |
1462 | SSVAL(first_req->outbuf, smb_rcls, SVAL(req->outbuf, smb_rcls)); |
1463 | SSVAL(first_req->outbuf, smb_err, SVAL(req->outbuf, smb_err)); | |
3b2c9beb | 1464 | |
8f93068c VL |
1465 | _smb_setlen_large( |
1466 | first_req->outbuf, talloc_get_size(first_req->outbuf) - 4); | |
3b2c9beb VL |
1467 | |
1468 | shipit: | |
a48bf243 | 1469 | if (!smb1_srv_send(first_req->xconn, |
12f1d94a VL |
1470 | (char *)first_req->outbuf, |
1471 | true, | |
1472 | first_req->seqnum + 1, | |
1473 | IS_CONN_ENCRYPTED(req->conn) || | |
1474 | first_req->encrypted)) { | |
a48bf243 | 1475 | exit_server_cleanly("construct_reply_chain: smb1_srv_send " |
3b2c9beb VL |
1476 | "failed."); |
1477 | } | |
8f93068c VL |
1478 | TALLOC_FREE(req); /* non-chained case */ |
1479 | TALLOC_FREE(reqs); /* chained case */ | |
3b2c9beb VL |
1480 | return; |
1481 | ||
1482 | error: | |
1483 | { | |
1484 | char errbuf[smb_size]; | |
1485 | error_packet(errbuf, 0, 0, status, __LINE__, __FILE__); | |
12f1d94a VL |
1486 | if (!smb1_srv_send(req->xconn, |
1487 | errbuf, | |
1488 | true, | |
1489 | req->seqnum + 1, | |
1490 | req->encrypted)) { | |
3b2c9beb | 1491 | exit_server_cleanly("construct_reply_chain: " |
a48bf243 | 1492 | "smb1_srv_send failed."); |
3b2c9beb VL |
1493 | } |
1494 | } | |
8f93068c VL |
1495 | TALLOC_FREE(req); /* non-chained case */ |
1496 | TALLOC_FREE(reqs); /* chained case */ | |
3b2c9beb VL |
1497 | } |
1498 | ||
e9ea36e4 | 1499 | /**************************************************************************** |
49ecd176 | 1500 | Process an smb from the client |
e9ea36e4 | 1501 | ****************************************************************************/ |
e8c36c25 | 1502 | |
f2fc4227 | 1503 | void process_smb1(struct smbXsrv_connection *xconn, |
5adbc366 VL |
1504 | uint8_t *inbuf, |
1505 | size_t nread, | |
1506 | size_t unread_bytes, | |
1507 | uint32_t seqnum, | |
1508 | bool encrypted) | |
e8c36c25 DM |
1509 | { |
1510 | struct smbd_server_connection *sconn = xconn->client->sconn; | |
1511 | ||
1512 | /* Make sure this is an SMB packet. smb_size contains NetBIOS header | |
1513 | * so subtract 4 from it. */ | |
d949073e | 1514 | if ((nread < (smb_size - 4)) || !valid_smb1_header(inbuf)) { |
e8c36c25 DM |
1515 | DEBUG(2,("Non-SMB packet of length %d. Terminating server\n", |
1516 | smb_len(inbuf))); | |
1517 | ||
1518 | /* special magic for immediate exit */ | |
1519 | if ((nread == 9) && | |
1520 | (IVAL(inbuf, 4) == SMB_SUICIDE_PACKET) && | |
1521 | lp_parm_bool(-1, "smbd", "suicide mode", false)) { | |
1522 | uint8_t exitcode = CVAL(inbuf, 8); | |
1523 | DBG_WARNING("SUICIDE: Exiting immediately with code %d\n", | |
1524 | (int)exitcode); | |
1525 | exit(exitcode); | |
1526 | } | |
1527 | ||
1528 | exit_server_cleanly("Non-SMB packet"); | |
1529 | } | |
1530 | ||
1531 | show_msg((char *)inbuf); | |
1532 | ||
1533 | if ((unread_bytes == 0) && smb1_is_chain(inbuf)) { | |
625056af VL |
1534 | construct_reply_chain(xconn, |
1535 | (char *)inbuf, | |
1536 | nread, | |
1537 | seqnum, | |
1538 | encrypted); | |
e8c36c25 | 1539 | } else { |
15503566 VL |
1540 | construct_reply(xconn, |
1541 | (char *)inbuf, | |
1542 | nread, | |
1543 | unread_bytes, | |
1544 | seqnum, | |
1545 | encrypted); | |
e8c36c25 DM |
1546 | } |
1547 | ||
1548 | sconn->trans_num++; | |
1549 | } | |
1550 | ||
e9ea36e4 | 1551 | /**************************************************************************** |
1eff0523 | 1552 | Return a string containing the function name of a SMB command. |
e9ea36e4 | 1553 | ****************************************************************************/ |
1eff0523 | 1554 | |
127e77e6 | 1555 | const char *smb_fn_name(int type) |
e9ea36e4 | 1556 | { |
634c5431 | 1557 | const char *unknown_name = "SMBunknown"; |
e9ea36e4 | 1558 | |
918c3ebe | 1559 | if (smb_messages[type].name == NULL) |
e9ea36e4 AT |
1560 | return(unknown_name); |
1561 | ||
918c3ebe | 1562 | return(smb_messages[type].name); |
c3effa8b AT |
1563 | } |
1564 | ||
dc76502c | 1565 | /**************************************************************************** |
6bb8f54e | 1566 | Helper functions for contruct_reply. |
dc76502c JA |
1567 | ****************************************************************************/ |
1568 | ||
6abd9867 | 1569 | void add_to_common_flags2(uint32_t v) |
6219c997 JA |
1570 | { |
1571 | common_flags2 |= v; | |
1572 | } | |
6bb8f54e | 1573 | |
6abd9867 | 1574 | void remove_from_common_flags2(uint32_t v) |
6bb8f54e | 1575 | { |
27891bde | 1576 | common_flags2 &= ~v; |
6bb8f54e JA |
1577 | } |
1578 | ||
b6f446ca VL |
1579 | /** |
1580 | * @brief Find the smb_cmd offset of the last command pushed | |
1581 | * @param[in] buf The buffer we're building up | |
1582 | * @retval Where can we put our next andx cmd? | |
1583 | * | |
1584 | * While chaining requests, the "next" request we're looking at needs to put | |
1585 | * its SMB_Command before the data the previous request already built up added | |
1586 | * to the chain. Find the offset to the place where we have to put our cmd. | |
1587 | */ | |
1588 | ||
1589 | static bool find_andx_cmd_ofs(uint8_t *buf, size_t *pofs) | |
1590 | { | |
1591 | uint8_t cmd; | |
1592 | size_t ofs; | |
1593 | ||
1594 | cmd = CVAL(buf, smb_com); | |
1595 | ||
888bff50 | 1596 | if (!smb1cli_is_andx_req(cmd)) { |
947a8bc4 VL |
1597 | return false; |
1598 | } | |
b6f446ca VL |
1599 | |
1600 | ofs = smb_vwv0; | |
1601 | ||
1602 | while (CVAL(buf, ofs) != 0xff) { | |
1603 | ||
888bff50 | 1604 | if (!smb1cli_is_andx_req(CVAL(buf, ofs))) { |
b6f446ca VL |
1605 | return false; |
1606 | } | |
1607 | ||
1608 | /* | |
1609 | * ofs is from start of smb header, so add the 4 length | |
1610 | * bytes. The next cmd is right after the wct field. | |
1611 | */ | |
1612 | ofs = SVAL(buf, ofs+2) + 4 + 1; | |
1613 | ||
947a8bc4 VL |
1614 | if (ofs+4 >= talloc_get_size(buf)) { |
1615 | return false; | |
1616 | } | |
b6f446ca VL |
1617 | } |
1618 | ||
1619 | *pofs = ofs; | |
1620 | return true; | |
1621 | } | |
1622 | ||
1623 | /** | |
1624 | * @brief Do the smb chaining at a buffer level | |
1625 | * @param[in] poutbuf Pointer to the talloc'ed buffer to be modified | |
da322e4f | 1626 | * @param[in] andx_buf Buffer to be appended |
b6f446ca VL |
1627 | */ |
1628 | ||
da322e4f | 1629 | static bool smb_splice_chain(uint8_t **poutbuf, const uint8_t *andx_buf) |
b6f446ca | 1630 | { |
da322e4f VL |
1631 | uint8_t smb_command = CVAL(andx_buf, smb_com); |
1632 | uint8_t wct = CVAL(andx_buf, smb_wct); | |
1633 | const uint16_t *vwv = (const uint16_t *)(andx_buf + smb_vwv); | |
da322e4f | 1634 | uint32_t num_bytes = smb_buflen(andx_buf); |
adac8858 | 1635 | const uint8_t *bytes = (const uint8_t *)smb_buf_const(andx_buf); |
da322e4f | 1636 | |
b6f446ca VL |
1637 | uint8_t *outbuf; |
1638 | size_t old_size, new_size; | |
1639 | size_t ofs; | |
1640 | size_t chain_padding = 0; | |
61953ab3 VL |
1641 | size_t andx_cmd_ofs; |
1642 | ||
b6f446ca VL |
1643 | |
1644 | old_size = talloc_get_size(*poutbuf); | |
1645 | ||
b07ae1ab | 1646 | if ((old_size % 4) != 0) { |
b6f446ca VL |
1647 | /* |
1648 | * Align the wct field of subsequent requests to a 4-byte | |
1649 | * boundary | |
1650 | */ | |
1651 | chain_padding = 4 - (old_size % 4); | |
1652 | } | |
1653 | ||
1654 | /* | |
1655 | * After the old request comes the new wct field (1 byte), the vwv's | |
5b7609db | 1656 | * and the num_bytes field. |
b6f446ca VL |
1657 | */ |
1658 | ||
1659 | new_size = old_size + chain_padding + 1 + wct * sizeof(uint16_t) + 2; | |
4708b97c | 1660 | new_size += num_bytes; |
b6f446ca VL |
1661 | |
1662 | if ((smb_command != SMBwriteX) && (new_size > 0xffff)) { | |
28901acd | 1663 | DEBUG(1, ("smb_splice_chain: %u bytes won't fit\n", |
b6f446ca VL |
1664 | (unsigned)new_size)); |
1665 | return false; | |
1666 | } | |
1667 | ||
73b37743 | 1668 | outbuf = talloc_realloc(NULL, *poutbuf, uint8_t, new_size); |
b6f446ca VL |
1669 | if (outbuf == NULL) { |
1670 | DEBUG(0, ("talloc failed\n")); | |
1671 | return false; | |
1672 | } | |
1673 | *poutbuf = outbuf; | |
1674 | ||
61953ab3 VL |
1675 | if (!find_andx_cmd_ofs(outbuf, &andx_cmd_ofs)) { |
1676 | DEBUG(1, ("invalid command chain\n")); | |
1677 | *poutbuf = talloc_realloc(NULL, *poutbuf, uint8_t, old_size); | |
1678 | return false; | |
1679 | } | |
b6f446ca | 1680 | |
61953ab3 VL |
1681 | if (chain_padding != 0) { |
1682 | memset(outbuf + old_size, 0, chain_padding); | |
1683 | old_size += chain_padding; | |
b6f446ca VL |
1684 | } |
1685 | ||
61953ab3 VL |
1686 | SCVAL(outbuf, andx_cmd_ofs, smb_command); |
1687 | SSVAL(outbuf, andx_cmd_ofs + 2, old_size - 4); | |
1688 | ||
b6f446ca VL |
1689 | ofs = old_size; |
1690 | ||
1691 | /* | |
1692 | * Push the chained request: | |
1693 | * | |
1694 | * wct field | |
1695 | */ | |
1696 | ||
1697 | SCVAL(outbuf, ofs, wct); | |
1698 | ofs += 1; | |
1699 | ||
1700 | /* | |
1701 | * vwv array | |
1702 | */ | |
1703 | ||
1704 | memcpy(outbuf + ofs, vwv, sizeof(uint16_t) * wct); | |
12068d4a VL |
1705 | |
1706 | /* | |
1707 | * HACK ALERT | |
1708 | * | |
1709 | * Read&X has an offset into its data buffer at | |
1710 | * vwv[6]. reply_read_andx has no idea anymore that it's | |
1711 | * running from within a chain, so we have to fix up the | |
1712 | * offset here. | |
1713 | * | |
1714 | * Although it looks disgusting at this place, I want to keep | |
1715 | * it here. The alternative would be to push knowledge about | |
1716 | * the andx chain down into read&x again. | |
1717 | */ | |
1718 | ||
1719 | if (smb_command == SMBreadX) { | |
1720 | uint8_t *bytes_addr; | |
1721 | ||
1722 | if (wct < 7) { | |
1723 | /* | |
1724 | * Invalid read&x response | |
1725 | */ | |
1726 | return false; | |
1727 | } | |
1728 | ||
1729 | bytes_addr = outbuf + ofs /* vwv start */ | |
1730 | + sizeof(uint16_t) * wct /* vwv array */ | |
2132acb4 CS |
1731 | + sizeof(uint16_t) /* bcc */ |
1732 | + 1; /* padding byte */ | |
12068d4a VL |
1733 | |
1734 | SSVAL(outbuf + ofs, 6 * sizeof(uint16_t), | |
1735 | bytes_addr - outbuf - 4); | |
1736 | } | |
1737 | ||
b6f446ca VL |
1738 | ofs += sizeof(uint16_t) * wct; |
1739 | ||
1740 | /* | |
1741 | * bcc (byte count) | |
1742 | */ | |
1743 | ||
4708b97c | 1744 | SSVAL(outbuf, ofs, num_bytes); |
b6f446ca VL |
1745 | ofs += sizeof(uint16_t); |
1746 | ||
b6f446ca VL |
1747 | /* |
1748 | * The bytes field | |
1749 | */ | |
1750 | ||
1751 | memcpy(outbuf + ofs, bytes, num_bytes); | |
1752 | ||
1753 | return true; | |
1754 | } | |
1755 | ||
c9870a62 VL |
1756 | bool smb1_is_chain(const uint8_t *buf) |
1757 | { | |
1758 | uint8_t cmd, wct, andx_cmd; | |
1759 | ||
1760 | cmd = CVAL(buf, smb_com); | |
888bff50 | 1761 | if (!smb1cli_is_andx_req(cmd)) { |
c9870a62 VL |
1762 | return false; |
1763 | } | |
1764 | wct = CVAL(buf, smb_wct); | |
1765 | if (wct < 2) { | |
1766 | return false; | |
1767 | } | |
1768 | andx_cmd = CVAL(buf, smb_vwv); | |
1769 | return (andx_cmd != 0xFF); | |
1770 | } | |
1771 | ||
1772 | bool smb1_walk_chain(const uint8_t *buf, | |
1773 | bool (*fn)(uint8_t cmd, | |
1774 | uint8_t wct, const uint16_t *vwv, | |
1775 | uint16_t num_bytes, const uint8_t *bytes, | |
1776 | void *private_data), | |
1777 | void *private_data) | |
1778 | { | |
1779 | size_t smblen = smb_len(buf); | |
1780 | const char *smb_buf = smb_base(buf); | |
1781 | uint8_t cmd, chain_cmd; | |
1782 | uint8_t wct; | |
1783 | const uint16_t *vwv; | |
1784 | uint16_t num_bytes; | |
1785 | const uint8_t *bytes; | |
1786 | ||
1787 | cmd = CVAL(buf, smb_com); | |
1788 | wct = CVAL(buf, smb_wct); | |
1789 | vwv = (const uint16_t *)(buf + smb_vwv); | |
1790 | num_bytes = smb_buflen(buf); | |
1b740e50 | 1791 | bytes = (const uint8_t *)smb_buf_const(buf); |
c9870a62 VL |
1792 | |
1793 | if (!fn(cmd, wct, vwv, num_bytes, bytes, private_data)) { | |
1794 | return false; | |
1795 | } | |
1796 | ||
888bff50 | 1797 | if (!smb1cli_is_andx_req(cmd)) { |
c9870a62 VL |
1798 | return true; |
1799 | } | |
1800 | if (wct < 2) { | |
1801 | return false; | |
1802 | } | |
1803 | ||
1804 | chain_cmd = CVAL(vwv, 0); | |
1805 | ||
1806 | while (chain_cmd != 0xff) { | |
1807 | uint32_t chain_offset; /* uint32_t to avoid overflow */ | |
1808 | size_t length_needed; | |
1809 | ptrdiff_t vwv_offset; | |
1810 | ||
1811 | chain_offset = SVAL(vwv+1, 0); | |
1812 | ||
1813 | /* | |
1814 | * Check if the client tries to fool us. The chain | |
1815 | * offset needs to point beyond the current request in | |
1816 | * the chain, it needs to strictly grow. Otherwise we | |
1817 | * might be tricked into an endless loop always | |
1818 | * processing the same request over and over again. We | |
1819 | * used to assume that vwv and the byte buffer array | |
1820 | * in a chain are always attached, but OS/2 the | |
1821 | * Write&X/Read&X chain puts the Read&X vwv array | |
1822 | * right behind the Write&X vwv chain. The Write&X bcc | |
1823 | * array is put behind the Read&X vwv array. So now we | |
1824 | * check whether the chain offset points strictly | |
1825 | * behind the previous vwv array. req->buf points | |
1826 | * right after the vwv array of the previous | |
1827 | * request. See | |
1828 | * https://bugzilla.samba.org/show_bug.cgi?id=8360 for | |
1829 | * more information. | |
1830 | */ | |
1831 | ||
1832 | vwv_offset = ((const char *)vwv - smb_buf); | |
1833 | if (chain_offset <= vwv_offset) { | |
1834 | return false; | |
1835 | } | |
1836 | ||
1837 | /* | |
1838 | * Next check: Make sure the chain offset does not | |
1839 | * point beyond the overall smb request length. | |
1840 | */ | |
1841 | ||
1842 | length_needed = chain_offset+1; /* wct */ | |
1843 | if (length_needed > smblen) { | |
1844 | return false; | |
1845 | } | |
1846 | ||
1847 | /* | |
1848 | * Now comes the pointer magic. Goal here is to set up | |
1849 | * vwv and buf correctly again. The chain offset (the | |
1850 | * former vwv[1]) points at the new wct field. | |
1851 | */ | |
1852 | ||
1853 | wct = CVAL(smb_buf, chain_offset); | |
1854 | ||
888bff50 | 1855 | if (smb1cli_is_andx_req(chain_cmd) && (wct < 2)) { |
c9870a62 VL |
1856 | return false; |
1857 | } | |
1858 | ||
1859 | /* | |
1860 | * Next consistency check: Make the new vwv array fits | |
1861 | * in the overall smb request. | |
1862 | */ | |
1863 | ||
1864 | length_needed += (wct+1)*sizeof(uint16_t); /* vwv+buflen */ | |
1865 | if (length_needed > smblen) { | |
1866 | return false; | |
1867 | } | |
1868 | vwv = (const uint16_t *)(smb_buf + chain_offset + 1); | |
1869 | ||
1870 | /* | |
1871 | * Now grab the new byte buffer.... | |
1872 | */ | |
1873 | ||
1874 | num_bytes = SVAL(vwv+wct, 0); | |
1875 | ||
1876 | /* | |
1877 | * .. and check that it fits. | |
1878 | */ | |
1879 | ||
1880 | length_needed += num_bytes; | |
1881 | if (length_needed > smblen) { | |
1882 | return false; | |
1883 | } | |
1884 | bytes = (const uint8_t *)(vwv+wct+1); | |
1885 | ||
1886 | if (!fn(chain_cmd, wct, vwv, num_bytes, bytes, private_data)) { | |
1887 | return false; | |
1888 | } | |
1889 | ||
888bff50 | 1890 | if (!smb1cli_is_andx_req(chain_cmd)) { |
c9870a62 VL |
1891 | return true; |
1892 | } | |
1893 | chain_cmd = CVAL(vwv, 0); | |
1894 | } | |
1895 | return true; | |
1896 | } | |
1897 | ||
1898 | static bool smb1_chain_length_cb(uint8_t cmd, | |
1899 | uint8_t wct, const uint16_t *vwv, | |
1900 | uint16_t num_bytes, const uint8_t *bytes, | |
1901 | void *private_data) | |
1902 | { | |
1903 | unsigned *count = (unsigned *)private_data; | |
1904 | *count += 1; | |
1905 | return true; | |
1906 | } | |
1907 | ||
1908 | unsigned smb1_chain_length(const uint8_t *buf) | |
1909 | { | |
1910 | unsigned count = 0; | |
1911 | ||
1912 | if (!smb1_walk_chain(buf, smb1_chain_length_cb, &count)) { | |
1913 | return 0; | |
1914 | } | |
1915 | return count; | |
1916 | } | |
1917 | ||
1918 | struct smb1_parse_chain_state { | |
1919 | TALLOC_CTX *mem_ctx; | |
1920 | const uint8_t *buf; | |
1921 | struct smbd_server_connection *sconn; | |
6669a84a | 1922 | struct smbXsrv_connection *xconn; |
c9870a62 VL |
1923 | bool encrypted; |
1924 | uint32_t seqnum; | |
1925 | ||
1926 | struct smb_request **reqs; | |
1927 | unsigned num_reqs; | |
1928 | }; | |
1929 | ||
1930 | static bool smb1_parse_chain_cb(uint8_t cmd, | |
1931 | uint8_t wct, const uint16_t *vwv, | |
1932 | uint16_t num_bytes, const uint8_t *bytes, | |
1933 | void *private_data) | |
1934 | { | |
1935 | struct smb1_parse_chain_state *state = | |
1936 | (struct smb1_parse_chain_state *)private_data; | |
1937 | struct smb_request **reqs; | |
1938 | struct smb_request *req; | |
1939 | bool ok; | |
1940 | ||
1941 | reqs = talloc_realloc(state->mem_ctx, state->reqs, | |
1942 | struct smb_request *, state->num_reqs+1); | |
1943 | if (reqs == NULL) { | |
1944 | return false; | |
1945 | } | |
1946 | state->reqs = reqs; | |
1947 | ||
1948 | req = talloc(reqs, struct smb_request); | |
1949 | if (req == NULL) { | |
1950 | return false; | |
1951 | } | |
1952 | ||
0de91444 | 1953 | ok = init_smb1_request(req, state->sconn, state->xconn, state->buf, 0, |
c9870a62 VL |
1954 | state->encrypted, state->seqnum); |
1955 | if (!ok) { | |
1956 | return false; | |
1957 | } | |
1958 | req->cmd = cmd; | |
1959 | req->wct = wct; | |
1960 | req->vwv = vwv; | |
1961 | req->buflen = num_bytes; | |
1962 | req->buf = bytes; | |
1963 | ||
1964 | reqs[state->num_reqs] = req; | |
1965 | state->num_reqs += 1; | |
1966 | return true; | |
1967 | } | |
1968 | ||
1969 | bool smb1_parse_chain(TALLOC_CTX *mem_ctx, const uint8_t *buf, | |
3a26bd1a | 1970 | struct smbXsrv_connection *xconn, |
c9870a62 VL |
1971 | bool encrypted, uint32_t seqnum, |
1972 | struct smb_request ***reqs, unsigned *num_reqs) | |
1973 | { | |
3a26bd1a | 1974 | struct smbd_server_connection *sconn = NULL; |
c9870a62 | 1975 | struct smb1_parse_chain_state state; |
8f93068c | 1976 | unsigned i; |
c9870a62 | 1977 | |
3a26bd1a | 1978 | if (xconn != NULL) { |
981fb261 | 1979 | sconn = xconn->client->sconn; |
3a26bd1a SM |
1980 | } |
1981 | ||
c9870a62 VL |
1982 | state.mem_ctx = mem_ctx; |
1983 | state.buf = buf; | |
1984 | state.sconn = sconn; | |
6669a84a | 1985 | state.xconn = xconn; |
c9870a62 VL |
1986 | state.encrypted = encrypted; |
1987 | state.seqnum = seqnum; | |
1988 | state.reqs = NULL; | |
1989 | state.num_reqs = 0; | |
1990 | ||
1991 | if (!smb1_walk_chain(buf, smb1_parse_chain_cb, &state)) { | |
1992 | TALLOC_FREE(state.reqs); | |
1993 | return false; | |
1994 | } | |
8f93068c VL |
1995 | for (i=0; i<state.num_reqs; i++) { |
1996 | state.reqs[i]->chain = state.reqs; | |
1997 | } | |
c9870a62 VL |
1998 | *reqs = state.reqs; |
1999 | *num_reqs = state.num_reqs; | |
2000 | return true; | |
2001 | } | |
2002 | ||
cad0c004 VL |
2003 | static bool fd_is_readable(int fd) |
2004 | { | |
0f082de5 | 2005 | int ret, revents; |
cad0c004 | 2006 | |
0f082de5 VL |
2007 | ret = poll_one_fd(fd, POLLIN|POLLHUP, 0, &revents); |
2008 | ||
2009 | return ((ret > 0) && ((revents & (POLLIN|POLLHUP|POLLERR)) != 0)); | |
cad0c004 | 2010 | |
cad0c004 VL |
2011 | } |
2012 | ||
fc79169c | 2013 | static void smbd_server_connection_write_handler( |
96ef921b | 2014 | struct smbXsrv_connection *xconn) |
aeb798c3 SM |
2015 | { |
2016 | /* TODO: make write nonblocking */ | |
2017 | } | |
2018 | ||
6f792afe DM |
2019 | void smbd_smb1_server_connection_read_handler(struct smbXsrv_connection *xconn, |
2020 | int fd) | |
aeb798c3 SM |
2021 | { |
2022 | uint8_t *inbuf = NULL; | |
2023 | size_t inbuf_len = 0; | |
2024 | size_t unread_bytes = 0; | |
2025 | bool encrypted = false; | |
2026 | TALLOC_CTX *mem_ctx = talloc_tos(); | |
2027 | NTSTATUS status; | |
c16c90a1 | 2028 | uint32_t seqnum; |
aeb798c3 | 2029 | |
0c7f36d2 SM |
2030 | bool async_echo = lp_async_smb_echo_handler(); |
2031 | bool from_client = false; | |
5e0258fc | 2032 | |
0c7f36d2 | 2033 | if (async_echo) { |
b0fe6c5c | 2034 | if (fd_is_readable(xconn->smb1.echo_handler.trusted_fd)) { |
0c7f36d2 SM |
2035 | /* |
2036 | * This is the super-ugly hack to prefer the packets | |
2037 | * forwarded by the echo handler over the ones by the | |
2038 | * client directly | |
2039 | */ | |
b0fe6c5c | 2040 | fd = xconn->smb1.echo_handler.trusted_fd; |
0c7f36d2 | 2041 | } |
5e0258fc VL |
2042 | } |
2043 | ||
0ccffffe | 2044 | from_client = (xconn->transport.sock == fd); |
977aa660 | 2045 | |
0c7f36d2 | 2046 | if (async_echo && from_client) { |
bb8e6d45 | 2047 | smbd_lock_socket(xconn); |
cad0c004 | 2048 | |
5e0258fc VL |
2049 | if (!fd_is_readable(fd)) { |
2050 | DEBUG(10,("the echo listener was faster\n")); | |
bb8e6d45 | 2051 | smbd_unlock_socket(xconn); |
5e0258fc | 2052 | return; |
cad0c004 | 2053 | } |
5e0258fc VL |
2054 | } |
2055 | ||
2056 | /* TODO: make this completely nonblocking */ | |
e3ab0a05 | 2057 | status = receive_smb_talloc(mem_ctx, xconn, fd, |
5e0258fc VL |
2058 | (char **)(void *)&inbuf, |
2059 | 0, /* timeout */ | |
2060 | &unread_bytes, | |
2061 | &encrypted, | |
2062 | &inbuf_len, &seqnum, | |
5aa9e552 | 2063 | !from_client /* trusted channel */); |
cad0c004 | 2064 | |
0c7f36d2 | 2065 | if (async_echo && from_client) { |
bb8e6d45 | 2066 | smbd_unlock_socket(xconn); |
977aa660 SM |
2067 | } |
2068 | ||
aeb798c3 SM |
2069 | if (NT_STATUS_EQUAL(status, NT_STATUS_RETRY)) { |
2070 | goto process; | |
2071 | } | |
2072 | if (NT_STATUS_IS_ERR(status)) { | |
2073 | exit_server_cleanly("failed to receive smb request"); | |
2074 | } | |
2075 | if (!NT_STATUS_IS_OK(status)) { | |
2076 | return; | |
2077 | } | |
2078 | ||
2079 | process: | |
d2dcc0fd | 2080 | process_smb(xconn, inbuf, inbuf_len, unread_bytes, seqnum, encrypted); |
aeb798c3 SM |
2081 | } |
2082 | ||
415e8e05 | 2083 | static void smbd_server_echo_handler(struct tevent_context *ev, |
a0d96b53 | 2084 | struct tevent_fd *fde, |
cad0c004 VL |
2085 | uint16_t flags, |
2086 | void *private_data) | |
2087 | { | |
96ef921b SM |
2088 | struct smbXsrv_connection *xconn = |
2089 | talloc_get_type_abort(private_data, | |
2090 | struct smbXsrv_connection); | |
cad0c004 | 2091 | |
b05b4cab | 2092 | if (!NT_STATUS_IS_OK(xconn->transport.status)) { |
52ccb40d SM |
2093 | /* |
2094 | * we're not supposed to do any io | |
2095 | */ | |
b0fe6c5c SM |
2096 | TEVENT_FD_NOT_READABLE(xconn->smb1.echo_handler.trusted_fde); |
2097 | TEVENT_FD_NOT_WRITEABLE(xconn->smb1.echo_handler.trusted_fde); | |
52ccb40d SM |
2098 | return; |
2099 | } | |
2100 | ||
2672c37a | 2101 | if (flags & TEVENT_FD_WRITE) { |
96ef921b | 2102 | smbd_server_connection_write_handler(xconn); |
c672797a VL |
2103 | return; |
2104 | } | |
2672c37a | 2105 | if (flags & TEVENT_FD_READ) { |
4f4c40bc | 2106 | smbd_smb1_server_connection_read_handler( |
96ef921b | 2107 | xconn, xconn->smb1.echo_handler.trusted_fd); |
c672797a | 2108 | return; |
cad0c004 VL |
2109 | } |
2110 | } | |
27f812f3 | 2111 | |
27f812f3 SM |
2112 | /* |
2113 | * Send keepalive packets to our client | |
2114 | */ | |
43672e15 | 2115 | bool keepalive_fn(const struct timeval *now, void *private_data) |
27f812f3 | 2116 | { |
d911bd5c VL |
2117 | struct smbd_server_connection *sconn = talloc_get_type_abort( |
2118 | private_data, struct smbd_server_connection); | |
555b3d18 | 2119 | struct smbXsrv_connection *xconn = NULL; |
c1653e3b SM |
2120 | bool ret; |
2121 | ||
80cd127b | 2122 | if (conn_using_smb2(sconn)) { |
efd0c35a JA |
2123 | /* Don't do keepalives on an SMB2 connection. */ |
2124 | return false; | |
2125 | } | |
2126 | ||
555b3d18 SM |
2127 | /* |
2128 | * With SMB1 we only have 1 connection | |
2129 | */ | |
2130 | xconn = sconn->client->connections; | |
bb8e6d45 | 2131 | smbd_lock_socket(xconn); |
0ccffffe | 2132 | ret = send_keepalive(xconn->transport.sock); |
bb8e6d45 | 2133 | smbd_unlock_socket(xconn); |
c1653e3b SM |
2134 | |
2135 | if (!ret) { | |
2fd53228 | 2136 | int saved_errno = errno; |
40ae8b74 VL |
2137 | /* |
2138 | * Try and give an error message saying what | |
2139 | * client failed. | |
2140 | */ | |
2141 | DEBUG(0, ("send_keepalive failed for client %s. " | |
2142 | "Error %s - exiting\n", | |
2fd53228 SM |
2143 | smbXsrv_connection_dbg(xconn), |
2144 | strerror(saved_errno))); | |
2145 | errno = saved_errno; | |
27f812f3 SM |
2146 | return False; |
2147 | } | |
2148 | return True; | |
2149 | } | |
2150 | ||
27afb891 VL |
2151 | /* |
2152 | * Read an smb packet in the echo handler child, giving the parent | |
2153 | * smbd one second to react once the socket becomes readable. | |
2154 | */ | |
2155 | ||
2156 | struct smbd_echo_read_state { | |
2157 | struct tevent_context *ev; | |
0b99a8ac | 2158 | struct smbXsrv_connection *xconn; |
27afb891 VL |
2159 | |
2160 | char *buf; | |
2161 | size_t buflen; | |
2162 | uint32_t seqnum; | |
2163 | }; | |
2164 | ||
2165 | static void smbd_echo_read_readable(struct tevent_req *subreq); | |
2166 | static void smbd_echo_read_waited(struct tevent_req *subreq); | |
2167 | ||
2168 | static struct tevent_req *smbd_echo_read_send( | |
2169 | TALLOC_CTX *mem_ctx, struct tevent_context *ev, | |
0b99a8ac | 2170 | struct smbXsrv_connection *xconn) |
27afb891 VL |
2171 | { |
2172 | struct tevent_req *req, *subreq; | |
2173 | struct smbd_echo_read_state *state; | |
2174 | ||
2175 | req = tevent_req_create(mem_ctx, &state, | |
2176 | struct smbd_echo_read_state); | |
2177 | if (req == NULL) { | |
2178 | return NULL; | |
2179 | } | |
2180 | state->ev = ev; | |
0b99a8ac | 2181 | state->xconn = xconn; |
27afb891 | 2182 | |
0c6dc1ec | 2183 | subreq = wait_for_read_send(state, ev, xconn->transport.sock, false); |
27afb891 VL |
2184 | if (tevent_req_nomem(subreq, req)) { |
2185 | return tevent_req_post(req, ev); | |
2186 | } | |
2187 | tevent_req_set_callback(subreq, smbd_echo_read_readable, req); | |
2188 | return req; | |
2189 | } | |
2190 | ||
2191 | static void smbd_echo_read_readable(struct tevent_req *subreq) | |
2192 | { | |
2193 | struct tevent_req *req = tevent_req_callback_data( | |
2194 | subreq, struct tevent_req); | |
2195 | struct smbd_echo_read_state *state = tevent_req_data( | |
2196 | req, struct smbd_echo_read_state); | |
2197 | bool ok; | |
2198 | int err; | |
2199 | ||
2200 | ok = wait_for_read_recv(subreq, &err); | |
2201 | TALLOC_FREE(subreq); | |
2202 | if (!ok) { | |
2203 | tevent_req_nterror(req, map_nt_error_from_unix(err)); | |
2204 | return; | |
2205 | } | |
2206 | ||
2207 | /* | |
2208 | * Give the parent smbd one second to step in | |
2209 | */ | |
2210 | ||
2211 | subreq = tevent_wakeup_send( | |
2212 | state, state->ev, timeval_current_ofs(1, 0)); | |
2213 | if (tevent_req_nomem(subreq, req)) { | |
2214 | return; | |
2215 | } | |
2216 | tevent_req_set_callback(subreq, smbd_echo_read_waited, req); | |
2217 | } | |
2218 | ||
2219 | static void smbd_echo_read_waited(struct tevent_req *subreq) | |
2220 | { | |
2221 | struct tevent_req *req = tevent_req_callback_data( | |
2222 | subreq, struct tevent_req); | |
2223 | struct smbd_echo_read_state *state = tevent_req_data( | |
2224 | req, struct smbd_echo_read_state); | |
0b99a8ac | 2225 | struct smbXsrv_connection *xconn = state->xconn; |
27afb891 VL |
2226 | bool ok; |
2227 | NTSTATUS status; | |
2228 | size_t unread = 0; | |
2229 | bool encrypted; | |
2230 | ||
2231 | ok = tevent_wakeup_recv(subreq); | |
2232 | TALLOC_FREE(subreq); | |
2233 | if (!ok) { | |
2234 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); | |
2235 | return; | |
2236 | } | |
2237 | ||
6905bb6c | 2238 | ok = smbd_lock_socket_internal(xconn); |
27afb891 VL |
2239 | if (!ok) { |
2240 | tevent_req_nterror(req, map_nt_error_from_unix(errno)); | |
2241 | DEBUG(0, ("%s: failed to lock socket\n", __location__)); | |
2242 | return; | |
2243 | } | |
2244 | ||
0ccffffe | 2245 | if (!fd_is_readable(xconn->transport.sock)) { |
27afb891 | 2246 | DEBUG(10,("echo_handler[%d] the parent smbd was faster\n", |
c0288e06 | 2247 | (int)getpid())); |
27afb891 | 2248 | |
6905bb6c | 2249 | ok = smbd_unlock_socket_internal(xconn); |
27afb891 VL |
2250 | if (!ok) { |
2251 | tevent_req_nterror(req, map_nt_error_from_unix(errno)); | |
2252 | DEBUG(1, ("%s: failed to unlock socket\n", | |
2253 | __location__)); | |
2254 | return; | |
2255 | } | |
2256 | ||
0ccffffe | 2257 | subreq = wait_for_read_send(state, state->ev, |
0c6dc1ec | 2258 | xconn->transport.sock, false); |
27afb891 VL |
2259 | if (tevent_req_nomem(subreq, req)) { |
2260 | return; | |
2261 | } | |
2262 | tevent_req_set_callback(subreq, smbd_echo_read_readable, req); | |
2263 | return; | |
2264 | } | |
2265 | ||
e3ab0a05 | 2266 | status = receive_smb_talloc(state, xconn, |
0ccffffe SM |
2267 | xconn->transport.sock, |
2268 | &state->buf, | |
27afb891 VL |
2269 | 0 /* timeout */, |
2270 | &unread, | |
2271 | &encrypted, | |
2272 | &state->buflen, | |
2273 | &state->seqnum, | |
2274 | false /* trusted_channel*/); | |
2275 | ||
2276 | if (tevent_req_nterror(req, status)) { | |
2277 | tevent_req_nterror(req, status); | |
2278 | DEBUG(1, ("echo_handler[%d]: receive_smb_raw_talloc failed: %s\n", | |
c0288e06 | 2279 | (int)getpid(), nt_errstr(status))); |
27afb891 VL |
2280 | return; |
2281 | } | |
2282 | ||
6905bb6c | 2283 | ok = smbd_unlock_socket_internal(xconn); |
27afb891 VL |
2284 | if (!ok) { |
2285 | tevent_req_nterror(req, map_nt_error_from_unix(errno)); | |
2286 | DEBUG(1, ("%s: failed to unlock socket\n", __location__)); | |
2287 | return; | |
2288 | } | |
2289 | tevent_req_done(req); | |
2290 | } | |
2291 | ||
2292 | static NTSTATUS smbd_echo_read_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, | |
2293 | char **pbuf, size_t *pbuflen, uint32_t *pseqnum) | |
2294 | { | |
2295 | struct smbd_echo_read_state *state = tevent_req_data( | |
2296 | req, struct smbd_echo_read_state); | |
2297 | NTSTATUS status; | |
2298 | ||
2299 | if (tevent_req_is_nterror(req, &status)) { | |
2300 | return status; | |
2301 | } | |
2302 | *pbuf = talloc_move(mem_ctx, &state->buf); | |
2303 | *pbuflen = state->buflen; | |
2304 | *pseqnum = state->seqnum; | |
2305 | return NT_STATUS_OK; | |
2306 | } | |
2307 | ||
cad0c004 VL |
2308 | struct smbd_echo_state { |
2309 | struct tevent_context *ev; | |
2310 | struct iovec *pending; | |
2311 | struct smbd_server_connection *sconn; | |
6669a84a | 2312 | struct smbXsrv_connection *xconn; |
cad0c004 VL |
2313 | int parent_pipe; |
2314 | ||
2315 | struct tevent_fd *parent_fde; | |
2316 | ||
cad0c004 VL |
2317 | struct tevent_req *write_req; |
2318 | }; | |
2319 | ||
2320 | static void smbd_echo_writer_done(struct tevent_req *req); | |
2321 | ||
2322 | static void smbd_echo_activate_writer(struct smbd_echo_state *state) | |
2323 | { | |
2324 | int num_pending; | |
2325 | ||
2326 | if (state->write_req != NULL) { | |
2327 | return; | |
2328 | } | |
2329 | ||
2330 | num_pending = talloc_array_length(state->pending); | |
2331 | if (num_pending == 0) { | |
2332 | return; | |
2333 | } | |
2334 | ||
2335 | state->write_req = writev_send(state, state->ev, NULL, | |
2336 | state->parent_pipe, false, | |
2337 | state->pending, num_pending); | |
2338 | if (state->write_req == NULL) { | |
2339 | DEBUG(1, ("writev_send failed\n")); | |
2340 | exit(1); | |
2341 | } | |
2342 | ||
2343 | talloc_steal(state->write_req, state->pending); | |
2344 | state->pending = NULL; | |
2345 | ||
2346 | tevent_req_set_callback(state->write_req, smbd_echo_writer_done, | |
2347 | state); | |
2348 | } | |
2349 | ||
2350 | static void smbd_echo_writer_done(struct tevent_req *req) | |
2351 | { | |
2352 | struct smbd_echo_state *state = tevent_req_callback_data( | |
2353 | req, struct smbd_echo_state); | |
2354 | ssize_t written; | |
2355 | int err; | |
2356 | ||
2357 | written = writev_recv(req, &err); | |
2358 | TALLOC_FREE(req); | |
2359 | state->write_req = NULL; | |
2360 | if (written == -1) { | |
2361 | DEBUG(1, ("writev to parent failed: %s\n", strerror(err))); | |
2362 | exit(1); | |
2363 | } | |
c0288e06 | 2364 | DEBUG(10,("echo_handler[%d]: forwarded pdu to main\n", (int)getpid())); |
cad0c004 VL |
2365 | smbd_echo_activate_writer(state); |
2366 | } | |
2367 | ||
0b8eeb1e SM |
2368 | static bool smbd_echo_reply(struct smbd_echo_state *state, |
2369 | uint8_t *inbuf, size_t inbuf_len, | |
cad0c004 VL |
2370 | uint32_t seqnum) |
2371 | { | |
2372 | struct smb_request req; | |
2373 | uint16_t num_replies; | |
cad0c004 VL |
2374 | char *outbuf; |
2375 | bool ok; | |
2376 | ||
0633c0f6 | 2377 | if ((inbuf_len == 4) && (CVAL(inbuf, 0) == NBSSkeepalive)) { |
fd9effce VL |
2378 | DEBUG(10, ("Got netbios keepalive\n")); |
2379 | /* | |
2380 | * Just swallow it | |
2381 | */ | |
2382 | return true; | |
2383 | } | |
2384 | ||
cad0c004 VL |
2385 | if (inbuf_len < smb_size) { |
2386 | DEBUG(10, ("Got short packet: %d bytes\n", (int)inbuf_len)); | |
2387 | return false; | |
2388 | } | |
d949073e | 2389 | if (!valid_smb1_header(inbuf)) { |
cad0c004 VL |
2390 | DEBUG(10, ("Got invalid SMB header\n")); |
2391 | return false; | |
2392 | } | |
2393 | ||
0de91444 | 2394 | if (!init_smb1_request(&req, state->sconn, state->xconn, inbuf, 0, false, |
d7bc5fe7 | 2395 | seqnum)) { |
cad0c004 VL |
2396 | return false; |
2397 | } | |
2398 | req.inbuf = inbuf; | |
2399 | ||
2400 | DEBUG(10, ("smbecho handler got cmd %d (%s)\n", (int)req.cmd, | |
9907fd3e | 2401 | smb_fn_name(req.cmd))); |
cad0c004 VL |
2402 | |
2403 | if (req.cmd != SMBecho) { | |
2404 | return false; | |
2405 | } | |
2406 | if (req.wct < 1) { | |
2407 | return false; | |
2408 | } | |
2409 | ||
2410 | num_replies = SVAL(req.vwv+0, 0); | |
2411 | if (num_replies != 1) { | |
2412 | /* Not a Windows "Hey, you're still there?" request */ | |
2413 | return false; | |
2414 | } | |
2415 | ||
97fd5e56 | 2416 | if (!create_smb1_outbuf(talloc_tos(), &req, req.inbuf, &outbuf, |
cad0c004 | 2417 | 1, req.buflen)) { |
97fd5e56 | 2418 | DEBUG(10, ("create_smb1_outbuf failed\n")); |
cad0c004 VL |
2419 | return false; |
2420 | } | |
2421 | req.outbuf = (uint8_t *)outbuf; | |
2422 | ||
2423 | SSVAL(req.outbuf, smb_vwv0, num_replies); | |
2424 | ||
2425 | if (req.buflen > 0) { | |
2426 | memcpy(smb_buf(req.outbuf), req.buf, req.buflen); | |
2427 | } | |
2428 | ||
12f1d94a | 2429 | ok = smb1_srv_send(req.xconn, (char *)outbuf, true, seqnum + 1, false); |
cad0c004 VL |
2430 | TALLOC_FREE(outbuf); |
2431 | if (!ok) { | |
2432 | exit(1); | |
2433 | } | |
2434 | ||
2435 | return true; | |
2436 | } | |
2437 | ||
2438 | static void smbd_echo_exit(struct tevent_context *ev, | |
2439 | struct tevent_fd *fde, uint16_t flags, | |
2440 | void *private_data) | |
2441 | { | |
2442 | DEBUG(2, ("smbd_echo_exit: lost connection to parent\n")); | |
2443 | exit(0); | |
2444 | } | |
2445 | ||
710e5d92 VL |
2446 | static void smbd_echo_got_packet(struct tevent_req *req); |
2447 | ||
0b99a8ac | 2448 | static void smbd_echo_loop(struct smbXsrv_connection *xconn, |
cad0c004 VL |
2449 | int parent_pipe) |
2450 | { | |
2451 | struct smbd_echo_state *state; | |
710e5d92 | 2452 | struct tevent_req *read_req; |
cad0c004 | 2453 | |
0b99a8ac | 2454 | state = talloc_zero(xconn, struct smbd_echo_state); |
cad0c004 VL |
2455 | if (state == NULL) { |
2456 | DEBUG(1, ("talloc failed\n")); | |
2457 | return; | |
2458 | } | |
6669a84a | 2459 | state->xconn = xconn; |
cad0c004 | 2460 | state->parent_pipe = parent_pipe; |
fbfea52e | 2461 | state->ev = samba_tevent_context_init(state); |
cad0c004 | 2462 | if (state->ev == NULL) { |
fbfea52e | 2463 | DEBUG(1, ("samba_tevent_context_init failed\n")); |
cad0c004 VL |
2464 | TALLOC_FREE(state); |
2465 | return; | |
2466 | } | |
2467 | state->parent_fde = tevent_add_fd(state->ev, state, parent_pipe, | |
2468 | TEVENT_FD_READ, smbd_echo_exit, | |
2469 | state); | |
2470 | if (state->parent_fde == NULL) { | |
2471 | DEBUG(1, ("tevent_add_fd failed\n")); | |
2472 | TALLOC_FREE(state); | |
2473 | return; | |
2474 | } | |
710e5d92 | 2475 | |
0b99a8ac | 2476 | read_req = smbd_echo_read_send(state, state->ev, xconn); |
710e5d92 VL |
2477 | if (read_req == NULL) { |
2478 | DEBUG(1, ("smbd_echo_read_send failed\n")); | |
cad0c004 VL |
2479 | TALLOC_FREE(state); |
2480 | return; | |
2481 | } | |
710e5d92 | 2482 | tevent_req_set_callback(read_req, smbd_echo_got_packet, state); |
cad0c004 VL |
2483 | |
2484 | while (true) { | |
2485 | if (tevent_loop_once(state->ev) == -1) { | |
2486 | DEBUG(1, ("tevent_loop_once failed: %s\n", | |
2487 | strerror(errno))); | |
2488 | break; | |
2489 | } | |
2490 | } | |
2491 | TALLOC_FREE(state); | |
2492 | } | |
2493 | ||
710e5d92 VL |
2494 | static void smbd_echo_got_packet(struct tevent_req *req) |
2495 | { | |
2496 | struct smbd_echo_state *state = tevent_req_callback_data( | |
2497 | req, struct smbd_echo_state); | |
2498 | NTSTATUS status; | |
2499 | char *buf = NULL; | |
2500 | size_t buflen = 0; | |
2501 | uint32_t seqnum = 0; | |
2502 | bool reply; | |
2503 | ||
2504 | status = smbd_echo_read_recv(req, state, &buf, &buflen, &seqnum); | |
2505 | TALLOC_FREE(req); | |
2506 | if (!NT_STATUS_IS_OK(status)) { | |
2507 | DEBUG(1, ("smbd_echo_read_recv returned %s\n", | |
2508 | nt_errstr(status))); | |
2509 | exit(1); | |
2510 | } | |
2511 | ||
0b8eeb1e | 2512 | reply = smbd_echo_reply(state, (uint8_t *)buf, buflen, seqnum); |
710e5d92 VL |
2513 | if (!reply) { |
2514 | size_t num_pending; | |
2515 | struct iovec *tmp; | |
2516 | struct iovec *iov; | |
2517 | ||
2518 | num_pending = talloc_array_length(state->pending); | |
2519 | tmp = talloc_realloc(state, state->pending, struct iovec, | |
2520 | num_pending+1); | |
2521 | if (tmp == NULL) { | |
2522 | DEBUG(1, ("talloc_realloc failed\n")); | |
2523 | exit(1); | |
2524 | } | |
2525 | state->pending = tmp; | |
2526 | ||
2527 | if (buflen >= smb_size) { | |
2528 | /* | |
2529 | * place the seqnum in the packet so that the main process | |
2530 | * can reply with signing | |
2531 | */ | |
2532 | SIVAL(buf, smb_ss_field, seqnum); | |
2533 | SIVAL(buf, smb_ss_field+4, NT_STATUS_V(NT_STATUS_OK)); | |
2534 | } | |
2535 | ||
2536 | iov = &state->pending[num_pending]; | |
3d5c534f | 2537 | iov->iov_base = talloc_move(state->pending, &buf); |
710e5d92 VL |
2538 | iov->iov_len = buflen; |
2539 | ||
2540 | DEBUG(10,("echo_handler[%d]: forward to main\n", | |
c0288e06 | 2541 | (int)getpid())); |
710e5d92 VL |
2542 | smbd_echo_activate_writer(state); |
2543 | } | |
2544 | ||
0b99a8ac | 2545 | req = smbd_echo_read_send(state, state->ev, state->xconn); |
710e5d92 VL |
2546 | if (req == NULL) { |
2547 | DEBUG(1, ("smbd_echo_read_send failed\n")); | |
2548 | exit(1); | |
2549 | } | |
2550 | tevent_req_set_callback(req, smbd_echo_got_packet, state); | |
2551 | } | |
2552 | ||
2553 | ||
cad0c004 VL |
2554 | /* |
2555 | * Handle SMBecho requests in a forked child process | |
2556 | */ | |
0b99a8ac | 2557 | bool fork_echo_handler(struct smbXsrv_connection *xconn) |
cad0c004 VL |
2558 | { |
2559 | int listener_pipe[2]; | |
2560 | int res; | |
2561 | pid_t child; | |
f01af728 | 2562 | bool use_mutex = false; |
cad0c004 VL |
2563 | |
2564 | res = pipe(listener_pipe); | |
2565 | if (res == -1) { | |
2566 | DEBUG(1, ("pipe() failed: %s\n", strerror(errno))); | |
2567 | return false; | |
2568 | } | |
f01af728 CS |
2569 | |
2570 | #ifdef HAVE_ROBUST_MUTEXES | |
2571 | use_mutex = tdb_runtime_check_for_robust_mutexes(); | |
2572 | ||
2573 | if (use_mutex) { | |
2574 | pthread_mutexattr_t a; | |
2575 | ||
b0fe6c5c | 2576 | xconn->smb1.echo_handler.socket_mutex = |
f01af728 | 2577 | anonymous_shared_allocate(sizeof(pthread_mutex_t)); |
b0fe6c5c | 2578 | if (xconn->smb1.echo_handler.socket_mutex == NULL) { |
f01af728 CS |
2579 | DEBUG(1, ("Could not create mutex shared memory: %s\n", |
2580 | strerror(errno))); | |
2581 | goto fail; | |
2582 | } | |
2583 | ||
2584 | res = pthread_mutexattr_init(&a); | |
2585 | if (res != 0) { | |
2586 | DEBUG(1, ("pthread_mutexattr_init failed: %s\n", | |
2587 | strerror(res))); | |
2588 | goto fail; | |
2589 | } | |
2590 | res = pthread_mutexattr_settype(&a, PTHREAD_MUTEX_ERRORCHECK); | |
2591 | if (res != 0) { | |
2592 | DEBUG(1, ("pthread_mutexattr_settype failed: %s\n", | |
2593 | strerror(res))); | |
2594 | pthread_mutexattr_destroy(&a); | |
2595 | goto fail; | |
2596 | } | |
2597 | res = pthread_mutexattr_setpshared(&a, PTHREAD_PROCESS_SHARED); | |
2598 | if (res != 0) { | |
2599 | DEBUG(1, ("pthread_mutexattr_setpshared failed: %s\n", | |
2600 | strerror(res))); | |
2601 | pthread_mutexattr_destroy(&a); | |
2602 | goto fail; | |
2603 | } | |
2604 | res = pthread_mutexattr_setrobust(&a, PTHREAD_MUTEX_ROBUST); | |
2605 | if (res != 0) { | |
2606 | DEBUG(1, ("pthread_mutexattr_setrobust failed: " | |
2607 | "%s\n", strerror(res))); | |
2608 | pthread_mutexattr_destroy(&a); | |
2609 | goto fail; | |
2610 | } | |
b0fe6c5c | 2611 | res = pthread_mutex_init(xconn->smb1.echo_handler.socket_mutex, |
f01af728 CS |
2612 | &a); |
2613 | pthread_mutexattr_destroy(&a); | |
2614 | if (res != 0) { | |
2615 | DEBUG(1, ("pthread_mutex_init failed: %s\n", | |
2616 | strerror(res))); | |
2617 | goto fail; | |
2618 | } | |
2619 | } | |
2620 | #endif | |
2621 | ||
2622 | if (!use_mutex) { | |
b0fe6c5c | 2623 | xconn->smb1.echo_handler.socket_lock_fd = |
f01af728 | 2624 | create_unlink_tmp(lp_lock_directory()); |
b0fe6c5c | 2625 | if (xconn->smb1.echo_handler.socket_lock_fd == -1) { |
f01af728 CS |
2626 | DEBUG(1, ("Could not create lock fd: %s\n", |
2627 | strerror(errno))); | |
2628 | goto fail; | |
2629 | } | |
cad0c004 VL |
2630 | } |
2631 | ||
c0288e06 | 2632 | child = fork(); |
cad0c004 VL |
2633 | if (child == 0) { |
2634 | NTSTATUS status; | |
2635 | ||
2636 | close(listener_pipe[0]); | |
342c79e2 | 2637 | set_blocking(listener_pipe[1], false); |
cad0c004 | 2638 | |
d2adcebd | 2639 | status = smbd_reinit_after_fork(xconn->client->msg_ctx, |
d39f6ce3 | 2640 | xconn->client->raw_ev_ctx, |
38ba7d14 | 2641 | true); |
cad0c004 VL |
2642 | if (!NT_STATUS_IS_OK(status)) { |
2643 | DEBUG(1, ("reinit_after_fork failed: %s\n", | |
2644 | nt_errstr(status))); | |
2645 | exit(1); | |
2646 | } | |
62cc0bba | 2647 | process_set_title("smbd-echo", "echo handler"); |
d39f6ce3 | 2648 | initialize_password_db(true, xconn->client->raw_ev_ctx); |
0b99a8ac | 2649 | smbd_echo_loop(xconn, listener_pipe[1]); |
cad0c004 VL |
2650 | exit(0); |
2651 | } | |
2652 | close(listener_pipe[1]); | |
2653 | listener_pipe[1] = -1; | |
b0fe6c5c | 2654 | xconn->smb1.echo_handler.trusted_fd = listener_pipe[0]; |
cad0c004 | 2655 | |
1fba1406 | 2656 | DEBUG(10,("fork_echo_handler: main[%d] echo_child[%d]\n", (int)getpid(), (int)child)); |
cad0c004 VL |
2657 | |
2658 | /* | |
2659 | * Without smb signing this is the same as the normal smbd | |
2660 | * listener. This needs to change once signing comes in. | |
2661 | */ | |
d39f6ce3 SM |
2662 | xconn->smb1.echo_handler.trusted_fde = tevent_add_fd( |
2663 | xconn->client->raw_ev_ctx, | |
96ef921b | 2664 | xconn, |
b0fe6c5c | 2665 | xconn->smb1.echo_handler.trusted_fd, |
bf8cce18 | 2666 | TEVENT_FD_READ, |
cad0c004 | 2667 | smbd_server_echo_handler, |
96ef921b | 2668 | xconn); |
b0fe6c5c | 2669 | if (xconn->smb1.echo_handler.trusted_fde == NULL) { |
cad0c004 VL |
2670 | DEBUG(1, ("event_add_fd failed\n")); |
2671 | goto fail; | |
2672 | } | |
2673 | ||
2674 | return true; | |
2675 | ||
2676 | fail: | |
2677 | if (listener_pipe[0] != -1) { | |
2678 | close(listener_pipe[0]); | |
2679 | } | |
2680 | if (listener_pipe[1] != -1) { | |
2681 | close(listener_pipe[1]); | |
2682 | } | |
b0fe6c5c SM |
2683 | if (xconn->smb1.echo_handler.socket_lock_fd != -1) { |
2684 | close(xconn->smb1.echo_handler.socket_lock_fd); | |
cad0c004 | 2685 | } |
f01af728 | 2686 | #ifdef HAVE_ROBUST_MUTEXES |
b0fe6c5c SM |
2687 | if (xconn->smb1.echo_handler.socket_mutex != NULL) { |
2688 | pthread_mutex_destroy(xconn->smb1.echo_handler.socket_mutex); | |
2689 | anonymous_shared_free(xconn->smb1.echo_handler.socket_mutex); | |
f01af728 CS |
2690 | } |
2691 | #endif | |
b0fe6c5c | 2692 | smbd_echo_init(xconn); |
f01af728 | 2693 | |
cad0c004 VL |
2694 | return false; |
2695 | } | |
2696 | ||
b80111ad | 2697 | bool req_is_in_chain(const struct smb_request *req) |
d94e9c80 | 2698 | { |
4f41be35 | 2699 | if (req->vwv != (const uint16_t *)(req->inbuf+smb_vwv)) { |
d94e9c80 VL |
2700 | /* |
2701 | * We're right now handling a subsequent request, so we must | |
2702 | * be in a chain | |
2703 | */ | |
2704 | return true; | |
2705 | } | |
2706 | ||
888bff50 | 2707 | if (!smb1cli_is_andx_req(req->cmd)) { |
d94e9c80 VL |
2708 | return false; |
2709 | } | |
2710 | ||
2711 | if (req->wct < 2) { | |
2712 | /* | |
2713 | * Okay, an illegal request, but definitely not chained :-) | |
2714 | */ | |
2715 | return false; | |
2716 | } | |
2717 | ||
2718 | return (CVAL(req->vwv+0, 0) != 0xFF); | |
2719 | } |