1184588Sdfr/*-
2184588Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/
3184588Sdfr * Authors: Doug Rabson <dfr@rabson.org>
4184588Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org>
5184588Sdfr *
6184588Sdfr * Redistribution and use in source and binary forms, with or without
7184588Sdfr * modification, are permitted provided that the following conditions
8184588Sdfr * are met:
9184588Sdfr * 1. Redistributions of source code must retain the above copyright
10184588Sdfr *    notice, this list of conditions and the following disclaimer.
11184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright
12184588Sdfr *    notice, this list of conditions and the following disclaimer in the
13184588Sdfr *    documentation and/or other materials provided with the distribution.
14184588Sdfr *
15184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18184588Sdfr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25184588Sdfr * SUCH DAMAGE.
26184588Sdfr */
27184588Sdfr
28184588Sdfr#include <sys/cdefs.h>
29184588Sdfr__FBSDID("$FreeBSD$");
30184588Sdfr
31184588Sdfr#include <sys/param.h>
32184588Sdfr#include <sys/stat.h>
33184588Sdfr#include <sys/linker.h>
34184588Sdfr#include <sys/module.h>
35184588Sdfr#include <sys/queue.h>
36245016Srmacklem#include <sys/syslog.h>
37184588Sdfr#include <ctype.h>
38245089Srmacklem#include <dirent.h>
39184588Sdfr#include <err.h>
40250689Srmacklem#include <errno.h>
41245089Srmacklem#ifndef WITHOUT_KERBEROS
42245089Srmacklem#include <krb5.h>
43245089Srmacklem#endif
44184588Sdfr#include <pwd.h>
45252674Srmacklem#include <signal.h>
46252068Srmacklem#include <stdarg.h>
47184588Sdfr#include <stdio.h>
48184588Sdfr#include <stdlib.h>
49184588Sdfr#include <string.h>
50184588Sdfr#include <unistd.h>
51184588Sdfr#include <gssapi/gssapi.h>
52184588Sdfr#include <rpc/rpc.h>
53184588Sdfr#include <rpc/rpc_com.h>
54184588Sdfr
55184588Sdfr#include "gssd.h"
56184588Sdfr
57184588Sdfr#ifndef _PATH_GSS_MECH
58184588Sdfr#define _PATH_GSS_MECH	"/etc/gss/mech"
59184588Sdfr#endif
60184588Sdfr#ifndef _PATH_GSSDSOCK
61184588Sdfr#define _PATH_GSSDSOCK	"/var/run/gssd.sock"
62184588Sdfr#endif
63184588Sdfr
64184588Sdfrstruct gss_resource {
65184588Sdfr	LIST_ENTRY(gss_resource) gr_link;
66184588Sdfr	uint64_t	gr_id;	/* indentifier exported to kernel */
67184588Sdfr	void*		gr_res;	/* GSS-API resource pointer */
68184588Sdfr};
69184588SdfrLIST_HEAD(gss_resource_list, gss_resource) gss_resources;
70184588Sdfrint gss_resource_count;
71184588Sdfruint32_t gss_next_id;
72184588Sdfruint32_t gss_start_time;
73184588Sdfrint debug_level;
74245089Srmacklemstatic char ccfile_dirlist[PATH_MAX + 1], ccfile_substring[NAME_MAX + 1];
75245089Srmacklemstatic char pref_realm[1024];
76252068Srmacklemstatic int verbose;
77252674Srmacklemstatic int use_old_des;
78252674Srmacklem#ifndef WITHOUT_KERBEROS
79252674Srmacklem/* 1.2.752.43.13.14 */
80252674Srmacklemstatic gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc =
81252674Srmacklem{6, (void *) "\x2a\x85\x70\x2b\x0d\x0e"};
82252674Srmacklemstatic gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X =
83252674Srmacklem    &gss_krb5_set_allowable_enctypes_x_desc;
84252674Srmacklemstatic gss_OID_desc gss_krb5_mech_oid_x_desc =
85252674Srmacklem{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" };
86252674Srmacklemstatic gss_OID GSS_KRB5_MECH_OID_X =
87252674Srmacklem    &gss_krb5_mech_oid_x_desc;
88252674Srmacklem#endif
89184588Sdfr
90184588Sdfrstatic void gssd_load_mech(void);
91245089Srmacklemstatic int find_ccache_file(const char *, uid_t, char *);
92245089Srmacklemstatic int is_a_valid_tgt_cache(const char *, uid_t, int *, time_t *);
93252068Srmacklemstatic void gssd_verbose_out(const char *, ...);
94252674Srmacklem#ifndef WITHOUT_KERBEROS
95252674Srmacklemstatic OM_uint32 gssd_get_user_cred(OM_uint32 *, uid_t, gss_cred_id_t *);
96252674Srmacklem#endif
97184588Sdfr
98184588Sdfrextern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp);
99184588Sdfrextern int gssd_syscall(char *path);
100184588Sdfr
101184588Sdfrint
102184588Sdfrmain(int argc, char **argv)
103184588Sdfr{
104184588Sdfr	/*
105184588Sdfr	 * We provide an RPC service on a local-domain socket. The
106184588Sdfr	 * kernel's GSS-API code will pass what it can't handle
107184588Sdfr	 * directly to us.
108184588Sdfr	 */
109184588Sdfr	struct sockaddr_un sun;
110184588Sdfr	int fd, oldmask, ch, debug;
111184588Sdfr	SVCXPRT *xprt;
112184588Sdfr
113245089Srmacklem	/*
114245089Srmacklem	 * Initialize the credential cache file name substring and the
115245089Srmacklem	 * search directory list.
116245089Srmacklem	 */
117245089Srmacklem	strlcpy(ccfile_substring, "krb5cc_", sizeof(ccfile_substring));
118245089Srmacklem	ccfile_dirlist[0] = '\0';
119245089Srmacklem	pref_realm[0] = '\0';
120184588Sdfr	debug = 0;
121252068Srmacklem	verbose = 0;
122252674Srmacklem	while ((ch = getopt(argc, argv, "dovs:c:r:")) != -1) {
123184588Sdfr		switch (ch) {
124184588Sdfr		case 'd':
125184588Sdfr			debug_level++;
126184588Sdfr			break;
127252674Srmacklem		case 'o':
128252674Srmacklem#ifndef WITHOUT_KERBEROS
129252674Srmacklem			/*
130252674Srmacklem			 * Force use of DES and the old type of GSSAPI token.
131252674Srmacklem			 */
132252674Srmacklem			use_old_des = 1;
133252674Srmacklem#else
134252674Srmacklem			errx(1, "This option not available when built"
135252674Srmacklem			    " without MK_KERBEROS\n");
136252674Srmacklem#endif
137252674Srmacklem			break;
138252068Srmacklem		case 'v':
139252068Srmacklem			verbose = 1;
140252068Srmacklem			break;
141245089Srmacklem		case 's':
142245089Srmacklem#ifndef WITHOUT_KERBEROS
143245089Srmacklem			/*
144245089Srmacklem			 * Set the directory search list. This enables use of
145245089Srmacklem			 * find_ccache_file() to search the directories for a
146245089Srmacklem			 * suitable credentials cache file.
147245089Srmacklem			 */
148245089Srmacklem			strlcpy(ccfile_dirlist, optarg, sizeof(ccfile_dirlist));
149245089Srmacklem#else
150245089Srmacklem			errx(1, "This option not available when built"
151245089Srmacklem			    " without MK_KERBEROS\n");
152245089Srmacklem#endif
153245089Srmacklem			break;
154245089Srmacklem		case 'c':
155245089Srmacklem			/*
156245089Srmacklem			 * Specify a non-default credential cache file
157245089Srmacklem			 * substring.
158245089Srmacklem			 */
159245089Srmacklem			strlcpy(ccfile_substring, optarg,
160245089Srmacklem			    sizeof(ccfile_substring));
161245089Srmacklem			break;
162245089Srmacklem		case 'r':
163245089Srmacklem			/*
164245089Srmacklem			 * Set the preferred realm for the credential cache tgt.
165245089Srmacklem			 */
166245089Srmacklem			strlcpy(pref_realm, optarg, sizeof(pref_realm));
167245089Srmacklem			break;
168184588Sdfr		default:
169245089Srmacklem			fprintf(stderr,
170245089Srmacklem			    "usage: %s [-d] [-s dir-list] [-c file-substring]"
171245089Srmacklem			    " [-r preferred-realm]\n", argv[0]);
172184588Sdfr			exit(1);
173184588Sdfr			break;
174184588Sdfr		}
175184588Sdfr	}
176184588Sdfr
177184588Sdfr	gssd_load_mech();
178184588Sdfr
179252674Srmacklem	if (!debug_level) {
180184588Sdfr		daemon(0, 0);
181252674Srmacklem		signal(SIGINT, SIG_IGN);
182252674Srmacklem		signal(SIGQUIT, SIG_IGN);
183252674Srmacklem		signal(SIGHUP, SIG_IGN);
184252674Srmacklem	}
185184588Sdfr
186184588Sdfr	memset(&sun, 0, sizeof sun);
187184588Sdfr	sun.sun_family = AF_LOCAL;
188184588Sdfr	unlink(_PATH_GSSDSOCK);
189184588Sdfr	strcpy(sun.sun_path, _PATH_GSSDSOCK);
190184588Sdfr	sun.sun_len = SUN_LEN(&sun);
191184588Sdfr	fd = socket(AF_LOCAL, SOCK_STREAM, 0);
192184588Sdfr	if (!fd) {
193245016Srmacklem		if (debug_level == 0) {
194245016Srmacklem			syslog(LOG_ERR, "Can't create local gssd socket");
195245016Srmacklem			exit(1);
196245016Srmacklem		}
197184588Sdfr		err(1, "Can't create local gssd socket");
198184588Sdfr	}
199184588Sdfr	oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO);
200184588Sdfr	if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) {
201245016Srmacklem		if (debug_level == 0) {
202245016Srmacklem			syslog(LOG_ERR, "Can't bind local gssd socket");
203245016Srmacklem			exit(1);
204245016Srmacklem		}
205184588Sdfr		err(1, "Can't bind local gssd socket");
206184588Sdfr	}
207184588Sdfr	umask(oldmask);
208184588Sdfr	if (listen(fd, SOMAXCONN) < 0) {
209245016Srmacklem		if (debug_level == 0) {
210245016Srmacklem			syslog(LOG_ERR, "Can't listen on local gssd socket");
211245016Srmacklem			exit(1);
212245016Srmacklem		}
213184588Sdfr		err(1, "Can't listen on local gssd socket");
214184588Sdfr	}
215184588Sdfr	xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE);
216184588Sdfr	if (!xprt) {
217245016Srmacklem		if (debug_level == 0) {
218245016Srmacklem			syslog(LOG_ERR,
219245016Srmacklem			    "Can't create transport for local gssd socket");
220245016Srmacklem			exit(1);
221245016Srmacklem		}
222184588Sdfr		err(1, "Can't create transport for local gssd socket");
223184588Sdfr	}
224184588Sdfr	if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) {
225245016Srmacklem		if (debug_level == 0) {
226245016Srmacklem			syslog(LOG_ERR,
227245016Srmacklem			    "Can't register service for local gssd socket");
228245016Srmacklem			exit(1);
229245016Srmacklem		}
230184588Sdfr		err(1, "Can't register service for local gssd socket");
231184588Sdfr	}
232184588Sdfr
233184588Sdfr	LIST_INIT(&gss_resources);
234184588Sdfr	gss_next_id = 1;
235184588Sdfr	gss_start_time = time(0);
236184588Sdfr
237184588Sdfr	gssd_syscall(_PATH_GSSDSOCK);
238184588Sdfr	svc_run();
239184588Sdfr
240184588Sdfr	return (0);
241184588Sdfr}
242184588Sdfr
243184588Sdfrstatic void
244184588Sdfrgssd_load_mech(void)
245184588Sdfr{
246184588Sdfr	FILE		*fp;
247184588Sdfr	char		buf[256];
248184588Sdfr	char		*p;
249184588Sdfr	char		*name, *oid, *lib, *kobj;
250184588Sdfr
251184588Sdfr	fp = fopen(_PATH_GSS_MECH, "r");
252184588Sdfr	if (!fp)
253184588Sdfr		return;
254184588Sdfr
255184588Sdfr	while (fgets(buf, sizeof(buf), fp)) {
256184588Sdfr		if (*buf == '#')
257184588Sdfr			continue;
258184588Sdfr		p = buf;
259184588Sdfr		name = strsep(&p, "\t\n ");
260184588Sdfr		if (p) while (isspace(*p)) p++;
261184588Sdfr		oid = strsep(&p, "\t\n ");
262184588Sdfr		if (p) while (isspace(*p)) p++;
263184588Sdfr		lib = strsep(&p, "\t\n ");
264184588Sdfr		if (p) while (isspace(*p)) p++;
265184588Sdfr		kobj = strsep(&p, "\t\n ");
266184588Sdfr		if (!name || !oid || !lib || !kobj)
267184588Sdfr			continue;
268184588Sdfr
269184588Sdfr		if (strcmp(kobj, "-")) {
270184588Sdfr			/*
271184588Sdfr			 * Attempt to load the kernel module if its
272184588Sdfr			 * not already present.
273184588Sdfr			 */
274184588Sdfr			if (modfind(kobj) < 0) {
275184588Sdfr				if (kldload(kobj) < 0) {
276184588Sdfr					fprintf(stderr,
277184588Sdfr			"%s: can't find or load kernel module %s for %s\n",
278184588Sdfr					    getprogname(), kobj, name);
279184588Sdfr				}
280184588Sdfr			}
281184588Sdfr		}
282184588Sdfr	}
283184588Sdfr	fclose(fp);
284184588Sdfr}
285184588Sdfr
286184588Sdfrstatic void *
287184588Sdfrgssd_find_resource(uint64_t id)
288184588Sdfr{
289184588Sdfr	struct gss_resource *gr;
290184588Sdfr
291184588Sdfr	if (!id)
292184588Sdfr		return (NULL);
293184588Sdfr
294184588Sdfr	LIST_FOREACH(gr, &gss_resources, gr_link)
295184588Sdfr		if (gr->gr_id == id)
296184588Sdfr			return (gr->gr_res);
297184588Sdfr
298184588Sdfr	return (NULL);
299184588Sdfr}
300184588Sdfr
301184588Sdfrstatic uint64_t
302184588Sdfrgssd_make_resource(void *res)
303184588Sdfr{
304184588Sdfr	struct gss_resource *gr;
305184588Sdfr
306184588Sdfr	if (!res)
307184588Sdfr		return (0);
308184588Sdfr
309184588Sdfr	gr = malloc(sizeof(struct gss_resource));
310184588Sdfr	if (!gr)
311184588Sdfr		return (0);
312184588Sdfr	gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32);
313184588Sdfr	gr->gr_res = res;
314184588Sdfr	LIST_INSERT_HEAD(&gss_resources, gr, gr_link);
315184588Sdfr	gss_resource_count++;
316184588Sdfr	if (debug_level > 1)
317184588Sdfr		printf("%d resources allocated\n", gss_resource_count);
318184588Sdfr
319184588Sdfr	return (gr->gr_id);
320184588Sdfr}
321184588Sdfr
322184588Sdfrstatic void
323184588Sdfrgssd_delete_resource(uint64_t id)
324184588Sdfr{
325184588Sdfr	struct gss_resource *gr;
326184588Sdfr
327184588Sdfr	LIST_FOREACH(gr, &gss_resources, gr_link) {
328184588Sdfr		if (gr->gr_id == id) {
329184588Sdfr			LIST_REMOVE(gr, gr_link);
330184588Sdfr			free(gr);
331184588Sdfr			gss_resource_count--;
332184588Sdfr			if (debug_level > 1)
333184588Sdfr				printf("%d resources allocated\n",
334184588Sdfr				    gss_resource_count);
335184588Sdfr			return;
336184588Sdfr		}
337184588Sdfr	}
338184588Sdfr}
339184588Sdfr
340252068Srmacklemstatic void
341252068Srmacklemgssd_verbose_out(const char *fmt, ...)
342252068Srmacklem{
343252068Srmacklem	va_list ap;
344252068Srmacklem
345252068Srmacklem	if (verbose != 0) {
346252068Srmacklem		va_start(ap, fmt);
347252068Srmacklem		if (debug_level == 0)
348252068Srmacklem			vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap);
349252068Srmacklem		else
350252068Srmacklem			vfprintf(stderr, fmt, ap);
351252068Srmacklem		va_end(ap);
352252068Srmacklem	}
353252068Srmacklem}
354252068Srmacklem
355184588Sdfrbool_t
356184588Sdfrgssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp)
357184588Sdfr{
358184588Sdfr
359252068Srmacklem	gssd_verbose_out("gssd_null: done\n");
360184588Sdfr	return (TRUE);
361184588Sdfr}
362184588Sdfr
363184588Sdfrbool_t
364184588Sdfrgssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp)
365184588Sdfr{
366184588Sdfr	gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
367184588Sdfr	gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
368184588Sdfr	gss_name_t name = GSS_C_NO_NAME;
369245089Srmacklem	char ccname[PATH_MAX + 5 + 1], *cp, *cp2;
370252674Srmacklem	int gotone, gotcred;
371252674Srmacklem	OM_uint32 min_stat;
372252674Srmacklem#ifndef WITHOUT_KERBEROS
373252674Srmacklem	gss_buffer_desc principal_desc;
374252674Srmacklem	char enctype[sizeof(uint32_t)];
375252674Srmacklem	int key_enctype;
376252674Srmacklem	OM_uint32 maj_stat;
377252674Srmacklem#endif
378184588Sdfr
379245089Srmacklem	memset(result, 0, sizeof(*result));
380245089Srmacklem	if (ccfile_dirlist[0] != '\0' && argp->cred == 0) {
381245089Srmacklem		/*
382245089Srmacklem		 * For the "-s" case and no credentials provided as an
383245089Srmacklem		 * argument, search the directory list for an appropriate
384245089Srmacklem		 * credential cache file. If the search fails, return failure.
385245089Srmacklem		 */
386245089Srmacklem		gotone = 0;
387245089Srmacklem		cp = ccfile_dirlist;
388245089Srmacklem		do {
389245089Srmacklem			cp2 = strchr(cp, ':');
390245089Srmacklem			if (cp2 != NULL)
391245089Srmacklem				*cp2 = '\0';
392245089Srmacklem			gotone = find_ccache_file(cp, argp->uid, ccname);
393245089Srmacklem			if (gotone != 0)
394245089Srmacklem				break;
395245089Srmacklem			if (cp2 != NULL)
396245089Srmacklem				*cp2++ = ':';
397245089Srmacklem			cp = cp2;
398245089Srmacklem		} while (cp != NULL && *cp != '\0');
399245089Srmacklem		if (gotone == 0) {
400245089Srmacklem			result->major_status = GSS_S_CREDENTIALS_EXPIRED;
401252068Srmacklem			gssd_verbose_out("gssd_init_sec_context: -s no"
402252068Srmacklem			    " credential cache file found for uid=%d\n",
403252068Srmacklem			    (int)argp->uid);
404245089Srmacklem			return (TRUE);
405245089Srmacklem		}
406245089Srmacklem	} else {
407245089Srmacklem		/*
408245089Srmacklem		 * If there wasn't a "-s" option or the credentials have
409245089Srmacklem		 * been provided as an argument, do it the old way.
410245089Srmacklem		 * When credentials are provided, the uid should be root.
411245089Srmacklem		 */
412245089Srmacklem		if (argp->cred != 0 && argp->uid != 0) {
413245089Srmacklem			if (debug_level == 0)
414245089Srmacklem				syslog(LOG_ERR, "gss_init_sec_context:"
415245089Srmacklem				    " cred for non-root");
416245089Srmacklem			else
417245089Srmacklem				fprintf(stderr, "gss_init_sec_context:"
418245089Srmacklem				    " cred for non-root\n");
419245089Srmacklem		}
420245089Srmacklem		snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
421245089Srmacklem		    (int) argp->uid);
422245089Srmacklem	}
423184588Sdfr	setenv("KRB5CCNAME", ccname, TRUE);
424184588Sdfr
425184588Sdfr	if (argp->cred) {
426184588Sdfr		cred = gssd_find_resource(argp->cred);
427184588Sdfr		if (!cred) {
428184588Sdfr			result->major_status = GSS_S_CREDENTIALS_EXPIRED;
429252068Srmacklem			gssd_verbose_out("gssd_init_sec_context: cred"
430252068Srmacklem			    " resource not found\n");
431184588Sdfr			return (TRUE);
432184588Sdfr		}
433184588Sdfr	}
434184588Sdfr	if (argp->ctx) {
435184588Sdfr		ctx = gssd_find_resource(argp->ctx);
436184588Sdfr		if (!ctx) {
437184588Sdfr			result->major_status = GSS_S_CONTEXT_EXPIRED;
438252068Srmacklem			gssd_verbose_out("gssd_init_sec_context: context"
439252068Srmacklem			    " resource not found\n");
440184588Sdfr			return (TRUE);
441184588Sdfr		}
442184588Sdfr	}
443184588Sdfr	if (argp->name) {
444184588Sdfr		name = gssd_find_resource(argp->name);
445184588Sdfr		if (!name) {
446184588Sdfr			result->major_status = GSS_S_BAD_NAME;
447252068Srmacklem			gssd_verbose_out("gssd_init_sec_context: name"
448252068Srmacklem			    " resource not found\n");
449184588Sdfr			return (TRUE);
450184588Sdfr		}
451184588Sdfr	}
452252674Srmacklem	gotcred = 0;
453184588Sdfr
454252674Srmacklem#ifndef WITHOUT_KERBEROS
455252674Srmacklem	if (use_old_des != 0) {
456252674Srmacklem		if (cred == GSS_C_NO_CREDENTIAL) {
457252674Srmacklem			/* Acquire a credential for the uid. */
458252674Srmacklem			maj_stat = gssd_get_user_cred(&min_stat, argp->uid,
459252674Srmacklem			    &cred);
460252674Srmacklem			if (maj_stat == GSS_S_COMPLETE)
461252674Srmacklem				gotcred = 1;
462252674Srmacklem			else
463252674Srmacklem				gssd_verbose_out("gssd_init_sec_context: "
464252674Srmacklem				    "get user cred failed uid=%d major=0x%x "
465252674Srmacklem				    "minor=%d\n", (int)argp->uid,
466252674Srmacklem				    (unsigned int)maj_stat, (int)min_stat);
467252674Srmacklem		}
468252674Srmacklem		if (cred != GSS_C_NO_CREDENTIAL) {
469252674Srmacklem			key_enctype = ETYPE_DES_CBC_CRC;
470252674Srmacklem			enctype[0] = (key_enctype >> 24) & 0xff;
471252674Srmacklem			enctype[1] = (key_enctype >> 16) & 0xff;
472252674Srmacklem			enctype[2] = (key_enctype >> 8) & 0xff;
473252674Srmacklem			enctype[3] = key_enctype & 0xff;
474252674Srmacklem			principal_desc.length = sizeof(enctype);
475252674Srmacklem			principal_desc.value = enctype;
476252674Srmacklem			result->major_status = gss_set_cred_option(
477252674Srmacklem			    &result->minor_status, &cred,
478252674Srmacklem			    GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X,
479252674Srmacklem			    &principal_desc);
480252674Srmacklem			gssd_verbose_out("gssd_init_sec_context: set allowable "
481252674Srmacklem			    "enctype major=0x%x minor=%d\n",
482252674Srmacklem			    (unsigned int)result->major_status,
483252674Srmacklem			    (int)result->minor_status);
484252674Srmacklem			if (result->major_status != GSS_S_COMPLETE) {
485252674Srmacklem				if (gotcred != 0)
486252674Srmacklem					gss_release_cred(&min_stat, &cred);
487252674Srmacklem				return (TRUE);
488252674Srmacklem			}
489252674Srmacklem		}
490252674Srmacklem	}
491252674Srmacklem#endif
492184588Sdfr	result->major_status = gss_init_sec_context(&result->minor_status,
493184588Sdfr	    cred, &ctx, name, argp->mech_type,
494184588Sdfr	    argp->req_flags, argp->time_req, argp->input_chan_bindings,
495184588Sdfr	    &argp->input_token, &result->actual_mech_type,
496184588Sdfr	    &result->output_token, &result->ret_flags, &result->time_rec);
497252068Srmacklem	gssd_verbose_out("gssd_init_sec_context: done major=0x%x minor=%d"
498252068Srmacklem	    " uid=%d\n", (unsigned int)result->major_status,
499252068Srmacklem	    (int)result->minor_status, (int)argp->uid);
500252674Srmacklem	if (gotcred != 0)
501252674Srmacklem		gss_release_cred(&min_stat, &cred);
502184588Sdfr
503184588Sdfr	if (result->major_status == GSS_S_COMPLETE
504184588Sdfr	    || result->major_status == GSS_S_CONTINUE_NEEDED) {
505184588Sdfr		if (argp->ctx)
506184588Sdfr			result->ctx = argp->ctx;
507184588Sdfr		else
508184588Sdfr			result->ctx = gssd_make_resource(ctx);
509184588Sdfr	}
510184588Sdfr
511184588Sdfr	return (TRUE);
512184588Sdfr}
513184588Sdfr
514184588Sdfrbool_t
515184588Sdfrgssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp)
516184588Sdfr{
517184588Sdfr	gss_ctx_id_t ctx = GSS_C_NO_CONTEXT;
518184588Sdfr	gss_cred_id_t cred = GSS_C_NO_CREDENTIAL;
519184588Sdfr	gss_name_t src_name;
520184588Sdfr	gss_cred_id_t delegated_cred_handle;
521184588Sdfr
522184588Sdfr	memset(result, 0, sizeof(*result));
523184588Sdfr	if (argp->ctx) {
524184588Sdfr		ctx = gssd_find_resource(argp->ctx);
525184588Sdfr		if (!ctx) {
526184588Sdfr			result->major_status = GSS_S_CONTEXT_EXPIRED;
527252068Srmacklem			gssd_verbose_out("gssd_accept_sec_context: ctx"
528252068Srmacklem			    " resource not found\n");
529184588Sdfr			return (TRUE);
530184588Sdfr		}
531184588Sdfr	}
532184588Sdfr	if (argp->cred) {
533184588Sdfr		cred = gssd_find_resource(argp->cred);
534184588Sdfr		if (!cred) {
535184588Sdfr			result->major_status = GSS_S_CREDENTIALS_EXPIRED;
536252068Srmacklem			gssd_verbose_out("gssd_accept_sec_context: cred"
537252068Srmacklem			    " resource not found\n");
538184588Sdfr			return (TRUE);
539184588Sdfr		}
540184588Sdfr	}
541184588Sdfr
542184588Sdfr	memset(result, 0, sizeof(*result));
543184588Sdfr	result->major_status = gss_accept_sec_context(&result->minor_status,
544184588Sdfr	    &ctx, cred, &argp->input_token, argp->input_chan_bindings,
545184588Sdfr	    &src_name, &result->mech_type, &result->output_token,
546184588Sdfr	    &result->ret_flags, &result->time_rec,
547184588Sdfr	    &delegated_cred_handle);
548252068Srmacklem	gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n",
549252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
550184588Sdfr
551184588Sdfr	if (result->major_status == GSS_S_COMPLETE
552184588Sdfr	    || result->major_status == GSS_S_CONTINUE_NEEDED) {
553184588Sdfr		if (argp->ctx)
554184588Sdfr			result->ctx = argp->ctx;
555184588Sdfr		else
556184588Sdfr			result->ctx = gssd_make_resource(ctx);
557184588Sdfr		result->src_name = gssd_make_resource(src_name);
558184588Sdfr		result->delegated_cred_handle =
559184588Sdfr			gssd_make_resource(delegated_cred_handle);
560184588Sdfr	}
561184588Sdfr
562184588Sdfr	return (TRUE);
563184588Sdfr}
564184588Sdfr
565184588Sdfrbool_t
566184588Sdfrgssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp)
567184588Sdfr{
568184588Sdfr	gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
569184588Sdfr
570184588Sdfr	if (ctx) {
571184588Sdfr		result->major_status = gss_delete_sec_context(
572184588Sdfr			&result->minor_status, &ctx, &result->output_token);
573184588Sdfr		gssd_delete_resource(argp->ctx);
574184588Sdfr	} else {
575184588Sdfr		result->major_status = GSS_S_COMPLETE;
576184588Sdfr		result->minor_status = 0;
577184588Sdfr	}
578252068Srmacklem	gssd_verbose_out("gssd_delete_sec_context: done major=0x%x minor=%d\n",
579252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
580184588Sdfr
581184588Sdfr	return (TRUE);
582184588Sdfr}
583184588Sdfr
584184588Sdfrbool_t
585184588Sdfrgssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp)
586184588Sdfr{
587184588Sdfr	gss_ctx_id_t ctx = gssd_find_resource(argp->ctx);
588184588Sdfr
589184588Sdfr	if (ctx) {
590184588Sdfr		result->major_status = gss_export_sec_context(
591184588Sdfr			&result->minor_status, &ctx,
592184588Sdfr			&result->interprocess_token);
593184588Sdfr		result->format = KGSS_HEIMDAL_1_1;
594184588Sdfr		gssd_delete_resource(argp->ctx);
595184588Sdfr	} else {
596184588Sdfr		result->major_status = GSS_S_FAILURE;
597184588Sdfr		result->minor_status = 0;
598184588Sdfr		result->interprocess_token.length = 0;
599184588Sdfr		result->interprocess_token.value = NULL;
600184588Sdfr	}
601252068Srmacklem	gssd_verbose_out("gssd_export_sec_context: done major=0x%x minor=%d\n",
602252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
603184588Sdfr
604184588Sdfr	return (TRUE);
605184588Sdfr}
606184588Sdfr
607184588Sdfrbool_t
608184588Sdfrgssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp)
609184588Sdfr{
610184588Sdfr	gss_name_t name;
611184588Sdfr
612184588Sdfr	result->major_status = gss_import_name(&result->minor_status,
613184588Sdfr	    &argp->input_name_buffer, argp->input_name_type, &name);
614252068Srmacklem	gssd_verbose_out("gssd_import_name: done major=0x%x minor=%d\n",
615252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
616184588Sdfr
617184588Sdfr	if (result->major_status == GSS_S_COMPLETE)
618184588Sdfr		result->output_name = gssd_make_resource(name);
619184588Sdfr	else
620184588Sdfr		result->output_name = 0;
621184588Sdfr
622184588Sdfr	return (TRUE);
623184588Sdfr}
624184588Sdfr
625184588Sdfrbool_t
626184588Sdfrgssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp)
627184588Sdfr{
628184588Sdfr	gss_name_t name = gssd_find_resource(argp->input_name);
629184588Sdfr	gss_name_t output_name;
630184588Sdfr
631184588Sdfr	memset(result, 0, sizeof(*result));
632184588Sdfr	if (!name) {
633184588Sdfr		result->major_status = GSS_S_BAD_NAME;
634184588Sdfr		return (TRUE);
635184588Sdfr	}
636184588Sdfr
637184588Sdfr	result->major_status = gss_canonicalize_name(&result->minor_status,
638184588Sdfr	    name, argp->mech_type, &output_name);
639252068Srmacklem	gssd_verbose_out("gssd_canonicalize_name: done major=0x%x minor=%d\n",
640252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
641184588Sdfr
642184588Sdfr	if (result->major_status == GSS_S_COMPLETE)
643184588Sdfr		result->output_name = gssd_make_resource(output_name);
644184588Sdfr	else
645184588Sdfr		result->output_name = 0;
646184588Sdfr
647184588Sdfr	return (TRUE);
648184588Sdfr}
649184588Sdfr
650184588Sdfrbool_t
651184588Sdfrgssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp)
652184588Sdfr{
653184588Sdfr	gss_name_t name = gssd_find_resource(argp->input_name);
654184588Sdfr
655184588Sdfr	memset(result, 0, sizeof(*result));
656184588Sdfr	if (!name) {
657184588Sdfr		result->major_status = GSS_S_BAD_NAME;
658252068Srmacklem		gssd_verbose_out("gssd_export_name: name resource not found\n");
659184588Sdfr		return (TRUE);
660184588Sdfr	}
661184588Sdfr
662184588Sdfr	result->major_status = gss_export_name(&result->minor_status,
663184588Sdfr	    name, &result->exported_name);
664252068Srmacklem	gssd_verbose_out("gssd_export_name: done major=0x%x minor=%d\n",
665252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
666184588Sdfr
667184588Sdfr	return (TRUE);
668184588Sdfr}
669184588Sdfr
670184588Sdfrbool_t
671184588Sdfrgssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp)
672184588Sdfr{
673184588Sdfr	gss_name_t name = gssd_find_resource(argp->input_name);
674184588Sdfr
675184588Sdfr	if (name) {
676184588Sdfr		result->major_status = gss_release_name(&result->minor_status,
677184588Sdfr		    &name);
678184588Sdfr		gssd_delete_resource(argp->input_name);
679184588Sdfr	} else {
680184588Sdfr		result->major_status = GSS_S_COMPLETE;
681184588Sdfr		result->minor_status = 0;
682184588Sdfr	}
683252068Srmacklem	gssd_verbose_out("gssd_release_name: done major=0x%x minor=%d\n",
684252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
685184588Sdfr
686184588Sdfr	return (TRUE);
687184588Sdfr}
688184588Sdfr
689184588Sdfrbool_t
690184588Sdfrgssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp)
691184588Sdfr{
692184588Sdfr	gss_name_t name = gssd_find_resource(argp->pname);
693184588Sdfr	uid_t uid;
694250689Srmacklem	char buf[1024], *bufp;
695184588Sdfr	struct passwd pwd, *pw;
696250689Srmacklem	size_t buflen;
697250689Srmacklem	int error;
698250689Srmacklem	static size_t buflen_hint = 1024;
699184588Sdfr
700184588Sdfr	memset(result, 0, sizeof(*result));
701184588Sdfr	if (name) {
702184588Sdfr		result->major_status =
703184588Sdfr			gss_pname_to_uid(&result->minor_status,
704184588Sdfr			    name, argp->mech, &uid);
705184588Sdfr		if (result->major_status == GSS_S_COMPLETE) {
706184588Sdfr			result->uid = uid;
707250689Srmacklem			buflen = buflen_hint;
708250689Srmacklem			for (;;) {
709250689Srmacklem				pw = NULL;
710250689Srmacklem				bufp = buf;
711250689Srmacklem				if (buflen > sizeof(buf))
712250689Srmacklem					bufp = malloc(buflen);
713250689Srmacklem				if (bufp == NULL)
714250689Srmacklem					break;
715250689Srmacklem				error = getpwuid_r(uid, &pwd, bufp, buflen,
716250689Srmacklem				    &pw);
717250689Srmacklem				if (error != ERANGE)
718250689Srmacklem					break;
719250689Srmacklem				if (buflen > sizeof(buf))
720250689Srmacklem					free(bufp);
721250689Srmacklem				buflen += 1024;
722250689Srmacklem				if (buflen > buflen_hint)
723250689Srmacklem					buflen_hint = buflen;
724250689Srmacklem			}
725184588Sdfr			if (pw) {
726184588Sdfr				int len = NGRPS;
727184588Sdfr				int groups[NGRPS];
728184588Sdfr				result->gid = pw->pw_gid;
729184588Sdfr				getgrouplist(pw->pw_name, pw->pw_gid,
730184588Sdfr				    groups, &len);
731184588Sdfr				result->gidlist.gidlist_len = len;
732184588Sdfr				result->gidlist.gidlist_val =
733184588Sdfr					mem_alloc(len * sizeof(int));
734184588Sdfr				memcpy(result->gidlist.gidlist_val, groups,
735184588Sdfr				    len * sizeof(int));
736252068Srmacklem				gssd_verbose_out("gssd_pname_to_uid: mapped"
737252068Srmacklem				    " to uid=%d, gid=%d\n", (int)result->uid,
738252068Srmacklem				    (int)result->gid);
739184588Sdfr			} else {
740184588Sdfr				result->gid = 65534;
741184588Sdfr				result->gidlist.gidlist_len = 0;
742184588Sdfr				result->gidlist.gidlist_val = NULL;
743252068Srmacklem				gssd_verbose_out("gssd_pname_to_uid: mapped"
744252068Srmacklem				    " to uid=%d, but no groups\n",
745252068Srmacklem				    (int)result->uid);
746184588Sdfr			}
747250689Srmacklem			if (bufp != NULL && buflen > sizeof(buf))
748250689Srmacklem				free(bufp);
749252068Srmacklem		} else
750252068Srmacklem			gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x"
751252068Srmacklem			    " minor=%d\n", (unsigned int)result->major_status,
752252068Srmacklem			    (int)result->minor_status);
753184588Sdfr	} else {
754184588Sdfr		result->major_status = GSS_S_BAD_NAME;
755184588Sdfr		result->minor_status = 0;
756252068Srmacklem		gssd_verbose_out("gssd_pname_to_uid: no name\n");
757184588Sdfr	}
758184588Sdfr
759184588Sdfr	return (TRUE);
760184588Sdfr}
761184588Sdfr
762184588Sdfrbool_t
763184588Sdfrgssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp)
764184588Sdfr{
765184588Sdfr	gss_name_t desired_name = GSS_C_NO_NAME;
766184588Sdfr	gss_cred_id_t cred;
767245089Srmacklem	char ccname[PATH_MAX + 5 + 1], *cp, *cp2;
768245089Srmacklem	int gotone;
769252674Srmacklem#ifndef WITHOUT_KERBEROS
770252674Srmacklem	gss_buffer_desc namebuf;
771252674Srmacklem	uint32_t minstat;
772252674Srmacklem	krb5_error_code kret;
773252674Srmacklem#endif
774184588Sdfr
775245089Srmacklem	memset(result, 0, sizeof(*result));
776245089Srmacklem	if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) {
777245089Srmacklem		/*
778245089Srmacklem		 * For the "-s" case and no name provided as an
779245089Srmacklem		 * argument, search the directory list for an appropriate
780245089Srmacklem		 * credential cache file. If the search fails, return failure.
781245089Srmacklem		 */
782245089Srmacklem		gotone = 0;
783245089Srmacklem		cp = ccfile_dirlist;
784245089Srmacklem		do {
785245089Srmacklem			cp2 = strchr(cp, ':');
786245089Srmacklem			if (cp2 != NULL)
787245089Srmacklem				*cp2 = '\0';
788245089Srmacklem			gotone = find_ccache_file(cp, argp->uid, ccname);
789245089Srmacklem			if (gotone != 0)
790245089Srmacklem				break;
791245089Srmacklem			if (cp2 != NULL)
792245089Srmacklem				*cp2++ = ':';
793245089Srmacklem			cp = cp2;
794245089Srmacklem		} while (cp != NULL && *cp != '\0');
795245089Srmacklem		if (gotone == 0) {
796245089Srmacklem			result->major_status = GSS_S_CREDENTIALS_EXPIRED;
797252068Srmacklem			gssd_verbose_out("gssd_acquire_cred: no cred cache"
798252068Srmacklem			    " file found\n");
799245089Srmacklem			return (TRUE);
800245089Srmacklem		}
801245089Srmacklem	} else {
802245089Srmacklem		/*
803245089Srmacklem		 * If there wasn't a "-s" option or the name has
804245089Srmacklem		 * been provided as an argument, do it the old way.
805245089Srmacklem		 * When a name is provided, it will normally exist in the
806245089Srmacklem		 * default keytab file and the uid will be root.
807245089Srmacklem		 */
808245089Srmacklem		if (argp->desired_name != 0 && argp->uid != 0) {
809245089Srmacklem			if (debug_level == 0)
810245089Srmacklem				syslog(LOG_ERR, "gss_acquire_cred:"
811245089Srmacklem				    " principal_name for non-root");
812245089Srmacklem			else
813245089Srmacklem				fprintf(stderr, "gss_acquire_cred:"
814245089Srmacklem				    " principal_name for non-root\n");
815245089Srmacklem		}
816245089Srmacklem		snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d",
817245089Srmacklem		    (int) argp->uid);
818245089Srmacklem	}
819184588Sdfr	setenv("KRB5CCNAME", ccname, TRUE);
820184588Sdfr
821184588Sdfr	if (argp->desired_name) {
822184588Sdfr		desired_name = gssd_find_resource(argp->desired_name);
823184588Sdfr		if (!desired_name) {
824184588Sdfr			result->major_status = GSS_S_BAD_NAME;
825252068Srmacklem			gssd_verbose_out("gssd_acquire_cred: no desired name"
826252068Srmacklem			    " found\n");
827184588Sdfr			return (TRUE);
828184588Sdfr		}
829184588Sdfr	}
830184588Sdfr
831184588Sdfr	result->major_status = gss_acquire_cred(&result->minor_status,
832184588Sdfr	    desired_name, argp->time_req, argp->desired_mechs,
833184588Sdfr	    argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec);
834252068Srmacklem	gssd_verbose_out("gssd_acquire_cred: done major=0x%x minor=%d\n",
835252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
836184588Sdfr
837184588Sdfr	if (result->major_status == GSS_S_COMPLETE)
838184588Sdfr		result->output_cred = gssd_make_resource(cred);
839184588Sdfr	else
840184588Sdfr		result->output_cred = 0;
841184588Sdfr
842184588Sdfr	return (TRUE);
843184588Sdfr}
844184588Sdfr
845184588Sdfrbool_t
846184588Sdfrgssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp)
847184588Sdfr{
848184588Sdfr	gss_cred_id_t cred = gssd_find_resource(argp->cred);
849184588Sdfr
850184588Sdfr	memset(result, 0, sizeof(*result));
851184588Sdfr	if (!cred) {
852184588Sdfr		result->major_status = GSS_S_CREDENTIALS_EXPIRED;
853252068Srmacklem		gssd_verbose_out("gssd_set_cred: no credentials\n");
854184588Sdfr		return (TRUE);
855184588Sdfr	}
856184588Sdfr
857184588Sdfr	result->major_status = gss_set_cred_option(&result->minor_status,
858184588Sdfr	    &cred, argp->option_name, &argp->option_value);
859252068Srmacklem	gssd_verbose_out("gssd_set_cred: done major=0x%x minor=%d\n",
860252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
861184588Sdfr
862184588Sdfr	return (TRUE);
863184588Sdfr}
864184588Sdfr
865184588Sdfrbool_t
866184588Sdfrgssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp)
867184588Sdfr{
868184588Sdfr	gss_cred_id_t cred = gssd_find_resource(argp->cred);
869184588Sdfr
870184588Sdfr	if (cred) {
871184588Sdfr		result->major_status = gss_release_cred(&result->minor_status,
872184588Sdfr		    &cred);
873184588Sdfr		gssd_delete_resource(argp->cred);
874184588Sdfr	} else {
875184588Sdfr		result->major_status = GSS_S_COMPLETE;
876184588Sdfr		result->minor_status = 0;
877184588Sdfr	}
878252068Srmacklem	gssd_verbose_out("gssd_release_cred: done major=0x%x minor=%d\n",
879252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
880184588Sdfr
881184588Sdfr	return (TRUE);
882184588Sdfr}
883184588Sdfr
884184588Sdfrbool_t
885184588Sdfrgssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp)
886184588Sdfr{
887184588Sdfr
888184588Sdfr	result->message_context = argp->message_context;
889184588Sdfr	result->major_status = gss_display_status(&result->minor_status,
890184588Sdfr	    argp->status_value, argp->status_type, argp->mech_type,
891184588Sdfr	    &result->message_context, &result->status_string);
892252068Srmacklem	gssd_verbose_out("gssd_display_status: done major=0x%x minor=%d\n",
893252068Srmacklem	    (unsigned int)result->major_status, (int)result->minor_status);
894184588Sdfr
895184588Sdfr	return (TRUE);
896184588Sdfr}
897184588Sdfr
898184588Sdfrint
899184588Sdfrgssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result)
900184588Sdfr{
901184588Sdfr	/*
902184588Sdfr	 * We don't use XDR to free the results - anything which was
903184588Sdfr	 * allocated came from GSS-API. We use xdr_result to figure
904184588Sdfr	 * out what to do.
905184588Sdfr	 */
906184588Sdfr	OM_uint32 junk;
907184588Sdfr
908184588Sdfr	if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) {
909184588Sdfr		init_sec_context_res *p = (init_sec_context_res *) result;
910184588Sdfr		gss_release_buffer(&junk, &p->output_token);
911184588Sdfr	} else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) {
912184588Sdfr		accept_sec_context_res *p = (accept_sec_context_res *) result;
913184588Sdfr		gss_release_buffer(&junk, &p->output_token);
914184588Sdfr	} else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) {
915184588Sdfr		delete_sec_context_res *p = (delete_sec_context_res *) result;
916184588Sdfr		gss_release_buffer(&junk, &p->output_token);
917184588Sdfr	} else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) {
918184588Sdfr		export_sec_context_res *p = (export_sec_context_res *) result;
919184588Sdfr		if (p->interprocess_token.length)
920184588Sdfr			memset(p->interprocess_token.value, 0,
921184588Sdfr			    p->interprocess_token.length);
922184588Sdfr		gss_release_buffer(&junk, &p->interprocess_token);
923184588Sdfr	} else if (xdr_result == (xdrproc_t) xdr_export_name_res) {
924184588Sdfr		export_name_res *p = (export_name_res *) result;
925184588Sdfr		gss_release_buffer(&junk, &p->exported_name);
926184588Sdfr	} else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) {
927184588Sdfr		acquire_cred_res *p = (acquire_cred_res *) result;
928184588Sdfr		gss_release_oid_set(&junk, &p->actual_mechs);
929184588Sdfr	} else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) {
930184588Sdfr		pname_to_uid_res *p = (pname_to_uid_res *) result;
931184588Sdfr		if (p->gidlist.gidlist_val)
932184588Sdfr			free(p->gidlist.gidlist_val);
933184588Sdfr	} else if (xdr_result == (xdrproc_t) xdr_display_status_res) {
934184588Sdfr		display_status_res *p = (display_status_res *) result;
935184588Sdfr		gss_release_buffer(&junk, &p->status_string);
936184588Sdfr	}
937184588Sdfr
938184588Sdfr	return (TRUE);
939184588Sdfr}
940245089Srmacklem
941245089Srmacklem/*
942245089Srmacklem * Search a directory for the most likely candidate to be used as the
943245089Srmacklem * credential cache for a uid. If successful, return 1 and fill the
944245089Srmacklem * file's path id into "rpath". Otherwise, return 0.
945245089Srmacklem */
946245089Srmacklemstatic int
947245089Srmacklemfind_ccache_file(const char *dirpath, uid_t uid, char *rpath)
948245089Srmacklem{
949245089Srmacklem	DIR *dirp;
950245089Srmacklem	struct dirent *dp;
951245089Srmacklem	struct stat sb;
952245089Srmacklem	time_t exptime, oexptime;
953245089Srmacklem	int gotone, len, rating, orating;
954245089Srmacklem	char namepath[PATH_MAX + 5 + 1];
955245089Srmacklem	char retpath[PATH_MAX + 5 + 1];
956245089Srmacklem
957245089Srmacklem	dirp = opendir(dirpath);
958245089Srmacklem	if (dirp == NULL)
959245089Srmacklem		return (0);
960245089Srmacklem	gotone = 0;
961245089Srmacklem	orating = 0;
962245089Srmacklem	oexptime = 0;
963245089Srmacklem	while ((dp = readdir(dirp)) != NULL) {
964245089Srmacklem		len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath,
965245089Srmacklem		    dp->d_name);
966245089Srmacklem		if (len < sizeof(namepath) &&
967245089Srmacklem		    strstr(dp->d_name, ccfile_substring) != NULL &&
968245089Srmacklem		    lstat(namepath, &sb) >= 0 &&
969245089Srmacklem		    sb.st_uid == uid &&
970245089Srmacklem		    S_ISREG(sb.st_mode)) {
971245089Srmacklem			len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s",
972245089Srmacklem			    dirpath, dp->d_name);
973245089Srmacklem			if (len < sizeof(namepath) &&
974245089Srmacklem			    is_a_valid_tgt_cache(namepath, uid, &rating,
975245089Srmacklem			    &exptime) != 0) {
976245089Srmacklem				if (gotone == 0 || rating > orating ||
977245089Srmacklem				    (rating == orating && exptime > oexptime)) {
978245089Srmacklem					orating = rating;
979245089Srmacklem					oexptime = exptime;
980245089Srmacklem					strcpy(retpath, namepath);
981245089Srmacklem					gotone = 1;
982245089Srmacklem				}
983245089Srmacklem			}
984245089Srmacklem		}
985245089Srmacklem	}
986245089Srmacklem	closedir(dirp);
987245089Srmacklem	if (gotone != 0) {
988245089Srmacklem		strcpy(rpath, retpath);
989245089Srmacklem		return (1);
990245089Srmacklem	}
991245089Srmacklem	return (0);
992245089Srmacklem}
993245089Srmacklem
994245089Srmacklem/*
995245089Srmacklem * Try to determine if the file is a valid tgt cache file.
996245089Srmacklem * Check that the file has a valid tgt for a principal.
997245089Srmacklem * If it does, return 1, otherwise return 0.
998245089Srmacklem * It also returns a "rating" and the expiry time for the TGT, when found.
999245089Srmacklem * This "rating" is higher based on heuristics that make it more
1000245089Srmacklem * likely to be the correct credential cache file to use. It can
1001245089Srmacklem * be used by the caller, along with expiry time, to select from
1002245089Srmacklem * multiple credential cache files.
1003245089Srmacklem */
1004245089Srmacklemstatic int
1005245089Srmacklemis_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating,
1006245089Srmacklem    time_t *retexptime)
1007245089Srmacklem{
1008245089Srmacklem#ifndef WITHOUT_KERBEROS
1009245089Srmacklem	krb5_context context;
1010245089Srmacklem	krb5_principal princ;
1011245089Srmacklem	krb5_ccache ccache;
1012245089Srmacklem	krb5_error_code retval;
1013245089Srmacklem	krb5_cc_cursor curse;
1014245089Srmacklem	krb5_creds krbcred;
1015245089Srmacklem	int gotone, orating, rating, ret;
1016245089Srmacklem	struct passwd *pw;
1017245089Srmacklem	char *cp, *cp2, *pname;
1018245089Srmacklem	time_t exptime;
1019245089Srmacklem
1020245089Srmacklem	/* Find a likely name for the uid principal. */
1021245089Srmacklem	pw = getpwuid(uid);
1022245089Srmacklem
1023245089Srmacklem	/*
1024245089Srmacklem	 * Do a bunch of krb5 library stuff to try and determine if
1025245089Srmacklem	 * this file is a credentials cache with an appropriate TGT
1026245089Srmacklem	 * in it.
1027245089Srmacklem	 */
1028245089Srmacklem	retval = krb5_init_context(&context);
1029245089Srmacklem	if (retval != 0)
1030245089Srmacklem		return (0);
1031245089Srmacklem	retval = krb5_cc_resolve(context, filepath, &ccache);
1032245089Srmacklem	if (retval != 0) {
1033245089Srmacklem		krb5_free_context(context);
1034245089Srmacklem		return (0);
1035245089Srmacklem	}
1036245089Srmacklem	ret = 0;
1037245089Srmacklem	orating = 0;
1038245089Srmacklem	exptime = 0;
1039245089Srmacklem	retval = krb5_cc_start_seq_get(context, ccache, &curse);
1040245089Srmacklem	if (retval == 0) {
1041245089Srmacklem		while ((retval = krb5_cc_next_cred(context, ccache, &curse,
1042245089Srmacklem		    &krbcred)) == 0) {
1043245089Srmacklem			gotone = 0;
1044245089Srmacklem			rating = 0;
1045245089Srmacklem			retval = krb5_unparse_name(context, krbcred.server,
1046245089Srmacklem			    &pname);
1047245089Srmacklem			if (retval == 0) {
1048245089Srmacklem				cp = strchr(pname, '/');
1049245089Srmacklem				if (cp != NULL) {
1050245089Srmacklem					*cp++ = '\0';
1051245089Srmacklem					if (strcmp(pname, "krbtgt") == 0 &&
1052245089Srmacklem					    krbcred.times.endtime > time(NULL)
1053245089Srmacklem					    ) {
1054245089Srmacklem						gotone = 1;
1055245089Srmacklem						/*
1056245089Srmacklem						 * Test to see if this is a
1057245089Srmacklem						 * tgt for cross-realm auth.
1058245089Srmacklem						 * Rate it higher, if it is not.
1059245089Srmacklem						 */
1060245089Srmacklem						cp2 = strchr(cp, '@');
1061245089Srmacklem						if (cp2 != NULL) {
1062245089Srmacklem							*cp2++ = '\0';
1063245089Srmacklem							if (strcmp(cp, cp2) ==
1064245089Srmacklem							    0)
1065245089Srmacklem								rating++;
1066245089Srmacklem						}
1067245089Srmacklem					}
1068245089Srmacklem				}
1069245089Srmacklem				free(pname);
1070245089Srmacklem			}
1071245089Srmacklem			if (gotone != 0) {
1072245089Srmacklem				retval = krb5_unparse_name(context,
1073245089Srmacklem				    krbcred.client, &pname);
1074245089Srmacklem				if (retval == 0) {
1075245089Srmacklem					cp = strchr(pname, '@');
1076245089Srmacklem					if (cp != NULL) {
1077245089Srmacklem						*cp++ = '\0';
1078245089Srmacklem						if (pw != NULL && strcmp(pname,
1079245089Srmacklem						    pw->pw_name) == 0)
1080245089Srmacklem							rating++;
1081245089Srmacklem						if (strchr(pname, '/') == NULL)
1082245089Srmacklem							rating++;
1083245089Srmacklem						if (pref_realm[0] != '\0' &&
1084245089Srmacklem						    strcmp(cp, pref_realm) == 0)
1085245089Srmacklem							rating++;
1086245089Srmacklem					}
1087245089Srmacklem				}
1088245089Srmacklem				free(pname);
1089245089Srmacklem				if (rating > orating) {
1090245089Srmacklem					orating = rating;
1091245089Srmacklem					exptime = krbcred.times.endtime;
1092245089Srmacklem				} else if (rating == orating &&
1093245089Srmacklem				    krbcred.times.endtime > exptime)
1094245089Srmacklem					exptime = krbcred.times.endtime;
1095245089Srmacklem				ret = 1;
1096245089Srmacklem			}
1097245089Srmacklem			krb5_free_cred_contents(context, &krbcred);
1098245089Srmacklem		}
1099245089Srmacklem		krb5_cc_end_seq_get(context, ccache, &curse);
1100245089Srmacklem	}
1101245089Srmacklem	krb5_cc_close(context, ccache);
1102245089Srmacklem	krb5_free_context(context);
1103245089Srmacklem	if (ret != 0) {
1104245089Srmacklem		*retrating = orating;
1105245089Srmacklem		*retexptime = exptime;
1106245089Srmacklem	}
1107245089Srmacklem	return (ret);
1108245089Srmacklem#else /* WITHOUT_KERBEROS */
1109245089Srmacklem	return (0);
1110245089Srmacklem#endif /* !WITHOUT_KERBEROS */
1111245089Srmacklem}
1112245089Srmacklem
1113252674Srmacklem#ifndef WITHOUT_KERBEROS
1114252674Srmacklem/*
1115252674Srmacklem * Acquire a gss credential for a uid.
1116252674Srmacklem */
1117252674Srmacklemstatic OM_uint32
1118252674Srmacklemgssd_get_user_cred(OM_uint32 *min_statp, uid_t uid, gss_cred_id_t *credp)
1119252674Srmacklem{
1120252674Srmacklem	gss_buffer_desc principal_desc;
1121252674Srmacklem	gss_name_t name;
1122252674Srmacklem	OM_uint32 maj_stat, min_stat;
1123252674Srmacklem	gss_OID_set mechlist;
1124252674Srmacklem	struct passwd *pw;
1125252674Srmacklem
1126252674Srmacklem	pw = getpwuid(uid);
1127252674Srmacklem	if (pw == NULL) {
1128252674Srmacklem		*min_statp = 0;
1129252674Srmacklem		return (GSS_S_FAILURE);
1130252674Srmacklem	}
1131252674Srmacklem
1132252674Srmacklem	/*
1133252674Srmacklem	 * The mechanism must be set to KerberosV for acquisition
1134252674Srmacklem	 * of credentials to work reliably.
1135252674Srmacklem	 */
1136252674Srmacklem	maj_stat = gss_create_empty_oid_set(min_statp, &mechlist);
1137252674Srmacklem	if (maj_stat != GSS_S_COMPLETE)
1138252674Srmacklem		return (maj_stat);
1139252674Srmacklem	maj_stat = gss_add_oid_set_member(min_statp, GSS_KRB5_MECH_OID_X,
1140252674Srmacklem	    &mechlist);
1141252674Srmacklem	if (maj_stat != GSS_S_COMPLETE) {
1142252674Srmacklem		gss_release_oid_set(&min_stat, &mechlist);
1143252674Srmacklem		return (maj_stat);
1144252674Srmacklem	}
1145252674Srmacklem
1146252674Srmacklem	principal_desc.value = (void *)pw->pw_name;
1147252674Srmacklem	principal_desc.length = strlen(pw->pw_name);
1148252674Srmacklem	maj_stat = gss_import_name(min_statp, &principal_desc,
1149252674Srmacklem	    GSS_C_NT_USER_NAME, &name);
1150252674Srmacklem	if (maj_stat != GSS_S_COMPLETE) {
1151252674Srmacklem		gss_release_oid_set(&min_stat, &mechlist);
1152252674Srmacklem		return (maj_stat);
1153252674Srmacklem	}
1154252674Srmacklem	/* Acquire the credentials. */
1155252674Srmacklem	maj_stat = gss_acquire_cred(min_statp, name, 0, mechlist,
1156252674Srmacklem	    GSS_C_INITIATE, credp, NULL, NULL);
1157252674Srmacklem	gss_release_name(&min_stat, &name);
1158252674Srmacklem	gss_release_oid_set(&min_stat, &mechlist);
1159252674Srmacklem	return (maj_stat);
1160252674Srmacklem}
1161252674Srmacklem#endif /* !WITHOUT_KERBEROS */
1162