1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
5 * Authors: Doug Rabson <dfr@rabson.org>
6 * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/param.h>
31#include <sys/stat.h>
32#include <sys/linker.h>
33#include <sys/module.h>
34#include <sys/queue.h>
35#include <sys/socket.h>
36#include <sys/sysctl.h>
37#include <sys/syslog.h>
38#include <ctype.h>
39#include <dirent.h>
40#include <err.h>
41#include <errno.h>
42#ifndef WITHOUT_KERBEROS
43#include <krb5.h>
44#endif
45#include <netdb.h>
46#include <pwd.h>
47#include <signal.h>
48#include <stdarg.h>
49#include <stdio.h>
50#include <stdlib.h>
51#include <string.h>
52#include <unistd.h>
53#include <arpa/inet.h>
54#include <netinet/in.h>
55#include <gssapi/gssapi.h>
56#include <rpc/rpc.h>
57#include <rpc/rpc_com.h>
58
59#include "gssd.h"
60
61#ifndef _PATH_GSS_MECH
62#define _PATH_GSS_MECH	"/etc/gss/mech"
63#endif
64#ifndef _PATH_GSSDSOCK
65#define _PATH_GSSDSOCK	"/var/run/gssd.sock"
66#endif
67#define GSSD_CREDENTIAL_CACHE_FILE	"/tmp/krb5cc_gssd"
68
69struct gss_resource {
70	LIST_ENTRY(gss_resource) gr_link;
71	uint64_t	gr_id;	/* identifier exported to kernel */
72	void*		gr_res;	/* GSS-API resource pointer */
73};
74LIST_HEAD(gss_resource_list, gss_resource) gss_resources;
75int gss_resource_count;
76uint32_t gss_next_id;
77uint32_t gss_start_time;
78int debug_level;
79static char ccfile_dirlist[PATH_MAX + 1], ccfile_substring[NAME_MAX + 1];
80static char pref_realm[1024];
81static int verbose;
82static int hostbased_initiator_cred;
83#ifndef WITHOUT_KERBEROS
84/* 1.2.752.43.13.14 */
85static gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc =
86{6, (void *) "\x2a\x85\x70\x2b\x0d\x0e"};
87static gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X =
88    &gss_krb5_set_allowable_enctypes_x_desc;
89static gss_OID_desc gss_krb5_mech_oid_x_desc =
90{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
91static gss_OID GSS_KRB5_MECH_OID_X =
92    &gss_krb5_mech_oid_x_desc;
93#endif
94
95static void gssd_load_mech(void);
96static int find_ccache_file(const char *, uid_t, char *);
97static int is_a_valid_tgt_cache(const char *, uid_t, int *, time_t *);
98static void gssd_verbose_out(const char *, ...);
99#ifndef WITHOUT_KERBEROS
100static krb5_error_code gssd_get_cc_from_keytab(const char *);
101static OM_uint32 gssd_get_user_cred(OM_uint32 *, uid_t, gss_cred_id_t *);
102#endif
103void gssd_terminate(int);
104
105extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp);
106extern int gssd_syscall(char *path);
107
108int
109main(int argc, char **argv)
110{
111	/*
112	 * We provide an RPC service on a local-domain socket. The
113	 * kernel's GSS-API code will pass what it can't handle
114	 * directly to us.
115	 */
116	struct sockaddr_un sun;
117	int fd, oldmask, ch, debug, jailed;
118	SVCXPRT *xprt;
119	size_t jailed_size;
120
121	/*
122	 * Initialize the credential cache file name substring and the
123	 * search directory list.
124	 */
125	strlcpy(ccfile_substring, "krb5cc_", sizeof(ccfile_substring));
126	ccfile_dirlist[0] = '\0';
127	pref_realm[0] = '\0';
128	debug = 0;
129	verbose = 0;
130	while ((ch = getopt(argc, argv, "dhvs:c:r:")) != -1) {
131		switch (ch) {
132		case 'd':
133			debug_level++;
134			break;
135		case 'h':
136#ifndef WITHOUT_KERBEROS
137			/*
138			 * Enable use of a host based initiator credential
139			 * in the default keytab file.
140			 */
141			hostbased_initiator_cred = 1;
142#else
143			errx(1, "This option not available when built"
144			    " without MK_KERBEROS\n");
145#endif
146			break;
147		case 'v':
148			verbose = 1;
149			break;
150		case 's':
151#ifndef WITHOUT_KERBEROS
152			/*
153			 * Set the directory search list. This enables use of
154			 * find_ccache_file() to search the directories for a
155			 * suitable credentials cache file.
156			 */
157			strlcpy(ccfile_dirlist, optarg, sizeof(ccfile_dirlist));
158#else
159			errx(1, "This option not available when built"
160			    " without MK_KERBEROS\n");
161#endif
162			break;
163		case 'c':
164			/*
165			 * Specify a non-default credential cache file
166			 * substring.
167			 */
168			strlcpy(ccfile_substring, optarg,
169			    sizeof(ccfile_substring));
170			break;
171		case 'r':
172			/*
173			 * Set the preferred realm for the credential cache tgt.
174			 */
175			strlcpy(pref_realm, optarg, sizeof(pref_realm));
176			break;
177		default:
178			fprintf(stderr,
179			    "usage: %s [-d] [-s dir-list] [-c file-substring]"
180			    " [-r preferred-realm]\n", argv[0]);
181			exit(1);
182			break;
183		}
184	}
185
186	gssd_load_mech();
187
188	if (!debug_level) {
189		if (daemon(0, 0) != 0)
190			err(1, "Can't daemonize");
191		signal(SIGINT, SIG_IGN);
192		signal(SIGQUIT, SIG_IGN);
193		signal(SIGHUP, SIG_IGN);
194	}
195	signal(SIGTERM, gssd_terminate);
196	signal(SIGPIPE, gssd_terminate);
197
198	memset(&sun, 0, sizeof sun);
199	sun.sun_family = AF_LOCAL;
200	unlink(_PATH_GSSDSOCK);
201	strcpy(sun.sun_path, _PATH_GSSDSOCK);
202	sun.sun_len = SUN_LEN(&sun);
203	fd = socket(AF_LOCAL, SOCK_STREAM, 0);
204	if (fd < 0) {
205		if (debug_level == 0) {
206			syslog(LOG_ERR, "Can't create local gssd socket");
207			exit(1);
208		}
209		err(1, "Can't create local gssd socket");
210	}
211	oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
212	if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) {
213		if (debug_level == 0) {
214			syslog(LOG_ERR, "Can't bind local gssd socket");
215			exit(1);
216		}
217		err(1, "Can't bind local gssd socket");
218	}
219	umask(oldmask);
220	if (listen(fd, SOMAXCONN) < 0) {
221		if (debug_level == 0) {
222			syslog(LOG_ERR, "Can't listen on local gssd socket");
223			exit(1);
224		}
225		err(1, "Can't listen on local gssd socket");
226	}
227	xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
228	if (!xprt) {
229		if (debug_level == 0) {
230			syslog(LOG_ERR,
231			    "Can't create transport for local gssd socket");
232			exit(1);
233		}
234		err(1, "Can't create transport for local gssd socket");
235	}
236	if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) {
237		if (debug_level == 0) {
238			syslog(LOG_ERR,
239			    "Can't register service for local gssd socket");
240			exit(1);
241		}
242		err(1, "Can't register service for local gssd socket");
243	}
244
245	LIST_INIT(&gss_resources);
246	gss_next_id = 1;
247	gss_start_time = time(0);
248
249	if (gssd_syscall(_PATH_GSSDSOCK) < 0) {
250		jailed = 0;
251		if (errno == EPERM) {
252			jailed_size = sizeof(jailed);
253			sysctlbyname("security.jail.jailed", &jailed,
254			    &jailed_size, NULL, 0);
255		}
256		if (debug_level == 0) {
257			if (jailed != 0)
258				syslog(LOG_ERR, "Cannot start gssd."
259				    " allow.nfsd must be configured");
260			else
261				syslog(LOG_ERR, "Cannot start gssd");
262			exit(1);
263		}
264		if (jailed != 0)
265			err(1, "Cannot start gssd."
266			    " allow.nfsd must be configured");
267		else
268			err(1, "Cannot start gssd");
269	}
270	svc_run();
271	gssd_syscall("");
272
273	return (0);
274}
275
276static void
277gssd_load_mech(void)
278{
279	FILE		*fp;
280	char		buf[256];
281	char		*p;
282	char		*name, *oid, *lib, *kobj;
283
284	fp = fopen(_PATH_GSS_MECH, "r");
285	if (!fp)
286		return;
287
288	while (fgets(buf, sizeof(buf), fp)) {
289		if (*buf == '#')
290			continue;
291		p = buf;
292		name = strsep(&p, "\t\n ");
293		if (p) while (isspace(*p)) p++;
294		oid = strsep(&p, "\t\n ");
295		if (p) while (isspace(*p)) p++;
296		lib = strsep(&p, "\t\n ");
297		if (p) while (isspace(*p)) p++;
298		kobj = strsep(&p, "\t\n ");
299		if (!name || !oid || !lib || !kobj)
300			continue;
301
302		if (strcmp(kobj, "-")) {
303			/*
304			 * Attempt to load the kernel module if its
305			 * not already present.
306			 */
307			if (modfind(kobj) < 0) {
308				if (kldload(kobj) < 0) {
309					fprintf(stderr,
310			"%s: can't find or load kernel module %s for %s\n",
311					    getprogname(), kobj, name);
312				}
313			}
314		}
315	}
316	fclose(fp);
317}
318
319static void *
320gssd_find_resource(uint64_t id)
321{
322	struct gss_resource *gr;
323
324	if (!id)
325		return (NULL);
326
327	LIST_FOREACH(gr, &gss_resources, gr_link)
328		if (gr->gr_id == id)
329			return (gr->gr_res);
330
331	return (NULL);
332}
333
334static uint64_t
335gssd_make_resource(void *res)
336{
337	struct gss_resource *gr;
338
339	if (!res)
340		return (0);
341
342	gr = malloc(sizeof(struct gss_resource));
343	if (!gr)
344		return (0);
345	gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32);
346	gr->gr_res = res;
347	LIST_INSERT_HEAD(&gss_resources, gr, gr_link);
348	gss_resource_count++;
349	if (debug_level > 1)
350		printf("%d resources allocated\n", gss_resource_count);
351
352	return (gr->gr_id);
353}
354
355static void
356gssd_delete_resource(uint64_t id)
357{
358	struct gss_resource *gr;
359
360	LIST_FOREACH(gr, &gss_resources, gr_link) {
361		if (gr->gr_id == id) {
362			LIST_REMOVE(gr, gr_link);
363			free(gr);
364			gss_resource_count--;
365			if (debug_level > 1)
366				printf("%d resources allocated\n",
367				    gss_resource_count);
368			return;
369		}
370	}
371}
372
373static void
374gssd_verbose_out(const char *fmt, ...)
375{
376	va_list ap;
377
378	if (verbose != 0) {
379		va_start(ap, fmt);
380		if (debug_level == 0)
381			vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap);
382		else
383			vfprintf(stderr, fmt, ap);
384		va_end(ap);
385	}
386}
387
388bool_t
389gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
390{
391
392	gssd_verbose_out("gssd_null: done\n");
393	return (TRUE);
394}
395
396bool_t
397gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp)
398{
399	gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
400	gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
401	gss_name_t name = GSS_C_NO_NAME;
402	char ccname[PATH_MAX + 5 + 1], *cp, *cp2;
403	int gotone, gotcred;
404	OM_uint32 min_stat;
405#ifndef WITHOUT_KERBEROS
406	gss_buffer_desc principal_desc;
407	char enctype[sizeof(uint32_t)];
408	int key_enctype;
409	OM_uint32 maj_stat;
410#endif
411
412	memset(result, 0, sizeof(*result));
413	if (hostbased_initiator_cred != 0 && argp->cred != 0 &&
414	    argp->uid == 0) {
415		/*
416		 * These credentials are for a host based initiator name
417		 * in a keytab file, which should now have credentials
418		 * in /tmp/krb5cc_gssd, because gss_acquire_cred() did
419		 * the equivalent of "kinit -k".
420		 */
421		snprintf(ccname, sizeof(ccname), "FILE:%s",
422		    GSSD_CREDENTIAL_CACHE_FILE);
423	} else if (ccfile_dirlist[0] != '\0' && argp->cred == 0) {
424		/*
425		 * For the "-s" case and no credentials provided as an
426		 * argument, search the directory list for an appropriate
427		 * credential cache file. If the search fails, return failure.
428		 */
429		gotone = 0;
430		cp = ccfile_dirlist;
431		do {
432			cp2 = strchr(cp, ':');
433			if (cp2 != NULL)
434				*cp2 = '\0';
435			gotone = find_ccache_file(cp, argp->uid, ccname);
436			if (gotone != 0)
437				break;
438			if (cp2 != NULL)
439				*cp2++ = ':';
440			cp = cp2;
441		} while (cp != NULL && *cp != '\0');
442		if (gotone == 0) {
443			result->major_status = GSS_S_CREDENTIALS_EXPIRED;
444			gssd_verbose_out("gssd_init_sec_context: -s no"
445			    " credential cache file found for uid=%d\n",
446			    (int)argp->uid);
447			return (TRUE);
448		}
449	} else {
450		/*
451		 * If there wasn't a "-s" option or the credentials have
452		 * been provided as an argument, do it the old way.
453		 * When credentials are provided, the uid should be root.
454		 */
455		if (argp->cred != 0 && argp->uid != 0) {
456			if (debug_level == 0)
457				syslog(LOG_ERR, "gss_init_sec_context:"
458				    " cred for non-root");
459			else
460				fprintf(stderr, "gss_init_sec_context:"
461				    " cred for non-root\n");
462		}
463		snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
464		    (int) argp->uid);
465	}
466	setenv("KRB5CCNAME", ccname, TRUE);
467
468	if (argp->cred) {
469		cred = gssd_find_resource(argp->cred);
470		if (!cred) {
471			result->major_status = GSS_S_CREDENTIALS_EXPIRED;
472			gssd_verbose_out("gssd_init_sec_context: cred"
473			    " resource not found\n");
474			return (TRUE);
475		}
476	}
477	if (argp->ctx) {
478		ctx = gssd_find_resource(argp->ctx);
479		if (!ctx) {
480			result->major_status = GSS_S_CONTEXT_EXPIRED;
481			gssd_verbose_out("gssd_init_sec_context: context"
482			    " resource not found\n");
483			return (TRUE);
484		}
485	}
486	if (argp->name) {
487		name = gssd_find_resource(argp->name);
488		if (!name) {
489			result->major_status = GSS_S_BAD_NAME;
490			gssd_verbose_out("gssd_init_sec_context: name"
491			    " resource not found\n");
492			return (TRUE);
493		}
494	}
495	gotcred = 0;
496
497	result->major_status = gss_init_sec_context(&result->minor_status,
498	    cred, &ctx, name, argp->mech_type,
499	    argp->req_flags, argp->time_req, argp->input_chan_bindings,
500	    &argp->input_token, &result->actual_mech_type,
501	    &result->output_token, &result->ret_flags, &result->time_rec);
502	gssd_verbose_out("gssd_init_sec_context: done major=0x%x minor=%d"
503	    " uid=%d\n", (unsigned int)result->major_status,
504	    (int)result->minor_status, (int)argp->uid);
505	if (gotcred != 0)
506		gss_release_cred(&min_stat, &cred);
507
508	if (result->major_status == GSS_S_COMPLETE
509	    || result->major_status == GSS_S_CONTINUE_NEEDED) {
510		if (argp->ctx)
511			result->ctx = argp->ctx;
512		else
513			result->ctx = gssd_make_resource(ctx);
514	}
515
516	return (TRUE);
517}
518
519bool_t
520gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp)
521{
522	gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
523	gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
524	gss_name_t src_name;
525	gss_cred_id_t delegated_cred_handle;
526
527	memset(result, 0, sizeof(*result));
528	if (argp->ctx) {
529		ctx = gssd_find_resource(argp->ctx);
530		if (!ctx) {
531			result->major_status = GSS_S_CONTEXT_EXPIRED;
532			gssd_verbose_out("gssd_accept_sec_context: ctx"
533			    " resource not found\n");
534			return (TRUE);
535		}
536	}
537	if (argp->cred) {
538		cred = gssd_find_resource(argp->cred);
539		if (!cred) {
540			result->major_status = GSS_S_CREDENTIALS_EXPIRED;
541			gssd_verbose_out("gssd_accept_sec_context: cred"
542			    " resource not found\n");
543			return (TRUE);
544		}
545	}
546
547	memset(result, 0, sizeof(*result));
548	result->major_status = gss_accept_sec_context(&result->minor_status,
549	    &ctx, cred, &argp->input_token, argp->input_chan_bindings,
550	    &src_name, &result->mech_type, &result->output_token,
551	    &result->ret_flags, &result->time_rec,
552	    &delegated_cred_handle);
553	gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n",
554	    (unsigned int)result->major_status, (int)result->minor_status);
555
556	if (result->major_status == GSS_S_COMPLETE
557	    || result->major_status == GSS_S_CONTINUE_NEEDED) {
558		if (argp->ctx)
559			result->ctx = argp->ctx;
560		else
561			result->ctx = gssd_make_resource(ctx);
562		result->src_name = gssd_make_resource(src_name);
563		result->delegated_cred_handle =
564			gssd_make_resource(delegated_cred_handle);
565	}
566
567	return (TRUE);
568}
569
570bool_t
571gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp)
572{
573	gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
574
575	if (ctx) {
576		result->major_status = gss_delete_sec_context(
577			&result->minor_status, &ctx, &result->output_token);
578		gssd_delete_resource(argp->ctx);
579	} else {
580		result->major_status = GSS_S_COMPLETE;
581		result->minor_status = 0;
582	}
583	gssd_verbose_out("gssd_delete_sec_context: done major=0x%x minor=%d\n",
584	    (unsigned int)result->major_status, (int)result->minor_status);
585
586	return (TRUE);
587}
588
589bool_t
590gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp)
591{
592	gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
593
594	if (ctx) {
595		result->major_status = gss_export_sec_context(
596			&result->minor_status, &ctx,
597			&result->interprocess_token);
598		result->format = KGSS_HEIMDAL_1_1;
599		gssd_delete_resource(argp->ctx);
600	} else {
601		result->major_status = GSS_S_FAILURE;
602		result->minor_status = 0;
603		result->interprocess_token.length = 0;
604		result->interprocess_token.value = NULL;
605	}
606	gssd_verbose_out("gssd_export_sec_context: done major=0x%x minor=%d\n",
607	    (unsigned int)result->major_status, (int)result->minor_status);
608
609	return (TRUE);
610}
611
612bool_t
613gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp)
614{
615	gss_name_t name;
616
617	result->major_status = gss_import_name(&result->minor_status,
618	    &argp->input_name_buffer, argp->input_name_type, &name);
619	gssd_verbose_out("gssd_import_name: done major=0x%x minor=%d\n",
620	    (unsigned int)result->major_status, (int)result->minor_status);
621
622	if (result->major_status == GSS_S_COMPLETE)
623		result->output_name = gssd_make_resource(name);
624	else
625		result->output_name = 0;
626
627	return (TRUE);
628}
629
630/*
631 * If the name is a numeric IP host address, do a DNS lookup on it and
632 * return the DNS name in a malloc'd string.
633 */
634static char *
635gssd_conv_ip_to_dns(int len, char *name)
636{
637	struct sockaddr_in sin;
638	struct sockaddr_in6 sin6;
639	char *retcp;
640
641	retcp = NULL;
642	if (len > 0) {
643		retcp = mem_alloc(NI_MAXHOST);
644		memcpy(retcp, name, len);
645		retcp[len] = '\0';
646		if (inet_pton(AF_INET, retcp, &sin.sin_addr) != 0) {
647			sin.sin_family = AF_INET;
648			sin.sin_len = sizeof(sin);
649			sin.sin_port = 0;
650			if (getnameinfo((struct sockaddr *)&sin,
651			    sizeof(sin), retcp, NI_MAXHOST,
652			    NULL, 0, NI_NAMEREQD) != 0) {
653				mem_free(retcp, NI_MAXHOST);
654				return (NULL);
655			}
656		} else if (inet_pton(AF_INET6, retcp, &sin6.sin6_addr) != 0) {
657			sin6.sin6_family = AF_INET6;
658			sin6.sin6_len = sizeof(sin6);
659			sin6.sin6_port = 0;
660			if (getnameinfo((struct sockaddr *)&sin6,
661			    sizeof(sin6), retcp, NI_MAXHOST,
662			    NULL, 0, NI_NAMEREQD) != 0) {
663				mem_free(retcp, NI_MAXHOST);
664				return (NULL);
665			}
666		} else {
667			mem_free(retcp, NI_MAXHOST);
668			return (NULL);
669		}
670		gssd_verbose_out("gssd_conv_ip_to_dns: %s\n", retcp);
671	}
672	return (retcp);
673}
674
675bool_t
676gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp)
677{
678	gss_name_t name = gssd_find_resource(argp->input_name);
679	gss_name_t output_name;
680
681	memset(result, 0, sizeof(*result));
682	if (!name) {
683		result->major_status = GSS_S_BAD_NAME;
684		return (TRUE);
685	}
686
687	result->major_status = gss_canonicalize_name(&result->minor_status,
688	    name, argp->mech_type, &output_name);
689	gssd_verbose_out("gssd_canonicalize_name: done major=0x%x minor=%d\n",
690	    (unsigned int)result->major_status, (int)result->minor_status);
691
692	if (result->major_status == GSS_S_COMPLETE)
693		result->output_name = gssd_make_resource(output_name);
694	else
695		result->output_name = 0;
696
697	return (TRUE);
698}
699
700bool_t
701gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp)
702{
703	gss_name_t name = gssd_find_resource(argp->input_name);
704
705	memset(result, 0, sizeof(*result));
706	if (!name) {
707		result->major_status = GSS_S_BAD_NAME;
708		gssd_verbose_out("gssd_export_name: name resource not found\n");
709		return (TRUE);
710	}
711
712	result->major_status = gss_export_name(&result->minor_status,
713	    name, &result->exported_name);
714	gssd_verbose_out("gssd_export_name: done major=0x%x minor=%d\n",
715	    (unsigned int)result->major_status, (int)result->minor_status);
716
717	return (TRUE);
718}
719
720bool_t
721gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp)
722{
723	gss_name_t name = gssd_find_resource(argp->input_name);
724
725	if (name) {
726		result->major_status = gss_release_name(&result->minor_status,
727		    &name);
728		gssd_delete_resource(argp->input_name);
729	} else {
730		result->major_status = GSS_S_COMPLETE;
731		result->minor_status = 0;
732	}
733	gssd_verbose_out("gssd_release_name: done major=0x%x minor=%d\n",
734	    (unsigned int)result->major_status, (int)result->minor_status);
735
736	return (TRUE);
737}
738
739bool_t
740gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp)
741{
742	gss_name_t name = gssd_find_resource(argp->pname);
743	uid_t uid;
744	char buf[1024], *bufp;
745	struct passwd pwd, *pw;
746	size_t buflen;
747	int error;
748	static size_t buflen_hint = 1024;
749
750	memset(result, 0, sizeof(*result));
751	if (name) {
752		result->major_status =
753			gss_pname_to_uid(&result->minor_status,
754			    name, argp->mech, &uid);
755		if (result->major_status == GSS_S_COMPLETE) {
756			result->uid = uid;
757			buflen = buflen_hint;
758			for (;;) {
759				pw = NULL;
760				bufp = buf;
761				if (buflen > sizeof(buf))
762					bufp = malloc(buflen);
763				if (bufp == NULL)
764					break;
765				error = getpwuid_r(uid, &pwd, bufp, buflen,
766				    &pw);
767				if (error != ERANGE)
768					break;
769				if (buflen > sizeof(buf))
770					free(bufp);
771				buflen += 1024;
772				if (buflen > buflen_hint)
773					buflen_hint = buflen;
774			}
775			if (pw) {
776				int len = NGROUPS;
777				int groups[NGROUPS];
778				result->gid = pw->pw_gid;
779				getgrouplist(pw->pw_name, pw->pw_gid,
780				    groups, &len);
781				result->gidlist.gidlist_len = len;
782				result->gidlist.gidlist_val =
783					mem_alloc(len * sizeof(int));
784				memcpy(result->gidlist.gidlist_val, groups,
785				    len * sizeof(int));
786				gssd_verbose_out("gssd_pname_to_uid: mapped"
787				    " to uid=%d, gid=%d\n", (int)result->uid,
788				    (int)result->gid);
789			} else {
790				result->gid = 65534;
791				result->gidlist.gidlist_len = 0;
792				result->gidlist.gidlist_val = NULL;
793				gssd_verbose_out("gssd_pname_to_uid: mapped"
794				    " to uid=%d, but no groups\n",
795				    (int)result->uid);
796			}
797			if (bufp != NULL && buflen > sizeof(buf))
798				free(bufp);
799		} else
800			gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x"
801			    " minor=%d\n", (unsigned int)result->major_status,
802			    (int)result->minor_status);
803	} else {
804		result->major_status = GSS_S_BAD_NAME;
805		result->minor_status = 0;
806		gssd_verbose_out("gssd_pname_to_uid: no name\n");
807	}
808
809	return (TRUE);
810}
811
812bool_t
813gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp)
814{
815	gss_name_t desired_name = GSS_C_NO_NAME;
816	gss_cred_id_t cred;
817	char ccname[PATH_MAX + 5 + 1], *cp, *cp2;
818	int gotone;
819#ifndef WITHOUT_KERBEROS
820	gss_buffer_desc namebuf;
821	uint32_t minstat;
822	krb5_error_code kret;
823#endif
824
825	memset(result, 0, sizeof(*result));
826	if (argp->desired_name) {
827		desired_name = gssd_find_resource(argp->desired_name);
828		if (!desired_name) {
829			result->major_status = GSS_S_BAD_NAME;
830			gssd_verbose_out("gssd_acquire_cred: no desired name"
831			    " found\n");
832			return (TRUE);
833		}
834	}
835
836#ifndef WITHOUT_KERBEROS
837	if (hostbased_initiator_cred != 0 && argp->desired_name != 0 &&
838	    argp->uid == 0 && argp->cred_usage == GSS_C_INITIATE) {
839		/* This is a host based initiator name in the keytab file. */
840		snprintf(ccname, sizeof(ccname), "FILE:%s",
841		    GSSD_CREDENTIAL_CACHE_FILE);
842		setenv("KRB5CCNAME", ccname, TRUE);
843		result->major_status = gss_display_name(&result->minor_status,
844		    desired_name, &namebuf, NULL);
845		gssd_verbose_out("gssd_acquire_cred: desired name for host "
846		    "based initiator cred major=0x%x minor=%d\n",
847		    (unsigned int)result->major_status,
848		    (int)result->minor_status);
849		if (result->major_status != GSS_S_COMPLETE)
850			return (TRUE);
851		if (namebuf.length > PATH_MAX + 5) {
852			result->minor_status = 0;
853			result->major_status = GSS_S_FAILURE;
854			return (TRUE);
855		}
856		memcpy(ccname, namebuf.value, namebuf.length);
857		ccname[namebuf.length] = '\0';
858		if ((cp = strchr(ccname, '@')) != NULL)
859			*cp = '/';
860		kret = gssd_get_cc_from_keytab(ccname);
861		gssd_verbose_out("gssd_acquire_cred: using keytab entry for "
862		    "%s, kerberos ret=%d\n", ccname, (int)kret);
863		gss_release_buffer(&minstat, &namebuf);
864		if (kret != 0) {
865			result->minor_status = kret;
866			result->major_status = GSS_S_FAILURE;
867			return (TRUE);
868		}
869	} else
870#endif /* !WITHOUT_KERBEROS */
871	if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) {
872		/*
873		 * For the "-s" case and no name provided as an
874		 * argument, search the directory list for an appropriate
875		 * credential cache file. If the search fails, return failure.
876		 */
877		gotone = 0;
878		cp = ccfile_dirlist;
879		do {
880			cp2 = strchr(cp, ':');
881			if (cp2 != NULL)
882				*cp2 = '\0';
883			gotone = find_ccache_file(cp, argp->uid, ccname);
884			if (gotone != 0)
885				break;
886			if (cp2 != NULL)
887				*cp2++ = ':';
888			cp = cp2;
889		} while (cp != NULL && *cp != '\0');
890		if (gotone == 0) {
891			result->major_status = GSS_S_CREDENTIALS_EXPIRED;
892			gssd_verbose_out("gssd_acquire_cred: no cred cache"
893			    " file found\n");
894			return (TRUE);
895		}
896		setenv("KRB5CCNAME", ccname, TRUE);
897	} else {
898		/*
899		 * If there wasn't a "-s" option or the name has
900		 * been provided as an argument, do it the old way.
901		 * When a name is provided, it will normally exist in the
902		 * default keytab file and the uid will be root.
903		 */
904		if (argp->desired_name != 0 && argp->uid != 0) {
905			if (debug_level == 0)
906				syslog(LOG_ERR, "gss_acquire_cred:"
907				    " principal_name for non-root");
908			else
909				fprintf(stderr, "gss_acquire_cred:"
910				    " principal_name for non-root\n");
911		}
912		snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
913		    (int) argp->uid);
914		setenv("KRB5CCNAME", ccname, TRUE);
915	}
916
917	result->major_status = gss_acquire_cred(&result->minor_status,
918	    desired_name, argp->time_req, argp->desired_mechs,
919	    argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec);
920	gssd_verbose_out("gssd_acquire_cred: done major=0x%x minor=%d\n",
921	    (unsigned int)result->major_status, (int)result->minor_status);
922
923	if (result->major_status == GSS_S_COMPLETE)
924		result->output_cred = gssd_make_resource(cred);
925	else
926		result->output_cred = 0;
927
928	return (TRUE);
929}
930
931bool_t
932gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp)
933{
934	gss_cred_id_t cred = gssd_find_resource(argp->cred);
935
936	memset(result, 0, sizeof(*result));
937	if (!cred) {
938		result->major_status = GSS_S_CREDENTIALS_EXPIRED;
939		gssd_verbose_out("gssd_set_cred: no credentials\n");
940		return (TRUE);
941	}
942
943	result->major_status = gss_set_cred_option(&result->minor_status,
944	    &cred, argp->option_name, &argp->option_value);
945	gssd_verbose_out("gssd_set_cred: done major=0x%x minor=%d\n",
946	    (unsigned int)result->major_status, (int)result->minor_status);
947
948	return (TRUE);
949}
950
951bool_t
952gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp)
953{
954	gss_cred_id_t cred = gssd_find_resource(argp->cred);
955
956	if (cred) {
957		result->major_status = gss_release_cred(&result->minor_status,
958		    &cred);
959		gssd_delete_resource(argp->cred);
960	} else {
961		result->major_status = GSS_S_COMPLETE;
962		result->minor_status = 0;
963	}
964	gssd_verbose_out("gssd_release_cred: done major=0x%x minor=%d\n",
965	    (unsigned int)result->major_status, (int)result->minor_status);
966
967	return (TRUE);
968}
969
970bool_t
971gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp)
972{
973
974	result->message_context = argp->message_context;
975	result->major_status = gss_display_status(&result->minor_status,
976	    argp->status_value, argp->status_type, argp->mech_type,
977	    &result->message_context, &result->status_string);
978	gssd_verbose_out("gssd_display_status: done major=0x%x minor=%d\n",
979	    (unsigned int)result->major_status, (int)result->minor_status);
980
981	return (TRUE);
982}
983
984bool_t
985gssd_ip_to_dns_1_svc(ip_to_dns_args *argp, ip_to_dns_res *result, struct svc_req *rqstp)
986{
987	char *host;
988
989	memset(result, 0, sizeof(*result));
990	/* Check to see if the name is actually an IP address. */
991	host = gssd_conv_ip_to_dns(argp->ip_addr.ip_addr_len,
992	    argp->ip_addr.ip_addr_val);
993	if (host != NULL) {
994		result->major_status = GSS_S_COMPLETE;
995		result->dns_name.dns_name_len = strlen(host);
996		result->dns_name.dns_name_val = host;
997		return (TRUE);
998	}
999	result->major_status = GSS_S_FAILURE;
1000	return (TRUE);
1001}
1002
1003int
1004gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
1005{
1006	/*
1007	 * We don't use XDR to free the results - anything which was
1008	 * allocated came from GSS-API. We use xdr_result to figure
1009	 * out what to do.
1010	 */
1011	OM_uint32 junk;
1012
1013	if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) {
1014		init_sec_context_res *p = (init_sec_context_res *) result;
1015		gss_release_buffer(&junk, &p->output_token);
1016	} else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) {
1017		accept_sec_context_res *p = (accept_sec_context_res *) result;
1018		gss_release_buffer(&junk, &p->output_token);
1019	} else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) {
1020		delete_sec_context_res *p = (delete_sec_context_res *) result;
1021		gss_release_buffer(&junk, &p->output_token);
1022	} else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) {
1023		export_sec_context_res *p = (export_sec_context_res *) result;
1024		if (p->interprocess_token.length)
1025			memset(p->interprocess_token.value, 0,
1026			    p->interprocess_token.length);
1027		gss_release_buffer(&junk, &p->interprocess_token);
1028	} else if (xdr_result == (xdrproc_t) xdr_export_name_res) {
1029		export_name_res *p = (export_name_res *) result;
1030		gss_release_buffer(&junk, &p->exported_name);
1031	} else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) {
1032		acquire_cred_res *p = (acquire_cred_res *) result;
1033		gss_release_oid_set(&junk, &p->actual_mechs);
1034	} else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) {
1035		pname_to_uid_res *p = (pname_to_uid_res *) result;
1036		if (p->gidlist.gidlist_val)
1037			free(p->gidlist.gidlist_val);
1038	} else if (xdr_result == (xdrproc_t) xdr_display_status_res) {
1039		display_status_res *p = (display_status_res *) result;
1040		gss_release_buffer(&junk, &p->status_string);
1041	}
1042
1043	return (TRUE);
1044}
1045
1046/*
1047 * Search a directory for the most likely candidate to be used as the
1048 * credential cache for a uid. If successful, return 1 and fill the
1049 * file's path id into "rpath". Otherwise, return 0.
1050 */
1051static int
1052find_ccache_file(const char *dirpath, uid_t uid, char *rpath)
1053{
1054	DIR *dirp;
1055	struct dirent *dp;
1056	struct stat sb;
1057	time_t exptime, oexptime;
1058	int gotone, len, rating, orating;
1059	char namepath[PATH_MAX + 5 + 1];
1060	char retpath[PATH_MAX + 5 + 1];
1061
1062	dirp = opendir(dirpath);
1063	if (dirp == NULL)
1064		return (0);
1065	gotone = 0;
1066	orating = 0;
1067	oexptime = 0;
1068	while ((dp = readdir(dirp)) != NULL) {
1069		len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath,
1070		    dp->d_name);
1071		if (len < sizeof(namepath) &&
1072		    (hostbased_initiator_cred == 0 || strcmp(namepath,
1073		     GSSD_CREDENTIAL_CACHE_FILE) != 0) &&
1074		    strstr(dp->d_name, ccfile_substring) != NULL &&
1075		    lstat(namepath, &sb) >= 0 &&
1076		    sb.st_uid == uid &&
1077		    S_ISREG(sb.st_mode)) {
1078			len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s",
1079			    dirpath, dp->d_name);
1080			if (len < sizeof(namepath) &&
1081			    is_a_valid_tgt_cache(namepath, uid, &rating,
1082			    &exptime) != 0) {
1083				if (gotone == 0 || rating > orating ||
1084				    (rating == orating && exptime > oexptime)) {
1085					orating = rating;
1086					oexptime = exptime;
1087					strcpy(retpath, namepath);
1088					gotone = 1;
1089				}
1090			}
1091		}
1092	}
1093	closedir(dirp);
1094	if (gotone != 0) {
1095		strcpy(rpath, retpath);
1096		return (1);
1097	}
1098	return (0);
1099}
1100
1101/*
1102 * Try to determine if the file is a valid tgt cache file.
1103 * Check that the file has a valid tgt for a principal.
1104 * If it does, return 1, otherwise return 0.
1105 * It also returns a "rating" and the expiry time for the TGT, when found.
1106 * This "rating" is higher based on heuristics that make it more
1107 * likely to be the correct credential cache file to use. It can
1108 * be used by the caller, along with expiry time, to select from
1109 * multiple credential cache files.
1110 */
1111static int
1112is_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating,
1113    time_t *retexptime)
1114{
1115#ifndef WITHOUT_KERBEROS
1116	krb5_context context;
1117	krb5_principal princ;
1118	krb5_ccache ccache;
1119	krb5_error_code retval;
1120	krb5_cc_cursor curse;
1121	krb5_creds krbcred;
1122	int gotone, orating, rating, ret;
1123	struct passwd *pw;
1124	char *cp, *cp2, *pname;
1125	time_t exptime;
1126
1127	/* Find a likely name for the uid principal. */
1128	pw = getpwuid(uid);
1129
1130	/*
1131	 * Do a bunch of krb5 library stuff to try and determine if
1132	 * this file is a credentials cache with an appropriate TGT
1133	 * in it.
1134	 */
1135	retval = krb5_init_context(&context);
1136	if (retval != 0)
1137		return (0);
1138	retval = krb5_cc_resolve(context, filepath, &ccache);
1139	if (retval != 0) {
1140		krb5_free_context(context);
1141		return (0);
1142	}
1143	ret = 0;
1144	orating = 0;
1145	exptime = 0;
1146	retval = krb5_cc_start_seq_get(context, ccache, &curse);
1147	if (retval == 0) {
1148		while ((retval = krb5_cc_next_cred(context, ccache, &curse,
1149		    &krbcred)) == 0) {
1150			gotone = 0;
1151			rating = 0;
1152			retval = krb5_unparse_name(context, krbcred.server,
1153			    &pname);
1154			if (retval == 0) {
1155				cp = strchr(pname, '/');
1156				if (cp != NULL) {
1157					*cp++ = '\0';
1158					if (strcmp(pname, "krbtgt") == 0 &&
1159					    krbcred.times.endtime > time(NULL)
1160					    ) {
1161						gotone = 1;
1162						/*
1163						 * Test to see if this is a
1164						 * tgt for cross-realm auth.
1165						 * Rate it higher, if it is not.
1166						 */
1167						cp2 = strchr(cp, '@');
1168						if (cp2 != NULL) {
1169							*cp2++ = '\0';
1170							if (strcmp(cp, cp2) ==
1171							    0)
1172								rating++;
1173						}
1174					}
1175				}
1176				free(pname);
1177			}
1178			if (gotone != 0) {
1179				retval = krb5_unparse_name(context,
1180				    krbcred.client, &pname);
1181				if (retval == 0) {
1182					cp = strchr(pname, '@');
1183					if (cp != NULL) {
1184						*cp++ = '\0';
1185						if (pw != NULL && strcmp(pname,
1186						    pw->pw_name) == 0)
1187							rating++;
1188						if (strchr(pname, '/') == NULL)
1189							rating++;
1190						if (pref_realm[0] != '\0' &&
1191						    strcmp(cp, pref_realm) == 0)
1192							rating++;
1193					}
1194				}
1195				free(pname);
1196				if (rating > orating) {
1197					orating = rating;
1198					exptime = krbcred.times.endtime;
1199				} else if (rating == orating &&
1200				    krbcred.times.endtime > exptime)
1201					exptime = krbcred.times.endtime;
1202				ret = 1;
1203			}
1204			krb5_free_cred_contents(context, &krbcred);
1205		}
1206		krb5_cc_end_seq_get(context, ccache, &curse);
1207	}
1208	krb5_cc_close(context, ccache);
1209	krb5_free_context(context);
1210	if (ret != 0) {
1211		*retrating = orating;
1212		*retexptime = exptime;
1213	}
1214	return (ret);
1215#else /* WITHOUT_KERBEROS */
1216	return (0);
1217#endif /* !WITHOUT_KERBEROS */
1218}
1219
1220#ifndef WITHOUT_KERBEROS
1221/*
1222 * This function attempts to do essentially a "kinit -k" for the principal
1223 * name provided as the argument, so that there will be a TGT in the
1224 * credential cache.
1225 */
1226static krb5_error_code
1227gssd_get_cc_from_keytab(const char *name)
1228{
1229	krb5_error_code ret, opt_ret, princ_ret, cc_ret, kt_ret, cred_ret;
1230	krb5_context context;
1231	krb5_principal principal;
1232	krb5_keytab kt;
1233	krb5_creds cred;
1234	krb5_get_init_creds_opt *opt;
1235	krb5_deltat start_time = 0;
1236	krb5_ccache ccache;
1237
1238	ret = krb5_init_context(&context);
1239	if (ret != 0)
1240		return (ret);
1241	opt_ret = cc_ret = kt_ret = cred_ret = 1;	/* anything non-zero */
1242	princ_ret = ret = krb5_parse_name(context, name, &principal);
1243	if (ret == 0)
1244		opt_ret = ret = krb5_get_init_creds_opt_alloc(context, &opt);
1245	if (ret == 0)
1246		cc_ret = ret = krb5_cc_default(context, &ccache);
1247	if (ret == 0)
1248		ret = krb5_cc_initialize(context, ccache, principal);
1249	if (ret == 0) {
1250		krb5_get_init_creds_opt_set_default_flags(context, "gssd",
1251		    krb5_principal_get_realm(context, principal), opt);
1252		kt_ret = ret = krb5_kt_default(context, &kt);
1253	}
1254	if (ret == 0)
1255		cred_ret = ret = krb5_get_init_creds_keytab(context, &cred,
1256		    principal, kt, start_time, NULL, opt);
1257	if (ret == 0)
1258		ret = krb5_cc_store_cred(context, ccache, &cred);
1259	if (kt_ret == 0)
1260		krb5_kt_close(context, kt);
1261	if (cc_ret == 0)
1262		krb5_cc_close(context, ccache);
1263	if (opt_ret == 0)
1264		krb5_get_init_creds_opt_free(context, opt);
1265	if (princ_ret == 0)
1266		krb5_free_principal(context, principal);
1267	if (cred_ret == 0)
1268		krb5_free_cred_contents(context, &cred);
1269	krb5_free_context(context);
1270	return (ret);
1271}
1272
1273/*
1274 * Acquire a gss credential for a uid.
1275 */
1276static OM_uint32
1277gssd_get_user_cred(OM_uint32 *min_statp, uid_t uid, gss_cred_id_t *credp)
1278{
1279	gss_buffer_desc principal_desc;
1280	gss_name_t name;
1281	OM_uint32 maj_stat, min_stat;
1282	gss_OID_set mechlist;
1283	struct passwd *pw;
1284
1285	pw = getpwuid(uid);
1286	if (pw == NULL) {
1287		*min_statp = 0;
1288		return (GSS_S_FAILURE);
1289	}
1290
1291	/*
1292	 * The mechanism must be set to KerberosV for acquisition
1293	 * of credentials to work reliably.
1294	 */
1295	maj_stat = gss_create_empty_oid_set(min_statp, &mechlist);
1296	if (maj_stat != GSS_S_COMPLETE)
1297		return (maj_stat);
1298	maj_stat = gss_add_oid_set_member(min_statp, GSS_KRB5_MECH_OID_X,
1299	    &mechlist);
1300	if (maj_stat != GSS_S_COMPLETE) {
1301		gss_release_oid_set(&min_stat, &mechlist);
1302		return (maj_stat);
1303	}
1304
1305	principal_desc.value = (void *)pw->pw_name;
1306	principal_desc.length = strlen(pw->pw_name);
1307	maj_stat = gss_import_name(min_statp, &principal_desc,
1308	    GSS_C_NT_USER_NAME, &name);
1309	if (maj_stat != GSS_S_COMPLETE) {
1310		gss_release_oid_set(&min_stat, &mechlist);
1311		return (maj_stat);
1312	}
1313	/* Acquire the credentials. */
1314	maj_stat = gss_acquire_cred(min_statp, name, 0, mechlist,
1315	    GSS_C_INITIATE, credp, NULL, NULL);
1316	gss_release_name(&min_stat, &name);
1317	gss_release_oid_set(&min_stat, &mechlist);
1318	return (maj_stat);
1319}
1320#endif /* !WITHOUT_KERBEROS */
1321
1322void gssd_terminate(int sig __unused)
1323{
1324
1325#ifndef WITHOUT_KERBEROS
1326	if (hostbased_initiator_cred != 0)
1327		unlink(GSSD_CREDENTIAL_CACHE_FILE);
1328#endif
1329	gssd_syscall("");
1330	exit(0);
1331}
1332
1333