| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252 | 
							- /*
 
-  * SSH agent forwarding.
 
-  */
 
- #include <assert.h>
 
- #include <stdio.h>
 
- #include <stdlib.h>
 
- #include "putty.h"
 
- #include "ssh.h"
 
- #include "pageant.h"
 
- #include "channel.h"
 
- typedef struct agentf {
 
-     Plug *plug; // WINSCP
 
-     SshChannel *c;
 
-     bufchain inbuffer;
 
-     agent_pending_query *pending;
 
-     bool input_wanted;
 
-     bool rcvd_eof;
 
-     Channel chan;
 
- } agentf;
 
- static void agentf_got_response(agentf *af, void *reply, int replylen)
 
- {
 
-     af->pending = NULL;
 
-     if (!reply) {
 
-         /* The real agent didn't send any kind of reply at all for
 
-          * some reason, so fake an SSH_AGENT_FAILURE. */
 
-         reply = "\0\0\0\1\5";
 
-         replylen = 5;
 
-     }
 
-     sshfwd_write(af->c, reply, replylen);
 
- }
 
- static void agentf_callback(void *vctx, void *reply, int replylen);
 
- static void agentf_try_forward(agentf *af)
 
- {
 
-     size_t datalen, length;
 
-     strbuf *message;
 
-     unsigned char msglen[4];
 
-     void *reply;
 
-     int replylen;
 
-     /*
 
-      * Don't try to parallelise agent requests. Wait for each one to
 
-      * return before attempting the next.
 
-      */
 
-     if (af->pending)
 
-         return;
 
-     /*
 
-      * If the outgoing side of the channel connection is currently
 
-      * throttled, don't submit any new forwarded requests to the real
 
-      * agent. This causes the input side of the agent forwarding not
 
-      * to be emptied, exerting the required back-pressure on the
 
-      * remote client, and encouraging it to read our responses before
 
-      * sending too many more requests.
 
-      */
 
-     if (!af->input_wanted)
 
-         return;
 
-     while (1) {
 
-         /*
 
-          * Try to extract a complete message from the input buffer.
 
-          */
 
-         datalen = bufchain_size(&af->inbuffer);
 
-         if (datalen < 4)
 
-             break;         /* not even a length field available yet */
 
-         bufchain_fetch(&af->inbuffer, msglen, 4);
 
-         length = GET_32BIT_MSB_FIRST(msglen);
 
-         if (length > AGENT_MAX_MSGLEN-4) {
 
-             /*
 
-              * If the remote has sent a message that's just _too_
 
-              * long, we should reject it in advance of seeing the rest
 
-              * of the incoming message, and also close the connection
 
-              * for good measure (which avoids us having to faff about
 
-              * with carefully ignoring just the right number of bytes
 
-              * from the overlong message).
 
-              */
 
-             agentf_got_response(af, NULL, 0);
 
-             sshfwd_write_eof(af->c);
 
-             return;
 
-         }
 
-         if (length > datalen - 4)
 
-             break;          /* a whole message is not yet available */
 
-         bufchain_consume(&af->inbuffer, 4);
 
-         message = strbuf_new_for_agent_query();
 
-         bufchain_fetch_consume(
 
-             &af->inbuffer, strbuf_append(message, length), length);
 
-         af->pending = agent_query(
 
-             message, &reply, &replylen, agentf_callback, af, get_callback_set(af->plug)); // WINSCP
 
-         strbuf_free(message);
 
-         if (af->pending)
 
-             return;   /* agent_query promised to reply in due course */
 
-         /*
 
-          * If the agent gave us an answer immediately, pass it
 
-          * straight on and go round this loop again.
 
-          */
 
-         agentf_got_response(af, reply, replylen);
 
-         sfree(reply);
 
-     }
 
-     /*
 
-      * If we get here (i.e. we left the above while loop via 'break'
 
-      * rather than 'return'), that means we've determined that the
 
-      * input buffer for the agent forwarding connection doesn't
 
-      * contain a complete request.
 
-      *
 
-      * So if there's potentially more data to come, we can return now,
 
-      * and wait for the remote client to send it. But if the remote
 
-      * has sent EOF, it would be a mistake to do that, because we'd be
 
-      * waiting a long time. So this is the moment to check for EOF,
 
-      * and respond appropriately.
 
-      */
 
-     if (af->rcvd_eof)
 
-         sshfwd_write_eof(af->c);
 
- }
 
- static void agentf_callback(void *vctx, void *reply, int replylen)
 
- {
 
-     agentf *af = (agentf *)vctx;
 
-     agentf_got_response(af, reply, replylen);
 
-     sfree(reply);
 
-     /*
 
-      * Now try to extract and send further messages from the channel's
 
-      * input-side buffer.
 
-      */
 
-     agentf_try_forward(af);
 
- }
 
- static void agentf_free(Channel *chan);
 
