xref: /titanic_50/usr/src/cmd/ssh/sshd/gss-serv.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 #ifdef GSSAPI
32 
33 #pragma ident	"%Z%%M%	%I%	%E% SMI"
34 
35 #include "includes.h"
36 #include "ssh.h"
37 #include "ssh2.h"
38 #include "xmalloc.h"
39 #include "buffer.h"
40 #include "bufaux.h"
41 #include "packet.h"
42 #include "compat.h"
43 #include <openssl/evp.h>
44 #include "cipher.h"
45 #include "kex.h"
46 #include "auth.h"
47 #include "log.h"
48 #include "channels.h"
49 #include "session.h"
50 #include "dispatch.h"
51 #include "servconf.h"
52 #include "uidswap.h"
53 #include "compat.h"
54 #include "monitor_wrap.h"
55 #include <pwd.h>
56 
57 #include "ssh-gss.h"
58 
59 extern char **environ;
60 
61 extern ServerOptions options;
62 extern u_char *session_id2;
63 extern int session_id2_len;
64 
65 Gssctxt	*xxx_gssctxt;
66 
67 void
68 ssh_gssapi_server_kex_hook(Kex *kex, char **proposal)
69 {
70 	gss_OID_set mechs = GSS_C_NULL_OID_SET;
71 
72 	if (kex == NULL || !kex->server)
73 		fatal("INTERNAL ERROR (%s)", __func__);
74 
75 	ssh_gssapi_server_mechs(&mechs);
76 	ssh_gssapi_modify_kex(kex, mechs, proposal);
77 }
78 
79 void
80 ssh_gssapi_server_mechs(gss_OID_set *mechs)
81 {
82 	static gss_OID_set	supported = GSS_C_NULL_OID_SET;
83 	gss_OID_set	s, acquired, indicated = GSS_C_NULL_OID_SET;
84 	gss_cred_id_t	creds;
85 	OM_uint32	maj, min;
86 	int		i;
87 
88 	if (!mechs) {
89 		(void) gss_release_oid_set(&min, &supported);
90 		return;
91 	}
92 
93 	if (supported != GSS_C_NULL_OID_SET) {
94 		*mechs = supported;
95 		return;
96 	}
97 
98 	*mechs = GSS_C_NULL_OID_SET;
99 
100 	maj = gss_create_empty_oid_set(&min, &s);
101 	if (GSS_ERROR(maj)) {
102 		debug("Could not allocate GSS-API resources (%s)",
103 			ssh_gssapi_last_error(NULL, &maj, &min));
104 		return;
105 	}
106 
107 	maj = gss_indicate_mechs(&min, &indicated);
108 	if (GSS_ERROR(maj)) {
109 		debug("No GSS-API mechanisms are installed");
110 		return;
111 	}
112 
113 	maj = gss_acquire_cred(&min, GSS_C_NO_NAME, 0, indicated,
114 			GSS_C_ACCEPT, &creds, &acquired, NULL);
115 
116 	if (GSS_ERROR(maj))
117 		debug("Failed to acquire GSS-API credentials for any "
118 			"mechanisms (%s)",
119 			ssh_gssapi_last_error(NULL, &maj, &min));
120 
121 	(void) gss_release_oid_set(&min, &indicated);
122 	(void) gss_release_cred(&min, &creds);
123 
124 	if (acquired == GSS_C_NULL_OID_SET || acquired->count == 0)
125 		return;
126 
127 	for (i = 0 ; i < acquired->count ; i++ ) {
128 		if (ssh_gssapi_is_spnego(&acquired->elements[i]))
129 			continue;
130 
131 		maj = gss_add_oid_set_member(&min, &acquired->elements[i], &s);
132 		if (GSS_ERROR(maj)) {
133 			debug("Could not allocate GSS-API resources (%s)",
134 				ssh_gssapi_last_error(NULL, &maj, &min));
135 			return;
136 		}
137 	}
138 	(void) gss_release_oid_set(&min, &acquired);
139 
140 	if (s->count) {
141 		supported = s;
142 		*mechs = s;
143 	}
144 }
145 
146 /* Wrapper around accept_sec_context
147  * Requires that the context contains:
148  *    oid
149  *    credentials	(from ssh_gssapi_acquire_cred)
150  */
151 /* Priviledged */
152 OM_uint32 ssh_gssapi_accept_ctx(Gssctxt *ctx, gss_buffer_t recv_tok,
153 				gss_buffer_t send_tok)
154 {
155 	/*
156 	 * Acquiring a cred for the ctx->desired_mech for GSS_C_NO_NAME
157 	 * may well be probably better than using GSS_C_NO_CREDENTIAL
158 	 * and then checking that ctx->desired_mech agrees with
159 	 * ctx->actual_mech...
160 	 */
161 	ctx->major=gss_accept_sec_context(&ctx->minor,
162 					  &ctx->context,
163 					  GSS_C_NO_CREDENTIAL,
164 					  recv_tok,
165 					  GSS_C_NO_CHANNEL_BINDINGS,
166 					  &ctx->src_name,
167 					  &ctx->actual_mech,
168 					  send_tok,
169 					  &ctx->flags,
170 					  NULL,
171 					  &ctx->deleg_creds);
172 	if (GSS_ERROR(ctx->major))
173 		ssh_gssapi_error(ctx, "accepting security context");
174 
175 	if (ctx->major == GSS_S_CONTINUE_NEEDED && send_tok->length == 0)
176 		fatal("Zero length GSS context token output when continue needed");
177 	else if (GSS_ERROR(ctx->major) && send_tok->length == 0)
178 		debug2("Zero length GSS context error token output");
179 
180 	if (ctx->major == GSS_S_COMPLETE &&
181 	    ctx->desired_mech != GSS_C_NULL_OID &&
182 	    (ctx->desired_mech->length != ctx->actual_mech->length ||
183 		memcmp(ctx->desired_mech->elements,
184 		    ctx->actual_mech->elements,
185 		    ctx->desired_mech->length) != 0)) {
186 		gss_OID_set supported;
187 		OM_uint32   min;
188 		int	    present = 0;
189 
190 		debug("The client did not use the GSS-API mechanism it asked for");
191 
192 		/* Let it slide as long as the mech is supported */
193 		ssh_gssapi_server_mechs(&supported);
194 		if (supported != GSS_C_NULL_OID_SET)
195 			(void) gss_test_oid_set_member(&min,
196 						       ctx->actual_mech,
197 						       supported, &present);
198 		if (!present)
199 			ctx->major = GSS_S_BAD_MECH;
200 	}
201 
202 	if (ctx->deleg_creds)
203 		debug("Received delegated GSS credentials");
204 
205 	if (ctx->major == GSS_S_COMPLETE) {
206 		ctx->major = gss_inquire_context(&ctx->minor, ctx->context,
207 					NULL, &ctx->dst_name, NULL, NULL,
208 					NULL, NULL, &ctx->established);
209 
210 		if (GSS_ERROR(ctx->major)) {
211 			ssh_gssapi_error(ctx, "inquiring established sec context");
212 			return (ctx->major);
213 		}
214 
215 		xxx_gssctxt = ctx;
216 	}
217 
218 	return (ctx->major);
219 }
220 
221 
222 /* As user - called through fatal cleanup hook */
223 void
224 ssh_gssapi_cleanup_creds(Gssctxt *ctx)
225 {
226 #ifdef HAVE_GSS_STORE_CRED
227 	/* pam_setcred() will take care of this */
228 	return;
229 #else
230 	return;
231 /*#error "Portability broken in cleanup of stored creds"*/
232 #endif /* HAVE_GSS_STORE_CRED */
233 }
234 
235 void
236 ssh_gssapi_storecreds(Gssctxt *ctx, Authctxt *authctxt)
237 {
238 #ifdef USE_PAM
239 	char **penv, **tmp_env;
240 #endif /* USE_PAM */
241 
242 	if (authctxt == NULL) {
243 		error("Missing context while storing GSS-API credentials");
244 		return;
245 	}
246 
247 	if (ctx == NULL && xxx_gssctxt == NULL)
248 		return;
249 
250 	if (ctx == NULL)
251 		ctx = xxx_gssctxt;
252 
253 	if (!options.gss_cleanup_creds ||
254 	    ctx->deleg_creds == GSS_C_NO_CREDENTIAL) {
255 		debug3("Not storing delegated GSS credentials"
256 			" (none delegated)");
257 		return;
258 	}
259 
260 	if (!authctxt->valid || authctxt->pw == NULL) {
261 		debug3("Not storing delegated GSS credentials"
262 			" for invalid user");
263 		return;
264 	}
265 
266 	debug("Storing delegated GSS-API credentials");
267 
268 	/*
269 	 * The GSS-API has a flaw in that it does not provide a
270 	 * mechanism by which delegated credentials can be made
271 	 * available for acquisition by GSS_Acquire_cred() et. al.;
272 	 * gss_store_cred() is the proposed GSS-API extension for
273 	 * generically storing delegated credentials.
274 	 *
275 	 * gss_store_cred() does not speak to how credential stores are
276 	 * referenced.  Generically this may be done by switching to the
277 	 * user context of the user in whose default credential store we
278 	 * wish to place delegated credentials.  But environment
279 	 * variables could conceivably affect the choice of credential
280 	 * store as well, and perhaps in a mechanism-specific manner.
281 	 *
282 	 * SUNW -- On Solaris the euid selects the current credential
283 	 * store, but PAM modules could select alternate stores by
284 	 * setting, for example, KRB5CCNAME, so we also use the PAM
285 	 * environment temporarily.
286 	 */
287 
288 #ifdef HAVE_GSS_STORE_CRED
289 #ifdef USE_PAM
290 	/*
291 	 * PAM may have set mechanism-specific variables (e.g.,
292 	 * KRB5CCNAME).  fetch_pam_environment() protects against LD_*
293 	 * and other environment variables.
294 	 */
295 	penv = fetch_pam_environment(authctxt);
296 	tmp_env = environ;
297 	environ = penv;
298 #endif /* USE_PAM */
299 	if (authctxt->pw->pw_uid != geteuid()) {
300 		temporarily_use_uid(authctxt->pw);
301 		ctx->major = gss_store_cred(&ctx->minor, ctx->deleg_creds,
302 				GSS_C_INITIATE, GSS_C_NULL_OID, 0,
303 				ctx->default_creds, NULL, NULL);
304 		restore_uid();
305 	} else {
306 		/* only when logging in as the privileged user used by sshd */
307 		ctx->major = gss_store_cred(&ctx->minor, ctx->deleg_creds,
308 				GSS_C_INITIATE, GSS_C_NULL_OID, 0,
309 				ctx->default_creds, NULL, NULL);
310 	}
311 #ifdef USE_PAM
312 	environ = tmp_env;
313 	free_pam_environment(penv);
314 #endif /* USE_PAM */
315 	if (GSS_ERROR(ctx->major))
316 		ssh_gssapi_error(ctx, "storing delegated credentials");
317 
318 #else
319 #ifdef KRB5_GSS
320 #error "MIT/Heimdal krb5-specific code missing in ssh_gssapi_storecreds()"
321 	if (ssh_gssapi_is_krb5(ctx->mech))
322 		ssh_gssapi_krb5_storecreds(ctx);
323 #endif /* KRB5_GSS */
324 #ifdef GSI_GSS
325 #error "GSI krb5-specific code missing in ssh_gssapi_storecreds()"
326 	if (ssh_gssapi_is_gsi(ctx->mech))
327 		ssh_gssapi_krb5_storecreds(ctx);
328 #endif /* GSI_GSS */
329 /*#error "Mechanism-specific code missing in ssh_gssapi_storecreds()"*/
330 	return;
331 #endif /* HAVE_GSS_STORE_CRED */
332 }
333 
334 void
335 ssh_gssapi_do_child(Gssctxt *ctx, char ***envp, u_int *envsizep)
336 {
337 	/*
338 	 * MIT/Heimdal/GSI specific code goes here.
339 	 *
340 	 * On Solaris there's nothing to do here as the GSS store and
341 	 * related environment variables are to be set by PAM, if at all
342 	 * (no environment variables are needed to address the default
343 	 * credential store -- the euid does that).
344 	 */
345 #ifdef KRB5_GSS
346 #error "MIT/Heimdal krb5-specific code missing in ssh_gssapi_storecreds()"
347 #endif /* KRB5_GSS */
348 #ifdef GSI_GSS
349 #error "GSI krb5-specific code missing in ssh_gssapi_storecreds()"
350 #endif /* GSI_GSS */
351 	return;
352 }
353 
354 int
355 ssh_gssapi_userok(Gssctxt *ctx, char *user)
356 {
357 	if (ctx == NULL) {
358 		debug3("INTERNAL ERROR: %s", __func__);
359 		return (0);
360 	}
361 
362 	if (user == NULL || *user == '\0')
363 		return (0);
364 
365 #ifdef HAVE___GSS_USEROK
366 	{
367 		int user_ok = 0;
368 
369 		ctx->major = __gss_userok(&ctx->minor, ctx->src_name, user,
370 			&user_ok);
371 		if (GSS_ERROR(ctx->major)) {
372 			debug2("__GSS_userok() failed");
373 			return (0);
374 		}
375 
376 		if (user_ok)
377 			return (1);
378 
379 		/* fall through */
380 	}
381 #else
382 #ifdef GSSAPI_SIMPLE_USEROK
383 	{
384 		/* Mechanism-generic */
385 		OM_uint32	min;
386 		gss_buffer_desc	buf, ename1, ename2;
387 		gss_name_t	iname, cname;
388 		int		eql;
389 
390 		buf.value = user;
391 		buf.length = strlen(user);
392 		ctx->major = gss_import_name(&ctx->minor, &buf,
393 					GSS_C_NULL_OID, &iname);
394 		if (GSS_ERROR(ctx->major)) {
395 			ssh_gssapi_error(ctx,
396 				"importing name for authorizing initiator");
397 			goto failed_simple_userok;
398 		}
399 
400 		ctx->major = gss_canonicalize_name(&ctx->minor, iname,
401 					ctx->actual_mech, &cname);
402 		(void) gss_release_name(&min, &iname);
403 		if (GSS_ERROR(ctx->major)) {
404 			ssh_gssapi_error(ctx, "canonicalizing name");
405 			goto failed_simple_userok;
406 		}
407 
408 		ctx->major = gss_export_name(&ctx->minor, cname, &ename1);
409 		(void) gss_release_name(&min, &cname);
410 		if (GSS_ERROR(ctx->major)) {
411 			ssh_gssapi_error(ctx, "exporting name");
412 			goto failed_simple_userok;
413 		}
414 
415 		ctx->major = gss_export_name(&ctx->minor, ctx->src_name,
416 			&ename2);
417 		if (GSS_ERROR(ctx->major)) {
418 			ssh_gssapi_error(ctx,
419 				"exporting client principal name");
420 			(void) gss_release_buffer(&min, &ename1);
421 			goto failed_simple_userok;
422 		}
423 
424 		eql = (ename1.length == ename2.length &&
425 			memcmp(ename1.value, ename2.value, ename1.length) == 0);
426 
427 		(void) gss_release_buffer(&min, &ename1);
428 		(void) gss_release_buffer(&min, &ename2);
429 
430 		if (eql)
431 			return (1);
432 		/* fall through */
433 	}
434 failed_simple_userok:
435 #endif /* GSSAPI_SIMPLE_USEROK */
436 #ifdef HAVE_GSSCRED_API
437 	{
438 		/* Mechanism-generic, Solaris-specific */
439 		OM_uint32	 maj;
440 		uid_t		 uid;
441 		struct passwd	*pw;
442 
443 		maj = gsscred_name_to_unix_cred(ctx->src_name,
444 				ctx->actual_mech, &uid, NULL, NULL, NULL);
445 
446 		if (GSS_ERROR(maj))
447 			goto failed_simple_gsscred_userok;
448 
449 		if ((pw = getpwnam(user)) == NULL)
450 			goto failed_simple_gsscred_userok;
451 
452 		if (pw->pw_uid == uid)
453 			return (1);
454 		/* fall through */
455 	}
456 failed_simple_gsscred_userok:
457 #endif /* HAVE_GSSCRED_API */
458 #ifdef KRB5_GSS
459 	if (ssh_gssapi_is_krb5(ctx->mech))
460 		if (ssh_gssapi_krb5_userok(ctx->src_name, user))
461 			return (1);
462 #endif /* KRB5_GSS */
463 #ifdef GSI_GSS
464 	if (ssh_gssapi_is_gsi(ctx->mech))
465 		if (ssh_gssapi_gsi_userok(ctx->src_name, user))
466 			return (1);
467 #endif /* GSI_GSS */
468 #endif /* HAVE___GSS_USEROK */
469 
470 	/* default to not authorized */
471 	return (0);
472 }
473 
474 char *
475 ssh_gssapi_localname(Gssctxt *ctx)
476 {
477 	if (ctx == NULL) {
478 		debug3("INTERNAL ERROR: %s", __func__);
479 		return (NULL);
480 	}
481 
482 	debug2("Mapping initiator GSS-API principal to local username");
483 #ifdef HAVE_GSSCRED_API
484 	{
485 		/* Mechanism-generic, Solaris-specific */
486 		OM_uint32	 maj;
487 		uid_t		 uid;
488 		struct passwd	*pw;
489 
490 		if (ctx->src_name == GSS_C_NO_NAME)
491 			goto failed_gsscred_localname;
492 
493 		maj = gsscred_name_to_unix_cred(ctx->src_name,
494 				ctx->actual_mech, &uid, NULL, NULL, NULL);
495 
496 		if (GSS_ERROR(maj))
497 			goto failed_gsscred_localname;
498 
499 		if ((pw = getpwuid(uid)) == NULL)
500 			goto failed_gsscred_localname;
501 
502 		debug2("Mapped the initiator to: %s", pw->pw_name);
503 		return (xstrdup(pw->pw_name));
504 	}
505 failed_gsscred_localname:
506 #endif /* HAVE_GSSCRED_API */
507 #ifdef KRB5_GSS
508 #error "ssh_gssapi_krb5_localname() not implemented"
509 	if (ssh_gssapi_is_krb5(ctx->mech))
510 		return (ssh_gssapi_krb5_localname(ctx->src_name));
511 #endif /* KRB5_GSS */
512 #ifdef GSI_GSS
513 #error "ssh_gssapi_gsi_localname() not implemented"
514 	if (ssh_gssapi_is_gsi(ctx->mech))
515 		return (ssh_gssapi_gsi_localname(ctx->src_name));
516 #endif /* GSI_GSS */
517 	return (NULL);
518 }
519 #endif /*GSSAPI */
520