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