]> asedeno.scripts.mit.edu Git - PuTTY.git/blob - sftp.c
Fix various trivial compiler warnings
[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 char 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
186     if (!sftp_recvdata(x, 4))
187         return NULL;
188
189     pkt = smalloc(sizeof(struct sftp_packet));
190     pkt->savedpos = 0;
191     pkt->length = pkt->maxlen = GET_32BIT(x);
192     pkt->data = smalloc(pkt->length);
193
194     if (!sftp_recvdata(pkt->data, pkt->length)) {
195         sftp_pkt_free(pkt);
196         return NULL;
197     }
198
199     pkt->type = sftp_pkt_getbyte(pkt);
200
201     return pkt;
202 }
203
204 /* ----------------------------------------------------------------------
205  * String handling routines.
206  */
207
208 static char *mkstr(char *s, int len) {
209     char *p = smalloc(len+1);
210     memcpy(p, s, len);
211     p[len] = '\0';
212     return p;
213 }
214
215 /* ----------------------------------------------------------------------
216  * SFTP primitives.
217  */
218
219 static const char *fxp_error_message;
220 static int fxp_errtype;
221
222 /*
223  * Deal with (and free) an FXP_STATUS packet. Return 1 if
224  * SSH_FX_OK, 0 if SSH_FX_EOF, and -1 for anything else (error).
225  * Also place the status into fxp_errtype.
226  */
227 static int fxp_got_status(struct sftp_packet *pktin) {
228     static const char *const messages[] = {
229         /* SSH_FX_OK. The only time we will display a _message_ for this
230          * is if we were expecting something other than FXP_STATUS on
231          * success, so this is actually an error message! */
232         "unexpected OK response",
233         "end of file",
234         "no such file or directory",
235         "permission denied",
236         "failure",
237         "bad message",
238         "no connection",
239         "connection lost",
240         "operation unsupported",
241     };
242
243     if (pktin->type != SSH_FXP_STATUS) {
244         fxp_error_message = "expected FXP_STATUS packet";
245         fxp_errtype = -1;
246     } else {
247         fxp_errtype = sftp_pkt_getuint32(pktin);
248         if (fxp_errtype < 0 ||
249             fxp_errtype >= sizeof(messages)/sizeof(*messages))
250             fxp_error_message = "unknown error code";
251         else
252             fxp_error_message = messages[fxp_errtype];
253     }
254
255     if (fxp_errtype == SSH_FX_OK)
256         return 1;
257     else if (fxp_errtype == SSH_FX_EOF)
258         return 0;
259     else
260         return -1;
261 }
262
263 static void fxp_internal_error(char *msg) {
264     fxp_error_message = msg;
265     fxp_errtype = -1;
266 }
267
268 const char *fxp_error(void) {
269     return fxp_error_message;
270 }
271
272 int fxp_error_type(void) {
273     return fxp_errtype;
274 }
275
276 /*
277  * Perform exchange of init/version packets. Return 0 on failure.
278  */
279 int fxp_init(void) {
280     struct sftp_packet *pktout, *pktin;
281     int remotever;
282
283     pktout = sftp_pkt_init(SSH_FXP_INIT);
284     sftp_pkt_adduint32(pktout, SFTP_PROTO_VERSION);
285     sftp_send(pktout);
286
287     pktin = sftp_recv();
288     if (!pktin) {
289         fxp_internal_error("could not connect");
290         return 0;
291     }
292     if (pktin->type != SSH_FXP_VERSION) {
293         fxp_internal_error("did not receive FXP_VERSION");
294         return 0;
295     }
296     remotever = sftp_pkt_getuint32(pktin);
297     if (remotever > SFTP_PROTO_VERSION) {
298         fxp_internal_error("remote protocol is more advanced than we support");
299         return 0;
300     }
301     /*
302      * In principle, this packet might also contain extension-
303      * string pairs. We should work through them and look for any
304      * we recognise. In practice we don't currently do so because
305      * we know we don't recognise _any_.
306      */
307     sftp_pkt_free(pktin);
308
309     return 1;
310 }
311
312 /*
313  * Canonify a pathname.
314  */
315 char *fxp_realpath(char *path) {
316     struct sftp_packet *pktin, *pktout;
317     int id;
318
319     pktout = sftp_pkt_init(SSH_FXP_REALPATH);
320     sftp_pkt_adduint32(pktout, 0x123); /* request id */
321     sftp_pkt_addstring_start(pktout);
322     sftp_pkt_addstring_str(pktout, path);
323     sftp_send(pktout);
324     pktin = sftp_recv();
325     id = sftp_pkt_getuint32(pktin);
326     if (id != 0x123) {
327         fxp_internal_error("request ID mismatch\n");
328         return NULL;
329     }
330     if (pktin->type == SSH_FXP_NAME) {
331         int count;
332         char *path;
333         int len;
334
335         count = sftp_pkt_getuint32(pktin);
336         if (count != 1) {
337             fxp_internal_error("REALPATH returned name count != 1\n");
338             return NULL;
339         }
340         sftp_pkt_getstring(pktin, &path, &len);
341         if (!path) {
342             fxp_internal_error("REALPATH returned malformed FXP_NAME\n");
343             return NULL;
344         }
345         path = mkstr(path, len);
346         sftp_pkt_free(pktin);
347         return path;
348     } else {
349         fxp_got_status(pktin);
350         return NULL;
351     }
352 }
353
354 /*
355  * Open a file.
356  */
357 struct fxp_handle *fxp_open(char *path, int type) {
358     struct sftp_packet *pktin, *pktout;
359     int id;
360
361     pktout = sftp_pkt_init(SSH_FXP_OPEN);
362     sftp_pkt_adduint32(pktout, 0x567); /* request id */
363     sftp_pkt_addstring(pktout, path);
364     sftp_pkt_adduint32(pktout, type);
365     sftp_pkt_adduint32(pktout, 0);     /* (FIXME) empty ATTRS structure */
366     sftp_send(pktout);
367     pktin = sftp_recv();
368     id = sftp_pkt_getuint32(pktin);
369     if (id != 0x567) {
370         fxp_internal_error("request ID mismatch\n");
371         return NULL;
372     }
373     if (pktin->type == SSH_FXP_HANDLE) {
374         char *hstring;
375         struct fxp_handle *handle;
376         int len;
377
378         sftp_pkt_getstring(pktin, &hstring, &len);
379         if (!hstring) {
380             fxp_internal_error("OPEN returned malformed FXP_HANDLE\n");
381             return NULL;
382         }
383         handle = smalloc(sizeof(struct fxp_handle));
384         handle->hstring = mkstr(hstring, len);
385         handle->hlen = len;
386         sftp_pkt_free(pktin);
387         return handle;
388     } else {
389         fxp_got_status(pktin);
390         return NULL;
391     }
392 }
393
394 /*
395  * Open a directory.
396  */
397 struct fxp_handle *fxp_opendir(char *path) {
398     struct sftp_packet *pktin, *pktout;
399     int id;
400
401     pktout = sftp_pkt_init(SSH_FXP_OPENDIR);
402     sftp_pkt_adduint32(pktout, 0x456); /* request id */
403     sftp_pkt_addstring(pktout, path);
404     sftp_send(pktout);
405     pktin = sftp_recv();
406     id = sftp_pkt_getuint32(pktin);
407     if (id != 0x456) {
408         fxp_internal_error("request ID mismatch\n");
409         return NULL;
410     }
411     if (pktin->type == SSH_FXP_HANDLE) {
412         char *hstring;
413         struct fxp_handle *handle;
414         int len;
415
416         sftp_pkt_getstring(pktin, &hstring, &len);
417         if (!hstring) {
418             fxp_internal_error("OPENDIR returned malformed FXP_HANDLE\n");
419             return NULL;
420         }
421         handle = smalloc(sizeof(struct fxp_handle));
422         handle->hstring = mkstr(hstring, len);
423         handle->hlen = len;
424         sftp_pkt_free(pktin);
425         return handle;
426     } else {
427         fxp_got_status(pktin);
428         return NULL;
429     }
430 }
431
432 /*
433  * Close a file/dir.
434  */
435 void fxp_close(struct fxp_handle *handle) {
436     struct sftp_packet *pktin, *pktout;
437     int id;
438
439     pktout = sftp_pkt_init(SSH_FXP_CLOSE);
440     sftp_pkt_adduint32(pktout, 0x789); /* request id */
441     sftp_pkt_addstring_start(pktout);
442     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
443     sftp_send(pktout);
444     pktin = sftp_recv();
445     id = sftp_pkt_getuint32(pktin);
446     if (id != 0x789) {
447         fxp_internal_error("request ID mismatch\n");
448         return;
449     }
450     fxp_got_status(pktin);
451     sfree(handle->hstring);
452     sfree(handle);
453 }
454
455 /*
456  * Read from a file. Returns the number of bytes read, or -1 on an
457  * error, or possibly 0 if EOF. (I'm not entirely sure whether it
458  * will return 0 on EOF, or return -1 and store SSH_FX_EOF in the
459  * error indicator. It might even depend on the SFTP server.)
460  */
461 int fxp_read(struct fxp_handle *handle, char *buffer, uint64 offset, int len) {
462     struct sftp_packet *pktin, *pktout;
463     int id;
464
465     pktout = sftp_pkt_init(SSH_FXP_READ);
466     sftp_pkt_adduint32(pktout, 0xBCD); /* request id */
467     sftp_pkt_addstring_start(pktout);
468     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
469     sftp_pkt_adduint64(pktout, offset);
470     sftp_pkt_adduint32(pktout, len);
471     sftp_send(pktout);
472     pktin = sftp_recv();
473     id = sftp_pkt_getuint32(pktin);
474     if (id != 0xBCD) {
475         fxp_internal_error("request ID mismatch");
476         return -1;
477     }
478     if (pktin->type == SSH_FXP_DATA) {
479         char *str;
480         int rlen;
481
482         sftp_pkt_getstring(pktin, &str, &rlen);
483
484         if (rlen > len || rlen < 0) {
485             fxp_internal_error("READ returned more bytes than requested");
486             return -1;
487         }
488
489         memcpy(buffer, str, rlen);
490         sfree(pktin);
491         return rlen;
492     } else {
493         fxp_got_status(pktin);
494         return -1;
495     }
496 }
497
498 /*
499  * Read from a directory.
500  */
501 struct fxp_names *fxp_readdir(struct fxp_handle *handle) {
502     struct sftp_packet *pktin, *pktout;
503     int id;
504
505     pktout = sftp_pkt_init(SSH_FXP_READDIR);
506     sftp_pkt_adduint32(pktout, 0xABC); /* request id */
507     sftp_pkt_addstring_start(pktout);
508     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
509     sftp_send(pktout);
510     pktin = sftp_recv();
511     id = sftp_pkt_getuint32(pktin);
512     if (id != 0xABC) {
513         fxp_internal_error("request ID mismatch\n");
514         return NULL;
515     }
516     if (pktin->type == SSH_FXP_NAME) {
517         struct fxp_names *ret;
518         int i;
519         ret = smalloc(sizeof(struct fxp_names));
520         ret->nnames = sftp_pkt_getuint32(pktin);
521         ret->names = smalloc(ret->nnames * sizeof(struct fxp_name));
522         for (i = 0; i < ret->nnames; i++) {
523             char *str;
524             int len;
525             sftp_pkt_getstring(pktin, &str, &len);
526             ret->names[i].filename = mkstr(str, len);
527             sftp_pkt_getstring(pktin, &str, &len);
528             ret->names[i].longname = mkstr(str, len);
529             ret->names[i].attrs = sftp_pkt_getattrs(pktin);
530         }
531         return ret;
532     } else {
533         fxp_got_status(pktin);
534         return NULL;
535     }
536 }
537
538 /*
539  * Write to a file. Returns 0 on error, 1 on OK.
540  */
541 int fxp_write(struct fxp_handle *handle, char *buffer, uint64 offset, int len) {
542     struct sftp_packet *pktin, *pktout;
543     int id;
544
545     pktout = sftp_pkt_init(SSH_FXP_WRITE);
546     sftp_pkt_adduint32(pktout, 0xDCB); /* request id */
547     sftp_pkt_addstring_start(pktout);
548     sftp_pkt_addstring_data(pktout, handle->hstring, handle->hlen);
549     sftp_pkt_adduint64(pktout, offset);
550     sftp_pkt_addstring_start(pktout);
551     sftp_pkt_addstring_data(pktout, buffer, len);
552     sftp_send(pktout);
553     pktin = sftp_recv();
554     id = sftp_pkt_getuint32(pktin);
555     if (id != 0xDCB) {
556         fxp_internal_error("request ID mismatch\n");
557         return 0;
558     }
559     fxp_got_status(pktin);
560     return fxp_errtype == SSH_FX_OK;
561 }
562
563 /*
564  * Free up an fxp_names structure.
565  */
566 void fxp_free_names(struct fxp_names *names) {
567     int i;
568
569     for (i = 0; i < names->nnames; i++) {
570         sfree(names->names[i].filename);
571         sfree(names->names[i].longname);
572     }
573     sfree(names->names);
574     sfree(names);
575 }