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(); 256184588Sdfr 257184588Sdfr return (0); 258184588Sdfr} 259184588Sdfr 260184588Sdfrstatic void 261184588Sdfrgssd_load_mech(void) 262184588Sdfr{ 263184588Sdfr FILE *fp; 264184588Sdfr char buf[256]; 265184588Sdfr char *p; 266184588Sdfr char *name, *oid, *lib, *kobj; 267184588Sdfr 268184588Sdfr fp = fopen(_PATH_GSS_MECH, "r"); 269184588Sdfr if (!fp) 270184588Sdfr return; 271184588Sdfr 272184588Sdfr while (fgets(buf, sizeof(buf), fp)) { 273184588Sdfr if (*buf == '#') 274184588Sdfr continue; 275184588Sdfr p = buf; 276184588Sdfr name = strsep(&p, "\t\n "); 277184588Sdfr if (p) while (isspace(*p)) p++; 278184588Sdfr oid = strsep(&p, "\t\n "); 279184588Sdfr if (p) while (isspace(*p)) p++; 280184588Sdfr lib = strsep(&p, "\t\n "); 281184588Sdfr if (p) while (isspace(*p)) p++; 282184588Sdfr kobj = strsep(&p, "\t\n "); 283184588Sdfr if (!name || !oid || !lib || !kobj) 284184588Sdfr continue; 285184588Sdfr 286184588Sdfr if (strcmp(kobj, "-")) { 287184588Sdfr /* 288184588Sdfr * Attempt to load the kernel module if its 289184588Sdfr * not already present. 290184588Sdfr */ 291184588Sdfr if (modfind(kobj) < 0) { 292184588Sdfr if (kldload(kobj) < 0) { 293184588Sdfr fprintf(stderr, 294184588Sdfr "%s: can't find or load kernel module %s for %s\n", 295184588Sdfr getprogname(), kobj, name); 296184588Sdfr } 297184588Sdfr } 298184588Sdfr } 299184588Sdfr } 300184588Sdfr fclose(fp); 301184588Sdfr} 302184588Sdfr 303184588Sdfrstatic void * 304184588Sdfrgssd_find_resource(uint64_t id) 305184588Sdfr{ 306184588Sdfr struct gss_resource *gr; 307184588Sdfr 308184588Sdfr if (!id) 309184588Sdfr return (NULL); 310184588Sdfr 311184588Sdfr LIST_FOREACH(gr, &gss_resources, gr_link) 312184588Sdfr if (gr->gr_id == id) 313184588Sdfr return (gr->gr_res); 314184588Sdfr 315184588Sdfr return (NULL); 316184588Sdfr} 317184588Sdfr 318184588Sdfrstatic uint64_t 319184588Sdfrgssd_make_resource(void *res) 320184588Sdfr{ 321184588Sdfr struct gss_resource *gr; 322184588Sdfr 323184588Sdfr if (!res) 324184588Sdfr return (0); 325184588Sdfr 326184588Sdfr gr = malloc(sizeof(struct gss_resource)); 327184588Sdfr if (!gr) 328184588Sdfr return (0); 329184588Sdfr gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32); 330184588Sdfr gr->gr_res = res; 331184588Sdfr LIST_INSERT_HEAD(&gss_resources, gr, gr_link); 332184588Sdfr gss_resource_count++; 333184588Sdfr if (debug_level > 1) 334184588Sdfr printf("%d resources allocated\n", gss_resource_count); 335184588Sdfr 336184588Sdfr return (gr->gr_id); 337184588Sdfr} 338184588Sdfr 339184588Sdfrstatic void 340184588Sdfrgssd_delete_resource(uint64_t id) 341184588Sdfr{ 342184588Sdfr struct gss_resource *gr; 343184588Sdfr 344184588Sdfr LIST_FOREACH(gr, &gss_resources, gr_link) { 345184588Sdfr if (gr->gr_id == id) { 346184588Sdfr LIST_REMOVE(gr, gr_link); 347184588Sdfr free(gr); 348184588Sdfr gss_resource_count--; 349184588Sdfr if (debug_level > 1) 350184588Sdfr printf("%d resources allocated\n", 351184588Sdfr gss_resource_count); 352184588Sdfr return; 353184588Sdfr } 354184588Sdfr } 355184588Sdfr} 356184588Sdfr 357251444Srmacklemstatic void 358251444Srmacklemgssd_verbose_out(const char *fmt, ...) 359251444Srmacklem{ 360251444Srmacklem va_list ap; 361251444Srmacklem 362251444Srmacklem if (verbose != 0) { 363251444Srmacklem va_start(ap, fmt); 364251444Srmacklem if (debug_level == 0) 365251444Srmacklem vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); 366251444Srmacklem else 367251444Srmacklem vfprintf(stderr, fmt, ap); 368251444Srmacklem va_end(ap); 369251444Srmacklem } 370251444Srmacklem} 371251444Srmacklem 372184588Sdfrbool_t 373184588Sdfrgssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) 374184588Sdfr{ 375184588Sdfr 376251444Srmacklem gssd_verbose_out("gssd_null: done\n"); 377184588Sdfr return (TRUE); 378184588Sdfr} 379184588Sdfr 380184588Sdfrbool_t 381184588Sdfrgssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp) 382184588Sdfr{ 383184588Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 384184588Sdfr gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 385184588Sdfr gss_name_t name = GSS_C_NO_NAME; 386244604Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 387252138Srmacklem int gotone, gotcred; 388252138Srmacklem OM_uint32 min_stat; 389252138Srmacklem#ifndef WITHOUT_KERBEROS 390252138Srmacklem gss_buffer_desc principal_desc; 391252138Srmacklem char enctype[sizeof(uint32_t)]; 392252138Srmacklem int key_enctype; 393252138Srmacklem OM_uint32 maj_stat; 394252138Srmacklem#endif 395184588Sdfr 396244604Srmacklem memset(result, 0, sizeof(*result)); 397253015Srmacklem if (hostbased_initiator_cred != 0 && argp->cred != 0 && 398253015Srmacklem argp->uid == 0) { 399244604Srmacklem /* 400253015Srmacklem * These credentials are for a host based initiator name 401253015Srmacklem * in a keytab file, which should now have credentials 402253015Srmacklem * in /tmp/krb5cc_gssd, because gss_acquire_cred() did 403253015Srmacklem * the equivalent of "kinit -k". 404253015Srmacklem */ 405253015Srmacklem snprintf(ccname, sizeof(ccname), "FILE:%s", 406253015Srmacklem GSSD_CREDENTIAL_CACHE_FILE); 407253015Srmacklem } else if (ccfile_dirlist[0] != '\0' && argp->cred == 0) { 408253015Srmacklem /* 409244604Srmacklem * For the "-s" case and no credentials provided as an 410244604Srmacklem * argument, search the directory list for an appropriate 411244604Srmacklem * credential cache file. If the search fails, return failure. 412244604Srmacklem */ 413244604Srmacklem gotone = 0; 414244604Srmacklem cp = ccfile_dirlist; 415244604Srmacklem do { 416244604Srmacklem cp2 = strchr(cp, ':'); 417244604Srmacklem if (cp2 != NULL) 418244604Srmacklem *cp2 = '\0'; 419244604Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 420244604Srmacklem if (gotone != 0) 421244604Srmacklem break; 422244604Srmacklem if (cp2 != NULL) 423244604Srmacklem *cp2++ = ':'; 424244604Srmacklem cp = cp2; 425244604Srmacklem } while (cp != NULL && *cp != '\0'); 426244604Srmacklem if (gotone == 0) { 427244604Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 428251444Srmacklem gssd_verbose_out("gssd_init_sec_context: -s no" 429251444Srmacklem " credential cache file found for uid=%d\n", 430251444Srmacklem (int)argp->uid); 431244604Srmacklem return (TRUE); 432244604Srmacklem } 433244604Srmacklem } else { 434244604Srmacklem /* 435244604Srmacklem * If there wasn't a "-s" option or the credentials have 436244604Srmacklem * been provided as an argument, do it the old way. 437244604Srmacklem * When credentials are provided, the uid should be root. 438244604Srmacklem */ 439244604Srmacklem if (argp->cred != 0 && argp->uid != 0) { 440244604Srmacklem if (debug_level == 0) 441244604Srmacklem syslog(LOG_ERR, "gss_init_sec_context:" 442244604Srmacklem " cred for non-root"); 443244604Srmacklem else 444244604Srmacklem fprintf(stderr, "gss_init_sec_context:" 445244604Srmacklem " cred for non-root\n"); 446244604Srmacklem } 447244604Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 448244604Srmacklem (int) argp->uid); 449244604Srmacklem } 450184588Sdfr setenv("KRB5CCNAME", ccname, TRUE); 451184588Sdfr 452184588Sdfr if (argp->cred) { 453184588Sdfr cred = gssd_find_resource(argp->cred); 454184588Sdfr if (!cred) { 455184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 456251444Srmacklem gssd_verbose_out("gssd_init_sec_context: cred" 457251444Srmacklem " resource not found\n"); 458184588Sdfr return (TRUE); 459184588Sdfr } 460184588Sdfr } 461184588Sdfr if (argp->ctx) { 462184588Sdfr ctx = gssd_find_resource(argp->ctx); 463184588Sdfr if (!ctx) { 464184588Sdfr result->major_status = GSS_S_CONTEXT_EXPIRED; 465251444Srmacklem gssd_verbose_out("gssd_init_sec_context: context" 466251444Srmacklem " resource not found\n"); 467184588Sdfr return (TRUE); 468184588Sdfr } 469184588Sdfr } 470184588Sdfr if (argp->name) { 471184588Sdfr name = gssd_find_resource(argp->name); 472184588Sdfr if (!name) { 473184588Sdfr result->major_status = GSS_S_BAD_NAME; 474251444Srmacklem gssd_verbose_out("gssd_init_sec_context: name" 475251444Srmacklem " resource not found\n"); 476184588Sdfr return (TRUE); 477184588Sdfr } 478184588Sdfr } 479252138Srmacklem gotcred = 0; 480184588Sdfr 481252138Srmacklem#ifndef WITHOUT_KERBEROS 482252138Srmacklem if (use_old_des != 0) { 483252138Srmacklem if (cred == GSS_C_NO_CREDENTIAL) { 484252138Srmacklem /* Acquire a credential for the uid. */ 485252138Srmacklem maj_stat = gssd_get_user_cred(&min_stat, argp->uid, 486252138Srmacklem &cred); 487252138Srmacklem if (maj_stat == GSS_S_COMPLETE) 488252138Srmacklem gotcred = 1; 489252138Srmacklem else 490252138Srmacklem gssd_verbose_out("gssd_init_sec_context: " 491252138Srmacklem "get user cred failed uid=%d major=0x%x " 492252138Srmacklem "minor=%d\n", (int)argp->uid, 493252138Srmacklem (unsigned int)maj_stat, (int)min_stat); 494252138Srmacklem } 495252138Srmacklem if (cred != GSS_C_NO_CREDENTIAL) { 496252138Srmacklem key_enctype = ETYPE_DES_CBC_CRC; 497252138Srmacklem enctype[0] = (key_enctype >> 24) & 0xff; 498252138Srmacklem enctype[1] = (key_enctype >> 16) & 0xff; 499252138Srmacklem enctype[2] = (key_enctype >> 8) & 0xff; 500252138Srmacklem enctype[3] = key_enctype & 0xff; 501252138Srmacklem principal_desc.length = sizeof(enctype); 502252138Srmacklem principal_desc.value = enctype; 503252138Srmacklem result->major_status = gss_set_cred_option( 504252138Srmacklem &result->minor_status, &cred, 505252138Srmacklem GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X, 506252138Srmacklem &principal_desc); 507252138Srmacklem gssd_verbose_out("gssd_init_sec_context: set allowable " 508252138Srmacklem "enctype major=0x%x minor=%d\n", 509252138Srmacklem (unsigned int)result->major_status, 510252138Srmacklem (int)result->minor_status); 511252138Srmacklem if (result->major_status != GSS_S_COMPLETE) { 512252138Srmacklem if (gotcred != 0) 513252138Srmacklem gss_release_cred(&min_stat, &cred); 514252138Srmacklem return (TRUE); 515252138Srmacklem } 516252138Srmacklem } 517252138Srmacklem } 518252138Srmacklem#endif 519184588Sdfr result->major_status = gss_init_sec_context(&result->minor_status, 520184588Sdfr cred, &ctx, name, argp->mech_type, 521184588Sdfr argp->req_flags, argp->time_req, argp->input_chan_bindings, 522184588Sdfr &argp->input_token, &result->actual_mech_type, 523184588Sdfr &result->output_token, &result->ret_flags, &result->time_rec); 524251444Srmacklem gssd_verbose_out("gssd_init_sec_context: done major=0x%x minor=%d" 525251444Srmacklem " uid=%d\n", (unsigned int)result->major_status, 526251444Srmacklem (int)result->minor_status, (int)argp->uid); 527252138Srmacklem if (gotcred != 0) 528252138Srmacklem gss_release_cred(&min_stat, &cred); 529184588Sdfr 530184588Sdfr if (result->major_status == GSS_S_COMPLETE 531184588Sdfr || result->major_status == GSS_S_CONTINUE_NEEDED) { 532184588Sdfr if (argp->ctx) 533184588Sdfr result->ctx = argp->ctx; 534184588Sdfr else 535184588Sdfr result->ctx = gssd_make_resource(ctx); 536184588Sdfr } 537184588Sdfr 538184588Sdfr return (TRUE); 539184588Sdfr} 540184588Sdfr 541184588Sdfrbool_t 542184588Sdfrgssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp) 543184588Sdfr{ 544184588Sdfr gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 545184588Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 546184588Sdfr gss_name_t src_name; 547184588Sdfr gss_cred_id_t delegated_cred_handle; 548184588Sdfr 549184588Sdfr memset(result, 0, sizeof(*result)); 550184588Sdfr if (argp->ctx) { 551184588Sdfr ctx = gssd_find_resource(argp->ctx); 552184588Sdfr if (!ctx) { 553184588Sdfr result->major_status = GSS_S_CONTEXT_EXPIRED; 554251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: ctx" 555251444Srmacklem " resource not found\n"); 556184588Sdfr return (TRUE); 557184588Sdfr } 558184588Sdfr } 559184588Sdfr if (argp->cred) { 560184588Sdfr cred = gssd_find_resource(argp->cred); 561184588Sdfr if (!cred) { 562184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 563251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: cred" 564251444Srmacklem " resource not found\n"); 565184588Sdfr return (TRUE); 566184588Sdfr } 567184588Sdfr } 568184588Sdfr 569184588Sdfr memset(result, 0, sizeof(*result)); 570184588Sdfr result->major_status = gss_accept_sec_context(&result->minor_status, 571184588Sdfr &ctx, cred, &argp->input_token, argp->input_chan_bindings, 572184588Sdfr &src_name, &result->mech_type, &result->output_token, 573184588Sdfr &result->ret_flags, &result->time_rec, 574184588Sdfr &delegated_cred_handle); 575251444Srmacklem gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n", 576251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 577184588Sdfr 578184588Sdfr if (result->major_status == GSS_S_COMPLETE 579184588Sdfr || result->major_status == GSS_S_CONTINUE_NEEDED) { 580184588Sdfr if (argp->ctx) 581184588Sdfr result->ctx = argp->ctx; 582184588Sdfr else 583184588Sdfr result->ctx = gssd_make_resource(ctx); 584184588Sdfr result->src_name = gssd_make_resource(src_name); 585184588Sdfr result->delegated_cred_handle = 586184588Sdfr gssd_make_resource(delegated_cred_handle); 587184588Sdfr } 588184588Sdfr 589184588Sdfr return (TRUE); 590184588Sdfr} 591184588Sdfr 592184588Sdfrbool_t 593184588Sdfrgssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp) 594184588Sdfr{ 595184588Sdfr gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 596184588Sdfr 597184588Sdfr if (ctx) { 598184588Sdfr result->major_status = gss_delete_sec_context( 599184588Sdfr &result->minor_status, &ctx, &result->output_token); 600184588Sdfr gssd_delete_resource(argp->ctx); 601184588Sdfr } else { 602184588Sdfr result->major_status = GSS_S_COMPLETE; 603184588Sdfr result->minor_status = 0; 604184588Sdfr } 605251444Srmacklem gssd_verbose_out("gssd_delete_sec_context: done major=0x%x minor=%d\n", 606251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 607184588Sdfr 608184588Sdfr return (TRUE); 609184588Sdfr} 610184588Sdfr 611184588Sdfrbool_t 612184588Sdfrgssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp) 613184588Sdfr{ 614184588Sdfr gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 615184588Sdfr 616184588Sdfr if (ctx) { 617184588Sdfr result->major_status = gss_export_sec_context( 618184588Sdfr &result->minor_status, &ctx, 619184588Sdfr &result->interprocess_token); 620184588Sdfr result->format = KGSS_HEIMDAL_1_1; 621184588Sdfr gssd_delete_resource(argp->ctx); 622184588Sdfr } else { 623184588Sdfr result->major_status = GSS_S_FAILURE; 624184588Sdfr result->minor_status = 0; 625184588Sdfr result->interprocess_token.length = 0; 626184588Sdfr result->interprocess_token.value = NULL; 627184588Sdfr } 628251444Srmacklem gssd_verbose_out("gssd_export_sec_context: done major=0x%x minor=%d\n", 629251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 630184588Sdfr 631184588Sdfr return (TRUE); 632184588Sdfr} 633184588Sdfr 634184588Sdfrbool_t 635184588Sdfrgssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp) 636184588Sdfr{ 637184588Sdfr gss_name_t name; 638184588Sdfr 639184588Sdfr result->major_status = gss_import_name(&result->minor_status, 640184588Sdfr &argp->input_name_buffer, argp->input_name_type, &name); 641251444Srmacklem gssd_verbose_out("gssd_import_name: done major=0x%x minor=%d\n", 642251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 643184588Sdfr 644184588Sdfr if (result->major_status == GSS_S_COMPLETE) 645184588Sdfr result->output_name = gssd_make_resource(name); 646184588Sdfr else 647184588Sdfr result->output_name = 0; 648184588Sdfr 649184588Sdfr return (TRUE); 650184588Sdfr} 651184588Sdfr 652184588Sdfrbool_t 653184588Sdfrgssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp) 654184588Sdfr{ 655184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 656184588Sdfr gss_name_t output_name; 657184588Sdfr 658184588Sdfr memset(result, 0, sizeof(*result)); 659184588Sdfr if (!name) { 660184588Sdfr result->major_status = GSS_S_BAD_NAME; 661184588Sdfr return (TRUE); 662184588Sdfr } 663184588Sdfr 664184588Sdfr result->major_status = gss_canonicalize_name(&result->minor_status, 665184588Sdfr name, argp->mech_type, &output_name); 666251444Srmacklem gssd_verbose_out("gssd_canonicalize_name: done major=0x%x minor=%d\n", 667251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 668184588Sdfr 669184588Sdfr if (result->major_status == GSS_S_COMPLETE) 670184588Sdfr result->output_name = gssd_make_resource(output_name); 671184588Sdfr else 672184588Sdfr result->output_name = 0; 673184588Sdfr 674184588Sdfr return (TRUE); 675184588Sdfr} 676184588Sdfr 677184588Sdfrbool_t 678184588Sdfrgssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp) 679184588Sdfr{ 680184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 681184588Sdfr 682184588Sdfr memset(result, 0, sizeof(*result)); 683184588Sdfr if (!name) { 684184588Sdfr result->major_status = GSS_S_BAD_NAME; 685251444Srmacklem gssd_verbose_out("gssd_export_name: name resource not found\n"); 686184588Sdfr return (TRUE); 687184588Sdfr } 688184588Sdfr 689184588Sdfr result->major_status = gss_export_name(&result->minor_status, 690184588Sdfr name, &result->exported_name); 691251444Srmacklem gssd_verbose_out("gssd_export_name: done major=0x%x minor=%d\n", 692251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 693184588Sdfr 694184588Sdfr return (TRUE); 695184588Sdfr} 696184588Sdfr 697184588Sdfrbool_t 698184588Sdfrgssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp) 699184588Sdfr{ 700184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 701184588Sdfr 702184588Sdfr if (name) { 703184588Sdfr result->major_status = gss_release_name(&result->minor_status, 704184588Sdfr &name); 705184588Sdfr gssd_delete_resource(argp->input_name); 706184588Sdfr } else { 707184588Sdfr result->major_status = GSS_S_COMPLETE; 708184588Sdfr result->minor_status = 0; 709184588Sdfr } 710251444Srmacklem gssd_verbose_out("gssd_release_name: done major=0x%x minor=%d\n", 711251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 712184588Sdfr 713184588Sdfr return (TRUE); 714184588Sdfr} 715184588Sdfr 716184588Sdfrbool_t 717184588Sdfrgssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp) 718184588Sdfr{ 719184588Sdfr gss_name_t name = gssd_find_resource(argp->pname); 720184588Sdfr uid_t uid; 721250176Srmacklem char buf[1024], *bufp; 722184588Sdfr struct passwd pwd, *pw; 723250176Srmacklem size_t buflen; 724250176Srmacklem int error; 725250176Srmacklem static size_t buflen_hint = 1024; 726184588Sdfr 727184588Sdfr memset(result, 0, sizeof(*result)); 728184588Sdfr if (name) { 729184588Sdfr result->major_status = 730184588Sdfr gss_pname_to_uid(&result->minor_status, 731184588Sdfr name, argp->mech, &uid); 732184588Sdfr if (result->major_status == GSS_S_COMPLETE) { 733184588Sdfr result->uid = uid; 734250176Srmacklem buflen = buflen_hint; 735250176Srmacklem for (;;) { 736250176Srmacklem pw = NULL; 737250176Srmacklem bufp = buf; 738250176Srmacklem if (buflen > sizeof(buf)) 739250176Srmacklem bufp = malloc(buflen); 740250176Srmacklem if (bufp == NULL) 741250176Srmacklem break; 742250176Srmacklem error = getpwuid_r(uid, &pwd, bufp, buflen, 743250176Srmacklem &pw); 744250176Srmacklem if (error != ERANGE) 745250176Srmacklem break; 746250176Srmacklem if (buflen > sizeof(buf)) 747250176Srmacklem free(bufp); 748250176Srmacklem buflen += 1024; 749250176Srmacklem if (buflen > buflen_hint) 750250176Srmacklem buflen_hint = buflen; 751250176Srmacklem } 752184588Sdfr if (pw) { 753184588Sdfr int len = NGRPS; 754184588Sdfr int groups[NGRPS]; 755184588Sdfr result->gid = pw->pw_gid; 756184588Sdfr getgrouplist(pw->pw_name, pw->pw_gid, 757184588Sdfr groups, &len); 758184588Sdfr result->gidlist.gidlist_len = len; 759184588Sdfr result->gidlist.gidlist_val = 760184588Sdfr mem_alloc(len * sizeof(int)); 761184588Sdfr memcpy(result->gidlist.gidlist_val, groups, 762184588Sdfr len * sizeof(int)); 763251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: mapped" 764251444Srmacklem " to uid=%d, gid=%d\n", (int)result->uid, 765251444Srmacklem (int)result->gid); 766184588Sdfr } else { 767184588Sdfr result->gid = 65534; 768184588Sdfr result->gidlist.gidlist_len = 0; 769184588Sdfr result->gidlist.gidlist_val = NULL; 770251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: mapped" 771251444Srmacklem " to uid=%d, but no groups\n", 772251444Srmacklem (int)result->uid); 773184588Sdfr } 774250176Srmacklem if (bufp != NULL && buflen > sizeof(buf)) 775250176Srmacklem free(bufp); 776251444Srmacklem } else 777251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x" 778251444Srmacklem " minor=%d\n", (unsigned int)result->major_status, 779251444Srmacklem (int)result->minor_status); 780184588Sdfr } else { 781184588Sdfr result->major_status = GSS_S_BAD_NAME; 782184588Sdfr result->minor_status = 0; 783251444Srmacklem gssd_verbose_out("gssd_pname_to_uid: no name\n"); 784184588Sdfr } 785184588Sdfr 786184588Sdfr return (TRUE); 787184588Sdfr} 788184588Sdfr 789184588Sdfrbool_t 790184588Sdfrgssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp) 791184588Sdfr{ 792184588Sdfr gss_name_t desired_name = GSS_C_NO_NAME; 793184588Sdfr gss_cred_id_t cred; 794244604Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 795244604Srmacklem int gotone; 796252138Srmacklem#ifndef WITHOUT_KERBEROS 797252138Srmacklem gss_buffer_desc namebuf; 798252138Srmacklem uint32_t minstat; 799252138Srmacklem krb5_error_code kret; 800252138Srmacklem#endif 801184588Sdfr 802244604Srmacklem memset(result, 0, sizeof(*result)); 803253015Srmacklem if (argp->desired_name) { 804253015Srmacklem desired_name = gssd_find_resource(argp->desired_name); 805253015Srmacklem if (!desired_name) { 806253015Srmacklem result->major_status = GSS_S_BAD_NAME; 807253015Srmacklem gssd_verbose_out("gssd_acquire_cred: no desired name" 808253015Srmacklem " found\n"); 809253015Srmacklem return (TRUE); 810253015Srmacklem } 811253015Srmacklem } 812253015Srmacklem 813253015Srmacklem#ifndef WITHOUT_KERBEROS 814253015Srmacklem if (hostbased_initiator_cred != 0 && argp->desired_name != 0 && 815253015Srmacklem argp->uid == 0 && argp->cred_usage == GSS_C_INITIATE) { 816253015Srmacklem /* This is a host based initiator name in the keytab file. */ 817253015Srmacklem snprintf(ccname, sizeof(ccname), "FILE:%s", 818253015Srmacklem GSSD_CREDENTIAL_CACHE_FILE); 819253015Srmacklem setenv("KRB5CCNAME", ccname, TRUE); 820253015Srmacklem result->major_status = gss_display_name(&result->minor_status, 821253015Srmacklem desired_name, &namebuf, NULL); 822253015Srmacklem gssd_verbose_out("gssd_acquire_cred: desired name for host " 823253015Srmacklem "based initiator cred major=0x%x minor=%d\n", 824253015Srmacklem (unsigned int)result->major_status, 825253015Srmacklem (int)result->minor_status); 826253015Srmacklem if (result->major_status != GSS_S_COMPLETE) 827253015Srmacklem return (TRUE); 828253015Srmacklem if (namebuf.length > PATH_MAX + 5) { 829253015Srmacklem result->minor_status = 0; 830253015Srmacklem result->major_status = GSS_S_FAILURE; 831253015Srmacklem return (TRUE); 832253015Srmacklem } 833253015Srmacklem memcpy(ccname, namebuf.value, namebuf.length); 834253015Srmacklem ccname[namebuf.length] = '\0'; 835253015Srmacklem if ((cp = strchr(ccname, '@')) != NULL) 836253015Srmacklem *cp = '/'; 837253015Srmacklem kret = gssd_get_cc_from_keytab(ccname); 838253015Srmacklem gssd_verbose_out("gssd_acquire_cred: using keytab entry for " 839253015Srmacklem "%s, kerberos ret=%d\n", ccname, (int)kret); 840253015Srmacklem gss_release_buffer(&minstat, &namebuf); 841253015Srmacklem if (kret != 0) { 842253015Srmacklem result->minor_status = kret; 843253015Srmacklem result->major_status = GSS_S_FAILURE; 844253015Srmacklem return (TRUE); 845253015Srmacklem } 846253015Srmacklem } else 847253015Srmacklem#endif /* !WITHOUT_KERBEROS */ 848244604Srmacklem if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) { 849244604Srmacklem /* 850244604Srmacklem * For the "-s" case and no name provided as an 851244604Srmacklem * argument, search the directory list for an appropriate 852244604Srmacklem * credential cache file. If the search fails, return failure. 853244604Srmacklem */ 854244604Srmacklem gotone = 0; 855244604Srmacklem cp = ccfile_dirlist; 856244604Srmacklem do { 857244604Srmacklem cp2 = strchr(cp, ':'); 858244604Srmacklem if (cp2 != NULL) 859244604Srmacklem *cp2 = '\0'; 860244604Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 861244604Srmacklem if (gotone != 0) 862244604Srmacklem break; 863244604Srmacklem if (cp2 != NULL) 864244604Srmacklem *cp2++ = ':'; 865244604Srmacklem cp = cp2; 866244604Srmacklem } while (cp != NULL && *cp != '\0'); 867244604Srmacklem if (gotone == 0) { 868244604Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 869251444Srmacklem gssd_verbose_out("gssd_acquire_cred: no cred cache" 870251444Srmacklem " file found\n"); 871244604Srmacklem return (TRUE); 872244604Srmacklem } 873253015Srmacklem setenv("KRB5CCNAME", ccname, TRUE); 874244604Srmacklem } else { 875244604Srmacklem /* 876244604Srmacklem * If there wasn't a "-s" option or the name has 877244604Srmacklem * been provided as an argument, do it the old way. 878244604Srmacklem * When a name is provided, it will normally exist in the 879244604Srmacklem * default keytab file and the uid will be root. 880244604Srmacklem */ 881244604Srmacklem if (argp->desired_name != 0 && argp->uid != 0) { 882244604Srmacklem if (debug_level == 0) 883244604Srmacklem syslog(LOG_ERR, "gss_acquire_cred:" 884244604Srmacklem " principal_name for non-root"); 885244604Srmacklem else 886244604Srmacklem fprintf(stderr, "gss_acquire_cred:" 887244604Srmacklem " principal_name for non-root\n"); 888244604Srmacklem } 889244604Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 890244604Srmacklem (int) argp->uid); 891253015Srmacklem setenv("KRB5CCNAME", ccname, TRUE); 892244604Srmacklem } 893184588Sdfr 894184588Sdfr result->major_status = gss_acquire_cred(&result->minor_status, 895184588Sdfr desired_name, argp->time_req, argp->desired_mechs, 896184588Sdfr argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec); 897251444Srmacklem gssd_verbose_out("gssd_acquire_cred: done major=0x%x minor=%d\n", 898251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 899184588Sdfr 900184588Sdfr if (result->major_status == GSS_S_COMPLETE) 901184588Sdfr result->output_cred = gssd_make_resource(cred); 902184588Sdfr else 903184588Sdfr result->output_cred = 0; 904184588Sdfr 905184588Sdfr return (TRUE); 906184588Sdfr} 907184588Sdfr 908184588Sdfrbool_t 909184588Sdfrgssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp) 910184588Sdfr{ 911184588Sdfr gss_cred_id_t cred = gssd_find_resource(argp->cred); 912184588Sdfr 913184588Sdfr memset(result, 0, sizeof(*result)); 914184588Sdfr if (!cred) { 915184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 916251444Srmacklem gssd_verbose_out("gssd_set_cred: no credentials\n"); 917184588Sdfr return (TRUE); 918184588Sdfr } 919184588Sdfr 920184588Sdfr result->major_status = gss_set_cred_option(&result->minor_status, 921184588Sdfr &cred, argp->option_name, &argp->option_value); 922251444Srmacklem gssd_verbose_out("gssd_set_cred: done major=0x%x minor=%d\n", 923251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 924184588Sdfr 925184588Sdfr return (TRUE); 926184588Sdfr} 927184588Sdfr 928184588Sdfrbool_t 929184588Sdfrgssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp) 930184588Sdfr{ 931184588Sdfr gss_cred_id_t cred = gssd_find_resource(argp->cred); 932184588Sdfr 933184588Sdfr if (cred) { 934184588Sdfr result->major_status = gss_release_cred(&result->minor_status, 935184588Sdfr &cred); 936184588Sdfr gssd_delete_resource(argp->cred); 937184588Sdfr } else { 938184588Sdfr result->major_status = GSS_S_COMPLETE; 939184588Sdfr result->minor_status = 0; 940184588Sdfr } 941251444Srmacklem gssd_verbose_out("gssd_release_cred: done major=0x%x minor=%d\n", 942251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 943184588Sdfr 944184588Sdfr return (TRUE); 945184588Sdfr} 946184588Sdfr 947184588Sdfrbool_t 948184588Sdfrgssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp) 949184588Sdfr{ 950184588Sdfr 951184588Sdfr result->message_context = argp->message_context; 952184588Sdfr result->major_status = gss_display_status(&result->minor_status, 953184588Sdfr argp->status_value, argp->status_type, argp->mech_type, 954184588Sdfr &result->message_context, &result->status_string); 955251444Srmacklem gssd_verbose_out("gssd_display_status: done major=0x%x minor=%d\n", 956251444Srmacklem (unsigned int)result->major_status, (int)result->minor_status); 957184588Sdfr 958184588Sdfr return (TRUE); 959184588Sdfr} 960184588Sdfr 961184588Sdfrint 962184588Sdfrgssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) 963184588Sdfr{ 964184588Sdfr /* 965184588Sdfr * We don't use XDR to free the results - anything which was 966184588Sdfr * allocated came from GSS-API. We use xdr_result to figure 967184588Sdfr * out what to do. 968184588Sdfr */ 969184588Sdfr OM_uint32 junk; 970184588Sdfr 971184588Sdfr if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) { 972184588Sdfr init_sec_context_res *p = (init_sec_context_res *) result; 973184588Sdfr gss_release_buffer(&junk, &p->output_token); 974184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) { 975184588Sdfr accept_sec_context_res *p = (accept_sec_context_res *) result; 976184588Sdfr gss_release_buffer(&junk, &p->output_token); 977184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) { 978184588Sdfr delete_sec_context_res *p = (delete_sec_context_res *) result; 979184588Sdfr gss_release_buffer(&junk, &p->output_token); 980184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) { 981184588Sdfr export_sec_context_res *p = (export_sec_context_res *) result; 982184588Sdfr if (p->interprocess_token.length) 983184588Sdfr memset(p->interprocess_token.value, 0, 984184588Sdfr p->interprocess_token.length); 985184588Sdfr gss_release_buffer(&junk, &p->interprocess_token); 986184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_export_name_res) { 987184588Sdfr export_name_res *p = (export_name_res *) result; 988184588Sdfr gss_release_buffer(&junk, &p->exported_name); 989184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) { 990184588Sdfr acquire_cred_res *p = (acquire_cred_res *) result; 991184588Sdfr gss_release_oid_set(&junk, &p->actual_mechs); 992184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) { 993184588Sdfr pname_to_uid_res *p = (pname_to_uid_res *) result; 994184588Sdfr if (p->gidlist.gidlist_val) 995184588Sdfr free(p->gidlist.gidlist_val); 996184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_display_status_res) { 997184588Sdfr display_status_res *p = (display_status_res *) result; 998184588Sdfr gss_release_buffer(&junk, &p->status_string); 999184588Sdfr } 1000184588Sdfr 1001184588Sdfr return (TRUE); 1002184588Sdfr} 1003244604Srmacklem 1004244604Srmacklem/* 1005244604Srmacklem * Search a directory for the most likely candidate to be used as the 1006244604Srmacklem * credential cache for a uid. If successful, return 1 and fill the 1007244604Srmacklem * file's path id into "rpath". Otherwise, return 0. 1008244604Srmacklem */ 1009244604Srmacklemstatic int 1010244604Srmacklemfind_ccache_file(const char *dirpath, uid_t uid, char *rpath) 1011244604Srmacklem{ 1012244604Srmacklem DIR *dirp; 1013244604Srmacklem struct dirent *dp; 1014244604Srmacklem struct stat sb; 1015244604Srmacklem time_t exptime, oexptime; 1016244604Srmacklem int gotone, len, rating, orating; 1017244604Srmacklem char namepath[PATH_MAX + 5 + 1]; 1018244604Srmacklem char retpath[PATH_MAX + 5 + 1]; 1019244604Srmacklem 1020244604Srmacklem dirp = opendir(dirpath); 1021244604Srmacklem if (dirp == NULL) 1022244604Srmacklem return (0); 1023244604Srmacklem gotone = 0; 1024244604Srmacklem orating = 0; 1025244604Srmacklem oexptime = 0; 1026244604Srmacklem while ((dp = readdir(dirp)) != NULL) { 1027244604Srmacklem len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath, 1028244604Srmacklem dp->d_name); 1029244604Srmacklem if (len < sizeof(namepath) && 1030253015Srmacklem (hostbased_initiator_cred == 0 || strcmp(namepath, 1031253015Srmacklem GSSD_CREDENTIAL_CACHE_FILE) != 0) && 1032244604Srmacklem strstr(dp->d_name, ccfile_substring) != NULL && 1033244604Srmacklem lstat(namepath, &sb) >= 0 && 1034244604Srmacklem sb.st_uid == uid && 1035244604Srmacklem S_ISREG(sb.st_mode)) { 1036244604Srmacklem len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s", 1037244604Srmacklem dirpath, dp->d_name); 1038244604Srmacklem if (len < sizeof(namepath) && 1039244604Srmacklem is_a_valid_tgt_cache(namepath, uid, &rating, 1040244604Srmacklem &exptime) != 0) { 1041244604Srmacklem if (gotone == 0 || rating > orating || 1042244604Srmacklem (rating == orating && exptime > oexptime)) { 1043244604Srmacklem orating = rating; 1044244604Srmacklem oexptime = exptime; 1045244604Srmacklem strcpy(retpath, namepath); 1046244604Srmacklem gotone = 1; 1047244604Srmacklem } 1048244604Srmacklem } 1049244604Srmacklem } 1050244604Srmacklem } 1051244604Srmacklem closedir(dirp); 1052244604Srmacklem if (gotone != 0) { 1053244604Srmacklem strcpy(rpath, retpath); 1054244604Srmacklem return (1); 1055244604Srmacklem } 1056244604Srmacklem return (0); 1057244604Srmacklem} 1058244604Srmacklem 1059244604Srmacklem/* 1060244604Srmacklem * Try to determine if the file is a valid tgt cache file. 1061244604Srmacklem * Check that the file has a valid tgt for a principal. 1062244604Srmacklem * If it does, return 1, otherwise return 0. 1063244604Srmacklem * It also returns a "rating" and the expiry time for the TGT, when found. 1064244604Srmacklem * This "rating" is higher based on heuristics that make it more 1065244604Srmacklem * likely to be the correct credential cache file to use. It can 1066244604Srmacklem * be used by the caller, along with expiry time, to select from 1067244604Srmacklem * multiple credential cache files. 1068244604Srmacklem */ 1069244604Srmacklemstatic int 1070244604Srmacklemis_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating, 1071244604Srmacklem time_t *retexptime) 1072244604Srmacklem{ 1073245014Srmacklem#ifndef WITHOUT_KERBEROS 1074244604Srmacklem krb5_context context; 1075244604Srmacklem krb5_principal princ; 1076244604Srmacklem krb5_ccache ccache; 1077244604Srmacklem krb5_error_code retval; 1078244604Srmacklem krb5_cc_cursor curse; 1079244604Srmacklem krb5_creds krbcred; 1080244604Srmacklem int gotone, orating, rating, ret; 1081244604Srmacklem struct passwd *pw; 1082244604Srmacklem char *cp, *cp2, *pname; 1083244604Srmacklem time_t exptime; 1084244604Srmacklem 1085244604Srmacklem /* Find a likely name for the uid principal. */ 1086244604Srmacklem pw = getpwuid(uid); 1087244604Srmacklem 1088244604Srmacklem /* 1089244604Srmacklem * Do a bunch of krb5 library stuff to try and determine if 1090244604Srmacklem * this file is a credentials cache with an appropriate TGT 1091244604Srmacklem * in it. 1092244604Srmacklem */ 1093244604Srmacklem retval = krb5_init_context(&context); 1094244604Srmacklem if (retval != 0) 1095244604Srmacklem return (0); 1096244604Srmacklem retval = krb5_cc_resolve(context, filepath, &ccache); 1097244604Srmacklem if (retval != 0) { 1098244604Srmacklem krb5_free_context(context); 1099244604Srmacklem return (0); 1100244604Srmacklem } 1101244604Srmacklem ret = 0; 1102244604Srmacklem orating = 0; 1103244604Srmacklem exptime = 0; 1104244604Srmacklem retval = krb5_cc_start_seq_get(context, ccache, &curse); 1105244604Srmacklem if (retval == 0) { 1106244604Srmacklem while ((retval = krb5_cc_next_cred(context, ccache, &curse, 1107244604Srmacklem &krbcred)) == 0) { 1108244604Srmacklem gotone = 0; 1109244604Srmacklem rating = 0; 1110244604Srmacklem retval = krb5_unparse_name(context, krbcred.server, 1111244604Srmacklem &pname); 1112244604Srmacklem if (retval == 0) { 1113244604Srmacklem cp = strchr(pname, '/'); 1114244604Srmacklem if (cp != NULL) { 1115244604Srmacklem *cp++ = '\0'; 1116244604Srmacklem if (strcmp(pname, "krbtgt") == 0 && 1117244604Srmacklem krbcred.times.endtime > time(NULL) 1118244604Srmacklem ) { 1119244604Srmacklem gotone = 1; 1120244604Srmacklem /* 1121244604Srmacklem * Test to see if this is a 1122244604Srmacklem * tgt for cross-realm auth. 1123244604Srmacklem * Rate it higher, if it is not. 1124244604Srmacklem */ 1125244604Srmacklem cp2 = strchr(cp, '@'); 1126244604Srmacklem if (cp2 != NULL) { 1127244604Srmacklem *cp2++ = '\0'; 1128244604Srmacklem if (strcmp(cp, cp2) == 1129244604Srmacklem 0) 1130244604Srmacklem rating++; 1131244604Srmacklem } 1132244604Srmacklem } 1133244604Srmacklem } 1134244604Srmacklem free(pname); 1135244604Srmacklem } 1136244604Srmacklem if (gotone != 0) { 1137244604Srmacklem retval = krb5_unparse_name(context, 1138244604Srmacklem krbcred.client, &pname); 1139244604Srmacklem if (retval == 0) { 1140244604Srmacklem cp = strchr(pname, '@'); 1141244604Srmacklem if (cp != NULL) { 1142244604Srmacklem *cp++ = '\0'; 1143244604Srmacklem if (pw != NULL && strcmp(pname, 1144244604Srmacklem pw->pw_name) == 0) 1145244604Srmacklem rating++; 1146244604Srmacklem if (strchr(pname, '/') == NULL) 1147244604Srmacklem rating++; 1148244604Srmacklem if (pref_realm[0] != '\0' && 1149244604Srmacklem strcmp(cp, pref_realm) == 0) 1150244604Srmacklem rating++; 1151244604Srmacklem } 1152244604Srmacklem } 1153244604Srmacklem free(pname); 1154244604Srmacklem if (rating > orating) { 1155244604Srmacklem orating = rating; 1156244604Srmacklem exptime = krbcred.times.endtime; 1157244604Srmacklem } else if (rating == orating && 1158244604Srmacklem krbcred.times.endtime > exptime) 1159244604Srmacklem exptime = krbcred.times.endtime; 1160244604Srmacklem ret = 1; 1161244604Srmacklem } 1162244604Srmacklem krb5_free_cred_contents(context, &krbcred); 1163244604Srmacklem } 1164244604Srmacklem krb5_cc_end_seq_get(context, ccache, &curse); 1165244604Srmacklem } 1166244604Srmacklem krb5_cc_close(context, ccache); 1167244604Srmacklem krb5_free_context(context); 1168244604Srmacklem if (ret != 0) { 1169244604Srmacklem *retrating = orating; 1170244604Srmacklem *retexptime = exptime; 1171244604Srmacklem } 1172244604Srmacklem return (ret); 1173245014Srmacklem#else /* WITHOUT_KERBEROS */ 1174245014Srmacklem return (0); 1175245014Srmacklem#endif /* !WITHOUT_KERBEROS */ 1176244604Srmacklem} 1177244604Srmacklem 1178252138Srmacklem#ifndef WITHOUT_KERBEROS 1179252138Srmacklem/* 1180253015Srmacklem * This function attempts to do essentially a "kinit -k" for the principal 1181253015Srmacklem * name provided as the argument, so that there will be a TGT in the 1182253015Srmacklem * credential cache. 1183253015Srmacklem */ 1184253015Srmacklemstatic krb5_error_code 1185253015Srmacklemgssd_get_cc_from_keytab(const char *name) 1186253015Srmacklem{ 1187253015Srmacklem krb5_error_code ret, opt_ret, princ_ret, cc_ret, kt_ret, cred_ret; 1188253015Srmacklem krb5_context context; 1189253015Srmacklem krb5_principal principal; 1190253015Srmacklem krb5_keytab kt; 1191253015Srmacklem krb5_creds cred; 1192253015Srmacklem krb5_get_init_creds_opt *opt; 1193253015Srmacklem krb5_deltat start_time = 0; 1194253015Srmacklem krb5_ccache ccache; 1195253015Srmacklem 1196253015Srmacklem ret = krb5_init_context(&context); 1197253015Srmacklem if (ret != 0) 1198253015Srmacklem return (ret); 1199253018Srmacklem opt_ret = cc_ret = kt_ret = cred_ret = 1; /* anything non-zero */ 1200253015Srmacklem princ_ret = ret = krb5_parse_name(context, name, &principal); 1201253015Srmacklem if (ret == 0) 1202253015Srmacklem opt_ret = ret = krb5_get_init_creds_opt_alloc(context, &opt); 1203253015Srmacklem if (ret == 0) 1204253015Srmacklem cc_ret = ret = krb5_cc_default(context, &ccache); 1205253015Srmacklem if (ret == 0) 1206253015Srmacklem ret = krb5_cc_initialize(context, ccache, principal); 1207253015Srmacklem if (ret == 0) { 1208253015Srmacklem krb5_get_init_creds_opt_set_default_flags(context, "gssd", 1209253015Srmacklem krb5_principal_get_realm(context, principal), opt); 1210253015Srmacklem kt_ret = ret = krb5_kt_default(context, &kt); 1211253015Srmacklem } 1212253015Srmacklem if (ret == 0) 1213253015Srmacklem cred_ret = ret = krb5_get_init_creds_keytab(context, &cred, 1214253015Srmacklem principal, kt, start_time, NULL, opt); 1215253015Srmacklem if (ret == 0) 1216253015Srmacklem ret = krb5_cc_store_cred(context, ccache, &cred); 1217253015Srmacklem if (kt_ret == 0) 1218253015Srmacklem krb5_kt_close(context, kt); 1219253015Srmacklem if (cc_ret == 0) 1220253015Srmacklem krb5_cc_close(context, ccache); 1221253015Srmacklem if (opt_ret == 0) 1222253015Srmacklem krb5_get_init_creds_opt_free(context, opt); 1223253015Srmacklem if (princ_ret == 0) 1224253015Srmacklem krb5_free_principal(context, principal); 1225253015Srmacklem if (cred_ret == 0) 1226253015Srmacklem krb5_free_cred_contents(context, &cred); 1227253015Srmacklem krb5_free_context(context); 1228253015Srmacklem return (ret); 1229253015Srmacklem} 1230253015Srmacklem 1231253015Srmacklem/* 1232252138Srmacklem * Acquire a gss credential for a uid. 1233252138Srmacklem */ 1234252138Srmacklemstatic OM_uint32 1235252138Srmacklemgssd_get_user_cred(OM_uint32 *min_statp, uid_t uid, gss_cred_id_t *credp) 1236252138Srmacklem{ 1237252138Srmacklem gss_buffer_desc principal_desc; 1238252138Srmacklem gss_name_t name; 1239252138Srmacklem OM_uint32 maj_stat, min_stat; 1240252138Srmacklem gss_OID_set mechlist; 1241252138Srmacklem struct passwd *pw; 1242252138Srmacklem 1243252138Srmacklem pw = getpwuid(uid); 1244252138Srmacklem if (pw == NULL) { 1245252138Srmacklem *min_statp = 0; 1246252138Srmacklem return (GSS_S_FAILURE); 1247252138Srmacklem } 1248252138Srmacklem 1249252138Srmacklem /* 1250252138Srmacklem * The mechanism must be set to KerberosV for acquisition 1251252138Srmacklem * of credentials to work reliably. 1252252138Srmacklem */ 1253252138Srmacklem maj_stat = gss_create_empty_oid_set(min_statp, &mechlist); 1254252138Srmacklem if (maj_stat != GSS_S_COMPLETE) 1255252138Srmacklem return (maj_stat); 1256252138Srmacklem maj_stat = gss_add_oid_set_member(min_statp, GSS_KRB5_MECH_OID_X, 1257252138Srmacklem &mechlist); 1258252138Srmacklem if (maj_stat != GSS_S_COMPLETE) { 1259252138Srmacklem gss_release_oid_set(&min_stat, &mechlist); 1260252138Srmacklem return (maj_stat); 1261252138Srmacklem } 1262252138Srmacklem 1263252138Srmacklem principal_desc.value = (void *)pw->pw_name; 1264252138Srmacklem principal_desc.length = strlen(pw->pw_name); 1265252138Srmacklem maj_stat = gss_import_name(min_statp, &principal_desc, 1266252138Srmacklem GSS_C_NT_USER_NAME, &name); 1267252138Srmacklem if (maj_stat != GSS_S_COMPLETE) { 1268252138Srmacklem gss_release_oid_set(&min_stat, &mechlist); 1269252138Srmacklem return (maj_stat); 1270252138Srmacklem } 1271252138Srmacklem /* Acquire the credentials. */ 1272252138Srmacklem maj_stat = gss_acquire_cred(min_statp, name, 0, mechlist, 1273252138Srmacklem GSS_C_INITIATE, credp, NULL, NULL); 1274252138Srmacklem gss_release_name(&min_stat, &name); 1275252138Srmacklem gss_release_oid_set(&min_stat, &mechlist); 1276252138Srmacklem return (maj_stat); 1277252138Srmacklem} 1278252138Srmacklem#endif /* !WITHOUT_KERBEROS */ 1279253015Srmacklem 1280253015Srmacklemvoid gssd_terminate(int sig __unused) 1281253015Srmacklem{ 1282253015Srmacklem 1283253015Srmacklem#ifndef WITHOUT_KERBEROS 1284253015Srmacklem if (hostbased_initiator_cred != 0) 1285253015Srmacklem unlink(GSSD_CREDENTIAL_CACHE_FILE); 1286253015Srmacklem#endif 1287253015Srmacklem exit(0); 1288253015Srmacklem} 1289253015Srmacklem 1290