xref: /openbsd-src/usr.bin/ssh/kexgexs.c (revision 897fc685943471cf985a0fe38ba076ea6fe74fa5)
1 /* $OpenBSD: kexgexs.c,v 1.33 2018/04/10 00:10:49 djm Exp $ */
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 
28 #include <stdio.h>
29 #include <string.h>
30 #include <signal.h>
31 
32 #include <openssl/dh.h>
33 
34 #include "sshkey.h"
35 #include "cipher.h"
36 #include "digest.h"
37 #include "kex.h"
38 #include "log.h"
39 #include "packet.h"
40 #include "dh.h"
41 #include "ssh2.h"
42 #include "compat.h"
43 #ifdef GSSAPI
44 #include "ssh-gss.h"
45 #endif
46 #include "monitor_wrap.h"
47 #include "dispatch.h"
48 #include "ssherr.h"
49 #include "sshbuf.h"
50 #include "misc.h"
51 
52 static int input_kex_dh_gex_request(int, u_int32_t, struct ssh *);
53 static int input_kex_dh_gex_init(int, u_int32_t, struct ssh *);
54 
55 int
56 kexgex_server(struct ssh *ssh)
57 {
58 	ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_REQUEST,
59 	    &input_kex_dh_gex_request);
60 	debug("expecting SSH2_MSG_KEX_DH_GEX_REQUEST");
61 	return 0;
62 }
63 
64 static int
65 input_kex_dh_gex_request(int type, u_int32_t seq, struct ssh *ssh)
66 {
67 	struct kex *kex = ssh->kex;
68 	int r;
69 	u_int min = 0, max = 0, nbits = 0;
70 
71 	debug("SSH2_MSG_KEX_DH_GEX_REQUEST received");
72 	if ((r = sshpkt_get_u32(ssh, &min)) != 0 ||
73 	    (r = sshpkt_get_u32(ssh, &nbits)) != 0 ||
74 	    (r = sshpkt_get_u32(ssh, &max)) != 0 ||
75 	    (r = sshpkt_get_end(ssh)) != 0)
76 		goto out;
77 	kex->nbits = nbits;
78 	kex->min = min;
79 	kex->max = max;
80 	min = MAXIMUM(DH_GRP_MIN, min);
81 	max = MINIMUM(DH_GRP_MAX, max);
82 	nbits = MAXIMUM(DH_GRP_MIN, nbits);
83 	nbits = MINIMUM(DH_GRP_MAX, nbits);
84 
85 	if (kex->max < kex->min || kex->nbits < kex->min ||
86 	    kex->max < kex->nbits || kex->max < DH_GRP_MIN) {
87 		r = SSH_ERR_DH_GEX_OUT_OF_RANGE;
88 		goto out;
89 	}
90 
91 	/* Contact privileged parent */
92 	kex->dh = PRIVSEP(choose_dh(min, nbits, max));
93 	if (kex->dh == NULL) {
94 		sshpkt_disconnect(ssh, "no matching DH grp found");
95 		r = SSH_ERR_ALLOC_FAIL;
96 		goto out;
97 	}
98 	debug("SSH2_MSG_KEX_DH_GEX_GROUP sent");
99 	if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_GROUP)) != 0 ||
100 	    (r = sshpkt_put_bignum2(ssh, kex->dh->p)) != 0 ||
101 	    (r = sshpkt_put_bignum2(ssh, kex->dh->g)) != 0 ||
102 	    (r = sshpkt_send(ssh)) != 0)
103 		goto out;
104 
105 	/* Compute our exchange value in parallel with the client */
106 	if ((r = dh_gen_key(kex->dh, kex->we_need * 8)) != 0)
107 		goto out;
108 
109 	debug("expecting SSH2_MSG_KEX_DH_GEX_INIT");
110 	ssh_dispatch_set(ssh, SSH2_MSG_KEX_DH_GEX_INIT, &input_kex_dh_gex_init);
111 	r = 0;
112  out:
113 	return r;
114 }
115 
116 static int
117 input_kex_dh_gex_init(int type, u_int32_t seq, struct ssh *ssh)
118 {
119 	struct kex *kex = ssh->kex;
120 	BIGNUM *shared_secret = NULL, *dh_client_pub = NULL;
121 	struct sshkey *server_host_public, *server_host_private;
122 	u_char *kbuf = NULL, *signature = NULL, *server_host_key_blob = NULL;
123 	u_char hash[SSH_DIGEST_MAX_LENGTH];
124 	size_t sbloblen, slen;
125 	size_t klen = 0, hashlen;
126 	int kout, r;
127 
128 	if (kex->load_host_public_key == NULL ||
129 	    kex->load_host_private_key == NULL) {
130 		r = SSH_ERR_INVALID_ARGUMENT;
131 		goto out;
132 	}
133 	server_host_public = kex->load_host_public_key(kex->hostkey_type,
134 	    kex->hostkey_nid, ssh);
135 	server_host_private = kex->load_host_private_key(kex->hostkey_type,
136 	    kex->hostkey_nid, ssh);
137 	if (server_host_public == NULL) {
138 		r = SSH_ERR_NO_HOSTKEY_LOADED;
139 		goto out;
140 	}
141 
142 	/* key, cert */
143 	if ((dh_client_pub = BN_new()) == NULL) {
144 		r = SSH_ERR_ALLOC_FAIL;
145 		goto out;
146 	}
147 	if ((r = sshpkt_get_bignum2(ssh, dh_client_pub)) != 0 ||
148 	    (r = sshpkt_get_end(ssh)) != 0)
149 		goto out;
150 
151 #ifdef DEBUG_KEXDH
152 	fprintf(stderr, "dh_client_pub= ");
153 	BN_print_fp(stderr, dh_client_pub);
154 	fprintf(stderr, "\n");
155 	debug("bits %d", BN_num_bits(dh_client_pub));
156 #endif
157 
158 #ifdef DEBUG_KEXDH
159 	DHparams_print_fp(stderr, kex->dh);
160 	fprintf(stderr, "pub= ");
161 	BN_print_fp(stderr, kex->dh->pub_key);
162 	fprintf(stderr, "\n");
163 #endif
164 	if (!dh_pub_is_valid(kex->dh, dh_client_pub)) {
165 		sshpkt_disconnect(ssh, "bad client public DH value");
166 		r = SSH_ERR_MESSAGE_INCOMPLETE;
167 		goto out;
168 	}
169 
170 	klen = DH_size(kex->dh);
171 	if ((kbuf = malloc(klen)) == NULL ||
172 	    (shared_secret = BN_new()) == NULL) {
173 		r = SSH_ERR_ALLOC_FAIL;
174 		goto out;
175 	}
176 	if ((kout = DH_compute_key(kbuf, dh_client_pub, kex->dh)) < 0 ||
177 	    BN_bin2bn(kbuf, kout, shared_secret) == NULL) {
178 		r = SSH_ERR_LIBCRYPTO_ERROR;
179 		goto out;
180 	}
181 #ifdef DEBUG_KEXDH
182 	dump_digest("shared secret", kbuf, kout);
183 #endif
184 	if ((r = sshkey_to_blob(server_host_public, &server_host_key_blob,
185 	    &sbloblen)) != 0)
186 		goto out;
187 	/* calc H */
188 	hashlen = sizeof(hash);
189 	if ((r = kexgex_hash(
190 	    kex->hash_alg,
191 	    kex->client_version_string,
192 	    kex->server_version_string,
193 	    sshbuf_ptr(kex->peer), sshbuf_len(kex->peer),
194 	    sshbuf_ptr(kex->my), sshbuf_len(kex->my),
195 	    server_host_key_blob, sbloblen,
196 	    kex->min, kex->nbits, kex->max,
197 	    kex->dh->p, kex->dh->g,
198 	    dh_client_pub,
199 	    kex->dh->pub_key,
200 	    shared_secret,
201 	    hash, &hashlen)) != 0)
202 		goto out;
203 
204 	/* save session id := H */
205 	if (kex->session_id == NULL) {
206 		kex->session_id_len = hashlen;
207 		kex->session_id = malloc(kex->session_id_len);
208 		if (kex->session_id == NULL) {
209 			r = SSH_ERR_ALLOC_FAIL;
210 			goto out;
211 		}
212 		memcpy(kex->session_id, hash, kex->session_id_len);
213 	}
214 
215 	/* sign H */
216 	if ((r = kex->sign(server_host_private, server_host_public, &signature,
217 	     &slen, hash, hashlen, kex->hostkey_alg, ssh->compat)) < 0)
218 		goto out;
219 
220 	/* destroy_sensitive_data(); */
221 
222 	/* send server hostkey, DH pubkey 'f' and signed H */
223 	if ((r = sshpkt_start(ssh, SSH2_MSG_KEX_DH_GEX_REPLY)) != 0 ||
224 	    (r = sshpkt_put_string(ssh, server_host_key_blob, sbloblen)) != 0 ||
225 	    (r = sshpkt_put_bignum2(ssh, kex->dh->pub_key)) != 0 ||     /* f */
226 	    (r = sshpkt_put_string(ssh, signature, slen)) != 0 ||
227 	    (r = sshpkt_send(ssh)) != 0)
228 		goto out;
229 
230 	if ((r = kex_derive_keys_bn(ssh, hash, hashlen, shared_secret)) == 0)
231 		r = kex_send_newkeys(ssh);
232  out:
233 	DH_free(kex->dh);
234 	kex->dh = NULL;
235 	BN_clear_free(dh_client_pub);
236 	if (kbuf) {
237 		explicit_bzero(kbuf, klen);
238 		free(kbuf);
239 	}
240 	BN_clear_free(shared_secret);
241 	free(server_host_key_blob);
242 	free(signature);
243 	return r;
244 }
245