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> 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> 45252138Srmacklem#include <signal.h> 46251476Srmacklem#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 63253015Srmacklem#define GSSD_CREDENTIAL_CACHE_FILE "/tmp/krb5cc_gssd" 64184588Sdfr 65184588Sdfrstruct gss_resource { 66184588Sdfr LIST_ENTRY(gss_resource) gr_link; 67184588Sdfr uint64_t gr_id; /* indentifier exported to kernel */ 68184588Sdfr void* gr_res; /* GSS-API resource pointer */ 69184588Sdfr}; 70184588SdfrLIST_HEAD(gss_resource_list, gss_resource) gss_resources; 71184588Sdfrint gss_resource_count; 72184588Sdfruint32_t gss_next_id; 73184588Sdfruint32_t gss_start_time; 74184588Sdfrint debug_level; 75244604Srmacklemstatic char ccfile_dirlist[PATH_MAX + 1], ccfile_substring[NAME_MAX + 1]; 76244604Srmacklemstatic char pref_realm[1024]; 77251444Srmacklemstatic int verbose; 78252138Srmacklemstatic int use_old_des; 79253015Srmacklemstatic int hostbased_initiator_cred; 80252138Srmacklem#ifndef WITHOUT_KERBEROS 81252138Srmacklem/* 1.2.752.43.13.14 */ 82252138Srmacklemstatic gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc = 83252138Srmacklem{6, (void *) "\x2a\x85\x70\x2b\x0d\x0e"}; 84252138Srmacklemstatic gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X = 85252138Srmacklem &gss_krb5_set_allowable_enctypes_x_desc; 86252138Srmacklemstatic gss_OID_desc gss_krb5_mech_oid_x_desc = 87252138Srmacklem{9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; 88252138Srmacklemstatic gss_OID GSS_KRB5_MECH_OID_X = 89252138Srmacklem &gss_krb5_mech_oid_x_desc; 90252138Srmacklem#endif 91184588Sdfr 92184588Sdfrstatic void gssd_load_mech(void); 93244604Srmacklemstatic int find_ccache_file(const char *, uid_t, char *); 94244604Srmacklemstatic int is_a_valid_tgt_cache(const char *, uid_t, int *, time_t *); 95251444Srmacklemstatic void gssd_verbose_out(const char *, ...); 96252138Srmacklem#ifndef WITHOUT_KERBEROS 97253015Srmacklemstatic krb5_error_code gssd_get_cc_from_keytab(const char *); 98252138Srmacklemstatic OM_uint32 gssd_get_user_cred(OM_uint32 *, uid_t, gss_cred_id_t *); 99252138Srmacklem#endif 100253015Srmacklemvoid gssd_terminate(int); 101184588Sdfr 102184588Sdfrextern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp); 103184588Sdfrextern int gssd_syscall(char *path); 104184588Sdfr 105184588Sdfrint 106184588Sdfrmain(int argc, char **argv) 107184588Sdfr{ 108184588Sdfr /* 109184588Sdfr * We provide an RPC service on a local-domain socket. The 110184588Sdfr * kernel's GSS-API code will pass what it can't handle 111184588Sdfr * directly to us. 112184588Sdfr */ 113184588Sdfr struct sockaddr_un sun; 114184588Sdfr int fd, oldmask, ch, debug; 115184588Sdfr SVCXPRT *xprt; 116184588Sdfr 117244604Srmacklem /* 118244604Srmacklem * Initialize the credential cache file name substring and the 119244604Srmacklem * search directory list. 120244604Srmacklem */ 121244604Srmacklem strlcpy(ccfile_substring, "krb5cc_", sizeof(ccfile_substring)); 122244604Srmacklem ccfile_dirlist[0] = '\0'; 123244604Srmacklem pref_realm[0] = '\0'; 124184588Sdfr debug = 0; 125251444Srmacklem verbose = 0; 126253015Srmacklem while ((ch = getopt(argc, argv, "dhovs:c:r:")) != -1) { 127184588Sdfr switch (ch) { 128184588Sdfr case 'd': 129184588Sdfr debug_level++; 130184588Sdfr break; 131253015Srmacklem case 'h': 132253015Srmacklem#ifndef WITHOUT_KERBEROS 133253015Srmacklem /* 134253015Srmacklem * Enable use of a host based initiator credential 135253015Srmacklem * in the default keytab file. 136253015Srmacklem */ 137253015Srmacklem hostbased_initiator_cred = 1; 138253015Srmacklem#else 139253015Srmacklem errx(1, "This option not available when built" 140253015Srmacklem " without MK_KERBEROS\n"); 141253015Srmacklem#endif 142253015Srmacklem break; 143252138Srmacklem case 'o': 144252138Srmacklem#ifndef WITHOUT_KERBEROS 145252138Srmacklem /* 146252138Srmacklem * Force use of DES and the old type of GSSAPI token. 147252138Srmacklem */ 148252138Srmacklem use_old_des = 1; 149252138Srmacklem#else 150252138Srmacklem errx(1, "This option not available when built" 151252138Srmacklem " without MK_KERBEROS\n"); 152252138Srmacklem#endif 153252138Srmacklem break; 154251444Srmacklem case 'v': 155251444Srmacklem verbose = 1; 156251444Srmacklem break; 157244604Srmacklem case 's': 158245014Srmacklem#ifndef WITHOUT_KERBEROS 159244604Srmacklem /* 160244604Srmacklem * Set the directory search list. This enables use of 161244604Srmacklem * find_ccache_file() to search the directories for a 162244604Srmacklem * suitable credentials cache file. 163244604Srmacklem */ 164244604Srmacklem strlcpy(ccfile_dirlist, optarg, sizeof(ccfile_dirlist)); 165245014Srmacklem#else 166245014Srmacklem errx(1, "This option not available when built" 167245014Srmacklem " without MK_KERBEROS\n"); 168245014Srmacklem#endif 169244604Srmacklem break; 170244604Srmacklem case 'c': 171244604Srmacklem /* 172244604Srmacklem * Specify a non-default credential cache file 173244604Srmacklem * substring. 174244604Srmacklem */ 175244604Srmacklem strlcpy(ccfile_substring, optarg, 176244604Srmacklem sizeof(ccfile_substring)); 177244604Srmacklem break; 178244604Srmacklem case 'r': 179244604Srmacklem /* 180244604Srmacklem * Set the preferred realm for the credential cache tgt. 181244604Srmacklem */ 182244604Srmacklem strlcpy(pref_realm, optarg, sizeof(pref_realm)); 183244604Srmacklem break; 184184588Sdfr default: 185244604Srmacklem fprintf(stderr, 186244604Srmacklem "usage: %s [-d] [-s dir-list] [-c file-substring]" 187244604Srmacklem " [-r preferred-realm]\n", argv[0]); 188184588Sdfr exit(1); 189184588Sdfr break; 190184588Sdfr } 191184588Sdfr } 192184588Sdfr 193184588Sdfr gssd_load_mech(); 194184588Sdfr 195252138Srmacklem if (!debug_level) { 196184588Sdfr daemon(0, 0); 197252138Srmacklem signal(SIGINT, SIG_IGN); 198252138Srmacklem signal(SIGQUIT, SIG_IGN); 199252138Srmacklem signal(SIGHUP, SIG_IGN); 200252138Srmacklem } 201253015Srmacklem signal(SIGTERM, gssd_terminate); 202184588Sdfr 203184588Sdfr memset(&sun, 0, sizeof sun); 204184588Sdfr sun.sun_family = AF_LOCAL; 205184588Sdfr unlink(_PATH_GSSDSOCK); 206184588Sdfr strcpy(sun.sun_path, _PATH_GSSDSOCK); 207184588Sdfr sun.sun_len = SUN_LEN(&sun); 208184588Sdfr fd = socket(AF_LOCAL, SOCK_STREAM, 0); 209184588Sdfr if (!fd) { 210244331Srmacklem if (debug_level == 0) { 211244331Srmacklem syslog(LOG_ERR, "Can't create local gssd socket"); 212244331Srmacklem exit(1); 213244331Srmacklem } 214184588Sdfr err(1, "Can't create local gssd socket"); 215184588Sdfr } 216184588Sdfr oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 217184588Sdfr if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) { 218244331Srmacklem if (debug_level == 0) { 219244331Srmacklem syslog(LOG_ERR, "Can't bind local gssd socket"); 220244331Srmacklem exit(1); 221244331Srmacklem } 222184588Sdfr err(1, "Can't bind local gssd socket"); 223184588Sdfr } 224184588Sdfr umask(oldmask); 225184588Sdfr if (listen(fd, SOMAXCONN) < 0) { 226244331Srmacklem if (debug_level == 0) { 227244331Srmacklem syslog(LOG_ERR, "Can't listen on local gssd socket"); 228244331Srmacklem exit(1); 229244331Srmacklem } 230184588Sdfr err(1, "Can't listen on local gssd socket"); 231184588Sdfr } 232184588Sdfr xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 233184588Sdfr if (!xprt) { 234244331Srmacklem if (debug_level == 0) { 235244331Srmacklem syslog(LOG_ERR, 236244331Srmacklem "Can't create transport for local gssd socket"); 237244331Srmacklem exit(1); 238244331Srmacklem } 239184588Sdfr err(1, "Can't create transport for local gssd socket"); 240184588Sdfr } 241184588Sdfr if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) { 242244331Srmacklem if (debug_level == 0) { 243244331Srmacklem syslog(LOG_ERR, 244244331Srmacklem "Can't register service for local gssd socket"); 245244331Srmacklem exit(1); 246244331Srmacklem } 247184588Sdfr err(1, "Can't register service for local gssd socket"); 248184588Sdfr } 249184588Sdfr 250184588Sdfr LIST_INIT(&gss_resources); 251184588Sdfr gss_next_id = 1; 252184588Sdfr gss_start_time = time(0); 253184588Sdfr 254184588Sdfr gssd_syscall(_PATH_GSSDSOCK); 255184588Sdfr svc_run(); 256293446Sjpaetzel gssd_syscall(""); 257184588Sdfr 258184588Sdfr return (0); 259184588Sdfr} 260184588Sdfr 261184588Sdfrstatic void 262184588Sdfrgssd_load_mech(void) 263184588Sdfr{ 264184588Sdfr FILE *fp; 265184588Sdfr char buf[256]; 266184588Sdfr char *p; 267184588Sdfr char *name, *oid, *lib, *kobj; 268184588Sdfr 269184588Sdfr fp = fopen(_PATH_GSS_MECH, "r"); 270184588Sdfr if (!fp) 271184588Sdfr return; 272184588Sdfr 273184588Sdfr while (fgets(buf, sizeof(buf), fp)) { 274184588Sdfr if (*buf == '#') 275184588Sdfr continue; 276184588Sdfr p = buf; 277184588Sdfr name = strsep(&p, "\t\n "); 278184588Sdfr if (p) while (isspace(*p)) p++; 279184588Sdfr oid = strsep(&p, "\t\n "); 280184588Sdfr if (p) while (isspace(*p)) p++; 281184588Sdfr lib = strsep(&p, "\t\n "); 282184588Sdfr if (p) while (isspace(*p)) p++; 283184588Sdfr kobj = strsep(&p, "\t\n "); 284184588Sdfr if (!name || !oid || !lib || !kobj) 285184588Sdfr continue; 286184588Sdfr 287184588Sdfr if (strcmp(kobj, "-")) { 288184588Sdfr /* 289184588Sdfr * Attempt to load the kernel module if its 290184588Sdfr * not already present. 291184588Sdfr */ 292184588Sdfr if (modfind(kobj) < 0) { 293184588Sdfr if (kldload(kobj) < 0) { 294184588Sdfr fprintf(stderr, 295184588Sdfr "%s: can't find or load kernel module %s for %s\n", 296184588Sdfr getprogname(), kobj, name); 297184588Sdfr } 298184588Sdfr } 299184588Sdfr } 300184588Sdfr } 301184588Sdfr fclose(fp); 302184588Sdfr} 303184588Sdfr 304184588Sdfrstatic void * 305184588Sdfrgssd_find_resource(uint64_t id) 306184588Sdfr{ 307184588Sdfr struct gss_resource *gr; 308184588Sdfr 309184588Sdfr if (!id) 310184588Sdfr return (NULL); 311184588Sdfr 312184588Sdfr LIST_FOREACH(gr, &gss_resources, gr_link) 313184588Sdfr if (gr->gr_id == id) 314184588Sdfr return (gr->gr_res); 315184588Sdfr 316184588Sdfr return (NULL); 317184588Sdfr} 318184588Sdfr 319184588Sdfrstatic uint64_t 320184588Sdfrgssd_make_resource(void *res) 321184588Sdfr{ 322184588Sdfr struct gss_resource *gr; 323184588Sdfr 324184588Sdfr if (!res) 325184588Sdfr return (0); 326184588Sdfr 327184588Sdfr gr = malloc(sizeof(struct gss_resource)); 328184588Sdfr if (!gr) 329184588Sdfr return (0); 330184588Sdfr gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32); 331184588Sdfr gr->gr_res = res; 332184588Sdfr LIST_INSERT_HEAD(&gss_resources, gr, gr_link); 333184588Sdfr gss_resource_count++; 334184588Sdfr if (debug_level > 1) 335184588Sdfr printf("%d resources allocated\n", gss_resource_count); 336184588Sdfr 337184588Sdfr return (gr->gr_id); 338184588Sdfr} 339184588Sdfr 340184588Sdfrstatic void 341184588Sdfrgssd_delete_resource(uint64_t id) 342184588Sdfr{ 343184588Sdfr struct gss_resource *gr; 344184588Sdfr 345184588Sdfr LIST_FOREACH(gr, &gss_resources, gr_link) { 346184588Sdfr if (gr->gr_id == id) { 347184588Sdfr LIST_REMOVE(gr, gr_link); 348184588Sdfr free(gr); 349184588Sdfr gss_resource_count--; 350184588Sdfr if (debug_level > 1) 351184588Sdfr printf("%d resources allocated\n", 352184588Sdfr gss_resource_count); 353184588Sdfr return; 354184588Sdfr } 355184588Sdfr } 356184588Sdfr} 357184588Sdfr 358251444Srmacklemstatic void 359251444Srmacklemgssd_verbose_out(const char *fmt, ...) 360251444Srmacklem{ 361251444Srmacklem va_list ap; 362251444Srmacklem 363251444Srmacklem if (verbose != 0) { 364251444Srmacklem va_start(ap, fmt); 365251444Srmacklem if (debug_level == 0) 366251444Srmacklem vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); 367251444Srmacklem else 368251444Srmacklem vfprintf(stderr, fmt, ap); 369251444Srmacklem va_end(ap); 370251444Srmacklem } 371251444Srmacklem} 372251444Srmacklem 373184588Sdfrbool_t 374184588Sdfrgssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) 375184588Sdfr{ 376184588Sdfr 377251444Srmacklem gssd_verbose_out("gssd_null: done\n"); 378184588Sdfr return (TRUE); 379184588Sdfr} 380184588Sdfr 381184588Sdfrbool_t 382184588Sdfrgssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp) 383184588Sdfr{ 384184588Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 385184588Sdfr gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 386184588Sdfr gss_name_t name = GSS_C_NO_NAME; 387244604Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 388252138Srmacklem int gotone, gotcred; 389252138Srmacklem OM_uint32 min_stat; 390252138Srmacklem#ifndef WITHOUT_KERBEROS 391252138Srmacklem gss_buffer_desc principal_desc; 392252138Srmacklem char enctype[sizeof(uint32_t)]; 393252138Srmacklem int key_enctype; 394252138Srmacklem OM_uint32 maj_stat; 395252138Srmacklem#endif 396184588Sdfr 397244604Srmacklem memset(result, 0, sizeof(*result)); 398253015Srmacklem if (hostbased_initiator_cred != 0 && argp->cred != 0 && 399253015Srmacklem argp->uid == 0) { 400244604Srmacklem /* 401253015Srmacklem * These credentials are for a host based initiator name 402253015Srmacklem * in a keytab file, which should now have credentials 403253015Srmacklem * in /tmp/krb5cc_gssd, because gss_acquire_cred() did 404253015Srmacklem * the equivalent of "kinit -k". 405253015Srmacklem */ 406253015Srmacklem snprintf(ccname, sizeof(ccname), "FILE:%s", 407253015Srmacklem GSSD_CREDENTIAL_CACHE_FILE); 408253015Srmacklem } else if (ccfile_dirlist[0] != '\0' && argp->cred == 0) { 409253015Srmacklem /* 410244604Srmacklem * For the "-s" case and no credentials provided as an 411244604Srmacklem * argument, search the directory list for an appropriate 412244604Srmacklem * credential cache file. If the search fails, return failure. 413244604Srmacklem */ 414244604Srmacklem gotone = 0; 415244604Srmacklem cp = ccfile_dirlist; 416244604Srmacklem do { 417244604Srmacklem cp2 = strchr(cp, ':'); 418244604Srmacklem if (cp2 != NULL) 419244604Srmacklem *cp2 = '\0'; 420244604Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 421244604Srmacklem if (gotone != 0) 422244604Srmacklem break; 423244604Srmacklem if (cp2 != NULL) 424244604Srmacklem *cp2++ = ':'; 425244604Srmacklem cp = cp2; 426244604Srmacklem } while (cp != NULL && *cp != '\0'); 427244604Srmacklem if (gotone == 0) { 428244604Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 429251444Srmacklem gssd_verbose_out("gssd_init_sec_context: -s no" 430251444Srmacklem " credential cache file found for uid=%d\n", 431251444Srmacklem (int)argp->uid); 432244604Srmacklem return (TRUE); 433244604Srmacklem } 434244604Srmacklem } else { 435244604Srmacklem /* 436244604Srmacklem * If there wasn't a "-s" option or the credentials have 437244604Srmacklem * been provided as an argument, do it the old way. 438244604Srmacklem * When credentials are provided, the uid should be root. 439244604Srmacklem */ 440244604Srmacklem if (argp->cred != 0 && argp->uid != 0) { 441244604Srmacklem if (debug_level == 0) 442244604Srmacklem syslog(LOG_ERR, "gss_init_sec_context:" 443244604Srmacklem " cred for non-root"); 444244604Srmacklem else 445244604Srmacklem fprintf(stderr, "gss_init_sec_context:" 446244604Srmacklem " cred for non-root\n"); 447244604Srmacklem } 448244604Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 449244604Srmacklem (int) argp->uid); 450244604Srmacklem } 451184588Sdfr setenv("KRB5CCNAME", ccname, TRUE); 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_init_sec_context: cred" 458251444Srmacklem " resource not found\n"); 459184588Sdfr return (TRUE); 460184588Sdfr } 461184588Sdfr } 462184588Sdfr if (argp->ctx) { 463184588Sdfr ctx = gssd_find_resource(argp->ctx); 464184588Sdfr if (!ctx) { 465184588Sdfr result->major_status = GSS_S_CONTEXT_EXPIRED; 466251444Srmacklem gssd_verbose_out("gssd_init_sec_context: context" 467251444Srmacklem " resource not found\n"); 468184588Sdfr return (TRUE); 469184588Sdfr } 470184588Sdfr } 471184588Sdfr if (argp->name) { 472184588Sdfr name = gssd_find_resource(argp->name); 473184588Sdfr if (!name) { 474184588Sdfr result->major_status = GSS_S_BAD_NAME; 475251444Srmacklem gssd_verbose_out("gssd_init_sec_context: name" 476251444Srmacklem " resource not found\n"); 477184588Sdfr return (TRUE); 478184588Sdfr } 479184588Sdfr } 480252138Srmacklem gotcred = 0; 481184588Sdfr 482252138Srmacklem#ifndef WITHOUT_KERBEROS 483252138Srmacklem if (use_old_des != 0) { 484252138Srmacklem if (cred == GSS_C_NO_CREDENTIAL) { 485252138Srmacklem /* Acquire a credential for the uid. */ 486252138Srmacklem maj_stat = gssd_get_user_cred(&min_stat, argp->uid, 487252138Srmacklem &cred); 488252138Srmacklem if (maj_stat == GSS_S_COMPLETE) 489252138Srmacklem gotcred = 1; 490252138Srmacklem else 491252138Srmacklem gssd_verbose_out("gssd_init_sec_context: " 492252138Srmacklem "get user cred failed uid=%d major=0x%x " 493252138Srmacklem "minor=%d\n", (int)argp->uid, 494252138Srmacklem (unsigned int)maj_stat, (int)min_stat); 495252138Srmacklem } 496252138Srmacklem if (cred != GSS_C_NO_CREDENTIAL) { 497252138Srmacklem key_enctype = ETYPE_DES_CBC_CRC; 498252138Srmacklem enctype[0] = (key_enctype >> 24) & 0xff; 499252138Srmacklem enctype[1] = (key_enctype >> 16) & 0xff; 500252138Srmacklem enctype[2] = (key_enctype >> 8) & 0xff; 501252138Srmacklem enctype[3] = key_enctype & 0xff; 502252138Srmacklem principal_desc.length = sizeof(enctype); 503252138Srmacklem principal_desc.value = enctype; 504252138Srmacklem result->major_status = gss_set_cred_option( 505252138Srmacklem &result->minor_status, &cred, 506252138Srmacklem GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, 507252138Srmacklem &principal_desc); 508252138Srmacklem gssd_verbose_out("gssd_init_sec_context: set allowable " 509252138Srmacklem "enctype major=0x%x minor=%d\n", 510252138Srmacklem (unsigned int)result->major_status, 511252138Srmacklem (int)result->minor_status); 512252138Srmacklem if (result->major_status != GSS_S_COMPLETE) { 513252138Srmacklem if (gotcred != 0) 514252138Srmacklem gss_release_cred(&min_stat, &cred); 515252138Srmacklem return (TRUE); 516252138Srmacklem } 517252138Srmacklem } 518252138Srmacklem } 519252138Srmacklem#endif 520184588Sdfr result->major_status = gss_init_sec_context(&result->minor_status, 521184588Sdfr cred, &ctx, name, argp->mech_type, 522184588Sdfr argp->req_flags, argp->time_req, argp->input_chan_bindings, 523184588Sdfr &argp->input_token, &result->actual_mech_type, 524184588Sdfr &result->output_token, &result->ret_flags, &result->time_rec); 525251444Srmacklem gssd_verbose_out("gssd_init_sec_context: done major=0x%x minor=%d" 526251444Srmacklem " uid=%d\n", (unsigned int)result->major_status, 527251444Srmacklem (int)result->minor_status, (int)argp->uid); 528252138Srmacklem if (gotcred != 0) 529252138Srmacklem gss_release_cred(&min_stat, &cred); 530184588Sdfr 531184588Sdfr if (result->major_status == GSS_S_COMPLETE 532184588Sdfr || result->major_status == GSS_S_CONTINUE_NEEDED) { 533184588Sdfr if (argp->ctx) 534184588Sdfr result->ctx = argp->ctx; 535184588Sdfr else 536184588Sdfr result->ctx = gssd_make_resource(ctx); 537184588Sdfr } 538184588Sdfr 539184588Sdfr return (TRUE); 540184588Sdfr} 541184588Sdfr 542184588Sdfrbool_t 543184588Sdfrgssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp) 544184588Sdfr{ 545184588Sdfr gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 546184588Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 547184588Sdfr gss_name_t src_name; 548184588Sdfr gss_cred_id_t delegated_cred_handle; 549184588Sdfr 550184588Sdfr memset(result, 0, sizeof(*result)); 551184588Sdfr if (argp->ctx) { 552184588Sdfr ctx = gssd_find_resource(argp->ctx); 553184588Sdfr if (!ctx) { 554184588Sdfr result->major_status = GSS_S_CONTEXT_EXPIRED; 555251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: ctx" 556251444Srmacklem " resource not found\n"); 557184588Sdfr return (TRUE); 558184588Sdfr } 559184588Sdfr } 560184588Sdfr if (argp->cred) { 561184588Sdfr cred = gssd_find_resource(argp->cred); 562184588Sdfr if (!cred) { 563184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 564251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: cred" 565251444Srmacklem " resource not found\n"); 566184588Sdfr return (TRUE); 567184588Sdfr } 568184588Sdfr } 569184588Sdfr 570184588Sdfr memset(result, 0, sizeof(*result)); 571184588Sdfr result->major_status = gss_accept_sec_context(&result->minor_status, 572184588Sdfr &ctx, cred, &argp->input_token, argp->input_chan_bindings, 573184588Sdfr &src_name, &result->mech_type, &result->output_token, 574184588Sdfr &result->ret_flags, &result->time_rec, 575184588Sdfr &delegated_cred_handle); 576251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n", 577251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 578184588Sdfr 579184588Sdfr if (result->major_status == GSS_S_COMPLETE 580184588Sdfr || result->major_status == GSS_S_CONTINUE_NEEDED) { 581184588Sdfr if (argp->ctx) 582184588Sdfr result->ctx = argp->ctx; 583184588Sdfr else 584184588Sdfr result->ctx = gssd_make_resource(ctx); 585184588Sdfr result->src_name = gssd_make_resource(src_name); 586184588Sdfr result->delegated_cred_handle = 587184588Sdfr gssd_make_resource(delegated_cred_handle); 588184588Sdfr } 589184588Sdfr 590184588Sdfr return (TRUE); 591184588Sdfr} 592184588Sdfr 593184588Sdfrbool_t 594184588Sdfrgssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp) 595184588Sdfr{ 596184588Sdfr gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 597184588Sdfr 598184588Sdfr if (ctx) { 599184588Sdfr result->major_status = gss_delete_sec_context( 600184588Sdfr &result->minor_status, &ctx, &result->output_token); 601184588Sdfr gssd_delete_resource(argp->ctx); 602184588Sdfr } else { 603184588Sdfr result->major_status = GSS_S_COMPLETE; 604184588Sdfr result->minor_status = 0; 605184588Sdfr } 606251444Srmacklem gssd_verbose_out("gssd_delete_sec_context: done major=0x%x minor=%d\n", 607251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 608184588Sdfr 609184588Sdfr return (TRUE); 610184588Sdfr} 611184588Sdfr 612184588Sdfrbool_t 613184588Sdfrgssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp) 614184588Sdfr{ 615184588Sdfr gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 616184588Sdfr 617184588Sdfr if (ctx) { 618184588Sdfr result->major_status = gss_export_sec_context( 619184588Sdfr &result->minor_status, &ctx, 620184588Sdfr &result->interprocess_token); 621184588Sdfr result->format = KGSS_HEIMDAL_1_1; 622184588Sdfr gssd_delete_resource(argp->ctx); 623184588Sdfr } else { 624184588Sdfr result->major_status = GSS_S_FAILURE; 625184588Sdfr result->minor_status = 0; 626184588Sdfr result->interprocess_token.length = 0; 627184588Sdfr result->interprocess_token.value = NULL; 628184588Sdfr } 629251444Srmacklem gssd_verbose_out("gssd_export_sec_context: done major=0x%x minor=%d\n", 630251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 631184588Sdfr 632184588Sdfr return (TRUE); 633184588Sdfr} 634184588Sdfr 635184588Sdfrbool_t 636184588Sdfrgssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp) 637184588Sdfr{ 638184588Sdfr gss_name_t name; 639184588Sdfr 640184588Sdfr result->major_status = gss_import_name(&result->minor_status, 641184588Sdfr &argp->input_name_buffer, argp->input_name_type, &name); 642251444Srmacklem gssd_verbose_out("gssd_import_name: done major=0x%x minor=%d\n", 643251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 644184588Sdfr 645184588Sdfr if (result->major_status == GSS_S_COMPLETE) 646184588Sdfr result->output_name = gssd_make_resource(name); 647184588Sdfr else 648184588Sdfr result->output_name = 0; 649184588Sdfr 650184588Sdfr return (TRUE); 651184588Sdfr} 652184588Sdfr 653184588Sdfrbool_t 654184588Sdfrgssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp) 655184588Sdfr{ 656184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 657184588Sdfr gss_name_t output_name; 658184588Sdfr 659184588Sdfr memset(result, 0, sizeof(*result)); 660184588Sdfr if (!name) { 661184588Sdfr result->major_status = GSS_S_BAD_NAME; 662184588Sdfr return (TRUE); 663184588Sdfr } 664184588Sdfr 665184588Sdfr result->major_status = gss_canonicalize_name(&result->minor_status, 666184588Sdfr name, argp->mech_type, &output_name); 667251444Srmacklem gssd_verbose_out("gssd_canonicalize_name: done major=0x%x minor=%d\n", 668251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 669184588Sdfr 670184588Sdfr if (result->major_status == GSS_S_COMPLETE) 671184588Sdfr result->output_name = gssd_make_resource(output_name); 672184588Sdfr else 673184588Sdfr result->output_name = 0; 674184588Sdfr 675184588Sdfr return (TRUE); 676184588Sdfr} 677184588Sdfr 678184588Sdfrbool_t 679184588Sdfrgssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp) 680184588Sdfr{ 681184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 682184588Sdfr 683184588Sdfr memset(result, 0, sizeof(*result)); 684184588Sdfr if (!name) { 685184588Sdfr result->major_status = GSS_S_BAD_NAME; 686251444Srmacklem gssd_verbose_out("gssd_export_name: name resource not found\n"); 687184588Sdfr return (TRUE); 688184588Sdfr } 689184588Sdfr 690184588Sdfr result->major_status = gss_export_name(&result->minor_status, 691184588Sdfr name, &result->exported_name); 692251444Srmacklem gssd_verbose_out("gssd_export_name: done major=0x%x minor=%d\n", 693251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 694184588Sdfr 695184588Sdfr return (TRUE); 696184588Sdfr} 697184588Sdfr 698184588Sdfrbool_t 699184588Sdfrgssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp) 700184588Sdfr{ 701184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 702184588Sdfr 703184588Sdfr if (name) { 704184588Sdfr result->major_status = gss_release_name(&result->minor_status, 705184588Sdfr &name); 706184588Sdfr gssd_delete_resource(argp->input_name); 707184588Sdfr } else { 708184588Sdfr result->major_status = GSS_S_COMPLETE; 709184588Sdfr result->minor_status = 0; 710184588Sdfr } 711251444Srmacklem gssd_verbose_out("gssd_release_name: done major=0x%x minor=%d\n", 712251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 713184588Sdfr 714184588Sdfr return (TRUE); 715184588Sdfr} 716184588Sdfr 717184588Sdfrbool_t 718184588Sdfrgssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp) 719184588Sdfr{ 720184588Sdfr gss_name_t name = gssd_find_resource(argp->pname); 721184588Sdfr uid_t uid; 722250176Srmacklem char buf[1024], *bufp; 723184588Sdfr struct passwd pwd, *pw; 724250176Srmacklem size_t buflen; 725250176Srmacklem int error; 726250176Srmacklem static size_t buflen_hint = 1024; 727184588Sdfr 728184588Sdfr memset(result, 0, sizeof(*result)); 729184588Sdfr if (name) { 730184588Sdfr result->major_status = 731184588Sdfr gss_pname_to_uid(&result->minor_status, 732184588Sdfr name, argp->mech, &uid); 733184588Sdfr if (result->major_status == GSS_S_COMPLETE) { 734184588Sdfr result->uid = uid; 735250176Srmacklem buflen = buflen_hint; 736250176Srmacklem for (;;) { 737250176Srmacklem pw = NULL; 738250176Srmacklem bufp = buf; 739250176Srmacklem if (buflen > sizeof(buf)) 740250176Srmacklem bufp = malloc(buflen); 741250176Srmacklem if (bufp == NULL) 742250176Srmacklem break; 743250176Srmacklem error = getpwuid_r(uid, &pwd, bufp, buflen, 744250176Srmacklem &pw); 745250176Srmacklem if (error != ERANGE) 746250176Srmacklem break; 747250176Srmacklem if (buflen > sizeof(buf)) 748250176Srmacklem free(bufp); 749250176Srmacklem buflen += 1024; 750250176Srmacklem if (buflen > buflen_hint) 751250176Srmacklem buflen_hint = buflen; 752250176Srmacklem } 753184588Sdfr if (pw) { 754288827Sjpaetzel int len = NGROUPS; 755288827Sjpaetzel int groups[NGROUPS]; 756184588Sdfr result->gid = pw->pw_gid; 757184588Sdfr getgrouplist(pw->pw_name, pw->pw_gid, 758184588Sdfr groups, &len); 759184588Sdfr result->gidlist.gidlist_len = len; 760184588Sdfr result->gidlist.gidlist_val = 761184588Sdfr mem_alloc(len * sizeof(int)); 762184588Sdfr memcpy(result->gidlist.gidlist_val, groups, 763184588Sdfr len * sizeof(int)); 764251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: mapped" 765251444Srmacklem " to uid=%d, gid=%d\n", (int)result->uid, 766251444Srmacklem (int)result->gid); 767184588Sdfr } else { 768184588Sdfr result->gid = 65534; 769184588Sdfr result->gidlist.gidlist_len = 0; 770184588Sdfr result->gidlist.gidlist_val = NULL; 771251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: mapped" 772251444Srmacklem " to uid=%d, but no groups\n", 773251444Srmacklem (int)result->uid); 774184588Sdfr } 775250176Srmacklem if (bufp != NULL && buflen > sizeof(buf)) 776250176Srmacklem free(bufp); 777251444Srmacklem } else 778251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x" 779251444Srmacklem " minor=%d\n", (unsigned int)result->major_status, 780251444Srmacklem (int)result->minor_status); 781184588Sdfr } else { 782184588Sdfr result->major_status = GSS_S_BAD_NAME; 783184588Sdfr result->minor_status = 0; 784251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: no name\n"); 785184588Sdfr } 786184588Sdfr 787184588Sdfr return (TRUE); 788184588Sdfr} 789184588Sdfr 790184588Sdfrbool_t 791184588Sdfrgssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp) 792184588Sdfr{ 793184588Sdfr gss_name_t desired_name = GSS_C_NO_NAME; 794184588Sdfr gss_cred_id_t cred; 795244604Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 796244604Srmacklem int gotone; 797252138Srmacklem#ifndef WITHOUT_KERBEROS 798252138Srmacklem gss_buffer_desc namebuf; 799252138Srmacklem uint32_t minstat; 800252138Srmacklem krb5_error_code kret; 801252138Srmacklem#endif 802184588Sdfr 803244604Srmacklem memset(result, 0, sizeof(*result)); 804253015Srmacklem if (argp->desired_name) { 805253015Srmacklem desired_name = gssd_find_resource(argp->desired_name); 806253015Srmacklem if (!desired_name) { 807253015Srmacklem result->major_status = GSS_S_BAD_NAME; 808253015Srmacklem gssd_verbose_out("gssd_acquire_cred: no desired name" 809253015Srmacklem " found\n"); 810253015Srmacklem return (TRUE); 811253015Srmacklem } 812253015Srmacklem } 813253015Srmacklem 814253015Srmacklem#ifndef WITHOUT_KERBEROS 815253015Srmacklem if (hostbased_initiator_cred != 0 && argp->desired_name != 0 && 816253015Srmacklem argp->uid == 0 && argp->cred_usage == GSS_C_INITIATE) { 817253015Srmacklem /* This is a host based initiator name in the keytab file. */ 818253015Srmacklem snprintf(ccname, sizeof(ccname), "FILE:%s", 819253015Srmacklem GSSD_CREDENTIAL_CACHE_FILE); 820253015Srmacklem setenv("KRB5CCNAME", ccname, TRUE); 821253015Srmacklem result->major_status = gss_display_name(&result->minor_status, 822253015Srmacklem desired_name, &namebuf, NULL); 823253015Srmacklem gssd_verbose_out("gssd_acquire_cred: desired name for host " 824253015Srmacklem "based initiator cred major=0x%x minor=%d\n", 825253015Srmacklem (unsigned int)result->major_status, 826253015Srmacklem (int)result->minor_status); 827253015Srmacklem if (result->major_status != GSS_S_COMPLETE) 828253015Srmacklem return (TRUE); 829253015Srmacklem if (namebuf.length > PATH_MAX + 5) { 830253015Srmacklem result->minor_status = 0; 831253015Srmacklem result->major_status = GSS_S_FAILURE; 832253015Srmacklem return (TRUE); 833253015Srmacklem } 834253015Srmacklem memcpy(ccname, namebuf.value, namebuf.length); 835253015Srmacklem ccname[namebuf.length] = '\0'; 836253015Srmacklem if ((cp = strchr(ccname, '@')) != NULL) 837253015Srmacklem *cp = '/'; 838253015Srmacklem kret = gssd_get_cc_from_keytab(ccname); 839253015Srmacklem gssd_verbose_out("gssd_acquire_cred: using keytab entry for " 840253015Srmacklem "%s, kerberos ret=%d\n", ccname, (int)kret); 841253015Srmacklem gss_release_buffer(&minstat, &namebuf); 842253015Srmacklem if (kret != 0) { 843253015Srmacklem result->minor_status = kret; 844253015Srmacklem result->major_status = GSS_S_FAILURE; 845253015Srmacklem return (TRUE); 846253015Srmacklem } 847253015Srmacklem } else 848253015Srmacklem#endif /* !WITHOUT_KERBEROS */ 849244604Srmacklem if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) { 850244604Srmacklem /* 851244604Srmacklem * For the "-s" case and no name provided as an 852244604Srmacklem * argument, search the directory list for an appropriate 853244604Srmacklem * credential cache file. If the search fails, return failure. 854244604Srmacklem */ 855244604Srmacklem gotone = 0; 856244604Srmacklem cp = ccfile_dirlist; 857244604Srmacklem do { 858244604Srmacklem cp2 = strchr(cp, ':'); 859244604Srmacklem if (cp2 != NULL) 860244604Srmacklem *cp2 = '\0'; 861244604Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 862244604Srmacklem if (gotone != 0) 863244604Srmacklem break; 864244604Srmacklem if (cp2 != NULL) 865244604Srmacklem *cp2++ = ':'; 866244604Srmacklem cp = cp2; 867244604Srmacklem } while (cp != NULL && *cp != '\0'); 868244604Srmacklem if (gotone == 0) { 869244604Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 870251444Srmacklem gssd_verbose_out("gssd_acquire_cred: no cred cache" 871251444Srmacklem " file found\n"); 872244604Srmacklem return (TRUE); 873244604Srmacklem } 874253015Srmacklem setenv("KRB5CCNAME", ccname, TRUE); 875244604Srmacklem } else { 876244604Srmacklem /* 877244604Srmacklem * If there wasn't a "-s" option or the name has 878244604Srmacklem * been provided as an argument, do it the old way. 879244604Srmacklem * When a name is provided, it will normally exist in the 880244604Srmacklem * default keytab file and the uid will be root. 881244604Srmacklem */ 882244604Srmacklem if (argp->desired_name != 0 && argp->uid != 0) { 883244604Srmacklem if (debug_level == 0) 884244604Srmacklem syslog(LOG_ERR, "gss_acquire_cred:" 885244604Srmacklem " principal_name for non-root"); 886244604Srmacklem else 887244604Srmacklem fprintf(stderr, "gss_acquire_cred:" 888244604Srmacklem " principal_name for non-root\n"); 889244604Srmacklem } 890244604Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 891244604Srmacklem (int) argp->uid); 892253015Srmacklem setenv("KRB5CCNAME", ccname, TRUE); 893244604Srmacklem } 894184588Sdfr 895184588Sdfr result->major_status = gss_acquire_cred(&result->minor_status, 896184588Sdfr desired_name, argp->time_req, argp->desired_mechs, 897184588Sdfr argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec); 898251444Srmacklem gssd_verbose_out("gssd_acquire_cred: done major=0x%x minor=%d\n", 899251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 900184588Sdfr 901184588Sdfr if (result->major_status == GSS_S_COMPLETE) 902184588Sdfr result->output_cred = gssd_make_resource(cred); 903184588Sdfr else 904184588Sdfr result->output_cred = 0; 905184588Sdfr 906184588Sdfr return (TRUE); 907184588Sdfr} 908184588Sdfr 909184588Sdfrbool_t 910184588Sdfrgssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp) 911184588Sdfr{ 912184588Sdfr gss_cred_id_t cred = gssd_find_resource(argp->cred); 913184588Sdfr 914184588Sdfr memset(result, 0, sizeof(*result)); 915184588Sdfr if (!cred) { 916184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 917251444Srmacklem gssd_verbose_out("gssd_set_cred: no credentials\n"); 918184588Sdfr return (TRUE); 919184588Sdfr } 920184588Sdfr 921184588Sdfr result->major_status = gss_set_cred_option(&result->minor_status, 922184588Sdfr &cred, argp->option_name, &argp->option_value); 923251444Srmacklem gssd_verbose_out("gssd_set_cred: done major=0x%x minor=%d\n", 924251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 925184588Sdfr 926184588Sdfr return (TRUE); 927184588Sdfr} 928184588Sdfr 929184588Sdfrbool_t 930184588Sdfrgssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp) 931184588Sdfr{ 932184588Sdfr gss_cred_id_t cred = gssd_find_resource(argp->cred); 933184588Sdfr 934184588Sdfr if (cred) { 935184588Sdfr result->major_status = gss_release_cred(&result->minor_status, 936184588Sdfr &cred); 937184588Sdfr gssd_delete_resource(argp->cred); 938184588Sdfr } else { 939184588Sdfr result->major_status = GSS_S_COMPLETE; 940184588Sdfr result->minor_status = 0; 941184588Sdfr } 942251444Srmacklem gssd_verbose_out("gssd_release_cred: done major=0x%x minor=%d\n", 943251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 944184588Sdfr 945184588Sdfr return (TRUE); 946184588Sdfr} 947184588Sdfr 948184588Sdfrbool_t 949184588Sdfrgssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp) 950184588Sdfr{ 951184588Sdfr 952184588Sdfr result->message_context = argp->message_context; 953184588Sdfr result->major_status = gss_display_status(&result->minor_status, 954184588Sdfr argp->status_value, argp->status_type, argp->mech_type, 955184588Sdfr &result->message_context, &result->status_string); 956251444Srmacklem gssd_verbose_out("gssd_display_status: done major=0x%x minor=%d\n", 957251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 958184588Sdfr 959184588Sdfr return (TRUE); 960184588Sdfr} 961184588Sdfr 962184588Sdfrint 963184588Sdfrgssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) 964184588Sdfr{ 965184588Sdfr /* 966184588Sdfr * We don't use XDR to free the results - anything which was 967184588Sdfr * allocated came from GSS-API. We use xdr_result to figure 968184588Sdfr * out what to do. 969184588Sdfr */ 970184588Sdfr OM_uint32 junk; 971184588Sdfr 972184588Sdfr if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) { 973184588Sdfr init_sec_context_res *p = (init_sec_context_res *) result; 974184588Sdfr gss_release_buffer(&junk, &p->output_token); 975184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) { 976184588Sdfr accept_sec_context_res *p = (accept_sec_context_res *) result; 977184588Sdfr gss_release_buffer(&junk, &p->output_token); 978184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) { 979184588Sdfr delete_sec_context_res *p = (delete_sec_context_res *) result; 980184588Sdfr gss_release_buffer(&junk, &p->output_token); 981184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) { 982184588Sdfr export_sec_context_res *p = (export_sec_context_res *) result; 983184588Sdfr if (p->interprocess_token.length) 984184588Sdfr memset(p->interprocess_token.value, 0, 985184588Sdfr p->interprocess_token.length); 986184588Sdfr gss_release_buffer(&junk, &p->interprocess_token); 987184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_export_name_res) { 988184588Sdfr export_name_res *p = (export_name_res *) result; 989184588Sdfr gss_release_buffer(&junk, &p->exported_name); 990184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) { 991184588Sdfr acquire_cred_res *p = (acquire_cred_res *) result; 992184588Sdfr gss_release_oid_set(&junk, &p->actual_mechs); 993184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) { 994184588Sdfr pname_to_uid_res *p = (pname_to_uid_res *) result; 995184588Sdfr if (p->gidlist.gidlist_val) 996184588Sdfr free(p->gidlist.gidlist_val); 997184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_display_status_res) { 998184588Sdfr display_status_res *p = (display_status_res *) result; 999184588Sdfr gss_release_buffer(&junk, &p->status_string); 1000184588Sdfr } 1001184588Sdfr 1002184588Sdfr return (TRUE); 1003184588Sdfr} 1004244604Srmacklem 1005244604Srmacklem/* 1006244604Srmacklem * Search a directory for the most likely candidate to be used as the 1007244604Srmacklem * credential cache for a uid. If successful, return 1 and fill the 1008244604Srmacklem * file's path id into "rpath". Otherwise, return 0. 1009244604Srmacklem */ 1010244604Srmacklemstatic int 1011244604Srmacklemfind_ccache_file(const char *dirpath, uid_t uid, char *rpath) 1012244604Srmacklem{ 1013244604Srmacklem DIR *dirp; 1014244604Srmacklem struct dirent *dp; 1015244604Srmacklem struct stat sb; 1016244604Srmacklem time_t exptime, oexptime; 1017244604Srmacklem int gotone, len, rating, orating; 1018244604Srmacklem char namepath[PATH_MAX + 5 + 1]; 1019244604Srmacklem char retpath[PATH_MAX + 5 + 1]; 1020244604Srmacklem 1021244604Srmacklem dirp = opendir(dirpath); 1022244604Srmacklem if (dirp == NULL) 1023244604Srmacklem return (0); 1024244604Srmacklem gotone = 0; 1025244604Srmacklem orating = 0; 1026244604Srmacklem oexptime = 0; 1027244604Srmacklem while ((dp = readdir(dirp)) != NULL) { 1028244604Srmacklem len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath, 1029244604Srmacklem dp->d_name); 1030244604Srmacklem if (len < sizeof(namepath) && 1031253015Srmacklem (hostbased_initiator_cred == 0 || strcmp(namepath, 1032253015Srmacklem GSSD_CREDENTIAL_CACHE_FILE) != 0) && 1033244604Srmacklem strstr(dp->d_name, ccfile_substring) != NULL && 1034244604Srmacklem lstat(namepath, &sb) >= 0 && 1035244604Srmacklem sb.st_uid == uid && 1036244604Srmacklem S_ISREG(sb.st_mode)) { 1037244604Srmacklem len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s", 1038244604Srmacklem dirpath, dp->d_name); 1039244604Srmacklem if (len < sizeof(namepath) && 1040244604Srmacklem is_a_valid_tgt_cache(namepath, uid, &rating, 1041244604Srmacklem &exptime) != 0) { 1042244604Srmacklem if (gotone == 0 || rating > orating || 1043244604Srmacklem (rating == orating && exptime > oexptime)) { 1044244604Srmacklem orating = rating; 1045244604Srmacklem oexptime = exptime; 1046244604Srmacklem strcpy(retpath, namepath); 1047244604Srmacklem gotone = 1; 1048244604Srmacklem } 1049244604Srmacklem } 1050244604Srmacklem } 1051244604Srmacklem } 1052244604Srmacklem closedir(dirp); 1053244604Srmacklem if (gotone != 0) { 1054244604Srmacklem strcpy(rpath, retpath); 1055244604Srmacklem return (1); 1056244604Srmacklem } 1057244604Srmacklem return (0); 1058244604Srmacklem} 1059244604Srmacklem 1060244604Srmacklem/* 1061244604Srmacklem * Try to determine if the file is a valid tgt cache file. 1062244604Srmacklem * Check that the file has a valid tgt for a principal. 1063244604Srmacklem * If it does, return 1, otherwise return 0. 1064244604Srmacklem * It also returns a "rating" and the expiry time for the TGT, when found. 1065244604Srmacklem * This "rating" is higher based on heuristics that make it more 1066244604Srmacklem * likely to be the correct credential cache file to use. It can 1067244604Srmacklem * be used by the caller, along with expiry time, to select from 1068244604Srmacklem * multiple credential cache files. 1069244604Srmacklem */ 1070244604Srmacklemstatic int 1071244604Srmacklemis_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating, 1072244604Srmacklem time_t *retexptime) 1073244604Srmacklem{ 1074245014Srmacklem#ifndef WITHOUT_KERBEROS 1075244604Srmacklem krb5_context context; 1076244604Srmacklem krb5_principal princ; 1077244604Srmacklem krb5_ccache ccache; 1078244604Srmacklem krb5_error_code retval; 1079244604Srmacklem krb5_cc_cursor curse; 1080244604Srmacklem krb5_creds krbcred; 1081244604Srmacklem int gotone, orating, rating, ret; 1082244604Srmacklem struct passwd *pw; 1083244604Srmacklem char *cp, *cp2, *pname; 1084244604Srmacklem time_t exptime; 1085244604Srmacklem 1086244604Srmacklem /* Find a likely name for the uid principal. */ 1087244604Srmacklem pw = getpwuid(uid); 1088244604Srmacklem 1089244604Srmacklem /* 1090244604Srmacklem * Do a bunch of krb5 library stuff to try and determine if 1091244604Srmacklem * this file is a credentials cache with an appropriate TGT 1092244604Srmacklem * in it. 1093244604Srmacklem */ 1094244604Srmacklem retval = krb5_init_context(&context); 1095244604Srmacklem if (retval != 0) 1096244604Srmacklem return (0); 1097244604Srmacklem retval = krb5_cc_resolve(context, filepath, &ccache); 1098244604Srmacklem if (retval != 0) { 1099244604Srmacklem krb5_free_context(context); 1100244604Srmacklem return (0); 1101244604Srmacklem } 1102244604Srmacklem ret = 0; 1103244604Srmacklem orating = 0; 1104244604Srmacklem exptime = 0; 1105244604Srmacklem retval = krb5_cc_start_seq_get(context, ccache, &curse); 1106244604Srmacklem if (retval == 0) { 1107244604Srmacklem while ((retval = krb5_cc_next_cred(context, ccache, &curse, 1108244604Srmacklem &krbcred)) == 0) { 1109244604Srmacklem gotone = 0; 1110244604Srmacklem rating = 0; 1111244604Srmacklem retval = krb5_unparse_name(context, krbcred.server, 1112244604Srmacklem &pname); 1113244604Srmacklem if (retval == 0) { 1114244604Srmacklem cp = strchr(pname, '/'); 1115244604Srmacklem if (cp != NULL) { 1116244604Srmacklem *cp++ = '\0'; 1117244604Srmacklem if (strcmp(pname, "krbtgt") == 0 && 1118244604Srmacklem krbcred.times.endtime > time(NULL) 1119244604Srmacklem ) { 1120244604Srmacklem gotone = 1; 1121244604Srmacklem /* 1122244604Srmacklem * Test to see if this is a 1123244604Srmacklem * tgt for cross-realm auth. 1124244604Srmacklem * Rate it higher, if it is not. 1125244604Srmacklem */ 1126244604Srmacklem cp2 = strchr(cp, '@'); 1127244604Srmacklem if (cp2 != NULL) { 1128244604Srmacklem *cp2++ = '\0'; 1129244604Srmacklem if (strcmp(cp, cp2) == 1130244604Srmacklem 0) 1131244604Srmacklem rating++; 1132244604Srmacklem } 1133244604Srmacklem } 1134244604Srmacklem } 1135244604Srmacklem free(pname); 1136244604Srmacklem } 1137244604Srmacklem if (gotone != 0) { 1138244604Srmacklem retval = krb5_unparse_name(context, 1139244604Srmacklem krbcred.client, &pname); 1140244604Srmacklem if (retval == 0) { 1141244604Srmacklem cp = strchr(pname, '@'); 1142244604Srmacklem if (cp != NULL) { 1143244604Srmacklem *cp++ = '\0'; 1144244604Srmacklem if (pw != NULL && strcmp(pname, 1145244604Srmacklem pw->pw_name) == 0) 1146244604Srmacklem rating++; 1147244604Srmacklem if (strchr(pname, '/') == NULL) 1148244604Srmacklem rating++; 1149244604Srmacklem if (pref_realm[0] != '\0' && 1150244604Srmacklem strcmp(cp, pref_realm) == 0) 1151244604Srmacklem rating++; 1152244604Srmacklem } 1153244604Srmacklem } 1154244604Srmacklem free(pname); 1155244604Srmacklem if (rating > orating) { 1156244604Srmacklem orating = rating; 1157244604Srmacklem exptime = krbcred.times.endtime; 1158244604Srmacklem } else if (rating == orating && 1159244604Srmacklem krbcred.times.endtime > exptime) 1160244604Srmacklem exptime = krbcred.times.endtime; 1161244604Srmacklem ret = 1; 1162244604Srmacklem } 1163244604Srmacklem krb5_free_cred_contents(context, &krbcred); 1164244604Srmacklem } 1165244604Srmacklem krb5_cc_end_seq_get(context, ccache, &curse); 1166244604Srmacklem } 1167244604Srmacklem krb5_cc_close(context, ccache); 1168244604Srmacklem krb5_free_context(context); 1169244604Srmacklem if (ret != 0) { 1170244604Srmacklem *retrating = orating; 1171244604Srmacklem *retexptime = exptime; 1172244604Srmacklem } 1173244604Srmacklem return (ret); 1174245014Srmacklem#else /* WITHOUT_KERBEROS */ 1175245014Srmacklem return (0); 1176245014Srmacklem#endif /* !WITHOUT_KERBEROS */ 1177244604Srmacklem} 1178244604Srmacklem 1179252138Srmacklem#ifndef WITHOUT_KERBEROS 1180252138Srmacklem/* 1181253015Srmacklem * This function attempts to do essentially a "kinit -k" for the principal 1182253015Srmacklem * name provided as the argument, so that there will be a TGT in the 1183253015Srmacklem * credential cache. 1184253015Srmacklem */ 1185253015Srmacklemstatic krb5_error_code 1186253015Srmacklemgssd_get_cc_from_keytab(const char *name) 1187253015Srmacklem{ 1188253015Srmacklem krb5_error_code ret, opt_ret, princ_ret, cc_ret, kt_ret, cred_ret; 1189253015Srmacklem krb5_context context; 1190253015Srmacklem krb5_principal principal; 1191253015Srmacklem krb5_keytab kt; 1192253015Srmacklem krb5_creds cred; 1193253015Srmacklem krb5_get_init_creds_opt *opt; 1194253015Srmacklem krb5_deltat start_time = 0; 1195253015Srmacklem krb5_ccache ccache; 1196253015Srmacklem 1197253015Srmacklem ret = krb5_init_context(&context); 1198253015Srmacklem if (ret != 0) 1199253015Srmacklem return (ret); 1200253018Srmacklem opt_ret = cc_ret = kt_ret = cred_ret = 1; /* anything non-zero */ 1201253015Srmacklem princ_ret = ret = krb5_parse_name(context, name, &principal); 1202253015Srmacklem if (ret == 0) 1203253015Srmacklem opt_ret = ret = krb5_get_init_creds_opt_alloc(context, &opt); 1204253015Srmacklem if (ret == 0) 1205253015Srmacklem cc_ret = ret = krb5_cc_default(context, &ccache); 1206253015Srmacklem if (ret == 0) 1207253015Srmacklem ret = krb5_cc_initialize(context, ccache, principal); 1208253015Srmacklem if (ret == 0) { 1209253015Srmacklem krb5_get_init_creds_opt_set_default_flags(context, "gssd", 1210253015Srmacklem krb5_principal_get_realm(context, principal), opt); 1211253015Srmacklem kt_ret = ret = krb5_kt_default(context, &kt); 1212253015Srmacklem } 1213253015Srmacklem if (ret == 0) 1214253015Srmacklem cred_ret = ret = krb5_get_init_creds_keytab(context, &cred, 1215253015Srmacklem principal, kt, start_time, NULL, opt); 1216253015Srmacklem if (ret == 0) 1217253015Srmacklem ret = krb5_cc_store_cred(context, ccache, &cred); 1218253015Srmacklem if (kt_ret == 0) 1219253015Srmacklem krb5_kt_close(context, kt); 1220253015Srmacklem if (cc_ret == 0) 1221253015Srmacklem krb5_cc_close(context, ccache); 1222253015Srmacklem if (opt_ret == 0) 1223253015Srmacklem krb5_get_init_creds_opt_free(context, opt); 1224253015Srmacklem if (princ_ret == 0) 1225253015Srmacklem krb5_free_principal(context, principal); 1226253015Srmacklem if (cred_ret == 0) 1227253015Srmacklem krb5_free_cred_contents(context, &cred); 1228253015Srmacklem krb5_free_context(context); 1229253015Srmacklem return (ret); 1230253015Srmacklem} 1231253015Srmacklem 1232253015Srmacklem/* 1233252138Srmacklem * Acquire a gss credential for a uid. 1234252138Srmacklem */ 1235252138Srmacklemstatic OM_uint32 1236252138Srmacklemgssd_get_user_cred(OM_uint32 *min_statp, uid_t uid, gss_cred_id_t *credp) 1237252138Srmacklem{ 1238252138Srmacklem gss_buffer_desc principal_desc; 1239252138Srmacklem gss_name_t name; 1240252138Srmacklem OM_uint32 maj_stat, min_stat; 1241252138Srmacklem gss_OID_set mechlist; 1242252138Srmacklem struct passwd *pw; 1243252138Srmacklem 1244252138Srmacklem pw = getpwuid(uid); 1245252138Srmacklem if (pw == NULL) { 1246252138Srmacklem *min_statp = 0; 1247252138Srmacklem return (GSS_S_FAILURE); 1248252138Srmacklem } 1249252138Srmacklem 1250252138Srmacklem /* 1251252138Srmacklem * The mechanism must be set to KerberosV for acquisition 1252252138Srmacklem * of credentials to work reliably. 1253252138Srmacklem */ 1254252138Srmacklem maj_stat = gss_create_empty_oid_set(min_statp, &mechlist); 1255252138Srmacklem if (maj_stat != GSS_S_COMPLETE) 1256252138Srmacklem return (maj_stat); 1257252138Srmacklem maj_stat = gss_add_oid_set_member(min_statp, GSS_KRB5_MECH_OID_X, 1258252138Srmacklem &mechlist); 1259252138Srmacklem if (maj_stat != GSS_S_COMPLETE) { 1260252138Srmacklem gss_release_oid_set(&min_stat, &mechlist); 1261252138Srmacklem return (maj_stat); 1262252138Srmacklem } 1263252138Srmacklem 1264252138Srmacklem principal_desc.value = (void *)pw->pw_name; 1265252138Srmacklem principal_desc.length = strlen(pw->pw_name); 1266252138Srmacklem maj_stat = gss_import_name(min_statp, &principal_desc, 1267252138Srmacklem GSS_C_NT_USER_NAME, &name); 1268252138Srmacklem if (maj_stat != GSS_S_COMPLETE) { 1269252138Srmacklem gss_release_oid_set(&min_stat, &mechlist); 1270252138Srmacklem return (maj_stat); 1271252138Srmacklem } 1272252138Srmacklem /* Acquire the credentials. */ 1273252138Srmacklem maj_stat = gss_acquire_cred(min_statp, name, 0, mechlist, 1274252138Srmacklem GSS_C_INITIATE, credp, NULL, NULL); 1275252138Srmacklem gss_release_name(&min_stat, &name); 1276252138Srmacklem gss_release_oid_set(&min_stat, &mechlist); 1277252138Srmacklem return (maj_stat); 1278252138Srmacklem} 1279252138Srmacklem#endif /* !WITHOUT_KERBEROS */ 1280253015Srmacklem 1281253015Srmacklemvoid gssd_terminate(int sig __unused) 1282253015Srmacklem{ 1283253015Srmacklem 1284253015Srmacklem#ifndef WITHOUT_KERBEROS 1285253015Srmacklem if (hostbased_initiator_cred != 0) 1286253015Srmacklem unlink(GSSD_CREDENTIAL_CACHE_FILE); 1287253015Srmacklem#endif 1288293446Sjpaetzel gssd_syscall(""); 1289253015Srmacklem exit(0); 1290253015Srmacklem} 1291253015Srmacklem 1292