1 /*
2  * sftpserver.c - server based function for the sftp protocol
3  *
4  * This file is part of the SSH Library
5  *
6  * Copyright (c) 2005      by Aris Adamantiadis
7  *
8  * The SSH Library is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or (at your
11  * option) any later version.
12  *
13  * The SSH Library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15  * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
16  * License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with the SSH Library; see the file COPYING.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
21  * MA 02111-1307, USA.
22  */
23 
24 #include "config.h"
25 
26 #include <stdio.h>
27 
28 #ifndef _WIN32
29 #include <netinet/in.h>
30 #include <arpa/inet.h>
31 #endif
32 
33 #include "libssh/libssh.h"
34 #include "libssh/sftp.h"
35 #include "libssh/sftp_priv.h"
36 #include "libssh/ssh2.h"
37 #include "libssh/priv.h"
38 #include "libssh/buffer.h"
39 #include "libssh/misc.h"
40 
41 #define SFTP_HANDLES 256
42 
sftp_get_client_message(sftp_session sftp)43 sftp_client_message sftp_get_client_message(sftp_session sftp) {
44   ssh_session session = sftp->session;
45   sftp_packet packet;
46   sftp_client_message msg;
47   ssh_buffer payload;
48   int rc;
49 
50   msg = malloc(sizeof (struct sftp_client_message_struct));
51   if (msg == NULL) {
52     ssh_set_error_oom(session);
53     return NULL;
54   }
55   ZERO_STRUCTP(msg);
56 
57   packet = sftp_packet_read(sftp);
58   if (packet == NULL) {
59     ssh_set_error_oom(session);
60     sftp_client_message_free(msg);
61     return NULL;
62   }
63 
64   payload = packet->payload;
65   msg->type = packet->type;
66   msg->sftp = sftp;
67 
68   /* take a copy of the whole packet */
69   msg->complete_message = ssh_buffer_new();
70   if (msg->complete_message == NULL) {
71       ssh_set_error_oom(session);
72       sftp_client_message_free(msg);
73       return NULL;
74   }
75 
76   rc = ssh_buffer_add_data(msg->complete_message,
77                            ssh_buffer_get(payload),
78                            ssh_buffer_get_len(payload));
79   if (rc < 0) {
80       ssh_set_error_oom(session);
81       sftp_client_message_free(msg);
82       return NULL;
83   }
84 
85   ssh_buffer_get_u32(payload, &msg->id);
86 
87   switch(msg->type) {
88     case SSH_FXP_CLOSE:
89     case SSH_FXP_READDIR:
90       msg->handle = ssh_buffer_get_ssh_string(payload);
91       if (msg->handle == NULL) {
92         ssh_set_error_oom(session);
93         sftp_client_message_free(msg);
94         return NULL;
95       }
96       break;
97     case SSH_FXP_READ:
98       rc = ssh_buffer_unpack(payload,
99                              "Sqd",
100                              &msg->handle,
101                              &msg->offset,
102                              &msg->len);
103       if (rc != SSH_OK) {
104         ssh_set_error_oom(session);
105         sftp_client_message_free(msg);
106         return NULL;
107       }
108       break;
109     case SSH_FXP_WRITE:
110       rc = ssh_buffer_unpack(payload,
111                              "SqS",
112                              &msg->handle,
113                              &msg->offset,
114                              &msg->data);
115       if (rc != SSH_OK) {
116         ssh_set_error_oom(session);
117         sftp_client_message_free(msg);
118         return NULL;
119       }
120       break;
121     case SSH_FXP_REMOVE:
122     case SSH_FXP_RMDIR:
123     case SSH_FXP_OPENDIR:
124     case SSH_FXP_READLINK:
125     case SSH_FXP_REALPATH:
126       rc = ssh_buffer_unpack(payload,
127                              "s",
128                              &msg->filename);
129       if (rc != SSH_OK) {
130         ssh_set_error_oom(session);
131         sftp_client_message_free(msg);
132         return NULL;
133       }
134       break;
135     case SSH_FXP_RENAME:
136     case SSH_FXP_SYMLINK:
137       rc = ssh_buffer_unpack(payload,
138                              "sS",
139                              &msg->filename,
140                              &msg->data);
141       if (rc != SSH_OK) {
142         ssh_set_error_oom(session);
143         sftp_client_message_free(msg);
144         return NULL;
145       }
146       break;
147     case SSH_FXP_MKDIR:
148     case SSH_FXP_SETSTAT:
149       rc = ssh_buffer_unpack(payload,
150                              "s",
151                              &msg->filename);
152       if (rc != SSH_OK) {
153         ssh_set_error_oom(session);
154         sftp_client_message_free(msg);
155         return NULL;
156       }
157       msg->attr = sftp_parse_attr(sftp, payload, 0);
158       if (msg->attr == NULL) {
159         ssh_set_error_oom(session);
160         sftp_client_message_free(msg);
161         return NULL;
162       }
163       break;
164     case SSH_FXP_FSETSTAT:
165       msg->handle = ssh_buffer_get_ssh_string(payload);
166       if (msg->handle == NULL) {
167         ssh_set_error_oom(session);
168         sftp_client_message_free(msg);
169         return NULL;
170       }
171       msg->attr = sftp_parse_attr(sftp, payload, 0);
172       if (msg->attr == NULL) {
173         ssh_set_error_oom(session);
174         sftp_client_message_free(msg);
175         return NULL;
176       }
177       break;
178     case SSH_FXP_LSTAT:
179     case SSH_FXP_STAT:
180       rc = ssh_buffer_unpack(payload,
181                              "s",
182                              &msg->filename);
183       if (rc != SSH_OK) {
184         ssh_set_error_oom(session);
185         sftp_client_message_free(msg);
186         return NULL;
187       }
188       if(sftp->version > 3) {
189         ssh_buffer_unpack(payload, "d", &msg->flags);
190       }
191       break;
192     case SSH_FXP_OPEN:
193       rc = ssh_buffer_unpack(payload,
194                              "sd",
195                              &msg->filename,
196                              &msg->flags);
197       if (rc != SSH_OK) {
198         ssh_set_error_oom(session);
199         sftp_client_message_free(msg);
200         return NULL;
201       }
202       msg->attr = sftp_parse_attr(sftp, payload, 0);
203       if (msg->attr == NULL) {
204         ssh_set_error_oom(session);
205         sftp_client_message_free(msg);
206         return NULL;
207       }
208       break;
209     case SSH_FXP_FSTAT:
210       rc = ssh_buffer_unpack(payload,
211                              "S",
212                              &msg->handle);
213       if (rc != SSH_OK) {
214         ssh_set_error_oom(session);
215         sftp_client_message_free(msg);
216         return NULL;
217       }
218       break;
219     case SSH_FXP_EXTENDED:
220       rc = ssh_buffer_unpack(payload,
221                              "s",
222                              &msg->submessage);
223       if (rc != SSH_OK) {
224         ssh_set_error_oom(session);
225         sftp_client_message_free(msg);
226         return NULL;
227       }
228 
229       if (strcmp(msg->submessage, "hardlink@openssh.com") == 0 ||
230           strcmp(msg->submessage, "posix-rename@openssh.com") == 0) {
231         rc = ssh_buffer_unpack(payload,
232                                "sS",
233                                &msg->filename,
234                                &msg->data);
235         if (rc != SSH_OK) {
236           ssh_set_error_oom(session);
237           sftp_client_message_free(msg);
238           return NULL;
239         }
240       }
241       break;
242     default:
243       ssh_set_error(sftp->session, SSH_FATAL,
244                     "Received unhandled sftp message %d", msg->type);
245       sftp_client_message_free(msg);
246       return NULL;
247   }
248 
249   return msg;
250 }
251 
252 /* Send an sftp client message. Can be used in cas of proxying */
sftp_send_client_message(sftp_session sftp,sftp_client_message msg)253 int sftp_send_client_message(sftp_session sftp, sftp_client_message msg){
254 	return sftp_packet_write(sftp, msg->type, msg->complete_message);
255 }
256 
sftp_client_message_get_type(sftp_client_message msg)257 uint8_t sftp_client_message_get_type(sftp_client_message msg){
258 	return msg->type;
259 }
260 
sftp_client_message_get_filename(sftp_client_message msg)261 const char *sftp_client_message_get_filename(sftp_client_message msg){
262 	return msg->filename;
263 }
264 
sftp_client_message_set_filename(sftp_client_message msg,const char * newname)265 void sftp_client_message_set_filename(sftp_client_message msg, const char *newname){
266 	free(msg->filename);
267 	msg->filename = strdup(newname);
268 }
269 
sftp_client_message_get_data(sftp_client_message msg)270 const char *sftp_client_message_get_data(sftp_client_message msg){
271 	if (msg->str_data == NULL)
272 		msg->str_data = ssh_string_to_char(msg->data);
273 	return msg->str_data;
274 }
275 
sftp_client_message_get_flags(sftp_client_message msg)276 uint32_t sftp_client_message_get_flags(sftp_client_message msg){
277 	return msg->flags;
278 }
279 
sftp_client_message_get_submessage(sftp_client_message msg)280 const char *sftp_client_message_get_submessage(sftp_client_message msg){
281         return msg->submessage;
282 }
283 
sftp_client_message_free(sftp_client_message msg)284 void sftp_client_message_free(sftp_client_message msg) {
285   if (msg == NULL) {
286     return;
287   }
288 
289   SAFE_FREE(msg->filename);
290   SAFE_FREE(msg->submessage);
291   SSH_STRING_FREE(msg->data);
292   SSH_STRING_FREE(msg->handle);
293   sftp_attributes_free(msg->attr);
294   SSH_BUFFER_FREE(msg->complete_message);
295   SAFE_FREE(msg->str_data);
296   ZERO_STRUCTP(msg);
297   SAFE_FREE(msg);
298 }
299 
sftp_reply_name(sftp_client_message msg,const char * name,sftp_attributes attr)300 int sftp_reply_name(sftp_client_message msg, const char *name,
301     sftp_attributes attr) {
302   ssh_buffer out;
303   ssh_string file;
304 
305   out = ssh_buffer_new();
306   if (out == NULL) {
307     return -1;
308   }
309 
310   file = ssh_string_from_char(name);
311   if (file == NULL) {
312     SSH_BUFFER_FREE(out);
313     return -1;
314   }
315 
316   if (ssh_buffer_add_u32(out, msg->id) < 0 ||
317       ssh_buffer_add_u32(out, htonl(1)) < 0 ||
318       ssh_buffer_add_ssh_string(out, file) < 0 ||
319       ssh_buffer_add_ssh_string(out, file) < 0 || /* The protocol is broken here between 3 & 4 */
320       buffer_add_attributes(out, attr) < 0 ||
321       sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
322     SSH_BUFFER_FREE(out);
323     SSH_STRING_FREE(file);
324     return -1;
325   }
326   SSH_BUFFER_FREE(out);
327   SSH_STRING_FREE(file);
328 
329   return 0;
330 }
331 
sftp_reply_handle(sftp_client_message msg,ssh_string handle)332 int sftp_reply_handle(sftp_client_message msg, ssh_string handle){
333   ssh_buffer out;
334 
335   out = ssh_buffer_new();
336   if (out == NULL) {
337     return -1;
338   }
339 
340   if (ssh_buffer_add_u32(out, msg->id) < 0 ||
341       ssh_buffer_add_ssh_string(out, handle) < 0 ||
342       sftp_packet_write(msg->sftp, SSH_FXP_HANDLE, out) < 0) {
343     SSH_BUFFER_FREE(out);
344     return -1;
345   }
346   SSH_BUFFER_FREE(out);
347 
348   return 0;
349 }
350 
sftp_reply_attr(sftp_client_message msg,sftp_attributes attr)351 int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr) {
352   ssh_buffer out;
353 
354   out = ssh_buffer_new();
355   if (out == NULL) {
356     return -1;
357   }
358 
359   if (ssh_buffer_add_u32(out, msg->id) < 0 ||
360       buffer_add_attributes(out, attr) < 0 ||
361       sftp_packet_write(msg->sftp, SSH_FXP_ATTRS, out) < 0) {
362     SSH_BUFFER_FREE(out);
363     return -1;
364   }
365   SSH_BUFFER_FREE(out);
366 
367   return 0;
368 }
369 
sftp_reply_names_add(sftp_client_message msg,const char * file,const char * longname,sftp_attributes attr)370 int sftp_reply_names_add(sftp_client_message msg, const char *file,
371     const char *longname, sftp_attributes attr) {
372   ssh_string name;
373 
374   name = ssh_string_from_char(file);
375   if (name == NULL) {
376     return -1;
377   }
378 
379   if (msg->attrbuf == NULL) {
380     msg->attrbuf = ssh_buffer_new();
381     if (msg->attrbuf == NULL) {
382       SSH_STRING_FREE(name);
383       return -1;
384     }
385   }
386 
387   if (ssh_buffer_add_ssh_string(msg->attrbuf, name) < 0) {
388     SSH_STRING_FREE(name);
389     return -1;
390   }
391 
392   SSH_STRING_FREE(name);
393   name = ssh_string_from_char(longname);
394   if (name == NULL) {
395     return -1;
396   }
397   if (ssh_buffer_add_ssh_string(msg->attrbuf,name) < 0 ||
398       buffer_add_attributes(msg->attrbuf,attr) < 0) {
399     SSH_STRING_FREE(name);
400     return -1;
401   }
402   SSH_STRING_FREE(name);
403   msg->attr_num++;
404 
405   return 0;
406 }
407 
sftp_reply_names(sftp_client_message msg)408 int sftp_reply_names(sftp_client_message msg) {
409   ssh_buffer out;
410 
411   out = ssh_buffer_new();
412   if (out == NULL) {
413     SSH_BUFFER_FREE(msg->attrbuf);
414     return -1;
415   }
416 
417   if (ssh_buffer_add_u32(out, msg->id) < 0 ||
418       ssh_buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
419       ssh_buffer_add_data(out, ssh_buffer_get(msg->attrbuf),
420         ssh_buffer_get_len(msg->attrbuf)) < 0 ||
421       sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
422     SSH_BUFFER_FREE(out);
423     SSH_BUFFER_FREE(msg->attrbuf);
424     return -1;
425   }
426 
427   SSH_BUFFER_FREE(out);
428   SSH_BUFFER_FREE(msg->attrbuf);
429 
430   msg->attr_num = 0;
431   msg->attrbuf = NULL;
432 
433   return 0;
434 }
435 
sftp_reply_status(sftp_client_message msg,uint32_t status,const char * message)436 int sftp_reply_status(sftp_client_message msg, uint32_t status,
437     const char *message) {
438   ssh_buffer out;
439   ssh_string s;
440 
441   out = ssh_buffer_new();
442   if (out == NULL) {
443     return -1;
444   }
445 
446   s = ssh_string_from_char(message ? message : "");
447   if (s == NULL) {
448     SSH_BUFFER_FREE(out);
449     return -1;
450   }
451 
452   if (ssh_buffer_add_u32(out, msg->id) < 0 ||
453       ssh_buffer_add_u32(out, htonl(status)) < 0 ||
454       ssh_buffer_add_ssh_string(out, s) < 0 ||
455       ssh_buffer_add_u32(out, 0) < 0 || /* language string */
456       sftp_packet_write(msg->sftp, SSH_FXP_STATUS, out) < 0) {
457     SSH_BUFFER_FREE(out);
458     SSH_STRING_FREE(s);
459     return -1;
460   }
461 
462   SSH_BUFFER_FREE(out);
463   SSH_STRING_FREE(s);
464 
465   return 0;
466 }
467 
sftp_reply_data(sftp_client_message msg,const void * data,int len)468 int sftp_reply_data(sftp_client_message msg, const void *data, int len) {
469   ssh_buffer out;
470 
471   out = ssh_buffer_new();
472   if (out == NULL) {
473     return -1;
474   }
475 
476   if (ssh_buffer_add_u32(out, msg->id) < 0 ||
477       ssh_buffer_add_u32(out, ntohl(len)) < 0 ||
478       ssh_buffer_add_data(out, data, len) < 0 ||
479       sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) {
480     SSH_BUFFER_FREE(out);
481     return -1;
482   }
483   SSH_BUFFER_FREE(out);
484 
485   return 0;
486 }
487 
488 /*
489  * This function will return you a new handle to give the client.
490  * the function accepts an info that can be retrieved later with
491  * the handle. Care is given that a corrupted handle won't give a
492  * valid info (or worse).
493  */
sftp_handle_alloc(sftp_session sftp,void * info)494 ssh_string sftp_handle_alloc(sftp_session sftp, void *info) {
495   ssh_string ret;
496   uint32_t val;
497   uint32_t i;
498 
499   if (sftp->handles == NULL) {
500     sftp->handles = calloc(SFTP_HANDLES, sizeof(void *));
501     if (sftp->handles == NULL) {
502       return NULL;
503     }
504   }
505 
506   for (i = 0; i < SFTP_HANDLES; i++) {
507     if (sftp->handles[i] == NULL) {
508       break;
509     }
510   }
511 
512   if (i == SFTP_HANDLES) {
513     return NULL; /* no handle available */
514   }
515 
516   val = i;
517   ret = ssh_string_new(4);
518   if (ret == NULL) {
519     return NULL;
520   }
521 
522   memcpy(ssh_string_data(ret), &val, sizeof(uint32_t));
523   sftp->handles[i] = info;
524 
525   return ret;
526 }
527 
sftp_handle(sftp_session sftp,ssh_string handle)528 void *sftp_handle(sftp_session sftp, ssh_string handle){
529   uint32_t val;
530 
531   if (sftp->handles == NULL) {
532     return NULL;
533   }
534 
535   if (ssh_string_len(handle) != sizeof(uint32_t)) {
536     return NULL;
537   }
538 
539   memcpy(&val, ssh_string_data(handle), sizeof(uint32_t));
540 
541   if (val > SFTP_HANDLES) {
542     return NULL;
543   }
544 
545   return sftp->handles[val];
546 }
547 
sftp_handle_remove(sftp_session sftp,void * handle)548 void sftp_handle_remove(sftp_session sftp, void *handle) {
549   int i;
550 
551   for (i = 0; i < SFTP_HANDLES; i++) {
552     if (sftp->handles[i] == handle) {
553       sftp->handles[i] = NULL;
554       break;
555     }
556   }
557 }
558