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 <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 
28 #ifndef _WIN32
29 #include <arpa/inet.h>
30 #endif
31 
32 #include "libssh/libssh.h"
33 #include "libssh/sftp.h"
34 #include "libssh/ssh2.h"
35 #include "libssh/priv.h"
36 #include "libssh/buffer.h"
37 #include "libssh/misc.h"
38 
sftp_get_client_message(sftp_session sftp)39 sftp_client_message sftp_get_client_message(sftp_session sftp) {
40   ssh_session session = sftp->session;
41   sftp_packet packet;
42   sftp_client_message msg;
43   ssh_buffer payload;
44   ssh_string tmp;
45 
46   msg = malloc(sizeof (struct sftp_client_message_struct));
47   if (msg == NULL) {
48     ssh_set_error_oom(session);
49     return NULL;
50   }
51   ZERO_STRUCTP(msg);
52 
53   packet = sftp_packet_read(sftp);
54   if (packet == NULL) {
55     ssh_set_error_oom(session);
56     sftp_client_message_free(msg);
57     return NULL;
58   }
59 
60   payload = packet->payload;
61   msg->type = packet->type;
62   msg->sftp = sftp;
63 
64   buffer_get_u32(payload, &msg->id);
65   switch(msg->type) {
66     case SSH_FXP_CLOSE:
67     case SSH_FXP_READDIR:
68       msg->handle = buffer_get_ssh_string(payload);
69       if (msg->handle == NULL) {
70         ssh_set_error_oom(session);
71         sftp_client_message_free(msg);
72         return NULL;
73       }
74       break;
75     case SSH_FXP_READ:
76       msg->handle = buffer_get_ssh_string(payload);
77       if (msg->handle == NULL) {
78         ssh_set_error_oom(session);
79         sftp_client_message_free(msg);
80         return NULL;
81       }
82       buffer_get_u64(payload, &msg->offset);
83       buffer_get_u32(payload, &msg->len);
84       break;
85     case SSH_FXP_WRITE:
86       msg->handle = buffer_get_ssh_string(payload);
87       if (msg->handle == NULL) {
88         ssh_set_error_oom(session);
89         sftp_client_message_free(msg);
90         return NULL;
91       }
92       buffer_get_u64(payload, &msg->offset);
93       msg->data = buffer_get_ssh_string(payload);
94       if (msg->data == NULL) {
95         ssh_set_error_oom(session);
96         sftp_client_message_free(msg);
97         return NULL;
98       }
99       break;
100     case SSH_FXP_REMOVE:
101     case SSH_FXP_RMDIR:
102     case SSH_FXP_OPENDIR:
103     case SSH_FXP_READLINK:
104     case SSH_FXP_REALPATH:
105       tmp = buffer_get_ssh_string(payload);
106       if (tmp == NULL) {
107         ssh_set_error_oom(session);
108         sftp_client_message_free(msg);
109         return NULL;
110       }
111       msg->filename = ssh_string_to_char(tmp);
112       ssh_string_free(tmp);
113       if (msg->filename == NULL) {
114         ssh_set_error_oom(session);
115         sftp_client_message_free(msg);
116         return NULL;
117       }
118       break;
119     case SSH_FXP_RENAME:
120     case SSH_FXP_SYMLINK:
121       tmp = buffer_get_ssh_string(payload);
122       if (tmp == NULL) {
123         ssh_set_error_oom(session);
124         sftp_client_message_free(msg);
125         return NULL;
126       }
127       msg->filename = ssh_string_to_char(tmp);
128       ssh_string_free(tmp);
129       if (msg->filename == NULL) {
130         ssh_set_error_oom(session);
131         sftp_client_message_free(msg);
132         return NULL;
133       }
134       msg->data = buffer_get_ssh_string(payload);
135       if (msg->data == NULL) {
136         ssh_set_error_oom(session);
137         sftp_client_message_free(msg);
138         return NULL;
139       }
140       break;
141     case SSH_FXP_MKDIR:
142     case SSH_FXP_SETSTAT:
143       tmp = buffer_get_ssh_string(payload);
144       if (tmp == NULL) {
145         ssh_set_error_oom(session);
146         sftp_client_message_free(msg);
147         return NULL;
148       }
149       msg->filename=ssh_string_to_char(tmp);
150       ssh_string_free(tmp);
151       if (msg->filename == NULL) {
152         ssh_set_error_oom(session);
153         sftp_client_message_free(msg);
154         return NULL;
155       }
156       msg->attr = sftp_parse_attr(sftp, payload, 0);
157       if (msg->attr == NULL) {
158         ssh_set_error_oom(session);
159         sftp_client_message_free(msg);
160         return NULL;
161       }
162       break;
163     case SSH_FXP_FSETSTAT:
164       msg->handle = buffer_get_ssh_string(payload);
165       if (msg->handle == NULL) {
166         ssh_set_error_oom(session);
167         sftp_client_message_free(msg);
168         return NULL;
169       }
170       msg->attr = sftp_parse_attr(sftp, payload, 0);
171       if (msg->attr == NULL) {
172         ssh_set_error_oom(session);
173         sftp_client_message_free(msg);
174         return NULL;
175       }
176       break;
177     case SSH_FXP_LSTAT:
178     case SSH_FXP_STAT:
179       tmp = buffer_get_ssh_string(payload);
180       if (tmp == NULL) {
181         ssh_set_error_oom(session);
182         sftp_client_message_free(msg);
183         return NULL;
184       }
185       msg->filename = ssh_string_to_char(tmp);
186       ssh_string_free(tmp);
187       if (msg->filename == NULL) {
188         ssh_set_error_oom(session);
189         sftp_client_message_free(msg);
190         return NULL;
191       }
192       if(sftp->version > 3) {
193         buffer_get_u32(payload,&msg->flags);
194       }
195       break;
196     case SSH_FXP_OPEN:
197       tmp=buffer_get_ssh_string(payload);
198       if (tmp == NULL) {
199         ssh_set_error_oom(session);
200         sftp_client_message_free(msg);
201         return NULL;
202       }
203       msg->filename = ssh_string_to_char(tmp);
204       ssh_string_free(tmp);
205       if (msg->filename == NULL) {
206         ssh_set_error_oom(session);
207         sftp_client_message_free(msg);
208         return NULL;
209       }
210       buffer_get_u32(payload,&msg->flags);
211       msg->attr = sftp_parse_attr(sftp, payload, 0);
212       if (msg->attr == NULL) {
213         ssh_set_error_oom(session);
214         sftp_client_message_free(msg);
215         return NULL;
216       }
217       break;
218     case SSH_FXP_FSTAT:
219       msg->handle = buffer_get_ssh_string(payload);
220       if (msg->handle == NULL) {
221         ssh_set_error_oom(session);
222         sftp_client_message_free(msg);
223         return NULL;
224       }
225       buffer_get_u32(payload, &msg->flags);
226       break;
227     default:
228       ssh_set_error(sftp->session, SSH_FATAL,
229                     "Received unhandled sftp message %d\n", msg->type);
230       sftp_client_message_free(msg);
231       return NULL;
232   }
233 
234   msg->flags = ntohl(msg->flags);
235   msg->offset = ntohll(msg->offset);
236   msg->len = ntohl(msg->len);
237   sftp_packet_free(packet);
238 
239   return msg;
240 }
241 
sftp_client_message_free(sftp_client_message msg)242 void sftp_client_message_free(sftp_client_message msg) {
243   if (msg == NULL) {
244     return;
245   }
246 
247   SAFE_FREE(msg->filename);
248   ssh_string_free(msg->data);
249   ssh_string_free(msg->handle);
250   sftp_attributes_free(msg->attr);
251 
252   ZERO_STRUCTP(msg);
253   SAFE_FREE(msg);
254 }
255 
sftp_reply_name(sftp_client_message msg,const char * name,sftp_attributes attr)256 int sftp_reply_name(sftp_client_message msg, const char *name,
257     sftp_attributes attr) {
258   ssh_buffer out;
259   ssh_string file;
260 
261   out = ssh_buffer_new();
262   if (out == NULL) {
263     return -1;
264   }
265 
266   file = ssh_string_from_char(name);
267   if (file == NULL) {
268     ssh_buffer_free(out);
269     return -1;
270   }
271 
272   if (buffer_add_u32(out, msg->id) < 0 ||
273       buffer_add_u32(out, htonl(1)) < 0 ||
274       buffer_add_ssh_string(out, file) < 0 ||
275       buffer_add_ssh_string(out, file) < 0 || /* The protocol is broken here between 3 & 4 */
276       buffer_add_attributes(out, attr) < 0 ||
277       sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
278     ssh_buffer_free(out);
279     ssh_string_free(file);
280     return -1;
281   }
282   ssh_buffer_free(out);
283   ssh_string_free(file);
284 
285   return 0;
286 }
287 
sftp_reply_handle(sftp_client_message msg,ssh_string handle)288 int sftp_reply_handle(sftp_client_message msg, ssh_string handle){
289   ssh_buffer out;
290 
291   out = ssh_buffer_new();
292   if (out == NULL) {
293     return -1;
294   }
295 
296   if (buffer_add_u32(out, msg->id) < 0 ||
297       buffer_add_ssh_string(out, handle) < 0 ||
298       sftp_packet_write(msg->sftp, SSH_FXP_HANDLE, out) < 0) {
299     ssh_buffer_free(out);
300     return -1;
301   }
302   ssh_buffer_free(out);
303 
304   return 0;
305 }
306 
sftp_reply_attr(sftp_client_message msg,sftp_attributes attr)307 int sftp_reply_attr(sftp_client_message msg, sftp_attributes attr) {
308   ssh_buffer out;
309 
310   out = ssh_buffer_new();
311   if (out == NULL) {
312     return -1;
313   }
314 
315   if (buffer_add_u32(out, msg->id) < 0 ||
316       buffer_add_attributes(out, attr) < 0 ||
317       sftp_packet_write(msg->sftp, SSH_FXP_ATTRS, out) < 0) {
318     ssh_buffer_free(out);
319     return -1;
320   }
321   ssh_buffer_free(out);
322 
323   return 0;
324 }
325 
sftp_reply_names_add(sftp_client_message msg,const char * file,const char * longname,sftp_attributes attr)326 int sftp_reply_names_add(sftp_client_message msg, const char *file,
327     const char *longname, sftp_attributes attr) {
328   ssh_string name;
329 
330   name = ssh_string_from_char(file);
331   if (name == NULL) {
332     return -1;
333   }
334 
335   if (msg->attrbuf == NULL) {
336     msg->attrbuf = ssh_buffer_new();
337     if (msg->attrbuf == NULL) {
338       ssh_string_free(name);
339       return -1;
340     }
341   }
342 
343   if (buffer_add_ssh_string(msg->attrbuf, name) < 0) {
344     ssh_string_free(name);
345     return -1;
346   }
347 
348   ssh_string_free(name);
349   name = ssh_string_from_char(longname);
350   if (name == NULL) {
351     return -1;
352   }
353   if (buffer_add_ssh_string(msg->attrbuf,name) < 0 ||
354       buffer_add_attributes(msg->attrbuf,attr) < 0) {
355     ssh_string_free(name);
356     return -1;
357   }
358   ssh_string_free(name);
359   msg->attr_num++;
360 
361   return 0;
362 }
363 
sftp_reply_names(sftp_client_message msg)364 int sftp_reply_names(sftp_client_message msg) {
365   ssh_buffer out;
366 
367   out = ssh_buffer_new();
368   if (out == NULL) {
369     ssh_buffer_free(msg->attrbuf);
370     return -1;
371   }
372 
373   if (buffer_add_u32(out, msg->id) < 0 ||
374       buffer_add_u32(out, htonl(msg->attr_num)) < 0 ||
375       buffer_add_data(out, buffer_get_rest(msg->attrbuf),
376         buffer_get_rest_len(msg->attrbuf)) < 0 ||
377       sftp_packet_write(msg->sftp, SSH_FXP_NAME, out) < 0) {
378     ssh_buffer_free(out);
379     ssh_buffer_free(msg->attrbuf);
380     return -1;
381   }
382 
383   ssh_buffer_free(out);
384   ssh_buffer_free(msg->attrbuf);
385 
386   msg->attr_num = 0;
387   msg->attrbuf = NULL;
388 
389   return 0;
390 }
391 
sftp_reply_status(sftp_client_message msg,uint32_t status,const char * message)392 int sftp_reply_status(sftp_client_message msg, uint32_t status,
393     const char *message) {
394   ssh_buffer out;
395   ssh_string s;
396 
397   out = ssh_buffer_new();
398   if (out == NULL) {
399     return -1;
400   }
401 
402   s = ssh_string_from_char(message ? message : "");
403   if (s == NULL) {
404     ssh_buffer_free(out);
405     return -1;
406   }
407 
408   if (buffer_add_u32(out, msg->id) < 0 ||
409       buffer_add_u32(out, htonl(status)) < 0 ||
410       buffer_add_ssh_string(out, s) < 0 ||
411       buffer_add_u32(out, 0) < 0 || /* language string */
412       sftp_packet_write(msg->sftp, SSH_FXP_STATUS, out) < 0) {
413     ssh_buffer_free(out);
414     ssh_string_free(s);
415     return -1;
416   }
417 
418   ssh_buffer_free(out);
419   ssh_string_free(s);
420 
421   return 0;
422 }
423 
sftp_reply_data(sftp_client_message msg,const void * data,int len)424 int sftp_reply_data(sftp_client_message msg, const void *data, int len) {
425   ssh_buffer out;
426 
427   out = ssh_buffer_new();
428   if (out == NULL) {
429     return -1;
430   }
431 
432   if (buffer_add_u32(out, msg->id) < 0 ||
433       buffer_add_u32(out, ntohl(len)) < 0 ||
434       buffer_add_data(out, data, len) < 0 ||
435       sftp_packet_write(msg->sftp, SSH_FXP_DATA, out) < 0) {
436     ssh_buffer_free(out);
437     return -1;
438   }
439   ssh_buffer_free(out);
440 
441   return 0;
442 }
443 
444 /*
445  * This function will return you a new handle to give the client.
446  * the function accepts an info that can be retrieved later with
447  * the handle. Care is given that a corrupted handle won't give a
448  * valid info (or worse).
449  */
sftp_handle_alloc(sftp_session sftp,void * info)450 ssh_string sftp_handle_alloc(sftp_session sftp, void *info) {
451   ssh_string ret;
452   uint32_t val;
453   int i;
454 
455   if (sftp->handles == NULL) {
456     sftp->handles = malloc(sizeof(void *) * SFTP_HANDLES);
457     if (sftp->handles == NULL) {
458       return NULL;
459     }
460     memset(sftp->handles, 0, sizeof(void *) * SFTP_HANDLES);
461   }
462 
463   for (i = 0; i < SFTP_HANDLES; i++) {
464     if (sftp->handles[i] == NULL) {
465       break;
466     }
467   }
468 
469   if (i == SFTP_HANDLES) {
470     return NULL; /* no handle available */
471   }
472 
473   val = i;
474   ret = ssh_string_new(4);
475   if (ret == NULL) {
476     return NULL;
477   }
478 
479   memcpy(ssh_string_data(ret), &val, sizeof(uint32_t));
480   sftp->handles[i] = info;
481 
482   return ret;
483 }
484 
sftp_handle(sftp_session sftp,ssh_string handle)485 void *sftp_handle(sftp_session sftp, ssh_string handle){
486   uint32_t val;
487 
488   if (sftp->handles == NULL) {
489     return NULL;
490   }
491 
492   if (ssh_string_len(handle) != sizeof(uint32_t)) {
493     return NULL;
494   }
495 
496   memcpy(&val, ssh_string_data(handle), sizeof(uint32_t));
497 
498   if (val > SFTP_HANDLES) {
499     return NULL;
500   }
501 
502   return sftp->handles[val];
503 }
504 
sftp_handle_remove(sftp_session sftp,void * handle)505 void sftp_handle_remove(sftp_session sftp, void *handle) {
506   int i;
507 
508   for (i = 0; i < SFTP_HANDLES; i++) {
509     if (sftp->handles[i] == handle) {
510       sftp->handles[i] = NULL;
511       break;
512     }
513   }
514 }
515 
516 /* vim: set ts=2 sw=2 et cindent: */
517