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