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