gssd.c revision 252068
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: stable/9/usr.sbin/gssd/gssd.c 252068 2013-06-21 20:16:41Z rmacklem $"); 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> 45252068Srmacklem#include <stdarg.h> 46184588Sdfr#include <stdio.h> 47184588Sdfr#include <stdlib.h> 48184588Sdfr#include <string.h> 49184588Sdfr#include <unistd.h> 50184588Sdfr#include <gssapi/gssapi.h> 51184588Sdfr#include <rpc/rpc.h> 52184588Sdfr#include <rpc/rpc_com.h> 53184588Sdfr 54184588Sdfr#include "gssd.h" 55184588Sdfr 56184588Sdfr#ifndef _PATH_GSS_MECH 57184588Sdfr#define _PATH_GSS_MECH "/etc/gss/mech" 58184588Sdfr#endif 59184588Sdfr#ifndef _PATH_GSSDSOCK 60184588Sdfr#define _PATH_GSSDSOCK "/var/run/gssd.sock" 61184588Sdfr#endif 62184588Sdfr 63184588Sdfrstruct gss_resource { 64184588Sdfr LIST_ENTRY(gss_resource) gr_link; 65184588Sdfr uint64_t gr_id; /* indentifier exported to kernel */ 66184588Sdfr void* gr_res; /* GSS-API resource pointer */ 67184588Sdfr}; 68184588SdfrLIST_HEAD(gss_resource_list, gss_resource) gss_resources; 69184588Sdfrint gss_resource_count; 70184588Sdfruint32_t gss_next_id; 71184588Sdfruint32_t gss_start_time; 72184588Sdfrint debug_level; 73245089Srmacklemstatic char ccfile_dirlist[PATH_MAX + 1], ccfile_substring[NAME_MAX + 1]; 74245089Srmacklemstatic char pref_realm[1024]; 75252068Srmacklemstatic int verbose; 76184588Sdfr 77184588Sdfrstatic void gssd_load_mech(void); 78245089Srmacklemstatic int find_ccache_file(const char *, uid_t, char *); 79245089Srmacklemstatic int is_a_valid_tgt_cache(const char *, uid_t, int *, time_t *); 80252068Srmacklemstatic void gssd_verbose_out(const char *, ...); 81184588Sdfr 82184588Sdfrextern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp); 83184588Sdfrextern int gssd_syscall(char *path); 84184588Sdfr 85184588Sdfrint 86184588Sdfrmain(int argc, char **argv) 87184588Sdfr{ 88184588Sdfr /* 89184588Sdfr * We provide an RPC service on a local-domain socket. The 90184588Sdfr * kernel's GSS-API code will pass what it can't handle 91184588Sdfr * directly to us. 92184588Sdfr */ 93184588Sdfr struct sockaddr_un sun; 94184588Sdfr int fd, oldmask, ch, debug; 95184588Sdfr SVCXPRT *xprt; 96184588Sdfr 97245089Srmacklem /* 98245089Srmacklem * Initialize the credential cache file name substring and the 99245089Srmacklem * search directory list. 100245089Srmacklem */ 101245089Srmacklem strlcpy(ccfile_substring, "krb5cc_", sizeof(ccfile_substring)); 102245089Srmacklem ccfile_dirlist[0] = '\0'; 103245089Srmacklem pref_realm[0] = '\0'; 104184588Sdfr debug = 0; 105252068Srmacklem verbose = 0; 106252068Srmacklem while ((ch = getopt(argc, argv, "dvs:c:r:")) != -1) { 107184588Sdfr switch (ch) { 108184588Sdfr case 'd': 109184588Sdfr debug_level++; 110184588Sdfr break; 111252068Srmacklem case 'v': 112252068Srmacklem verbose = 1; 113252068Srmacklem break; 114245089Srmacklem case 's': 115245089Srmacklem#ifndef WITHOUT_KERBEROS 116245089Srmacklem /* 117245089Srmacklem * Set the directory search list. This enables use of 118245089Srmacklem * find_ccache_file() to search the directories for a 119245089Srmacklem * suitable credentials cache file. 120245089Srmacklem */ 121245089Srmacklem strlcpy(ccfile_dirlist, optarg, sizeof(ccfile_dirlist)); 122245089Srmacklem#else 123245089Srmacklem errx(1, "This option not available when built" 124245089Srmacklem " without MK_KERBEROS\n"); 125245089Srmacklem#endif 126245089Srmacklem break; 127245089Srmacklem case 'c': 128245089Srmacklem /* 129245089Srmacklem * Specify a non-default credential cache file 130245089Srmacklem * substring. 131245089Srmacklem */ 132245089Srmacklem strlcpy(ccfile_substring, optarg, 133245089Srmacklem sizeof(ccfile_substring)); 134245089Srmacklem break; 135245089Srmacklem case 'r': 136245089Srmacklem /* 137245089Srmacklem * Set the preferred realm for the credential cache tgt. 138245089Srmacklem */ 139245089Srmacklem strlcpy(pref_realm, optarg, sizeof(pref_realm)); 140245089Srmacklem break; 141184588Sdfr default: 142245089Srmacklem fprintf(stderr, 143245089Srmacklem "usage: %s [-d] [-s dir-list] [-c file-substring]" 144245089Srmacklem " [-r preferred-realm]\n", argv[0]); 145184588Sdfr exit(1); 146184588Sdfr break; 147184588Sdfr } 148184588Sdfr } 149184588Sdfr 150184588Sdfr gssd_load_mech(); 151184588Sdfr 152184588Sdfr if (!debug_level) 153184588Sdfr daemon(0, 0); 154184588Sdfr 155184588Sdfr memset(&sun, 0, sizeof sun); 156184588Sdfr sun.sun_family = AF_LOCAL; 157184588Sdfr unlink(_PATH_GSSDSOCK); 158184588Sdfr strcpy(sun.sun_path, _PATH_GSSDSOCK); 159184588Sdfr sun.sun_len = SUN_LEN(&sun); 160184588Sdfr fd = socket(AF_LOCAL, SOCK_STREAM, 0); 161184588Sdfr if (!fd) { 162245016Srmacklem if (debug_level == 0) { 163245016Srmacklem syslog(LOG_ERR, "Can't create local gssd socket"); 164245016Srmacklem exit(1); 165245016Srmacklem } 166184588Sdfr err(1, "Can't create local gssd socket"); 167184588Sdfr } 168184588Sdfr oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 169184588Sdfr if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) { 170245016Srmacklem if (debug_level == 0) { 171245016Srmacklem syslog(LOG_ERR, "Can't bind local gssd socket"); 172245016Srmacklem exit(1); 173245016Srmacklem } 174184588Sdfr err(1, "Can't bind local gssd socket"); 175184588Sdfr } 176184588Sdfr umask(oldmask); 177184588Sdfr if (listen(fd, SOMAXCONN) < 0) { 178245016Srmacklem if (debug_level == 0) { 179245016Srmacklem syslog(LOG_ERR, "Can't listen on local gssd socket"); 180245016Srmacklem exit(1); 181245016Srmacklem } 182184588Sdfr err(1, "Can't listen on local gssd socket"); 183184588Sdfr } 184184588Sdfr xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 185184588Sdfr if (!xprt) { 186245016Srmacklem if (debug_level == 0) { 187245016Srmacklem syslog(LOG_ERR, 188245016Srmacklem "Can't create transport for local gssd socket"); 189245016Srmacklem exit(1); 190245016Srmacklem } 191184588Sdfr err(1, "Can't create transport for local gssd socket"); 192184588Sdfr } 193184588Sdfr if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) { 194245016Srmacklem if (debug_level == 0) { 195245016Srmacklem syslog(LOG_ERR, 196245016Srmacklem "Can't register service for local gssd socket"); 197245016Srmacklem exit(1); 198245016Srmacklem } 199184588Sdfr err(1, "Can't register service for local gssd socket"); 200184588Sdfr } 201184588Sdfr 202184588Sdfr LIST_INIT(&gss_resources); 203184588Sdfr gss_next_id = 1; 204184588Sdfr gss_start_time = time(0); 205184588Sdfr 206184588Sdfr gssd_syscall(_PATH_GSSDSOCK); 207184588Sdfr svc_run(); 208184588Sdfr 209184588Sdfr return (0); 210184588Sdfr} 211184588Sdfr 212184588Sdfrstatic void 213184588Sdfrgssd_load_mech(void) 214184588Sdfr{ 215184588Sdfr FILE *fp; 216184588Sdfr char buf[256]; 217184588Sdfr char *p; 218184588Sdfr char *name, *oid, *lib, *kobj; 219184588Sdfr 220184588Sdfr fp = fopen(_PATH_GSS_MECH, "r"); 221184588Sdfr if (!fp) 222184588Sdfr return; 223184588Sdfr 224184588Sdfr while (fgets(buf, sizeof(buf), fp)) { 225184588Sdfr if (*buf == '#') 226184588Sdfr continue; 227184588Sdfr p = buf; 228184588Sdfr name = strsep(&p, "\t\n "); 229184588Sdfr if (p) while (isspace(*p)) p++; 230184588Sdfr oid = strsep(&p, "\t\n "); 231184588Sdfr if (p) while (isspace(*p)) p++; 232184588Sdfr lib = strsep(&p, "\t\n "); 233184588Sdfr if (p) while (isspace(*p)) p++; 234184588Sdfr kobj = strsep(&p, "\t\n "); 235184588Sdfr if (!name || !oid || !lib || !kobj) 236184588Sdfr continue; 237184588Sdfr 238184588Sdfr if (strcmp(kobj, "-")) { 239184588Sdfr /* 240184588Sdfr * Attempt to load the kernel module if its 241184588Sdfr * not already present. 242184588Sdfr */ 243184588Sdfr if (modfind(kobj) < 0) { 244184588Sdfr if (kldload(kobj) < 0) { 245184588Sdfr fprintf(stderr, 246184588Sdfr "%s: can't find or load kernel module %s for %s\n", 247184588Sdfr getprogname(), kobj, name); 248184588Sdfr } 249184588Sdfr } 250184588Sdfr } 251184588Sdfr } 252184588Sdfr fclose(fp); 253184588Sdfr} 254184588Sdfr 255184588Sdfrstatic void * 256184588Sdfrgssd_find_resource(uint64_t id) 257184588Sdfr{ 258184588Sdfr struct gss_resource *gr; 259184588Sdfr 260184588Sdfr if (!id) 261184588Sdfr return (NULL); 262184588Sdfr 263184588Sdfr LIST_FOREACH(gr, &gss_resources, gr_link) 264184588Sdfr if (gr->gr_id == id) 265184588Sdfr return (gr->gr_res); 266184588Sdfr 267184588Sdfr return (NULL); 268184588Sdfr} 269184588Sdfr 270184588Sdfrstatic uint64_t 271184588Sdfrgssd_make_resource(void *res) 272184588Sdfr{ 273184588Sdfr struct gss_resource *gr; 274184588Sdfr 275184588Sdfr if (!res) 276184588Sdfr return (0); 277184588Sdfr 278184588Sdfr gr = malloc(sizeof(struct gss_resource)); 279184588Sdfr if (!gr) 280184588Sdfr return (0); 281184588Sdfr gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32); 282184588Sdfr gr->gr_res = res; 283184588Sdfr LIST_INSERT_HEAD(&gss_resources, gr, gr_link); 284184588Sdfr gss_resource_count++; 285184588Sdfr if (debug_level > 1) 286184588Sdfr printf("%d resources allocated\n", gss_resource_count); 287184588Sdfr 288184588Sdfr return (gr->gr_id); 289184588Sdfr} 290184588Sdfr 291184588Sdfrstatic void 292184588Sdfrgssd_delete_resource(uint64_t id) 293184588Sdfr{ 294184588Sdfr struct gss_resource *gr; 295184588Sdfr 296184588Sdfr LIST_FOREACH(gr, &gss_resources, gr_link) { 297184588Sdfr if (gr->gr_id == id) { 298184588Sdfr LIST_REMOVE(gr, gr_link); 299184588Sdfr free(gr); 300184588Sdfr gss_resource_count--; 301184588Sdfr if (debug_level > 1) 302184588Sdfr printf("%d resources allocated\n", 303184588Sdfr gss_resource_count); 304184588Sdfr return; 305184588Sdfr } 306184588Sdfr } 307184588Sdfr} 308184588Sdfr 309252068Srmacklemstatic void 310252068Srmacklemgssd_verbose_out(const char *fmt, ...) 311252068Srmacklem{ 312252068Srmacklem va_list ap; 313252068Srmacklem 314252068Srmacklem if (verbose != 0) { 315252068Srmacklem va_start(ap, fmt); 316252068Srmacklem if (debug_level == 0) 317252068Srmacklem vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); 318252068Srmacklem else 319252068Srmacklem vfprintf(stderr, fmt, ap); 320252068Srmacklem va_end(ap); 321252068Srmacklem } 322252068Srmacklem} 323252068Srmacklem 324184588Sdfrbool_t 325184588Sdfrgssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) 326184588Sdfr{ 327184588Sdfr 328252068Srmacklem gssd_verbose_out("gssd_null: done\n"); 329184588Sdfr return (TRUE); 330184588Sdfr} 331184588Sdfr 332184588Sdfrbool_t 333184588Sdfrgssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp) 334184588Sdfr{ 335184588Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 336184588Sdfr gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 337184588Sdfr gss_name_t name = GSS_C_NO_NAME; 338245089Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 339245089Srmacklem int gotone; 340184588Sdfr 341245089Srmacklem memset(result, 0, sizeof(*result)); 342245089Srmacklem if (ccfile_dirlist[0] != '\0' && argp->cred == 0) { 343245089Srmacklem /* 344245089Srmacklem * For the "-s" case and no credentials provided as an 345245089Srmacklem * argument, search the directory list for an appropriate 346245089Srmacklem * credential cache file. If the search fails, return failure. 347245089Srmacklem */ 348245089Srmacklem gotone = 0; 349245089Srmacklem cp = ccfile_dirlist; 350245089Srmacklem do { 351245089Srmacklem cp2 = strchr(cp, ':'); 352245089Srmacklem if (cp2 != NULL) 353245089Srmacklem *cp2 = '\0'; 354245089Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 355245089Srmacklem if (gotone != 0) 356245089Srmacklem break; 357245089Srmacklem if (cp2 != NULL) 358245089Srmacklem *cp2++ = ':'; 359245089Srmacklem cp = cp2; 360245089Srmacklem } while (cp != NULL && *cp != '\0'); 361245089Srmacklem if (gotone == 0) { 362245089Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 363252068Srmacklem gssd_verbose_out("gssd_init_sec_context: -s no" 364252068Srmacklem " credential cache file found for uid=%d\n", 365252068Srmacklem (int)argp->uid); 366245089Srmacklem return (TRUE); 367245089Srmacklem } 368245089Srmacklem } else { 369245089Srmacklem /* 370245089Srmacklem * If there wasn't a "-s" option or the credentials have 371245089Srmacklem * been provided as an argument, do it the old way. 372245089Srmacklem * When credentials are provided, the uid should be root. 373245089Srmacklem */ 374245089Srmacklem if (argp->cred != 0 && argp->uid != 0) { 375245089Srmacklem if (debug_level == 0) 376245089Srmacklem syslog(LOG_ERR, "gss_init_sec_context:" 377245089Srmacklem " cred for non-root"); 378245089Srmacklem else 379245089Srmacklem fprintf(stderr, "gss_init_sec_context:" 380245089Srmacklem " cred for non-root\n"); 381245089Srmacklem } 382245089Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 383245089Srmacklem (int) argp->uid); 384245089Srmacklem } 385184588Sdfr setenv("KRB5CCNAME", ccname, TRUE); 386184588Sdfr 387184588Sdfr if (argp->cred) { 388184588Sdfr cred = gssd_find_resource(argp->cred); 389184588Sdfr if (!cred) { 390184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 391252068Srmacklem gssd_verbose_out("gssd_init_sec_context: cred" 392252068Srmacklem " resource not found\n"); 393184588Sdfr return (TRUE); 394184588Sdfr } 395184588Sdfr } 396184588Sdfr if (argp->ctx) { 397184588Sdfr ctx = gssd_find_resource(argp->ctx); 398184588Sdfr if (!ctx) { 399184588Sdfr result->major_status = GSS_S_CONTEXT_EXPIRED; 400252068Srmacklem gssd_verbose_out("gssd_init_sec_context: context" 401252068Srmacklem " resource not found\n"); 402184588Sdfr return (TRUE); 403184588Sdfr } 404184588Sdfr } 405184588Sdfr if (argp->name) { 406184588Sdfr name = gssd_find_resource(argp->name); 407184588Sdfr if (!name) { 408184588Sdfr result->major_status = GSS_S_BAD_NAME; 409252068Srmacklem gssd_verbose_out("gssd_init_sec_context: name" 410252068Srmacklem " resource not found\n"); 411184588Sdfr return (TRUE); 412184588Sdfr } 413184588Sdfr } 414184588Sdfr 415184588Sdfr result->major_status = gss_init_sec_context(&result->minor_status, 416184588Sdfr cred, &ctx, name, argp->mech_type, 417184588Sdfr argp->req_flags, argp->time_req, argp->input_chan_bindings, 418184588Sdfr &argp->input_token, &result->actual_mech_type, 419184588Sdfr &result->output_token, &result->ret_flags, &result->time_rec); 420252068Srmacklem gssd_verbose_out("gssd_init_sec_context: done major=0x%x minor=%d" 421252068Srmacklem " uid=%d\n", (unsigned int)result->major_status, 422252068Srmacklem (int)result->minor_status, (int)argp->uid); 423184588Sdfr 424184588Sdfr if (result->major_status == GSS_S_COMPLETE 425184588Sdfr || result->major_status == GSS_S_CONTINUE_NEEDED) { 426184588Sdfr if (argp->ctx) 427184588Sdfr result->ctx = argp->ctx; 428184588Sdfr else 429184588Sdfr result->ctx = gssd_make_resource(ctx); 430184588Sdfr } 431184588Sdfr 432184588Sdfr return (TRUE); 433184588Sdfr} 434184588Sdfr 435184588Sdfrbool_t 436184588Sdfrgssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp) 437184588Sdfr{ 438184588Sdfr gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 439184588Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 440184588Sdfr gss_name_t src_name; 441184588Sdfr gss_cred_id_t delegated_cred_handle; 442184588Sdfr 443184588Sdfr memset(result, 0, sizeof(*result)); 444184588Sdfr if (argp->ctx) { 445184588Sdfr ctx = gssd_find_resource(argp->ctx); 446184588Sdfr if (!ctx) { 447184588Sdfr result->major_status = GSS_S_CONTEXT_EXPIRED; 448252068Srmacklem gssd_verbose_out("gssd_accept_sec_context: ctx" 449252068Srmacklem " resource not found\n"); 450184588Sdfr return (TRUE); 451184588Sdfr } 452184588Sdfr } 453184588Sdfr if (argp->cred) { 454184588Sdfr cred = gssd_find_resource(argp->cred); 455184588Sdfr if (!cred) { 456184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 457252068Srmacklem gssd_verbose_out("gssd_accept_sec_context: cred" 458252068Srmacklem " resource not found\n"); 459184588Sdfr return (TRUE); 460184588Sdfr } 461184588Sdfr } 462184588Sdfr 463184588Sdfr memset(result, 0, sizeof(*result)); 464184588Sdfr result->major_status = gss_accept_sec_context(&result->minor_status, 465184588Sdfr &ctx, cred, &argp->input_token, argp->input_chan_bindings, 466184588Sdfr &src_name, &result->mech_type, &result->output_token, 467184588Sdfr &result->ret_flags, &result->time_rec, 468184588Sdfr &delegated_cred_handle); 469252068Srmacklem gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n", 470252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 471184588Sdfr 472184588Sdfr if (result->major_status == GSS_S_COMPLETE 473184588Sdfr || result->major_status == GSS_S_CONTINUE_NEEDED) { 474184588Sdfr if (argp->ctx) 475184588Sdfr result->ctx = argp->ctx; 476184588Sdfr else 477184588Sdfr result->ctx = gssd_make_resource(ctx); 478184588Sdfr result->src_name = gssd_make_resource(src_name); 479184588Sdfr result->delegated_cred_handle = 480184588Sdfr gssd_make_resource(delegated_cred_handle); 481184588Sdfr } 482184588Sdfr 483184588Sdfr return (TRUE); 484184588Sdfr} 485184588Sdfr 486184588Sdfrbool_t 487184588Sdfrgssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp) 488184588Sdfr{ 489184588Sdfr gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 490184588Sdfr 491184588Sdfr if (ctx) { 492184588Sdfr result->major_status = gss_delete_sec_context( 493184588Sdfr &result->minor_status, &ctx, &result->output_token); 494184588Sdfr gssd_delete_resource(argp->ctx); 495184588Sdfr } else { 496184588Sdfr result->major_status = GSS_S_COMPLETE; 497184588Sdfr result->minor_status = 0; 498184588Sdfr } 499252068Srmacklem gssd_verbose_out("gssd_delete_sec_context: done major=0x%x minor=%d\n", 500252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 501184588Sdfr 502184588Sdfr return (TRUE); 503184588Sdfr} 504184588Sdfr 505184588Sdfrbool_t 506184588Sdfrgssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp) 507184588Sdfr{ 508184588Sdfr gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 509184588Sdfr 510184588Sdfr if (ctx) { 511184588Sdfr result->major_status = gss_export_sec_context( 512184588Sdfr &result->minor_status, &ctx, 513184588Sdfr &result->interprocess_token); 514184588Sdfr result->format = KGSS_HEIMDAL_1_1; 515184588Sdfr gssd_delete_resource(argp->ctx); 516184588Sdfr } else { 517184588Sdfr result->major_status = GSS_S_FAILURE; 518184588Sdfr result->minor_status = 0; 519184588Sdfr result->interprocess_token.length = 0; 520184588Sdfr result->interprocess_token.value = NULL; 521184588Sdfr } 522252068Srmacklem gssd_verbose_out("gssd_export_sec_context: done major=0x%x minor=%d\n", 523252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 524184588Sdfr 525184588Sdfr return (TRUE); 526184588Sdfr} 527184588Sdfr 528184588Sdfrbool_t 529184588Sdfrgssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp) 530184588Sdfr{ 531184588Sdfr gss_name_t name; 532184588Sdfr 533184588Sdfr result->major_status = gss_import_name(&result->minor_status, 534184588Sdfr &argp->input_name_buffer, argp->input_name_type, &name); 535252068Srmacklem gssd_verbose_out("gssd_import_name: done major=0x%x minor=%d\n", 536252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 537184588Sdfr 538184588Sdfr if (result->major_status == GSS_S_COMPLETE) 539184588Sdfr result->output_name = gssd_make_resource(name); 540184588Sdfr else 541184588Sdfr result->output_name = 0; 542184588Sdfr 543184588Sdfr return (TRUE); 544184588Sdfr} 545184588Sdfr 546184588Sdfrbool_t 547184588Sdfrgssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp) 548184588Sdfr{ 549184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 550184588Sdfr gss_name_t output_name; 551184588Sdfr 552184588Sdfr memset(result, 0, sizeof(*result)); 553184588Sdfr if (!name) { 554184588Sdfr result->major_status = GSS_S_BAD_NAME; 555184588Sdfr return (TRUE); 556184588Sdfr } 557184588Sdfr 558184588Sdfr result->major_status = gss_canonicalize_name(&result->minor_status, 559184588Sdfr name, argp->mech_type, &output_name); 560252068Srmacklem gssd_verbose_out("gssd_canonicalize_name: done major=0x%x minor=%d\n", 561252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 562184588Sdfr 563184588Sdfr if (result->major_status == GSS_S_COMPLETE) 564184588Sdfr result->output_name = gssd_make_resource(output_name); 565184588Sdfr else 566184588Sdfr result->output_name = 0; 567184588Sdfr 568184588Sdfr return (TRUE); 569184588Sdfr} 570184588Sdfr 571184588Sdfrbool_t 572184588Sdfrgssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp) 573184588Sdfr{ 574184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 575184588Sdfr 576184588Sdfr memset(result, 0, sizeof(*result)); 577184588Sdfr if (!name) { 578184588Sdfr result->major_status = GSS_S_BAD_NAME; 579252068Srmacklem gssd_verbose_out("gssd_export_name: name resource not found\n"); 580184588Sdfr return (TRUE); 581184588Sdfr } 582184588Sdfr 583184588Sdfr result->major_status = gss_export_name(&result->minor_status, 584184588Sdfr name, &result->exported_name); 585252068Srmacklem gssd_verbose_out("gssd_export_name: done major=0x%x minor=%d\n", 586252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 587184588Sdfr 588184588Sdfr return (TRUE); 589184588Sdfr} 590184588Sdfr 591184588Sdfrbool_t 592184588Sdfrgssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp) 593184588Sdfr{ 594184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 595184588Sdfr 596184588Sdfr if (name) { 597184588Sdfr result->major_status = gss_release_name(&result->minor_status, 598184588Sdfr &name); 599184588Sdfr gssd_delete_resource(argp->input_name); 600184588Sdfr } else { 601184588Sdfr result->major_status = GSS_S_COMPLETE; 602184588Sdfr result->minor_status = 0; 603184588Sdfr } 604252068Srmacklem gssd_verbose_out("gssd_release_name: done major=0x%x minor=%d\n", 605252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 606184588Sdfr 607184588Sdfr return (TRUE); 608184588Sdfr} 609184588Sdfr 610184588Sdfrbool_t 611184588Sdfrgssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp) 612184588Sdfr{ 613184588Sdfr gss_name_t name = gssd_find_resource(argp->pname); 614184588Sdfr uid_t uid; 615250689Srmacklem char buf[1024], *bufp; 616184588Sdfr struct passwd pwd, *pw; 617250689Srmacklem size_t buflen; 618250689Srmacklem int error; 619250689Srmacklem static size_t buflen_hint = 1024; 620184588Sdfr 621184588Sdfr memset(result, 0, sizeof(*result)); 622184588Sdfr if (name) { 623184588Sdfr result->major_status = 624184588Sdfr gss_pname_to_uid(&result->minor_status, 625184588Sdfr name, argp->mech, &uid); 626184588Sdfr if (result->major_status == GSS_S_COMPLETE) { 627184588Sdfr result->uid = uid; 628250689Srmacklem buflen = buflen_hint; 629250689Srmacklem for (;;) { 630250689Srmacklem pw = NULL; 631250689Srmacklem bufp = buf; 632250689Srmacklem if (buflen > sizeof(buf)) 633250689Srmacklem bufp = malloc(buflen); 634250689Srmacklem if (bufp == NULL) 635250689Srmacklem break; 636250689Srmacklem error = getpwuid_r(uid, &pwd, bufp, buflen, 637250689Srmacklem &pw); 638250689Srmacklem if (error != ERANGE) 639250689Srmacklem break; 640250689Srmacklem if (buflen > sizeof(buf)) 641250689Srmacklem free(bufp); 642250689Srmacklem buflen += 1024; 643250689Srmacklem if (buflen > buflen_hint) 644250689Srmacklem buflen_hint = buflen; 645250689Srmacklem } 646184588Sdfr if (pw) { 647184588Sdfr int len = NGRPS; 648184588Sdfr int groups[NGRPS]; 649184588Sdfr result->gid = pw->pw_gid; 650184588Sdfr getgrouplist(pw->pw_name, pw->pw_gid, 651184588Sdfr groups, &len); 652184588Sdfr result->gidlist.gidlist_len = len; 653184588Sdfr result->gidlist.gidlist_val = 654184588Sdfr mem_alloc(len * sizeof(int)); 655184588Sdfr memcpy(result->gidlist.gidlist_val, groups, 656184588Sdfr len * sizeof(int)); 657252068Srmacklem gssd_verbose_out("gssd_pname_to_uid: mapped" 658252068Srmacklem " to uid=%d, gid=%d\n", (int)result->uid, 659252068Srmacklem (int)result->gid); 660184588Sdfr } else { 661184588Sdfr result->gid = 65534; 662184588Sdfr result->gidlist.gidlist_len = 0; 663184588Sdfr result->gidlist.gidlist_val = NULL; 664252068Srmacklem gssd_verbose_out("gssd_pname_to_uid: mapped" 665252068Srmacklem " to uid=%d, but no groups\n", 666252068Srmacklem (int)result->uid); 667184588Sdfr } 668250689Srmacklem if (bufp != NULL && buflen > sizeof(buf)) 669250689Srmacklem free(bufp); 670252068Srmacklem } else 671252068Srmacklem gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x" 672252068Srmacklem " minor=%d\n", (unsigned int)result->major_status, 673252068Srmacklem (int)result->minor_status); 674184588Sdfr } else { 675184588Sdfr result->major_status = GSS_S_BAD_NAME; 676184588Sdfr result->minor_status = 0; 677252068Srmacklem gssd_verbose_out("gssd_pname_to_uid: no name\n"); 678184588Sdfr } 679184588Sdfr 680184588Sdfr return (TRUE); 681184588Sdfr} 682184588Sdfr 683184588Sdfrbool_t 684184588Sdfrgssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp) 685184588Sdfr{ 686184588Sdfr gss_name_t desired_name = GSS_C_NO_NAME; 687184588Sdfr gss_cred_id_t cred; 688245089Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 689245089Srmacklem int gotone; 690184588Sdfr 691245089Srmacklem memset(result, 0, sizeof(*result)); 692245089Srmacklem if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) { 693245089Srmacklem /* 694245089Srmacklem * For the "-s" case and no name provided as an 695245089Srmacklem * argument, search the directory list for an appropriate 696245089Srmacklem * credential cache file. If the search fails, return failure. 697245089Srmacklem */ 698245089Srmacklem gotone = 0; 699245089Srmacklem cp = ccfile_dirlist; 700245089Srmacklem do { 701245089Srmacklem cp2 = strchr(cp, ':'); 702245089Srmacklem if (cp2 != NULL) 703245089Srmacklem *cp2 = '\0'; 704245089Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 705245089Srmacklem if (gotone != 0) 706245089Srmacklem break; 707245089Srmacklem if (cp2 != NULL) 708245089Srmacklem *cp2++ = ':'; 709245089Srmacklem cp = cp2; 710245089Srmacklem } while (cp != NULL && *cp != '\0'); 711245089Srmacklem if (gotone == 0) { 712245089Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 713252068Srmacklem gssd_verbose_out("gssd_acquire_cred: no cred cache" 714252068Srmacklem " file found\n"); 715245089Srmacklem return (TRUE); 716245089Srmacklem } 717245089Srmacklem } else { 718245089Srmacklem /* 719245089Srmacklem * If there wasn't a "-s" option or the name has 720245089Srmacklem * been provided as an argument, do it the old way. 721245089Srmacklem * When a name is provided, it will normally exist in the 722245089Srmacklem * default keytab file and the uid will be root. 723245089Srmacklem */ 724245089Srmacklem if (argp->desired_name != 0 && argp->uid != 0) { 725245089Srmacklem if (debug_level == 0) 726245089Srmacklem syslog(LOG_ERR, "gss_acquire_cred:" 727245089Srmacklem " principal_name for non-root"); 728245089Srmacklem else 729245089Srmacklem fprintf(stderr, "gss_acquire_cred:" 730245089Srmacklem " principal_name for non-root\n"); 731245089Srmacklem } 732245089Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 733245089Srmacklem (int) argp->uid); 734245089Srmacklem } 735184588Sdfr setenv("KRB5CCNAME", ccname, TRUE); 736184588Sdfr 737184588Sdfr if (argp->desired_name) { 738184588Sdfr desired_name = gssd_find_resource(argp->desired_name); 739184588Sdfr if (!desired_name) { 740184588Sdfr result->major_status = GSS_S_BAD_NAME; 741252068Srmacklem gssd_verbose_out("gssd_acquire_cred: no desired name" 742252068Srmacklem " found\n"); 743184588Sdfr return (TRUE); 744184588Sdfr } 745184588Sdfr } 746184588Sdfr 747184588Sdfr result->major_status = gss_acquire_cred(&result->minor_status, 748184588Sdfr desired_name, argp->time_req, argp->desired_mechs, 749184588Sdfr argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec); 750252068Srmacklem gssd_verbose_out("gssd_acquire_cred: done major=0x%x minor=%d\n", 751252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 752184588Sdfr 753184588Sdfr if (result->major_status == GSS_S_COMPLETE) 754184588Sdfr result->output_cred = gssd_make_resource(cred); 755184588Sdfr else 756184588Sdfr result->output_cred = 0; 757184588Sdfr 758184588Sdfr return (TRUE); 759184588Sdfr} 760184588Sdfr 761184588Sdfrbool_t 762184588Sdfrgssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp) 763184588Sdfr{ 764184588Sdfr gss_cred_id_t cred = gssd_find_resource(argp->cred); 765184588Sdfr 766184588Sdfr memset(result, 0, sizeof(*result)); 767184588Sdfr if (!cred) { 768184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 769252068Srmacklem gssd_verbose_out("gssd_set_cred: no credentials\n"); 770184588Sdfr return (TRUE); 771184588Sdfr } 772184588Sdfr 773184588Sdfr result->major_status = gss_set_cred_option(&result->minor_status, 774184588Sdfr &cred, argp->option_name, &argp->option_value); 775252068Srmacklem gssd_verbose_out("gssd_set_cred: done major=0x%x minor=%d\n", 776252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 777184588Sdfr 778184588Sdfr return (TRUE); 779184588Sdfr} 780184588Sdfr 781184588Sdfrbool_t 782184588Sdfrgssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp) 783184588Sdfr{ 784184588Sdfr gss_cred_id_t cred = gssd_find_resource(argp->cred); 785184588Sdfr 786184588Sdfr if (cred) { 787184588Sdfr result->major_status = gss_release_cred(&result->minor_status, 788184588Sdfr &cred); 789184588Sdfr gssd_delete_resource(argp->cred); 790184588Sdfr } else { 791184588Sdfr result->major_status = GSS_S_COMPLETE; 792184588Sdfr result->minor_status = 0; 793184588Sdfr } 794252068Srmacklem gssd_verbose_out("gssd_release_cred: done major=0x%x minor=%d\n", 795252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 796184588Sdfr 797184588Sdfr return (TRUE); 798184588Sdfr} 799184588Sdfr 800184588Sdfrbool_t 801184588Sdfrgssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp) 802184588Sdfr{ 803184588Sdfr 804184588Sdfr result->message_context = argp->message_context; 805184588Sdfr result->major_status = gss_display_status(&result->minor_status, 806184588Sdfr argp->status_value, argp->status_type, argp->mech_type, 807184588Sdfr &result->message_context, &result->status_string); 808252068Srmacklem gssd_verbose_out("gssd_display_status: done major=0x%x minor=%d\n", 809252068Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 810184588Sdfr 811184588Sdfr return (TRUE); 812184588Sdfr} 813184588Sdfr 814184588Sdfrint 815184588Sdfrgssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) 816184588Sdfr{ 817184588Sdfr /* 818184588Sdfr * We don't use XDR to free the results - anything which was 819184588Sdfr * allocated came from GSS-API. We use xdr_result to figure 820184588Sdfr * out what to do. 821184588Sdfr */ 822184588Sdfr OM_uint32 junk; 823184588Sdfr 824184588Sdfr if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) { 825184588Sdfr init_sec_context_res *p = (init_sec_context_res *) result; 826184588Sdfr gss_release_buffer(&junk, &p->output_token); 827184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) { 828184588Sdfr accept_sec_context_res *p = (accept_sec_context_res *) result; 829184588Sdfr gss_release_buffer(&junk, &p->output_token); 830184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) { 831184588Sdfr delete_sec_context_res *p = (delete_sec_context_res *) result; 832184588Sdfr gss_release_buffer(&junk, &p->output_token); 833184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) { 834184588Sdfr export_sec_context_res *p = (export_sec_context_res *) result; 835184588Sdfr if (p->interprocess_token.length) 836184588Sdfr memset(p->interprocess_token.value, 0, 837184588Sdfr p->interprocess_token.length); 838184588Sdfr gss_release_buffer(&junk, &p->interprocess_token); 839184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_export_name_res) { 840184588Sdfr export_name_res *p = (export_name_res *) result; 841184588Sdfr gss_release_buffer(&junk, &p->exported_name); 842184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) { 843184588Sdfr acquire_cred_res *p = (acquire_cred_res *) result; 844184588Sdfr gss_release_oid_set(&junk, &p->actual_mechs); 845184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) { 846184588Sdfr pname_to_uid_res *p = (pname_to_uid_res *) result; 847184588Sdfr if (p->gidlist.gidlist_val) 848184588Sdfr free(p->gidlist.gidlist_val); 849184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_display_status_res) { 850184588Sdfr display_status_res *p = (display_status_res *) result; 851184588Sdfr gss_release_buffer(&junk, &p->status_string); 852184588Sdfr } 853184588Sdfr 854184588Sdfr return (TRUE); 855184588Sdfr} 856245089Srmacklem 857245089Srmacklem/* 858245089Srmacklem * Search a directory for the most likely candidate to be used as the 859245089Srmacklem * credential cache for a uid. If successful, return 1 and fill the 860245089Srmacklem * file's path id into "rpath". Otherwise, return 0. 861245089Srmacklem */ 862245089Srmacklemstatic int 863245089Srmacklemfind_ccache_file(const char *dirpath, uid_t uid, char *rpath) 864245089Srmacklem{ 865245089Srmacklem DIR *dirp; 866245089Srmacklem struct dirent *dp; 867245089Srmacklem struct stat sb; 868245089Srmacklem time_t exptime, oexptime; 869245089Srmacklem int gotone, len, rating, orating; 870245089Srmacklem char namepath[PATH_MAX + 5 + 1]; 871245089Srmacklem char retpath[PATH_MAX + 5 + 1]; 872245089Srmacklem 873245089Srmacklem dirp = opendir(dirpath); 874245089Srmacklem if (dirp == NULL) 875245089Srmacklem return (0); 876245089Srmacklem gotone = 0; 877245089Srmacklem orating = 0; 878245089Srmacklem oexptime = 0; 879245089Srmacklem while ((dp = readdir(dirp)) != NULL) { 880245089Srmacklem len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath, 881245089Srmacklem dp->d_name); 882245089Srmacklem if (len < sizeof(namepath) && 883245089Srmacklem strstr(dp->d_name, ccfile_substring) != NULL && 884245089Srmacklem lstat(namepath, &sb) >= 0 && 885245089Srmacklem sb.st_uid == uid && 886245089Srmacklem S_ISREG(sb.st_mode)) { 887245089Srmacklem len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s", 888245089Srmacklem dirpath, dp->d_name); 889245089Srmacklem if (len < sizeof(namepath) && 890245089Srmacklem is_a_valid_tgt_cache(namepath, uid, &rating, 891245089Srmacklem &exptime) != 0) { 892245089Srmacklem if (gotone == 0 || rating > orating || 893245089Srmacklem (rating == orating && exptime > oexptime)) { 894245089Srmacklem orating = rating; 895245089Srmacklem oexptime = exptime; 896245089Srmacklem strcpy(retpath, namepath); 897245089Srmacklem gotone = 1; 898245089Srmacklem } 899245089Srmacklem } 900245089Srmacklem } 901245089Srmacklem } 902245089Srmacklem closedir(dirp); 903245089Srmacklem if (gotone != 0) { 904245089Srmacklem strcpy(rpath, retpath); 905245089Srmacklem return (1); 906245089Srmacklem } 907245089Srmacklem return (0); 908245089Srmacklem} 909245089Srmacklem 910245089Srmacklem/* 911245089Srmacklem * Try to determine if the file is a valid tgt cache file. 912245089Srmacklem * Check that the file has a valid tgt for a principal. 913245089Srmacklem * If it does, return 1, otherwise return 0. 914245089Srmacklem * It also returns a "rating" and the expiry time for the TGT, when found. 915245089Srmacklem * This "rating" is higher based on heuristics that make it more 916245089Srmacklem * likely to be the correct credential cache file to use. It can 917245089Srmacklem * be used by the caller, along with expiry time, to select from 918245089Srmacklem * multiple credential cache files. 919245089Srmacklem */ 920245089Srmacklemstatic int 921245089Srmacklemis_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating, 922245089Srmacklem time_t *retexptime) 923245089Srmacklem{ 924245089Srmacklem#ifndef WITHOUT_KERBEROS 925245089Srmacklem krb5_context context; 926245089Srmacklem krb5_principal princ; 927245089Srmacklem krb5_ccache ccache; 928245089Srmacklem krb5_error_code retval; 929245089Srmacklem krb5_cc_cursor curse; 930245089Srmacklem krb5_creds krbcred; 931245089Srmacklem int gotone, orating, rating, ret; 932245089Srmacklem struct passwd *pw; 933245089Srmacklem char *cp, *cp2, *pname; 934245089Srmacklem time_t exptime; 935245089Srmacklem 936245089Srmacklem /* Find a likely name for the uid principal. */ 937245089Srmacklem pw = getpwuid(uid); 938245089Srmacklem 939245089Srmacklem /* 940245089Srmacklem * Do a bunch of krb5 library stuff to try and determine if 941245089Srmacklem * this file is a credentials cache with an appropriate TGT 942245089Srmacklem * in it. 943245089Srmacklem */ 944245089Srmacklem retval = krb5_init_context(&context); 945245089Srmacklem if (retval != 0) 946245089Srmacklem return (0); 947245089Srmacklem retval = krb5_cc_resolve(context, filepath, &ccache); 948245089Srmacklem if (retval != 0) { 949245089Srmacklem krb5_free_context(context); 950245089Srmacklem return (0); 951245089Srmacklem } 952245089Srmacklem ret = 0; 953245089Srmacklem orating = 0; 954245089Srmacklem exptime = 0; 955245089Srmacklem retval = krb5_cc_start_seq_get(context, ccache, &curse); 956245089Srmacklem if (retval == 0) { 957245089Srmacklem while ((retval = krb5_cc_next_cred(context, ccache, &curse, 958245089Srmacklem &krbcred)) == 0) { 959245089Srmacklem gotone = 0; 960245089Srmacklem rating = 0; 961245089Srmacklem retval = krb5_unparse_name(context, krbcred.server, 962245089Srmacklem &pname); 963245089Srmacklem if (retval == 0) { 964245089Srmacklem cp = strchr(pname, '/'); 965245089Srmacklem if (cp != NULL) { 966245089Srmacklem *cp++ = '\0'; 967245089Srmacklem if (strcmp(pname, "krbtgt") == 0 && 968245089Srmacklem krbcred.times.endtime > time(NULL) 969245089Srmacklem ) { 970245089Srmacklem gotone = 1; 971245089Srmacklem /* 972245089Srmacklem * Test to see if this is a 973245089Srmacklem * tgt for cross-realm auth. 974245089Srmacklem * Rate it higher, if it is not. 975245089Srmacklem */ 976245089Srmacklem cp2 = strchr(cp, '@'); 977245089Srmacklem if (cp2 != NULL) { 978245089Srmacklem *cp2++ = '\0'; 979245089Srmacklem if (strcmp(cp, cp2) == 980245089Srmacklem 0) 981245089Srmacklem rating++; 982245089Srmacklem } 983245089Srmacklem } 984245089Srmacklem } 985245089Srmacklem free(pname); 986245089Srmacklem } 987245089Srmacklem if (gotone != 0) { 988245089Srmacklem retval = krb5_unparse_name(context, 989245089Srmacklem krbcred.client, &pname); 990245089Srmacklem if (retval == 0) { 991245089Srmacklem cp = strchr(pname, '@'); 992245089Srmacklem if (cp != NULL) { 993245089Srmacklem *cp++ = '\0'; 994245089Srmacklem if (pw != NULL && strcmp(pname, 995245089Srmacklem pw->pw_name) == 0) 996245089Srmacklem rating++; 997245089Srmacklem if (strchr(pname, '/') == NULL) 998245089Srmacklem rating++; 999245089Srmacklem if (pref_realm[0] != '\0' && 1000245089Srmacklem strcmp(cp, pref_realm) == 0) 1001245089Srmacklem rating++; 1002245089Srmacklem } 1003245089Srmacklem } 1004245089Srmacklem free(pname); 1005245089Srmacklem if (rating > orating) { 1006245089Srmacklem orating = rating; 1007245089Srmacklem exptime = krbcred.times.endtime; 1008245089Srmacklem } else if (rating == orating && 1009245089Srmacklem krbcred.times.endtime > exptime) 1010245089Srmacklem exptime = krbcred.times.endtime; 1011245089Srmacklem ret = 1; 1012245089Srmacklem } 1013245089Srmacklem krb5_free_cred_contents(context, &krbcred); 1014245089Srmacklem } 1015245089Srmacklem krb5_cc_end_seq_get(context, ccache, &curse); 1016245089Srmacklem } 1017245089Srmacklem krb5_cc_close(context, ccache); 1018245089Srmacklem krb5_free_context(context); 1019245089Srmacklem if (ret != 0) { 1020245089Srmacklem *retrating = orating; 1021245089Srmacklem *retexptime = exptime; 1022245089Srmacklem } 1023245089Srmacklem return (ret); 1024245089Srmacklem#else /* WITHOUT_KERBEROS */ 1025245089Srmacklem return (0); 1026245089Srmacklem#endif /* !WITHOUT_KERBEROS */ 1027245089Srmacklem} 1028245089Srmacklem 1029