2 * Dropbear - a SSH2 server
4 * Copyright (c) 2002,2003 Matt Johnston
5 * Copyright (c) 2004 by Mihnea Stoenescu
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
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() {
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
:
59 if (buf_getmpint(ses
.payload
, &dh_e
) != DROPBEAR_SUCCESS
) {
60 dropbear_exit("Bad kex value");
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
);
70 if (ses
.payload
->pos
!= ses
.payload
->len
) {
71 dropbear_exit("Bad kex value");
74 send_msg_kexdh_reply(&dh_e
, ecdh_qs
);
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
) {
92 char *fn_dir
= m_strdup(fn
);
93 char *dir
= dirname(fn_dir
);
94 int dirfd
= open(dir
, O_RDONLY
);
97 if (fsync(dirfd
) != 0) {
98 TRACE(("fsync of directory %s failed: %s", dir
, strerror(errno
)))
102 TRACE(("error opening directory %s for fsync: %s", dir
, strerror(errno
)))
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
) {
124 case DROPBEAR_SIGNKEY_RSA
:
125 fn
= RSA_PRIV_FILENAME
;
129 case DROPBEAR_SIGNKEY_DSS
:
130 fn
= DSS_PRIV_FILENAME
;
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
;
144 if (readhostkey(fn
, svr_opts
.hostkey
, &type
) == DROPBEAR_SUCCESS
) {
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
) {
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
,
161 /* XXX fallback to non-atomic copy for some filesystems? */
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
) {
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",
192 if (ret
== DROPBEAR_FAILURE
)
194 dropbear_exit("Couldn't read or generate hostkey %s", fn
);
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 */
212 #ifdef DROPBEAR_DELAY_HOSTKEY
213 if (svr_opts
.delay_hostkey
)
215 svr_ensure_hostkey();
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
);
230 buf_putmpint(ses
.writepayload
, &dh_param
->pub
);
231 free_kexdh_param(dh_param
);
234 case DROPBEAR_KEX_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
);
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
, (const char*)param
->pub
, CURVE25519_LEN
);
251 free_kexcurve25519_param(param
);
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 */
264 TRACE(("leave send_msg_kexdh_reply"))