gssd.c revision 251476
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: head/usr.sbin/gssd/gssd.c 251476 2013-06-06 22:02:03Z 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> 36244331Srmacklem#include <sys/syslog.h> 37184588Sdfr#include <ctype.h> 38244604Srmacklem#include <dirent.h> 39184588Sdfr#include <err.h> 40250176Srmacklem#include <errno.h> 41245014Srmacklem#ifndef WITHOUT_KERBEROS 42244604Srmacklem#include <krb5.h> 43245014Srmacklem#endif 44184588Sdfr#include <pwd.h> 45251476Srmacklem#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; 73244604Srmacklemstatic char ccfile_dirlist[PATH_MAX + 1], ccfile_substring[NAME_MAX + 1]; 74244604Srmacklemstatic char pref_realm[1024]; 75251444Srmacklemstatic int verbose; 76184588Sdfr 77184588Sdfrstatic void gssd_load_mech(void); 78244604Srmacklemstatic int find_ccache_file(const char *, uid_t, char *); 79244604Srmacklemstatic int is_a_valid_tgt_cache(const char *, uid_t, int *, time_t *); 80251444Srmacklemstatic 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 97244604Srmacklem /* 98244604Srmacklem * Initialize the credential cache file name substring and the 99244604Srmacklem * search directory list. 100244604Srmacklem */ 101244604Srmacklem strlcpy(ccfile_substring, "krb5cc_", sizeof(ccfile_substring)); 102244604Srmacklem ccfile_dirlist[0] = '\0'; 103244604Srmacklem pref_realm[0] = '\0'; 104184588Sdfr debug = 0; 105251444Srmacklem verbose = 0; 106251444Srmacklem while ((ch = getopt(argc, argv, "dvs:c:r:")) != -1) { 107184588Sdfr switch (ch) { 108184588Sdfr case 'd': 109184588Sdfr debug_level++; 110184588Sdfr break; 111251444Srmacklem case 'v': 112251444Srmacklem verbose = 1; 113251444Srmacklem break; 114244604Srmacklem case 's': 115245014Srmacklem#ifndef WITHOUT_KERBEROS 116244604Srmacklem /* 117244604Srmacklem * Set the directory search list. This enables use of 118244604Srmacklem * find_ccache_file() to search the directories for a 119244604Srmacklem * suitable credentials cache file. 120244604Srmacklem */ 121244604Srmacklem strlcpy(ccfile_dirlist, optarg, sizeof(ccfile_dirlist)); 122245014Srmacklem#else 123245014Srmacklem errx(1, "This option not available when built" 124245014Srmacklem " without MK_KERBEROS\n"); 125245014Srmacklem#endif 126244604Srmacklem break; 127244604Srmacklem case 'c': 128244604Srmacklem /* 129244604Srmacklem * Specify a non-default credential cache file 130244604Srmacklem * substring. 131244604Srmacklem */ 132244604Srmacklem strlcpy(ccfile_substring, optarg, 133244604Srmacklem sizeof(ccfile_substring)); 134244604Srmacklem break; 135244604Srmacklem case 'r': 136244604Srmacklem /* 137244604Srmacklem * Set the preferred realm for the credential cache tgt. 138244604Srmacklem */ 139244604Srmacklem strlcpy(pref_realm, optarg, sizeof(pref_realm)); 140244604Srmacklem break; 141184588Sdfr default: 142244604Srmacklem fprintf(stderr, 143244604Srmacklem "usage: %s [-d] [-s dir-list] [-c file-substring]" 144244604Srmacklem " [-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) { 162244331Srmacklem if (debug_level == 0) { 163244331Srmacklem syslog(LOG_ERR, "Can't create local gssd socket"); 164244331Srmacklem exit(1); 165244331Srmacklem } 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) { 170244331Srmacklem if (debug_level == 0) { 171244331Srmacklem syslog(LOG_ERR, "Can't bind local gssd socket"); 172244331Srmacklem exit(1); 173244331Srmacklem } 174184588Sdfr err(1, "Can't bind local gssd socket"); 175184588Sdfr } 176184588Sdfr umask(oldmask); 177184588Sdfr if (listen(fd, SOMAXCONN) < 0) { 178244331Srmacklem if (debug_level == 0) { 179244331Srmacklem syslog(LOG_ERR, "Can't listen on local gssd socket"); 180244331Srmacklem exit(1); 181244331Srmacklem } 182184588Sdfr err(1, "Can't listen on local gssd socket"); 183184588Sdfr } 184184588Sdfr xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 185184588Sdfr if (!xprt) { 186244331Srmacklem if (debug_level == 0) { 187244331Srmacklem syslog(LOG_ERR, 188244331Srmacklem "Can't create transport for local gssd socket"); 189244331Srmacklem exit(1); 190244331Srmacklem } 191184588Sdfr err(1, "Can't create transport for local gssd socket"); 192184588Sdfr } 193184588Sdfr if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) { 194244331Srmacklem if (debug_level == 0) { 195244331Srmacklem syslog(LOG_ERR, 196244331Srmacklem "Can't register service for local gssd socket"); 197244331Srmacklem exit(1); 198244331Srmacklem } 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 309251444Srmacklemstatic void 310251444Srmacklemgssd_verbose_out(const char *fmt, ...) 311251444Srmacklem{ 312251444Srmacklem va_list ap; 313251444Srmacklem 314251444Srmacklem if (verbose != 0) { 315251444Srmacklem va_start(ap, fmt); 316251444Srmacklem if (debug_level == 0) 317251444Srmacklem vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); 318251444Srmacklem else 319251444Srmacklem vfprintf(stderr, fmt, ap); 320251444Srmacklem va_end(ap); 321251444Srmacklem } 322251444Srmacklem} 323251444Srmacklem 324184588Sdfrbool_t 325184588Sdfrgssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) 326184588Sdfr{ 327184588Sdfr 328251444Srmacklem 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; 338244604Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 339244604Srmacklem int gotone; 340184588Sdfr 341244604Srmacklem memset(result, 0, sizeof(*result)); 342244604Srmacklem if (ccfile_dirlist[0] != '\0' && argp->cred == 0) { 343244604Srmacklem /* 344244604Srmacklem * For the "-s" case and no credentials provided as an 345244604Srmacklem * argument, search the directory list for an appropriate 346244604Srmacklem * credential cache file. If the search fails, return failure. 347244604Srmacklem */ 348244604Srmacklem gotone = 0; 349244604Srmacklem cp = ccfile_dirlist; 350244604Srmacklem do { 351244604Srmacklem cp2 = strchr(cp, ':'); 352244604Srmacklem if (cp2 != NULL) 353244604Srmacklem *cp2 = '\0'; 354244604Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 355244604Srmacklem if (gotone != 0) 356244604Srmacklem break; 357244604Srmacklem if (cp2 != NULL) 358244604Srmacklem *cp2++ = ':'; 359244604Srmacklem cp = cp2; 360244604Srmacklem } while (cp != NULL && *cp != '\0'); 361244604Srmacklem if (gotone == 0) { 362244604Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 363251444Srmacklem gssd_verbose_out("gssd_init_sec_context: -s no" 364251444Srmacklem " credential cache file found for uid=%d\n", 365251444Srmacklem (int)argp->uid); 366244604Srmacklem return (TRUE); 367244604Srmacklem } 368244604Srmacklem } else { 369244604Srmacklem /* 370244604Srmacklem * If there wasn't a "-s" option or the credentials have 371244604Srmacklem * been provided as an argument, do it the old way. 372244604Srmacklem * When credentials are provided, the uid should be root. 373244604Srmacklem */ 374244604Srmacklem if (argp->cred != 0 && argp->uid != 0) { 375244604Srmacklem if (debug_level == 0) 376244604Srmacklem syslog(LOG_ERR, "gss_init_sec_context:" 377244604Srmacklem " cred for non-root"); 378244604Srmacklem else 379244604Srmacklem fprintf(stderr, "gss_init_sec_context:" 380244604Srmacklem " cred for non-root\n"); 381244604Srmacklem } 382244604Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 383244604Srmacklem (int) argp->uid); 384244604Srmacklem } 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; 391251444Srmacklem gssd_verbose_out("gssd_init_sec_context: cred" 392251444Srmacklem " 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; 400251444Srmacklem gssd_verbose_out("gssd_init_sec_context: context" 401251444Srmacklem " 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; 409251444Srmacklem gssd_verbose_out("gssd_init_sec_context: name" 410251444Srmacklem " 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); 420251444Srmacklem gssd_verbose_out("gssd_init_sec_context: done major=0x%x minor=%d" 421251444Srmacklem " uid=%d\n", (unsigned int)result->major_status, 422251444Srmacklem (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; 448251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: ctx" 449251444Srmacklem " 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; 457251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: cred" 458251444Srmacklem " 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); 469251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n", 470251444Srmacklem (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 } 499251444Srmacklem gssd_verbose_out("gssd_delete_sec_context: done major=0x%x minor=%d\n", 500251444Srmacklem (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 } 522251444Srmacklem gssd_verbose_out("gssd_export_sec_context: done major=0x%x minor=%d\n", 523251444Srmacklem (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); 535251444Srmacklem gssd_verbose_out("gssd_import_name: done major=0x%x minor=%d\n", 536251444Srmacklem (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); 560251444Srmacklem gssd_verbose_out("gssd_canonicalize_name: done major=0x%x minor=%d\n", 561251444Srmacklem (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; 579251444Srmacklem 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); 585251444Srmacklem gssd_verbose_out("gssd_export_name: done major=0x%x minor=%d\n", 586251444Srmacklem (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 } 604251444Srmacklem gssd_verbose_out("gssd_release_name: done major=0x%x minor=%d\n", 605251444Srmacklem (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; 615250176Srmacklem char buf[1024], *bufp; 616184588Sdfr struct passwd pwd, *pw; 617250176Srmacklem size_t buflen; 618250176Srmacklem int error; 619250176Srmacklem 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; 628250176Srmacklem buflen = buflen_hint; 629250176Srmacklem for (;;) { 630250176Srmacklem pw = NULL; 631250176Srmacklem bufp = buf; 632250176Srmacklem if (buflen > sizeof(buf)) 633250176Srmacklem bufp = malloc(buflen); 634250176Srmacklem if (bufp == NULL) 635250176Srmacklem break; 636250176Srmacklem error = getpwuid_r(uid, &pwd, bufp, buflen, 637250176Srmacklem &pw); 638250176Srmacklem if (error != ERANGE) 639250176Srmacklem break; 640250176Srmacklem if (buflen > sizeof(buf)) 641250176Srmacklem free(bufp); 642250176Srmacklem buflen += 1024; 643250176Srmacklem if (buflen > buflen_hint) 644250176Srmacklem buflen_hint = buflen; 645250176Srmacklem } 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)); 657251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: mapped" 658251444Srmacklem " to uid=%d, gid=%d\n", (int)result->uid, 659251444Srmacklem (int)result->gid); 660184588Sdfr } else { 661184588Sdfr result->gid = 65534; 662184588Sdfr result->gidlist.gidlist_len = 0; 663184588Sdfr result->gidlist.gidlist_val = NULL; 664251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: mapped" 665251444Srmacklem " to uid=%d, but no groups\n", 666251444Srmacklem (int)result->uid); 667184588Sdfr } 668250176Srmacklem if (bufp != NULL && buflen > sizeof(buf)) 669250176Srmacklem free(bufp); 670251444Srmacklem } else 671251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x" 672251444Srmacklem " minor=%d\n", (unsigned int)result->major_status, 673251444Srmacklem (int)result->minor_status); 674184588Sdfr } else { 675184588Sdfr result->major_status = GSS_S_BAD_NAME; 676184588Sdfr result->minor_status = 0; 677251444Srmacklem 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; 688244604Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 689244604Srmacklem int gotone; 690184588Sdfr 691244604Srmacklem memset(result, 0, sizeof(*result)); 692244604Srmacklem if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) { 693244604Srmacklem /* 694244604Srmacklem * For the "-s" case and no name provided as an 695244604Srmacklem * argument, search the directory list for an appropriate 696244604Srmacklem * credential cache file. If the search fails, return failure. 697244604Srmacklem */ 698244604Srmacklem gotone = 0; 699244604Srmacklem cp = ccfile_dirlist; 700244604Srmacklem do { 701244604Srmacklem cp2 = strchr(cp, ':'); 702244604Srmacklem if (cp2 != NULL) 703244604Srmacklem *cp2 = '\0'; 704244604Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 705244604Srmacklem if (gotone != 0) 706244604Srmacklem break; 707244604Srmacklem if (cp2 != NULL) 708244604Srmacklem *cp2++ = ':'; 709244604Srmacklem cp = cp2; 710244604Srmacklem } while (cp != NULL && *cp != '\0'); 711244604Srmacklem if (gotone == 0) { 712244604Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 713251444Srmacklem gssd_verbose_out("gssd_acquire_cred: no cred cache" 714251444Srmacklem " file found\n"); 715244604Srmacklem return (TRUE); 716244604Srmacklem } 717244604Srmacklem } else { 718244604Srmacklem /* 719244604Srmacklem * If there wasn't a "-s" option or the name has 720244604Srmacklem * been provided as an argument, do it the old way. 721244604Srmacklem * When a name is provided, it will normally exist in the 722244604Srmacklem * default keytab file and the uid will be root. 723244604Srmacklem */ 724244604Srmacklem if (argp->desired_name != 0 && argp->uid != 0) { 725244604Srmacklem if (debug_level == 0) 726244604Srmacklem syslog(LOG_ERR, "gss_acquire_cred:" 727244604Srmacklem " principal_name for non-root"); 728244604Srmacklem else 729244604Srmacklem fprintf(stderr, "gss_acquire_cred:" 730244604Srmacklem " principal_name for non-root\n"); 731244604Srmacklem } 732244604Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 733244604Srmacklem (int) argp->uid); 734244604Srmacklem } 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; 741251444Srmacklem gssd_verbose_out("gssd_acquire_cred: no desired name" 742251444Srmacklem " 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); 750251444Srmacklem gssd_verbose_out("gssd_acquire_cred: done major=0x%x minor=%d\n", 751251444Srmacklem (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; 769251444Srmacklem 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); 775251444Srmacklem gssd_verbose_out("gssd_set_cred: done major=0x%x minor=%d\n", 776251444Srmacklem (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 } 794251444Srmacklem gssd_verbose_out("gssd_release_cred: done major=0x%x minor=%d\n", 795251444Srmacklem (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); 808251444Srmacklem gssd_verbose_out("gssd_display_status: done major=0x%x minor=%d\n", 809251444Srmacklem (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} 856244604Srmacklem 857244604Srmacklem/* 858244604Srmacklem * Search a directory for the most likely candidate to be used as the 859244604Srmacklem * credential cache for a uid. If successful, return 1 and fill the 860244604Srmacklem * file's path id into "rpath". Otherwise, return 0. 861244604Srmacklem */ 862244604Srmacklemstatic int 863244604Srmacklemfind_ccache_file(const char *dirpath, uid_t uid, char *rpath) 864244604Srmacklem{ 865244604Srmacklem DIR *dirp; 866244604Srmacklem struct dirent *dp; 867244604Srmacklem struct stat sb; 868244604Srmacklem time_t exptime, oexptime; 869244604Srmacklem int gotone, len, rating, orating; 870244604Srmacklem char namepath[PATH_MAX + 5 + 1]; 871244604Srmacklem char retpath[PATH_MAX + 5 + 1]; 872244604Srmacklem 873244604Srmacklem dirp = opendir(dirpath); 874244604Srmacklem if (dirp == NULL) 875244604Srmacklem return (0); 876244604Srmacklem gotone = 0; 877244604Srmacklem orating = 0; 878244604Srmacklem oexptime = 0; 879244604Srmacklem while ((dp = readdir(dirp)) != NULL) { 880244604Srmacklem len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath, 881244604Srmacklem dp->d_name); 882244604Srmacklem if (len < sizeof(namepath) && 883244604Srmacklem strstr(dp->d_name, ccfile_substring) != NULL && 884244604Srmacklem lstat(namepath, &sb) >= 0 && 885244604Srmacklem sb.st_uid == uid && 886244604Srmacklem S_ISREG(sb.st_mode)) { 887244604Srmacklem len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s", 888244604Srmacklem dirpath, dp->d_name); 889244604Srmacklem if (len < sizeof(namepath) && 890244604Srmacklem is_a_valid_tgt_cache(namepath, uid, &rating, 891244604Srmacklem &exptime) != 0) { 892244604Srmacklem if (gotone == 0 || rating > orating || 893244604Srmacklem (rating == orating && exptime > oexptime)) { 894244604Srmacklem orating = rating; 895244604Srmacklem oexptime = exptime; 896244604Srmacklem strcpy(retpath, namepath); 897244604Srmacklem gotone = 1; 898244604Srmacklem } 899244604Srmacklem } 900244604Srmacklem } 901244604Srmacklem } 902244604Srmacklem closedir(dirp); 903244604Srmacklem if (gotone != 0) { 904244604Srmacklem strcpy(rpath, retpath); 905244604Srmacklem return (1); 906244604Srmacklem } 907244604Srmacklem return (0); 908244604Srmacklem} 909244604Srmacklem 910244604Srmacklem/* 911244604Srmacklem * Try to determine if the file is a valid tgt cache file. 912244604Srmacklem * Check that the file has a valid tgt for a principal. 913244604Srmacklem * If it does, return 1, otherwise return 0. 914244604Srmacklem * It also returns a "rating" and the expiry time for the TGT, when found. 915244604Srmacklem * This "rating" is higher based on heuristics that make it more 916244604Srmacklem * likely to be the correct credential cache file to use. It can 917244604Srmacklem * be used by the caller, along with expiry time, to select from 918244604Srmacklem * multiple credential cache files. 919244604Srmacklem */ 920244604Srmacklemstatic int 921244604Srmacklemis_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating, 922244604Srmacklem time_t *retexptime) 923244604Srmacklem{ 924245014Srmacklem#ifndef WITHOUT_KERBEROS 925244604Srmacklem krb5_context context; 926244604Srmacklem krb5_principal princ; 927244604Srmacklem krb5_ccache ccache; 928244604Srmacklem krb5_error_code retval; 929244604Srmacklem krb5_cc_cursor curse; 930244604Srmacklem krb5_creds krbcred; 931244604Srmacklem int gotone, orating, rating, ret; 932244604Srmacklem struct passwd *pw; 933244604Srmacklem char *cp, *cp2, *pname; 934244604Srmacklem time_t exptime; 935244604Srmacklem 936244604Srmacklem /* Find a likely name for the uid principal. */ 937244604Srmacklem pw = getpwuid(uid); 938244604Srmacklem 939244604Srmacklem /* 940244604Srmacklem * Do a bunch of krb5 library stuff to try and determine if 941244604Srmacklem * this file is a credentials cache with an appropriate TGT 942244604Srmacklem * in it. 943244604Srmacklem */ 944244604Srmacklem retval = krb5_init_context(&context); 945244604Srmacklem if (retval != 0) 946244604Srmacklem return (0); 947244604Srmacklem retval = krb5_cc_resolve(context, filepath, &ccache); 948244604Srmacklem if (retval != 0) { 949244604Srmacklem krb5_free_context(context); 950244604Srmacklem return (0); 951244604Srmacklem } 952244604Srmacklem ret = 0; 953244604Srmacklem orating = 0; 954244604Srmacklem exptime = 0; 955244604Srmacklem retval = krb5_cc_start_seq_get(context, ccache, &curse); 956244604Srmacklem if (retval == 0) { 957244604Srmacklem while ((retval = krb5_cc_next_cred(context, ccache, &curse, 958244604Srmacklem &krbcred)) == 0) { 959244604Srmacklem gotone = 0; 960244604Srmacklem rating = 0; 961244604Srmacklem retval = krb5_unparse_name(context, krbcred.server, 962244604Srmacklem &pname); 963244604Srmacklem if (retval == 0) { 964244604Srmacklem cp = strchr(pname, '/'); 965244604Srmacklem if (cp != NULL) { 966244604Srmacklem *cp++ = '\0'; 967244604Srmacklem if (strcmp(pname, "krbtgt") == 0 && 968244604Srmacklem krbcred.times.endtime > time(NULL) 969244604Srmacklem ) { 970244604Srmacklem gotone = 1; 971244604Srmacklem /* 972244604Srmacklem * Test to see if this is a 973244604Srmacklem * tgt for cross-realm auth. 974244604Srmacklem * Rate it higher, if it is not. 975244604Srmacklem */ 976244604Srmacklem cp2 = strchr(cp, '@'); 977244604Srmacklem if (cp2 != NULL) { 978244604Srmacklem *cp2++ = '\0'; 979244604Srmacklem if (strcmp(cp, cp2) == 980244604Srmacklem 0) 981244604Srmacklem rating++; 982244604Srmacklem } 983244604Srmacklem } 984244604Srmacklem } 985244604Srmacklem free(pname); 986244604Srmacklem } 987244604Srmacklem if (gotone != 0) { 988244604Srmacklem retval = krb5_unparse_name(context, 989244604Srmacklem krbcred.client, &pname); 990244604Srmacklem if (retval == 0) { 991244604Srmacklem cp = strchr(pname, '@'); 992244604Srmacklem if (cp != NULL) { 993244604Srmacklem *cp++ = '\0'; 994244604Srmacklem if (pw != NULL && strcmp(pname, 995244604Srmacklem pw->pw_name) == 0) 996244604Srmacklem rating++; 997244604Srmacklem if (strchr(pname, '/') == NULL) 998244604Srmacklem rating++; 999244604Srmacklem if (pref_realm[0] != '\0' && 1000244604Srmacklem strcmp(cp, pref_realm) == 0) 1001244604Srmacklem rating++; 1002244604Srmacklem } 1003244604Srmacklem } 1004244604Srmacklem free(pname); 1005244604Srmacklem if (rating > orating) { 1006244604Srmacklem orating = rating; 1007244604Srmacklem exptime = krbcred.times.endtime; 1008244604Srmacklem } else if (rating == orating && 1009244604Srmacklem krbcred.times.endtime > exptime) 1010244604Srmacklem exptime = krbcred.times.endtime; 1011244604Srmacklem ret = 1; 1012244604Srmacklem } 1013244604Srmacklem krb5_free_cred_contents(context, &krbcred); 1014244604Srmacklem } 1015244604Srmacklem krb5_cc_end_seq_get(context, ccache, &curse); 1016244604Srmacklem } 1017244604Srmacklem krb5_cc_close(context, ccache); 1018244604Srmacklem krb5_free_context(context); 1019244604Srmacklem if (ret != 0) { 1020244604Srmacklem *retrating = orating; 1021244604Srmacklem *retexptime = exptime; 1022244604Srmacklem } 1023244604Srmacklem return (ret); 1024245014Srmacklem#else /* WITHOUT_KERBEROS */ 1025245014Srmacklem return (0); 1026245014Srmacklem#endif /* !WITHOUT_KERBEROS */ 1027244604Srmacklem} 1028244604Srmacklem 1029