sftpserver.c 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. /*
  2. * Implement the centralised parts of the server side of SFTP.
  3. */
  4. #include <assert.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include "putty.h"
  8. #include "ssh.h"
  9. #include "sftp.h"
  10. struct sftp_packet *sftp_handle_request(
  11. SftpServer *srv, struct sftp_packet *req)
  12. {
  13. struct sftp_packet *reply;
  14. unsigned id;
  15. uint32_t flags;
  16. ptrlen path, dstpath, handle, data;
  17. uint64_t offset;
  18. unsigned length;
  19. struct fxp_attrs attrs;
  20. DefaultSftpReplyBuilder dsrb;
  21. SftpReplyBuilder *rb;
  22. if (req->type == SSH_FXP_INIT) {
  23. /*
  24. * Special case which doesn't have a request id at the start.
  25. */
  26. reply = sftp_pkt_init(SSH_FXP_VERSION);
  27. /*
  28. * Since we support only the lowest protocol version, we don't
  29. * need to take the min of this and the client's version, or
  30. * even to bother reading the client version number out of the
  31. * input packet.
  32. */
  33. put_uint32(reply, SFTP_PROTO_VERSION);
  34. return reply;
  35. }
  36. /*
  37. * Centralise the request id handling. We'll overwrite the type
  38. * code of the output packet later.
  39. */
  40. id = get_uint32(req);
  41. reply = sftp_pkt_init(0);
  42. put_uint32(reply, id);
  43. dsrb.rb.vt = &DefaultSftpReplyBuilder_vt;
  44. dsrb.pkt = reply;
  45. rb = &dsrb.rb;
  46. switch (req->type) {
  47. case SSH_FXP_REALPATH:
  48. path = get_string(req);
  49. if (get_err(req))
  50. goto decode_error;
  51. sftpsrv_realpath(srv, rb, path);
  52. break;
  53. case SSH_FXP_OPEN:
  54. path = get_string(req);
  55. flags = get_uint32(req);
  56. get_fxp_attrs(req, &attrs);
  57. if (get_err(req))
  58. goto decode_error;
  59. if ((flags & (SSH_FXF_READ|SSH_FXF_WRITE)) == 0) {
  60. fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
  61. "open without READ or WRITE flag");
  62. } else if ((flags & (SSH_FXF_CREAT|SSH_FXF_TRUNC)) == SSH_FXF_TRUNC) {
  63. fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
  64. "open with TRUNC but not CREAT");
  65. } else if ((flags & (SSH_FXF_CREAT|SSH_FXF_EXCL)) == SSH_FXF_EXCL) {
  66. fxp_reply_error(rb, SSH_FX_BAD_MESSAGE,
  67. "open with EXCL but not CREAT");
  68. } else {
  69. sftpsrv_open(srv, rb, path, flags, attrs);
  70. }
  71. break;
  72. case SSH_FXP_OPENDIR:
  73. path = get_string(req);
  74. if (get_err(req))
  75. goto decode_error;
  76. sftpsrv_opendir(srv, rb, path);
  77. break;
  78. case SSH_FXP_CLOSE:
  79. handle = get_string(req);
  80. if (get_err(req))
  81. goto decode_error;
  82. sftpsrv_close(srv, rb, handle);
  83. break;
  84. case SSH_FXP_MKDIR:
  85. path = get_string(req);
  86. get_fxp_attrs(req, &attrs);
  87. if (get_err(req))
  88. goto decode_error;
  89. sftpsrv_mkdir(srv, rb, path, attrs);
  90. break;
  91. case SSH_FXP_RMDIR:
  92. path = get_string(req);
  93. if (get_err(req))
  94. goto decode_error;
  95. sftpsrv_rmdir(srv, rb, path);
  96. break;
  97. case SSH_FXP_REMOVE:
  98. path = get_string(req);
  99. if (get_err(req))
  100. goto decode_error;
  101. sftpsrv_remove(srv, rb, path);
  102. break;
  103. case SSH_FXP_RENAME:
  104. path = get_string(req);
  105. dstpath = get_string(req);
  106. if (get_err(req))
  107. goto decode_error;
  108. sftpsrv_rename(srv, rb, path, dstpath);
  109. break;
  110. case SSH_FXP_STAT:
  111. path = get_string(req);
  112. if (get_err(req))
  113. goto decode_error;
  114. sftpsrv_stat(srv, rb, path, true);
  115. break;
  116. case SSH_FXP_LSTAT:
  117. path = get_string(req);
  118. if (get_err(req))
  119. goto decode_error;
  120. sftpsrv_stat(srv, rb, path, false);
  121. break;
  122. case SSH_FXP_FSTAT:
  123. handle = get_string(req);
  124. if (get_err(req))
  125. goto decode_error;
  126. sftpsrv_fstat(srv, rb, handle);
  127. break;
  128. case SSH_FXP_SETSTAT:
  129. path = get_string(req);
  130. get_fxp_attrs(req, &attrs);
  131. if (get_err(req))
  132. goto decode_error;
  133. sftpsrv_setstat(srv, rb, path, attrs);
  134. break;
  135. case SSH_FXP_FSETSTAT:
  136. handle = get_string(req);
  137. get_fxp_attrs(req, &attrs);
  138. if (get_err(req))
  139. goto decode_error;
  140. sftpsrv_fsetstat(srv, rb, handle, attrs);
  141. break;
  142. case SSH_FXP_READ:
  143. handle = get_string(req);
  144. offset = get_uint64(req);
  145. length = get_uint32(req);
  146. if (get_err(req))
  147. goto decode_error;
  148. sftpsrv_read(srv, rb, handle, offset, length);
  149. break;
  150. case SSH_FXP_READDIR:
  151. handle = get_string(req);
  152. if (get_err(req))
  153. goto decode_error;
  154. sftpsrv_readdir(srv, rb, handle, INT_MAX, false);
  155. break;
  156. case SSH_FXP_WRITE:
  157. handle = get_string(req);
  158. offset = get_uint64(req);
  159. data = get_string(req);
  160. if (get_err(req))
  161. goto decode_error;
  162. sftpsrv_write(srv, rb, handle, offset, data);
  163. break;
  164. default:
  165. if (get_err(req))
  166. goto decode_error;
  167. fxp_reply_error(rb, SSH_FX_OP_UNSUPPORTED,
  168. "Unrecognised request type");
  169. break;
  170. decode_error:
  171. fxp_reply_error(rb, SSH_FX_BAD_MESSAGE, "Unable to decode request");
  172. }
  173. return reply;
  174. }
  175. static void default_reply_ok(SftpReplyBuilder *reply)
  176. {
  177. DefaultSftpReplyBuilder *d =
  178. container_of(reply, DefaultSftpReplyBuilder, rb);
  179. d->pkt->type = SSH_FXP_STATUS;
  180. put_uint32(d->pkt, SSH_FX_OK);
  181. put_stringz(d->pkt, "");
  182. }
  183. static void default_reply_error(
  184. SftpReplyBuilder *reply, unsigned code, const char *msg)
  185. {
  186. DefaultSftpReplyBuilder *d =
  187. container_of(reply, DefaultSftpReplyBuilder, rb);
  188. d->pkt->type = SSH_FXP_STATUS;
  189. put_uint32(d->pkt, code);
  190. put_stringz(d->pkt, msg);
  191. }
  192. static void default_reply_name_count(SftpReplyBuilder *reply, unsigned count)
  193. {
  194. DefaultSftpReplyBuilder *d =
  195. container_of(reply, DefaultSftpReplyBuilder, rb);
  196. d->pkt->type = SSH_FXP_NAME;
  197. put_uint32(d->pkt, count);
  198. }
  199. static void default_reply_full_name(SftpReplyBuilder *reply, ptrlen name,
  200. ptrlen longname, struct fxp_attrs attrs)
  201. {
  202. DefaultSftpReplyBuilder *d =
  203. container_of(reply, DefaultSftpReplyBuilder, rb);
  204. d->pkt->type = SSH_FXP_NAME;
  205. put_stringpl(d->pkt, name);
  206. put_stringpl(d->pkt, longname);
  207. put_fxp_attrs(d->pkt, attrs);
  208. }
  209. static void default_reply_simple_name(SftpReplyBuilder *reply, ptrlen name)
  210. {
  211. fxp_reply_name_count(reply, 1);
  212. fxp_reply_full_name(reply, name, PTRLEN_LITERAL(""), no_attrs);
  213. }
  214. static void default_reply_handle(SftpReplyBuilder *reply, ptrlen handle)
  215. {
  216. DefaultSftpReplyBuilder *d =
  217. container_of(reply, DefaultSftpReplyBuilder, rb);
  218. d->pkt->type = SSH_FXP_HANDLE;
  219. put_stringpl(d->pkt, handle);
  220. }
  221. static void default_reply_data(SftpReplyBuilder *reply, ptrlen data)
  222. {
  223. DefaultSftpReplyBuilder *d =
  224. container_of(reply, DefaultSftpReplyBuilder, rb);
  225. d->pkt->type = SSH_FXP_DATA;
  226. put_stringpl(d->pkt, data);
  227. }
  228. static void default_reply_attrs(
  229. SftpReplyBuilder *reply, struct fxp_attrs attrs)
  230. {
  231. DefaultSftpReplyBuilder *d =
  232. container_of(reply, DefaultSftpReplyBuilder, rb);
  233. d->pkt->type = SSH_FXP_ATTRS;
  234. put_fxp_attrs(d->pkt, attrs);
  235. }
  236. const SftpReplyBuilderVtable DefaultSftpReplyBuilder_vt = {
  237. .reply_ok = default_reply_ok,
  238. .reply_error = default_reply_error,
  239. .reply_simple_name = default_reply_simple_name,
  240. .reply_name_count = default_reply_name_count,
  241. .reply_full_name = default_reply_full_name,
  242. .reply_handle = default_reply_handle,
  243. .reply_data = default_reply_data,
  244. .reply_attrs = default_reply_attrs,
  245. };