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>2014-03-19 16:39:52 +0000
commit429c595dbaff7f7c2b3a53fe4235211f6d788025 (patch)
tree085cf7273c133b74238c968c9c9f591f8fb0308e /kexgssc.c
parent9a975a9faed7c4f334e8c8490db3e77e102f2b21 (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: 2014-03-19 Patch-Name: gssapi.patch
Diffstat (limited to 'kexgssc.c')
-rw-r--r--kexgssc.c332
1 files changed, 332 insertions, 0 deletions
diff --git a/kexgssc.c b/kexgssc.c
new file mode 100644
index 000000000..92a31c5a3
--- /dev/null
+++ b/kexgssc.c
@@ -0,0 +1,332 @@
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 "buffer.h"
38#include "ssh2.h"
39#include "key.h"
40#include "cipher.h"
41#include "kex.h"
42#include "log.h"
43#include "packet.h"
44#include "dh.h"
45
46#include "ssh-gss.h"
47
48void
49kexgss_client(Kex *kex) {
50 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
51 gss_buffer_desc recv_tok, gssbuf, msg_tok, *token_ptr;
52 Gssctxt *ctxt;
53 OM_uint32 maj_status, min_status, ret_flags;
54 u_int klen, kout, slen = 0, hashlen, strlen;
55 DH *dh;
56 BIGNUM *dh_server_pub = NULL;
57 BIGNUM *shared_secret = NULL;
58 BIGNUM *p = NULL;
59 BIGNUM *g = NULL;
60 u_char *kbuf, *hash;
61 u_char *serverhostkey = NULL;
62 u_char *empty = "";
63 char *msg;
64 int type = 0;
65 int first = 1;
66 int nbits = 0, min = DH_GRP_MIN, max = DH_GRP_MAX;
67
68 /* Initialise our GSSAPI world */
69 ssh_gssapi_build_ctx(&ctxt);
70 if (ssh_gssapi_id_kex(ctxt, kex->name, kex->kex_type)
71 == GSS_C_NO_OID)
72 fatal("Couldn't identify host exchange");
73
74 if (ssh_gssapi_import_name(ctxt, kex->gss_host))
75 fatal("Couldn't import hostname");
76
77 if (kex->gss_client &&
78 ssh_gssapi_client_identity(ctxt, kex->gss_client))
79 fatal("Couldn't acquire client credentials");
80
81 switch (kex->kex_type) {
82 case KEX_GSS_GRP1_SHA1:
83 dh = dh_new_group1();
84 break;
85 case KEX_GSS_GRP14_SHA1:
86 dh = dh_new_group14();
87 break;
88 case KEX_GSS_GEX_SHA1:
89 debug("Doing group exchange\n");
90 nbits = dh_estimate(kex->we_need * 8);
91 packet_start(SSH2_MSG_KEXGSS_GROUPREQ);
92 packet_put_int(min);
93 packet_put_int(nbits);
94 packet_put_int(max);
95
96 packet_send();
97
98 packet_read_expect(SSH2_MSG_KEXGSS_GROUP);
99
100 if ((p = BN_new()) == NULL)
101 fatal("BN_new() failed");
102 packet_get_bignum2(p);
103 if ((g = BN_new()) == NULL)
104 fatal("BN_new() failed");
105 packet_get_bignum2(g);
106 packet_check_eom();
107
108 if (BN_num_bits(p) < min || BN_num_bits(p) > max)
109 fatal("GSSGRP_GEX group out of range: %d !< %d !< %d",
110 min, BN_num_bits(p), max);
111
112 dh = dh_new_group(g, p);
113 break;
114 default:
115 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
116 }
117
118 /* Step 1 - e is dh->pub_key */
119 dh_gen_key(dh, kex->we_need * 8);
120
121 /* This is f, we initialise it now to make life easier */
122 dh_server_pub = BN_new();
123 if (dh_server_pub == NULL)
124 fatal("dh_server_pub == NULL");
125
126 token_ptr = GSS_C_NO_BUFFER;
127
128 do {
129 debug("Calling gss_init_sec_context");
130
131 maj_status = ssh_gssapi_init_ctx(ctxt,
132 kex->gss_deleg_creds, token_ptr, &send_tok,
133 &ret_flags);
134
135 if (GSS_ERROR(maj_status)) {
136 if (send_tok.length != 0) {
137 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
138 packet_put_string(send_tok.value,
139 send_tok.length);
140 }
141 fatal("gss_init_context failed");
142 }
143
144 /* If we've got an old receive buffer get rid of it */
145 if (token_ptr != GSS_C_NO_BUFFER)
146 free(recv_tok.value);
147
148 if (maj_status == GSS_S_COMPLETE) {
149 /* If mutual state flag is not true, kex fails */
150 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
151 fatal("Mutual authentication failed");
152
153 /* If integ avail flag is not true kex fails */
154 if (!(ret_flags & GSS_C_INTEG_FLAG))
155 fatal("Integrity check failed");
156 }
157
158 /*
159 * If we have data to send, then the last message that we
160 * received cannot have been a 'complete'.
161 */
162 if (send_tok.length != 0) {
163 if (first) {
164 packet_start(SSH2_MSG_KEXGSS_INIT);
165 packet_put_string(send_tok.value,
166 send_tok.length);
167 packet_put_bignum2(dh->pub_key);
168 first = 0;
169 } else {
170 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
171 packet_put_string(send_tok.value,
172 send_tok.length);
173 }
174 packet_send();
175 gss_release_buffer(&min_status, &send_tok);
176
177 /* If we've sent them data, they should reply */
178 do {
179 type = packet_read();
180 if (type == SSH2_MSG_KEXGSS_HOSTKEY) {
181 debug("Received KEXGSS_HOSTKEY");
182 if (serverhostkey)
183 fatal("Server host key received more than once");
184 serverhostkey =
185 packet_get_string(&slen);
186 }
187 } while (type == SSH2_MSG_KEXGSS_HOSTKEY);
188
189 switch (type) {
190 case SSH2_MSG_KEXGSS_CONTINUE:
191 debug("Received GSSAPI_CONTINUE");
192 if (maj_status == GSS_S_COMPLETE)
193 fatal("GSSAPI Continue received from server when complete");
194 recv_tok.value = packet_get_string(&strlen);
195 recv_tok.length = strlen;
196 break;
197 case SSH2_MSG_KEXGSS_COMPLETE:
198 debug("Received GSSAPI_COMPLETE");
199 packet_get_bignum2(dh_server_pub);
200 msg_tok.value = packet_get_string(&strlen);
201 msg_tok.length = strlen;
202
203 /* Is there a token included? */
204 if (packet_get_char()) {
205 recv_tok.value=
206 packet_get_string(&strlen);
207 recv_tok.length = strlen;
208 /* If we're already complete - protocol error */
209 if (maj_status == GSS_S_COMPLETE)
210 packet_disconnect("Protocol error: received token when complete");
211 } else {
212 /* No token included */
213 if (maj_status != GSS_S_COMPLETE)
214 packet_disconnect("Protocol error: did not receive final token");
215 }
216 break;
217 case SSH2_MSG_KEXGSS_ERROR:
218 debug("Received Error");
219 maj_status = packet_get_int();
220 min_status = packet_get_int();
221 msg = packet_get_string(NULL);
222 (void) packet_get_string_ptr(NULL);
223 fatal("GSSAPI Error: \n%.400s",msg);
224 default:
225 packet_disconnect("Protocol error: didn't expect packet type %d",
226 type);
227 }
228 token_ptr = &recv_tok;
229 } else {
230 /* No data, and not complete */
231 if (maj_status != GSS_S_COMPLETE)
232 fatal("Not complete, and no token output");
233 }
234 } while (maj_status & GSS_S_CONTINUE_NEEDED);
235
236 /*
237 * We _must_ have received a COMPLETE message in reply from the
238 * server, which will have set dh_server_pub and msg_tok
239 */
240
241 if (type != SSH2_MSG_KEXGSS_COMPLETE)
242 fatal("Didn't receive a SSH2_MSG_KEXGSS_COMPLETE when I expected it");
243
244 /* Check f in range [1, p-1] */
245 if (!dh_pub_is_valid(dh, dh_server_pub))
246 packet_disconnect("bad server public DH value");
247
248 /* compute K=f^x mod p */
249 klen = DH_size(dh);
250 kbuf = xmalloc(klen);
251 kout = DH_compute_key(kbuf, dh_server_pub, dh);
252 if (kout < 0)
253 fatal("DH_compute_key: failed");
254
255 shared_secret = BN_new();
256 if (shared_secret == NULL)
257 fatal("kexgss_client: BN_new failed");
258
259 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
260 fatal("kexdh_client: BN_bin2bn failed");
261
262 memset(kbuf, 0, klen);
263 free(kbuf);
264
265 switch (kex->kex_type) {
266 case KEX_GSS_GRP1_SHA1:
267 case KEX_GSS_GRP14_SHA1:
268 kex_dh_hash( kex->client_version_string,
269 kex->server_version_string,
270 buffer_ptr(&kex->my), buffer_len(&kex->my),
271 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
272 (serverhostkey ? serverhostkey : empty), slen,
273 dh->pub_key, /* e */
274 dh_server_pub, /* f */
275 shared_secret, /* K */
276 &hash, &hashlen
277 );
278 break;
279 case KEX_GSS_GEX_SHA1:
280 kexgex_hash(
281 kex->hash_alg,
282 kex->client_version_string,
283 kex->server_version_string,
284 buffer_ptr(&kex->my), buffer_len(&kex->my),
285 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
286 (serverhostkey ? serverhostkey : empty), slen,
287 min, nbits, max,
288 dh->p, dh->g,
289 dh->pub_key,
290 dh_server_pub,
291 shared_secret,
292 &hash, &hashlen
293 );
294 break;
295 default:
296 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
297 }
298
299 gssbuf.value = hash;
300 gssbuf.length = hashlen;
301
302 /* Verify that the hash matches the MIC we just got. */
303 if (GSS_ERROR(ssh_gssapi_checkmic(ctxt, &gssbuf, &msg_tok)))
304 packet_disconnect("Hash's MIC didn't verify");
305
306 free(msg_tok.value);
307
308 DH_free(dh);
309 free(serverhostkey);
310 BN_clear_free(dh_server_pub);
311
312 /* save session id */
313 if (kex->session_id == NULL) {
314 kex->session_id_len = hashlen;
315 kex->session_id = xmalloc(kex->session_id_len);
316 memcpy(kex->session_id, hash, kex->session_id_len);
317 }
318
319 if (kex->gss_deleg_creds)
320 ssh_gssapi_credentials_updated(ctxt);
321
322 if (gss_kex_context == NULL)
323 gss_kex_context = ctxt;
324 else
325 ssh_gssapi_delete_ctx(&ctxt);
326
327 kex_derive_keys_bn(kex, hash, hashlen, shared_secret);
328 BN_clear_free(shared_secret);
329 kex_finish(kex);
330}
331
332#endif /* GSSAPI */