ftpcmdio.c 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. /*
  2. * Part of Very Secure FTPd
  3. * Licence: GPL v2
  4. * Author: Chris Evans
  5. * ftpcmdio.c
  6. *
  7. * Routines applicable to reading and writing the FTP command stream.
  8. */
  9. #include "ftpcmdio.h"
  10. #include "ftpcodes.h"
  11. #include "str.h"
  12. #include "netstr.h"
  13. #include "sysutil.h"
  14. #include "tunables.h"
  15. #include "defs.h"
  16. #include "secbuf.h"
  17. #include "utility.h"
  18. #include "logging.h"
  19. #include "session.h"
  20. #include "readwrite.h"
  21. /* Internal functions */
  22. static int control_getline(struct mystr* p_str, struct vsf_session* p_sess);
  23. static void ftp_write_text_common(struct vsf_session* p_sess, int status,
  24. const char* p_text, char sep);
  25. static void ftp_write_str_common(struct vsf_session* p_sess, int status,
  26. char sep, const struct mystr* p_str);
  27. static void handle_alarm_timeout(void* p_private);
  28. void
  29. vsf_cmdio_sock_setup(void)
  30. {
  31. vsf_sysutil_activate_keepalive(VSFTP_COMMAND_FD);
  32. vsf_sysutil_set_nodelay(VSFTP_COMMAND_FD);
  33. vsf_sysutil_activate_oobinline(VSFTP_COMMAND_FD);
  34. }
  35. static void
  36. handle_alarm_timeout(void* p_private)
  37. {
  38. struct vsf_session* p_sess = (struct vsf_session*) p_private;
  39. p_sess->idle_timeout = 1;
  40. vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
  41. vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD);
  42. }
  43. void
  44. vsf_cmdio_write(struct vsf_session* p_sess, int status, const char* p_text)
  45. {
  46. ftp_write_text_common(p_sess, status, p_text, ' ');
  47. }
  48. void
  49. vsf_cmdio_write_hyphen(struct vsf_session* p_sess, int status,
  50. const char* p_text)
  51. {
  52. ftp_write_text_common(p_sess, status, p_text, '-');
  53. }
  54. void
  55. vsf_cmdio_write_raw(struct vsf_session* p_sess, const char* p_text)
  56. {
  57. static struct mystr s_the_str;
  58. int retval;
  59. str_alloc_text(&s_the_str, p_text);
  60. if (tunable_log_ftp_protocol)
  61. {
  62. vsf_log_line(p_sess, kVSFLogEntryFTPOutput, &s_the_str);
  63. }
  64. retval = ftp_write_str(p_sess, &s_the_str, kVSFRWControl);
  65. if (retval != 0)
  66. {
  67. die("ftp_write_str");
  68. }
  69. }
  70. void
  71. vsf_cmdio_write_exit(struct vsf_session* p_sess, int status, const char* p_text,
  72. int exit_val)
  73. {
  74. /* Unblock any readers on the dying control channel. This is needed for SSL
  75. * connections, where the SSL control channel slave is in a separate
  76. * process.
  77. */
  78. vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
  79. vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD);
  80. vsf_cmdio_write(p_sess, status, p_text);
  81. vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD);
  82. vsf_sysutil_exit(exit_val);
  83. }
  84. static void
  85. ftp_write_text_common(struct vsf_session* p_sess, int status,
  86. const char* p_text, char sep)
  87. {
  88. /* XXX - could optimize */
  89. static struct mystr s_the_str;
  90. str_alloc_text(&s_the_str, p_text);
  91. ftp_write_str_common(p_sess, status, sep, &s_the_str);
  92. }
  93. void
  94. vsf_cmdio_write_str_hyphen(struct vsf_session* p_sess, int status,
  95. const struct mystr* p_str)
  96. {
  97. ftp_write_str_common(p_sess, status, '-', p_str);
  98. }
  99. void
  100. vsf_cmdio_write_str(struct vsf_session* p_sess, int status,
  101. const struct mystr* p_str)
  102. {
  103. ftp_write_str_common(p_sess, status, ' ', p_str);
  104. }
  105. static void
  106. ftp_write_str_common(struct vsf_session* p_sess, int status, char sep,
  107. const struct mystr* p_str)
  108. {
  109. static struct mystr s_write_buf_str;
  110. static struct mystr s_text_mangle_str;
  111. int retval;
  112. if (tunable_log_ftp_protocol)
  113. {
  114. str_alloc_ulong(&s_write_buf_str, (unsigned long) status);
  115. str_append_char(&s_write_buf_str, sep);
  116. str_append_str(&s_write_buf_str, p_str);
  117. vsf_log_line(p_sess, kVSFLogEntryFTPOutput, &s_write_buf_str);
  118. }
  119. str_copy(&s_text_mangle_str, p_str);
  120. /* Process the output response according to the specifications.. */
  121. /* Escape telnet characters properly */
  122. str_replace_text(&s_text_mangle_str, "\377", "\377\377");
  123. /* Change \n for \0 in response */
  124. str_replace_char(&s_text_mangle_str, '\n', '\0');
  125. /* Build string to squirt down network */
  126. str_alloc_ulong(&s_write_buf_str, (unsigned long) status);
  127. str_append_char(&s_write_buf_str, sep);
  128. str_append_str(&s_write_buf_str, &s_text_mangle_str);
  129. str_append_text(&s_write_buf_str, "\r\n");
  130. retval = ftp_write_str(p_sess, &s_write_buf_str, kVSFRWControl);
  131. if (retval != 0)
  132. {
  133. die("ftp_write");
  134. }
  135. }
  136. void
  137. vsf_cmdio_set_alarm(struct vsf_session* p_sess)
  138. {
  139. if (tunable_idle_session_timeout > 0)
  140. {
  141. vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM,
  142. handle_alarm_timeout,
  143. p_sess,
  144. 1);
  145. vsf_sysutil_set_alarm(tunable_idle_session_timeout);
  146. }
  147. }
  148. void
  149. vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str,
  150. struct mystr* p_arg_str, int set_alarm)
  151. {
  152. int ret;
  153. /* Prepare an alarm to timeout the session.. */
  154. if (set_alarm)
  155. {
  156. vsf_cmdio_set_alarm(p_sess);
  157. }
  158. /* Blocks */
  159. ret = control_getline(p_cmd_str, p_sess);
  160. if (p_sess->idle_timeout)
  161. {
  162. vsf_cmdio_write_exit(p_sess, FTP_IDLE_TIMEOUT, "Timeout.", 1);
  163. }
  164. if (ret == 0)
  165. {
  166. /* Remote end hung up without a polite QUIT. The shutdown is to make
  167. * sure buggy clients don't ever see an OOPS message.
  168. */
  169. vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD);
  170. vsf_sysutil_exit(1);
  171. }
  172. /* View a single space as a command of " ", which although a useless command,
  173. * permits the caller to distinguish input of "" from " ".
  174. */
  175. if (str_getlen(p_cmd_str) == 1 && str_get_char_at(p_cmd_str, 0) == ' ')
  176. {
  177. str_empty(p_arg_str);
  178. }
  179. else
  180. {
  181. str_split_char(p_cmd_str, p_arg_str, ' ');
  182. }
  183. str_upper(p_cmd_str);
  184. if (tunable_log_ftp_protocol)
  185. {
  186. static struct mystr s_log_str;
  187. if (str_equal_text(p_cmd_str, "PASS"))
  188. {
  189. str_alloc_text(&s_log_str, "PASS <password>");
  190. }
  191. else
  192. {
  193. str_copy(&s_log_str, p_cmd_str);
  194. if (!str_isempty(p_arg_str))
  195. {
  196. str_append_char(&s_log_str, ' ');
  197. str_append_str(&s_log_str, p_arg_str);
  198. }
  199. }
  200. vsf_log_line(p_sess, kVSFLogEntryFTPInput, &s_log_str);
  201. }
  202. }
  203. static int
  204. control_getline(struct mystr* p_str, struct vsf_session* p_sess)
  205. {
  206. int ret;
  207. if (p_sess->p_control_line_buf == 0)
  208. {
  209. vsf_secbuf_alloc(&p_sess->p_control_line_buf, VSFTP_MAX_COMMAND_LINE);
  210. }
  211. ret = ftp_getline(p_sess, p_str, p_sess->p_control_line_buf);
  212. if (ret == 0)
  213. {
  214. return ret;
  215. }
  216. else if (ret < 0)
  217. {
  218. vsf_cmdio_write_exit(p_sess, FTP_BADCMD, "Input line too long.", 1);
  219. }
  220. /* As mandated by the FTP specifications.. */
  221. str_replace_char(p_str, '\0', '\n');
  222. /* If the last character is a \r, strip it */
  223. {
  224. unsigned int len = str_getlen(p_str);
  225. while (len > 0 && str_get_char_at(p_str, len - 1) == '\r')
  226. {
  227. str_trunc(p_str, len - 1);
  228. --len;
  229. }
  230. }
  231. return 1;
  232. }