agentf.c 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. /*
  2. * SSH agent forwarding.
  3. */
  4. #include <assert.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include "putty.h"
  8. #include "ssh.h"
  9. #include "pageant.h"
  10. #include "channel.h"
  11. typedef struct agentf {
  12. SshChannel *c;
  13. bufchain inbuffer;
  14. agent_pending_query *pending;
  15. bool input_wanted;
  16. bool rcvd_eof;
  17. Channel chan;
  18. } agentf;
  19. static void agentf_got_response(agentf *af, void *reply, int replylen)
  20. {
  21. af->pending = NULL;
  22. if (!reply) {
  23. /* The real agent didn't send any kind of reply at all for
  24. * some reason, so fake an SSH_AGENT_FAILURE. */
  25. reply = "\0\0\0\1\5";
  26. replylen = 5;
  27. }
  28. sshfwd_write(af->c, reply, replylen);
  29. }
  30. static void agentf_callback(void *vctx, void *reply, int replylen);
  31. static void agentf_try_forward(agentf *af)
  32. {
  33. size_t datalen, length;
  34. strbuf *message;
  35. unsigned char msglen[4];
  36. void *reply;
  37. int replylen;
  38. /*
  39. * Don't try to parallelise agent requests. Wait for each one to
  40. * return before attempting the next.
  41. */
  42. if (af->pending)
  43. return;
  44. /*
  45. * If the outgoing side of the channel connection is currently
  46. * throttled, don't submit any new forwarded requests to the real
  47. * agent. This causes the input side of the agent forwarding not
  48. * to be emptied, exerting the required back-pressure on the
  49. * remote client, and encouraging it to read our responses before
  50. * sending too many more requests.
  51. */
  52. if (!af->input_wanted)
  53. return;
  54. while (1) {
  55. /*
  56. * Try to extract a complete message from the input buffer.
  57. */
  58. datalen = bufchain_size(&af->inbuffer);
  59. if (datalen < 4)
  60. break; /* not even a length field available yet */
  61. bufchain_fetch(&af->inbuffer, msglen, 4);
  62. length = GET_32BIT_MSB_FIRST(msglen);
  63. if (length > AGENT_MAX_MSGLEN-4) {
  64. /*
  65. * If the remote has sent a message that's just _too_
  66. * long, we should reject it in advance of seeing the rest
  67. * of the incoming message, and also close the connection
  68. * for good measure (which avoids us having to faff about
  69. * with carefully ignoring just the right number of bytes
  70. * from the overlong message).
  71. */
  72. agentf_got_response(af, NULL, 0);
  73. sshfwd_write_eof(af->c);
  74. return;
  75. }
  76. if (length > datalen - 4)
  77. break; /* a whole message is not yet available */
  78. bufchain_consume(&af->inbuffer, 4);
  79. message = strbuf_new_for_agent_query();
  80. bufchain_fetch_consume(
  81. &af->inbuffer, strbuf_append(message, length), length);
  82. af->pending = agent_query(
  83. message, &reply, &replylen, agentf_callback, af);
  84. strbuf_free(message);
  85. if (af->pending)
  86. return; /* agent_query promised to reply in due course */
  87. /*
  88. * If the agent gave us an answer immediately, pass it
  89. * straight on and go round this loop again.
  90. */
  91. agentf_got_response(af, reply, replylen);
  92. sfree(reply);
  93. }
  94. /*
  95. * If we get here (i.e. we left the above while loop via 'break'
  96. * rather than 'return'), that means we've determined that the
  97. * input buffer for the agent forwarding connection doesn't
  98. * contain a complete request.
  99. *
  100. * So if there's potentially more data to come, we can return now,
  101. * and wait for the remote client to send it. But if the remote
  102. * has sent EOF, it would be a mistake to do that, because we'd be
  103. * waiting a long time. So this is the moment to check for EOF,
  104. * and respond appropriately.
  105. */
  106. if (af->rcvd_eof)
  107. sshfwd_write_eof(af->c);
  108. }
  109. static void agentf_callback(void *vctx, void *reply, int replylen)
  110. {
  111. agentf *af = (agentf *)vctx;
  112. agentf_got_response(af, reply, replylen);
  113. sfree(reply);
  114. /*
  115. * Now try to extract and send further messages from the channel's
  116. * input-side buffer.
  117. */
  118. agentf_try_forward(af);
  119. }
  120. static void agentf_free(Channel *chan);
  121. static size_t agentf_send(Channel *chan, bool is_stderr, const void *, size_t);
  122. static void agentf_send_eof(Channel *chan);
  123. static char *agentf_log_close_msg(Channel *chan);
  124. static void agentf_set_input_wanted(Channel *chan, bool wanted);
  125. static const ChannelVtable agentf_channelvt = {
  126. .free = agentf_free,
  127. .open_confirmation = chan_remotely_opened_confirmation,
  128. .open_failed = chan_remotely_opened_failure,
  129. .send = agentf_send,
  130. .send_eof = agentf_send_eof,
  131. .set_input_wanted = agentf_set_input_wanted,
  132. .log_close_msg = agentf_log_close_msg,
  133. .want_close = chan_default_want_close,
  134. .rcvd_exit_status = chan_no_exit_status,
  135. .rcvd_exit_signal = chan_no_exit_signal,
  136. .rcvd_exit_signal_numeric = chan_no_exit_signal_numeric,
  137. .run_shell = chan_no_run_shell,
  138. .run_command = chan_no_run_command,
  139. .run_subsystem = chan_no_run_subsystem,
  140. .enable_x11_forwarding = chan_no_enable_x11_forwarding,
  141. .enable_agent_forwarding = chan_no_enable_agent_forwarding,
  142. .allocate_pty = chan_no_allocate_pty,
  143. .set_env = chan_no_set_env,
  144. .send_break = chan_no_send_break,
  145. .send_signal = chan_no_send_signal,
  146. .change_window_size = chan_no_change_window_size,
  147. .request_response = chan_no_request_response,
  148. };
  149. Channel *agentf_new(SshChannel *c)
  150. {
  151. agentf *af = snew(agentf);
  152. af->c = c;
  153. af->chan.vt = &agentf_channelvt;
  154. af->chan.initial_fixed_window_size = 0;
  155. af->rcvd_eof = false;
  156. bufchain_init(&af->inbuffer);
  157. af->pending = NULL;
  158. af->input_wanted = true;
  159. return &af->chan;
  160. }
  161. static void agentf_free(Channel *chan)
  162. {
  163. assert(chan->vt == &agentf_channelvt);
  164. agentf *af = container_of(chan, agentf, chan);
  165. if (af->pending)
  166. agent_cancel_query(af->pending);
  167. bufchain_clear(&af->inbuffer);
  168. sfree(af);
  169. }
  170. static size_t agentf_send(Channel *chan, bool is_stderr,
  171. const void *data, size_t length)
  172. {
  173. assert(chan->vt == &agentf_channelvt);
  174. agentf *af = container_of(chan, agentf, chan);
  175. bufchain_add(&af->inbuffer, data, length);
  176. agentf_try_forward(af);
  177. /*
  178. * We exert back-pressure on an agent forwarding client if and
  179. * only if we're waiting for the response to an asynchronous agent
  180. * request. This prevents the client running out of window while
  181. * receiving the _first_ message, but means that if any message
  182. * takes time to process, the client will be discouraged from
  183. * sending an endless stream of further ones after it.
  184. */
  185. return (af->pending ? bufchain_size(&af->inbuffer) : 0);
  186. }
  187. static void agentf_send_eof(Channel *chan)
  188. {
  189. assert(chan->vt == &agentf_channelvt);
  190. agentf *af = container_of(chan, agentf, chan);
  191. af->rcvd_eof = true;
  192. /* Call try_forward, which will respond to the EOF now if
  193. * appropriate, or wait until the queue of outstanding requests is
  194. * dealt with if not. */
  195. agentf_try_forward(af);
  196. }
  197. static char *agentf_log_close_msg(Channel *chan)
  198. {
  199. return dupstr("Agent-forwarding connection closed");
  200. }
  201. static void agentf_set_input_wanted(Channel *chan, bool wanted)
  202. {
  203. assert(chan->vt == &agentf_channelvt);
  204. agentf *af = container_of(chan, agentf, chan);
  205. af->input_wanted = wanted;
  206. /* Agent forwarding channels are buffer-managed by not asking the
  207. * agent questions if the SSH channel isn't accepting input. So if
  208. * it's started again, we should ask a question if we have one
  209. * pending.. */
  210. if (wanted)
  211. agentf_try_forward(af);
  212. }