xref: /onnv-gate/usr/src/cmd/ssh/libssh/common/kexgssc.c (revision 6080:dcbf9e5d9874)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * Copyright (c) 2001-2003 Simon Wilkinson. All rights reserved.
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * Redistribution and use in source and binary forms, with or without
50Sstevel@tonic-gate  * modification, are permitted provided that the following conditions
60Sstevel@tonic-gate  * are met:
70Sstevel@tonic-gate  * 1. Redistributions of source code must retain the above copyright
80Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer.
90Sstevel@tonic-gate  * 2. Redistributions in binary form must reproduce the above copyright
100Sstevel@tonic-gate  *    notice, this list of conditions and the following disclaimer in the
110Sstevel@tonic-gate  *    documentation and/or other materials provided with the distribution.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR `AS IS'' AND ANY EXPRESS OR
140Sstevel@tonic-gate  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
150Sstevel@tonic-gate  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
160Sstevel@tonic-gate  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
170Sstevel@tonic-gate  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
180Sstevel@tonic-gate  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
190Sstevel@tonic-gate  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
200Sstevel@tonic-gate  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
210Sstevel@tonic-gate  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
220Sstevel@tonic-gate  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
230Sstevel@tonic-gate  */
240Sstevel@tonic-gate /*
25*6080Sjp161948  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
260Sstevel@tonic-gate  * Use is subject to license terms.
270Sstevel@tonic-gate  */
280Sstevel@tonic-gate 
29*6080Sjp161948 #pragma ident	"%Z%%M%	%I%	%E% SMI"
300Sstevel@tonic-gate 
310Sstevel@tonic-gate 
320Sstevel@tonic-gate #include "includes.h"
330Sstevel@tonic-gate 
340Sstevel@tonic-gate #ifdef GSSAPI
350Sstevel@tonic-gate 
360Sstevel@tonic-gate #include <openssl/crypto.h>
370Sstevel@tonic-gate #include <openssl/bn.h>
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #include "xmalloc.h"
400Sstevel@tonic-gate #include "buffer.h"
410Sstevel@tonic-gate #include "bufaux.h"
420Sstevel@tonic-gate #include "kex.h"
430Sstevel@tonic-gate #include "log.h"
440Sstevel@tonic-gate #include "packet.h"
450Sstevel@tonic-gate #include "dh.h"
460Sstevel@tonic-gate #include "canohost.h"
470Sstevel@tonic-gate #include "ssh2.h"
480Sstevel@tonic-gate #include "ssh-gss.h"
490Sstevel@tonic-gate 
500Sstevel@tonic-gate extern char *xxx_host;
510Sstevel@tonic-gate 
520Sstevel@tonic-gate Gssctxt *xxx_gssctxt;
530Sstevel@tonic-gate 
540Sstevel@tonic-gate static void kexgss_verbose_cleanup(void *arg);
550Sstevel@tonic-gate 
560Sstevel@tonic-gate void
kexgss_client(Kex * kex)570Sstevel@tonic-gate kexgss_client(Kex *kex)
580Sstevel@tonic-gate {
59*6080Sjp161948 	gss_buffer_desc gssbuf, send_tok, recv_tok, msg_tok;
600Sstevel@tonic-gate 	gss_buffer_t token_ptr;
610Sstevel@tonic-gate 	gss_OID mech = GSS_C_NULL_OID;
620Sstevel@tonic-gate 	Gssctxt *ctxt = NULL;
630Sstevel@tonic-gate 	OM_uint32 maj_status, min_status, smaj_status, smin_status;
640Sstevel@tonic-gate 	unsigned int klen, kout;
65*6080Sjp161948 	DH *dh;
660Sstevel@tonic-gate 	BIGNUM *dh_server_pub = 0;
67*6080Sjp161948 	BIGNUM *shared_secret = 0;
680Sstevel@tonic-gate 	Key *server_host_key = NULL;
690Sstevel@tonic-gate 	unsigned char *kbuf;
700Sstevel@tonic-gate 	unsigned char *hash;
710Sstevel@tonic-gate 	unsigned char *server_host_key_blob = NULL;
720Sstevel@tonic-gate 	char *msg, *lang;
730Sstevel@tonic-gate 	int type = 0;
740Sstevel@tonic-gate 	int first = 1;
75*6080Sjp161948 	uint_t sbloblen = 0;
76*6080Sjp161948 	uint_t strlen;
77*6080Sjp161948 
78*6080Sjp161948 	/* Map the negotiated kex name to a mech OID */
790Sstevel@tonic-gate 	ssh_gssapi_oid_of_kexname(kex->name, &mech);
800Sstevel@tonic-gate 	if (mech == GSS_C_NULL_OID)
810Sstevel@tonic-gate 		fatal("Couldn't match the negotiated GSS key exchange");
820Sstevel@tonic-gate 
830Sstevel@tonic-gate 	ssh_gssapi_build_ctx(&ctxt, 1, mech);
840Sstevel@tonic-gate 
850Sstevel@tonic-gate 	/* This code should match that in ssh_dh1_client */
86*6080Sjp161948 
870Sstevel@tonic-gate 	/* Step 1 - e is dh->pub_key */
880Sstevel@tonic-gate 	dh = dh_new_group1();
890Sstevel@tonic-gate 	dh_gen_key(dh, kex->we_need * 8);
900Sstevel@tonic-gate 
910Sstevel@tonic-gate 	/* This is f, we initialise it now to make life easier */
92*6080Sjp161948 	dh_server_pub = BN_new();
93*6080Sjp161948 	if (dh_server_pub == NULL) {
94*6080Sjp161948 		fatal("dh_server_pub == NULL");
95*6080Sjp161948 	}
96*6080Sjp161948 
970Sstevel@tonic-gate 	token_ptr = GSS_C_NO_BUFFER;
98*6080Sjp161948 
99*6080Sjp161948 	recv_tok.value = NULL;
100*6080Sjp161948 	recv_tok.length = 0;
1010Sstevel@tonic-gate 
1020Sstevel@tonic-gate 	do {
1030Sstevel@tonic-gate 		debug("Calling gss_init_sec_context");
104*6080Sjp161948 
105*6080Sjp161948 		maj_status = ssh_gssapi_init_ctx(ctxt, xxx_host,
106*6080Sjp161948 		    kex->options.gss_deleg_creds, token_ptr, &send_tok);
1070Sstevel@tonic-gate 
1080Sstevel@tonic-gate 		if (GSS_ERROR(maj_status)) {
1090Sstevel@tonic-gate 			ssh_gssapi_error(ctxt, "performing GSS-API protected "
110*6080Sjp161948 			    "SSHv2 key exchange");
1110Sstevel@tonic-gate 			(void) gss_release_buffer(&min_status, &send_tok);
1120Sstevel@tonic-gate 			packet_disconnect("A GSS-API error occurred during "
113*6080Sjp161948 			    "GSS-API protected SSHv2 key exchange\n");
1140Sstevel@tonic-gate 		}
1150Sstevel@tonic-gate 
1160Sstevel@tonic-gate 		/* If we've got an old receive buffer get rid of it */
1170Sstevel@tonic-gate 		if (token_ptr != GSS_C_NO_BUFFER) {
1180Sstevel@tonic-gate 			/* We allocated recv_tok */
1190Sstevel@tonic-gate 			xfree(recv_tok.value);
1200Sstevel@tonic-gate 			recv_tok.value = NULL;
1210Sstevel@tonic-gate 			recv_tok.length = 0;
1220Sstevel@tonic-gate 			token_ptr = GSS_C_NO_BUFFER;
1230Sstevel@tonic-gate 		}
124*6080Sjp161948 
1250Sstevel@tonic-gate 		if (maj_status == GSS_S_COMPLETE) {
1260Sstevel@tonic-gate 			/* If mutual state flag is not true, kex fails */
1270Sstevel@tonic-gate 			if (!(ctxt->flags & GSS_C_MUTUAL_FLAG)) {
1280Sstevel@tonic-gate 				fatal("Mutual authentication failed");
1290Sstevel@tonic-gate 			}
1300Sstevel@tonic-gate 			/* If integ avail flag is not true kex fails */
1310Sstevel@tonic-gate 			if (!(ctxt->flags & GSS_C_INTEG_FLAG)) {
1320Sstevel@tonic-gate 				fatal("Integrity check failed");
1330Sstevel@tonic-gate 			}
1340Sstevel@tonic-gate 		}
135*6080Sjp161948 
136*6080Sjp161948 		/*
137*6080Sjp161948 		 * If we have data to send, then the last message that we
138*6080Sjp161948 		 * received cannot have been a 'complete'.
139*6080Sjp161948 		 */
140*6080Sjp161948 		if (send_tok.length != 0) {
1410Sstevel@tonic-gate 			if (first) {
1420Sstevel@tonic-gate 				packet_start(SSH2_MSG_KEXGSS_INIT);
1430Sstevel@tonic-gate 				packet_put_string(send_tok.value,
144*6080Sjp161948 				    send_tok.length);
1450Sstevel@tonic-gate 				packet_put_bignum2(dh->pub_key);
146*6080Sjp161948 				first = 0;
1470Sstevel@tonic-gate 			} else {
1480Sstevel@tonic-gate 				packet_start(SSH2_MSG_KEXGSS_CONTINUE);
1490Sstevel@tonic-gate 				packet_put_string(send_tok.value,
150*6080Sjp161948 				    send_tok.length);
1510Sstevel@tonic-gate 			}
1520Sstevel@tonic-gate 			(void) gss_release_buffer(&min_status, &send_tok);
1530Sstevel@tonic-gate 			packet_send();
1540Sstevel@tonic-gate 			packet_write_wait();
1550Sstevel@tonic-gate 
156*6080Sjp161948 
157*6080Sjp161948 			/*
158*6080Sjp161948 			 * If we've sent them data, they'd better be polite and
159*6080Sjp161948 			 * reply.
160*6080Sjp161948 			 */
161*6080Sjp161948 
1620Sstevel@tonic-gate next_packet:
1630Sstevel@tonic-gate 			/*
1640Sstevel@tonic-gate 			 * We need to catch connection closing w/o error
1650Sstevel@tonic-gate 			 * tokens or messages so we can tell the user
1660Sstevel@tonic-gate 			 * _something_ more useful than "Connection
1670Sstevel@tonic-gate 			 * closed by ..."
1680Sstevel@tonic-gate 			 *
1690Sstevel@tonic-gate 			 * We use a fatal cleanup function as that's
1700Sstevel@tonic-gate 			 * all, really, that we can do for now.
1710Sstevel@tonic-gate 			 */
1720Sstevel@tonic-gate 			fatal_add_cleanup(kexgss_verbose_cleanup, NULL);
1730Sstevel@tonic-gate 			type = packet_read();
1740Sstevel@tonic-gate 			fatal_remove_cleanup(kexgss_verbose_cleanup, NULL);
1750Sstevel@tonic-gate 			switch (type) {
1760Sstevel@tonic-gate 			case SSH2_MSG_KEXGSS_HOSTKEY:
1770Sstevel@tonic-gate 				debug("Received KEXGSS_HOSTKEY");
1780Sstevel@tonic-gate 				server_host_key_blob =
179*6080Sjp161948 				    packet_get_string(&sbloblen);
1800Sstevel@tonic-gate 				server_host_key =
181*6080Sjp161948 				    key_from_blob(server_host_key_blob,
182*6080Sjp161948 				    sbloblen);
1830Sstevel@tonic-gate 				goto next_packet; /* there MUSt be another */
1840Sstevel@tonic-gate 				break;
1850Sstevel@tonic-gate 			case SSH2_MSG_KEXGSS_CONTINUE:
1860Sstevel@tonic-gate 				debug("Received GSSAPI_CONTINUE");
187*6080Sjp161948 				if (maj_status == GSS_S_COMPLETE)
1880Sstevel@tonic-gate 					packet_disconnect("Protocol error: "
189*6080Sjp161948 					    "received GSS-API context token "
190*6080Sjp161948 					    "though the context was already "
191*6080Sjp161948 					    "established");
192*6080Sjp161948 				recv_tok.value = packet_get_string(&strlen);
193*6080Sjp161948 				recv_tok.length = strlen; /* u_int vs. size_t */
1940Sstevel@tonic-gate 				break;
1950Sstevel@tonic-gate 			case SSH2_MSG_KEXGSS_COMPLETE:
1960Sstevel@tonic-gate 				debug("Received GSSAPI_COMPLETE");
197*6080Sjp161948 				packet_get_bignum2(dh_server_pub);
198*6080Sjp161948 				msg_tok.value = packet_get_string(&strlen);
199*6080Sjp161948 				msg_tok.length = strlen; /* u_int vs. size_t */
2000Sstevel@tonic-gate 
2010Sstevel@tonic-gate 				/* Is there a token included? */
2020Sstevel@tonic-gate 				if (packet_get_char()) {
203*6080Sjp161948 					recv_tok.value =
2040Sstevel@tonic-gate 					    packet_get_string(&strlen);
205*6080Sjp161948 					/* u_int/size_t */
206*6080Sjp161948 					recv_tok.length = strlen;
2070Sstevel@tonic-gate 				}
2080Sstevel@tonic-gate 				if (recv_tok.length > 0 &&
2090Sstevel@tonic-gate 				    maj_status == GSS_S_COMPLETE) {
2100Sstevel@tonic-gate 					packet_disconnect("Protocol error: "
211*6080Sjp161948 					    "received GSS-API context token "
212*6080Sjp161948 					    "though the context was already "
213*6080Sjp161948 					    "established");
2140Sstevel@tonic-gate 				} else if (recv_tok.length == 0 &&
215*6080Sjp161948 				    maj_status == GSS_S_CONTINUE_NEEDED) {
216*6080Sjp161948 					/* No token included */
2170Sstevel@tonic-gate 					packet_disconnect("Protocol error: "
218*6080Sjp161948 					    "did not receive expected "
219*6080Sjp161948 					    "GSS-API context token");
2200Sstevel@tonic-gate 				}
2210Sstevel@tonic-gate 				break;
2220Sstevel@tonic-gate 			case SSH2_MSG_KEXGSS_ERROR:
223*6080Sjp161948 				smaj_status = packet_get_int();
224*6080Sjp161948 				smin_status = packet_get_int();
2250Sstevel@tonic-gate 				msg = packet_get_string(NULL);
2260Sstevel@tonic-gate 				lang = packet_get_string(NULL);
2270Sstevel@tonic-gate 				xfree(lang);
2280Sstevel@tonic-gate 				error("Server had a GSS-API error; the "
229*6080Sjp161948 				    "connection will close (%d/%d):\n%s",
230*6080Sjp161948 				    smaj_status, smin_status, msg);
2310Sstevel@tonic-gate 				error("Use the GssKeyEx option to disable "
232*6080Sjp161948 				    "GSS-API key exchange and try again.");
2330Sstevel@tonic-gate 				packet_disconnect("The server had a GSS-API "
234*6080Sjp161948 				    "error during GSS-API protected SSHv2 "
235*6080Sjp161948 				    "key exchange\n");
2360Sstevel@tonic-gate 				break;
2370Sstevel@tonic-gate 			default:
2380Sstevel@tonic-gate 				packet_disconnect("Protocol error: "
239*6080Sjp161948 				    "didn't expect packet type %d", type);
2400Sstevel@tonic-gate 			}
2410Sstevel@tonic-gate 			if (recv_tok.value)
242*6080Sjp161948 				token_ptr = &recv_tok;
2430Sstevel@tonic-gate 		} else {
2440Sstevel@tonic-gate 			/* No data, and not complete */
2450Sstevel@tonic-gate 			if (maj_status != GSS_S_COMPLETE) {
2460Sstevel@tonic-gate 				fatal("Not complete, and no token output");
2470Sstevel@tonic-gate 			}
2480Sstevel@tonic-gate 		}
249*6080Sjp161948 	} while (maj_status == GSS_S_CONTINUE_NEEDED);
250*6080Sjp161948 
251*6080Sjp161948 	/*
252*6080Sjp161948 	 * We _must_ have received a COMPLETE message in reply from the
253*6080Sjp161948 	 * server, which will have set dh_server_pub and msg_tok.
254*6080Sjp161948 	 */
255*6080Sjp161948 	if (type != SSH2_MSG_KEXGSS_COMPLETE)
256*6080Sjp161948 		fatal("Expected SSH2_MSG_KEXGSS_COMPLETE never arrived");
257*6080Sjp161948 	if (maj_status != GSS_S_COMPLETE)
258*6080Sjp161948 		fatal("Internal error in GSS-API protected SSHv2 key exchange");
259*6080Sjp161948 
2600Sstevel@tonic-gate 	/* Check f in range [1, p-1] */
261*6080Sjp161948 	if (!dh_pub_is_valid(dh, dh_server_pub))
262*6080Sjp161948 		packet_disconnect("bad server public DH value");
263*6080Sjp161948 
264*6080Sjp161948 	/* compute K=f^x mod p */
265*6080Sjp161948 	klen = DH_size(dh);
266*6080Sjp161948 	kbuf = xmalloc(klen);
267*6080Sjp161948 	kout = DH_compute_key(kbuf, dh_server_pub, dh);
268*6080Sjp161948 
269*6080Sjp161948 	shared_secret = BN_new();
270*6080Sjp161948 	BN_bin2bn(kbuf, kout, shared_secret);
271*6080Sjp161948 	(void) memset(kbuf, 0, klen);
272*6080Sjp161948 	xfree(kbuf);
2730Sstevel@tonic-gate 
274*6080Sjp161948 	/* The GSS hash is identical to the DH one */
275*6080Sjp161948 	hash = kex_dh_hash(
276*6080Sjp161948 	    kex->client_version_string,
277*6080Sjp161948 	    kex->server_version_string,
278*6080Sjp161948 	    buffer_ptr(&kex->my), buffer_len(&kex->my),
279*6080Sjp161948 	    buffer_ptr(&kex->peer), buffer_len(&kex->peer),
280*6080Sjp161948 	    server_host_key_blob, sbloblen, /* server host key */
281*6080Sjp161948 	    dh->pub_key,	/* e */
282*6080Sjp161948 	    dh_server_pub,	/* f */
283*6080Sjp161948 	    shared_secret);	/* K */
284*6080Sjp161948 
285*6080Sjp161948 	gssbuf.value = hash;
286*6080Sjp161948 	gssbuf.length = 20;
287*6080Sjp161948 
288*6080Sjp161948 	/* Verify that H matches the token we just got. */
289*6080Sjp161948 	if ((maj_status = gss_verify_mic(&min_status, ctxt->context, &gssbuf,
290*6080Sjp161948 	    &msg_tok, NULL))) {
2910Sstevel@tonic-gate 		packet_disconnect("Hash's MIC didn't verify");
292*6080Sjp161948 	}
2930Sstevel@tonic-gate 
2940Sstevel@tonic-gate 	if (server_host_key && kex->accept_host_key != NULL)
2950Sstevel@tonic-gate 		(void) kex->accept_host_key(server_host_key);
296*6080Sjp161948 
297*6080Sjp161948 	DH_free(dh);
2980Sstevel@tonic-gate 
2990Sstevel@tonic-gate 	xxx_gssctxt = ctxt; /* for gss keyex w/ mic userauth */
3000Sstevel@tonic-gate 
301*6080Sjp161948 	/* save session id */
302*6080Sjp161948 	if (kex->session_id == NULL) {
303*6080Sjp161948 		kex->session_id_len = 20;
304*6080Sjp161948 		kex->session_id = xmalloc(kex->session_id_len);
305*6080Sjp161948 		(void) memcpy(kex->session_id, hash, kex->session_id_len);
306*6080Sjp161948 	}
307*6080Sjp161948 
3080Sstevel@tonic-gate 	kex_derive_keys(kex, hash, shared_secret);
3090Sstevel@tonic-gate 	BN_clear_free(shared_secret);
310*6080Sjp161948 	kex_finish(kex);
3110Sstevel@tonic-gate }
3120Sstevel@tonic-gate 
3130Sstevel@tonic-gate /* ARGSUSED */
3140Sstevel@tonic-gate static
3150Sstevel@tonic-gate void
kexgss_verbose_cleanup(void * arg)3160Sstevel@tonic-gate kexgss_verbose_cleanup(void *arg)
3170Sstevel@tonic-gate {
3180Sstevel@tonic-gate 	error("The GSS-API protected key exchange has failed without "
319*6080Sjp161948 	    "indication\nfrom the server, possibly due to misconfiguration "
320*6080Sjp161948 	    "of the server.");
3210Sstevel@tonic-gate 	error("Use the GssKeyEx option to disable GSS-API key exchange "
322*6080Sjp161948 	    "and try again.");
3230Sstevel@tonic-gate }
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate #endif /* GSSAPI */
326