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