VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/smb.c@ 99005

Last change on this file since 99005 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

  • Property svn:eol-style set to native
File size: 28.9 KB
Line 
1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) 2016 - 2022, Daniel Stenberg, <[email protected]>, et al.
9 * Copyright (C) 2014, Bill Nagel <[email protected]>, Exacq Technologies
10 *
11 * This software is licensed as described in the file COPYING, which
12 * you should have received as part of this distribution. The terms
13 * are also available at https://curl.se/docs/copyright.html.
14 *
15 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
16 * copies of the Software, and permit persons to whom the Software is
17 * furnished to do so, under the terms of the COPYING file.
18 *
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
21 *
22 * SPDX-License-Identifier: curl
23 *
24 ***************************************************************************/
25
26#include "curl_setup.h"
27
28#if !defined(CURL_DISABLE_SMB) && defined(USE_CURL_NTLM_CORE) && \
29 (SIZEOF_CURL_OFF_T > 4)
30
31#define BUILDING_CURL_SMB_C
32
33#ifdef WIN32
34#define getpid GetCurrentProcessId
35#endif
36
37#include "smb.h"
38#include "urldata.h"
39#include "sendf.h"
40#include "multiif.h"
41#include "cfilters.h"
42#include "connect.h"
43#include "progress.h"
44#include "transfer.h"
45#include "vtls/vtls.h"
46#include "curl_ntlm_core.h"
47#include "escape.h"
48#include "curl_endian.h"
49
50/* The last #include files should be: */
51#include "curl_memory.h"
52#include "memdebug.h"
53
54/* Local API functions */
55static CURLcode smb_setup_connection(struct Curl_easy *data,
56 struct connectdata *conn);
57static CURLcode smb_connect(struct Curl_easy *data, bool *done);
58static CURLcode smb_connection_state(struct Curl_easy *data, bool *done);
59static CURLcode smb_do(struct Curl_easy *data, bool *done);
60static CURLcode smb_request_state(struct Curl_easy *data, bool *done);
61static CURLcode smb_disconnect(struct Curl_easy *data,
62 struct connectdata *conn, bool dead);
63static int smb_getsock(struct Curl_easy *data, struct connectdata *conn,
64 curl_socket_t *socks);
65static CURLcode smb_parse_url_path(struct Curl_easy *data,
66 struct connectdata *conn);
67
68/*
69 * SMB handler interface
70 */
71const struct Curl_handler Curl_handler_smb = {
72 "SMB", /* scheme */
73 smb_setup_connection, /* setup_connection */
74 smb_do, /* do_it */
75 ZERO_NULL, /* done */
76 ZERO_NULL, /* do_more */
77 smb_connect, /* connect_it */
78 smb_connection_state, /* connecting */
79 smb_request_state, /* doing */
80 smb_getsock, /* proto_getsock */
81 smb_getsock, /* doing_getsock */
82 ZERO_NULL, /* domore_getsock */
83 ZERO_NULL, /* perform_getsock */
84 smb_disconnect, /* disconnect */
85 ZERO_NULL, /* readwrite */
86 ZERO_NULL, /* connection_check */
87 ZERO_NULL, /* attach connection */
88 PORT_SMB, /* defport */
89 CURLPROTO_SMB, /* protocol */
90 CURLPROTO_SMB, /* family */
91 PROTOPT_NONE /* flags */
92};
93
94#ifdef USE_SSL
95/*
96 * SMBS handler interface
97 */
98const struct Curl_handler Curl_handler_smbs = {
99 "SMBS", /* scheme */
100 smb_setup_connection, /* setup_connection */
101 smb_do, /* do_it */
102 ZERO_NULL, /* done */
103 ZERO_NULL, /* do_more */
104 smb_connect, /* connect_it */
105 smb_connection_state, /* connecting */
106 smb_request_state, /* doing */
107 smb_getsock, /* proto_getsock */
108 smb_getsock, /* doing_getsock */
109 ZERO_NULL, /* domore_getsock */
110 ZERO_NULL, /* perform_getsock */
111 smb_disconnect, /* disconnect */
112 ZERO_NULL, /* readwrite */
113 ZERO_NULL, /* connection_check */
114 ZERO_NULL, /* attach connection */
115 PORT_SMBS, /* defport */
116 CURLPROTO_SMBS, /* protocol */
117 CURLPROTO_SMB, /* family */
118 PROTOPT_SSL /* flags */
119};
120#endif
121
122#define MAX_PAYLOAD_SIZE 0x8000
123#define MAX_MESSAGE_SIZE (MAX_PAYLOAD_SIZE + 0x1000)
124#define CLIENTNAME "curl"
125#define SERVICENAME "?????"
126
127/* Append a string to an SMB message */
128#define MSGCAT(str) \
129 do { \
130 strcpy(p, (str)); \
131 p += strlen(str); \
132 } while(0)
133
134/* Append a null-terminated string to an SMB message */
135#define MSGCATNULL(str) \
136 do { \
137 strcpy(p, (str)); \
138 p += strlen(str) + 1; \
139 } while(0)
140
141/* SMB is mostly little endian */
142#if (defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) || \
143 defined(__OS400__)
144static unsigned short smb_swap16(unsigned short x)
145{
146 return (unsigned short) ((x << 8) | ((x >> 8) & 0xff));
147}
148
149static unsigned int smb_swap32(unsigned int x)
150{
151 return (x << 24) | ((x << 8) & 0xff0000) | ((x >> 8) & 0xff00) |
152 ((x >> 24) & 0xff);
153}
154
155static curl_off_t smb_swap64(curl_off_t x)
156{
157 return ((curl_off_t) smb_swap32((unsigned int) x) << 32) |
158 smb_swap32((unsigned int) (x >> 32));
159}
160
161#else
162# define smb_swap16(x) (x)
163# define smb_swap32(x) (x)
164# define smb_swap64(x) (x)
165#endif
166
167/* SMB request state */
168enum smb_req_state {
169 SMB_REQUESTING,
170 SMB_TREE_CONNECT,
171 SMB_OPEN,
172 SMB_DOWNLOAD,
173 SMB_UPLOAD,
174 SMB_CLOSE,
175 SMB_TREE_DISCONNECT,
176 SMB_DONE
177};
178
179/* SMB request data */
180struct smb_request {
181 enum smb_req_state state;
182 char *path;
183 unsigned short tid; /* Even if we connect to the same tree as another */
184 unsigned short fid; /* request, the tid will be different */
185 CURLcode result;
186};
187
188static void conn_state(struct Curl_easy *data, enum smb_conn_state newstate)
189{
190 struct smb_conn *smbc = &data->conn->proto.smbc;
191#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
192 /* For debug purposes */
193 static const char * const names[] = {
194 "SMB_NOT_CONNECTED",
195 "SMB_CONNECTING",
196 "SMB_NEGOTIATE",
197 "SMB_SETUP",
198 "SMB_CONNECTED",
199 /* LAST */
200 };
201
202 if(smbc->state != newstate)
203 infof(data, "SMB conn %p state change from %s to %s",
204 (void *)smbc, names[smbc->state], names[newstate]);
205#endif
206
207 smbc->state = newstate;
208}
209
210static void request_state(struct Curl_easy *data,
211 enum smb_req_state newstate)
212{
213 struct smb_request *req = data->req.p.smb;
214#if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
215 /* For debug purposes */
216 static const char * const names[] = {
217 "SMB_REQUESTING",
218 "SMB_TREE_CONNECT",
219 "SMB_OPEN",
220 "SMB_DOWNLOAD",
221 "SMB_UPLOAD",
222 "SMB_CLOSE",
223 "SMB_TREE_DISCONNECT",
224 "SMB_DONE",
225 /* LAST */
226 };
227
228 if(req->state != newstate)
229 infof(data, "SMB request %p state change from %s to %s",
230 (void *)req, names[req->state], names[newstate]);
231#endif
232
233 req->state = newstate;
234}
235
236/* this should setup things in the connection, not in the easy
237 handle */
238static CURLcode smb_setup_connection(struct Curl_easy *data,
239 struct connectdata *conn)
240{
241 struct smb_request *req;
242
243 /* Initialize the request state */
244 data->req.p.smb = req = calloc(1, sizeof(struct smb_request));
245 if(!req)
246 return CURLE_OUT_OF_MEMORY;
247
248 /* Parse the URL path */
249 return smb_parse_url_path(data, conn);
250}
251
252static CURLcode smb_connect(struct Curl_easy *data, bool *done)
253{
254 struct connectdata *conn = data->conn;
255 struct smb_conn *smbc = &conn->proto.smbc;
256 char *slash;
257
258 (void) done;
259
260 /* Check we have a username and password to authenticate with */
261 if(!data->state.aptr.user)
262 return CURLE_LOGIN_DENIED;
263
264 /* Initialize the connection state */
265 smbc->state = SMB_CONNECTING;
266 smbc->recv_buf = malloc(MAX_MESSAGE_SIZE);
267 if(!smbc->recv_buf)
268 return CURLE_OUT_OF_MEMORY;
269
270 /* Multiple requests are allowed with this connection */
271 connkeep(conn, "SMB default");
272
273 /* Parse the username, domain, and password */
274 slash = strchr(conn->user, '/');
275 if(!slash)
276 slash = strchr(conn->user, '\\');
277
278 if(slash) {
279 smbc->user = slash + 1;
280 smbc->domain = strdup(conn->user);
281 if(!smbc->domain)
282 return CURLE_OUT_OF_MEMORY;
283 smbc->domain[slash - conn->user] = 0;
284 }
285 else {
286 smbc->user = conn->user;
287 smbc->domain = strdup(conn->host.name);
288 if(!smbc->domain)
289 return CURLE_OUT_OF_MEMORY;
290 }
291
292 return CURLE_OK;
293}
294
295static CURLcode smb_recv_message(struct Curl_easy *data, void **msg)
296{
297 struct connectdata *conn = data->conn;
298 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
299 struct smb_conn *smbc = &conn->proto.smbc;
300 char *buf = smbc->recv_buf;
301 ssize_t bytes_read;
302 size_t nbt_size;
303 size_t msg_size;
304 size_t len = MAX_MESSAGE_SIZE - smbc->got;
305 CURLcode result;
306
307 result = Curl_read(data, sockfd, buf + smbc->got, len, &bytes_read);
308 if(result)
309 return result;
310
311 if(!bytes_read)
312 return CURLE_OK;
313
314 smbc->got += bytes_read;
315
316 /* Check for a 32-bit nbt header */
317 if(smbc->got < sizeof(unsigned int))
318 return CURLE_OK;
319
320 nbt_size = Curl_read16_be((const unsigned char *)
321 (buf + sizeof(unsigned short))) +
322 sizeof(unsigned int);
323 if(smbc->got < nbt_size)
324 return CURLE_OK;
325
326 msg_size = sizeof(struct smb_header);
327 if(nbt_size >= msg_size + 1) {
328 /* Add the word count */
329 msg_size += 1 + ((unsigned char) buf[msg_size]) * sizeof(unsigned short);
330 if(nbt_size >= msg_size + sizeof(unsigned short)) {
331 /* Add the byte count */
332 msg_size += sizeof(unsigned short) +
333 Curl_read16_le((const unsigned char *)&buf[msg_size]);
334 if(nbt_size < msg_size)
335 return CURLE_READ_ERROR;
336 }
337 }
338
339 *msg = buf;
340
341 return CURLE_OK;
342}
343
344static void smb_pop_message(struct connectdata *conn)
345{
346 struct smb_conn *smbc = &conn->proto.smbc;
347
348 smbc->got = 0;
349}
350
351static void smb_format_message(struct Curl_easy *data, struct smb_header *h,
352 unsigned char cmd, size_t len)
353{
354 struct connectdata *conn = data->conn;
355 struct smb_conn *smbc = &conn->proto.smbc;
356 struct smb_request *req = data->req.p.smb;
357 unsigned int pid;
358
359 memset(h, 0, sizeof(*h));
360 h->nbt_length = htons((unsigned short) (sizeof(*h) - sizeof(unsigned int) +
361 len));
362 memcpy((char *)h->magic, "\xffSMB", 4);
363 h->command = cmd;
364 h->flags = SMB_FLAGS_CANONICAL_PATHNAMES | SMB_FLAGS_CASELESS_PATHNAMES;
365 h->flags2 = smb_swap16(SMB_FLAGS2_IS_LONG_NAME | SMB_FLAGS2_KNOWS_LONG_NAME);
366 h->uid = smb_swap16(smbc->uid);
367 h->tid = smb_swap16(req->tid);
368 pid = getpid();
369 h->pid_high = smb_swap16((unsigned short)(pid >> 16));
370 h->pid = smb_swap16((unsigned short) pid);
371}
372
373static CURLcode smb_send(struct Curl_easy *data, ssize_t len,
374 size_t upload_size)
375{
376 struct connectdata *conn = data->conn;
377 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
378 struct smb_conn *smbc = &conn->proto.smbc;
379 ssize_t bytes_written;
380 CURLcode result;
381
382 result = Curl_write(data, sockfd, data->state.ulbuf,
383 len, &bytes_written);
384 if(result)
385 return result;
386
387 if(bytes_written != len) {
388 smbc->send_size = len;
389 smbc->sent = bytes_written;
390 }
391
392 smbc->upload_size = upload_size;
393
394 return CURLE_OK;
395}
396
397static CURLcode smb_flush(struct Curl_easy *data)
398{
399 struct connectdata *conn = data->conn;
400 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
401 struct smb_conn *smbc = &conn->proto.smbc;
402 ssize_t bytes_written;
403 ssize_t len = smbc->send_size - smbc->sent;
404 CURLcode result;
405
406 if(!smbc->send_size)
407 return CURLE_OK;
408
409 result = Curl_write(data, sockfd,
410 data->state.ulbuf + smbc->sent,
411 len, &bytes_written);
412 if(result)
413 return result;
414
415 if(bytes_written != len)
416 smbc->sent += bytes_written;
417 else
418 smbc->send_size = 0;
419
420 return CURLE_OK;
421}
422
423static CURLcode smb_send_message(struct Curl_easy *data, unsigned char cmd,
424 const void *msg, size_t msg_len)
425{
426 CURLcode result = Curl_get_upload_buffer(data);
427 if(result)
428 return result;
429 smb_format_message(data, (struct smb_header *)data->state.ulbuf,
430 cmd, msg_len);
431 memcpy(data->state.ulbuf + sizeof(struct smb_header),
432 msg, msg_len);
433
434 return smb_send(data, sizeof(struct smb_header) + msg_len, 0);
435}
436
437static CURLcode smb_send_negotiate(struct Curl_easy *data)
438{
439 const char *msg = "\x00\x0c\x00\x02NT LM 0.12";
440
441 return smb_send_message(data, SMB_COM_NEGOTIATE, msg, 15);
442}
443
444static CURLcode smb_send_setup(struct Curl_easy *data)
445{
446 struct connectdata *conn = data->conn;
447 struct smb_conn *smbc = &conn->proto.smbc;
448 struct smb_setup msg;
449 char *p = msg.bytes;
450 unsigned char lm_hash[21];
451 unsigned char lm[24];
452 unsigned char nt_hash[21];
453 unsigned char nt[24];
454
455 size_t byte_count = sizeof(lm) + sizeof(nt);
456 byte_count += strlen(smbc->user) + strlen(smbc->domain);
457 byte_count += strlen(OS) + strlen(CLIENTNAME) + 4; /* 4 null chars */
458 if(byte_count > sizeof(msg.bytes))
459 return CURLE_FILESIZE_EXCEEDED;
460
461 Curl_ntlm_core_mk_lm_hash(conn->passwd, lm_hash);
462 Curl_ntlm_core_lm_resp(lm_hash, smbc->challenge, lm);
463 Curl_ntlm_core_mk_nt_hash(conn->passwd, nt_hash);
464 Curl_ntlm_core_lm_resp(nt_hash, smbc->challenge, nt);
465
466 memset(&msg, 0, sizeof(msg));
467 msg.word_count = SMB_WC_SETUP_ANDX;
468 msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
469 msg.max_buffer_size = smb_swap16(MAX_MESSAGE_SIZE);
470 msg.max_mpx_count = smb_swap16(1);
471 msg.vc_number = smb_swap16(1);
472 msg.session_key = smb_swap32(smbc->session_key);
473 msg.capabilities = smb_swap32(SMB_CAP_LARGE_FILES);
474 msg.lengths[0] = smb_swap16(sizeof(lm));
475 msg.lengths[1] = smb_swap16(sizeof(nt));
476 memcpy(p, lm, sizeof(lm));
477 p += sizeof(lm);
478 memcpy(p, nt, sizeof(nt));
479 p += sizeof(nt);
480 MSGCATNULL(smbc->user);
481 MSGCATNULL(smbc->domain);
482 MSGCATNULL(OS);
483 MSGCATNULL(CLIENTNAME);
484 byte_count = p - msg.bytes;
485 msg.byte_count = smb_swap16((unsigned short)byte_count);
486
487 return smb_send_message(data, SMB_COM_SETUP_ANDX, &msg,
488 sizeof(msg) - sizeof(msg.bytes) + byte_count);
489}
490
491static CURLcode smb_send_tree_connect(struct Curl_easy *data)
492{
493 struct smb_tree_connect msg;
494 struct connectdata *conn = data->conn;
495 struct smb_conn *smbc = &conn->proto.smbc;
496 char *p = msg.bytes;
497
498 size_t byte_count = strlen(conn->host.name) + strlen(smbc->share);
499 byte_count += strlen(SERVICENAME) + 5; /* 2 nulls and 3 backslashes */
500 if(byte_count > sizeof(msg.bytes))
501 return CURLE_FILESIZE_EXCEEDED;
502
503 memset(&msg, 0, sizeof(msg));
504 msg.word_count = SMB_WC_TREE_CONNECT_ANDX;
505 msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
506 msg.pw_len = 0;
507 MSGCAT("\\\\");
508 MSGCAT(conn->host.name);
509 MSGCAT("\\");
510 MSGCATNULL(smbc->share);
511 MSGCATNULL(SERVICENAME); /* Match any type of service */
512 byte_count = p - msg.bytes;
513 msg.byte_count = smb_swap16((unsigned short)byte_count);
514
515 return smb_send_message(data, SMB_COM_TREE_CONNECT_ANDX, &msg,
516 sizeof(msg) - sizeof(msg.bytes) + byte_count);
517}
518
519static CURLcode smb_send_open(struct Curl_easy *data)
520{
521 struct smb_request *req = data->req.p.smb;
522 struct smb_nt_create msg;
523 size_t byte_count;
524
525 if((strlen(req->path) + 1) > sizeof(msg.bytes))
526 return CURLE_FILESIZE_EXCEEDED;
527
528 memset(&msg, 0, sizeof(msg));
529 msg.word_count = SMB_WC_NT_CREATE_ANDX;
530 msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
531 byte_count = strlen(req->path);
532 msg.name_length = smb_swap16((unsigned short)byte_count);
533 msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL);
534 if(data->set.upload) {
535 msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE);
536 msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF);
537 }
538 else {
539 msg.access = smb_swap32(SMB_GENERIC_READ);
540 msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
541 }
542 msg.byte_count = smb_swap16((unsigned short) ++byte_count);
543 strcpy(msg.bytes, req->path);
544
545 return smb_send_message(data, SMB_COM_NT_CREATE_ANDX, &msg,
546 sizeof(msg) - sizeof(msg.bytes) + byte_count);
547}
548
549static CURLcode smb_send_close(struct Curl_easy *data)
550{
551 struct smb_request *req = data->req.p.smb;
552 struct smb_close msg;
553
554 memset(&msg, 0, sizeof(msg));
555 msg.word_count = SMB_WC_CLOSE;
556 msg.fid = smb_swap16(req->fid);
557
558 return smb_send_message(data, SMB_COM_CLOSE, &msg, sizeof(msg));
559}
560
561static CURLcode smb_send_tree_disconnect(struct Curl_easy *data)
562{
563 struct smb_tree_disconnect msg;
564
565 memset(&msg, 0, sizeof(msg));
566
567 return smb_send_message(data, SMB_COM_TREE_DISCONNECT, &msg, sizeof(msg));
568}
569
570static CURLcode smb_send_read(struct Curl_easy *data)
571{
572 struct smb_request *req = data->req.p.smb;
573 curl_off_t offset = data->req.offset;
574 struct smb_read msg;
575
576 memset(&msg, 0, sizeof(msg));
577 msg.word_count = SMB_WC_READ_ANDX;
578 msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
579 msg.fid = smb_swap16(req->fid);
580 msg.offset = smb_swap32((unsigned int) offset);
581 msg.offset_high = smb_swap32((unsigned int) (offset >> 32));
582 msg.min_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
583 msg.max_bytes = smb_swap16(MAX_PAYLOAD_SIZE);
584
585 return smb_send_message(data, SMB_COM_READ_ANDX, &msg, sizeof(msg));
586}
587
588static CURLcode smb_send_write(struct Curl_easy *data)
589{
590 struct smb_write *msg;
591 struct smb_request *req = data->req.p.smb;
592 curl_off_t offset = data->req.offset;
593 curl_off_t upload_size = data->req.size - data->req.bytecount;
594 CURLcode result = Curl_get_upload_buffer(data);
595 if(result)
596 return result;
597 msg = (struct smb_write *)data->state.ulbuf;
598
599 if(upload_size >= MAX_PAYLOAD_SIZE - 1) /* There is one byte of padding */
600 upload_size = MAX_PAYLOAD_SIZE - 1;
601
602 memset(msg, 0, sizeof(*msg));
603 msg->word_count = SMB_WC_WRITE_ANDX;
604 msg->andx.command = SMB_COM_NO_ANDX_COMMAND;
605 msg->fid = smb_swap16(req->fid);
606 msg->offset = smb_swap32((unsigned int) offset);
607 msg->offset_high = smb_swap32((unsigned int) (offset >> 32));
608 msg->data_length = smb_swap16((unsigned short) upload_size);
609 msg->data_offset = smb_swap16(sizeof(*msg) - sizeof(unsigned int));
610 msg->byte_count = smb_swap16((unsigned short) (upload_size + 1));
611
612 smb_format_message(data, &msg->h, SMB_COM_WRITE_ANDX,
613 sizeof(*msg) - sizeof(msg->h) + (size_t) upload_size);
614
615 return smb_send(data, sizeof(*msg), (size_t) upload_size);
616}
617
618static CURLcode smb_send_and_recv(struct Curl_easy *data, void **msg)
619{
620 struct connectdata *conn = data->conn;
621 struct smb_conn *smbc = &conn->proto.smbc;
622 CURLcode result;
623 *msg = NULL; /* if it returns early */
624
625 /* Check if there is data in the transfer buffer */
626 if(!smbc->send_size && smbc->upload_size) {
627 size_t nread = smbc->upload_size > (size_t)data->set.upload_buffer_size ?
628 (size_t)data->set.upload_buffer_size : smbc->upload_size;
629 data->req.upload_fromhere = data->state.ulbuf;
630 result = Curl_fillreadbuffer(data, nread, &nread);
631 if(result && result != CURLE_AGAIN)
632 return result;
633 if(!nread)
634 return CURLE_OK;
635
636 smbc->upload_size -= nread;
637 smbc->send_size = nread;
638 smbc->sent = 0;
639 }
640
641 /* Check if there is data to send */
642 if(smbc->send_size) {
643 result = smb_flush(data);
644 if(result)
645 return result;
646 }
647
648 /* Check if there is still data to be sent */
649 if(smbc->send_size || smbc->upload_size)
650 return CURLE_AGAIN;
651
652 return smb_recv_message(data, msg);
653}
654
655static CURLcode smb_connection_state(struct Curl_easy *data, bool *done)
656{
657 struct connectdata *conn = data->conn;
658 struct smb_conn *smbc = &conn->proto.smbc;
659 struct smb_negotiate_response *nrsp;
660 struct smb_header *h;
661 CURLcode result;
662 void *msg = NULL;
663
664 if(smbc->state == SMB_CONNECTING) {
665#ifdef USE_SSL
666 if((conn->handler->flags & PROTOPT_SSL)) {
667 bool ssl_done = FALSE;
668 result = Curl_conn_connect(data, FIRSTSOCKET, FALSE, &ssl_done);
669 if(result && result != CURLE_AGAIN)
670 return result;
671 if(!ssl_done)
672 return CURLE_OK;
673 }
674#endif
675
676 result = smb_send_negotiate(data);
677 if(result) {
678 connclose(conn, "SMB: failed to send negotiate message");
679 return result;
680 }
681
682 conn_state(data, SMB_NEGOTIATE);
683 }
684
685 /* Send the previous message and check for a response */
686 result = smb_send_and_recv(data, &msg);
687 if(result && result != CURLE_AGAIN) {
688 connclose(conn, "SMB: failed to communicate");
689 return result;
690 }
691
692 if(!msg)
693 return CURLE_OK;
694
695 h = msg;
696
697 switch(smbc->state) {
698 case SMB_NEGOTIATE:
699 if((smbc->got < sizeof(*nrsp) + sizeof(smbc->challenge) - 1) ||
700 h->status) {
701 connclose(conn, "SMB: negotiation failed");
702 return CURLE_COULDNT_CONNECT;
703 }
704 nrsp = msg;
705 memcpy(smbc->challenge, nrsp->bytes, sizeof(smbc->challenge));
706 smbc->session_key = smb_swap32(nrsp->session_key);
707 result = smb_send_setup(data);
708 if(result) {
709 connclose(conn, "SMB: failed to send setup message");
710 return result;
711 }
712 conn_state(data, SMB_SETUP);
713 break;
714
715 case SMB_SETUP:
716 if(h->status) {
717 connclose(conn, "SMB: authentication failed");
718 return CURLE_LOGIN_DENIED;
719 }
720 smbc->uid = smb_swap16(h->uid);
721 conn_state(data, SMB_CONNECTED);
722 *done = true;
723 break;
724
725 default:
726 smb_pop_message(conn);
727 return CURLE_OK; /* ignore */
728 }
729
730 smb_pop_message(conn);
731
732 return CURLE_OK;
733}
734
735/*
736 * Convert a timestamp from the Windows world (100 nsec units from 1 Jan 1601)
737 * to Posix time. Cap the output to fit within a time_t.
738 */
739static void get_posix_time(time_t *out, curl_off_t timestamp)
740{
741 timestamp -= 116444736000000000;
742 timestamp /= 10000000;
743#if SIZEOF_TIME_T < SIZEOF_CURL_OFF_T
744 if(timestamp > TIME_T_MAX)
745 *out = TIME_T_MAX;
746 else if(timestamp < TIME_T_MIN)
747 *out = TIME_T_MIN;
748 else
749#endif
750 *out = (time_t) timestamp;
751}
752
753static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
754{
755 struct connectdata *conn = data->conn;
756 struct smb_request *req = data->req.p.smb;
757 struct smb_header *h;
758 struct smb_conn *smbc = &conn->proto.smbc;
759 enum smb_req_state next_state = SMB_DONE;
760 unsigned short len;
761 unsigned short off;
762 CURLcode result;
763 void *msg = NULL;
764 const struct smb_nt_create_response *smb_m;
765
766 /* Start the request */
767 if(req->state == SMB_REQUESTING) {
768 result = smb_send_tree_connect(data);
769 if(result) {
770 connclose(conn, "SMB: failed to send tree connect message");
771 return result;
772 }
773
774 request_state(data, SMB_TREE_CONNECT);
775 }
776
777 /* Send the previous message and check for a response */
778 result = smb_send_and_recv(data, &msg);
779 if(result && result != CURLE_AGAIN) {
780 connclose(conn, "SMB: failed to communicate");
781 return result;
782 }
783
784 if(!msg)
785 return CURLE_OK;
786
787 h = msg;
788
789 switch(req->state) {
790 case SMB_TREE_CONNECT:
791 if(h->status) {
792 req->result = CURLE_REMOTE_FILE_NOT_FOUND;
793 if(h->status == smb_swap32(SMB_ERR_NOACCESS))
794 req->result = CURLE_REMOTE_ACCESS_DENIED;
795 break;
796 }
797 req->tid = smb_swap16(h->tid);
798 next_state = SMB_OPEN;
799 break;
800
801 case SMB_OPEN:
802 if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) {
803 req->result = CURLE_REMOTE_FILE_NOT_FOUND;
804 if(h->status == smb_swap32(SMB_ERR_NOACCESS))
805 req->result = CURLE_REMOTE_ACCESS_DENIED;
806 next_state = SMB_TREE_DISCONNECT;
807 break;
808 }
809 smb_m = (const struct smb_nt_create_response*) msg;
810 req->fid = smb_swap16(smb_m->fid);
811 data->req.offset = 0;
812 if(data->set.upload) {
813 data->req.size = data->state.infilesize;
814 Curl_pgrsSetUploadSize(data, data->req.size);
815 next_state = SMB_UPLOAD;
816 }
817 else {
818 smb_m = (const struct smb_nt_create_response*) msg;
819 data->req.size = smb_swap64(smb_m->end_of_file);
820 if(data->req.size < 0) {
821 req->result = CURLE_WEIRD_SERVER_REPLY;
822 next_state = SMB_CLOSE;
823 }
824 else {
825 Curl_pgrsSetDownloadSize(data, data->req.size);
826 if(data->set.get_filetime)
827 get_posix_time(&data->info.filetime, smb_m->last_change_time);
828 next_state = SMB_DOWNLOAD;
829 }
830 }
831 break;
832
833 case SMB_DOWNLOAD:
834 if(h->status || smbc->got < sizeof(struct smb_header) + 14) {
835 req->result = CURLE_RECV_ERROR;
836 next_state = SMB_CLOSE;
837 break;
838 }
839 len = Curl_read16_le(((const unsigned char *) msg) +
840 sizeof(struct smb_header) + 11);
841 off = Curl_read16_le(((const unsigned char *) msg) +
842 sizeof(struct smb_header) + 13);
843 if(len > 0) {
844 if(off + sizeof(unsigned int) + len > smbc->got) {
845 failf(data, "Invalid input packet");
846 result = CURLE_RECV_ERROR;
847 }
848 else
849 result = Curl_client_write(data, CLIENTWRITE_BODY,
850 (char *)msg + off + sizeof(unsigned int),
851 len);
852 if(result) {
853 req->result = result;
854 next_state = SMB_CLOSE;
855 break;
856 }
857 }
858 data->req.bytecount += len;
859 data->req.offset += len;
860 Curl_pgrsSetDownloadCounter(data, data->req.bytecount);
861 next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
862 break;
863
864 case SMB_UPLOAD:
865 if(h->status || smbc->got < sizeof(struct smb_header) + 6) {
866 req->result = CURLE_UPLOAD_FAILED;
867 next_state = SMB_CLOSE;
868 break;
869 }
870 len = Curl_read16_le(((const unsigned char *) msg) +
871 sizeof(struct smb_header) + 5);
872 data->req.bytecount += len;
873 data->req.offset += len;
874 Curl_pgrsSetUploadCounter(data, data->req.bytecount);
875 if(data->req.bytecount >= data->req.size)
876 next_state = SMB_CLOSE;
877 else
878 next_state = SMB_UPLOAD;
879 break;
880
881 case SMB_CLOSE:
882 /* We don't care if the close failed, proceed to tree disconnect anyway */
883 next_state = SMB_TREE_DISCONNECT;
884 break;
885
886 case SMB_TREE_DISCONNECT:
887 next_state = SMB_DONE;
888 break;
889
890 default:
891 smb_pop_message(conn);
892 return CURLE_OK; /* ignore */
893 }
894
895 smb_pop_message(conn);
896
897 switch(next_state) {
898 case SMB_OPEN:
899 result = smb_send_open(data);
900 break;
901
902 case SMB_DOWNLOAD:
903 result = smb_send_read(data);
904 break;
905
906 case SMB_UPLOAD:
907 result = smb_send_write(data);
908 break;
909
910 case SMB_CLOSE:
911 result = smb_send_close(data);
912 break;
913
914 case SMB_TREE_DISCONNECT:
915 result = smb_send_tree_disconnect(data);
916 break;
917
918 case SMB_DONE:
919 result = req->result;
920 *done = true;
921 break;
922
923 default:
924 break;
925 }
926
927 if(result) {
928 connclose(conn, "SMB: failed to send message");
929 return result;
930 }
931
932 request_state(data, next_state);
933
934 return CURLE_OK;
935}
936
937static CURLcode smb_disconnect(struct Curl_easy *data,
938 struct connectdata *conn, bool dead)
939{
940 struct smb_conn *smbc = &conn->proto.smbc;
941 (void) dead;
942 (void) data;
943 Curl_safefree(smbc->share);
944 Curl_safefree(smbc->domain);
945 Curl_safefree(smbc->recv_buf);
946 return CURLE_OK;
947}
948
949static int smb_getsock(struct Curl_easy *data,
950 struct connectdata *conn, curl_socket_t *socks)
951{
952 (void)data;
953 socks[0] = conn->sock[FIRSTSOCKET];
954 return GETSOCK_READSOCK(0) | GETSOCK_WRITESOCK(0);
955}
956
957static CURLcode smb_do(struct Curl_easy *data, bool *done)
958{
959 struct connectdata *conn = data->conn;
960 struct smb_conn *smbc = &conn->proto.smbc;
961
962 *done = FALSE;
963 if(smbc->share) {
964 return CURLE_OK;
965 }
966 return CURLE_URL_MALFORMAT;
967}
968
969static CURLcode smb_parse_url_path(struct Curl_easy *data,
970 struct connectdata *conn)
971{
972 struct smb_request *req = data->req.p.smb;
973 struct smb_conn *smbc = &conn->proto.smbc;
974 char *path;
975 char *slash;
976
977 /* URL decode the path */
978 CURLcode result = Curl_urldecode(data->state.up.path, 0, &path, NULL,
979 REJECT_CTRL);
980 if(result)
981 return result;
982
983 /* Parse the path for the share */
984 smbc->share = strdup((*path == '/' || *path == '\\') ? path + 1 : path);
985 free(path);
986 if(!smbc->share)
987 return CURLE_OUT_OF_MEMORY;
988
989 slash = strchr(smbc->share, '/');
990 if(!slash)
991 slash = strchr(smbc->share, '\\');
992
993 /* The share must be present */
994 if(!slash) {
995 Curl_safefree(smbc->share);
996 return CURLE_URL_MALFORMAT;
997 }
998
999 /* Parse the path for the file path converting any forward slashes into
1000 backslashes */
1001 *slash++ = 0;
1002 req->path = slash;
1003
1004 for(; *slash; slash++) {
1005 if(*slash == '/')
1006 *slash = '\\';
1007 }
1008 return CURLE_OK;
1009}
1010
1011#endif /* CURL_DISABLE_SMB && USE_CURL_NTLM_CORE &&
1012 SIZEOF_CURL_OFF_T > 4 */
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette