dropbear: update to 2015.67
[tomato.git] / release / src-rt-6.x.4708 / router / dropbear / svr-kex.c
blob6cc5433a3c5dc31c5391411df9179eb22027e42f
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:
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
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. */
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"
40 static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs);
42 /* Handle a diffie-hellman key exchange initialisation. This involves
43 * calculating a session key reply value, and corresponding hash. These
44 * are carried out by send_msg_kexdh_reply(). recv_msg_kexdh_init() calls
45 * that function, then brings the new keys into use */
46 void recv_msg_kexdh_init() {
48 DEF_MP_INT(dh_e);
49 buffer *ecdh_qs = NULL;
51 TRACE(("enter recv_msg_kexdh_init"))
52 if (!ses.kexstate.recvkexinit) {
53 dropbear_exit("Premature kexdh_init message received");
56 switch (ses.newkeys->algo_kex->mode) {
57 case DROPBEAR_KEX_NORMAL_DH:
58 m_mp_init(&dh_e);
59 if (buf_getmpint(ses.payload, &dh_e) != DROPBEAR_SUCCESS) {
60 dropbear_exit("Bad kex value");
62 break;
63 case DROPBEAR_KEX_ECDH:
64 case DROPBEAR_KEX_CURVE25519:
65 #if defined(DROPBEAR_ECDH) || defined(DROPBEAR_CURVE25519)
66 ecdh_qs = buf_getstringbuf(ses.payload);
67 #endif
68 break;
70 if (ses.payload->pos != ses.payload->len) {
71 dropbear_exit("Bad kex value");
74 send_msg_kexdh_reply(&dh_e, ecdh_qs);
76 mp_clear(&dh_e);
77 if (ecdh_qs) {
78 buf_free(ecdh_qs);
79 ecdh_qs = NULL;
82 send_msg_newkeys();
83 ses.requirenext = SSH_MSG_NEWKEYS;
84 TRACE(("leave recv_msg_kexdh_init"))
88 #ifdef DROPBEAR_DELAY_HOSTKEY
90 static void fsync_parent_dir(const char* fn) {
91 #ifdef HAVE_LIBGEN_H
92 char *fn_dir = m_strdup(fn);
93 char *dir = dirname(fn_dir);
94 int dirfd = open(dir, O_RDONLY);
96 if (dirfd != -1) {
97 if (fsync(dirfd) != 0) {
98 TRACE(("fsync of directory %s failed: %s", dir, strerror(errno)))
100 m_close(dirfd);
101 } else {
102 TRACE(("error opening directory %s for fsync: %s", dir, strerror(errno)))
105 free(fn_dir);
106 #endif
109 static void svr_ensure_hostkey() {
111 const char* fn = NULL;
112 char *fn_temp = NULL;
113 enum signkey_type type = ses.newkeys->algo_hostkey;
114 void **hostkey = signkey_key_ptr(svr_opts.hostkey, type);
115 int ret = DROPBEAR_FAILURE;
117 if (hostkey && *hostkey) {
118 return;
121 switch (type)
123 #ifdef DROPBEAR_RSA
124 case DROPBEAR_SIGNKEY_RSA:
125 fn = RSA_PRIV_FILENAME;
126 break;
127 #endif
128 #ifdef DROPBEAR_DSS
129 case DROPBEAR_SIGNKEY_DSS:
130 fn = DSS_PRIV_FILENAME;
131 break;
132 #endif
133 #ifdef DROPBEAR_ECDSA
134 case DROPBEAR_SIGNKEY_ECDSA_NISTP256:
135 case DROPBEAR_SIGNKEY_ECDSA_NISTP384:
136 case DROPBEAR_SIGNKEY_ECDSA_NISTP521:
137 fn = ECDSA_PRIV_FILENAME;
138 break;
139 #endif
140 default:
141 (void)0;
144 if (readhostkey(fn, svr_opts.hostkey, &type) == DROPBEAR_SUCCESS) {
145 return;
148 fn_temp = m_malloc(strlen(fn) + 20);
149 snprintf(fn_temp, strlen(fn)+20, "%s.tmp%d", fn, getpid());
151 if (signkey_generate(type, 0, fn_temp) == DROPBEAR_FAILURE) {
152 goto out;
155 if (link(fn_temp, fn) < 0) {
156 /* It's OK to get EEXIST - we probably just lost a race
157 with another connection to generate the key */
158 if (errno != EEXIST) {
159 dropbear_log(LOG_ERR, "Failed moving key file to %s: %s", fn,
160 strerror(errno));
161 /* XXX fallback to non-atomic copy for some filesystems? */
162 goto out;
166 /* ensure directory update is flushed to disk, otherwise we can end up
167 with zero-byte hostkey files if the power goes off */
168 fsync_parent_dir(fn);
170 ret = readhostkey(fn, svr_opts.hostkey, &type);
172 if (ret == DROPBEAR_SUCCESS) {
173 char *fp = NULL;
174 unsigned int len;
175 buffer *key_buf = buf_new(MAX_PUBKEY_SIZE);
176 buf_put_pub_key(key_buf, svr_opts.hostkey, type);
177 buf_setpos(key_buf, 4);
178 len = key_buf->len - key_buf->pos;
179 fp = sign_key_fingerprint(buf_getptr(key_buf, len), len);
180 dropbear_log(LOG_INFO, "Generated hostkey %s, fingerprint is %s",
181 fn, fp);
182 m_free(fp);
183 buf_free(key_buf);
186 out:
187 if (fn_temp) {
188 unlink(fn_temp);
189 m_free(fn_temp);
192 if (ret == DROPBEAR_FAILURE)
194 dropbear_exit("Couldn't read or generate hostkey %s", fn);
197 #endif
199 /* Generate our side of the diffie-hellman key exchange value (dh_f), and
200 * calculate the session key using the diffie-hellman algorithm. Following
201 * that, the session hash is calculated, and signed with RSA or DSS. The
202 * result is sent to the client.
204 * See the transport RFC4253 section 8 for details
205 * or RFC5656 section 4 for elliptic curve variant. */
206 static void send_msg_kexdh_reply(mp_int *dh_e, buffer *ecdh_qs) {
207 TRACE(("enter send_msg_kexdh_reply"))
209 /* we can start creating the kexdh_reply packet */
210 CHECKCLEARTOWRITE();
212 #ifdef DROPBEAR_DELAY_HOSTKEY
213 if (svr_opts.delay_hostkey)
215 svr_ensure_hostkey();
217 #endif
219 buf_putbyte(ses.writepayload, SSH_MSG_KEXDH_REPLY);
220 buf_put_pub_key(ses.writepayload, svr_opts.hostkey,
221 ses.newkeys->algo_hostkey);
223 switch (ses.newkeys->algo_kex->mode) {
224 case DROPBEAR_KEX_NORMAL_DH:
226 struct kex_dh_param * dh_param = gen_kexdh_param();
227 kexdh_comb_key(dh_param, dh_e, svr_opts.hostkey);
229 /* put f */
230 buf_putmpint(ses.writepayload, &dh_param->pub);
231 free_kexdh_param(dh_param);
233 break;
234 case DROPBEAR_KEX_ECDH:
235 #ifdef DROPBEAR_ECDH
237 struct kex_ecdh_param *ecdh_param = gen_kexecdh_param();
238 kexecdh_comb_key(ecdh_param, ecdh_qs, svr_opts.hostkey);
240 buf_put_ecc_raw_pubkey_string(ses.writepayload, &ecdh_param->key);
241 free_kexecdh_param(ecdh_param);
243 #endif
244 break;
245 case DROPBEAR_KEX_CURVE25519:
246 #ifdef DROPBEAR_CURVE25519
248 struct kex_curve25519_param *param = gen_kexcurve25519_param();
249 kexcurve25519_comb_key(param, ecdh_qs, svr_opts.hostkey);
250 buf_putstring(ses.writepayload, param->pub, CURVE25519_LEN);
251 free_kexcurve25519_param(param);
253 #endif
254 break;
257 /* calc the signature */
258 buf_put_sign(ses.writepayload, svr_opts.hostkey,
259 ses.newkeys->algo_hostkey, ses.hash);
261 /* the SSH_MSG_KEXDH_REPLY is done */
262 encrypt_packet();
264 TRACE(("leave send_msg_kexdh_reply"))