xref: /titanic_50/usr/src/cmd/ssh/sshd/auth2-gss.c (revision db9ce1c953f094d292df951aada6b8a85e1ff103)
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 
46 extern ServerOptions options;
47 extern uchar_t *session_id2;
48 extern int session_id2_len;
49 extern Gssctxt *xxx_gssctxt;
50 
51 static void userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt);
52 
53 static void
54 userauth_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 
98 static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
99 static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
100 static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
101 static void input_gssapi_errtok(int, u_int32_t, void *);
102 static void input_gssapi_exchange_complete(int type, u_int32_t plen,
103     void *ctxt);
104 
105 static void
106 userauth_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 
116 static void
117 userauth_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 
192 static void
193 input_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 
244 static void
245 input_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 
284 static void
285 input_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  */
348 static void
349 input_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 
400 static void
401 ssh_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  */
424 static void
425 userauth_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 */
480 Authmethod 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 
493 Authmethod 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 
507 Authmethod 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 
520 Authmethod 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