]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
Patches to prevent a couple of silly crashes
[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) {
290         fxp_internal_error("could not connect");
291         return 0;
292     }
293     if (pktin->type != SSH_FXP_VERSION) {
294         fxp_internal_error("did not receive FXP_VERSION");
295         return 0;
296     }
297     remotever = sftp_pkt_getuint32(pktin);
298     if (remotever > SFTP_PROTO_VERSION) {
299         fxp_internal_error("remote protocol is more advanced than we support");
300         return 0;
301     }
302     /*
303      * In principle, this packet might also contain extension-
304      * string pairs. We should work through them and look for any
305      * we recognise. In practice we don't currently do so because
306      * we know we don't recognise _any_.
307      */
308     sftp_pkt_free(pktin);
309
310     return 1;
311 }
312
313 /*
314  * Canonify a pathname.
315  */
316 char *fxp_realpath(char *path) {
317     struct sftp_packet *pktin, *pktout;
318     int id;
319
320     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
321     sftp_pkt_adduint32(pktout, 0x123); /* request id */
322     sftp_pkt_addstring_start(pktout);
323     sftp_pkt_addstring_str(pktout, path);
324     sftp_send(pktout);
325     pktin = sftp_recv();
326     id = sftp_pkt_getuint32(pktin);
327     if (id != 0x123) {
328         fxp_internal_error("request ID mismatch\n");
329         return NULL;
330     }
331     if (pktin->type == SSH_FXP_NAME) {
332         int count;
333         char *path;
334         int len;
335
336         count = sftp_pkt_getuint32(pktin);
337         if (count != 1) {
338             fxp_internal_error("REALPATH returned name count != 1\n");
339             return NULL;
340         }
341         sftp_pkt_getstring(pktin, &path, &len);
342         if (!path) {
343             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
344             return NULL;
345         }
346         path = mkstr(path, len);
347         sftp_pkt_free(pktin);
348         return path;
349     } else {
350         fxp_got_status(pktin);
351         return NULL;
352     }
353 }
354
355 /*
356  * Open a file.
357  */
358 struct fxp_handle *fxp_open(char *path, int type) {
359     struct sftp_packet *pktin, *pktout;
360     int id;
361
362     pktout = sftp_pkt_init(SSH_FXP_OPEN);
363     sftp_pkt_adduint32(pktout, 0x567); /* request id */
364     sftp_pkt_addstring(pktout, path);
365     sftp_pkt_adduint32(pktout, type);
366     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
367     sftp_send(pktout);
368     pktin = sftp_recv();
369     id = sftp_pkt_getuint32(pktin);
370     if (id != 0x567) {
371         fxp_internal_error("request ID mismatch\n");
372         return NULL;
373     }
374     if (pktin->type == SSH_FXP_HANDLE) {
375         int count;
376         char *hstring;
377         struct fxp_handle *handle;
378         int len;
379
380         sftp_pkt_getstring(pktin, &hstring, &len);
381         if (!hstring) {
382             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
383             return NULL;
384         }
385         handle = smalloc(sizeof(struct fxp_handle));
386         handle->hstring = mkstr(hstring, len);
387         handle->hlen = len;
388         sftp_pkt_free(pktin);
389         return handle;
390     } else {
391         fxp_got_status(pktin);
392         return NULL;
393     }
394 }
395
396 /*
397  * Open a directory.
398  */
399 struct fxp_handle *fxp_opendir(char *path) {
400     struct sftp_packet *pktin, *pktout;
401     int id;
402
403     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
404     sftp_pkt_adduint32(pktout, 0x456); /* request id */
405     sftp_pkt_addstring(pktout, path);
406     sftp_send(pktout);
407     pktin = sftp_recv();
408     id = sftp_pkt_getuint32(pktin);
409     if (id != 0x456) {
410         fxp_internal_error("request ID mismatch\n");
411         return NULL;
412     }
413     if (pktin->type == SSH_FXP_HANDLE) {
414         int count;
415         char *hstring;
416         struct fxp_handle *handle;
417         int len;
418
419         sftp_pkt_getstring(pktin, &hstring, &len);
420         if (!hstring) {
421             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
422             return NULL;
423         }
424         handle = smalloc(sizeof(struct fxp_handle));
425         handle->hstring = mkstr(hstring, len);
426         handle->hlen = len;
427         sftp_pkt_free(pktin);
428         return handle;
429     } else {
430         fxp_got_status(pktin);
431         return NULL;
432     }
433 }
434
435 /*
436  * Close a file/dir.
437  */
438 void fxp_close(struct fxp_handle *handle) {
439     struct sftp_packet *pktin, *pktout;
440     int id;
441
442     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
443     sftp_pkt_adduint32(pktout, 0x789); /* request id */
444     sftp_pkt_addstring_start(pktout);
445     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
446     sftp_send(pktout);
447     pktin = sftp_recv();
448     id = sftp_pkt_getuint32(pktin);
449     if (id != 0x789) {
450         fxp_internal_error("request ID mismatch\n");
451         return;
452     }
453     fxp_got_status(pktin);
454     sfree(handle->hstring);
455     sfree(handle);
456 }
457
458 /*
459  * Read from a file. Returns the number of bytes read, or -1 on an
460  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
461  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
462  * error indicator. It might even depend on the SFTP server.)
463  */
464 int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset, int len) {
465     struct sftp_packet *pktin, *pktout;
466     int id;
467
468     pktout = sftp_pkt_init(SSH_FXP_READ);
469     sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
470     sftp_pkt_addstring_start(pktout);
471     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
472     sftp_pkt_adduint64(pktout, offset);
473     sftp_pkt_adduint32(pktout, len);
474     sftp_send(pktout);
475     pktin = sftp_recv();
476     id = sftp_pkt_getuint32(pktin);
477     if (id != 0xBCD) {
478         fxp_internal_error("request ID mismatch");
479         return -1;
480     }
481     if (pktin->type == SSH_FXP_DATA) {
482         char *str;
483         int rlen;
484
485         sftp_pkt_getstring(pktin, &str, &rlen);
486
487         if (rlen > len || rlen < 0) {
488             fxp_internal_error("READ returned more bytes than requested");
489             return -1;
490         }
491
492         memcpy(buffer, str, rlen);
493         sfree(pktin);
494         return rlen;
495     } else {
496         fxp_got_status(pktin);
497         return -1;
498     }
499 }
500
501 /*
502  * Read from a directory.
503  */
504 struct fxp_names *fxp_readdir(struct fxp_handle *handle) {
505     struct sftp_packet *pktin, *pktout;
506     int id;
507
508     pktout = sftp_pkt_init(SSH_FXP_READDIR);
509     sftp_pkt_adduint32(pktout, 0xABC); /* request id */
510     sftp_pkt_addstring_start(pktout);
511     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
512     sftp_send(pktout);
513     pktin = sftp_recv();
514     id = sftp_pkt_getuint32(pktin);
515     if (id != 0xABC) {
516         fxp_internal_error("request ID mismatch\n");
517         return NULL;
518     }
519     if (pktin->type == SSH_FXP_NAME) {
520         struct fxp_names *ret;
521         int i;
522         ret = smalloc(sizeof(struct fxp_names));
523         ret->nnames = sftp_pkt_getuint32(pktin);
524         ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
525         for (i = 0; i < ret->nnames; i++) {
526             char *str;
527             int len;
528             sftp_pkt_getstring(pktin, &str, &len);
529             ret->names[i].filename = mkstr(str, len);
530             sftp_pkt_getstring(pktin, &str, &len);
531             ret->names[i].longname = mkstr(str, len);
532             ret->names[i].attrs = sftp_pkt_getattrs(pktin);
533         }
534         return ret;
535     } else {
536         fxp_got_status(pktin);
537         return NULL;
538     }
539 }
540
541 /*
542  * Write to a file. Returns 0 on error, 1 on OK.
543  */
544 int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset, int len) {
545     struct sftp_packet *pktin, *pktout;
546     int id;
547
548     pktout = sftp_pkt_init(SSH_FXP_WRITE);
549     sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
550     sftp_pkt_addstring_start(pktout);
551     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
552     sftp_pkt_adduint64(pktout, offset);
553     sftp_pkt_addstring_start(pktout);
554     sftp_pkt_addstring_data(pktout, buffer, len);
555     sftp_send(pktout);
556     pktin = sftp_recv();
557     id = sftp_pkt_getuint32(pktin);
558     if (id != 0xDCB) {
559         fxp_internal_error("request ID mismatch\n");
560         return NULL;
561     }
562     fxp_got_status(pktin);
563     return fxp_errtype == SSH_FX_OK;
564 }
565
566 /*
567  * Free up an fxp_names structure.
568  */
569 void fxp_free_names(struct fxp_names *names) {
570     int i;
571
572     for (i = 0; i < names->nnames; i++) {
573         sfree(names->names[i].filename);
574         sfree(names->names[i].longname);
575     }
576     sfree(names->names);
577     sfree(names);
578 }