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