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