gssd.c revision 245089
1184588Sdfr/*- 2184588Sdfr * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ 3184588Sdfr * Authors: Doug Rabson <dfr@rabson.org> 4184588Sdfr * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org> 5184588Sdfr * 6184588Sdfr * Redistribution and use in source and binary forms, with or without 7184588Sdfr * modification, are permitted provided that the following conditions 8184588Sdfr * are met: 9184588Sdfr * 1. Redistributions of source code must retain the above copyright 10184588Sdfr * notice, this list of conditions and the following disclaimer. 11184588Sdfr * 2. Redistributions in binary form must reproduce the above copyright 12184588Sdfr * notice, this list of conditions and the following disclaimer in the 13184588Sdfr * documentation and/or other materials provided with the distribution. 14184588Sdfr * 15184588Sdfr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16184588Sdfr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17184588Sdfr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18184588Sdfr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19184588Sdfr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20184588Sdfr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21184588Sdfr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22184588Sdfr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23184588Sdfr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24184588Sdfr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25184588Sdfr * SUCH DAMAGE. 26184588Sdfr */ 27184588Sdfr 28184588Sdfr#include <sys/cdefs.h> 29184588Sdfr__FBSDID("$FreeBSD: stable/9/usr.sbin/gssd/gssd.c 245089 2013-01-06 01:41:14Z rmacklem $"); 30184588Sdfr 31184588Sdfr#include <sys/param.h> 32184588Sdfr#include <sys/stat.h> 33184588Sdfr#include <sys/linker.h> 34184588Sdfr#include <sys/module.h> 35184588Sdfr#include <sys/queue.h> 36245016Srmacklem#include <sys/syslog.h> 37184588Sdfr#include <ctype.h> 38245089Srmacklem#include <dirent.h> 39184588Sdfr#include <err.h> 40245089Srmacklem#ifndef WITHOUT_KERBEROS 41245089Srmacklem#include <krb5.h> 42245089Srmacklem#endif 43184588Sdfr#include <pwd.h> 44184588Sdfr#include <stdio.h> 45184588Sdfr#include <stdlib.h> 46184588Sdfr#include <string.h> 47184588Sdfr#include <unistd.h> 48184588Sdfr#include <gssapi/gssapi.h> 49184588Sdfr#include <rpc/rpc.h> 50184588Sdfr#include <rpc/rpc_com.h> 51184588Sdfr 52184588Sdfr#include "gssd.h" 53184588Sdfr 54184588Sdfr#ifndef _PATH_GSS_MECH 55184588Sdfr#define _PATH_GSS_MECH "/etc/gss/mech" 56184588Sdfr#endif 57184588Sdfr#ifndef _PATH_GSSDSOCK 58184588Sdfr#define _PATH_GSSDSOCK "/var/run/gssd.sock" 59184588Sdfr#endif 60184588Sdfr 61184588Sdfrstruct gss_resource { 62184588Sdfr LIST_ENTRY(gss_resource) gr_link; 63184588Sdfr uint64_t gr_id; /* indentifier exported to kernel */ 64184588Sdfr void* gr_res; /* GSS-API resource pointer */ 65184588Sdfr}; 66184588SdfrLIST_HEAD(gss_resource_list, gss_resource) gss_resources; 67184588Sdfrint gss_resource_count; 68184588Sdfruint32_t gss_next_id; 69184588Sdfruint32_t gss_start_time; 70184588Sdfrint debug_level; 71245089Srmacklemstatic char ccfile_dirlist[PATH_MAX + 1], ccfile_substring[NAME_MAX + 1]; 72245089Srmacklemstatic char pref_realm[1024]; 73184588Sdfr 74184588Sdfrstatic void gssd_load_mech(void); 75245089Srmacklemstatic int find_ccache_file(const char *, uid_t, char *); 76245089Srmacklemstatic int is_a_valid_tgt_cache(const char *, uid_t, int *, time_t *); 77184588Sdfr 78184588Sdfrextern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp); 79184588Sdfrextern int gssd_syscall(char *path); 80184588Sdfr 81184588Sdfrint 82184588Sdfrmain(int argc, char **argv) 83184588Sdfr{ 84184588Sdfr /* 85184588Sdfr * We provide an RPC service on a local-domain socket. The 86184588Sdfr * kernel's GSS-API code will pass what it can't handle 87184588Sdfr * directly to us. 88184588Sdfr */ 89184588Sdfr struct sockaddr_un sun; 90184588Sdfr int fd, oldmask, ch, debug; 91184588Sdfr SVCXPRT *xprt; 92184588Sdfr 93245089Srmacklem /* 94245089Srmacklem * Initialize the credential cache file name substring and the 95245089Srmacklem * search directory list. 96245089Srmacklem */ 97245089Srmacklem strlcpy(ccfile_substring, "krb5cc_", sizeof(ccfile_substring)); 98245089Srmacklem ccfile_dirlist[0] = '\0'; 99245089Srmacklem pref_realm[0] = '\0'; 100184588Sdfr debug = 0; 101245089Srmacklem while ((ch = getopt(argc, argv, "ds:c:r:")) != -1) { 102184588Sdfr switch (ch) { 103184588Sdfr case 'd': 104184588Sdfr debug_level++; 105184588Sdfr break; 106245089Srmacklem case 's': 107245089Srmacklem#ifndef WITHOUT_KERBEROS 108245089Srmacklem /* 109245089Srmacklem * Set the directory search list. This enables use of 110245089Srmacklem * find_ccache_file() to search the directories for a 111245089Srmacklem * suitable credentials cache file. 112245089Srmacklem */ 113245089Srmacklem strlcpy(ccfile_dirlist, optarg, sizeof(ccfile_dirlist)); 114245089Srmacklem#else 115245089Srmacklem errx(1, "This option not available when built" 116245089Srmacklem " without MK_KERBEROS\n"); 117245089Srmacklem#endif 118245089Srmacklem break; 119245089Srmacklem case 'c': 120245089Srmacklem /* 121245089Srmacklem * Specify a non-default credential cache file 122245089Srmacklem * substring. 123245089Srmacklem */ 124245089Srmacklem strlcpy(ccfile_substring, optarg, 125245089Srmacklem sizeof(ccfile_substring)); 126245089Srmacklem break; 127245089Srmacklem case 'r': 128245089Srmacklem /* 129245089Srmacklem * Set the preferred realm for the credential cache tgt. 130245089Srmacklem */ 131245089Srmacklem strlcpy(pref_realm, optarg, sizeof(pref_realm)); 132245089Srmacklem break; 133184588Sdfr default: 134245089Srmacklem fprintf(stderr, 135245089Srmacklem "usage: %s [-d] [-s dir-list] [-c file-substring]" 136245089Srmacklem " [-r preferred-realm]\n", argv[0]); 137184588Sdfr exit(1); 138184588Sdfr break; 139184588Sdfr } 140184588Sdfr } 141184588Sdfr 142184588Sdfr gssd_load_mech(); 143184588Sdfr 144184588Sdfr if (!debug_level) 145184588Sdfr daemon(0, 0); 146184588Sdfr 147184588Sdfr memset(&sun, 0, sizeof sun); 148184588Sdfr sun.sun_family = AF_LOCAL; 149184588Sdfr unlink(_PATH_GSSDSOCK); 150184588Sdfr strcpy(sun.sun_path, _PATH_GSSDSOCK); 151184588Sdfr sun.sun_len = SUN_LEN(&sun); 152184588Sdfr fd = socket(AF_LOCAL, SOCK_STREAM, 0); 153184588Sdfr if (!fd) { 154245016Srmacklem if (debug_level == 0) { 155245016Srmacklem syslog(LOG_ERR, "Can't create local gssd socket"); 156245016Srmacklem exit(1); 157245016Srmacklem } 158184588Sdfr err(1, "Can't create local gssd socket"); 159184588Sdfr } 160184588Sdfr oldmask = umask(S_IXUSR|S_IRWXG|S_IRWXO); 161184588Sdfr if (bind(fd, (struct sockaddr *) &sun, sun.sun_len) < 0) { 162245016Srmacklem if (debug_level == 0) { 163245016Srmacklem syslog(LOG_ERR, "Can't bind local gssd socket"); 164245016Srmacklem exit(1); 165245016Srmacklem } 166184588Sdfr err(1, "Can't bind local gssd socket"); 167184588Sdfr } 168184588Sdfr umask(oldmask); 169184588Sdfr if (listen(fd, SOMAXCONN) < 0) { 170245016Srmacklem if (debug_level == 0) { 171245016Srmacklem syslog(LOG_ERR, "Can't listen on local gssd socket"); 172245016Srmacklem exit(1); 173245016Srmacklem } 174184588Sdfr err(1, "Can't listen on local gssd socket"); 175184588Sdfr } 176184588Sdfr xprt = svc_vc_create(fd, RPC_MAXDATASIZE, RPC_MAXDATASIZE); 177184588Sdfr if (!xprt) { 178245016Srmacklem if (debug_level == 0) { 179245016Srmacklem syslog(LOG_ERR, 180245016Srmacklem "Can't create transport for local gssd socket"); 181245016Srmacklem exit(1); 182245016Srmacklem } 183184588Sdfr err(1, "Can't create transport for local gssd socket"); 184184588Sdfr } 185184588Sdfr if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) { 186245016Srmacklem if (debug_level == 0) { 187245016Srmacklem syslog(LOG_ERR, 188245016Srmacklem "Can't register service for local gssd socket"); 189245016Srmacklem exit(1); 190245016Srmacklem } 191184588Sdfr err(1, "Can't register service for local gssd socket"); 192184588Sdfr } 193184588Sdfr 194184588Sdfr LIST_INIT(&gss_resources); 195184588Sdfr gss_next_id = 1; 196184588Sdfr gss_start_time = time(0); 197184588Sdfr 198184588Sdfr gssd_syscall(_PATH_GSSDSOCK); 199184588Sdfr svc_run(); 200184588Sdfr 201184588Sdfr return (0); 202184588Sdfr} 203184588Sdfr 204184588Sdfrstatic void 205184588Sdfrgssd_load_mech(void) 206184588Sdfr{ 207184588Sdfr FILE *fp; 208184588Sdfr char buf[256]; 209184588Sdfr char *p; 210184588Sdfr char *name, *oid, *lib, *kobj; 211184588Sdfr 212184588Sdfr fp = fopen(_PATH_GSS_MECH, "r"); 213184588Sdfr if (!fp) 214184588Sdfr return; 215184588Sdfr 216184588Sdfr while (fgets(buf, sizeof(buf), fp)) { 217184588Sdfr if (*buf == '#') 218184588Sdfr continue; 219184588Sdfr p = buf; 220184588Sdfr name = strsep(&p, "\t\n "); 221184588Sdfr if (p) while (isspace(*p)) p++; 222184588Sdfr oid = strsep(&p, "\t\n "); 223184588Sdfr if (p) while (isspace(*p)) p++; 224184588Sdfr lib = strsep(&p, "\t\n "); 225184588Sdfr if (p) while (isspace(*p)) p++; 226184588Sdfr kobj = strsep(&p, "\t\n "); 227184588Sdfr if (!name || !oid || !lib || !kobj) 228184588Sdfr continue; 229184588Sdfr 230184588Sdfr if (strcmp(kobj, "-")) { 231184588Sdfr /* 232184588Sdfr * Attempt to load the kernel module if its 233184588Sdfr * not already present. 234184588Sdfr */ 235184588Sdfr if (modfind(kobj) < 0) { 236184588Sdfr if (kldload(kobj) < 0) { 237184588Sdfr fprintf(stderr, 238184588Sdfr "%s: can't find or load kernel module %s for %s\n", 239184588Sdfr getprogname(), kobj, name); 240184588Sdfr } 241184588Sdfr } 242184588Sdfr } 243184588Sdfr } 244184588Sdfr fclose(fp); 245184588Sdfr} 246184588Sdfr 247184588Sdfrstatic void * 248184588Sdfrgssd_find_resource(uint64_t id) 249184588Sdfr{ 250184588Sdfr struct gss_resource *gr; 251184588Sdfr 252184588Sdfr if (!id) 253184588Sdfr return (NULL); 254184588Sdfr 255184588Sdfr LIST_FOREACH(gr, &gss_resources, gr_link) 256184588Sdfr if (gr->gr_id == id) 257184588Sdfr return (gr->gr_res); 258184588Sdfr 259184588Sdfr return (NULL); 260184588Sdfr} 261184588Sdfr 262184588Sdfrstatic uint64_t 263184588Sdfrgssd_make_resource(void *res) 264184588Sdfr{ 265184588Sdfr struct gss_resource *gr; 266184588Sdfr 267184588Sdfr if (!res) 268184588Sdfr return (0); 269184588Sdfr 270184588Sdfr gr = malloc(sizeof(struct gss_resource)); 271184588Sdfr if (!gr) 272184588Sdfr return (0); 273184588Sdfr gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32); 274184588Sdfr gr->gr_res = res; 275184588Sdfr LIST_INSERT_HEAD(&gss_resources, gr, gr_link); 276184588Sdfr gss_resource_count++; 277184588Sdfr if (debug_level > 1) 278184588Sdfr printf("%d resources allocated\n", gss_resource_count); 279184588Sdfr 280184588Sdfr return (gr->gr_id); 281184588Sdfr} 282184588Sdfr 283184588Sdfrstatic void 284184588Sdfrgssd_delete_resource(uint64_t id) 285184588Sdfr{ 286184588Sdfr struct gss_resource *gr; 287184588Sdfr 288184588Sdfr LIST_FOREACH(gr, &gss_resources, gr_link) { 289184588Sdfr if (gr->gr_id == id) { 290184588Sdfr LIST_REMOVE(gr, gr_link); 291184588Sdfr free(gr); 292184588Sdfr gss_resource_count--; 293184588Sdfr if (debug_level > 1) 294184588Sdfr printf("%d resources allocated\n", 295184588Sdfr gss_resource_count); 296184588Sdfr return; 297184588Sdfr } 298184588Sdfr } 299184588Sdfr} 300184588Sdfr 301184588Sdfrbool_t 302184588Sdfrgssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) 303184588Sdfr{ 304184588Sdfr 305184588Sdfr return (TRUE); 306184588Sdfr} 307184588Sdfr 308184588Sdfrbool_t 309184588Sdfrgssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp) 310184588Sdfr{ 311184588Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 312184588Sdfr gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 313184588Sdfr gss_name_t name = GSS_C_NO_NAME; 314245089Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 315245089Srmacklem int gotone; 316184588Sdfr 317245089Srmacklem memset(result, 0, sizeof(*result)); 318245089Srmacklem if (ccfile_dirlist[0] != '\0' && argp->cred == 0) { 319245089Srmacklem /* 320245089Srmacklem * For the "-s" case and no credentials provided as an 321245089Srmacklem * argument, search the directory list for an appropriate 322245089Srmacklem * credential cache file. If the search fails, return failure. 323245089Srmacklem */ 324245089Srmacklem gotone = 0; 325245089Srmacklem cp = ccfile_dirlist; 326245089Srmacklem do { 327245089Srmacklem cp2 = strchr(cp, ':'); 328245089Srmacklem if (cp2 != NULL) 329245089Srmacklem *cp2 = '\0'; 330245089Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 331245089Srmacklem if (gotone != 0) 332245089Srmacklem break; 333245089Srmacklem if (cp2 != NULL) 334245089Srmacklem *cp2++ = ':'; 335245089Srmacklem cp = cp2; 336245089Srmacklem } while (cp != NULL && *cp != '\0'); 337245089Srmacklem if (gotone == 0) { 338245089Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 339245089Srmacklem return (TRUE); 340245089Srmacklem } 341245089Srmacklem } else { 342245089Srmacklem /* 343245089Srmacklem * If there wasn't a "-s" option or the credentials have 344245089Srmacklem * been provided as an argument, do it the old way. 345245089Srmacklem * When credentials are provided, the uid should be root. 346245089Srmacklem */ 347245089Srmacklem if (argp->cred != 0 && argp->uid != 0) { 348245089Srmacklem if (debug_level == 0) 349245089Srmacklem syslog(LOG_ERR, "gss_init_sec_context:" 350245089Srmacklem " cred for non-root"); 351245089Srmacklem else 352245089Srmacklem fprintf(stderr, "gss_init_sec_context:" 353245089Srmacklem " cred for non-root\n"); 354245089Srmacklem } 355245089Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 356245089Srmacklem (int) argp->uid); 357245089Srmacklem } 358184588Sdfr setenv("KRB5CCNAME", ccname, TRUE); 359184588Sdfr 360184588Sdfr if (argp->cred) { 361184588Sdfr cred = gssd_find_resource(argp->cred); 362184588Sdfr if (!cred) { 363184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 364184588Sdfr return (TRUE); 365184588Sdfr } 366184588Sdfr } 367184588Sdfr if (argp->ctx) { 368184588Sdfr ctx = gssd_find_resource(argp->ctx); 369184588Sdfr if (!ctx) { 370184588Sdfr result->major_status = GSS_S_CONTEXT_EXPIRED; 371184588Sdfr return (TRUE); 372184588Sdfr } 373184588Sdfr } 374184588Sdfr if (argp->name) { 375184588Sdfr name = gssd_find_resource(argp->name); 376184588Sdfr if (!name) { 377184588Sdfr result->major_status = GSS_S_BAD_NAME; 378184588Sdfr return (TRUE); 379184588Sdfr } 380184588Sdfr } 381184588Sdfr 382184588Sdfr result->major_status = gss_init_sec_context(&result->minor_status, 383184588Sdfr cred, &ctx, name, argp->mech_type, 384184588Sdfr argp->req_flags, argp->time_req, argp->input_chan_bindings, 385184588Sdfr &argp->input_token, &result->actual_mech_type, 386184588Sdfr &result->output_token, &result->ret_flags, &result->time_rec); 387184588Sdfr 388184588Sdfr if (result->major_status == GSS_S_COMPLETE 389184588Sdfr || result->major_status == GSS_S_CONTINUE_NEEDED) { 390184588Sdfr if (argp->ctx) 391184588Sdfr result->ctx = argp->ctx; 392184588Sdfr else 393184588Sdfr result->ctx = gssd_make_resource(ctx); 394184588Sdfr } 395184588Sdfr 396184588Sdfr return (TRUE); 397184588Sdfr} 398184588Sdfr 399184588Sdfrbool_t 400184588Sdfrgssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp) 401184588Sdfr{ 402184588Sdfr gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 403184588Sdfr gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 404184588Sdfr gss_name_t src_name; 405184588Sdfr gss_cred_id_t delegated_cred_handle; 406184588Sdfr 407184588Sdfr memset(result, 0, sizeof(*result)); 408184588Sdfr if (argp->ctx) { 409184588Sdfr ctx = gssd_find_resource(argp->ctx); 410184588Sdfr if (!ctx) { 411184588Sdfr result->major_status = GSS_S_CONTEXT_EXPIRED; 412184588Sdfr return (TRUE); 413184588Sdfr } 414184588Sdfr } 415184588Sdfr if (argp->cred) { 416184588Sdfr cred = gssd_find_resource(argp->cred); 417184588Sdfr if (!cred) { 418184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 419184588Sdfr return (TRUE); 420184588Sdfr } 421184588Sdfr } 422184588Sdfr 423184588Sdfr memset(result, 0, sizeof(*result)); 424184588Sdfr result->major_status = gss_accept_sec_context(&result->minor_status, 425184588Sdfr &ctx, cred, &argp->input_token, argp->input_chan_bindings, 426184588Sdfr &src_name, &result->mech_type, &result->output_token, 427184588Sdfr &result->ret_flags, &result->time_rec, 428184588Sdfr &delegated_cred_handle); 429184588Sdfr 430184588Sdfr if (result->major_status == GSS_S_COMPLETE 431184588Sdfr || result->major_status == GSS_S_CONTINUE_NEEDED) { 432184588Sdfr if (argp->ctx) 433184588Sdfr result->ctx = argp->ctx; 434184588Sdfr else 435184588Sdfr result->ctx = gssd_make_resource(ctx); 436184588Sdfr result->src_name = gssd_make_resource(src_name); 437184588Sdfr result->delegated_cred_handle = 438184588Sdfr gssd_make_resource(delegated_cred_handle); 439184588Sdfr } 440184588Sdfr 441184588Sdfr return (TRUE); 442184588Sdfr} 443184588Sdfr 444184588Sdfrbool_t 445184588Sdfrgssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp) 446184588Sdfr{ 447184588Sdfr gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 448184588Sdfr 449184588Sdfr if (ctx) { 450184588Sdfr result->major_status = gss_delete_sec_context( 451184588Sdfr &result->minor_status, &ctx, &result->output_token); 452184588Sdfr gssd_delete_resource(argp->ctx); 453184588Sdfr } else { 454184588Sdfr result->major_status = GSS_S_COMPLETE; 455184588Sdfr result->minor_status = 0; 456184588Sdfr } 457184588Sdfr 458184588Sdfr return (TRUE); 459184588Sdfr} 460184588Sdfr 461184588Sdfrbool_t 462184588Sdfrgssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp) 463184588Sdfr{ 464184588Sdfr gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 465184588Sdfr 466184588Sdfr if (ctx) { 467184588Sdfr result->major_status = gss_export_sec_context( 468184588Sdfr &result->minor_status, &ctx, 469184588Sdfr &result->interprocess_token); 470184588Sdfr result->format = KGSS_HEIMDAL_1_1; 471184588Sdfr gssd_delete_resource(argp->ctx); 472184588Sdfr } else { 473184588Sdfr result->major_status = GSS_S_FAILURE; 474184588Sdfr result->minor_status = 0; 475184588Sdfr result->interprocess_token.length = 0; 476184588Sdfr result->interprocess_token.value = NULL; 477184588Sdfr } 478184588Sdfr 479184588Sdfr return (TRUE); 480184588Sdfr} 481184588Sdfr 482184588Sdfrbool_t 483184588Sdfrgssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp) 484184588Sdfr{ 485184588Sdfr gss_name_t name; 486184588Sdfr 487184588Sdfr result->major_status = gss_import_name(&result->minor_status, 488184588Sdfr &argp->input_name_buffer, argp->input_name_type, &name); 489184588Sdfr 490184588Sdfr if (result->major_status == GSS_S_COMPLETE) 491184588Sdfr result->output_name = gssd_make_resource(name); 492184588Sdfr else 493184588Sdfr result->output_name = 0; 494184588Sdfr 495184588Sdfr return (TRUE); 496184588Sdfr} 497184588Sdfr 498184588Sdfrbool_t 499184588Sdfrgssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp) 500184588Sdfr{ 501184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 502184588Sdfr gss_name_t output_name; 503184588Sdfr 504184588Sdfr memset(result, 0, sizeof(*result)); 505184588Sdfr if (!name) { 506184588Sdfr result->major_status = GSS_S_BAD_NAME; 507184588Sdfr return (TRUE); 508184588Sdfr } 509184588Sdfr 510184588Sdfr result->major_status = gss_canonicalize_name(&result->minor_status, 511184588Sdfr name, argp->mech_type, &output_name); 512184588Sdfr 513184588Sdfr if (result->major_status == GSS_S_COMPLETE) 514184588Sdfr result->output_name = gssd_make_resource(output_name); 515184588Sdfr else 516184588Sdfr result->output_name = 0; 517184588Sdfr 518184588Sdfr return (TRUE); 519184588Sdfr} 520184588Sdfr 521184588Sdfrbool_t 522184588Sdfrgssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp) 523184588Sdfr{ 524184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 525184588Sdfr 526184588Sdfr memset(result, 0, sizeof(*result)); 527184588Sdfr if (!name) { 528184588Sdfr result->major_status = GSS_S_BAD_NAME; 529184588Sdfr return (TRUE); 530184588Sdfr } 531184588Sdfr 532184588Sdfr result->major_status = gss_export_name(&result->minor_status, 533184588Sdfr name, &result->exported_name); 534184588Sdfr 535184588Sdfr return (TRUE); 536184588Sdfr} 537184588Sdfr 538184588Sdfrbool_t 539184588Sdfrgssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp) 540184588Sdfr{ 541184588Sdfr gss_name_t name = gssd_find_resource(argp->input_name); 542184588Sdfr 543184588Sdfr if (name) { 544184588Sdfr result->major_status = gss_release_name(&result->minor_status, 545184588Sdfr &name); 546184588Sdfr gssd_delete_resource(argp->input_name); 547184588Sdfr } else { 548184588Sdfr result->major_status = GSS_S_COMPLETE; 549184588Sdfr result->minor_status = 0; 550184588Sdfr } 551184588Sdfr 552184588Sdfr return (TRUE); 553184588Sdfr} 554184588Sdfr 555184588Sdfrbool_t 556184588Sdfrgssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp) 557184588Sdfr{ 558184588Sdfr gss_name_t name = gssd_find_resource(argp->pname); 559184588Sdfr uid_t uid; 560184588Sdfr char buf[128]; 561184588Sdfr struct passwd pwd, *pw; 562184588Sdfr 563184588Sdfr memset(result, 0, sizeof(*result)); 564184588Sdfr if (name) { 565184588Sdfr result->major_status = 566184588Sdfr gss_pname_to_uid(&result->minor_status, 567184588Sdfr name, argp->mech, &uid); 568184588Sdfr if (result->major_status == GSS_S_COMPLETE) { 569184588Sdfr result->uid = uid; 570184588Sdfr getpwuid_r(uid, &pwd, buf, sizeof(buf), &pw); 571184588Sdfr if (pw) { 572184588Sdfr int len = NGRPS; 573184588Sdfr int groups[NGRPS]; 574184588Sdfr result->gid = pw->pw_gid; 575184588Sdfr getgrouplist(pw->pw_name, pw->pw_gid, 576184588Sdfr groups, &len); 577184588Sdfr result->gidlist.gidlist_len = len; 578184588Sdfr result->gidlist.gidlist_val = 579184588Sdfr mem_alloc(len * sizeof(int)); 580184588Sdfr memcpy(result->gidlist.gidlist_val, groups, 581184588Sdfr len * sizeof(int)); 582184588Sdfr } else { 583184588Sdfr result->gid = 65534; 584184588Sdfr result->gidlist.gidlist_len = 0; 585184588Sdfr result->gidlist.gidlist_val = NULL; 586184588Sdfr } 587184588Sdfr } 588184588Sdfr } else { 589184588Sdfr result->major_status = GSS_S_BAD_NAME; 590184588Sdfr result->minor_status = 0; 591184588Sdfr } 592184588Sdfr 593184588Sdfr return (TRUE); 594184588Sdfr} 595184588Sdfr 596184588Sdfrbool_t 597184588Sdfrgssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp) 598184588Sdfr{ 599184588Sdfr gss_name_t desired_name = GSS_C_NO_NAME; 600184588Sdfr gss_cred_id_t cred; 601245089Srmacklem char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 602245089Srmacklem int gotone; 603184588Sdfr 604245089Srmacklem memset(result, 0, sizeof(*result)); 605245089Srmacklem if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) { 606245089Srmacklem /* 607245089Srmacklem * For the "-s" case and no name provided as an 608245089Srmacklem * argument, search the directory list for an appropriate 609245089Srmacklem * credential cache file. If the search fails, return failure. 610245089Srmacklem */ 611245089Srmacklem gotone = 0; 612245089Srmacklem cp = ccfile_dirlist; 613245089Srmacklem do { 614245089Srmacklem cp2 = strchr(cp, ':'); 615245089Srmacklem if (cp2 != NULL) 616245089Srmacklem *cp2 = '\0'; 617245089Srmacklem gotone = find_ccache_file(cp, argp->uid, ccname); 618245089Srmacklem if (gotone != 0) 619245089Srmacklem break; 620245089Srmacklem if (cp2 != NULL) 621245089Srmacklem *cp2++ = ':'; 622245089Srmacklem cp = cp2; 623245089Srmacklem } while (cp != NULL && *cp != '\0'); 624245089Srmacklem if (gotone == 0) { 625245089Srmacklem result->major_status = GSS_S_CREDENTIALS_EXPIRED; 626245089Srmacklem return (TRUE); 627245089Srmacklem } 628245089Srmacklem } else { 629245089Srmacklem /* 630245089Srmacklem * If there wasn't a "-s" option or the name has 631245089Srmacklem * been provided as an argument, do it the old way. 632245089Srmacklem * When a name is provided, it will normally exist in the 633245089Srmacklem * default keytab file and the uid will be root. 634245089Srmacklem */ 635245089Srmacklem if (argp->desired_name != 0 && argp->uid != 0) { 636245089Srmacklem if (debug_level == 0) 637245089Srmacklem syslog(LOG_ERR, "gss_acquire_cred:" 638245089Srmacklem " principal_name for non-root"); 639245089Srmacklem else 640245089Srmacklem fprintf(stderr, "gss_acquire_cred:" 641245089Srmacklem " principal_name for non-root\n"); 642245089Srmacklem } 643245089Srmacklem snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 644245089Srmacklem (int) argp->uid); 645245089Srmacklem } 646184588Sdfr setenv("KRB5CCNAME", ccname, TRUE); 647184588Sdfr 648184588Sdfr if (argp->desired_name) { 649184588Sdfr desired_name = gssd_find_resource(argp->desired_name); 650184588Sdfr if (!desired_name) { 651184588Sdfr result->major_status = GSS_S_BAD_NAME; 652184588Sdfr return (TRUE); 653184588Sdfr } 654184588Sdfr } 655184588Sdfr 656184588Sdfr result->major_status = gss_acquire_cred(&result->minor_status, 657184588Sdfr desired_name, argp->time_req, argp->desired_mechs, 658184588Sdfr argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec); 659184588Sdfr 660184588Sdfr if (result->major_status == GSS_S_COMPLETE) 661184588Sdfr result->output_cred = gssd_make_resource(cred); 662184588Sdfr else 663184588Sdfr result->output_cred = 0; 664184588Sdfr 665184588Sdfr return (TRUE); 666184588Sdfr} 667184588Sdfr 668184588Sdfrbool_t 669184588Sdfrgssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp) 670184588Sdfr{ 671184588Sdfr gss_cred_id_t cred = gssd_find_resource(argp->cred); 672184588Sdfr 673184588Sdfr memset(result, 0, sizeof(*result)); 674184588Sdfr if (!cred) { 675184588Sdfr result->major_status = GSS_S_CREDENTIALS_EXPIRED; 676184588Sdfr return (TRUE); 677184588Sdfr } 678184588Sdfr 679184588Sdfr result->major_status = gss_set_cred_option(&result->minor_status, 680184588Sdfr &cred, argp->option_name, &argp->option_value); 681184588Sdfr 682184588Sdfr return (TRUE); 683184588Sdfr} 684184588Sdfr 685184588Sdfrbool_t 686184588Sdfrgssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp) 687184588Sdfr{ 688184588Sdfr gss_cred_id_t cred = gssd_find_resource(argp->cred); 689184588Sdfr 690184588Sdfr if (cred) { 691184588Sdfr result->major_status = gss_release_cred(&result->minor_status, 692184588Sdfr &cred); 693184588Sdfr gssd_delete_resource(argp->cred); 694184588Sdfr } else { 695184588Sdfr result->major_status = GSS_S_COMPLETE; 696184588Sdfr result->minor_status = 0; 697184588Sdfr } 698184588Sdfr 699184588Sdfr return (TRUE); 700184588Sdfr} 701184588Sdfr 702184588Sdfrbool_t 703184588Sdfrgssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp) 704184588Sdfr{ 705184588Sdfr 706184588Sdfr result->message_context = argp->message_context; 707184588Sdfr result->major_status = gss_display_status(&result->minor_status, 708184588Sdfr argp->status_value, argp->status_type, argp->mech_type, 709184588Sdfr &result->message_context, &result->status_string); 710184588Sdfr 711184588Sdfr return (TRUE); 712184588Sdfr} 713184588Sdfr 714184588Sdfrint 715184588Sdfrgssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) 716184588Sdfr{ 717184588Sdfr /* 718184588Sdfr * We don't use XDR to free the results - anything which was 719184588Sdfr * allocated came from GSS-API. We use xdr_result to figure 720184588Sdfr * out what to do. 721184588Sdfr */ 722184588Sdfr OM_uint32 junk; 723184588Sdfr 724184588Sdfr if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) { 725184588Sdfr init_sec_context_res *p = (init_sec_context_res *) result; 726184588Sdfr gss_release_buffer(&junk, &p->output_token); 727184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) { 728184588Sdfr accept_sec_context_res *p = (accept_sec_context_res *) result; 729184588Sdfr gss_release_buffer(&junk, &p->output_token); 730184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) { 731184588Sdfr delete_sec_context_res *p = (delete_sec_context_res *) result; 732184588Sdfr gss_release_buffer(&junk, &p->output_token); 733184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) { 734184588Sdfr export_sec_context_res *p = (export_sec_context_res *) result; 735184588Sdfr if (p->interprocess_token.length) 736184588Sdfr memset(p->interprocess_token.value, 0, 737184588Sdfr p->interprocess_token.length); 738184588Sdfr gss_release_buffer(&junk, &p->interprocess_token); 739184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_export_name_res) { 740184588Sdfr export_name_res *p = (export_name_res *) result; 741184588Sdfr gss_release_buffer(&junk, &p->exported_name); 742184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) { 743184588Sdfr acquire_cred_res *p = (acquire_cred_res *) result; 744184588Sdfr gss_release_oid_set(&junk, &p->actual_mechs); 745184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) { 746184588Sdfr pname_to_uid_res *p = (pname_to_uid_res *) result; 747184588Sdfr if (p->gidlist.gidlist_val) 748184588Sdfr free(p->gidlist.gidlist_val); 749184588Sdfr } else if (xdr_result == (xdrproc_t) xdr_display_status_res) { 750184588Sdfr display_status_res *p = (display_status_res *) result; 751184588Sdfr gss_release_buffer(&junk, &p->status_string); 752184588Sdfr } 753184588Sdfr 754184588Sdfr return (TRUE); 755184588Sdfr} 756245089Srmacklem 757245089Srmacklem/* 758245089Srmacklem * Search a directory for the most likely candidate to be used as the 759245089Srmacklem * credential cache for a uid. If successful, return 1 and fill the 760245089Srmacklem * file's path id into "rpath". Otherwise, return 0. 761245089Srmacklem */ 762245089Srmacklemstatic int 763245089Srmacklemfind_ccache_file(const char *dirpath, uid_t uid, char *rpath) 764245089Srmacklem{ 765245089Srmacklem DIR *dirp; 766245089Srmacklem struct dirent *dp; 767245089Srmacklem struct stat sb; 768245089Srmacklem time_t exptime, oexptime; 769245089Srmacklem int gotone, len, rating, orating; 770245089Srmacklem char namepath[PATH_MAX + 5 + 1]; 771245089Srmacklem char retpath[PATH_MAX + 5 + 1]; 772245089Srmacklem 773245089Srmacklem dirp = opendir(dirpath); 774245089Srmacklem if (dirp == NULL) 775245089Srmacklem return (0); 776245089Srmacklem gotone = 0; 777245089Srmacklem orating = 0; 778245089Srmacklem oexptime = 0; 779245089Srmacklem while ((dp = readdir(dirp)) != NULL) { 780245089Srmacklem len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath, 781245089Srmacklem dp->d_name); 782245089Srmacklem if (len < sizeof(namepath) && 783245089Srmacklem strstr(dp->d_name, ccfile_substring) != NULL && 784245089Srmacklem lstat(namepath, &sb) >= 0 && 785245089Srmacklem sb.st_uid == uid && 786245089Srmacklem S_ISREG(sb.st_mode)) { 787245089Srmacklem len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s", 788245089Srmacklem dirpath, dp->d_name); 789245089Srmacklem if (len < sizeof(namepath) && 790245089Srmacklem is_a_valid_tgt_cache(namepath, uid, &rating, 791245089Srmacklem &exptime) != 0) { 792245089Srmacklem if (gotone == 0 || rating > orating || 793245089Srmacklem (rating == orating && exptime > oexptime)) { 794245089Srmacklem orating = rating; 795245089Srmacklem oexptime = exptime; 796245089Srmacklem strcpy(retpath, namepath); 797245089Srmacklem gotone = 1; 798245089Srmacklem } 799245089Srmacklem } 800245089Srmacklem } 801245089Srmacklem } 802245089Srmacklem closedir(dirp); 803245089Srmacklem if (gotone != 0) { 804245089Srmacklem strcpy(rpath, retpath); 805245089Srmacklem return (1); 806245089Srmacklem } 807245089Srmacklem return (0); 808245089Srmacklem} 809245089Srmacklem 810245089Srmacklem/* 811245089Srmacklem * Try to determine if the file is a valid tgt cache file. 812245089Srmacklem * Check that the file has a valid tgt for a principal. 813245089Srmacklem * If it does, return 1, otherwise return 0. 814245089Srmacklem * It also returns a "rating" and the expiry time for the TGT, when found. 815245089Srmacklem * This "rating" is higher based on heuristics that make it more 816245089Srmacklem * likely to be the correct credential cache file to use. It can 817245089Srmacklem * be used by the caller, along with expiry time, to select from 818245089Srmacklem * multiple credential cache files. 819245089Srmacklem */ 820245089Srmacklemstatic int 821245089Srmacklemis_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating, 822245089Srmacklem time_t *retexptime) 823245089Srmacklem{ 824245089Srmacklem#ifndef WITHOUT_KERBEROS 825245089Srmacklem krb5_context context; 826245089Srmacklem krb5_principal princ; 827245089Srmacklem krb5_ccache ccache; 828245089Srmacklem krb5_error_code retval; 829245089Srmacklem krb5_cc_cursor curse; 830245089Srmacklem krb5_creds krbcred; 831245089Srmacklem int gotone, orating, rating, ret; 832245089Srmacklem struct passwd *pw; 833245089Srmacklem char *cp, *cp2, *pname; 834245089Srmacklem time_t exptime; 835245089Srmacklem 836245089Srmacklem /* Find a likely name for the uid principal. */ 837245089Srmacklem pw = getpwuid(uid); 838245089Srmacklem 839245089Srmacklem /* 840245089Srmacklem * Do a bunch of krb5 library stuff to try and determine if 841245089Srmacklem * this file is a credentials cache with an appropriate TGT 842245089Srmacklem * in it. 843245089Srmacklem */ 844245089Srmacklem retval = krb5_init_context(&context); 845245089Srmacklem if (retval != 0) 846245089Srmacklem return (0); 847245089Srmacklem retval = krb5_cc_resolve(context, filepath, &ccache); 848245089Srmacklem if (retval != 0) { 849245089Srmacklem krb5_free_context(context); 850245089Srmacklem return (0); 851245089Srmacklem } 852245089Srmacklem ret = 0; 853245089Srmacklem orating = 0; 854245089Srmacklem exptime = 0; 855245089Srmacklem retval = krb5_cc_start_seq_get(context, ccache, &curse); 856245089Srmacklem if (retval == 0) { 857245089Srmacklem while ((retval = krb5_cc_next_cred(context, ccache, &curse, 858245089Srmacklem &krbcred)) == 0) { 859245089Srmacklem gotone = 0; 860245089Srmacklem rating = 0; 861245089Srmacklem retval = krb5_unparse_name(context, krbcred.server, 862245089Srmacklem &pname); 863245089Srmacklem if (retval == 0) { 864245089Srmacklem cp = strchr(pname, '/'); 865245089Srmacklem if (cp != NULL) { 866245089Srmacklem *cp++ = '\0'; 867245089Srmacklem if (strcmp(pname, "krbtgt") == 0 && 868245089Srmacklem krbcred.times.endtime > time(NULL) 869245089Srmacklem ) { 870245089Srmacklem gotone = 1; 871245089Srmacklem /* 872245089Srmacklem * Test to see if this is a 873245089Srmacklem * tgt for cross-realm auth. 874245089Srmacklem * Rate it higher, if it is not. 875245089Srmacklem */ 876245089Srmacklem cp2 = strchr(cp, '@'); 877245089Srmacklem if (cp2 != NULL) { 878245089Srmacklem *cp2++ = '\0'; 879245089Srmacklem if (strcmp(cp, cp2) == 880245089Srmacklem 0) 881245089Srmacklem rating++; 882245089Srmacklem } 883245089Srmacklem } 884245089Srmacklem } 885245089Srmacklem free(pname); 886245089Srmacklem } 887245089Srmacklem if (gotone != 0) { 888245089Srmacklem retval = krb5_unparse_name(context, 889245089Srmacklem krbcred.client, &pname); 890245089Srmacklem if (retval == 0) { 891245089Srmacklem cp = strchr(pname, '@'); 892245089Srmacklem if (cp != NULL) { 893245089Srmacklem *cp++ = '\0'; 894245089Srmacklem if (pw != NULL && strcmp(pname, 895245089Srmacklem pw->pw_name) == 0) 896245089Srmacklem rating++; 897245089Srmacklem if (strchr(pname, '/') == NULL) 898245089Srmacklem rating++; 899245089Srmacklem if (pref_realm[0] != '\0' && 900245089Srmacklem strcmp(cp, pref_realm) == 0) 901245089Srmacklem rating++; 902245089Srmacklem } 903245089Srmacklem } 904245089Srmacklem free(pname); 905245089Srmacklem if (rating > orating) { 906245089Srmacklem orating = rating; 907245089Srmacklem exptime = krbcred.times.endtime; 908245089Srmacklem } else if (rating == orating && 909245089Srmacklem krbcred.times.endtime > exptime) 910245089Srmacklem exptime = krbcred.times.endtime; 911245089Srmacklem ret = 1; 912245089Srmacklem } 913245089Srmacklem krb5_free_cred_contents(context, &krbcred); 914245089Srmacklem } 915245089Srmacklem krb5_cc_end_seq_get(context, ccache, &curse); 916245089Srmacklem } 917245089Srmacklem krb5_cc_close(context, ccache); 918245089Srmacklem krb5_free_context(context); 919245089Srmacklem if (ret != 0) { 920245089Srmacklem *retrating = orating; 921245089Srmacklem *retexptime = exptime; 922245089Srmacklem } 923245089Srmacklem return (ret); 924245089Srmacklem#else /* WITHOUT_KERBEROS */ 925245089Srmacklem return (0); 926245089Srmacklem#endif /* !WITHOUT_KERBEROS */ 927245089Srmacklem} 928245089Srmacklem 929