1 /*
2 * Trivial binary packet protocol for the 'bare' ssh-connection
3 * protocol used in PuTTY's SSH-2 connection sharing system.
4 */
5
6 #include <assert.h>
7
8 #include "putty.h"
9 #include "ssh.h"
10 #include "sshbpp.h"
11 #include "sshcr.h"
12
13 struct ssh2_bare_bpp_state {
14 int crState;
15 long packetlen, maxlen;
16 unsigned char *data;
17 unsigned long incoming_sequence, outgoing_sequence;
18 PktIn *pktin;
19
20 BinaryPacketProtocol bpp;
21 };
22
23 static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp);
24 static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp);
25 static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp);
26 static PktOut *ssh2_bare_bpp_new_pktout(int type);
27
28 static const BinaryPacketProtocolVtable ssh2_bare_bpp_vtable = {
29 .free = ssh2_bare_bpp_free,
30 .handle_input = ssh2_bare_bpp_handle_input,
31 .handle_output = ssh2_bare_bpp_handle_output,
32 .new_pktout = ssh2_bare_bpp_new_pktout,
33 .queue_disconnect = ssh2_bpp_queue_disconnect, /* in sshcommon.c */
34
35 /* packet size limit, per protocol spec in sshshare.c comment */
36 .packet_size_limit = 0x4000,
37 };
38
ssh2_bare_bpp_new(LogContext * logctx)39 BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx)
40 {
41 struct ssh2_bare_bpp_state *s = snew(struct ssh2_bare_bpp_state);
42 memset(s, 0, sizeof(*s));
43 s->bpp.vt = &ssh2_bare_bpp_vtable;
44 s->bpp.logctx = logctx;
45 ssh_bpp_common_setup(&s->bpp);
46 return &s->bpp;
47 }
48
ssh2_bare_bpp_free(BinaryPacketProtocol * bpp)49 static void ssh2_bare_bpp_free(BinaryPacketProtocol *bpp)
50 {
51 struct ssh2_bare_bpp_state *s =
52 container_of(bpp, struct ssh2_bare_bpp_state, bpp);
53 sfree(s->pktin);
54 sfree(s);
55 }
56
57 #define BPP_READ(ptr, len) do \
58 { \
59 bool success; \
60 crMaybeWaitUntilV((success = bufchain_try_fetch_consume( \
61 s->bpp.in_raw, ptr, len)) || \
62 s->bpp.input_eof); \
63 if (!success) \
64 goto eof; \
65 ssh_check_frozen(s->bpp.ssh); \
66 } while (0)
67
ssh2_bare_bpp_handle_input(BinaryPacketProtocol * bpp)68 static void ssh2_bare_bpp_handle_input(BinaryPacketProtocol *bpp)
69 {
70 struct ssh2_bare_bpp_state *s =
71 container_of(bpp, struct ssh2_bare_bpp_state, bpp);
72
73 crBegin(s->crState);
74
75 while (1) {
76 /* Read the length field. */
77 {
78 unsigned char lenbuf[4];
79 BPP_READ(lenbuf, 4);
80 s->packetlen = toint(GET_32BIT_MSB_FIRST(lenbuf));
81 }
82
83 if (s->packetlen <= 0 || s->packetlen >= (long)OUR_V2_PACKETLIMIT) {
84 ssh_sw_abort(s->bpp.ssh, "Invalid packet length received");
85 crStopV;
86 }
87
88 /*
89 * Allocate the packet to return, now we know its length.
90 */
91 s->pktin = snew_plus(PktIn, s->packetlen);
92 s->pktin->qnode.prev = s->pktin->qnode.next = NULL;
93 s->pktin->qnode.on_free_queue = false;
94 s->maxlen = 0;
95 s->data = snew_plus_get_aux(s->pktin);
96
97 s->pktin->sequence = s->incoming_sequence++;
98
99 /*
100 * Read the remainder of the packet.
101 */
102 BPP_READ(s->data, s->packetlen);
103
104 /*
105 * The data we just read is precisely the initial type byte
106 * followed by the packet payload.
107 */
108 s->pktin->type = s->data[0];
109 s->data++;
110 s->packetlen--;
111 BinarySource_INIT(s->pktin, s->data, s->packetlen);
112
113 if (s->pktin->type == SSH2_MSG_EXT_INFO) {
114 /*
115 * Mild layer violation: EXT_INFO is not permitted in the
116 * bare ssh-connection protocol. Faulting it here means
117 * that ssh2_common_filter_queue doesn't receive it in the
118 * first place unless it's legal to have sent it.
119 */
120 ssh_proto_error(s->bpp.ssh, "Remote side sent SSH2_MSG_EXT_INFO "
121 "in bare connection protocol");
122 return;
123 }
124
125 /*
126 * Log incoming packet, possibly omitting sensitive fields.
127 */
128 if (s->bpp.logctx) {
129 logblank_t blanks[MAX_BLANKS];
130 int nblanks = ssh2_censor_packet(
131 s->bpp.pls, s->pktin->type, false,
132 make_ptrlen(s->data, s->packetlen), blanks);
133 log_packet(s->bpp.logctx, PKT_INCOMING, s->pktin->type,
134 ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
135 s->pktin->type),
136 get_ptr(s->pktin), get_avail(s->pktin), nblanks, blanks,
137 &s->pktin->sequence, 0, NULL);
138 }
139
140 if (ssh2_bpp_check_unimplemented(&s->bpp, s->pktin)) {
141 sfree(s->pktin);
142 s->pktin = NULL;
143 continue;
144 }
145
146 s->pktin->qnode.formal_size = get_avail(s->pktin);
147 pq_push(&s->bpp.in_pq, s->pktin);
148 s->pktin = NULL;
149 }
150
151 eof:
152 if (!s->bpp.expect_close) {
153 ssh_remote_error(s->bpp.ssh,
154 "Remote side unexpectedly closed network connection");
155 } else {
156 ssh_remote_eof(s->bpp.ssh, "Remote side closed network connection");
157 }
158 return; /* avoid touching s now it's been freed */
159
160 crFinishV;
161 }
162
ssh2_bare_bpp_new_pktout(int pkt_type)163 static PktOut *ssh2_bare_bpp_new_pktout(int pkt_type)
164 {
165 PktOut *pkt = ssh_new_packet();
166 pkt->length = 4; /* space for packet length */
167 pkt->type = pkt_type;
168 put_byte(pkt, pkt_type);
169 return pkt;
170 }
171
ssh2_bare_bpp_format_packet(struct ssh2_bare_bpp_state * s,PktOut * pkt)172 static void ssh2_bare_bpp_format_packet(struct ssh2_bare_bpp_state *s,
173 PktOut *pkt)
174 {
175 if (s->bpp.logctx) {
176 ptrlen pktdata = make_ptrlen(pkt->data + 5, pkt->length - 5);
177 logblank_t blanks[MAX_BLANKS];
178 int nblanks = ssh2_censor_packet(
179 s->bpp.pls, pkt->type, true, pktdata, blanks);
180 log_packet(s->bpp.logctx, PKT_OUTGOING, pkt->type,
181 ssh2_pkt_type(s->bpp.pls->kctx, s->bpp.pls->actx,
182 pkt->type),
183 pktdata.ptr, pktdata.len, nblanks, blanks,
184 &s->outgoing_sequence,
185 pkt->downstream_id, pkt->additional_log_text);
186 }
187
188 s->outgoing_sequence++; /* only for diagnostics, really */
189
190 PUT_32BIT_MSB_FIRST(pkt->data, pkt->length - 4);
191 bufchain_add(s->bpp.out_raw, pkt->data, pkt->length);
192 }
193
ssh2_bare_bpp_handle_output(BinaryPacketProtocol * bpp)194 static void ssh2_bare_bpp_handle_output(BinaryPacketProtocol *bpp)
195 {
196 struct ssh2_bare_bpp_state *s =
197 container_of(bpp, struct ssh2_bare_bpp_state, bpp);
198 PktOut *pkt;
199
200 while ((pkt = pq_pop(&s->bpp.out_pq)) != NULL) {
201 ssh2_bare_bpp_format_packet(s, pkt);
202 ssh_free_pktout(pkt);
203 }
204 }
205