summaryrefslogtreecommitdiff
path: root/kexgsss.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-02-10 02:40:08 +0000
commitcd404114ded78fc51d5d9cbd458d55c9b2f67daa (patch)
treedf7a424d9301b69af906b50d550bfce6e6e2c5f3 /kexgsss.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-02-10 Patch-Name: gssapi.patch
Diffstat (limited to 'kexgsss.c')
-rw-r--r--kexgsss.c289
1 files changed, 289 insertions, 0 deletions
diff --git a/kexgsss.c b/kexgsss.c
new file mode 100644
index 000000000..809525924
--- /dev/null
+++ b/kexgsss.c
@@ -0,0 +1,289 @@
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 <string.h>
30
31#include <openssl/crypto.h>
32#include <openssl/bn.h>
33
34#include "xmalloc.h"
35#include "buffer.h"
36#include "ssh2.h"
37#include "key.h"
38#include "cipher.h"
39#include "kex.h"
40#include "log.h"
41#include "packet.h"
42#include "dh.h"
43#include "ssh-gss.h"
44#include "monitor_wrap.h"
45#include "servconf.h"
46
47extern ServerOptions options;
48
49void
50kexgss_server(Kex *kex)
51{
52 OM_uint32 maj_status, min_status;
53
54 /*
55 * Some GSSAPI implementations use the input value of ret_flags (an
56 * output variable) as a means of triggering mechanism specific
57 * features. Initializing it to zero avoids inadvertently
58 * activating this non-standard behaviour.
59 */
60
61 OM_uint32 ret_flags = 0;
62 gss_buffer_desc gssbuf, recv_tok, msg_tok;
63 gss_buffer_desc send_tok = GSS_C_EMPTY_BUFFER;
64 Gssctxt *ctxt = NULL;
65 u_int slen, klen, kout, hashlen;
66 u_char *kbuf, *hash;
67 DH *dh;
68 int min = -1, max = -1, nbits = -1;
69 BIGNUM *shared_secret = NULL;
70 BIGNUM *dh_client_pub = NULL;
71 int type = 0;
72 gss_OID oid;
73 char *mechs;
74
75 /* Initialise GSSAPI */
76
77 /* If we're rekeying, privsep means that some of the private structures
78 * in the GSSAPI code are no longer available. This kludges them back
79 * into life
80 */
81 if (!ssh_gssapi_oid_table_ok()) {
82 mechs = ssh_gssapi_server_mechanisms();
83 free(mechs);
84 }
85
86 debug2("%s: Identifying %s", __func__, kex->name);
87 oid = ssh_gssapi_id_kex(NULL, kex->name, kex->kex_type);
88 if (oid == GSS_C_NO_OID)
89 fatal("Unknown gssapi mechanism");
90
91 debug2("%s: Acquiring credentials", __func__);
92
93 if (GSS_ERROR(PRIVSEP(ssh_gssapi_server_ctx(&ctxt, oid))))
94 fatal("Unable to acquire credentials for the server");
95
96 switch (kex->kex_type) {
97 case KEX_GSS_GRP1_SHA1:
98 dh = dh_new_group1();
99 break;
100 case KEX_GSS_GRP14_SHA1:
101 dh = dh_new_group14();
102 break;
103 case KEX_GSS_GEX_SHA1:
104 debug("Doing group exchange");
105 packet_read_expect(SSH2_MSG_KEXGSS_GROUPREQ);
106 min = packet_get_int();
107 nbits = packet_get_int();
108 max = packet_get_int();
109 min = MAX(DH_GRP_MIN, min);
110 max = MIN(DH_GRP_MAX, max);
111 packet_check_eom();
112 if (max < min || nbits < min || max < nbits)
113 fatal("GSS_GEX, bad parameters: %d !< %d !< %d",
114 min, nbits, max);
115 dh = PRIVSEP(choose_dh(min, nbits, max));
116 if (dh == NULL)
117 packet_disconnect("Protocol error: no matching group found");
118
119 packet_start(SSH2_MSG_KEXGSS_GROUP);
120 packet_put_bignum2(dh->p);
121 packet_put_bignum2(dh->g);
122 packet_send();
123
124 packet_write_wait();
125 break;
126 default:
127 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
128 }
129
130 dh_gen_key(dh, kex->we_need * 8);
131
132 do {
133 debug("Wait SSH2_MSG_GSSAPI_INIT");
134 type = packet_read();
135 switch(type) {
136 case SSH2_MSG_KEXGSS_INIT:
137 if (dh_client_pub != NULL)
138 fatal("Received KEXGSS_INIT after initialising");
139 recv_tok.value = packet_get_string(&slen);
140 recv_tok.length = slen;
141
142 if ((dh_client_pub = BN_new()) == NULL)
143 fatal("dh_client_pub == NULL");
144
145 packet_get_bignum2(dh_client_pub);
146
147 /* Send SSH_MSG_KEXGSS_HOSTKEY here, if we want */
148 break;
149 case SSH2_MSG_KEXGSS_CONTINUE:
150 recv_tok.value = packet_get_string(&slen);
151 recv_tok.length = slen;
152 break;
153 default:
154 packet_disconnect(
155 "Protocol error: didn't expect packet type %d",
156 type);
157 }
158
159 maj_status = PRIVSEP(ssh_gssapi_accept_ctx(ctxt, &recv_tok,
160 &send_tok, &ret_flags));
161
162 free(recv_tok.value);
163
164 if (maj_status != GSS_S_COMPLETE && send_tok.length == 0)
165 fatal("Zero length token output when incomplete");
166
167 if (dh_client_pub == NULL)
168 fatal("No client public key");
169
170 if (maj_status & GSS_S_CONTINUE_NEEDED) {
171 debug("Sending GSSAPI_CONTINUE");
172 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
173 packet_put_string(send_tok.value, send_tok.length);
174 packet_send();
175 gss_release_buffer(&min_status, &send_tok);
176 }
177 } while (maj_status & GSS_S_CONTINUE_NEEDED);
178
179 if (GSS_ERROR(maj_status)) {
180 if (send_tok.length > 0) {
181 packet_start(SSH2_MSG_KEXGSS_CONTINUE);
182 packet_put_string(send_tok.value, send_tok.length);
183 packet_send();
184 }
185 fatal("accept_ctx died");
186 }
187
188 if (!(ret_flags & GSS_C_MUTUAL_FLAG))
189 fatal("Mutual Authentication flag wasn't set");
190
191 if (!(ret_flags & GSS_C_INTEG_FLAG))
192 fatal("Integrity flag wasn't set");
193
194 if (!dh_pub_is_valid(dh, dh_client_pub))
195 packet_disconnect("bad client public DH value");
196
197 klen = DH_size(dh);
198 kbuf = xmalloc(klen);
199 kout = DH_compute_key(kbuf, dh_client_pub, dh);
200 if (kout < 0)
201 fatal("DH_compute_key: failed");
202
203 shared_secret = BN_new();
204 if (shared_secret == NULL)
205 fatal("kexgss_server: BN_new failed");
206
207 if (BN_bin2bn(kbuf, kout, shared_secret) == NULL)
208 fatal("kexgss_server: BN_bin2bn failed");
209
210 memset(kbuf, 0, klen);
211 free(kbuf);
212
213 switch (kex->kex_type) {
214 case KEX_GSS_GRP1_SHA1:
215 case KEX_GSS_GRP14_SHA1:
216 kex_dh_hash(
217 kex->client_version_string, kex->server_version_string,
218 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
219 buffer_ptr(&kex->my), buffer_len(&kex->my),
220 NULL, 0, /* Change this if we start sending host keys */
221 dh_client_pub, dh->pub_key, shared_secret,
222 &hash, &hashlen
223 );
224 break;
225 case KEX_GSS_GEX_SHA1:
226 kexgex_hash(
227 kex->hash_alg,
228 kex->client_version_string, kex->server_version_string,
229 buffer_ptr(&kex->peer), buffer_len(&kex->peer),
230 buffer_ptr(&kex->my), buffer_len(&kex->my),
231 NULL, 0,
232 min, nbits, max,
233 dh->p, dh->g,
234 dh_client_pub,
235 dh->pub_key,
236 shared_secret,
237 &hash, &hashlen
238 );
239 break;
240 default:
241 fatal("%s: Unexpected KEX type %d", __func__, kex->kex_type);
242 }
243
244 BN_clear_free(dh_client_pub);
245
246 if (kex->session_id == NULL) {
247 kex->session_id_len = hashlen;
248 kex->session_id = xmalloc(kex->session_id_len);
249 memcpy(kex->session_id, hash, kex->session_id_len);
250 }
251
252 gssbuf.value = hash;
253 gssbuf.length = hashlen;
254
255 if (GSS_ERROR(PRIVSEP(ssh_gssapi_sign(ctxt,&gssbuf,&msg_tok))))
256 fatal("Couldn't get MIC");
257
258 packet_start(SSH2_MSG_KEXGSS_COMPLETE);
259 packet_put_bignum2(dh->pub_key);
260 packet_put_string(msg_tok.value,msg_tok.length);
261
262 if (send_tok.length != 0) {
263 packet_put_char(1); /* true */
264 packet_put_string(send_tok.value, send_tok.length);
265 } else {
266 packet_put_char(0); /* false */
267 }
268 packet_send();
269
270 gss_release_buffer(&min_status, &send_tok);
271 gss_release_buffer(&min_status, &msg_tok);
272
273 if (gss_kex_context == NULL)
274 gss_kex_context = ctxt;
275 else
276 ssh_gssapi_delete_ctx(&ctxt);
277
278 DH_free(dh);
279
280 kex_derive_keys_bn(kex, hash, hashlen, shared_secret);
281 BN_clear_free(shared_secret);
282 kex_finish(kex);
283
284 /* If this was a rekey, then save out any delegated credentials we
285 * just exchanged. */
286 if (options.gss_store_rekey)
287 ssh_gssapi_rekey_creds();
288}
289#endif /* GSSAPI */