1/*
2 * Copyright (c) 2001-2003 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 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
26 * Use is subject to license terms.
27 */
28
29#include "includes.h"
30
31#ifdef GSSAPI
32#include "auth.h"
33#include "ssh2.h"
34#include "xmalloc.h"
35#include "log.h"
36#include "dispatch.h"
37#include "buffer.h"
38#include "servconf.h"
39#include "compat.h"
40#include "bufaux.h"
41#include "packet.h"
42
43#include <gssapi/gssapi.h>
44#include "ssh-gss.h"
45
46extern ServerOptions options;
47extern uchar_t *session_id2;
48extern int session_id2_len;
49extern Gssctxt *xxx_gssctxt;
50
51static void userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt);
52
53static void
54userauth_gssapi_keyex(Authctxt *authctxt)
55{
56	gss_buffer_desc g_mic_data, mic_tok;
57	Buffer mic_data;
58	OM_uint32 maj_status, min_status;
59
60	if (authctxt == NULL || authctxt->method == NULL)
61		fatal("No authentication context during gssapi-keyex userauth");
62
63	if (xxx_gssctxt == NULL || xxx_gssctxt->context == GSS_C_NO_CONTEXT) {
64		/* fatal()?  or return? */
65		debug("No GSS-API context during gssapi-keyex userauth");
66		return;
67	}
68
69	/* Make data buffer to verify MIC with */
70	buffer_init(&mic_data);
71	buffer_put_string(&mic_data, session_id2, session_id2_len);
72	buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST);
73	buffer_put_cstring(&mic_data, authctxt->user);
74	buffer_put_cstring(&mic_data, authctxt->service);
75	buffer_put_cstring(&mic_data, authctxt->method->name);
76
77	g_mic_data.value  = buffer_ptr(&mic_data);
78	g_mic_data.length = buffer_len(&mic_data);
79
80	mic_tok.value = packet_get_string(&mic_tok.length);
81
82	maj_status = gss_verify_mic(&min_status, xxx_gssctxt->context,
83	    &g_mic_data, &mic_tok, NULL);
84
85	packet_check_eom();
86	buffer_clear(&mic_data);
87
88	if (maj_status != GSS_S_COMPLETE)
89		debug2("MIC verification failed, GSSAPI userauth failed");
90	else
91		userauth_gssapi_finish(authctxt, xxx_gssctxt);
92
93	/* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */
94	if (xxx_gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL)
95		ssh_gssapi_delete_ctx(&xxx_gssctxt);
96}
97
98static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
99static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
100static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
101static void input_gssapi_errtok(int, u_int32_t, void *);
102static void input_gssapi_exchange_complete(int type, u_int32_t plen,
103    void *ctxt);
104
105static void
106userauth_gssapi_abandon(Authctxt *authctxt, Authmethod *method)
107{
108	ssh_gssapi_delete_ctx((Gssctxt **)&method->method_data);
109	xxx_gssctxt = NULL;
110	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
111	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
112	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
113	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
114}
115
116static void
117userauth_gssapi(Authctxt *authctxt)
118{
119	gss_OID_set supported_mechs;
120	int mechs, present = 0;
121	OM_uint32 min_status;
122	uint_t len;
123	char *doid = NULL;
124	gss_OID oid = GSS_C_NULL_OID;
125
126	if (datafellows & SSH_OLD_GSSAPI) {
127		debug("Early drafts of GSSAPI userauth not supported");
128		return;
129	}
130
131	mechs = packet_get_int();
132	if (mechs == 0) {
133		packet_check_eom();
134		debug("Mechanism negotiation is not supported");
135		return;
136	}
137
138	ssh_gssapi_server_mechs(&supported_mechs);
139
140	do {
141		mechs--;
142
143		if (oid != GSS_C_NULL_OID)
144			ssh_gssapi_release_oid(&oid);
145
146		doid = packet_get_string(&len);
147
148		/* ick */
149		if (doid[0] != 0x06 || (len > 2 && doid[1] != len - 2)) {
150			log("Mechanism OID received using the old "
151			    "encoding form");
152			oid = ssh_gssapi_make_oid(len, doid);
153		} else {
154			oid = ssh_gssapi_make_oid(len - 2, doid + 2);
155		}
156
157		(void) gss_test_oid_set_member(&min_status, oid,
158		    supported_mechs, &present);
159
160		debug("Client offered gssapi userauth with %s (%s)",
161		    ssh_gssapi_oid_to_str(oid),
162		    present ? "supported" : "unsupported");
163	} while (!present && (mechs > 0));
164
165	if (!present) {
166		/* userauth_finish() will send SSH2_MSG_USERAUTH_FAILURE */
167		debug2("No mechanism offered by the client is available");
168		ssh_gssapi_release_oid(&oid);
169		return;
170	}
171
172	ssh_gssapi_build_ctx((Gssctxt **)&authctxt->method->method_data,
173	    0, oid);
174	ssh_gssapi_release_oid(&oid);
175	/* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
176
177	packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
178
179	/* Just return whatever we found -- the matched mech does us no good */
180	packet_put_string(doid, len);
181	xfree(doid);
182
183	packet_send();
184	packet_write_wait();
185
186	/* Setup rest of gssapi userauth conversation */
187	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
188	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
189	authctxt->method->postponed = 1;
190}
191
192static void
193input_gssapi_token(int type, u_int32_t plen, void *ctxt)
194{
195	Authctxt *authctxt = ctxt;
196	Gssctxt *gssctxt;
197	gss_buffer_desc send_tok, recv_tok;
198	OM_uint32 maj_status, min_status;
199	uint_t len;
200
201	if (authctxt == NULL || authctxt->method == NULL ||
202	    (authctxt->method->method_data == NULL)) {
203		fatal("No authentication or GSSAPI context during "
204		    "gssapi-with-mic userauth");
205	}
206
207	gssctxt = authctxt->method->method_data;
208	recv_tok.value = packet_get_string(&len);
209	recv_tok.length = len; /* u_int vs. size_t */
210
211	maj_status = ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok);
212	packet_check_eom();
213
214	if (GSS_ERROR(maj_status)) {
215		ssh_gssapi_userauth_error(gssctxt);
216		if (send_tok.length != 0) {
217			packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
218			packet_put_string(send_tok.value, send_tok.length);
219			packet_send();
220			packet_write_wait();
221		}
222		authctxt->method->postponed = 0;
223		dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
224		userauth_finish(authctxt, authctxt->method->name);
225	} else {
226		if (send_tok.length != 0) {
227			packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
228			packet_put_string(send_tok.value, send_tok.length);
229			packet_send();
230			packet_write_wait();
231		}
232		if (maj_status == GSS_S_COMPLETE) {
233			dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
234			dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC,
235			    &input_gssapi_mic);
236			dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
237			    &input_gssapi_exchange_complete);
238		}
239	}
240
241	gss_release_buffer(&min_status, &send_tok);
242}
243
244static void
245input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
246{
247	Authctxt *authctxt = ctxt;
248	Gssctxt *gssctxt;
249	gss_buffer_desc send_tok, recv_tok;
250
251	if (authctxt == NULL || authctxt->method == NULL ||
252	    (authctxt->method->method_data == NULL)) {
253		fatal("No authentication or GSSAPI context during "
254		    "gssapi-with-mic userauth");
255	}
256
257	gssctxt = authctxt->method->method_data;
258	recv_tok.value = packet_get_string(&recv_tok.length);
259	packet_check_eom();
260
261	/* Push the error token into GSSAPI to see what it says */
262	(void) ssh_gssapi_accept_ctx(gssctxt, &recv_tok, &send_tok);
263
264	debug("Client sent GSS-API error token during GSS userauth-- %s",
265	    ssh_gssapi_last_error(gssctxt, NULL, NULL));
266
267	/* We can't return anything to the client, even if we wanted to */
268	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
269	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
270
271
272	/*
273	 * The client will have already moved on to the next auth and
274	 * will send a new userauth request.  The spec says that the
275	 * server MUST NOT send a SSH_MSG_USERAUTH_FAILURE packet in
276	 * response to this.
277	 *
278	 * We leave authctxt->method->postponed == 1 here so that a call
279	 * to input_userauth_request() will detect this failure (as
280	 * userauth abandonment) and act accordingly.
281	 */
282}
283
284static void
285input_gssapi_mic(int type, u_int32_t plen, void *ctxt)
286{
287	Authctxt *authctxt = ctxt;
288	Gssctxt *gssctxt;
289	gss_buffer_desc g_mic_data, mic_tok;
290	Buffer mic_data;
291	OM_uint32 maj_status, min_status;
292
293	if (authctxt == NULL || authctxt->method == NULL ||
294	    (authctxt->method->method_data == NULL)) {
295		debug3("No authentication or GSSAPI context during "
296		    "gssapi-with-mic userauth");
297		return;
298	}
299
300	gssctxt = authctxt->method->method_data;
301
302	/* Make data buffer to verify MIC with */
303	buffer_init(&mic_data);
304	buffer_put_string(&mic_data, session_id2, session_id2_len);
305	buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST);
306	buffer_put_cstring(&mic_data, authctxt->user);
307	buffer_put_cstring(&mic_data, authctxt->service);
308	buffer_put_cstring(&mic_data, authctxt->method->name);
309
310	g_mic_data.value  = buffer_ptr(&mic_data);
311	g_mic_data.length = buffer_len(&mic_data);
312
313	mic_tok.value = packet_get_string(&mic_tok.length);
314
315	maj_status = gss_verify_mic(&min_status, gssctxt->context,
316	    &g_mic_data, &mic_tok, NULL);
317
318	packet_check_eom();
319	buffer_free(&mic_data);
320
321	if (maj_status != GSS_S_COMPLETE)
322		debug2("MIC verification failed, GSSAPI userauth failed");
323	else
324		userauth_gssapi_finish(authctxt, gssctxt);
325
326	/* Delete context from keyex */
327	if (xxx_gssctxt != gssctxt)
328		ssh_gssapi_delete_ctx(&xxx_gssctxt);
329
330	/* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */
331	if (gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL)
332		ssh_gssapi_delete_ctx(&gssctxt);
333
334	xxx_gssctxt = gssctxt;
335
336	authctxt->method->postponed = 0;
337	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
338	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
339	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
340	userauth_finish(authctxt, authctxt->method->name);
341}
342
343/*
344 * This is called when the client thinks we've completed authentication.
345 * It should only be enabled in the dispatch handler by the function above,
346 * which only enables it once the GSSAPI exchange is complete.
347 */
348static void
349input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
350{
351	Authctxt *authctxt = ctxt;
352	Gssctxt *gssctxt;
353
354	packet_check_eom();
355
356	if (authctxt == NULL || authctxt->method == NULL ||
357	    (authctxt->method->method_data == NULL))
358		fatal("No authentication or GSSAPI context");
359
360	gssctxt = authctxt->method->method_data;
361
362	/*
363	 * SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE -> gssapi userauth
364	 * failure, the client should use SSH2_MSG_USERAUTH_GSSAPI_MIC
365	 * instead.
366	 *
367	 * There's two reasons for this:
368	 *
369	 * 1) we don't have GSS mechs that don't support integrity
370	 * protection, and even if we did we'd not want to use them with
371	 * SSHv2, and,
372	 *
373	 * 2) we currently have no way to dynamically detect whether a
374	 * given mechanism does or does not support integrity
375	 * protection, so when a context's flags do not indicate
376	 * integrity protection we can't know if the client simply
377	 * didn't request it, so we assume it didn't and reject the
378	 * userauth.
379	 *
380	 * We could fail partially (i.e., force the use of other
381	 * userauth methods without counting this one as failed).  But
382	 * this will do for now.
383	 */
384#if 0
385	authctxt->method->authenticated = ssh_gssapi_userok(gssctxt,
386	    authctxt->user);
387#endif
388
389	if (xxx_gssctxt != gssctxt)
390		ssh_gssapi_delete_ctx(&gssctxt);
391	ssh_gssapi_delete_ctx(&gssctxt);
392	authctxt->method->postponed = 0;
393	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
394	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
395	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
396	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
397	userauth_finish(authctxt, authctxt->method->name);
398}
399
400static void
401ssh_gssapi_userauth_error(Gssctxt *ctxt)
402{
403	char *errstr;
404	OM_uint32 maj, min;
405
406	errstr = ssh_gssapi_last_error(ctxt, &maj, &min);
407	if (errstr) {
408		packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR);
409		packet_put_int(maj);
410		packet_put_int(min);
411		packet_put_cstring(errstr);
412		packet_put_cstring("");
413		packet_send();
414		packet_write_wait();
415		xfree(errstr);
416	}
417}
418
419/*
420 * Code common to gssapi-keyex and gssapi-with-mic userauth.
421 *
422 * Does authorization, figures out how to store delegated creds.
423 */
424static void
425userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt)
426{
427	char *local_user = NULL;
428	gss_buffer_desc dispname;
429	OM_uint32 major;
430
431	if (*authctxt->user != '\0' &&
432	    ssh_gssapi_userok(gssctxt, authctxt->user)) {
433
434		/*
435		 * If the client princ did not map to the requested
436		 * username then we don't want to clobber existing creds
437		 * for the user with the delegated creds.
438		 */
439		local_user = ssh_gssapi_localname(gssctxt);
440		if (local_user == NULL ||
441		    strcmp(local_user, authctxt->user) == 0)
442			gssctxt->default_creds = 1; /* store creds as default */
443
444		authctxt->method->authenticated =
445		    do_pam_non_initial_userauth(authctxt);
446
447	} else if (*authctxt->user == '\0') {
448		/* Requested username == ""; derive username from princ name */
449		if ((local_user = ssh_gssapi_localname(gssctxt)) == NULL)
450			return;
451
452		/* Changed username (from implicit, '') */
453		userauth_user_svc_change(authctxt, local_user, NULL);
454
455		gssctxt->default_creds = 1; /* store creds as default */
456
457		authctxt->method->authenticated =
458		    do_pam_non_initial_userauth(authctxt);
459	}
460
461	if (local_user != NULL)
462		xfree(local_user);
463
464	if (*authctxt->user != '\0' && authctxt->method->authenticated != 0) {
465		major = gss_display_name(&gssctxt->minor, gssctxt->src_name,
466		    &dispname, NULL);
467		if (major == GSS_S_COMPLETE) {
468			log("Authorized principal %.*s, authenticated with "
469			    "GSS mechanism %s, to: %s",
470			    dispname.length, (char *)dispname.value,
471			    ssh_gssapi_oid_to_name(gssctxt->actual_mech),
472			    authctxt->user);
473		}
474		(void) gss_release_buffer(&gssctxt->minor, &dispname);
475	}
476}
477
478#if 0
479/* Deprecated userauths -- should not be enabled */
480Authmethod method_external = {
481	"external-keyx",
482	&options.gss_authentication,
483	userauth_gssapi_keyex,
484	NULL,	/* no abandon function */
485	NULL,
486	NULL,
487	/* State counters */
488	0, 0, 0, 0,
489	/* State flags */
490	0, 0, 0, 0, 0, 0
491};
492
493Authmethod method_gssapi = {
494	"gssapi",
495	&options.gss_authentication,
496	userauth_gssapi,
497	userauth_gssapi_abandon,
498	NULL,
499	NULL,
500	/* State counters */
501	0, 0, 0, 0,
502	/* State flags */
503	0, 0, 0, 0, 0, 0
504};
505#endif
506
507Authmethod method_external = {
508	"gssapi-keyex",
509	&options.gss_authentication,
510	userauth_gssapi_keyex,
511	NULL,	/* no abandon function */
512	NULL,
513	NULL,
514	/* State counters */
515	0, 0, 0, 0,
516	/* State flags */
517	0, 0, 0, 0, 0, 0
518};
519
520Authmethod method_gssapi = {
521	"gssapi-with-mic",
522	&options.gss_authentication,
523	userauth_gssapi,
524	userauth_gssapi_abandon,
525	NULL,
526	NULL,
527	/* State counters */
528	0, 0, 0, 0,
529	/* State flags */
530	0, 0, 0, 0, 0, 0
531};
532
533#endif /* GSSAPI */
534