/src/dropbear/src/svr-kex.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Dropbear - a SSH2 server |
3 | | * |
4 | | * Copyright (c) 2002,2003 Matt Johnston |
5 | | * Copyright (c) 2004 by Mihnea Stoenescu |
6 | | * All rights reserved. |
7 | | * |
8 | | * Permission is hereby granted, free of charge, to any person obtaining a copy |
9 | | * of this software and associated documentation files (the "Software"), to deal |
10 | | * in the Software without restriction, including without limitation the rights |
11 | | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
12 | | * copies of the Software, and to permit persons to whom the Software is |
13 | | * furnished to do so, subject to the following conditions: |
14 | | * |
15 | | * The above copyright notice and this permission notice shall be included in |
16 | | * all copies or substantial portions of the Software. |
17 | | * |
18 | | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
19 | | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
20 | | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
21 | | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
22 | | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
23 | | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
24 | | * SOFTWARE. */ |
25 | | |
26 | | #include "includes.h" |
27 | | #include "dbutil.h" |
28 | | #include "algo.h" |
29 | | #include "buffer.h" |
30 | | #include "session.h" |
31 | | #include "kex.h" |
32 | | #include "ssh.h" |
33 | | #include "packet.h" |
34 | | #include "bignum.h" |
35 | | #include "dbrandom.h" |
36 | | #include "runopts.h" |
37 | | #include "ecc.h" |
38 | | #include "gensignkey.h" |
39 | | |
40 | | static void send_msg_kexdh_reply(mp_int *dh_e, buffer *q_c); |
41 | | #if DROPBEAR_EXT_INFO |
42 | | static void send_msg_ext_info(void); |
43 | | #endif |
44 | | |
45 | | /* Handle a diffie-hellman key exchange initialisation. This involves |
46 | | * calculating a session key reply value, and corresponding hash. These |
47 | | * are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls |
48 | | * that function, then brings the new keys into use */ |
49 | 18.7k | void recv_msg_kexdh_init() { |
50 | 18.7k | DEF_MP_INT(dh_e); |
51 | 18.7k | buffer *q_c = NULL; |
52 | | |
53 | 18.7k | TRACE(("enter recv_msg_kexdh_init")) |
54 | 18.7k | if (!ses.kexstate.recvkexinit) { |
55 | 8 | dropbear_exit("Premature kexdh_init message received"); |
56 | 8 | } |
57 | | |
58 | 18.7k | switch (ses.newkeys->algo_kex->mode) { |
59 | 0 | #if DROPBEAR_NORMAL_DH |
60 | 9.41k | case DROPBEAR_KEX_NORMAL_DH: |
61 | 9.41k | m_mp_init(&dh_e); |
62 | 9.41k | if (buf_getmpint(ses.payload, &dh_e) != DROPBEAR_SUCCESS) { |
63 | 46 | dropbear_exit("Bad kex value"); |
64 | 46 | } |
65 | 9.36k | break; |
66 | 9.36k | #endif |
67 | 9.36k | #if DROPBEAR_ECDH |
68 | 9.36k | case DROPBEAR_KEX_ECDH: |
69 | 350 | #endif |
70 | 350 | #if DROPBEAR_CURVE25519 |
71 | 9.30k | case DROPBEAR_KEX_CURVE25519: |
72 | 9.30k | #endif |
73 | 9.30k | #if DROPBEAR_PQHYBRID |
74 | 9.31k | case DROPBEAR_KEX_PQHYBRID: |
75 | 9.31k | #endif |
76 | 9.31k | #if DROPBEAR_ECDH || DROPBEAR_CURVE25519 || DROPBEAR_PQHYBRID |
77 | 9.31k | q_c = buf_getstringbuf(ses.payload); |
78 | 9.31k | break; |
79 | 18.7k | #endif |
80 | 18.7k | } |
81 | 18.5k | if (ses.payload->pos != ses.payload->len) { |
82 | 62 | dropbear_exit("Bad kex value"); |
83 | 62 | } |
84 | | |
85 | 18.5k | send_msg_kexdh_reply(&dh_e, q_c); |
86 | | |
87 | 18.5k | mp_clear(&dh_e); |
88 | 18.5k | if (q_c) { |
89 | 8.88k | buf_free(q_c); |
90 | 8.88k | q_c = NULL; |
91 | 8.88k | } |
92 | | |
93 | 18.5k | send_msg_newkeys(); |
94 | | |
95 | 18.5k | #if DROPBEAR_EXT_INFO |
96 | | /* Only send it following the first newkeys */ |
97 | 18.5k | if (!ses.kexstate.donesecondkex && ses.allow_ext_info) { |
98 | 69 | send_msg_ext_info(); |
99 | 69 | } |
100 | 18.5k | #endif |
101 | | |
102 | 18.5k | ses.requirenext = SSH_MSG_NEWKEYS; |
103 | 18.5k | TRACE(("leave recv_msg_kexdh_init")) |
104 | 18.5k | } |
105 | | |
106 | | |
107 | | #if DROPBEAR_DELAY_HOSTKEY |
108 | | |
109 | 0 | static void svr_ensure_hostkey() { |
110 | |
|
111 | 0 | const char* fn = NULL; |
112 | 0 | char *expand_fn = NULL; |
113 | 0 | enum signkey_type type = ses.newkeys->algo_hostkey; |
114 | 0 | void **hostkey = signkey_key_ptr(svr_opts.hostkey, type); |
115 | 0 | int ret = DROPBEAR_FAILURE; |
116 | |
|
117 | 0 | if (hostkey && *hostkey) { |
118 | 0 | return; |
119 | 0 | } |
120 | | |
121 | 0 | switch (type) |
122 | 0 | { |
123 | 0 | #if DROPBEAR_RSA |
124 | 0 | case DROPBEAR_SIGNKEY_RSA: |
125 | 0 | fn = RSA_PRIV_FILENAME; |
126 | 0 | break; |
127 | 0 | #endif |
128 | 0 | #if DROPBEAR_DSS |
129 | 0 | case DROPBEAR_SIGNKEY_DSS: |
130 | 0 | fn = DSS_PRIV_FILENAME; |
131 | 0 | break; |
132 | 0 | #endif |
133 | 0 | #if DROPBEAR_ECDSA |
134 | 0 | case DROPBEAR_SIGNKEY_ECDSA_NISTP256: |
135 | 0 | case DROPBEAR_SIGNKEY_ECDSA_NISTP384: |
136 | 0 | case DROPBEAR_SIGNKEY_ECDSA_NISTP521: |
137 | 0 | fn = ECDSA_PRIV_FILENAME; |
138 | 0 | break; |
139 | 0 | #endif |
140 | 0 | #if DROPBEAR_ED25519 |
141 | 0 | case DROPBEAR_SIGNKEY_ED25519: |
142 | 0 | fn = ED25519_PRIV_FILENAME; |
143 | 0 | break; |
144 | 0 | #endif |
145 | 0 | default: |
146 | 0 | dropbear_assert(0); |
147 | 0 | } |
148 | | |
149 | 0 | expand_fn = expand_homedir_path(fn); |
150 | |
|
151 | 0 | ret = readhostkey(expand_fn, svr_opts.hostkey, &type); |
152 | 0 | if (ret == DROPBEAR_SUCCESS) { |
153 | 0 | goto out; |
154 | 0 | } |
155 | | |
156 | 0 | if (signkey_generate(type, 0, expand_fn, 1) == DROPBEAR_FAILURE) { |
157 | 0 | goto out; |
158 | 0 | } |
159 | | |
160 | | /* Read what we just generated (or another process raced us) */ |
161 | 0 | ret = readhostkey(expand_fn, svr_opts.hostkey, &type); |
162 | |
|
163 | 0 | if (ret == DROPBEAR_SUCCESS) { |
164 | 0 | char *fp = NULL; |
165 | 0 | unsigned int len; |
166 | 0 | buffer *key_buf = buf_new(MAX_PUBKEY_SIZE); |
167 | 0 | buf_put_pub_key(key_buf, svr_opts.hostkey, type); |
168 | 0 | buf_setpos(key_buf, 4); |
169 | 0 | len = key_buf->len - key_buf->pos; |
170 | 0 | fp = sign_key_fingerprint(buf_getptr(key_buf, len), len); |
171 | 0 | dropbear_log(LOG_INFO, "Generated hostkey %s, fingerprint is %s", |
172 | 0 | expand_fn, fp); |
173 | 0 | m_free(fp); |
174 | 0 | buf_free(key_buf); |
175 | 0 | } |
176 | |
|
177 | 0 | out: |
178 | 0 | if (ret == DROPBEAR_FAILURE) { |
179 | 0 | dropbear_exit("Couldn't read or generate hostkey %s", expand_fn); |
180 | 0 | } |
181 | 0 | m_free(expand_fn); |
182 | 0 | } |
183 | | #endif |
184 | | |
185 | | /* Generate our side of the diffie-hellman key exchange value (dh_f), and |
186 | | * calculate the session key using the diffie-hellman algorithm. Following |
187 | | * that, the session hash is calculated, and signed with RSA or DSS. The |
188 | | * result is sent to the client. |
189 | | * |
190 | | * See the transport RFC4253 section 8 for details |
191 | | * or RFC5656 section 4 for elliptic curve variant. */ |
192 | 18.5k | static void send_msg_kexdh_reply(mp_int *dh_e, buffer *q_c) { |
193 | 18.5k | TRACE(("enter send_msg_kexdh_reply")) |
194 | | |
195 | | /* we can start creating the kexdh_reply packet */ |
196 | 18.5k | CHECKCLEARTOWRITE(); |
197 | | |
198 | 18.5k | #if DROPBEAR_DELAY_HOSTKEY |
199 | 18.5k | if (svr_opts.delay_hostkey) |
200 | 0 | { |
201 | 0 | svr_ensure_hostkey(); |
202 | 0 | } |
203 | 18.5k | #endif |
204 | | |
205 | 18.5k | #if DROPBEAR_FUZZ |
206 | 18.5k | if (fuzz.fuzzing && fuzz.skip_kexmaths) { |
207 | 0 | fuzz_fake_send_kexdh_reply(); |
208 | 0 | return; |
209 | 0 | } |
210 | 18.5k | #endif |
211 | | |
212 | 18.5k | buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); |
213 | 18.5k | buf_put_pub_key(ses.writepayload, svr_opts.hostkey, |
214 | 18.5k | ses.newkeys->algo_hostkey); |
215 | | |
216 | 18.5k | switch (ses.newkeys->algo_kex->mode) { |
217 | 0 | #if DROPBEAR_NORMAL_DH |
218 | 9.30k | case DROPBEAR_KEX_NORMAL_DH: |
219 | 9.30k | { |
220 | 9.30k | struct kex_dh_param * dh_param = gen_kexdh_param(); |
221 | 9.30k | kexdh_comb_key(dh_param, dh_e, svr_opts.hostkey); |
222 | | |
223 | | /* put f */ |
224 | 9.30k | buf_putmpint(ses.writepayload, &dh_param->pub); |
225 | 9.30k | free_kexdh_param(dh_param); |
226 | 9.30k | } |
227 | 9.30k | break; |
228 | 0 | #endif |
229 | 0 | #if DROPBEAR_ECDH |
230 | 306 | case DROPBEAR_KEX_ECDH: |
231 | 306 | { |
232 | 306 | struct kex_ecdh_param *ecdh_param = gen_kexecdh_param(); |
233 | 306 | kexecdh_comb_key(ecdh_param, q_c, svr_opts.hostkey); |
234 | | |
235 | 306 | buf_put_ecc_raw_pubkey_string(ses.writepayload, &ecdh_param->key); |
236 | 306 | free_kexecdh_param(ecdh_param); |
237 | 306 | } |
238 | 306 | break; |
239 | 0 | #endif |
240 | 0 | #if DROPBEAR_CURVE25519 |
241 | 8.91k | case DROPBEAR_KEX_CURVE25519: |
242 | 8.91k | { |
243 | 8.91k | struct kex_curve25519_param *param = gen_kexcurve25519_param(); |
244 | 8.91k | kexcurve25519_comb_key(param, q_c, svr_opts.hostkey); |
245 | | |
246 | 8.91k | buf_putstring(ses.writepayload, param->pub, CURVE25519_LEN); |
247 | 8.91k | free_kexcurve25519_param(param); |
248 | 8.91k | } |
249 | 8.91k | break; |
250 | 0 | #endif |
251 | 0 | #if DROPBEAR_PQHYBRID |
252 | 1 | case DROPBEAR_KEX_PQHYBRID: |
253 | 1 | { |
254 | 1 | struct kex_pqhybrid_param *param = gen_kexpqhybrid_param(); |
255 | 1 | kexpqhybrid_comb_key(param, q_c, svr_opts.hostkey); |
256 | | |
257 | 1 | buf_putbufstring(ses.writepayload, param->concat_public); |
258 | 1 | free_kexpqhybrid_param(param); |
259 | 1 | } |
260 | 1 | break; |
261 | 18.5k | #endif |
262 | 18.5k | } |
263 | | |
264 | | /* calc the signature */ |
265 | 18.1k | buf_put_sign(ses.writepayload, svr_opts.hostkey, |
266 | 18.1k | ses.newkeys->algo_signature, ses.hash); |
267 | | |
268 | | /* the SSH_MSG_KEXDH_REPLY is done */ |
269 | 18.1k | encrypt_packet(); |
270 | | |
271 | 18.1k | TRACE(("leave send_msg_kexdh_reply")) |
272 | 18.1k | } |
273 | | |
274 | | #if DROPBEAR_EXT_INFO |
275 | | /* Only used for server-sig-algs on the server side */ |
276 | 69 | static void send_msg_ext_info(void) { |
277 | 69 | TRACE(("enter send_msg_ext_info")) |
278 | | |
279 | 69 | buf_putbyte(ses.writepayload, SSH_MSG_EXT_INFO); |
280 | | /* nr-extensions */ |
281 | 69 | buf_putint(ses.writepayload, 1); |
282 | | |
283 | 69 | buf_putstring(ses.writepayload, SSH_SERVER_SIG_ALGS, strlen(SSH_SERVER_SIG_ALGS)); |
284 | 69 | buf_put_algolist_all(ses.writepayload, sigalgs, 1); |
285 | | |
286 | 69 | encrypt_packet(); |
287 | | |
288 | 69 | TRACE(("leave send_msg_ext_info")) |
289 | 69 | } |
290 | | #endif |