xref: /titanic_50/usr/src/cmd/ssh/sshd/auth2-gss.c (revision c77a61a72b5ecdc507d6cf104142edd371a16c84)
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 2004 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 #include "monitor_wrap.h"
45 
46 #include <gssapi/gssapi.h>
47 #include "ssh-gss.h"
48 
49 extern ServerOptions options;
50 extern u_char *session_id2;
51 extern int session_id2_len;
52 extern Gssctxt *xxx_gssctxt;
53 
54 static void userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt);
55 
56 static void
57 userauth_gssapi_keyex(Authctxt *authctxt)
58 {
59         gss_buffer_desc g_mic_data, mic_tok;
60 	Buffer mic_data;
61         OM_uint32 maj_status, min_status;
62 
63 	if (authctxt == NULL || authctxt->method == NULL)
64 		fatal("No authentication context during gssapi-keyex userauth");
65 
66 	if (xxx_gssctxt == NULL || xxx_gssctxt->context == GSS_C_NO_CONTEXT) {
67 		/* fatal()?  or return? */
68 		debug("No GSS-API context during gssapi-keyex userauth");
69 		return;
70 	}
71 
72 	/* Make data buffer to verify MIC with */
73 	buffer_init(&mic_data);
74 	buffer_put_string(&mic_data, session_id2, session_id2_len);
75 	buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST);
76 	buffer_put_cstring(&mic_data, authctxt->user);
77 	buffer_put_cstring(&mic_data, authctxt->service);
78 	buffer_put_cstring(&mic_data, authctxt->method->name);
79 
80 	g_mic_data.value  = buffer_ptr(&mic_data);
81 	g_mic_data.length = buffer_len(&mic_data);
82 
83 	mic_tok.value=packet_get_string(&mic_tok.length);
84 
85 	maj_status = gss_verify_mic(&min_status, xxx_gssctxt->context,
86 				&g_mic_data, &mic_tok, NULL);
87 
88         packet_check_eom();
89 	buffer_clear(&mic_data);
90 
91 	if (maj_status != GSS_S_COMPLETE)
92 		debug2("MIC verification failed, GSSAPI userauth failed");
93 	else
94 		userauth_gssapi_finish(authctxt, xxx_gssctxt);
95 
96 	/* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */
97 	if (xxx_gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL)
98 		ssh_gssapi_delete_ctx(&xxx_gssctxt);
99 
100         return;
101 }
102 
103 static void ssh_gssapi_userauth_error(Gssctxt *ctxt);
104 static void input_gssapi_token(int type, u_int32_t plen, void *ctxt);
105 static void input_gssapi_mic(int type, u_int32_t plen, void *ctxt);
106 static void input_gssapi_errtok(int, u_int32_t, void *);
107 static void input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt);
108 
109 static void
110 userauth_gssapi_abandon(Authctxt *authctxt, Authmethod *method)
111 {
112 	ssh_gssapi_delete_ctx((Gssctxt **)&method->method_data);
113 	xxx_gssctxt = NULL;
114 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
115 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
116 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
117         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
118 }
119 
120 static void
121 userauth_gssapi(Authctxt *authctxt)
122 {
123 	gss_OID_set     supported_mechs;
124 	int		mechs;
125 	int		present = 0;
126 	OM_uint32       min_status;
127 	u_int		len;
128 	char 		*doid = NULL;
129 	gss_OID		oid = GSS_C_NULL_OID;
130 
131         if (datafellows & SSH_OLD_GSSAPI) {
132                 debug("Early drafts of GSSAPI userauth not supported");
133                 return;
134         }
135 
136         mechs=packet_get_int();
137         if (mechs==0) {
138 		packet_check_eom();
139                 debug("Mechanism negotiation is not supported");
140                 return;
141         }
142 
143 	ssh_gssapi_server_mechs(&supported_mechs);
144 
145         do {
146                 mechs--;
147 
148 		if (oid != GSS_C_NULL_OID)
149 			ssh_gssapi_release_oid(&oid);
150 
151                 doid = packet_get_string(&len);
152 
153 		/* ick */
154                	if (doid[0]!=0x06 || (len > 2 && doid[1]!=len-2)) {
155                		log("Mechanism OID received using the old encoding form");
156 			oid = ssh_gssapi_make_oid(len, doid);
157                	} else {
158 			oid = ssh_gssapi_make_oid(len - 2, doid + 2);
159                	}
160             	(void) gss_test_oid_set_member(&min_status, oid,
161 					       supported_mechs, &present);
162                 debug("Client offered gssapi userauth with %s (%s)",
163 			ssh_gssapi_oid_to_str(oid),
164 			present ? "supported" : "unsupported");
165         } while (!present && (mechs > 0));
166 
167         if (!present) {
168 		/* userauth_finish() will send SSH2_MSG_USERAUTH_FAILURE */
169 		debug2("No mechanism offered by the client is available");
170                 ssh_gssapi_release_oid(&oid);
171                 return;
172         }
173 
174 	ssh_gssapi_build_ctx((Gssctxt **)&authctxt->method->method_data, 0, oid);
175         ssh_gssapi_release_oid(&oid);
176         /* Send SSH_MSG_USERAUTH_GSSAPI_RESPONSE */
177 
178        	packet_start(SSH2_MSG_USERAUTH_GSSAPI_RESPONSE);
179 
180 	/* Just return whatever we found -- the matched mech does us no good */
181 	packet_put_string(doid, len);
182 	xfree(doid);
183 
184         packet_send();
185         packet_write_wait();
186 
187 	/* Setup rest of gssapi userauth conversation */
188         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, &input_gssapi_token);
189         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, &input_gssapi_errtok);
190         authctxt->method->postponed = 1;
191 
192         return;
193 }
194 
195 static void
196 input_gssapi_token(int type, u_int32_t plen, void *ctxt)
197 {
198         Authctxt *authctxt = ctxt;
199         Gssctxt *gssctxt;
200         gss_buffer_desc send_tok,recv_tok;
201         OM_uint32 maj_status, min_status;
202 	u_int len;
203 
204         if (authctxt == NULL || authctxt->method == NULL ||
205 	    (authctxt->method->method_data == NULL && !use_privsep))
206                 fatal("No authentication or GSSAPI context during gssapi-with-mic userauth");
207 
208         gssctxt=authctxt->method->method_data;
209         recv_tok.value=packet_get_string(&len);
210         recv_tok.length=len; /* u_int vs. size_t */
211 
212         maj_status=PRIVSEP(ssh_gssapi_accept_ctx(gssctxt, &recv_tok,
213         					 &send_tok));
214         packet_check_eom();
215 
216         if (GSS_ERROR(maj_status)) {
217         	ssh_gssapi_userauth_error(gssctxt);
218 		if (send_tok.length != 0) {
219 			packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK);
220 	                packet_put_string(send_tok.value,send_tok.length);
221         	        packet_send();
222                		packet_write_wait();
223                	}
224                 authctxt->method->postponed = 0;
225                 dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
226                 userauth_finish(authctxt, authctxt->method->name);
227         } else {
228                	if (send_tok.length != 0) {
229                		packet_start(SSH2_MSG_USERAUTH_GSSAPI_TOKEN);
230                		packet_put_string(send_tok.value,send_tok.length);
231                		packet_send();
232                		packet_write_wait();
233                 }
234 	        if (maj_status == GSS_S_COMPLETE) {
235         	        dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN,NULL);
236                 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC,
237                              	     &input_gssapi_mic);
238                 	dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE,
239                              	     &input_gssapi_exchange_complete);
240                 }
241         }
242 
243         gss_release_buffer(&min_status, &send_tok);
244 }
245 
246 static void
247 input_gssapi_errtok(int type, u_int32_t plen, void *ctxt)
248 {
249         Authctxt *authctxt = ctxt;
250         Gssctxt *gssctxt;
251         gss_buffer_desc send_tok,recv_tok;
252 
253         if (authctxt == NULL || authctxt->method == NULL ||
254 	    (authctxt->method->method_data == NULL && !use_privsep))
255                 fatal("No authentication or GSSAPI context during gssapi-with-mic userauth");
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) PRIVSEP(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 gssapi-with-mic userauth");
296 		return;
297 	}
298 
299 	gssctxt=authctxt->method->method_data;
300 
301 	/* Make data buffer to verify MIC with */
302 	buffer_init(&mic_data);
303 	buffer_put_string(&mic_data, session_id2, session_id2_len);
304 	buffer_put_char(&mic_data, SSH2_MSG_USERAUTH_REQUEST);
305 	buffer_put_cstring(&mic_data, authctxt->user);
306 	buffer_put_cstring(&mic_data, authctxt->service);
307 	buffer_put_cstring(&mic_data, authctxt->method->name);
308 
309 	g_mic_data.value  = buffer_ptr(&mic_data);
310 	g_mic_data.length = buffer_len(&mic_data);
311 
312 	mic_tok.value=packet_get_string(&mic_tok.length);
313 
314 	maj_status = gss_verify_mic(&min_status, gssctxt->context,
315 				&g_mic_data, &mic_tok, NULL);
316 
317         packet_check_eom();
318 	buffer_free(&mic_data);
319 
320 	if (maj_status != GSS_S_COMPLETE)
321 		debug2("MIC verification failed, GSSAPI userauth failed");
322 	else
323 		userauth_gssapi_finish(authctxt, gssctxt);
324 
325 	/* Delete context from keyex */
326 	if (xxx_gssctxt != gssctxt)
327 		ssh_gssapi_delete_ctx(&xxx_gssctxt);
328 
329 	/* Leave Gssctxt around for ssh_gssapi_cleanup/storecreds() */
330 	if (gssctxt->deleg_creds == GSS_C_NO_CREDENTIAL)
331 		ssh_gssapi_delete_ctx(&gssctxt);
332 
333 	xxx_gssctxt = gssctxt;
334 
335         authctxt->method->postponed = 0;
336         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
337         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
338         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
339         userauth_finish(authctxt, authctxt->method->name);
340 }
341 
342 /* This is called when the client thinks we've completed authentication.
343  * It should only be enabled in the dispatch handler by the function above,
344  * which only enables it once the GSSAPI exchange is complete.
345  */
346 static void
347 input_gssapi_exchange_complete(int type, u_int32_t plen, void *ctxt)
348 {
349         Authctxt *authctxt = ctxt;
350         Gssctxt *gssctxt;
351 
352 	packet_check_eom();
353 
354 	if (authctxt == NULL || authctxt->method == NULL ||
355 	    (authctxt->method->method_data == NULL && !use_privsep))
356                 fatal("No authentication or GSSAPI context");
357 
358         gssctxt=authctxt->method->method_data;
359 
360 	/*
361 	 * SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE -> gssapi userauth
362 	 * failure, the client should use SSH2_MSG_USERAUTH_GSSAPI_MIC
363 	 * instead.
364 	 *
365 	 * There's two reasons for this:
366 	 *
367 	 * 1) we don't have GSS mechs that don't support integrity
368 	 * protection, and even if we did we'd not want to use them with
369 	 * SSHv2, and,
370 	 *
371 	 * 2) we currently have no way to dynamically detect whether a
372 	 * given mechanism does or does not support integrity
373 	 * protection, so when a context's flags do not indicate
374 	 * integrity protection we can't know if the client simply
375 	 * didn't request it, so we assume it didn't and reject the
376 	 * userauth.
377 	 *
378 	 * We could fail partially (i.e., force the use of other
379 	 * userauth methods without counting this one as failed).  But
380 	 * this will do for now.
381 	 */
382 #if 0
383         authctxt->method->authenticated = PRIVSEP(ssh_gssapi_userok(gssctxt, authctxt->user));
384 #endif
385 
386 	if (xxx_gssctxt != gssctxt)
387 		ssh_gssapi_delete_ctx(&gssctxt);
388 	ssh_gssapi_delete_ctx(&gssctxt);
389         authctxt->method->postponed = 0;
390         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL);
391         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_ERRTOK, NULL);
392         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_MIC, NULL);
393         dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL);
394         userauth_finish(authctxt, authctxt->method->name);
395 }
396 
397 static void ssh_gssapi_userauth_error(Gssctxt *ctxt) {
398 	char *errstr;
399 	OM_uint32 maj,min;
400 
401 	errstr=ssh_gssapi_last_error(ctxt,&maj,&min);
402 	if (errstr) {
403 		packet_start(SSH2_MSG_USERAUTH_GSSAPI_ERROR);
404 		packet_put_int(maj);
405 		packet_put_int(min);
406 		packet_put_cstring(errstr);
407 		packet_put_cstring("");
408 		packet_send();
409 		packet_write_wait();
410 		xfree(errstr);
411 	}
412 }
413 
414 /*
415  * Code common to gssapi-keyex and gssapi-with-mic userauth.
416  *
417  * Does authorization, figures out how to store delegated creds.
418  */
419 static
420 void
421 userauth_gssapi_finish(Authctxt *authctxt, Gssctxt *gssctxt)
422 {
423 	char *local_user = NULL;
424 
425 	if (*authctxt->user != '\0' &&
426 		PRIVSEP(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 
459 #if 0
460 /* Deprecated userauths -- should not be enabled */
461 Authmethod method_external = {
462 	"external-keyx",
463 	&options.gss_authentication,
464 	userauth_gssapi_keyex,
465 	NULL,	/* no abandon function */
466 	NULL,
467 	NULL,
468 	/* State counters */
469 	0, 0, 0, 0,
470 	/* State flags */
471 	0, 0, 0, 0, 0, 0
472 };
473 
474 Authmethod method_gssapi = {
475         "gssapi",
476         &options.gss_authentication,
477         userauth_gssapi,
478 	userauth_gssapi_abandon,
479 	NULL,
480 	NULL,
481 	/* State counters */
482 	0, 0, 0, 0,
483 	/* State flags */
484 	0, 0, 0, 0, 0, 0
485 };
486 #endif
487 
488 Authmethod method_external = {
489 	"gssapi-keyex",
490 	&options.gss_authentication,
491 	userauth_gssapi_keyex,
492 	NULL,	/* no abandon function */
493 	NULL,
494 	NULL,
495 	/* State counters */
496 	0, 0, 0, 0,
497 	/* State flags */
498 	0, 0, 0, 0, 0, 0
499 };
500 
501 Authmethod method_gssapi = {
502         "gssapi-with-mic",
503         &options.gss_authentication,
504         userauth_gssapi,
505 	userauth_gssapi_abandon,
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 #endif /* GSSAPI */
515