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