summaryrefslogtreecommitdiff
path: root/kexgexs.c
diff options
context:
space:
mode:
Diffstat (limited to 'kexgexs.c')
-rw-r--r--kexgexs.c263
1 files changed, 166 insertions, 97 deletions
diff --git a/kexgexs.c b/kexgexs.c
index 770ad28a8..9c281d288 100644
--- a/kexgexs.c
+++ b/kexgexs.c
@@ -1,4 +1,4 @@
1/* $OpenBSD: kexgexs.c,v 1.19 2014/02/02 03:44:31 djm Exp $ */ 1/* $OpenBSD: kexgexs.c,v 1.24 2015/01/26 06:10:03 djm Exp $ */
2/* 2/*
3 * Copyright (c) 2000 Niels Provos. All rights reserved. 3 * Copyright (c) 2000 Niels Provos. All rights reserved.
4 * Copyright (c) 2001 Markus Friedl. All rights reserved. 4 * Copyright (c) 2001 Markus Friedl. All rights reserved.
@@ -26,7 +26,9 @@
26 26
27#include "includes.h" 27#include "includes.h"
28 28
29#include <sys/param.h> 29#ifdef WITH_OPENSSL
30
31#include <sys/param.h> /* MIN MAX */
30 32
31#include <stdarg.h> 33#include <stdarg.h>
32#include <stdio.h> 34#include <stdio.h>
@@ -35,10 +37,9 @@
35 37
36#include <openssl/dh.h> 38#include <openssl/dh.h>
37 39
38#include "xmalloc.h" 40#include "sshkey.h"
39#include "buffer.h"
40#include "key.h"
41#include "cipher.h" 41#include "cipher.h"
42#include "digest.h"
42#include "kex.h" 43#include "kex.h"
43#include "log.h" 44#include "log.h"
44#include "packet.h" 45#include "packet.h"
@@ -49,33 +50,43 @@
49#include "ssh-gss.h" 50#include "ssh-gss.h"
50#endif 51#endif
51#include "monitor_wrap.h" 52#include "monitor_wrap.h"
53#include "dispatch.h"
54#include "ssherr.h"
55#include "sshbuf.h"
56
57static int input_kex_dh_gex_request(int, u_int32_t, void *);
58static int input_kex_dh_gex_init(int, u_int32_t, void *);
52 59
53void 60int
54kexgex_server(Kex *kex) 61kexgex_server(struct ssh *ssh)
55{ 62{
56 BIGNUM *shared_secret = NULL, *dh_client_pub = NULL; 63 ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST_OLD,
57 Key *server_host_public, *server_host_private; 64 &input_kex_dh_gex_request);
58 DH *dh; 65 ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST,
59 u_char *kbuf, *hash, *signature = NULL, *server_host_key_blob = NULL; 66 &input_kex_dh_gex_request);
60 u_int sbloblen, klen, slen, hashlen; 67 debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST");
61 int omin = -1, min = -1, omax = -1, max = -1, onbits = -1, nbits = -1; 68 return 0;
62 int type, kout; 69}
70
71static int
72input_kex_dh_gex_request(int type, u_int32_t seq, void *ctxt)
73{
74 struct ssh *ssh = ctxt;
75 struct kex *kex = ssh->kex;
76 int r;
77 u_int min = 0, max = 0, nbits = 0;
63 78
64 if (kex->load_host_public_key == NULL ||
65 kex->load_host_private_key == NULL)
66 fatal("Cannot load hostkey");
67 server_host_public = kex->load_host_public_key(kex->hostkey_type);
68 if (server_host_public == NULL)
69 fatal("Unsupported hostkey type %d", kex->hostkey_type);
70 server_host_private = kex->load_host_private_key(kex->hostkey_type);
71
72 type = packet_read();
73 switch (type) { 79 switch (type) {
74 case SSH2_MSG_KEX_DH_GEX_REQUEST: 80 case SSH2_MSG_KEX_DH_GEX_REQUEST:
75 debug("SSH2_MSG_KEX_DH_GEX_REQUEST received"); 81 debug("SSH2_MSG_KEX_DH_GEX_REQUEST received");
76 omin = min = packet_get_int(); 82 if ((r = sshpkt_get_u32(ssh, &min)) != 0 ||
77 onbits = nbits = packet_get_int(); 83 (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
78 omax = max = packet_get_int(); 84 (r = sshpkt_get_u32(ssh, &max)) != 0 ||
85 (r = sshpkt_get_end(ssh)) != 0)
86 goto out;
87 kex->nbits = nbits;
88 kex->min = min;
89 kex->max = max;
79 min = MAX(DH_GRP_MIN, min); 90 min = MAX(DH_GRP_MIN, min);
80 max = MIN(DH_GRP_MAX, max); 91 max = MIN(DH_GRP_MAX, max);
81 nbits = MAX(DH_GRP_MIN, nbits); 92 nbits = MAX(DH_GRP_MIN, nbits);
@@ -83,45 +94,89 @@ kexgex_server(Kex *kex)
83 break; 94 break;
84 case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD: 95 case SSH2_MSG_KEX_DH_GEX_REQUEST_OLD:
85 debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received"); 96 debug("SSH2_MSG_KEX_DH_GEX_REQUEST_OLD received");
86 onbits = nbits = packet_get_int(); 97 if ((r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
98 (r = sshpkt_get_end(ssh)) != 0)
99 goto out;
100 kex->nbits = nbits;
87 /* unused for old GEX */ 101 /* unused for old GEX */
88 omin = min = DH_GRP_MIN; 102 kex->min = min = DH_GRP_MIN;
89 omax = max = DH_GRP_MAX; 103 kex->max = max = DH_GRP_MAX;
90 break; 104 break;
91 default: 105 default:
92 fatal("protocol error during kex, no DH_GEX_REQUEST: %d", type); 106 r = SSH_ERR_INVALID_ARGUMENT;
107 goto out;
93 } 108 }
94 packet_check_eom();
95 109
96 if (omax < omin || onbits < omin || omax < onbits) 110 if (kex->max < kex->min || kex->nbits < kex->min ||
97 fatal("DH_GEX_REQUEST, bad parameters: %d !< %d !< %d", 111 kex->max < kex->nbits) {
98 omin, onbits, omax); 112 r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
113 goto out;
114 }
99 115
100 /* Contact privileged parent */ 116 /* Contact privileged parent */
101 dh = PRIVSEP(choose_dh(min, nbits, max)); 117 kex->dh = PRIVSEP(choose_dh(min, nbits, max));
102 if (dh == NULL) 118 if (kex->dh == NULL) {
103 packet_disconnect("Protocol error: no matching DH grp found"); 119 sshpkt_disconnect(ssh, "no matching DH grp found");
104 120 r = SSH_ERR_ALLOC_FAIL;
121 goto out;
122 }
105 debug("SSH2_MSG_KEX_DH_GEX_GROUP sent"); 123 debug("SSH2_MSG_KEX_DH_GEX_GROUP sent");
106 packet_start(SSH2_MSG_KEX_DH_GEX_GROUP); 124 if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 ||
107 packet_put_bignum2(dh->p); 125 (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 ||
108 packet_put_bignum2(dh->g); 126 (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 ||
109 packet_send(); 127 (r = sshpkt_send(ssh)) != 0)
110 128 goto out;
111 /* flush */
112 packet_write_wait();
113 129
114 /* Compute our exchange value in parallel with the client */ 130 /* Compute our exchange value in parallel with the client */
115 dh_gen_key(dh, kex->we_need * 8); 131 if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
132 goto out;
133
134 /* old KEX does not use min/max in kexgex_hash() */
135 if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD)
136 kex->min = kex->max = -1;
116 137
117 debug("expecting SSH2_MSG_KEX_DH_GEX_INIT"); 138 debug("expecting SSH2_MSG_KEX_DH_GEX_INIT");
118 packet_read_expect(SSH2_MSG_KEX_DH_GEX_INIT); 139 ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init);
140 r = 0;
141 out:
142 return r;
143}
144
145static int
146input_kex_dh_gex_init(int type, u_int32_t seq, void *ctxt)
147{
148 struct ssh *ssh = ctxt;
149 struct kex *kex = ssh->kex;
150 BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
151 struct sshkey *server_host_public, *server_host_private;
152 u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
153 u_char hash[SSH_DIGEST_MAX_LENGTH];
154 size_t sbloblen, slen;
155 size_t klen = 0, hashlen;
156 int kout, r;
157
158 if (kex->load_host_public_key == NULL ||
159 kex->load_host_private_key == NULL) {
160 r = SSH_ERR_INVALID_ARGUMENT;
161 goto out;
162 }
163 server_host_public = kex->load_host_public_key(kex->hostkey_type,
164 kex->hostkey_nid, ssh);
165 server_host_private = kex->load_host_private_key(kex->hostkey_type,
166 kex->hostkey_nid, ssh);
167 if (server_host_public == NULL) {
168 r = SSH_ERR_NO_HOSTKEY_LOADED;
169 goto out;
170 }
119 171
120 /* key, cert */ 172 /* key, cert */
121 if ((dh_client_pub = BN_new()) == NULL) 173 if ((dh_client_pub = BN_new()) == NULL) {
122 fatal("dh_client_pub == NULL"); 174 r = SSH_ERR_ALLOC_FAIL;
123 packet_get_bignum2(dh_client_pub); 175 goto out;
124 packet_check_eom(); 176 }
177 if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
178 (r = sshpkt_get_end(ssh)) != 0)
179 goto out;
125 180
126#ifdef DEBUG_KEXDH 181#ifdef DEBUG_KEXDH
127 fprintf(stderr, "dh_client_pub= "); 182 fprintf(stderr, "dh_client_pub= ");
@@ -131,78 +186,92 @@ kexgex_server(Kex *kex)
131#endif 186#endif
132 187
133#ifdef DEBUG_KEXDH 188#ifdef DEBUG_KEXDH
134 DHparams_print_fp(stderr, dh); 189 DHparams_print_fp(stderr, kex->dh);
135 fprintf(stderr, "pub= "); 190 fprintf(stderr, "pub= ");
136 BN_print_fp(stderr, dh->pub_key); 191 BN_print_fp(stderr, kex->dh->pub_key);
137 fprintf(stderr, "\n"); 192 fprintf(stderr, "\n");
138#endif 193#endif
139 if (!dh_pub_is_valid(dh, dh_client_pub)) 194 if (!dh_pub_is_valid(kex->dh, dh_client_pub)) {
140 packet_disconnect("bad client public DH value"); 195 sshpkt_disconnect(ssh, "bad client public DH value");
196 r = SSH_ERR_MESSAGE_INCOMPLETE;
197 goto out;
198 }
141 199
142 klen = DH_size(dh); 200 klen = DH_size(kex->dh);
143 kbuf = xmalloc(klen); 201 if ((kbuf = malloc(klen)) == NULL ||
144 if ((kout = DH_compute_key(kbuf, dh_client_pub, dh)) < 0) 202 (shared_secret = BN_new()) == NULL) {
145 fatal("DH_compute_key: failed"); 203 r = SSH_ERR_ALLOC_FAIL;
204 goto out;
205 }
206 if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 ||
207 BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
208 r = SSH_ERR_LIBCRYPTO_ERROR;
209 goto out;
210 }
146#ifdef DEBUG_KEXDH 211#ifdef DEBUG_KEXDH
147 dump_digest("shared secret", kbuf, kout); 212 dump_digest("shared secret", kbuf, kout);
148#endif 213#endif
149 if ((shared_secret = BN_new()) == NULL) 214 if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
150 fatal("kexgex_server: BN_new failed"); 215 &sbloblen)) != 0)
151 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL) 216 goto out;
152 fatal("kexgex_server: BN_bin2bn failed");
153 explicit_bzero(kbuf, klen);
154 free(kbuf);
155
156 key_to_blob(server_host_public, &server_host_key_blob, &sbloblen);
157
158 if (type == SSH2_MSG_KEX_DH_GEX_REQUEST_OLD)
159 omin = min = omax = max = -1;
160
161 /* calc H */ 217 /* calc H */
162 kexgex_hash( 218 hashlen = sizeof(hash);
219 if ((r = kexgex_hash(
163 kex->hash_alg, 220 kex->hash_alg,
164 kex->client_version_string, 221 kex->client_version_string,
165 kex->server_version_string, 222 kex->server_version_string,
166 buffer_ptr(&kex->peer), buffer_len(&kex->peer), 223 sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
167 buffer_ptr(&kex->my), buffer_len(&kex->my), 224 sshbuf_ptr(kex->my), sshbuf_len(kex->my),
168 server_host_key_blob, sbloblen, 225 server_host_key_blob, sbloblen,
169 omin, onbits, omax, 226 kex->min, kex->nbits, kex->max,
170 dh->p, dh->g, 227 kex->dh->p, kex->dh->g,
171 dh_client_pub, 228 dh_client_pub,
172 dh->pub_key, 229 kex->dh->pub_key,
173 shared_secret, 230 shared_secret,
174 &hash, &hashlen 231 hash, &hashlen)) != 0)
175 ); 232 goto out;
176 BN_clear_free(dh_client_pub);
177 233
178 /* save session id := H */ 234 /* save session id := H */
179 if (kex->session_id == NULL) { 235 if (kex->session_id == NULL) {
180 kex->session_id_len = hashlen; 236 kex->session_id_len = hashlen;
181 kex->session_id = xmalloc(kex->session_id_len); 237 kex->session_id = malloc(kex->session_id_len);
238 if (kex->session_id == NULL) {
239 r = SSH_ERR_ALLOC_FAIL;
240 goto out;
241 }
182 memcpy(kex->session_id, hash, kex->session_id_len); 242 memcpy(kex->session_id, hash, kex->session_id_len);
183 } 243 }
184 244
185 /* sign H */ 245 /* sign H */
186 kex->sign(server_host_private, server_host_public, &signature, &slen, 246 if ((r = kex->sign(server_host_private, server_host_public,
187 hash, hashlen); 247 &signature, &slen, hash, hashlen, ssh->compat)) < 0)
248 goto out;
188 249
189 /* destroy_sensitive_data(); */ 250 /* destroy_sensitive_data(); */
190 251
191 /* send server hostkey, DH pubkey 'f' and singed H */ 252 /* send server hostkey, DH pubkey 'f' and singed H */
192 debug("SSH2_MSG_KEX_DH_GEX_REPLY sent"); 253 if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 ||
193 packet_start(SSH2_MSG_KEX_DH_GEX_REPLY); 254 (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
194 packet_put_string(server_host_key_blob, sbloblen); 255 (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 || /* f */
195 packet_put_bignum2(dh->pub_key); /* f */ 256 (r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
196 packet_put_string(signature, slen); 257 (r = sshpkt_send(ssh)) != 0)
197 packet_send(); 258 goto out;
198 259
199 free(signature); 260 if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
261 r = kex_send_newkeys(ssh);
262 out:
263 DH_free(kex->dh);
264 kex->dh = NULL;
265 if (dh_client_pub)
266 BN_clear_free(dh_client_pub);
267 if (kbuf) {
268 explicit_bzero(kbuf, klen);
269 free(kbuf);
270 }
271 if (shared_secret)
272 BN_clear_free(shared_secret);
200 free(server_host_key_blob); 273 free(server_host_key_blob);
201 /* have keys, free DH */ 274 free(signature);
202 DH_free(dh); 275 return r;
203
204 kex_derive_keys_bn(kex, hash, hashlen, shared_secret);
205 BN_clear_free(shared_secret);
206
207 kex_finish(kex);
208} 276}
277#endif /* WITH_OPENSSL */