summaryrefslogtreecommitdiff
path: root/kexgssc.c
diff options
context:
space:
mode:
authorSimon Wilkinson <simon@sxw.org.uk>2014-02-09 16:09:48 +0000
committerColin Watson <cjwatson@debian.org>2018-10-20 22:54:00 +0100
commit72b1d308e6400194ef6e4e7dd45bfa48fa39b5e6 (patch)
tree2a3b57ae5446f4273804064ccc42659adfc2a3b2 /kexgssc.c
parent3d246f10429fc9a37b98eabef94fe8dc7c61002b (diff)
GSSAPI key exchange support
This patch has been rejected upstream: "None of the OpenSSH developers are in favour of adding this, and this situation has not changed for several years. This is not a slight on Simon's patch, which is of fine quality, but just that a) we don't trust GSSAPI implementations that much and b) we don't like adding new KEX since they are pre-auth attack surface. This one is particularly scary, since it requires hooks out to typically root-owned system resources." However, quite a lot of people rely on this in Debian, and it's better to have it merged into the main openssh package rather than having separate -krb5 packages (as we used to have). It seems to have a generally good security history. Bug: https://bugzilla.mindrot.org/show_bug.cgi?id=1242 Last-Updated: 2018-10-20 Patch-Name: gssapi.patch
Diffstat (limited to 'kexgssc.c')
-rw-r--r--kexgssc.c341
1 files changed, 341 insertions, 0 deletions
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..3c8ae08dd
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,341 @@
1/*
2 * Copyright (c) 2001-2009 Simon Wilkinson. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "includes.h"
26
27#ifdef GSSAPI
28
29#include "includes.h"
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include <string.h>
35
36#include "xmalloc.h"
37#include "sshbuf.h"
38#include "ssh2.h"
39#include "sshkey.h"
40#include "cipher.h"
41#include "kex.h"
42#include "log.h"
43#include "packet.h"
44#include "dh.h"
45#include "digest.h"
46
47#include "ssh-gss.h"
48
49int
50kexgss_client(struct ssh *ssh) {
51 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
52 gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
53 Gssctxt *ctxt;
54 OM_uint32 maj_status, min_status, ret_flags;
55 u_int klen, kout, slen = 0, strlen;
56 DH *dh;
57 BIGNUM *dh_server_pub = NULL;
58 BIGNUM *shared_secret = NULL;
59 const BIGNUM *pub_key, *dh_p, *dh_g;
60 BIGNUM *p = NULL;
61 BIGNUM *g = NULL;
62 u_char *kbuf;
63 u_char *serverhostkey = NULL;
64 u_char *empty = "";
65 char *msg;
66 int type = 0;
67 int first = 1;
68 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
69 u_char hash[SSH_DIGEST_MAX_LENGTH];
70 size_t hashlen;
71
72 /* Initialise our GSSAPI world */
73 ssh_gssapi_build_ctx(&ctxt);
74 if (ssh_gssapi_id_kex(ctxt, ssh->kex->name, ssh->kex->kex_type)
75 == GSS_C_NO_OID)
76 fatal("Couldn't identify host exchange");
77
78 if (ssh_gssapi_import_name(ctxt, ssh->kex->gss_host))
79 fatal("Couldn't import hostname");
80
81 if (ssh->kex->gss_client &&
82 ssh_gssapi_client_identity(ctxt, ssh->kex->gss_client))
83 fatal("Couldn't acquire client credentials");
84
85 switch (ssh->kex->kex_type) {
86 case KEX_GSS_GRP1_SHA1:
87 dh = dh_new_group1();
88 break;
89 case KEX_GSS_GRP14_SHA1:
90 dh = dh_new_group14();
91 break;
92 case KEX_GSS_GEX_SHA1:
93 debug("Doing group exchange\n");
94 nbits = dh_estimate(ssh->kex->we_need * 8);
95 packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
96 packet_put_int(min);
97 packet_put_int(nbits);
98 packet_put_int(max);
99
100 packet_send();
101
102 packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
103
104 if ((p = BN_new()) == NULL)
105 fatal("BN_new() failed");
106 packet_get_bignum2(p);
107 if ((g = BN_new()) == NULL)
108 fatal("BN_new() failed");
109 packet_get_bignum2(g);
110 packet_check_eom();
111
112 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
113 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
114 min, BN_num_bits(p), max);
115
116 dh = dh_new_group(g, p);
117 break;
118 default:
119 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
120 }
121
122 /* Step 1 - e is dh->pub_key */
123 dh_gen_key(dh, ssh->kex->we_need * 8);
124 DH_get0_key(dh, &pub_key, NULL);
125 DH_get0_pqg(dh, &dh_p, NULL, &dh_g);
126
127 /* This is f, we initialise it now to make life easier */
128 dh_server_pub = BN_new();
129 if (dh_server_pub == NULL)
130 fatal("dh_server_pub == NULL");
131
132 token_ptr = GSS_C_NO_BUFFER;
133
134 do {
135 debug("Calling gss_init_sec_context");
136
137 maj_status = ssh_gssapi_init_ctx(ctxt,
138 ssh->kex->gss_deleg_creds, token_ptr, &send_tok,
139 &ret_flags);
140
141 if (GSS_ERROR(maj_status)) {
142 if (send_tok.length != 0) {
143 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
144 packet_put_string(send_tok.value,
145 send_tok.length);
146 }
147 fatal("gss_init_context failed");
148 }
149
150 /* If we've got an old receive buffer get rid of it */
151 if (token_ptr != GSS_C_NO_BUFFER)
152 free(recv_tok.value);
153
154 if (maj_status == GSS_S_COMPLETE) {
155 /* If mutual state flag is not true, kex fails */
156 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
157 fatal("Mutual authentication failed");
158
159 /* If integ avail flag is not true kex fails */
160 if (!(ret_flags & GSS_C_INTEG_FLAG))
161 fatal("Integrity check failed");
162 }
163
164 /*
165 * If we have data to send, then the last message that we
166 * received cannot have been a 'complete'.
167 */
168 if (send_tok.length != 0) {
169 if (first) {
170 packet_start(SSH2_MSG_KEXGSS_INIT);
171 packet_put_string(send_tok.value,
172 send_tok.length);
173 packet_put_bignum2(pub_key);
174 first = 0;
175 } else {
176 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
177 packet_put_string(send_tok.value,
178 send_tok.length);
179 }
180 packet_send();
181 gss_release_buffer(&min_status, &send_tok);
182
183 /* If we've sent them data, they should reply */
184 do {
185 type = packet_read();
186 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
187 debug("Received KEXGSS_HOSTKEY");
188 if (serverhostkey)
189 fatal("Server host key received more than once");
190 serverhostkey =
191 packet_get_string(&slen);
192 }
193 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
194
195 switch (type) {
196 case SSH2_MSG_KEXGSS_CONTINUE:
197 debug("Received GSSAPI_CONTINUE");
198 if (maj_status == GSS_S_COMPLETE)
199 fatal("GSSAPI Continue received from server when complete");
200 recv_tok.value = packet_get_string(&strlen);
201 recv_tok.length = strlen;
202 break;
203 case SSH2_MSG_KEXGSS_COMPLETE:
204 debug("Received GSSAPI_COMPLETE");
205 packet_get_bignum2(dh_server_pub);
206 msg_tok.value = packet_get_string(&strlen);
207 msg_tok.length = strlen;
208
209 /* Is there a token included? */
210 if (packet_get_char()) {
211 recv_tok.value=
212 packet_get_string(&strlen);
213 recv_tok.length = strlen;
214 /* If we're already complete - protocol error */
215 if (maj_status == GSS_S_COMPLETE)
216 packet_disconnect("Protocol error: received token when complete");
217 } else {
218 /* No token included */
219 if (maj_status != GSS_S_COMPLETE)
220 packet_disconnect("Protocol error: did not receive final token");
221 }
222 break;
223 case SSH2_MSG_KEXGSS_ERROR:
224 debug("Received Error");
225 maj_status = packet_get_int();
226 min_status = packet_get_int();
227 msg = packet_get_string(NULL);
228 (void) packet_get_string_ptr(NULL);
229 fatal("GSSAPI Error: \n%.400s",msg);
230 default:
231 packet_disconnect("Protocol error: didn't expect packet type %d",
232 type);
233 }
234 token_ptr = &recv_tok;
235 } else {
236 /* No data, and not complete */
237 if (maj_status != GSS_S_COMPLETE)
238 fatal("Not complete, and no token output");
239 }
240 } while (maj_status & GSS_S_CONTINUE_NEEDED);
241
242 /*
243 * We _must_ have received a COMPLETE message in reply from the
244 * server, which will have set dh_server_pub and msg_tok
245 */
246
247 if (type != SSH2_MSG_KEXGSS_COMPLETE)
248 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
249
250 /* Check f in range [1, p-1] */
251 if (!dh_pub_is_valid(dh, dh_server_pub))
252 packet_disconnect("bad server public DH value");
253
254 /* compute K=f^x mod p */
255 klen = DH_size(dh);
256 kbuf = xmalloc(klen);
257 kout = DH_compute_key(kbuf, dh_server_pub, dh);
258 if (kout < 0)
259 fatal("DH_compute_key: failed");
260
261 shared_secret = BN_new();
262 if (shared_secret == NULL)
263 fatal("kexgss_client: BN_new failed");
264
265 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
266 fatal("kexdh_client: BN_bin2bn failed");
267
268 memset(kbuf, 0, klen);
269 free(kbuf);
270
271 hashlen = sizeof(hash);
272 switch (ssh->kex->kex_type) {
273 case KEX_GSS_GRP1_SHA1:
274 case KEX_GSS_GRP14_SHA1:
275 kex_dh_hash(
276 ssh->kex->hash_alg,
277 ssh->kex->client_version_string,
278 ssh->kex->server_version_string,
279 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
280 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
281 (serverhostkey ? serverhostkey : empty), slen,
282 pub_key, /* e */
283 dh_server_pub, /* f */
284 shared_secret, /* K */
285 hash, &hashlen
286 );
287 break;
288 case KEX_GSS_GEX_SHA1:
289 kexgex_hash(
290 ssh->kex->hash_alg,
291 ssh->kex->client_version_string,
292 ssh->kex->server_version_string,
293 sshbuf_ptr(ssh->kex->my), sshbuf_len(ssh->kex->my),
294 sshbuf_ptr(ssh->kex->peer), sshbuf_len(ssh->kex->peer),
295 (serverhostkey ? serverhostkey : empty), slen,
296 min, nbits, max,
297 dh_p, dh_g,
298 pub_key,
299 dh_server_pub,
300 shared_secret,
301 hash, &hashlen
302 );
303 break;
304 default:
305 fatal("%s: Unexpected KEX type %d", __func__, ssh->kex->kex_type);
306 }
307
308 gssbuf.value = hash;
309 gssbuf.length = hashlen;
310
311 /* Verify that the hash matches the MIC we just got. */
312 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
313 packet_disconnect("Hash's MIC didn't verify");
314
315 free(msg_tok.value);
316
317 DH_free(dh);
318 free(serverhostkey);
319 BN_clear_free(dh_server_pub);
320
321 /* save session id */
322 if (ssh->kex->session_id == NULL) {
323 ssh->kex->session_id_len = hashlen;
324 ssh->kex->session_id = xmalloc(ssh->kex->session_id_len);
325 memcpy(ssh->kex->session_id, hash, ssh->kex->session_id_len);
326 }
327
328 if (ssh->kex->gss_deleg_creds)
329 ssh_gssapi_credentials_updated(ctxt);
330
331 if (gss_kex_context == NULL)
332 gss_kex_context = ctxt;
333 else
334 ssh_gssapi_delete_ctx(&ctxt);
335
336 kex_derive_keys_bn(ssh, hash, hashlen, shared_secret);
337 BN_clear_free(shared_secret);
338 return kex_send_newkeys(ssh);
339}
340
341#endif /* GSSAPI */