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