/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 *ecdh_qs); |
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 | 8.92k | void recv_msg_kexdh_init() { |
50 | 8.92k | DEF_MP_INT(dh_e); |
51 | 8.92k | buffer *ecdh_qs = NULL; |
52 | | |
53 | 8.92k | TRACE(("enter recv_msg_kexdh_init")) |
54 | 8.92k | if (!ses.kexstate.recvkexinit) { |
55 | 3 | dropbear_exit("Premature kexdh_init message received"); |
56 | 3 | } |
57 | | |
58 | 8.91k | switch (ses.newkeys->algo_kex->mode) { |
59 | 0 | #if DROPBEAR_NORMAL_DH |
60 | 2.89k | case DROPBEAR_KEX_NORMAL_DH: |
61 | 2.89k | m_mp_init(&dh_e); |
62 | 2.89k | if (buf_getmpint(ses.payload, &dh_e) != DROPBEAR_SUCCESS) { |
63 | 3 | dropbear_exit("Bad kex value"); |
64 | 3 | } |
65 | 2.89k | break; |
66 | 2.89k | #endif |
67 | 2.89k | #if DROPBEAR_ECDH |
68 | 2.89k | case DROPBEAR_KEX_ECDH: |
69 | 2.67k | #endif |
70 | 2.67k | #if DROPBEAR_CURVE25519 |
71 | 6.01k | case DROPBEAR_KEX_CURVE25519: |
72 | 6.01k | #endif |
73 | 6.01k | #if DROPBEAR_ECDH || DROPBEAR_CURVE25519 |
74 | 6.01k | ecdh_qs = buf_getstringbuf(ses.payload); |
75 | 6.01k | break; |
76 | 8.91k | #endif |
77 | 8.91k | } |
78 | 8.91k | if (ses.payload->pos != ses.payload->len) { |
79 | 38 | dropbear_exit("Bad kex value"); |
80 | 38 | } |
81 | | |
82 | 8.87k | send_msg_kexdh_reply(&dh_e, ecdh_qs); |
83 | | |
84 | 8.87k | mp_clear(&dh_e); |
85 | 8.87k | if (ecdh_qs) { |
86 | 6.00k | buf_free(ecdh_qs); |
87 | 6.00k | ecdh_qs = NULL; |
88 | 6.00k | } |
89 | | |
90 | 8.87k | send_msg_newkeys(); |
91 | | |
92 | 8.87k | #if DROPBEAR_EXT_INFO |
93 | | /* Only send it following the first newkeys */ |
94 | 8.87k | if (!ses.kexstate.donesecondkex && ses.allow_ext_info) { |
95 | 9 | send_msg_ext_info(); |
96 | 9 | } |
97 | 8.87k | #endif |
98 | | |
99 | 8.87k | ses.requirenext = SSH_MSG_NEWKEYS; |
100 | 8.87k | TRACE(("leave recv_msg_kexdh_init")) |
101 | 8.87k | } |
102 | | |
103 | | |
104 | | #if DROPBEAR_DELAY_HOSTKEY |
105 | | |
106 | 0 | static void svr_ensure_hostkey() { |
107 | |
|
108 | 0 | const char* fn = NULL; |
109 | 0 | char *expand_fn = NULL; |
110 | 0 | enum signkey_type type = ses.newkeys->algo_hostkey; |
111 | 0 | void **hostkey = signkey_key_ptr(svr_opts.hostkey, type); |
112 | 0 | int ret = DROPBEAR_FAILURE; |
113 | |
|
114 | 0 | if (hostkey && *hostkey) { |
115 | 0 | return; |
116 | 0 | } |
117 | | |
118 | 0 | switch (type) |
119 | 0 | { |
120 | 0 | #if DROPBEAR_RSA |
121 | 0 | case DROPBEAR_SIGNKEY_RSA: |
122 | 0 | fn = RSA_PRIV_FILENAME; |
123 | 0 | break; |
124 | 0 | #endif |
125 | 0 | #if DROPBEAR_DSS |
126 | 0 | case DROPBEAR_SIGNKEY_DSS: |
127 | 0 | fn = DSS_PRIV_FILENAME; |
128 | 0 | break; |
129 | 0 | #endif |
130 | 0 | #if DROPBEAR_ECDSA |
131 | 0 | case DROPBEAR_SIGNKEY_ECDSA_NISTP256: |
132 | 0 | case DROPBEAR_SIGNKEY_ECDSA_NISTP384: |
133 | 0 | case DROPBEAR_SIGNKEY_ECDSA_NISTP521: |
134 | 0 | fn = ECDSA_PRIV_FILENAME; |
135 | 0 | break; |
136 | 0 | #endif |
137 | 0 | #if DROPBEAR_ED25519 |
138 | 0 | case DROPBEAR_SIGNKEY_ED25519: |
139 | 0 | fn = ED25519_PRIV_FILENAME; |
140 | 0 | break; |
141 | 0 | #endif |
142 | 0 | default: |
143 | 0 | dropbear_assert(0); |
144 | 0 | } |
145 | | |
146 | 0 | expand_fn = expand_homedir_path(fn); |
147 | |
|
148 | 0 | ret = readhostkey(expand_fn, svr_opts.hostkey, &type); |
149 | 0 | if (ret == DROPBEAR_SUCCESS) { |
150 | 0 | goto out; |
151 | 0 | } |
152 | | |
153 | 0 | if (signkey_generate(type, 0, expand_fn, 1) == DROPBEAR_FAILURE) { |
154 | 0 | goto out; |
155 | 0 | } |
156 | | |
157 | | /* Read what we just generated (or another process raced us) */ |
158 | 0 | ret = readhostkey(expand_fn, svr_opts.hostkey, &type); |
159 | |
|
160 | 0 | if (ret == DROPBEAR_SUCCESS) { |
161 | 0 | char *fp = NULL; |
162 | 0 | unsigned int len; |
163 | 0 | buffer *key_buf = buf_new(MAX_PUBKEY_SIZE); |
164 | 0 | buf_put_pub_key(key_buf, svr_opts.hostkey, type); |
165 | 0 | buf_setpos(key_buf, 4); |
166 | 0 | len = key_buf->len - key_buf->pos; |
167 | 0 | fp = sign_key_fingerprint(buf_getptr(key_buf, len), len); |
168 | 0 | dropbear_log(LOG_INFO, "Generated hostkey %s, fingerprint is %s", |
169 | 0 | expand_fn, fp); |
170 | 0 | m_free(fp); |
171 | 0 | buf_free(key_buf); |
172 | 0 | } |
173 | |
|
174 | 0 | out: |
175 | 0 | if (ret == DROPBEAR_FAILURE) { |
176 | 0 | dropbear_exit("Couldn't read or generate hostkey %s", expand_fn); |
177 | 0 | } |
178 | 0 | m_free(expand_fn); |
179 | 0 | } |
180 | | #endif |
181 | | |
182 | | /* Generate our side of the diffie-hellman key exchange value (dh_f), and |
183 | | * calculate the session key using the diffie-hellman algorithm. Following |
184 | | * that, the session hash is calculated, and signed with RSA or DSS. The |
185 | | * result is sent to the client. |
186 | | * |
187 | | * See the transport RFC4253 section 8 for details |
188 | | * or RFC5656 section 4 for elliptic curve variant. */ |
189 | 8.87k | static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) { |
190 | 8.87k | TRACE(("enter send_msg_kexdh_reply")) |
191 | | |
192 | | /* we can start creating the kexdh_reply packet */ |
193 | 8.87k | CHECKCLEARTOWRITE(); |
194 | | |
195 | 8.87k | #if DROPBEAR_DELAY_HOSTKEY |
196 | 8.87k | if (svr_opts.delay_hostkey) |
197 | 0 | { |
198 | 0 | svr_ensure_hostkey(); |
199 | 0 | } |
200 | 8.87k | #endif |
201 | | |
202 | 8.87k | #if DROPBEAR_FUZZ |
203 | 8.87k | if (fuzz.fuzzing && fuzz.skip_kexmaths) { |
204 | 8.87k | fuzz_fake_send_kexdh_reply(); |
205 | 8.87k | return; |
206 | 8.87k | } |
207 | 0 | #endif |
208 | | |
209 | 0 | buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY); |
210 | 0 | buf_put_pub_key(ses.writepayload, svr_opts.hostkey, |
211 | 0 | ses.newkeys->algo_hostkey); |
212 | |
|
213 | 0 | switch (ses.newkeys->algo_kex->mode) { |
214 | 0 | #if DROPBEAR_NORMAL_DH |
215 | 0 | case DROPBEAR_KEX_NORMAL_DH: |
216 | 0 | { |
217 | 0 | struct kex_dh_param * dh_param = gen_kexdh_param(); |
218 | 0 | kexdh_comb_key(dh_param, dh_e, svr_opts.hostkey); |
219 | | |
220 | | /* put f */ |
221 | 0 | buf_putmpint(ses.writepayload, &dh_param->pub); |
222 | 0 | free_kexdh_param(dh_param); |
223 | 0 | } |
224 | 0 | break; |
225 | 0 | #endif |
226 | 0 | #if DROPBEAR_ECDH |
227 | 0 | case DROPBEAR_KEX_ECDH: |
228 | 0 | { |
229 | 0 | struct kex_ecdh_param *ecdh_param = gen_kexecdh_param(); |
230 | 0 | kexecdh_comb_key(ecdh_param, ecdh_qs, svr_opts.hostkey); |
231 | |
|
232 | 0 | buf_put_ecc_raw_pubkey_string(ses.writepayload, &ecdh_param->key); |
233 | 0 | free_kexecdh_param(ecdh_param); |
234 | 0 | } |
235 | 0 | break; |
236 | 0 | #endif |
237 | 0 | #if DROPBEAR_CURVE25519 |
238 | 0 | case DROPBEAR_KEX_CURVE25519: |
239 | 0 | { |
240 | 0 | struct kex_curve25519_param *param = gen_kexcurve25519_param(); |
241 | 0 | kexcurve25519_comb_key(param, ecdh_qs, svr_opts.hostkey); |
242 | |
|
243 | 0 | buf_putstring(ses.writepayload, param->pub, CURVE25519_LEN); |
244 | 0 | free_kexcurve25519_param(param); |
245 | 0 | } |
246 | 0 | break; |
247 | 0 | #endif |
248 | 0 | } |
249 | | |
250 | | /* calc the signature */ |
251 | 0 | buf_put_sign(ses.writepayload, svr_opts.hostkey, |
252 | 0 | ses.newkeys->algo_signature, ses.hash); |
253 | | |
254 | | /* the SSH_MSG_KEXDH_REPLY is done */ |
255 | 0 | encrypt_packet(); |
256 | |
|
257 | 0 | TRACE(("leave send_msg_kexdh_reply")) |
258 | 0 | } |
259 | | |
260 | | #if DROPBEAR_EXT_INFO |
261 | | /* Only used for server-sig-algs on the server side */ |
262 | 9 | static void send_msg_ext_info(void) { |
263 | 9 | TRACE(("enter send_msg_ext_info")) |
264 | | |
265 | 9 | buf_putbyte(ses.writepayload, SSH_MSG_EXT_INFO); |
266 | | /* nr-extensions */ |
267 | 9 | buf_putint(ses.writepayload, 1); |
268 | | |
269 | 9 | buf_putstring(ses.writepayload, SSH_SERVER_SIG_ALGS, strlen(SSH_SERVER_SIG_ALGS)); |
270 | 9 | buf_put_algolist_all(ses.writepayload, sigalgs, 1); |
271 | | |
272 | 9 | encrypt_packet(); |
273 | | |
274 | 9 | TRACE(("leave send_msg_ext_info")) |
275 | 9 | } |
276 | | #endif |