]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
Run entire source base through GNU indent to tidy up the varying
[PuTTY.git] / sftp.c
1 /*
2  * sftp.c: SFTP generic client code.
3  */
4
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8 #include <assert.h>
9
10 #include "int64.h"
11 #include "sftp.h"
12
13 #define smalloc malloc
14 #define srealloc realloc
15 #define sfree free
16
17 #define GET_32BIT(cp) \
18     (((unsigned long)(unsigned char)(cp)[0] << 24) | \
19     ((unsigned long)(unsigned char)(cp)[1] << 16) | \
20     ((unsigned long)(unsigned char)(cp)[2] << 8) | \
21     ((unsigned long)(unsigned char)(cp)[3]))
22
23 #define PUT_32BIT(cp, value) { \
24     (cp)[0] = (unsigned char)((value) >> 24); \
25     (cp)[1] = (unsigned char)((value) >> 16); \
26     (cp)[2] = (unsigned char)((value) >> 8); \
27     (cp)[3] = (unsigned char)(value); }
28
29 struct sftp_packet {
30     char *data;
31     int length, maxlen;
32     int savedpos;
33     int type;
34 };
35
36 /* ----------------------------------------------------------------------
37  * SFTP packet construction functions.
38  */
39 static void sftp_pkt_ensure(struct sftp_packet *pkt, int length)
40 {
41     if (pkt->maxlen < length) {
42         pkt->maxlen = length + 256;
43         pkt->data = srealloc(pkt->data, pkt->maxlen);
44     }
45 }
46 static void sftp_pkt_adddata(struct sftp_packet *pkt, void *data, int len)
47 {
48     pkt->length += len;
49     sftp_pkt_ensure(pkt, pkt->length);
50     memcpy(pkt->data + pkt->length - len, data, len);
51 }
52 static void sftp_pkt_addbyte(struct sftp_packet *pkt, unsigned char byte)
53 {
54     sftp_pkt_adddata(pkt, &byte, 1);
55 }
56 static struct sftp_packet *sftp_pkt_init(int pkt_type)
57 {
58     struct sftp_packet *pkt;
59     pkt = smalloc(sizeof(struct sftp_packet));
60     pkt->data = NULL;
61     pkt->savedpos = -1;
62     pkt->length = 0;
63     pkt->maxlen = 0;
64     sftp_pkt_addbyte(pkt, (unsigned char) pkt_type);
65     return pkt;
66 }
67 static void sftp_pkt_addbool(struct sftp_packet *pkt, unsigned char value)
68 {
69     sftp_pkt_adddata(pkt, &value, 1);
70 }
71 static void sftp_pkt_adduint32(struct sftp_packet *pkt,
72                                unsigned long value)
73 {
74     unsigned char x[4];
75     PUT_32BIT(x, value);
76     sftp_pkt_adddata(pkt, x, 4);
77 }
78 static void sftp_pkt_adduint64(struct sftp_packet *pkt, uint64 value)
79 {
80     unsigned char x[8];
81     PUT_32BIT(x, value.hi);
82     PUT_32BIT(x + 4, value.lo);
83     sftp_pkt_adddata(pkt, x, 8);
84 }
85 static void sftp_pkt_addstring_start(struct sftp_packet *pkt)
86 {
87     sftp_pkt_adduint32(pkt, 0);
88     pkt->savedpos = pkt->length;
89 }
90 static void sftp_pkt_addstring_str(struct sftp_packet *pkt, char *data)
91 {
92     sftp_pkt_adddata(pkt, data, strlen(data));
93     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
94 }
95 static void sftp_pkt_addstring_data(struct sftp_packet *pkt,
96                                     char *data, int len)
97 {
98     sftp_pkt_adddata(pkt, data, len);
99     PUT_32BIT(pkt->data + pkt->savedpos - 4, pkt->length - pkt->savedpos);
100 }
101 static void sftp_pkt_addstring(struct sftp_packet *pkt, char *data)
102 {
103     sftp_pkt_addstring_start(pkt);
104     sftp_pkt_addstring_str(pkt, data);
105 }
106
107 /* ----------------------------------------------------------------------
108  * SFTP packet decode functions.
109  */
110
111 static unsigned char sftp_pkt_getbyte(struct sftp_packet *pkt)
112 {
113     unsigned char value;
114     if (pkt->length - pkt->savedpos < 1)
115         return 0;                      /* arrgh, no way to decline (FIXME?) */
116     value = (unsigned char) pkt->data[pkt->savedpos];
117     pkt->savedpos++;
118     return value;
119 }
120 static unsigned long sftp_pkt_getuint32(struct sftp_packet *pkt)
121 {
122     unsigned long value;
123     if (pkt->length - pkt->savedpos < 4)
124         return 0;                      /* arrgh, no way to decline (FIXME?) */
125     value = GET_32BIT(pkt->data + pkt->savedpos);
126     pkt->savedpos += 4;
127     return value;
128 }
129 static void sftp_pkt_getstring(struct sftp_packet *pkt,
130                                char **p, int *length)
131 {
132     *p = NULL;
133     if (pkt->length - pkt->savedpos < 4)
134         return;
135     *length = GET_32BIT(pkt->data + pkt->savedpos);
136     pkt->savedpos += 4;
137     if (pkt->length - pkt->savedpos < *length)
138         return;
139     *p = pkt->data + pkt->savedpos;
140     pkt->savedpos += *length;
141 }
142 static struct fxp_attrs sftp_pkt_getattrs(struct sftp_packet *pkt)
143 {
144     struct fxp_attrs ret;
145     ret.flags = sftp_pkt_getuint32(pkt);
146     if (ret.flags & SSH_FILEXFER_ATTR_SIZE) {
147         unsigned long hi, lo;
148         hi = sftp_pkt_getuint32(pkt);
149         lo = sftp_pkt_getuint32(pkt);
150         ret.size = uint64_make(hi, lo);
151     }
152     if (ret.flags & SSH_FILEXFER_ATTR_UIDGID) {
153         ret.uid = sftp_pkt_getuint32(pkt);
154         ret.gid = sftp_pkt_getuint32(pkt);
155     }
156     if (ret.flags & SSH_FILEXFER_ATTR_PERMISSIONS) {
157         ret.permissions = sftp_pkt_getuint32(pkt);
158     }
159     if (ret.flags & SSH_FILEXFER_ATTR_ACMODTIME) {
160         ret.atime = sftp_pkt_getuint32(pkt);
161         ret.mtime = sftp_pkt_getuint32(pkt);
162     }
163     if (ret.flags & SSH_FILEXFER_ATTR_EXTENDED) {
164         int count;
165         count = sftp_pkt_getuint32(pkt);
166         while (count--) {
167             char *str;
168             int len;
169             /*
170              * We should try to analyse these, if we ever find one
171              * we recognise.
172              */
173             sftp_pkt_getstring(pkt, &str, &len);
174             sftp_pkt_getstring(pkt, &str, &len);
175         }
176     }
177     return ret;
178 }
179 static void sftp_pkt_free(struct sftp_packet *pkt)
180 {
181     if (pkt->data)
182         sfree(pkt->data);
183     sfree(pkt);
184 }
185
186 /* ----------------------------------------------------------------------
187  * Send and receive packet functions.
188  */
189 int sftp_send(struct sftp_packet *pkt)
190 {
191     int ret;
192     char x[4];
193     PUT_32BIT(x, pkt->length);
194     ret = (sftp_senddata(x, 4) && sftp_senddata(pkt->data, pkt->length));
195     sftp_pkt_free(pkt);
196     return ret;
197 }
198 struct sftp_packet *sftp_recv(void)
199 {
200     struct sftp_packet *pkt;
201     char x[4];
202
203     if (!sftp_recvdata(x, 4))
204         return NULL;
205
206     pkt = smalloc(sizeof(struct sftp_packet));
207     pkt->savedpos = 0;
208     pkt->length = pkt->maxlen = GET_32BIT(x);
209     pkt->data = smalloc(pkt->length);
210
211     if (!sftp_recvdata(pkt->data, pkt->length)) {
212         sftp_pkt_free(pkt);
213         return NULL;
214     }
215
216     pkt->type = sftp_pkt_getbyte(pkt);
217
218     return pkt;
219 }
220
221 /* ----------------------------------------------------------------------
222  * String handling routines.
223  */
224
225 static char *mkstr(char *s, int len)
226 {
227     char *p = smalloc(len + 1);
228     memcpy(p, s, len);
229     p[len] = '\0';
230     return p;
231 }
232
233 /* ----------------------------------------------------------------------
234  * SFTP primitives.
235  */
236
237 static const char *fxp_error_message;
238 static int fxp_errtype;
239
240 /*
241  * Deal with (and free) an FXP_STATUS packet. Return 1 if
242  * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
243  * Also place the status into fxp_errtype.
244  */
245 static int fxp_got_status(struct sftp_packet *pktin)
246 {
247     static const char *const messages[] = {
248         /* SSH_FX_OK. The only time we will display a _message_ for this
249          * is if we were expecting something other than FXP_STATUS on
250          * success, so this is actually an error message! */
251         "unexpected OK response",
252         "end of file",
253         "no such file or directory",
254         "permission denied",
255         "failure",
256         "bad message",
257         "no connection",
258         "connection lost",
259         "operation unsupported",
260     };
261
262     if (pktin->type != SSH_FXP_STATUS) {
263         fxp_error_message = "expected FXP_STATUS packet";
264         fxp_errtype = -1;
265     } else {
266         fxp_errtype = sftp_pkt_getuint32(pktin);
267         if (fxp_errtype < 0 ||
268             fxp_errtype >= sizeof(messages) / sizeof(*messages))
269                 fxp_error_message = "unknown error code";
270         else
271             fxp_error_message = messages[fxp_errtype];
272     }
273
274     if (fxp_errtype == SSH_FX_OK)
275         return 1;
276     else if (fxp_errtype == SSH_FX_EOF)
277         return 0;
278     else
279         return -1;
280 }
281
282 static void fxp_internal_error(char *msg)
283 {
284     fxp_error_message = msg;
285     fxp_errtype = -1;
286 }
287
288 const char *fxp_error(void)
289 {
290     return fxp_error_message;
291 }
292
293 int fxp_error_type(void)
294 {
295     return fxp_errtype;
296 }
297
298 /*
299  * Perform exchange of init/version packets. Return 0 on failure.
300  */
301 int fxp_init(void)
302 {
303     struct sftp_packet *pktout, *pktin;
304     int remotever;
305
306     pktout = sftp_pkt_init(SSH_FXP_INIT);
307     sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
308     sftp_send(pktout);
309
310     pktin = sftp_recv();
311     if (!pktin) {
312         fxp_internal_error("could not connect");
313         return 0;
314     }
315     if (pktin->type != SSH_FXP_VERSION) {
316         fxp_internal_error("did not receive FXP_VERSION");
317         return 0;
318     }
319     remotever = sftp_pkt_getuint32(pktin);
320     if (remotever > SFTP_PROTO_VERSION) {
321         fxp_internal_error
322             ("remote protocol is more advanced than we support");
323         return 0;
324     }
325     /*
326      * In principle, this packet might also contain extension-
327      * string pairs. We should work through them and look for any
328      * we recognise. In practice we don't currently do so because
329      * we know we don't recognise _any_.
330      */
331     sftp_pkt_free(pktin);
332
333     return 1;
334 }
335
336 /*
337  * Canonify a pathname.
338  */
339 char *fxp_realpath(char *path)
340 {
341     struct sftp_packet *pktin, *pktout;
342     int id;
343
344     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
345     sftp_pkt_adduint32(pktout, 0x123); /* request id */
346     sftp_pkt_addstring_start(pktout);
347     sftp_pkt_addstring_str(pktout, path);
348     sftp_send(pktout);
349     pktin = sftp_recv();
350     id = sftp_pkt_getuint32(pktin);
351     if (id != 0x123) {
352         fxp_internal_error("request ID mismatch\n");
353         return NULL;
354     }
355     if (pktin->type == SSH_FXP_NAME) {
356         int count;
357         char *path;
358         int len;
359
360         count = sftp_pkt_getuint32(pktin);
361         if (count != 1) {
362             fxp_internal_error("REALPATH returned name count != 1\n");
363             return NULL;
364         }
365         sftp_pkt_getstring(pktin, &path, &len);
366         if (!path) {
367             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
368             return NULL;
369         }
370         path = mkstr(path, len);
371         sftp_pkt_free(pktin);
372         return path;
373     } else {
374         fxp_got_status(pktin);
375         return NULL;
376     }
377 }
378
379 /*
380  * Open a file.
381  */
382 struct fxp_handle *fxp_open(char *path, int type)
383 {
384     struct sftp_packet *pktin, *pktout;
385     int id;
386
387     pktout = sftp_pkt_init(SSH_FXP_OPEN);
388     sftp_pkt_adduint32(pktout, 0x567); /* request id */
389     sftp_pkt_addstring(pktout, path);
390     sftp_pkt_adduint32(pktout, type);
391     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
392     sftp_send(pktout);
393     pktin = sftp_recv();
394     id = sftp_pkt_getuint32(pktin);
395     if (id != 0x567) {
396         fxp_internal_error("request ID mismatch\n");
397         return NULL;
398     }
399     if (pktin->type == SSH_FXP_HANDLE) {
400         char *hstring;
401         struct fxp_handle *handle;
402         int len;
403
404         sftp_pkt_getstring(pktin, &hstring, &len);
405         if (!hstring) {
406             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
407             return NULL;
408         }
409         handle = smalloc(sizeof(struct fxp_handle));
410         handle->hstring = mkstr(hstring, len);
411         handle->hlen = len;
412         sftp_pkt_free(pktin);
413         return handle;
414     } else {
415         fxp_got_status(pktin);
416         return NULL;
417     }
418 }
419
420 /*
421  * Open a directory.
422  */
423 struct fxp_handle *fxp_opendir(char *path)
424 {
425     struct sftp_packet *pktin, *pktout;
426     int id;
427
428     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
429     sftp_pkt_adduint32(pktout, 0x456); /* request id */
430     sftp_pkt_addstring(pktout, path);
431     sftp_send(pktout);
432     pktin = sftp_recv();
433     id = sftp_pkt_getuint32(pktin);
434     if (id != 0x456) {
435         fxp_internal_error("request ID mismatch\n");
436         return NULL;
437     }
438     if (pktin->type == SSH_FXP_HANDLE) {
439         char *hstring;
440         struct fxp_handle *handle;
441         int len;
442
443         sftp_pkt_getstring(pktin, &hstring, &len);
444         if (!hstring) {
445             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
446             return NULL;
447         }
448         handle = smalloc(sizeof(struct fxp_handle));
449         handle->hstring = mkstr(hstring, len);
450         handle->hlen = len;
451         sftp_pkt_free(pktin);
452         return handle;
453     } else {
454         fxp_got_status(pktin);
455         return NULL;
456     }
457 }
458
459 /*
460  * Close a file/dir.
461  */
462 void fxp_close(struct fxp_handle *handle)
463 {
464     struct sftp_packet *pktin, *pktout;
465     int id;
466
467     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
468     sftp_pkt_adduint32(pktout, 0x789); /* request id */
469     sftp_pkt_addstring_start(pktout);
470     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
471     sftp_send(pktout);
472     pktin = sftp_recv();
473     id = sftp_pkt_getuint32(pktin);
474     if (id != 0x789) {
475         fxp_internal_error("request ID mismatch\n");
476         return;
477     }
478     fxp_got_status(pktin);
479     sfree(handle->hstring);
480     sfree(handle);
481 }
482
483 /*
484  * Read from a file. Returns the number of bytes read, or -1 on an
485  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
486  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
487  * error indicator. It might even depend on the SFTP server.)
488  */
489 int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset,
490              int len)
491 {
492     struct sftp_packet *pktin, *pktout;
493     int id;
494
495     pktout = sftp_pkt_init(SSH_FXP_READ);
496     sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
497     sftp_pkt_addstring_start(pktout);
498     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
499     sftp_pkt_adduint64(pktout, offset);
500     sftp_pkt_adduint32(pktout, len);
501     sftp_send(pktout);
502     pktin = sftp_recv();
503     id = sftp_pkt_getuint32(pktin);
504     if (id != 0xBCD) {
505         fxp_internal_error("request ID mismatch");
506         return -1;
507     }
508     if (pktin->type == SSH_FXP_DATA) {
509         char *str;
510         int rlen;
511
512         sftp_pkt_getstring(pktin, &str, &rlen);
513
514         if (rlen > len || rlen < 0) {
515             fxp_internal_error("READ returned more bytes than requested");
516             return -1;
517         }
518
519         memcpy(buffer, str, rlen);
520         sfree(pktin);
521         return rlen;
522     } else {
523         fxp_got_status(pktin);
524         return -1;
525     }
526 }
527
528 /*
529  * Read from a directory.
530  */
531 struct fxp_names *fxp_readdir(struct fxp_handle *handle)
532 {
533     struct sftp_packet *pktin, *pktout;
534     int id;
535
536     pktout = sftp_pkt_init(SSH_FXP_READDIR);
537     sftp_pkt_adduint32(pktout, 0xABC); /* request id */
538     sftp_pkt_addstring_start(pktout);
539     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
540     sftp_send(pktout);
541     pktin = sftp_recv();
542     id = sftp_pkt_getuint32(pktin);
543     if (id != 0xABC) {
544         fxp_internal_error("request ID mismatch\n");
545         return NULL;
546     }
547     if (pktin->type == SSH_FXP_NAME) {
548         struct fxp_names *ret;
549         int i;
550         ret = smalloc(sizeof(struct fxp_names));
551         ret->nnames = sftp_pkt_getuint32(pktin);
552         ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
553         for (i = 0; i < ret->nnames; i++) {
554             char *str;
555             int len;
556             sftp_pkt_getstring(pktin, &str, &len);
557             ret->names[i].filename = mkstr(str, len);
558             sftp_pkt_getstring(pktin, &str, &len);
559             ret->names[i].longname = mkstr(str, len);
560             ret->names[i].attrs = sftp_pkt_getattrs(pktin);
561         }
562         return ret;
563     } else {
564         fxp_got_status(pktin);
565         return NULL;
566     }
567 }
568
569 /*
570  * Write to a file. Returns 0 on error, 1 on OK.
571  */
572 int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset,
573               int len)
574 {
575     struct sftp_packet *pktin, *pktout;
576     int id;
577
578     pktout = sftp_pkt_init(SSH_FXP_WRITE);
579     sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
580     sftp_pkt_addstring_start(pktout);
581     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
582     sftp_pkt_adduint64(pktout, offset);
583     sftp_pkt_addstring_start(pktout);
584     sftp_pkt_addstring_data(pktout, buffer, len);
585     sftp_send(pktout);
586     pktin = sftp_recv();
587     id = sftp_pkt_getuint32(pktin);
588     if (id != 0xDCB) {
589         fxp_internal_error("request ID mismatch\n");
590         return 0;
591     }
592     fxp_got_status(pktin);
593     return fxp_errtype == SSH_FX_OK;
594 }
595
596 /*
597  * Free up an fxp_names structure.
598  */
599 void fxp_free_names(struct fxp_names *names)
600 {
601     int i;
602
603     for (i = 0; i < names->nnames; i++) {
604         sfree(names->names[i].filename);
605         sfree(names->names[i].longname);
606     }
607     sfree(names->names);
608     sfree(names);
609 }