1 /*
2  * Abstraction of the binary packet protocols used in SSH.
3  */
4 
5 #ifndef PUTTY_SSHBPP_H
6 #define PUTTY_SSHBPP_H
7 
8 typedef struct BinaryPacketProtocolVtable BinaryPacketProtocolVtable;
9 
10 struct BinaryPacketProtocolVtable {
11     void (*free)(BinaryPacketProtocol *);
12     void (*handle_input)(BinaryPacketProtocol *);
13     void (*handle_output)(BinaryPacketProtocol *);
14     PktOut *(*new_pktout)(int type);
15     void (*queue_disconnect)(BinaryPacketProtocol *,
16                              const char *msg, int category);
17     uint32_t packet_size_limit;
18 };
19 
20 struct BinaryPacketProtocol {
21     const struct BinaryPacketProtocolVtable *vt;
22     bufchain *in_raw, *out_raw;
23     bool input_eof;   /* set this if in_raw will never be added to again */
24     PktInQueue in_pq;
25     PktOutQueue out_pq;
26     PacketLogSettings *pls;
27     LogContext *logctx;
28     Ssh *ssh;
29 
30     /* ic_in_raw is filled in by the BPP (probably by calling
31      * ssh_bpp_common_setup). The BPP's owner triggers it when data is
32      * added to in_raw, and also when the BPP is newly created. */
33     IdempotentCallback ic_in_raw;
34 
35     /* ic_out_pq is entirely internal to the BPP itself; it's used as
36      * the callback on out_pq. */
37     IdempotentCallback ic_out_pq;
38 
39     /* Information that all packet layers sharing this BPP will
40      * potentially be interested in. */
41     int remote_bugs;
42     bool ext_info_rsa_sha256_ok, ext_info_rsa_sha512_ok;
43 
44     /* Set this if remote connection closure should not generate an
45      * error message (either because it's not to be treated as an
46      * error at all, or because some other error message has already
47      * been emitted). */
48     bool expect_close;
49 };
50 
ssh_bpp_handle_input(BinaryPacketProtocol * bpp)51 static inline void ssh_bpp_handle_input(BinaryPacketProtocol *bpp)
52 { bpp->vt->handle_input(bpp); }
ssh_bpp_handle_output(BinaryPacketProtocol * bpp)53 static inline void ssh_bpp_handle_output(BinaryPacketProtocol *bpp)
54 { bpp->vt->handle_output(bpp); }
ssh_bpp_new_pktout(BinaryPacketProtocol * bpp,int type)55 static inline PktOut *ssh_bpp_new_pktout(BinaryPacketProtocol *bpp, int type)
56 { return bpp->vt->new_pktout(type); }
ssh_bpp_queue_disconnect(BinaryPacketProtocol * bpp,const char * msg,int category)57 static inline void ssh_bpp_queue_disconnect(BinaryPacketProtocol *bpp,
58                                             const char *msg, int category)
59 { bpp->vt->queue_disconnect(bpp, msg, category); }
60 
61 /* ssh_bpp_free is more than just a macro wrapper on the vtable; it
62  * does centralised parts of the freeing too. */
63 void ssh_bpp_free(BinaryPacketProtocol *bpp);
64 
65 BinaryPacketProtocol *ssh1_bpp_new(LogContext *logctx);
66 void ssh1_bpp_new_cipher(BinaryPacketProtocol *bpp,
67                          const ssh_cipheralg *cipher,
68                          const void *session_key);
69 /* This is only called from outside the BPP in server mode; in client
70  * mode the BPP detects compression start time automatically by
71  * snooping message types */
72 void ssh1_bpp_start_compression(BinaryPacketProtocol *bpp);
73 
74 /* Helper routine which does common BPP initialisation, e.g. setting
75  * up in_pq and out_pq, and initialising input_consumer. */
76 void ssh_bpp_common_setup(BinaryPacketProtocol *);
77 
78 /* Common helper functions between the SSH-2 full and bare BPPs */
79 void ssh2_bpp_queue_disconnect(BinaryPacketProtocol *bpp,
80                                const char *msg, int category);
81 bool ssh2_bpp_check_unimplemented(BinaryPacketProtocol *bpp, PktIn *pktin);
82 
83 /* Convenience macro for BPPs to send formatted strings to the Event
84  * Log. Assumes a function parameter called 'bpp' is in scope. */
85 #define bpp_logevent(...) ( \
86     logevent_and_free((bpp)->logctx, dupprintf(__VA_ARGS__)))
87 
88 /*
89  * Structure that tracks how much data is sent and received, for
90  * purposes of triggering an SSH-2 rekey when either one gets over a
91  * configured limit. In each direction, the flag 'running' indicates
92  * that we haven't hit the limit yet, and 'remaining' tracks how much
93  * longer until we do. The function dts_consume() subtracts a given
94  * amount from the counter in a particular direction, and sets
95  * 'expired' if the limit has been hit.
96  *
97  * The limit is sticky: once 'running' has flipped to false,
98  * 'remaining' is no longer decremented, so it shouldn't dangerously
99  * wrap round.
100  */
101 struct DataTransferStatsDirection {
102     bool running, expired;
103     unsigned long remaining;
104 };
105 struct DataTransferStats {
106     struct DataTransferStatsDirection in, out;
107 };
dts_consume(struct DataTransferStatsDirection * s,unsigned long size_consumed)108 static inline void dts_consume(struct DataTransferStatsDirection *s,
109                                unsigned long size_consumed)
110 {
111     if (s->running) {
112         if (s->remaining <= size_consumed) {
113             s->running = false;
114             s->expired = true;
115         } else {
116             s->remaining -= size_consumed;
117         }
118     }
119 }
dts_reset(struct DataTransferStatsDirection * s,unsigned long starting_size)120 static inline void dts_reset(struct DataTransferStatsDirection *s,
121                              unsigned long starting_size)
122 {
123     s->expired = false;
124     s->remaining = starting_size;
125     /*
126      * The semantics of setting CONF_ssh_rekey_data to zero are to
127      * disable data-volume based rekeying completely. So if the
128      * starting size is actually zero, we don't set 'running' to true
129      * in the first place, which means we won't ever set the expired
130      * flag.
131      */
132     s->running = (starting_size != 0);
133 }
134 
135 BinaryPacketProtocol *ssh2_bpp_new(
136     LogContext *logctx, struct DataTransferStats *stats, bool is_server);
137 void ssh2_bpp_new_outgoing_crypto(
138     BinaryPacketProtocol *bpp,
139     const ssh_cipheralg *cipher, const void *ckey, const void *iv,
140     const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
141     const ssh_compression_alg *compression, bool delayed_compression);
142 void ssh2_bpp_new_incoming_crypto(
143     BinaryPacketProtocol *bpp,
144     const ssh_cipheralg *cipher, const void *ckey, const void *iv,
145     const ssh2_macalg *mac, bool etm_mode, const void *mac_key,
146     const ssh_compression_alg *compression, bool delayed_compression);
147 
148 /*
149  * A query method specific to the interface between ssh2transport and
150  * ssh2bpp. If true, it indicates that we're potentially in the
151  * race-condition-prone part of delayed compression setup and so
152  * asynchronous outgoing transport-layer packets are currently not
153  * being sent, which means in particular that it would be a bad idea
154  * to start a rekey because then we'd stop responding to anything
155  * _other_ than transport-layer packets and deadlock the protocol.
156  */
157 bool ssh2_bpp_rekey_inadvisable(BinaryPacketProtocol *bpp);
158 
159 BinaryPacketProtocol *ssh2_bare_bpp_new(LogContext *logctx);
160 
161 /*
162  * The initial code to handle the SSH version exchange is also
163  * structured as an implementation of BinaryPacketProtocol, because
164  * that makes it easy to switch from that to the next BPP once it
165  * tells us which one we're using.
166  */
167 struct ssh_version_receiver {
168     void (*got_ssh_version)(struct ssh_version_receiver *rcv,
169                             int major_version);
170 };
171 BinaryPacketProtocol *ssh_verstring_new(
172     Conf *conf, LogContext *logctx, bool bare_connection_mode,
173     const char *protoversion, struct ssh_version_receiver *rcv,
174     bool server_mode, const char *impl_name);
175 const char *ssh_verstring_get_remote(BinaryPacketProtocol *);
176 const char *ssh_verstring_get_local(BinaryPacketProtocol *);
177 int ssh_verstring_get_bugs(BinaryPacketProtocol *);
178 
179 #endif /* PUTTY_SSHBPP_H */
180