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