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