1*0Sstevel@tonic-gate /*
2*0Sstevel@tonic-gate  * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
3*0Sstevel@tonic-gate  *
4*0Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
5*0Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
6*0Sstevel@tonic-gate  * are met:
7*0Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
8*0Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
9*0Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
10*0Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
11*0Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
12*0Sstevel@tonic-gate  *
13*0Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
14*0Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15*0Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16*0Sstevel@tonic-gate  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17*0Sstevel@tonic-gate  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
18*0Sstevel@tonic-gate  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
19*0Sstevel@tonic-gate  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
20*0Sstevel@tonic-gate  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21*0Sstevel@tonic-gate  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
22*0Sstevel@tonic-gate  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23*0Sstevel@tonic-gate  */
24*0Sstevel@tonic-gate /*
25*0Sstevel@tonic-gate  * Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
26*0Sstevel@tonic-gate  * Use is subject to license terms.
27*0Sstevel@tonic-gate  */
28*0Sstevel@tonic-gate 
29*0Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
30*0Sstevel@tonic-gate 
31*0Sstevel@tonic-gate 
32*0Sstevel@tonic-gate #include "includes.h"
33*0Sstevel@tonic-gate 
34*0Sstevel@tonic-gate #ifdef GSSAPI
35*0Sstevel@tonic-gate 
36*0Sstevel@tonic-gate #include <openssl/crypto.h>
37*0Sstevel@tonic-gate #include <openssl/bn.h>
38*0Sstevel@tonic-gate 
39*0Sstevel@tonic-gate #include "xmalloc.h"
40*0Sstevel@tonic-gate #include "buffer.h"
41*0Sstevel@tonic-gate #include "bufaux.h"
42*0Sstevel@tonic-gate #include "kex.h"
43*0Sstevel@tonic-gate #include "log.h"
44*0Sstevel@tonic-gate #include "packet.h"
45*0Sstevel@tonic-gate #include "dh.h"
46*0Sstevel@tonic-gate #include "canohost.h"
47*0Sstevel@tonic-gate #include "ssh2.h"
48*0Sstevel@tonic-gate #include "ssh-gss.h"
49*0Sstevel@tonic-gate 
50*0Sstevel@tonic-gate extern char *xxx_host;
51*0Sstevel@tonic-gate 
52*0Sstevel@tonic-gate Gssctxt *xxx_gssctxt;
53*0Sstevel@tonic-gate 
54*0Sstevel@tonic-gate static void kexgss_verbose_cleanup(void *arg);
55*0Sstevel@tonic-gate 
56*0Sstevel@tonic-gate void
57*0Sstevel@tonic-gate kexgss_client(Kex *kex)
58*0Sstevel@tonic-gate {
59*0Sstevel@tonic-gate 	gss_buffer_desc gssbuf,send_tok,recv_tok, msg_tok;
60*0Sstevel@tonic-gate 	gss_buffer_t token_ptr;
61*0Sstevel@tonic-gate 	gss_OID mech = GSS_C_NULL_OID;
62*0Sstevel@tonic-gate 	Gssctxt *ctxt = NULL;
63*0Sstevel@tonic-gate 	OM_uint32 maj_status, min_status, smaj_status, smin_status;
64*0Sstevel@tonic-gate 	unsigned int klen, kout;
65*0Sstevel@tonic-gate 	DH *dh;
66*0Sstevel@tonic-gate 	BIGNUM *dh_server_pub = 0;
67*0Sstevel@tonic-gate 	BIGNUM *shared_secret = 0;
68*0Sstevel@tonic-gate 	Key *server_host_key = NULL;
69*0Sstevel@tonic-gate 	unsigned char *kbuf;
70*0Sstevel@tonic-gate 	unsigned char *hash;
71*0Sstevel@tonic-gate 	unsigned char *server_host_key_blob = NULL;
72*0Sstevel@tonic-gate 	char *msg, *lang;
73*0Sstevel@tonic-gate 	int type = 0;
74*0Sstevel@tonic-gate 	int first = 1;
75*0Sstevel@tonic-gate 	u_int sbloblen = 0;
76*0Sstevel@tonic-gate 	u_int strlen;
77*0Sstevel@tonic-gate 
78*0Sstevel@tonic-gate 	/* Map the negotiated kex name to a mech OID*/
79*0Sstevel@tonic-gate 	ssh_gssapi_oid_of_kexname(kex->name, &mech);
80*0Sstevel@tonic-gate 	if (mech == GSS_C_NULL_OID)
81*0Sstevel@tonic-gate 		fatal("Couldn't match the negotiated GSS key exchange");
82*0Sstevel@tonic-gate 
83*0Sstevel@tonic-gate 	ssh_gssapi_build_ctx(&ctxt, 1, mech);
84*0Sstevel@tonic-gate 
85*0Sstevel@tonic-gate 	/* This code should match that in ssh_dh1_client */
86*0Sstevel@tonic-gate 
87*0Sstevel@tonic-gate 	/* Step 1 - e is dh->pub_key */
88*0Sstevel@tonic-gate 	dh = dh_new_group1();
89*0Sstevel@tonic-gate 	dh_gen_key(dh, kex->we_need * 8);
90*0Sstevel@tonic-gate 
91*0Sstevel@tonic-gate 	/* This is f, we initialise it now to make life easier */
92*0Sstevel@tonic-gate     	dh_server_pub = BN_new();
93*0Sstevel@tonic-gate     	if (dh_server_pub == NULL) {
94*0Sstevel@tonic-gate     		fatal("dh_server_pub == NULL");
95*0Sstevel@tonic-gate     	}
96*0Sstevel@tonic-gate 
97*0Sstevel@tonic-gate 	token_ptr = GSS_C_NO_BUFFER;
98*0Sstevel@tonic-gate 
99*0Sstevel@tonic-gate 	recv_tok.value=NULL;
100*0Sstevel@tonic-gate 	recv_tok.length=0;
101*0Sstevel@tonic-gate 
102*0Sstevel@tonic-gate 	do {
103*0Sstevel@tonic-gate 		debug("Calling gss_init_sec_context");
104*0Sstevel@tonic-gate 
105*0Sstevel@tonic-gate 		maj_status=ssh_gssapi_init_ctx(ctxt,
106*0Sstevel@tonic-gate 					       xxx_host,
107*0Sstevel@tonic-gate 					       kex->options.gss_deleg_creds,
108*0Sstevel@tonic-gate 					       token_ptr,
109*0Sstevel@tonic-gate 					       &send_tok);
110*0Sstevel@tonic-gate 
111*0Sstevel@tonic-gate 		if (GSS_ERROR(maj_status)) {
112*0Sstevel@tonic-gate 			ssh_gssapi_error(ctxt, "performing GSS-API protected "
113*0Sstevel@tonic-gate 				"SSHv2 key exchange");
114*0Sstevel@tonic-gate 			(void) gss_release_buffer(&min_status, &send_tok);
115*0Sstevel@tonic-gate 			packet_disconnect("A GSS-API error occurred during "
116*0Sstevel@tonic-gate 				"GSS-API protected SSHv2 key exchange\n");
117*0Sstevel@tonic-gate 		}
118*0Sstevel@tonic-gate 
119*0Sstevel@tonic-gate 		/* If we've got an old receive buffer get rid of it */
120*0Sstevel@tonic-gate 		if (token_ptr != GSS_C_NO_BUFFER) {
121*0Sstevel@tonic-gate 			/* We allocated recv_tok */
122*0Sstevel@tonic-gate 			xfree(recv_tok.value);
123*0Sstevel@tonic-gate 			recv_tok.value = NULL;
124*0Sstevel@tonic-gate 			recv_tok.length = 0;
125*0Sstevel@tonic-gate 			token_ptr = GSS_C_NO_BUFFER;
126*0Sstevel@tonic-gate 		}
127*0Sstevel@tonic-gate 
128*0Sstevel@tonic-gate 		if (maj_status == GSS_S_COMPLETE) {
129*0Sstevel@tonic-gate 			/* If mutual state flag is not true, kex fails */
130*0Sstevel@tonic-gate 			if (!(ctxt->flags & GSS_C_MUTUAL_FLAG)) {
131*0Sstevel@tonic-gate 				fatal("Mutual authentication failed");
132*0Sstevel@tonic-gate 			}
133*0Sstevel@tonic-gate 			/* If integ avail flag is not true kex fails */
134*0Sstevel@tonic-gate 			if (!(ctxt->flags & GSS_C_INTEG_FLAG)) {
135*0Sstevel@tonic-gate 				fatal("Integrity check failed");
136*0Sstevel@tonic-gate 			}
137*0Sstevel@tonic-gate 		}
138*0Sstevel@tonic-gate 
139*0Sstevel@tonic-gate 		/* If we have data to send, then the last message that we
140*0Sstevel@tonic-gate 		 * received cannot have been a 'complete'. */
141*0Sstevel@tonic-gate 		if (send_tok.length !=0) {
142*0Sstevel@tonic-gate 			if (first) {
143*0Sstevel@tonic-gate 				packet_start(SSH2_MSG_KEXGSS_INIT);
144*0Sstevel@tonic-gate 				packet_put_string(send_tok.value,
145*0Sstevel@tonic-gate 					  	  send_tok.length);
146*0Sstevel@tonic-gate 				packet_put_bignum2(dh->pub_key);
147*0Sstevel@tonic-gate 				first=0;
148*0Sstevel@tonic-gate 			} else {
149*0Sstevel@tonic-gate 				packet_start(SSH2_MSG_KEXGSS_CONTINUE);
150*0Sstevel@tonic-gate 				packet_put_string(send_tok.value,
151*0Sstevel@tonic-gate 						  send_tok.length);
152*0Sstevel@tonic-gate 			}
153*0Sstevel@tonic-gate 			(void) gss_release_buffer(&min_status, &send_tok);
154*0Sstevel@tonic-gate 			packet_send();
155*0Sstevel@tonic-gate 			packet_write_wait();
156*0Sstevel@tonic-gate 
157*0Sstevel@tonic-gate 
158*0Sstevel@tonic-gate 			/* If we've sent them data, they'd better be polite
159*0Sstevel@tonic-gate 			 * and reply. */
160*0Sstevel@tonic-gate 
161*0Sstevel@tonic-gate next_packet:
162*0Sstevel@tonic-gate 			/*
163*0Sstevel@tonic-gate 			 * We need to catch connection closing w/o error
164*0Sstevel@tonic-gate 			 * tokens or messages so we can tell the user
165*0Sstevel@tonic-gate 			 * _something_ more useful than "Connection
166*0Sstevel@tonic-gate 			 * closed by ..."
167*0Sstevel@tonic-gate 			 *
168*0Sstevel@tonic-gate 			 * We use a fatal cleanup function as that's
169*0Sstevel@tonic-gate 			 * all, really, that we can do for now.
170*0Sstevel@tonic-gate 			 */
171*0Sstevel@tonic-gate 			fatal_add_cleanup(kexgss_verbose_cleanup, NULL);
172*0Sstevel@tonic-gate 			type = packet_read();
173*0Sstevel@tonic-gate 			fatal_remove_cleanup(kexgss_verbose_cleanup, NULL);
174*0Sstevel@tonic-gate 			switch (type) {
175*0Sstevel@tonic-gate 			case SSH2_MSG_KEXGSS_HOSTKEY:
176*0Sstevel@tonic-gate 				debug("Received KEXGSS_HOSTKEY");
177*0Sstevel@tonic-gate 				server_host_key_blob =
178*0Sstevel@tonic-gate 					packet_get_string(&sbloblen);
179*0Sstevel@tonic-gate 				server_host_key =
180*0Sstevel@tonic-gate 					key_from_blob(server_host_key_blob,
181*0Sstevel@tonic-gate 						sbloblen);
182*0Sstevel@tonic-gate 				goto next_packet; /* there MUSt be another */
183*0Sstevel@tonic-gate 				break;
184*0Sstevel@tonic-gate 			case SSH2_MSG_KEXGSS_CONTINUE:
185*0Sstevel@tonic-gate 				debug("Received GSSAPI_CONTINUE");
186*0Sstevel@tonic-gate 				if (maj_status == GSS_S_COMPLETE)
187*0Sstevel@tonic-gate 					packet_disconnect("Protocol error: "
188*0Sstevel@tonic-gate 						"received GSS-API context token"
189*0Sstevel@tonic-gate 						" though the context was already"
190*0Sstevel@tonic-gate 						" established");
191*0Sstevel@tonic-gate 				recv_tok.value=packet_get_string(&strlen);
192*0Sstevel@tonic-gate 				recv_tok.length=strlen; /* u_int vs. size_t */
193*0Sstevel@tonic-gate 				break;
194*0Sstevel@tonic-gate 			case SSH2_MSG_KEXGSS_COMPLETE:
195*0Sstevel@tonic-gate 				debug("Received GSSAPI_COMPLETE");
196*0Sstevel@tonic-gate 			        packet_get_bignum2(dh_server_pub);
197*0Sstevel@tonic-gate 			    	msg_tok.value=packet_get_string(&strlen);
198*0Sstevel@tonic-gate 				msg_tok.length=strlen; /* u_int vs. size_t */
199*0Sstevel@tonic-gate 
200*0Sstevel@tonic-gate 				/* Is there a token included? */
201*0Sstevel@tonic-gate 				if (packet_get_char()) {
202*0Sstevel@tonic-gate 					recv_tok.value=
203*0Sstevel@tonic-gate 					    packet_get_string(&strlen);
204*0Sstevel@tonic-gate 					recv_tok.length=strlen; /*u_int/size_t*/
205*0Sstevel@tonic-gate 				}
206*0Sstevel@tonic-gate 				if (recv_tok.length > 0 &&
207*0Sstevel@tonic-gate 				    maj_status == GSS_S_COMPLETE) {
208*0Sstevel@tonic-gate 					packet_disconnect("Protocol error: "
209*0Sstevel@tonic-gate 						"received GSS-API context token"
210*0Sstevel@tonic-gate 						" though the context was already"
211*0Sstevel@tonic-gate 						" established");
212*0Sstevel@tonic-gate 				} else if (recv_tok.length == 0 &&
213*0Sstevel@tonic-gate 					maj_status == GSS_S_CONTINUE_NEEDED) {
214*0Sstevel@tonic-gate 				   	/* No token included */
215*0Sstevel@tonic-gate 					packet_disconnect("Protocol error: "
216*0Sstevel@tonic-gate 						"did not receive expected "
217*0Sstevel@tonic-gate 						"GSS-API context token");
218*0Sstevel@tonic-gate 				}
219*0Sstevel@tonic-gate 				break;
220*0Sstevel@tonic-gate 			case SSH2_MSG_KEXGSS_ERROR:
221*0Sstevel@tonic-gate 				smaj_status=packet_get_int();
222*0Sstevel@tonic-gate 				smin_status=packet_get_int();
223*0Sstevel@tonic-gate 				msg = packet_get_string(NULL);
224*0Sstevel@tonic-gate 				lang = packet_get_string(NULL);
225*0Sstevel@tonic-gate 				xfree(lang);
226*0Sstevel@tonic-gate 				error("Server had a GSS-API error; the "
227*0Sstevel@tonic-gate 					"connection will close (%d/%d):\n%s",
228*0Sstevel@tonic-gate 					smaj_status, smin_status, msg);
229*0Sstevel@tonic-gate 				error("Use the GssKeyEx option to disable "
230*0Sstevel@tonic-gate 					"GSS-API key exchange and try again.");
231*0Sstevel@tonic-gate 				packet_disconnect("The server had a GSS-API "
232*0Sstevel@tonic-gate 					"error during GSS-API protected SSHv2 "
233*0Sstevel@tonic-gate 					"key exchange\n");
234*0Sstevel@tonic-gate 				break;
235*0Sstevel@tonic-gate 			default:
236*0Sstevel@tonic-gate 				packet_disconnect("Protocol error: "
237*0Sstevel@tonic-gate 					"didn't expect packet type %d", type);
238*0Sstevel@tonic-gate 			}
239*0Sstevel@tonic-gate 			if (recv_tok.value)
240*0Sstevel@tonic-gate 				token_ptr=&recv_tok;
241*0Sstevel@tonic-gate 		} else {
242*0Sstevel@tonic-gate 			/* No data, and not complete */
243*0Sstevel@tonic-gate 			if (maj_status != GSS_S_COMPLETE) {
244*0Sstevel@tonic-gate 				fatal("Not complete, and no token output");
245*0Sstevel@tonic-gate 			}
246*0Sstevel@tonic-gate 		}
247*0Sstevel@tonic-gate     	} while (maj_status == GSS_S_CONTINUE_NEEDED);
248*0Sstevel@tonic-gate 
249*0Sstevel@tonic-gate     	/* We _must_ have received a COMPLETE message in reply from the
250*0Sstevel@tonic-gate     	 * server, which will have set dh_server_pub and msg_tok */
251*0Sstevel@tonic-gate 
252*0Sstevel@tonic-gate     	if (type != SSH2_MSG_KEXGSS_COMPLETE)
253*0Sstevel@tonic-gate 	    fatal("Expected SSH2_MSG_KEXGSS_COMPLETE never arrived");
254*0Sstevel@tonic-gate     	if (maj_status != GSS_S_COMPLETE)
255*0Sstevel@tonic-gate 	    fatal("Internal error in GSS-API protected SSHv2 key exchange");
256*0Sstevel@tonic-gate 
257*0Sstevel@tonic-gate 	/* Check f in range [1, p-1] */
258*0Sstevel@tonic-gate         if (!dh_pub_is_valid(dh, dh_server_pub))
259*0Sstevel@tonic-gate                         packet_disconnect("bad server public DH value");
260*0Sstevel@tonic-gate 
261*0Sstevel@tonic-gate         /* compute K=f^x mod p */
262*0Sstevel@tonic-gate         klen = DH_size(dh);
263*0Sstevel@tonic-gate         kbuf = xmalloc(klen);
264*0Sstevel@tonic-gate         kout = DH_compute_key(kbuf, dh_server_pub, dh);
265*0Sstevel@tonic-gate 
266*0Sstevel@tonic-gate         shared_secret = BN_new();
267*0Sstevel@tonic-gate         BN_bin2bn(kbuf,kout, shared_secret);
268*0Sstevel@tonic-gate         (void) memset(kbuf, 0, klen);
269*0Sstevel@tonic-gate         xfree(kbuf);
270*0Sstevel@tonic-gate 
271*0Sstevel@tonic-gate         /* The GSS hash is identical to the DH one */
272*0Sstevel@tonic-gate         hash = kex_dh_hash(
273*0Sstevel@tonic-gate  	    kex->client_version_string,
274*0Sstevel@tonic-gate             kex->server_version_string,
275*0Sstevel@tonic-gate             buffer_ptr(&kex->my), buffer_len(&kex->my),
276*0Sstevel@tonic-gate             buffer_ptr(&kex->peer), buffer_len(&kex->peer),
277*0Sstevel@tonic-gate             server_host_key_blob, sbloblen, /* server host key */
278*0Sstevel@tonic-gate             dh->pub_key,	/* e */
279*0Sstevel@tonic-gate             dh_server_pub,	/* f */
280*0Sstevel@tonic-gate             shared_secret	/* K */
281*0Sstevel@tonic-gate         );
282*0Sstevel@tonic-gate 
283*0Sstevel@tonic-gate         gssbuf.value=hash;
284*0Sstevel@tonic-gate         gssbuf.length=20;
285*0Sstevel@tonic-gate 
286*0Sstevel@tonic-gate         /* Verify that H matches the token we just got. */
287*0Sstevel@tonic-gate 	if ((maj_status = gss_verify_mic(&min_status,
288*0Sstevel@tonic-gate         	       		         ctxt->context,
289*0Sstevel@tonic-gate         	                         &gssbuf,
290*0Sstevel@tonic-gate         	                         &msg_tok,
291*0Sstevel@tonic-gate         	                         NULL))) {
292*0Sstevel@tonic-gate 
293*0Sstevel@tonic-gate 		packet_disconnect("Hash's MIC didn't verify");
294*0Sstevel@tonic-gate       	}
295*0Sstevel@tonic-gate 
296*0Sstevel@tonic-gate 	if (server_host_key && kex->accept_host_key != NULL)
297*0Sstevel@tonic-gate 		(void) kex->accept_host_key(server_host_key);
298*0Sstevel@tonic-gate 
299*0Sstevel@tonic-gate         DH_free(dh);
300*0Sstevel@tonic-gate 
301*0Sstevel@tonic-gate 	xxx_gssctxt = ctxt; /* for gss keyex w/ mic userauth */
302*0Sstevel@tonic-gate 
303*0Sstevel@tonic-gate         /* save session id */
304*0Sstevel@tonic-gate         if (kex->session_id == NULL) {
305*0Sstevel@tonic-gate         	kex->session_id_len = 20;
306*0Sstevel@tonic-gate         	kex->session_id = xmalloc(kex->session_id_len);
307*0Sstevel@tonic-gate         	(void) memcpy(kex->session_id, hash, kex->session_id_len);
308*0Sstevel@tonic-gate         }
309*0Sstevel@tonic-gate 
310*0Sstevel@tonic-gate 	kex_derive_keys(kex, hash, shared_secret);
311*0Sstevel@tonic-gate 	BN_clear_free(shared_secret);
312*0Sstevel@tonic-gate         kex_finish(kex);
313*0Sstevel@tonic-gate }
314*0Sstevel@tonic-gate 
315*0Sstevel@tonic-gate /* ARGSUSED */
316*0Sstevel@tonic-gate static
317*0Sstevel@tonic-gate void
318*0Sstevel@tonic-gate kexgss_verbose_cleanup(void *arg)
319*0Sstevel@tonic-gate {
320*0Sstevel@tonic-gate 	error("The GSS-API protected key exchange has failed without "
321*0Sstevel@tonic-gate 		"indication\nfrom the server, possibly due to misconfiguration "
322*0Sstevel@tonic-gate 		"of the server.");
323*0Sstevel@tonic-gate 	error("Use the GssKeyEx option to disable GSS-API key exchange "
324*0Sstevel@tonic-gate 		"and try again.");
325*0Sstevel@tonic-gate }
326*0Sstevel@tonic-gate 
327*0Sstevel@tonic-gate #endif /* GSSAPI */
328