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