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