- static size_t agentf_send(Channel *chan, bool is_stderr, const void *, size_t);
 
- static void agentf_send_eof(Channel *chan);
 
- static char *agentf_log_close_msg(Channel *chan);
 
- static void agentf_set_input_wanted(Channel *chan, bool wanted);
 
- static const ChannelVtable agentf_channelvt = {
 
-     // WINSCP
 
-     /*.free =*/ agentf_free,
 
-     /*.open_confirmation =*/ chan_remotely_opened_confirmation,
 
-     /*.open_failed =*/ chan_remotely_opened_failure,
 
-     /*.send =*/ agentf_send,
 
-     /*.send_eof =*/ agentf_send_eof,
 
-     /*.set_input_wanted =*/ agentf_set_input_wanted,
 
-     /*.log_close_msg =*/ agentf_log_close_msg,
 
-     /*.want_close =*/ chan_default_want_close,
 
-     /*.rcvd_exit_status =*/ chan_no_exit_status,
 
-     /*.rcvd_exit_signal =*/ chan_no_exit_signal,
 
-     /*.rcvd_exit_signal_numeric =*/ chan_no_exit_signal_numeric,
 
-     /*.run_shell =*/ chan_no_run_shell,
 
-     /*.run_command =*/ chan_no_run_command,
 
-     /*.run_subsystem =*/ chan_no_run_subsystem,
 
-     /*.enable_x11_forwarding =*/ chan_no_enable_x11_forwarding,
 
-     /*.enable_agent_forwarding =*/ chan_no_enable_agent_forwarding,
 
-     /*.allocate_pty =*/ chan_no_allocate_pty,
 
-     /*.set_env =*/ chan_no_set_env,
 
-     /*.send_break =*/ chan_no_send_break,
 
-     /*.send_signal =*/ chan_no_send_signal,
 
-     /*.change_window_size =*/ chan_no_change_window_size,
 
-     /*.request_response =*/ chan_no_request_response,
 
- };
 
- Channel *agentf_new(SshChannel *c, Plug *plug) // WINSCP
 
- {
 
-     agentf *af = snew(agentf);
 
-     af->plug = plug; // WINSCP
 
-     af->c = c;
 
-     af->chan.vt = &agentf_channelvt;
 
-     af->chan.initial_fixed_window_size = 0;
 
-     af->rcvd_eof = false;
 
-     bufchain_init(&af->inbuffer);
 
-     af->pending = NULL;
 
-     af->input_wanted = true;
 
-     return &af->chan;
 
- }
 
- static void agentf_free(Channel *chan)
 
- {
 
-     pinitassert(chan->vt == &agentf_channelvt);
 
-     agentf *af = container_of(chan, agentf, chan);
 
-     if (af->pending)
 
-         agent_cancel_query(af->pending);
 
-     bufchain_clear(&af->inbuffer);
 
-     sfree(af);
 
- }
 
- static size_t agentf_send(Channel *chan, bool is_stderr,
 
-                           const void *data, size_t length)
 
- {
 
-     pinitassert(chan->vt == &agentf_channelvt);
 
-     agentf *af = container_of(chan, agentf, chan);
 
-     bufchain_add(&af->inbuffer, data, length);
 
-     agentf_try_forward(af);
 
-     /*
 
-      * We exert back-pressure on an agent forwarding client if and
 
-      * only if we're waiting for the response to an asynchronous agent
 
-      * request. This prevents the client running out of window while
 
-      * receiving the _first_ message, but means that if any message
 
-      * takes time to process, the client will be discouraged from
 
-      * sending an endless stream of further ones after it.
 
-      */
 
-     return (af->pending ? bufchain_size(&af->inbuffer) : 0);
 
- }
 
- static void agentf_send_eof(Channel *chan)
 
- {
 
-     pinitassert(chan->vt == &agentf_channelvt);
 
-     agentf *af = container_of(chan, agentf, chan);
 
-     af->rcvd_eof = true;
 
-     /* Call try_forward, which will respond to the EOF now if
 
-      * appropriate, or wait until the queue of outstanding requests is
 
-      * dealt with if not. */
 
-     agentf_try_forward(af);
 
- }
 
- static char *agentf_log_close_msg(Channel *chan)
 
- {
 
-     return dupstr("Agent-forwarding connection closed");
 
- }
 
- static void agentf_set_input_wanted(Channel *chan, bool wanted)
 
- {
 
-     pinitassert(chan->vt == &agentf_channelvt);
 
-     agentf *af = container_of(chan, agentf, chan);
 
-     af->input_wanted = wanted;
 
-     /* Agent forwarding channels are buffer-managed by not asking the
 
-      * agent questions if the SSH channel isn't accepting input. So if
 
-      * it's started again, we should ask a question if we have one
 
-      * pending.. */
 
-     if (wanted)
 
-         agentf_try_forward(af);
 
- }
 
 
  |