1*a03ec00cSchristos /* $NetBSD: kexgexc.c,v 1.17 2022/02/23 19:07:20 christos Exp $ */
2*a03ec00cSchristos /* $OpenBSD: kexgexc.c,v 1.38 2021/12/19 22:08:06 djm Exp $ */
3ca32bd8dSchristos /*
4ca32bd8dSchristos * Copyright (c) 2000 Niels Provos. All rights reserved.
5ca32bd8dSchristos * Copyright (c) 2001 Markus Friedl. All rights reserved.
6ca32bd8dSchristos *
7ca32bd8dSchristos * Redistribution and use in source and binary forms, with or without
8ca32bd8dSchristos * modification, are permitted provided that the following conditions
9ca32bd8dSchristos * are met:
10ca32bd8dSchristos * 1. Redistributions of source code must retain the above copyright
11ca32bd8dSchristos * notice, this list of conditions and the following disclaimer.
12ca32bd8dSchristos * 2. Redistributions in binary form must reproduce the above copyright
13ca32bd8dSchristos * notice, this list of conditions and the following disclaimer in the
14ca32bd8dSchristos * documentation and/or other materials provided with the distribution.
15ca32bd8dSchristos *
16ca32bd8dSchristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17ca32bd8dSchristos * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18ca32bd8dSchristos * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19ca32bd8dSchristos * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20ca32bd8dSchristos * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21ca32bd8dSchristos * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22ca32bd8dSchristos * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23ca32bd8dSchristos * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24ca32bd8dSchristos * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25ca32bd8dSchristos * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26ca32bd8dSchristos */
27ca32bd8dSchristos
28313c6c94Schristos #include "includes.h"
29*a03ec00cSchristos __RCSID("$NetBSD: kexgexc.c,v 1.17 2022/02/23 19:07:20 christos Exp $");
304054ffb0Schristos
314054ffb0Schristos #include <sys/param.h>
32ca32bd8dSchristos #include <sys/types.h>
33ca32bd8dSchristos
34185c8f97Schristos #include <openssl/dh.h>
35185c8f97Schristos
36ca32bd8dSchristos #include <stdio.h>
37ca32bd8dSchristos #include <string.h>
38ca32bd8dSchristos #include <signal.h>
39ca32bd8dSchristos
40e4d43b82Schristos #include "sshkey.h"
41ca32bd8dSchristos #include "cipher.h"
42e4d43b82Schristos #include "digest.h"
43ca32bd8dSchristos #include "kex.h"
44ca32bd8dSchristos #include "log.h"
45ca32bd8dSchristos #include "packet.h"
46ca32bd8dSchristos #include "dh.h"
47ca32bd8dSchristos #include "ssh2.h"
48ca32bd8dSchristos #include "compat.h"
49e4d43b82Schristos #include "dispatch.h"
50e4d43b82Schristos #include "ssherr.h"
51e4d43b82Schristos #include "sshbuf.h"
52ee85abc4Schristos #include "misc.h"
53ca32bd8dSchristos
547a183406Schristos static int input_kex_dh_gex_group(int, u_int32_t, struct ssh *);
557a183406Schristos static int input_kex_dh_gex_reply(int, u_int32_t, struct ssh *);
56e4d43b82Schristos
57e4d43b82Schristos int
kexgex_client(struct ssh * ssh)58e4d43b82Schristos kexgex_client(struct ssh *ssh)
59ca32bd8dSchristos {
60e4d43b82Schristos struct kex *kex = ssh->kex;
61e4d43b82Schristos int r;
62e4d43b82Schristos u_int nbits;
63ca32bd8dSchristos
648a4530f9Schristos nbits = dh_estimate(kex->dh_need * 8);
65ca32bd8dSchristos
66e4d43b82Schristos kex->min = DH_GRP_MIN;
67e4d43b82Schristos kex->max = DH_GRP_MAX;
68e4d43b82Schristos kex->nbits = nbits;
6917418e98Schristos if (ssh->compat & SSH_BUG_DHGEX_LARGE)
70ee85abc4Schristos kex->nbits = MINIMUM(kex->nbits, 4096);
71ca32bd8dSchristos /* New GEX request */
72e4d43b82Schristos if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST)) != 0 ||
73e4d43b82Schristos (r = sshpkt_put_u32(ssh, kex->min)) != 0 ||
74e4d43b82Schristos (r = sshpkt_put_u32(ssh, kex->nbits)) != 0 ||
75e4d43b82Schristos (r = sshpkt_put_u32(ssh, kex->max)) != 0 ||
76e4d43b82Schristos (r = sshpkt_send(ssh)) != 0)
77e4d43b82Schristos goto out;
78ca32bd8dSchristos debug("SSH2_MSG_KEX_DH_GEX_REQUEST(%u<%u<%u) sent",
79e4d43b82Schristos kex->min, kex->nbits, kex->max);
80ca32bd8dSchristos #ifdef DEBUG_KEXDH
81ca32bd8dSchristos fprintf(stderr, "\nmin = %d, nbits = %d, max = %d\n",
82e4d43b82Schristos kex->min, kex->nbits, kex->max);
83ca32bd8dSchristos #endif
8417418e98Schristos debug("expecting SSH2_MSG_KEX_DH_GEX_GROUP");
85e4d43b82Schristos ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP,
86e4d43b82Schristos &input_kex_dh_gex_group);
87e4d43b82Schristos r = 0;
88e4d43b82Schristos out:
89e4d43b82Schristos return r;
90e4d43b82Schristos }
91ca32bd8dSchristos
92e4d43b82Schristos static int
input_kex_dh_gex_group(int type,u_int32_t seq,struct ssh * ssh)937a183406Schristos input_kex_dh_gex_group(int type, u_int32_t seq, struct ssh *ssh)
94e4d43b82Schristos {
95e4d43b82Schristos struct kex *kex = ssh->kex;
96e4d43b82Schristos BIGNUM *p = NULL, *g = NULL;
97aa36fcacSchristos const BIGNUM *pub_key;
98e4d43b82Schristos int r, bits;
99ca32bd8dSchristos
10017418e98Schristos debug("SSH2_MSG_KEX_DH_GEX_GROUP received");
10117418e98Schristos ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_GROUP, &kex_protocol_error);
102ca32bd8dSchristos
103aa36fcacSchristos if ((r = sshpkt_get_bignum2(ssh, &p)) != 0 ||
104aa36fcacSchristos (r = sshpkt_get_bignum2(ssh, &g)) != 0 ||
105e4d43b82Schristos (r = sshpkt_get_end(ssh)) != 0)
106e4d43b82Schristos goto out;
107e4d43b82Schristos if ((bits = BN_num_bits(p)) < 0 ||
108e4d43b82Schristos (u_int)bits < kex->min || (u_int)bits > kex->max) {
109e4d43b82Schristos r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
110e4d43b82Schristos goto out;
111e4d43b82Schristos }
112e4d43b82Schristos if ((kex->dh = dh_new_group(g, p)) == NULL) {
113e4d43b82Schristos r = SSH_ERR_ALLOC_FAIL;
114e4d43b82Schristos goto out;
115e4d43b82Schristos }
116e4d43b82Schristos p = g = NULL; /* belong to kex->dh now */
117ca32bd8dSchristos
118e4d43b82Schristos /* generate and send 'e', client DH public key */
119b400d007Schristos if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
120e4d43b82Schristos goto out;
121b400d007Schristos DH_get0_key(kex->dh, &pub_key, NULL);
122b400d007Schristos if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_INIT)) != 0 ||
123b400d007Schristos (r = sshpkt_put_bignum2(ssh, pub_key)) != 0 ||
124aa36fcacSchristos (r = sshpkt_send(ssh)) != 0)
125b400d007Schristos goto out;
126e4d43b82Schristos debug("SSH2_MSG_KEX_DH_GEX_INIT sent");
127ca32bd8dSchristos #ifdef DEBUG_KEXDH
128e4d43b82Schristos DHparams_print_fp(stderr, kex->dh);
129ca32bd8dSchristos fprintf(stderr, "pub= ");
130aa36fcacSchristos BN_print_fp(stderr, pub_key);
131ca32bd8dSchristos fprintf(stderr, "\n");
132ca32bd8dSchristos #endif
13317418e98Schristos debug("expecting SSH2_MSG_KEX_DH_GEX_REPLY");
134e4d43b82Schristos ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &input_kex_dh_gex_reply);
135e4d43b82Schristos r = 0;
136e4d43b82Schristos out:
137e4d43b82Schristos BN_clear_free(p);
138e4d43b82Schristos BN_clear_free(g);
139e4d43b82Schristos return r;
140e4d43b82Schristos }
141ca32bd8dSchristos
142e4d43b82Schristos static int
input_kex_dh_gex_reply(int type,u_int32_t seq,struct ssh * ssh)1437a183406Schristos input_kex_dh_gex_reply(int type, u_int32_t seq, struct ssh *ssh)
144e4d43b82Schristos {
145e4d43b82Schristos struct kex *kex = ssh->kex;
146aa36fcacSchristos BIGNUM *dh_server_pub = NULL;
147aa36fcacSchristos const BIGNUM *pub_key, *dh_p, *dh_g;
148aa36fcacSchristos struct sshbuf *shared_secret = NULL;
149aa36fcacSchristos struct sshbuf *tmp = NULL, *server_host_key_blob = NULL;
150e4d43b82Schristos struct sshkey *server_host_key = NULL;
151aa36fcacSchristos u_char *signature = NULL;
152e4d43b82Schristos u_char hash[SSH_DIGEST_MAX_LENGTH];
153aa36fcacSchristos size_t slen, hashlen;
154aa36fcacSchristos int r;
155ca32bd8dSchristos
15617418e98Schristos debug("SSH2_MSG_KEX_DH_GEX_REPLY received");
15717418e98Schristos ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REPLY, &kex_protocol_error);
15817418e98Schristos
159ca32bd8dSchristos /* key, cert */
160aa36fcacSchristos if ((r = sshpkt_getb_froms(ssh, &server_host_key_blob)) != 0)
161e4d43b82Schristos goto out;
162aa36fcacSchristos /* sshkey_fromb() consumes its buffer, so make a copy */
163aa36fcacSchristos if ((tmp = sshbuf_fromb(server_host_key_blob)) == NULL) {
164e4d43b82Schristos r = SSH_ERR_ALLOC_FAIL;
165e4d43b82Schristos goto out;
166e4d43b82Schristos }
167aa36fcacSchristos if ((r = sshkey_fromb(tmp, &server_host_key)) != 0 ||
168aa36fcacSchristos (r = kex_verify_host_key(ssh, server_host_key)) != 0)
169aa36fcacSchristos goto out;
170aa36fcacSchristos /* DH parameter f, server public DH key, signed H */
171aa36fcacSchristos if ((r = sshpkt_get_bignum2(ssh, &dh_server_pub)) != 0 ||
172e4d43b82Schristos (r = sshpkt_get_string(ssh, &signature, &slen)) != 0 ||
173e4d43b82Schristos (r = sshpkt_get_end(ssh)) != 0)
174e4d43b82Schristos goto out;
175aa36fcacSchristos if ((shared_secret = sshbuf_new()) == NULL) {
176e4d43b82Schristos r = SSH_ERR_ALLOC_FAIL;
177e4d43b82Schristos goto out;
178e4d43b82Schristos }
179aa36fcacSchristos if ((r = kex_dh_compute_key(kex, dh_server_pub, shared_secret)) != 0)
180e4d43b82Schristos goto out;
181e4d43b82Schristos if (ssh->compat & SSH_OLD_DHGEX)
182e4d43b82Schristos kex->min = kex->max = -1;
183ca32bd8dSchristos
184ca32bd8dSchristos /* calc and verify H */
185b400d007Schristos DH_get0_key(kex->dh, &pub_key, NULL);
186aa36fcacSchristos DH_get0_pqg(kex->dh, &dh_p, NULL, &dh_g);
187aa36fcacSchristos hashlen = sizeof(hash);
188e4d43b82Schristos if ((r = kexgex_hash(
1898a4530f9Schristos kex->hash_alg,
190aa36fcacSchristos kex->client_version,
191aa36fcacSchristos kex->server_version,
192aa36fcacSchristos kex->my,
193aa36fcacSchristos kex->peer,
194aa36fcacSchristos server_host_key_blob,
195e4d43b82Schristos kex->min, kex->nbits, kex->max,
196aa36fcacSchristos dh_p, dh_g,
197b400d007Schristos pub_key,
198ca32bd8dSchristos dh_server_pub,
199aa36fcacSchristos sshbuf_ptr(shared_secret), sshbuf_len(shared_secret),
200aa36fcacSchristos hash, &hashlen)) != 0)
201e4d43b82Schristos goto out;
202ca32bd8dSchristos
203e4d43b82Schristos if ((r = sshkey_verify(server_host_key, signature, slen, hash,
204ed75d7a8Schristos hashlen, kex->hostkey_alg, ssh->compat, NULL)) != 0)
205e4d43b82Schristos goto out;
206ca32bd8dSchristos
207*a03ec00cSchristos if ((r = kex_derive_keys(ssh, hash, hashlen, shared_secret)) != 0 ||
208*a03ec00cSchristos (r = kex_send_newkeys(ssh)) != 0)
209*a03ec00cSchristos goto out;
210*a03ec00cSchristos
211*a03ec00cSchristos /* save initial signature and hostkey */
212*a03ec00cSchristos if ((kex->flags & KEX_INITIAL) != 0) {
213*a03ec00cSchristos if (kex->initial_hostkey != NULL || kex->initial_sig != NULL) {
214*a03ec00cSchristos r = SSH_ERR_INTERNAL_ERROR;
215*a03ec00cSchristos goto out;
216*a03ec00cSchristos }
217*a03ec00cSchristos if ((kex->initial_sig = sshbuf_new()) == NULL) {
218*a03ec00cSchristos r = SSH_ERR_ALLOC_FAIL;
219*a03ec00cSchristos goto out;
220*a03ec00cSchristos }
221*a03ec00cSchristos if ((r = sshbuf_put(kex->initial_sig, signature, slen)) != 0)
222*a03ec00cSchristos goto out;
223*a03ec00cSchristos kex->initial_hostkey = server_host_key;
224*a03ec00cSchristos server_host_key = NULL;
225*a03ec00cSchristos }
226*a03ec00cSchristos /* success */
227e4d43b82Schristos out:
228e4d43b82Schristos explicit_bzero(hash, sizeof(hash));
229e4d43b82Schristos DH_free(kex->dh);
230e4d43b82Schristos kex->dh = NULL;
231e4d43b82Schristos BN_clear_free(dh_server_pub);
232aa36fcacSchristos sshbuf_free(shared_secret);
233e4d43b82Schristos sshkey_free(server_host_key);
234aa36fcacSchristos sshbuf_free(tmp);
235aa36fcacSchristos sshbuf_free(server_host_key_blob);
236e4d43b82Schristos free(signature);
237e4d43b82Schristos return r;
238ca32bd8dSchristos }
239