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
[0] = SSH_MSG_NEWKEYS
;
84 ses
.requirenext
[1] = 0;
85 TRACE(("leave recv_msg_kexdh_init"))
88 #ifdef DROPBEAR_DELAY_HOSTKEY
89 static void svr_ensure_hostkey() {
91 const char* fn
= NULL
;
93 enum signkey_type type
= ses
.newkeys
->algo_hostkey
;
94 void **hostkey
= signkey_key_ptr(svr_opts
.hostkey
, type
);
95 int ret
= DROPBEAR_FAILURE
;
97 if (hostkey
&& *hostkey
) {
104 case DROPBEAR_SIGNKEY_RSA
:
105 fn
= RSA_PRIV_FILENAME
;
109 case DROPBEAR_SIGNKEY_DSS
:
110 fn
= DSS_PRIV_FILENAME
;
113 #ifdef DROPBEAR_ECDSA
114 case DROPBEAR_SIGNKEY_ECDSA_NISTP256
:
115 case DROPBEAR_SIGNKEY_ECDSA_NISTP384
:
116 case DROPBEAR_SIGNKEY_ECDSA_NISTP521
:
117 fn
= ECDSA_PRIV_FILENAME
;
124 if (readhostkey(fn
, svr_opts
.hostkey
, &type
) == DROPBEAR_SUCCESS
) {
128 fn_temp
= m_malloc(strlen(fn
) + 20);
129 snprintf(fn_temp
, strlen(fn
)+20, "%s.tmp%d", fn
, getpid());
131 if (signkey_generate(type
, 0, fn_temp
) == DROPBEAR_FAILURE
) {
135 if (link(fn_temp
, fn
) < 0) {
136 /* It's OK to get EEXIST - we probably just lost a race
137 with another connection to generate the key */
138 if (errno
!= EEXIST
) {
139 dropbear_log(LOG_ERR
, "Failed moving key file to %s: %s", fn
,
141 /* XXX fallback to non-atomic copy for some filesystems? */
146 ret
= readhostkey(fn
, svr_opts
.hostkey
, &type
);
148 if (ret
== DROPBEAR_SUCCESS
) {
151 buffer
*key_buf
= buf_new(MAX_PUBKEY_SIZE
);
152 buf_put_pub_key(key_buf
, svr_opts
.hostkey
, type
);
153 buf_setpos(key_buf
, 4);
154 len
= key_buf
->len
- key_buf
->pos
;
155 fp
= sign_key_fingerprint(buf_getptr(key_buf
, len
), len
);
156 dropbear_log(LOG_INFO
, "Generated hostkey %s, fingerprint is %s",
168 if (ret
== DROPBEAR_FAILURE
)
170 dropbear_exit("Couldn't read or generate hostkey %s", fn
);
175 /* Generate our side of the diffie-hellman key exchange value (dh_f), and
176 * calculate the session key using the diffie-hellman algorithm. Following
177 * that, the session hash is calculated, and signed with RSA or DSS. The
178 * result is sent to the client.
180 * See the transport RFC4253 section 8 for details
181 * or RFC5656 section 4 for elliptic curve variant. */
182 static void send_msg_kexdh_reply(mp_int
*dh_e
, buffer
*ecdh_qs
) {
183 TRACE(("enter send_msg_kexdh_reply"))
185 /* we can start creating the kexdh_reply packet */
188 #ifdef DROPBEAR_DELAY_HOSTKEY
189 if (svr_opts
.delay_hostkey
)
191 svr_ensure_hostkey();
195 buf_putbyte(ses
.writepayload
, SSH_MSG_KEXDH_REPLY
);
196 buf_put_pub_key(ses
.writepayload
, svr_opts
.hostkey
,
197 ses
.newkeys
->algo_hostkey
);
199 switch (ses
.newkeys
->algo_kex
->mode
) {
200 case DROPBEAR_KEX_NORMAL_DH
:
202 struct kex_dh_param
* dh_param
= gen_kexdh_param();
203 kexdh_comb_key(dh_param
, dh_e
, svr_opts
.hostkey
);
206 buf_putmpint(ses
.writepayload
, &dh_param
->pub
);
207 free_kexdh_param(dh_param
);
210 case DROPBEAR_KEX_ECDH
:
213 struct kex_ecdh_param
*ecdh_param
= gen_kexecdh_param();
214 kexecdh_comb_key(ecdh_param
, ecdh_qs
, svr_opts
.hostkey
);
216 buf_put_ecc_raw_pubkey_string(ses
.writepayload
, &ecdh_param
->key
);
217 free_kexecdh_param(ecdh_param
);
221 case DROPBEAR_KEX_CURVE25519
:
222 #ifdef DROPBEAR_CURVE25519
224 struct kex_curve25519_param
*param
= gen_kexcurve25519_param();
225 kexcurve25519_comb_key(param
, ecdh_qs
, svr_opts
.hostkey
);
226 buf_putstring(ses
.writepayload
, param
->pub
, CURVE25519_LEN
);
227 free_kexcurve25519_param(param
);
233 /* calc the signature */
234 buf_put_sign(ses
.writepayload
, svr_opts
.hostkey
,
235 ses
.newkeys
->algo_hostkey
, ses
.hash
);
237 /* the SSH_MSG_KEXDH_REPLY is done */
240 TRACE(("leave send_msg_kexdh_reply"))