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