1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2008 Isilon Inc http://www.isilon.com/ 5 * Authors: Doug Rabson <dfr@rabson.org> 6 * Developed with Red Inc: Alfred Perlstein <alfred@freebsd.org> 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/stat.h> 32 #include <sys/linker.h> 33 #include <sys/module.h> 34 #include <sys/queue.h> 35 #include <sys/socket.h> 36 #include <sys/sysctl.h> 37 #include <sys/syslog.h> 38 #include <ctype.h> 39 #include <dirent.h> 40 #include <err.h> 41 #include <errno.h> 42 #include <krb5.h> 43 #include <netdb.h> 44 #include <pwd.h> 45 #include <signal.h> 46 #include <stdarg.h> 47 #include <stdio.h> 48 #include <stdlib.h> 49 #include <string.h> 50 #include <unistd.h> 51 #include <arpa/inet.h> 52 #include <netinet/in.h> 53 #include <gssapi/gssapi.h> 54 #ifdef MK_MITKRB5 55 #include <gssapi/gssapi_krb5.h> 56 #endif 57 #include <rpc/rpc.h> 58 #include <rpc/rpc_com.h> 59 60 #include "gssd.h" 61 62 #ifndef _PATH_GSS_MECH 63 #define _PATH_GSS_MECH "/etc/gss/mech" 64 #endif 65 #define GSSD_CREDENTIAL_CACHE_FILE "/tmp/krb5cc_gssd" 66 67 struct gss_resource { 68 LIST_ENTRY(gss_resource) gr_link; 69 uint64_t gr_id; /* identifier exported to kernel */ 70 void* gr_res; /* GSS-API resource pointer */ 71 }; 72 LIST_HEAD(gss_resource_list, gss_resource) gss_resources; 73 int gss_resource_count; 74 uint32_t gss_next_id; 75 uint32_t gss_start_time; 76 int debug_level; 77 static char ccfile_dirlist[PATH_MAX + 1], ccfile_substring[NAME_MAX + 1]; 78 static char pref_realm[1024]; 79 static int verbose; 80 static int hostbased_initiator_cred; 81 /* 1.2.752.43.13.14 */ 82 static gss_OID_desc gss_krb5_set_allowable_enctypes_x_desc = 83 {6, (void *) "\x2a\x85\x70\x2b\x0d\x0e"}; 84 static gss_OID GSS_KRB5_SET_ALLOWABLE_ENCTYPES_X = 85 &gss_krb5_set_allowable_enctypes_x_desc; 86 static gss_OID_desc gss_krb5_mech_oid_x_desc = 87 {9, (void *) "\x2a\x86\x48\x86\xf7\x12\x01\x02\x02" }; 88 static gss_OID GSS_KRB5_MECH_OID_X = 89 &gss_krb5_mech_oid_x_desc; 90 91 static void gssd_load_mech(void); 92 static int find_ccache_file(const char *, uid_t, char *); 93 static int is_a_valid_tgt_cache(const char *, uid_t, int *, time_t *); 94 static void gssd_verbose_out(const char *, ...); 95 static krb5_error_code gssd_get_cc_from_keytab(const char *); 96 static OM_uint32 gssd_get_user_cred(OM_uint32 *, uid_t, gss_cred_id_t *); 97 void gssd_terminate(int); 98 99 extern void gssd_1(struct svc_req *rqstp, SVCXPRT *transp); 100 101 int 102 main(int argc, char **argv) 103 { 104 /* 105 * We provide an RPC service on a Netlink socket. The kernel's GSS API 106 * code will multicast its calls, we will listen to them, receive them, 107 * process them and reply. 108 */ 109 int oldmask, ch, debug, jailed; 110 SVCXPRT *xprt; 111 size_t jailed_size; 112 113 /* 114 * Initialize the credential cache file name substring and the 115 * search directory list. 116 */ 117 strlcpy(ccfile_substring, "krb5cc_", sizeof(ccfile_substring)); 118 ccfile_dirlist[0] = '\0'; 119 pref_realm[0] = '\0'; 120 debug = 0; 121 verbose = 0; 122 while ((ch = getopt(argc, argv, "dhvs:c:r:")) != -1) { 123 switch (ch) { 124 case 'd': 125 debug_level++; 126 break; 127 case 'h': 128 /* 129 * Enable use of a host based initiator credential 130 * in the default keytab file. 131 */ 132 hostbased_initiator_cred = 1; 133 break; 134 case 'v': 135 verbose = 1; 136 break; 137 case 's': 138 /* 139 * Set the directory search list. This enables use of 140 * find_ccache_file() to search the directories for a 141 * suitable credentials cache file. 142 */ 143 strlcpy(ccfile_dirlist, optarg, sizeof(ccfile_dirlist)); 144 break; 145 case 'c': 146 /* 147 * Specify a non-default credential cache file 148 * substring. 149 */ 150 strlcpy(ccfile_substring, optarg, 151 sizeof(ccfile_substring)); 152 break; 153 case 'r': 154 /* 155 * Set the preferred realm for the credential cache tgt. 156 */ 157 strlcpy(pref_realm, optarg, sizeof(pref_realm)); 158 break; 159 default: 160 fprintf(stderr, 161 "usage: %s [-d] [-s dir-list] [-c file-substring]" 162 " [-r preferred-realm]\n", argv[0]); 163 exit(1); 164 break; 165 } 166 } 167 168 gssd_load_mech(); 169 170 if (!debug_level) { 171 if (daemon(0, 0) != 0) 172 err(1, "Can't daemonize"); 173 signal(SIGINT, SIG_IGN); 174 signal(SIGQUIT, SIG_IGN); 175 signal(SIGHUP, SIG_IGN); 176 } 177 signal(SIGTERM, gssd_terminate); 178 signal(SIGPIPE, gssd_terminate); 179 180 if ((xprt = svc_nl_create("kgss")) == NULL) { 181 if (debug_level == 0) { 182 syslog(LOG_ERR, 183 "Can't create transport for local gssd socket"); 184 exit(1); 185 } 186 err(1, "Can't create transport for local gssd socket"); 187 } 188 if (!svc_reg(xprt, GSSD, GSSDVERS, gssd_1, NULL)) { 189 if (debug_level == 0) { 190 syslog(LOG_ERR, 191 "Can't register service for local gssd socket"); 192 exit(1); 193 } 194 err(1, "Can't register service for local gssd socket"); 195 } 196 197 LIST_INIT(&gss_resources); 198 gss_next_id = 1; 199 gss_start_time = time(0); 200 svc_run(); 201 202 return (0); 203 } 204 205 static void 206 gssd_load_mech(void) 207 { 208 FILE *fp; 209 char buf[256]; 210 char *p; 211 char *name, *oid, *lib, *kobj; 212 213 fp = fopen(_PATH_GSS_MECH, "r"); 214 if (!fp) 215 return; 216 217 while (fgets(buf, sizeof(buf), fp)) { 218 if (*buf == '#') 219 continue; 220 p = buf; 221 name = strsep(&p, "\t\n "); 222 if (p) while (isspace(*p)) p++; 223 oid = strsep(&p, "\t\n "); 224 if (p) while (isspace(*p)) p++; 225 lib = strsep(&p, "\t\n "); 226 if (p) while (isspace(*p)) p++; 227 kobj = strsep(&p, "\t\n "); 228 if (!name || !oid || !lib || !kobj) 229 continue; 230 231 if (strcmp(kobj, "-")) { 232 /* 233 * Attempt to load the kernel module if its 234 * not already present. 235 */ 236 if (modfind(kobj) < 0) { 237 if (kldload(kobj) < 0) { 238 fprintf(stderr, 239 "%s: can't find or load kernel module %s for %s\n", 240 getprogname(), kobj, name); 241 } 242 } 243 } 244 } 245 fclose(fp); 246 } 247 248 static void * 249 gssd_find_resource(uint64_t id) 250 { 251 struct gss_resource *gr; 252 253 if (!id) 254 return (NULL); 255 256 LIST_FOREACH(gr, &gss_resources, gr_link) 257 if (gr->gr_id == id) 258 return (gr->gr_res); 259 260 return (NULL); 261 } 262 263 static uint64_t 264 gssd_make_resource(void *res) 265 { 266 struct gss_resource *gr; 267 268 if (!res) 269 return (0); 270 271 gr = malloc(sizeof(struct gss_resource)); 272 if (!gr) 273 return (0); 274 gr->gr_id = (gss_next_id++) + ((uint64_t) gss_start_time << 32); 275 gr->gr_res = res; 276 LIST_INSERT_HEAD(&gss_resources, gr, gr_link); 277 gss_resource_count++; 278 if (debug_level > 1) 279 printf("%d resources allocated\n", gss_resource_count); 280 281 return (gr->gr_id); 282 } 283 284 static void 285 gssd_delete_resource(uint64_t id) 286 { 287 struct gss_resource *gr; 288 289 LIST_FOREACH(gr, &gss_resources, gr_link) { 290 if (gr->gr_id == id) { 291 LIST_REMOVE(gr, gr_link); 292 free(gr); 293 gss_resource_count--; 294 if (debug_level > 1) 295 printf("%d resources allocated\n", 296 gss_resource_count); 297 return; 298 } 299 } 300 } 301 302 static void 303 gssd_verbose_out(const char *fmt, ...) 304 { 305 va_list ap; 306 307 if (verbose != 0) { 308 va_start(ap, fmt); 309 if (debug_level == 0) 310 vsyslog(LOG_INFO | LOG_DAEMON, fmt, ap); 311 else 312 vfprintf(stderr, fmt, ap); 313 va_end(ap); 314 } 315 } 316 317 bool_t 318 gssd_null_1_svc(void *argp, void *result, struct svc_req *rqstp) 319 { 320 321 gssd_verbose_out("gssd_null: done\n"); 322 return (TRUE); 323 } 324 325 #ifndef MK_MITKRB5 326 bool_t 327 gssd_init_sec_context_1_svc(init_sec_context_args *argp, init_sec_context_res *result, struct svc_req *rqstp) 328 { 329 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 330 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 331 gss_name_t name = GSS_C_NO_NAME; 332 char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 333 int gotone, gotcred; 334 OM_uint32 min_stat; 335 gss_buffer_desc principal_desc; 336 char enctype[sizeof(uint32_t)]; 337 int key_enctype; 338 OM_uint32 maj_stat; 339 340 memset(result, 0, sizeof(*result)); 341 if (hostbased_initiator_cred != 0 && argp->cred != 0 && 342 argp->uid == 0) { 343 /* 344 * These credentials are for a host based initiator name 345 * in a keytab file, which should now have credentials 346 * in /tmp/krb5cc_gssd, because gss_acquire_cred() did 347 * the equivalent of "kinit -k". 348 */ 349 snprintf(ccname, sizeof(ccname), "FILE:%s", 350 GSSD_CREDENTIAL_CACHE_FILE); 351 } else if (ccfile_dirlist[0] != '\0' && argp->cred == 0) { 352 /* 353 * For the "-s" case and no credentials provided as an 354 * argument, search the directory list for an appropriate 355 * credential cache file. If the search fails, return failure. 356 */ 357 gotone = 0; 358 cp = ccfile_dirlist; 359 do { 360 cp2 = strchr(cp, ':'); 361 if (cp2 != NULL) 362 *cp2 = '\0'; 363 gotone = find_ccache_file(cp, argp->uid, ccname); 364 if (gotone != 0) 365 break; 366 if (cp2 != NULL) 367 *cp2++ = ':'; 368 cp = cp2; 369 } while (cp != NULL && *cp != '\0'); 370 if (gotone == 0) { 371 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 372 gssd_verbose_out("gssd_init_sec_context: -s no" 373 " credential cache file found for uid=%d\n", 374 (int)argp->uid); 375 return (TRUE); 376 } 377 } else { 378 /* 379 * If there wasn't a "-s" option or the credentials have 380 * been provided as an argument, do it the old way. 381 * When credentials are provided, the uid should be root. 382 */ 383 if (argp->cred != 0 && argp->uid != 0) { 384 if (debug_level == 0) 385 syslog(LOG_ERR, "gss_init_sec_context:" 386 " cred for non-root"); 387 else 388 fprintf(stderr, "gss_init_sec_context:" 389 " cred for non-root\n"); 390 } 391 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 392 (int) argp->uid); 393 } 394 setenv("KRB5CCNAME", ccname, TRUE); 395 396 if (argp->cred) { 397 cred = gssd_find_resource(argp->cred); 398 if (!cred) { 399 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 400 gssd_verbose_out("gssd_init_sec_context: cred" 401 " resource not found\n"); 402 return (TRUE); 403 } 404 } 405 if (argp->ctx) { 406 ctx = gssd_find_resource(argp->ctx); 407 if (!ctx) { 408 result->major_status = GSS_S_CONTEXT_EXPIRED; 409 gssd_verbose_out("gssd_init_sec_context: context" 410 " resource not found\n"); 411 return (TRUE); 412 } 413 } 414 if (argp->name) { 415 name = gssd_find_resource(argp->name); 416 if (!name) { 417 result->major_status = GSS_S_BAD_NAME; 418 gssd_verbose_out("gssd_init_sec_context: name" 419 " resource not found\n"); 420 return (TRUE); 421 } 422 } 423 gotcred = 0; 424 425 result->major_status = gss_init_sec_context(&result->minor_status, 426 cred, &ctx, name, argp->mech_type, 427 argp->req_flags, argp->time_req, argp->input_chan_bindings, 428 &argp->input_token, &result->actual_mech_type, 429 &result->output_token, &result->ret_flags, &result->time_rec); 430 gssd_verbose_out("gssd_init_sec_context: done major=0x%x minor=%d" 431 " uid=%d\n", (unsigned int)result->major_status, 432 (int)result->minor_status, (int)argp->uid); 433 if (gotcred != 0) 434 gss_release_cred(&min_stat, &cred); 435 436 if (result->major_status == GSS_S_COMPLETE 437 || result->major_status == GSS_S_CONTINUE_NEEDED) { 438 if (argp->ctx) 439 result->ctx = argp->ctx; 440 else 441 result->ctx = gssd_make_resource(ctx); 442 } 443 444 return (TRUE); 445 } 446 447 bool_t 448 gssd_supports_lucid_1_svc(void *argp, supports_lucid_res *result, struct svc_req *rqstp) 449 { 450 451 gssd_verbose_out("gssd_lucid: done\n"); 452 result->major_status = GSS_S_UNAVAILABLE; 453 return (TRUE); 454 } 455 456 bool_t 457 gssd_init_sec_context_lucid_v1_1_svc(init_sec_context_lucid_v1_args *argp, 458 init_sec_context_lucid_v1_res *result, struct svc_req *rqstp) 459 { 460 461 gssd_verbose_out("gssd_init_sec_context_lucid_v1: Heimdal\n"); 462 result->major_status = GSS_S_UNAVAILABLE; 463 return (TRUE); 464 } 465 466 bool_t 467 gssd_accept_sec_context_lucid_v1_1_svc(accept_sec_context_lucid_v1_args *argp, 468 accept_sec_context_lucid_v1_res *result, struct svc_req *rqstp) 469 { 470 471 gssd_verbose_out("gssd_accept_sec_context_lucid_v1: Heimdal\n"); 472 result->major_status = GSS_S_UNAVAILABLE; 473 return (TRUE); 474 } 475 476 bool_t 477 gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, accept_sec_context_res *result, struct svc_req *rqstp) 478 { 479 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 480 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 481 gss_name_t src_name; 482 gss_cred_id_t delegated_cred_handle; 483 484 memset(result, 0, sizeof(*result)); 485 if (argp->ctx) { 486 ctx = gssd_find_resource(argp->ctx); 487 if (!ctx) { 488 result->major_status = GSS_S_CONTEXT_EXPIRED; 489 gssd_verbose_out("gssd_accept_sec_context: ctx" 490 " resource not found\n"); 491 return (TRUE); 492 } 493 } 494 if (argp->cred) { 495 cred = gssd_find_resource(argp->cred); 496 if (!cred) { 497 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 498 gssd_verbose_out("gssd_accept_sec_context: cred" 499 " resource not found\n"); 500 return (TRUE); 501 } 502 } 503 504 memset(result, 0, sizeof(*result)); 505 result->major_status = gss_accept_sec_context(&result->minor_status, 506 &ctx, cred, &argp->input_token, argp->input_chan_bindings, 507 &src_name, &result->mech_type, &result->output_token, 508 &result->ret_flags, &result->time_rec, 509 &delegated_cred_handle); 510 gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n", 511 (unsigned int)result->major_status, (int)result->minor_status); 512 513 if (result->major_status == GSS_S_COMPLETE 514 || result->major_status == GSS_S_CONTINUE_NEEDED) { 515 if (argp->ctx) 516 result->ctx = argp->ctx; 517 else 518 result->ctx = gssd_make_resource(ctx); 519 result->src_name = gssd_make_resource(src_name); 520 result->delegated_cred_handle = 521 gssd_make_resource(delegated_cred_handle); 522 } 523 524 return (TRUE); 525 } 526 #else /* MK_MITKRB5 */ 527 bool_t 528 gssd_supports_lucid_1_svc(void *argp, supports_lucid_res *result, struct svc_req *rqstp) 529 { 530 531 gssd_verbose_out("gssd_lucid: done\n"); 532 result->vers = 1; 533 result->major_status = GSS_S_COMPLETE; 534 return (TRUE); 535 } 536 537 bool_t 538 gssd_init_sec_context_1_svc(init_sec_context_args *argp, 539 init_sec_context_res *result, struct svc_req *rqstp) 540 { 541 542 gssd_verbose_out("gssd_init_sec_context: MIT\n"); 543 result->major_status = GSS_S_UNAVAILABLE; 544 return (TRUE); 545 } 546 547 bool_t 548 gssd_accept_sec_context_1_svc(accept_sec_context_args *argp, 549 accept_sec_context_res *result, struct svc_req *rqstp) 550 { 551 552 gssd_verbose_out("gssd_accept_sec_context: MIT\n"); 553 result->major_status = GSS_S_UNAVAILABLE; 554 return (TRUE); 555 } 556 557 bool_t 558 gssd_init_sec_context_lucid_v1_1_svc(init_sec_context_lucid_v1_args *argp, 559 init_sec_context_lucid_v1_res *result, struct svc_req *rqstp) 560 { 561 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 562 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 563 gss_name_t name = GSS_C_NO_NAME; 564 char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 565 int gotone, gotcred; 566 OM_uint32 min_stat; 567 gss_buffer_desc principal_desc; 568 char enctype[sizeof(uint32_t)]; 569 int key_enctype; 570 OM_uint32 maj_stat; 571 572 memset(result, 0, sizeof(*result)); 573 if (hostbased_initiator_cred != 0 && argp->cred != 0 && 574 argp->uid == 0) { 575 /* 576 * These credentials are for a host based initiator name 577 * in a keytab file, which should now have credentials 578 * in /tmp/krb5cc_gssd, because gss_acquire_cred() did 579 * the equivalent of "kinit -k". 580 */ 581 snprintf(ccname, sizeof(ccname), "FILE:%s", 582 GSSD_CREDENTIAL_CACHE_FILE); 583 } else if (ccfile_dirlist[0] != '\0' && argp->cred == 0) { 584 /* 585 * For the "-s" case and no credentials provided as an 586 * argument, search the directory list for an appropriate 587 * credential cache file. If the search fails, return failure. 588 */ 589 gotone = 0; 590 cp = ccfile_dirlist; 591 do { 592 cp2 = strchr(cp, ':'); 593 if (cp2 != NULL) 594 *cp2 = '\0'; 595 gotone = find_ccache_file(cp, argp->uid, ccname); 596 if (gotone != 0) 597 break; 598 if (cp2 != NULL) 599 *cp2++ = ':'; 600 cp = cp2; 601 } while (cp != NULL && *cp != '\0'); 602 if (gotone == 0) { 603 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 604 gssd_verbose_out("gssd_init_sec_context_plus: -s no" 605 " credential cache file found for uid=%d\n", 606 (int)argp->uid); 607 return (TRUE); 608 } 609 } else { 610 /* 611 * If there wasn't a "-s" option or the credentials have 612 * been provided as an argument, do it the old way. 613 * When credentials are provided, the uid should be root. 614 */ 615 if (argp->cred != 0 && argp->uid != 0) { 616 if (debug_level == 0) 617 syslog(LOG_ERR, "gss_init_sec_context_plus:" 618 " cred for non-root"); 619 else 620 fprintf(stderr, "gss_init_sec_context_plus:" 621 " cred for non-root\n"); 622 } 623 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 624 (int) argp->uid); 625 } 626 setenv("KRB5CCNAME", ccname, TRUE); 627 628 if (argp->cred) { 629 cred = gssd_find_resource(argp->cred); 630 if (!cred) { 631 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 632 gssd_verbose_out("gssd_init_sec_context_plus: cred" 633 " resource not found\n"); 634 return (TRUE); 635 } 636 } 637 if (argp->ctx) { 638 ctx = gssd_find_resource(argp->ctx); 639 if (!ctx) { 640 result->major_status = GSS_S_CONTEXT_EXPIRED; 641 gssd_verbose_out("gssd_init_sec_context_plus: context" 642 " resource not found\n"); 643 return (TRUE); 644 } 645 } 646 if (argp->name) { 647 name = gssd_find_resource(argp->name); 648 if (!name) { 649 result->major_status = GSS_S_BAD_NAME; 650 gssd_verbose_out("gssd_init_sec_context_plus: name" 651 " resource not found\n"); 652 return (TRUE); 653 } 654 } 655 gotcred = 0; 656 657 result->major_status = gss_init_sec_context(&result->minor_status, 658 cred, &ctx, name, argp->mech_type, 659 argp->req_flags, argp->time_req, argp->input_chan_bindings, 660 &argp->input_token, &result->actual_mech_type, 661 &result->output_token, &result->ret_flags, &result->time_rec); 662 gssd_verbose_out("gssd_init_sec_context_plus: done major=0x%x minor=%d" 663 " uid=%d\n", (unsigned int)result->major_status, 664 (int)result->minor_status, (int)argp->uid); 665 if (gotcred != 0) 666 gss_release_cred(&min_stat, &cred); 667 668 if (result->actual_mech_type) { 669 /* 670 * Just to keep the bogus "elements" pointer 671 * from core dumping the daemon when linked to MIT 672 * libraries. For some reason, the "elements" pointer 673 * in actual_mech_type cannot be read. 674 */ 675 result->actual_mech_type = GSS_KRB5_MECH_OID_X; 676 } 677 678 if (result->major_status == GSS_S_COMPLETE 679 || result->major_status == GSS_S_CONTINUE_NEEDED) { 680 if (argp->ctx) 681 result->ctx = argp->ctx; 682 else 683 result->ctx = gssd_make_resource(ctx); 684 } 685 686 if (result->major_status == GSS_S_COMPLETE) { 687 gss_krb5_lucid_context_v1_t *lctx; 688 689 result->major_status = gss_krb5_export_lucid_sec_context( 690 &result->minor_status, &ctx, 1, (void *)&lctx); 691 gssd_delete_resource(result->ctx); 692 if (result->major_status == GSS_S_COMPLETE && 693 lctx != NULL) { 694 result->lucid.initiate = lctx->initiate; 695 result->lucid.endtime = lctx->endtime; 696 result->lucid.send_seq = lctx->send_seq; 697 result->lucid.recv_seq = lctx->recv_seq; 698 result->lucid.protocol = lctx->protocol; 699 if (lctx->protocol == 0) { 700 result->lucid.rfc_sign = 701 lctx->rfc1964_kd.sign_alg; 702 result->lucid.rfc_seal = 703 lctx->rfc1964_kd.seal_alg; 704 result->lucid.ctx_type = 705 lctx->rfc1964_kd.ctx_key.type; 706 result->lucid.ctx_key.length = 707 lctx->rfc1964_kd.ctx_key.length; 708 result->lucid.ctx_key.value = 709 mem_alloc(result->lucid.ctx_key.length); 710 memcpy(result->lucid.ctx_key.value, 711 lctx->rfc1964_kd.ctx_key.data, 712 result->lucid.ctx_key.length); 713 } else if (lctx->protocol == 1) { 714 result->lucid.have_subkey = 715 lctx->cfx_kd.have_acceptor_subkey; 716 result->lucid.ctx_type = 717 lctx->cfx_kd.ctx_key.type; 718 result->lucid.ctx_key.length = 719 lctx->cfx_kd.ctx_key.length; 720 result->lucid.ctx_key.value = 721 mem_alloc(result->lucid.ctx_key.length); 722 memcpy(result->lucid.ctx_key.value, 723 lctx->cfx_kd.ctx_key.data, 724 result->lucid.ctx_key.length); 725 if (result->lucid.have_subkey != 0) { 726 result->lucid.subkey_type = 727 lctx->cfx_kd.acceptor_subkey.type; 728 result->lucid.subkey_key.length = 729 lctx->cfx_kd.acceptor_subkey.length; 730 result->lucid.subkey_key.value = 731 mem_alloc( 732 result->lucid.subkey_key.length); 733 memcpy(result->lucid.subkey_key.value, 734 lctx->cfx_kd.acceptor_subkey.data, 735 result->lucid.subkey_key.length); 736 } else { 737 result->lucid.subkey_type = 0; 738 result->lucid.subkey_key.length = 0; 739 result->lucid.subkey_key.value = NULL; 740 } 741 } 742 (void)gss_krb5_free_lucid_sec_context(&min_stat, 743 (void *)lctx); 744 } else { 745 gssd_verbose_out("gss_krb5_export_lucid_set_context" 746 " failed: major=0x%x minor=%d lctx=%p\n", 747 result->major_status, result->minor_status, lctx); 748 } 749 } 750 751 return (TRUE); 752 } 753 754 /* 755 * Internal function to acquire unix credentials. 756 */ 757 static OM_uint32 758 _gss_get_unix_cred(OM_uint32 *minor_stat, gss_name_t name, gss_OID mech, 759 uid_t *uidp, gid_t *gidp, int *numgroups, gid_t *groups) 760 { 761 OM_uint32 major_stat; 762 uid_t uid; 763 char buf[1024], *bufp; 764 struct passwd pwd, *pw; 765 size_t buflen; 766 int error; 767 static size_t buflen_hint = 1024; 768 769 major_stat = gss_pname_to_uid(minor_stat, name, mech, &uid); 770 if (major_stat == GSS_S_COMPLETE) { 771 *uidp = uid; 772 buflen = buflen_hint; 773 for (;;) { 774 pw = NULL; 775 bufp = buf; 776 if (buflen > sizeof(buf)) 777 bufp = malloc(buflen); 778 if (bufp == NULL) 779 break; 780 error = getpwuid_r(uid, &pwd, bufp, buflen, 781 &pw); 782 if (error != ERANGE) 783 break; 784 if (buflen > sizeof(buf)) 785 free(bufp); 786 buflen += 1024; 787 if (buflen > buflen_hint) 788 buflen_hint = buflen; 789 } 790 if (pw) { 791 *gidp = pw->pw_gid; 792 getgrouplist(pw->pw_name, pw->pw_gid, 793 groups, numgroups); 794 } else { 795 major_stat = GSS_S_FAILURE; 796 gssd_verbose_out("get_unix_cred: cannot find" 797 " passwd entry\n"); 798 } 799 if (bufp != NULL && buflen > sizeof(buf)) 800 free(bufp); 801 } else if (major_stat != GSS_S_UNAVAILABLE) { 802 gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x" 803 " minor=%d\n", major_stat, *minor_stat); 804 } 805 return (major_stat); 806 } 807 808 bool_t 809 gssd_accept_sec_context_lucid_v1_1_svc(accept_sec_context_lucid_v1_args *argp, 810 accept_sec_context_lucid_v1_res *result, struct svc_req *rqstp) 811 { 812 gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; 813 gss_cred_id_t cred = GSS_C_NO_CREDENTIAL; 814 gss_name_t src_name; 815 gss_cred_id_t delegated_cred_handle; 816 OM_uint32 min_stat; 817 818 memset(result, 0, sizeof(*result)); 819 if (argp->ctx) { 820 ctx = gssd_find_resource(argp->ctx); 821 if (!ctx) { 822 result->major_status = GSS_S_CONTEXT_EXPIRED; 823 gssd_verbose_out("gssd_accept_sec_context: ctx" 824 " resource not found\n"); 825 return (TRUE); 826 } 827 } 828 if (argp->cred) { 829 cred = gssd_find_resource(argp->cred); 830 if (!cred) { 831 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 832 gssd_verbose_out("gssd_accept_sec_context: cred" 833 " resource not found\n"); 834 return (TRUE); 835 } 836 } 837 838 memset(result, 0, sizeof(*result)); 839 result->major_status = gss_accept_sec_context(&result->minor_status, 840 &ctx, cred, &argp->input_token, argp->input_chan_bindings, 841 &src_name, &result->mech_type, &result->output_token, 842 &result->ret_flags, &result->time_rec, 843 &delegated_cred_handle); 844 gssd_verbose_out("gssd_accept_sec_context: done major=0x%x minor=%d\n", 845 (unsigned int)result->major_status, (int)result->minor_status); 846 847 if (result->major_status == GSS_S_COMPLETE 848 || result->major_status == GSS_S_CONTINUE_NEEDED) { 849 if (argp->ctx) 850 result->ctx = argp->ctx; 851 else 852 result->ctx = gssd_make_resource(ctx); 853 result->src_name = gssd_make_resource(src_name); 854 result->delegated_cred_handle = 855 gssd_make_resource(delegated_cred_handle); 856 } 857 858 if (result->major_status == GSS_S_COMPLETE) { 859 gss_krb5_lucid_context_v1_t *lctx; 860 861 /* Get the lucid context stuff. */ 862 result->major_status = gss_krb5_export_lucid_sec_context( 863 &result->minor_status, &ctx, 1, (void *)&lctx); 864 gssd_delete_resource(result->ctx); 865 if (result->major_status == GSS_S_COMPLETE && 866 lctx != NULL) { 867 result->lucid.initiate = lctx->initiate; 868 result->lucid.endtime = lctx->endtime; 869 result->lucid.send_seq = lctx->send_seq; 870 result->lucid.recv_seq = lctx->recv_seq; 871 result->lucid.protocol = lctx->protocol; 872 if (lctx->protocol == 0) { 873 result->lucid.rfc_sign = 874 lctx->rfc1964_kd.sign_alg; 875 result->lucid.rfc_seal = 876 lctx->rfc1964_kd.seal_alg; 877 result->lucid.ctx_type = 878 lctx->rfc1964_kd.ctx_key.type; 879 result->lucid.ctx_key.length = 880 lctx->rfc1964_kd.ctx_key.length; 881 result->lucid.ctx_key.value = 882 mem_alloc(result->lucid.ctx_key.length); 883 memcpy(result->lucid.ctx_key.value, 884 lctx->rfc1964_kd.ctx_key.data, 885 result->lucid.ctx_key.length); 886 } else if (lctx->protocol == 1) { 887 result->lucid.have_subkey = 888 lctx->cfx_kd.have_acceptor_subkey; 889 result->lucid.ctx_type = 890 lctx->cfx_kd.ctx_key.type; 891 result->lucid.ctx_key.length = 892 lctx->cfx_kd.ctx_key.length; 893 result->lucid.ctx_key.value = 894 mem_alloc(result->lucid.ctx_key.length); 895 memcpy(result->lucid.ctx_key.value, 896 lctx->cfx_kd.ctx_key.data, 897 result->lucid.ctx_key.length); 898 if (result->lucid.have_subkey != 0) { 899 result->lucid.subkey_type = 900 lctx->cfx_kd.acceptor_subkey.type; 901 result->lucid.subkey_key.length = 902 lctx->cfx_kd.acceptor_subkey.length; 903 result->lucid.subkey_key.value = 904 mem_alloc( 905 result->lucid.subkey_key.length); 906 memcpy(result->lucid.subkey_key.value, 907 lctx->cfx_kd.acceptor_subkey.data, 908 result->lucid.subkey_key.length); 909 } else { 910 result->lucid.subkey_type = 0; 911 result->lucid.subkey_key.length = 0; 912 result->lucid.subkey_key.value = NULL; 913 } 914 } 915 (void)gss_krb5_free_lucid_sec_context(&min_stat, 916 (void *)lctx); 917 } else { 918 gssd_verbose_out("gss_krb5_export_lucid_set_context" 919 " failed: major=0x%x minor=%d lctx=%p\n", 920 result->major_status, result->minor_status, lctx); 921 } 922 923 /* Now, get the exported name. */ 924 if (result->major_status == GSS_S_COMPLETE) { 925 result->major_status = gss_export_name( 926 &result->minor_status, src_name, 927 &result->exported_name); 928 gssd_verbose_out("gssd_accept_sec_context (name):" 929 " done major=0x%x minor=%d\n", 930 result->major_status, result->minor_status); 931 } 932 933 /* Finally, get the unix credentials. */ 934 if (result->major_status == GSS_S_COMPLETE) { 935 gid_t groups[NGROUPS]; 936 int i, len = NGROUPS; 937 OM_uint32 major_stat, minor_stat; 938 939 major_stat = _gss_get_unix_cred(&minor_stat, 940 src_name, result->mech_type, 941 &result->uid, &result->gid, &len, groups); 942 if (major_stat == GSS_S_COMPLETE) { 943 result->gidlist.gidlist_len = len; 944 result->gidlist.gidlist_val = 945 mem_alloc(len * sizeof(uint32_t)); 946 /* 947 * Just in case 948 * sizeof(gid_t) != sizeof(uint32_t). 949 */ 950 for (i = 0; i < len; i++) 951 result->gidlist.gidlist_val[i] = 952 groups[i]; 953 } else { 954 result->gid = 65534; 955 result->gidlist.gidlist_len = 0; 956 result->gidlist.gidlist_val = NULL; 957 gssd_verbose_out("gssd_pname_to_uid: mapped" 958 " to uid=%d, but no groups\n", 959 (int)result->uid); 960 } 961 } 962 } 963 return (TRUE); 964 } 965 #endif /* !MK_MITKRB5 */ 966 967 bool_t 968 gssd_delete_sec_context_1_svc(delete_sec_context_args *argp, delete_sec_context_res *result, struct svc_req *rqstp) 969 { 970 gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 971 972 if (ctx) { 973 result->major_status = gss_delete_sec_context( 974 &result->minor_status, &ctx, &result->output_token); 975 gssd_delete_resource(argp->ctx); 976 } else { 977 result->major_status = GSS_S_COMPLETE; 978 result->minor_status = 0; 979 } 980 gssd_verbose_out("gssd_delete_sec_context: done major=0x%x minor=%d\n", 981 (unsigned int)result->major_status, (int)result->minor_status); 982 983 return (TRUE); 984 } 985 986 bool_t 987 gssd_export_sec_context_1_svc(export_sec_context_args *argp, export_sec_context_res *result, struct svc_req *rqstp) 988 { 989 gss_ctx_id_t ctx = gssd_find_resource(argp->ctx); 990 991 if (ctx) { 992 result->major_status = gss_export_sec_context( 993 &result->minor_status, &ctx, 994 &result->interprocess_token); 995 result->format = KGSS_HEIMDAL_1_1; 996 gssd_delete_resource(argp->ctx); 997 } else { 998 result->major_status = GSS_S_FAILURE; 999 result->minor_status = 0; 1000 result->interprocess_token.length = 0; 1001 result->interprocess_token.value = NULL; 1002 } 1003 gssd_verbose_out("gssd_export_sec_context: done major=0x%x minor=%d\n", 1004 (unsigned int)result->major_status, (int)result->minor_status); 1005 1006 return (TRUE); 1007 } 1008 1009 bool_t 1010 gssd_import_name_1_svc(import_name_args *argp, import_name_res *result, struct svc_req *rqstp) 1011 { 1012 gss_name_t name; 1013 1014 result->major_status = gss_import_name(&result->minor_status, 1015 &argp->input_name_buffer, argp->input_name_type, &name); 1016 gssd_verbose_out("gssd_import_name: done major=0x%x minor=%d\n", 1017 (unsigned int)result->major_status, (int)result->minor_status); 1018 1019 if (result->major_status == GSS_S_COMPLETE) 1020 result->output_name = gssd_make_resource(name); 1021 else 1022 result->output_name = 0; 1023 1024 return (TRUE); 1025 } 1026 1027 /* 1028 * If the name is a numeric IP host address, do a DNS lookup on it and 1029 * return the DNS name in a malloc'd string. 1030 */ 1031 static char * 1032 gssd_conv_ip_to_dns(int len, char *name) 1033 { 1034 struct sockaddr_in sin; 1035 struct sockaddr_in6 sin6; 1036 char *retcp; 1037 1038 retcp = NULL; 1039 if (len > 0) { 1040 retcp = mem_alloc(NI_MAXHOST); 1041 memcpy(retcp, name, len); 1042 retcp[len] = '\0'; 1043 if (inet_pton(AF_INET, retcp, &sin.sin_addr) != 0) { 1044 sin.sin_family = AF_INET; 1045 sin.sin_len = sizeof(sin); 1046 sin.sin_port = 0; 1047 if (getnameinfo((struct sockaddr *)&sin, 1048 sizeof(sin), retcp, NI_MAXHOST, 1049 NULL, 0, NI_NAMEREQD) != 0) { 1050 mem_free(retcp, NI_MAXHOST); 1051 return (NULL); 1052 } 1053 } else if (inet_pton(AF_INET6, retcp, &sin6.sin6_addr) != 0) { 1054 sin6.sin6_family = AF_INET6; 1055 sin6.sin6_len = sizeof(sin6); 1056 sin6.sin6_port = 0; 1057 if (getnameinfo((struct sockaddr *)&sin6, 1058 sizeof(sin6), retcp, NI_MAXHOST, 1059 NULL, 0, NI_NAMEREQD) != 0) { 1060 mem_free(retcp, NI_MAXHOST); 1061 return (NULL); 1062 } 1063 } else { 1064 mem_free(retcp, NI_MAXHOST); 1065 return (NULL); 1066 } 1067 gssd_verbose_out("gssd_conv_ip_to_dns: %s\n", retcp); 1068 } 1069 return (retcp); 1070 } 1071 1072 bool_t 1073 gssd_canonicalize_name_1_svc(canonicalize_name_args *argp, canonicalize_name_res *result, struct svc_req *rqstp) 1074 { 1075 gss_name_t name = gssd_find_resource(argp->input_name); 1076 gss_name_t output_name; 1077 1078 memset(result, 0, sizeof(*result)); 1079 if (!name) { 1080 result->major_status = GSS_S_BAD_NAME; 1081 return (TRUE); 1082 } 1083 1084 result->major_status = gss_canonicalize_name(&result->minor_status, 1085 name, argp->mech_type, &output_name); 1086 gssd_verbose_out("gssd_canonicalize_name: done major=0x%x minor=%d\n", 1087 (unsigned int)result->major_status, (int)result->minor_status); 1088 1089 if (result->major_status == GSS_S_COMPLETE) 1090 result->output_name = gssd_make_resource(output_name); 1091 else 1092 result->output_name = 0; 1093 1094 return (TRUE); 1095 } 1096 1097 bool_t 1098 gssd_export_name_1_svc(export_name_args *argp, export_name_res *result, struct svc_req *rqstp) 1099 { 1100 gss_name_t name = gssd_find_resource(argp->input_name); 1101 1102 memset(result, 0, sizeof(*result)); 1103 if (!name) { 1104 result->major_status = GSS_S_BAD_NAME; 1105 gssd_verbose_out("gssd_export_name: name resource not found\n"); 1106 return (TRUE); 1107 } 1108 1109 result->major_status = gss_export_name(&result->minor_status, 1110 name, &result->exported_name); 1111 gssd_verbose_out("gssd_export_name: done major=0x%x minor=%d\n", 1112 (unsigned int)result->major_status, (int)result->minor_status); 1113 1114 return (TRUE); 1115 } 1116 1117 bool_t 1118 gssd_release_name_1_svc(release_name_args *argp, release_name_res *result, struct svc_req *rqstp) 1119 { 1120 gss_name_t name = gssd_find_resource(argp->input_name); 1121 1122 if (name) { 1123 result->major_status = gss_release_name(&result->minor_status, 1124 &name); 1125 gssd_delete_resource(argp->input_name); 1126 } else { 1127 result->major_status = GSS_S_COMPLETE; 1128 result->minor_status = 0; 1129 } 1130 gssd_verbose_out("gssd_release_name: done major=0x%x minor=%d\n", 1131 (unsigned int)result->major_status, (int)result->minor_status); 1132 1133 return (TRUE); 1134 } 1135 1136 bool_t 1137 gssd_pname_to_uid_1_svc(pname_to_uid_args *argp, pname_to_uid_res *result, struct svc_req *rqstp) 1138 { 1139 gss_name_t name = gssd_find_resource(argp->pname); 1140 uid_t uid; 1141 char buf[1024], *bufp; 1142 struct passwd pwd, *pw; 1143 size_t buflen; 1144 int error; 1145 static size_t buflen_hint = 1024; 1146 1147 memset(result, 0, sizeof(*result)); 1148 if (name) { 1149 result->major_status = 1150 gss_pname_to_uid(&result->minor_status, 1151 name, argp->mech, &uid); 1152 if (result->major_status == GSS_S_COMPLETE) { 1153 result->uid = uid; 1154 buflen = buflen_hint; 1155 for (;;) { 1156 pw = NULL; 1157 bufp = buf; 1158 if (buflen > sizeof(buf)) 1159 bufp = malloc(buflen); 1160 if (bufp == NULL) 1161 break; 1162 error = getpwuid_r(uid, &pwd, bufp, buflen, 1163 &pw); 1164 if (error != ERANGE) 1165 break; 1166 if (buflen > sizeof(buf)) 1167 free(bufp); 1168 buflen += 1024; 1169 if (buflen > buflen_hint) 1170 buflen_hint = buflen; 1171 } 1172 if (pw) { 1173 int len = NGROUPS; 1174 int groups[NGROUPS]; 1175 result->gid = pw->pw_gid; 1176 getgrouplist(pw->pw_name, pw->pw_gid, 1177 groups, &len); 1178 result->gidlist.gidlist_len = len; 1179 result->gidlist.gidlist_val = 1180 mem_alloc(len * sizeof(int)); 1181 memcpy(result->gidlist.gidlist_val, groups, 1182 len * sizeof(int)); 1183 gssd_verbose_out("gssd_pname_to_uid: mapped" 1184 " to uid=%d, gid=%d\n", (int)result->uid, 1185 (int)result->gid); 1186 } else { 1187 result->gid = 65534; 1188 result->gidlist.gidlist_len = 0; 1189 result->gidlist.gidlist_val = NULL; 1190 gssd_verbose_out("gssd_pname_to_uid: mapped" 1191 " to uid=%d, but no groups\n", 1192 (int)result->uid); 1193 } 1194 if (bufp != NULL && buflen > sizeof(buf)) 1195 free(bufp); 1196 } else 1197 gssd_verbose_out("gssd_pname_to_uid: failed major=0x%x" 1198 " minor=%d\n", (unsigned int)result->major_status, 1199 (int)result->minor_status); 1200 } else { 1201 result->major_status = GSS_S_BAD_NAME; 1202 result->minor_status = 0; 1203 gssd_verbose_out("gssd_pname_to_uid: no name\n"); 1204 } 1205 1206 return (TRUE); 1207 } 1208 1209 bool_t 1210 gssd_acquire_cred_1_svc(acquire_cred_args *argp, acquire_cred_res *result, struct svc_req *rqstp) 1211 { 1212 gss_name_t desired_name = GSS_C_NO_NAME; 1213 gss_cred_id_t cred; 1214 char ccname[PATH_MAX + 5 + 1], *cp, *cp2; 1215 int gotone; 1216 gss_buffer_desc namebuf; 1217 uint32_t minstat; 1218 krb5_error_code kret; 1219 1220 memset(result, 0, sizeof(*result)); 1221 if (argp->desired_name) { 1222 desired_name = gssd_find_resource(argp->desired_name); 1223 if (!desired_name) { 1224 result->major_status = GSS_S_BAD_NAME; 1225 gssd_verbose_out("gssd_acquire_cred: no desired name" 1226 " found\n"); 1227 return (TRUE); 1228 } 1229 } 1230 1231 if (hostbased_initiator_cred != 0 && argp->desired_name != 0 && 1232 argp->uid == 0 && argp->cred_usage == GSS_C_INITIATE) { 1233 /* This is a host based initiator name in the keytab file. */ 1234 snprintf(ccname, sizeof(ccname), "FILE:%s", 1235 GSSD_CREDENTIAL_CACHE_FILE); 1236 setenv("KRB5CCNAME", ccname, TRUE); 1237 result->major_status = gss_display_name(&result->minor_status, 1238 desired_name, &namebuf, NULL); 1239 gssd_verbose_out("gssd_acquire_cred: desired name for host " 1240 "based initiator cred major=0x%x minor=%d\n", 1241 (unsigned int)result->major_status, 1242 (int)result->minor_status); 1243 if (result->major_status != GSS_S_COMPLETE) 1244 return (TRUE); 1245 if (namebuf.length > PATH_MAX + 5) { 1246 result->minor_status = 0; 1247 result->major_status = GSS_S_FAILURE; 1248 return (TRUE); 1249 } 1250 memcpy(ccname, namebuf.value, namebuf.length); 1251 ccname[namebuf.length] = '\0'; 1252 if ((cp = strchr(ccname, '@')) != NULL) 1253 *cp = '/'; 1254 kret = gssd_get_cc_from_keytab(ccname); 1255 gssd_verbose_out("gssd_acquire_cred: using keytab entry for " 1256 "%s, kerberos ret=%d\n", ccname, (int)kret); 1257 gss_release_buffer(&minstat, &namebuf); 1258 if (kret != 0) { 1259 result->minor_status = kret; 1260 result->major_status = GSS_S_FAILURE; 1261 return (TRUE); 1262 } 1263 } else if (ccfile_dirlist[0] != '\0' && argp->desired_name == 0) { 1264 /* 1265 * For the "-s" case and no name provided as an 1266 * argument, search the directory list for an appropriate 1267 * credential cache file. If the search fails, return failure. 1268 */ 1269 gotone = 0; 1270 cp = ccfile_dirlist; 1271 do { 1272 cp2 = strchr(cp, ':'); 1273 if (cp2 != NULL) 1274 *cp2 = '\0'; 1275 gotone = find_ccache_file(cp, argp->uid, ccname); 1276 if (gotone != 0) 1277 break; 1278 if (cp2 != NULL) 1279 *cp2++ = ':'; 1280 cp = cp2; 1281 } while (cp != NULL && *cp != '\0'); 1282 if (gotone == 0) { 1283 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 1284 gssd_verbose_out("gssd_acquire_cred: no cred cache" 1285 " file found\n"); 1286 return (TRUE); 1287 } 1288 setenv("KRB5CCNAME", ccname, TRUE); 1289 } else { 1290 /* 1291 * If there wasn't a "-s" option or the name has 1292 * been provided as an argument, do it the old way. 1293 * When a name is provided, it will normally exist in the 1294 * default keytab file and the uid will be root. 1295 */ 1296 if (argp->desired_name != 0 && argp->uid != 0) { 1297 if (debug_level == 0) 1298 syslog(LOG_ERR, "gss_acquire_cred:" 1299 " principal_name for non-root"); 1300 else 1301 fprintf(stderr, "gss_acquire_cred:" 1302 " principal_name for non-root\n"); 1303 } 1304 snprintf(ccname, sizeof(ccname), "FILE:/tmp/krb5cc_%d", 1305 (int) argp->uid); 1306 setenv("KRB5CCNAME", ccname, TRUE); 1307 } 1308 1309 result->major_status = gss_acquire_cred(&result->minor_status, 1310 desired_name, argp->time_req, argp->desired_mechs, 1311 argp->cred_usage, &cred, &result->actual_mechs, &result->time_rec); 1312 gssd_verbose_out("gssd_acquire_cred: done major=0x%x minor=%d\n", 1313 (unsigned int)result->major_status, (int)result->minor_status); 1314 1315 if (result->major_status == GSS_S_COMPLETE) 1316 result->output_cred = gssd_make_resource(cred); 1317 else 1318 result->output_cred = 0; 1319 1320 return (TRUE); 1321 } 1322 1323 bool_t 1324 gssd_set_cred_option_1_svc(set_cred_option_args *argp, set_cred_option_res *result, struct svc_req *rqstp) 1325 { 1326 gss_cred_id_t cred = gssd_find_resource(argp->cred); 1327 1328 memset(result, 0, sizeof(*result)); 1329 if (!cred) { 1330 result->major_status = GSS_S_CREDENTIALS_EXPIRED; 1331 gssd_verbose_out("gssd_set_cred: no credentials\n"); 1332 return (TRUE); 1333 } 1334 1335 result->major_status = gss_set_cred_option(&result->minor_status, 1336 &cred, argp->option_name, &argp->option_value); 1337 gssd_verbose_out("gssd_set_cred: done major=0x%x minor=%d\n", 1338 (unsigned int)result->major_status, (int)result->minor_status); 1339 1340 return (TRUE); 1341 } 1342 1343 bool_t 1344 gssd_release_cred_1_svc(release_cred_args *argp, release_cred_res *result, struct svc_req *rqstp) 1345 { 1346 gss_cred_id_t cred = gssd_find_resource(argp->cred); 1347 1348 if (cred) { 1349 result->major_status = gss_release_cred(&result->minor_status, 1350 &cred); 1351 gssd_delete_resource(argp->cred); 1352 } else { 1353 result->major_status = GSS_S_COMPLETE; 1354 result->minor_status = 0; 1355 } 1356 gssd_verbose_out("gssd_release_cred: done major=0x%x minor=%d\n", 1357 (unsigned int)result->major_status, (int)result->minor_status); 1358 1359 return (TRUE); 1360 } 1361 1362 bool_t 1363 gssd_display_status_1_svc(display_status_args *argp, display_status_res *result, struct svc_req *rqstp) 1364 { 1365 1366 result->message_context = argp->message_context; 1367 result->major_status = gss_display_status(&result->minor_status, 1368 argp->status_value, argp->status_type, argp->mech_type, 1369 &result->message_context, &result->status_string); 1370 gssd_verbose_out("gssd_display_status: done major=0x%x minor=%d\n", 1371 (unsigned int)result->major_status, (int)result->minor_status); 1372 1373 return (TRUE); 1374 } 1375 1376 bool_t 1377 gssd_ip_to_dns_1_svc(ip_to_dns_args *argp, ip_to_dns_res *result, struct svc_req *rqstp) 1378 { 1379 char *host; 1380 1381 memset(result, 0, sizeof(*result)); 1382 /* Check to see if the name is actually an IP address. */ 1383 host = gssd_conv_ip_to_dns(argp->ip_addr.ip_addr_len, 1384 argp->ip_addr.ip_addr_val); 1385 if (host != NULL) { 1386 result->major_status = GSS_S_COMPLETE; 1387 result->dns_name.dns_name_len = strlen(host); 1388 result->dns_name.dns_name_val = host; 1389 return (TRUE); 1390 } 1391 result->major_status = GSS_S_FAILURE; 1392 return (TRUE); 1393 } 1394 1395 int 1396 gssd_1_freeresult(SVCXPRT *transp, xdrproc_t xdr_result, caddr_t result) 1397 { 1398 /* 1399 * We don't use XDR to free the results - anything which was 1400 * allocated came from GSS-API. We use xdr_result to figure 1401 * out what to do. 1402 */ 1403 OM_uint32 junk; 1404 1405 if (xdr_result == (xdrproc_t) xdr_init_sec_context_res) { 1406 init_sec_context_res *p = (init_sec_context_res *) result; 1407 gss_release_buffer(&junk, &p->output_token); 1408 } else if (xdr_result == (xdrproc_t) xdr_accept_sec_context_res) { 1409 accept_sec_context_res *p = (accept_sec_context_res *) result; 1410 gss_release_buffer(&junk, &p->output_token); 1411 } else if (xdr_result == (xdrproc_t) xdr_delete_sec_context_res) { 1412 delete_sec_context_res *p = (delete_sec_context_res *) result; 1413 gss_release_buffer(&junk, &p->output_token); 1414 } else if (xdr_result == (xdrproc_t) xdr_export_sec_context_res) { 1415 export_sec_context_res *p = (export_sec_context_res *) result; 1416 if (p->interprocess_token.length) 1417 memset(p->interprocess_token.value, 0, 1418 p->interprocess_token.length); 1419 gss_release_buffer(&junk, &p->interprocess_token); 1420 } else if (xdr_result == (xdrproc_t) xdr_export_name_res) { 1421 export_name_res *p = (export_name_res *) result; 1422 gss_release_buffer(&junk, &p->exported_name); 1423 } else if (xdr_result == (xdrproc_t) xdr_acquire_cred_res) { 1424 acquire_cred_res *p = (acquire_cred_res *) result; 1425 gss_release_oid_set(&junk, &p->actual_mechs); 1426 } else if (xdr_result == (xdrproc_t) xdr_pname_to_uid_res) { 1427 pname_to_uid_res *p = (pname_to_uid_res *) result; 1428 if (p->gidlist.gidlist_val) 1429 free(p->gidlist.gidlist_val); 1430 } else if (xdr_result == (xdrproc_t) xdr_display_status_res) { 1431 display_status_res *p = (display_status_res *) result; 1432 gss_release_buffer(&junk, &p->status_string); 1433 } 1434 1435 return (TRUE); 1436 } 1437 1438 /* 1439 * Search a directory for the most likely candidate to be used as the 1440 * credential cache for a uid. If successful, return 1 and fill the 1441 * file's path id into "rpath". Otherwise, return 0. 1442 */ 1443 static int 1444 find_ccache_file(const char *dirpath, uid_t uid, char *rpath) 1445 { 1446 DIR *dirp; 1447 struct dirent *dp; 1448 struct stat sb; 1449 time_t exptime, oexptime; 1450 int gotone, len, rating, orating; 1451 char namepath[PATH_MAX + 5 + 1]; 1452 char retpath[PATH_MAX + 5 + 1]; 1453 1454 dirp = opendir(dirpath); 1455 if (dirp == NULL) 1456 return (0); 1457 gotone = 0; 1458 orating = 0; 1459 oexptime = 0; 1460 while ((dp = readdir(dirp)) != NULL) { 1461 len = snprintf(namepath, sizeof(namepath), "%s/%s", dirpath, 1462 dp->d_name); 1463 if (len < sizeof(namepath) && 1464 (hostbased_initiator_cred == 0 || strcmp(namepath, 1465 GSSD_CREDENTIAL_CACHE_FILE) != 0) && 1466 strstr(dp->d_name, ccfile_substring) != NULL && 1467 lstat(namepath, &sb) >= 0 && 1468 sb.st_uid == uid && 1469 S_ISREG(sb.st_mode)) { 1470 len = snprintf(namepath, sizeof(namepath), "FILE:%s/%s", 1471 dirpath, dp->d_name); 1472 if (len < sizeof(namepath) && 1473 is_a_valid_tgt_cache(namepath, uid, &rating, 1474 &exptime) != 0) { 1475 if (gotone == 0 || rating > orating || 1476 (rating == orating && exptime > oexptime)) { 1477 orating = rating; 1478 oexptime = exptime; 1479 strcpy(retpath, namepath); 1480 gotone = 1; 1481 } 1482 } 1483 } 1484 } 1485 closedir(dirp); 1486 if (gotone != 0) { 1487 strcpy(rpath, retpath); 1488 return (1); 1489 } 1490 return (0); 1491 } 1492 1493 /* 1494 * Try to determine if the file is a valid tgt cache file. 1495 * Check that the file has a valid tgt for a principal. 1496 * If it does, return 1, otherwise return 0. 1497 * It also returns a "rating" and the expiry time for the TGT, when found. 1498 * This "rating" is higher based on heuristics that make it more 1499 * likely to be the correct credential cache file to use. It can 1500 * be used by the caller, along with expiry time, to select from 1501 * multiple credential cache files. 1502 */ 1503 static int 1504 is_a_valid_tgt_cache(const char *filepath, uid_t uid, int *retrating, 1505 time_t *retexptime) 1506 { 1507 krb5_context context; 1508 krb5_principal princ; 1509 krb5_ccache ccache; 1510 krb5_error_code retval; 1511 krb5_cc_cursor curse; 1512 krb5_creds krbcred; 1513 int gotone, orating, rating, ret; 1514 struct passwd *pw; 1515 char *cp, *cp2, *pname; 1516 time_t exptime; 1517 1518 /* Find a likely name for the uid principal. */ 1519 pw = getpwuid(uid); 1520 1521 /* 1522 * Do a bunch of krb5 library stuff to try and determine if 1523 * this file is a credentials cache with an appropriate TGT 1524 * in it. 1525 */ 1526 retval = krb5_init_context(&context); 1527 if (retval != 0) 1528 return (0); 1529 retval = krb5_cc_resolve(context, filepath, &ccache); 1530 if (retval != 0) { 1531 krb5_free_context(context); 1532 return (0); 1533 } 1534 ret = 0; 1535 orating = 0; 1536 exptime = 0; 1537 retval = krb5_cc_start_seq_get(context, ccache, &curse); 1538 if (retval == 0) { 1539 while ((retval = krb5_cc_next_cred(context, ccache, &curse, 1540 &krbcred)) == 0) { 1541 gotone = 0; 1542 rating = 0; 1543 retval = krb5_unparse_name(context, krbcred.server, 1544 &pname); 1545 if (retval == 0) { 1546 cp = strchr(pname, '/'); 1547 if (cp != NULL) { 1548 *cp++ = '\0'; 1549 if (strcmp(pname, "krbtgt") == 0 && 1550 krbcred.times.endtime > time(NULL) 1551 ) { 1552 gotone = 1; 1553 /* 1554 * Test to see if this is a 1555 * tgt for cross-realm auth. 1556 * Rate it higher, if it is not. 1557 */ 1558 cp2 = strchr(cp, '@'); 1559 if (cp2 != NULL) { 1560 *cp2++ = '\0'; 1561 if (strcmp(cp, cp2) == 1562 0) 1563 rating++; 1564 } 1565 } 1566 } 1567 free(pname); 1568 } 1569 if (gotone != 0) { 1570 retval = krb5_unparse_name(context, 1571 krbcred.client, &pname); 1572 if (retval == 0) { 1573 cp = strchr(pname, '@'); 1574 if (cp != NULL) { 1575 *cp++ = '\0'; 1576 if (pw != NULL && strcmp(pname, 1577 pw->pw_name) == 0) 1578 rating++; 1579 if (strchr(pname, '/') == NULL) 1580 rating++; 1581 if (pref_realm[0] != '\0' && 1582 strcmp(cp, pref_realm) == 0) 1583 rating++; 1584 } 1585 } 1586 free(pname); 1587 if (rating > orating) { 1588 orating = rating; 1589 exptime = krbcred.times.endtime; 1590 } else if (rating == orating && 1591 krbcred.times.endtime > exptime) 1592 exptime = krbcred.times.endtime; 1593 ret = 1; 1594 } 1595 krb5_free_cred_contents(context, &krbcred); 1596 } 1597 krb5_cc_end_seq_get(context, ccache, &curse); 1598 } 1599 krb5_cc_close(context, ccache); 1600 krb5_free_context(context); 1601 if (ret != 0) { 1602 *retrating = orating; 1603 *retexptime = exptime; 1604 } 1605 return (ret); 1606 } 1607 1608 /* 1609 * This function attempts to do essentially a "kinit -k" for the principal 1610 * name provided as the argument, so that there will be a TGT in the 1611 * credential cache. 1612 */ 1613 static krb5_error_code 1614 gssd_get_cc_from_keytab(const char *name) 1615 { 1616 krb5_error_code ret, opt_ret, princ_ret, cc_ret, kt_ret, cred_ret; 1617 krb5_context context; 1618 krb5_principal principal; 1619 krb5_keytab kt; 1620 krb5_creds cred; 1621 krb5_get_init_creds_opt *opt; 1622 krb5_deltat start_time = 0; 1623 krb5_ccache ccache; 1624 1625 ret = krb5_init_context(&context); 1626 if (ret != 0) 1627 return (ret); 1628 opt_ret = cc_ret = kt_ret = cred_ret = 1; /* anything non-zero */ 1629 princ_ret = ret = krb5_parse_name(context, name, &principal); 1630 if (ret == 0) 1631 opt_ret = ret = krb5_get_init_creds_opt_alloc(context, &opt); 1632 if (ret == 0) 1633 cc_ret = ret = krb5_cc_default(context, &ccache); 1634 if (ret == 0) 1635 ret = krb5_cc_initialize(context, ccache, principal); 1636 if (ret == 0) { 1637 #ifndef MK_MITKRB5 1638 /* For Heimdal only */ 1639 krb5_get_init_creds_opt_set_default_flags(context, "gssd", 1640 krb5_principal_get_realm(context, principal), opt); 1641 #endif 1642 kt_ret = ret = krb5_kt_default(context, &kt); 1643 } 1644 if (ret == 0) 1645 cred_ret = ret = krb5_get_init_creds_keytab(context, &cred, 1646 principal, kt, start_time, NULL, opt); 1647 if (ret == 0) 1648 ret = krb5_cc_store_cred(context, ccache, &cred); 1649 if (kt_ret == 0) 1650 krb5_kt_close(context, kt); 1651 if (cc_ret == 0) 1652 krb5_cc_close(context, ccache); 1653 if (opt_ret == 0) 1654 krb5_get_init_creds_opt_free(context, opt); 1655 if (princ_ret == 0) 1656 krb5_free_principal(context, principal); 1657 if (cred_ret == 0) 1658 krb5_free_cred_contents(context, &cred); 1659 krb5_free_context(context); 1660 return (ret); 1661 } 1662 1663 /* 1664 * Acquire a gss credential for a uid. 1665 */ 1666 static OM_uint32 1667 gssd_get_user_cred(OM_uint32 *min_statp, uid_t uid, gss_cred_id_t *credp) 1668 { 1669 gss_buffer_desc principal_desc; 1670 gss_name_t name; 1671 OM_uint32 maj_stat, min_stat; 1672 gss_OID_set mechlist; 1673 struct passwd *pw; 1674 1675 pw = getpwuid(uid); 1676 if (pw == NULL) { 1677 *min_statp = 0; 1678 return (GSS_S_FAILURE); 1679 } 1680 1681 /* 1682 * The mechanism must be set to KerberosV for acquisition 1683 * of credentials to work reliably. 1684 */ 1685 maj_stat = gss_create_empty_oid_set(min_statp, &mechlist); 1686 if (maj_stat != GSS_S_COMPLETE) 1687 return (maj_stat); 1688 maj_stat = gss_add_oid_set_member(min_statp, GSS_KRB5_MECH_OID_X, 1689 &mechlist); 1690 if (maj_stat != GSS_S_COMPLETE) { 1691 gss_release_oid_set(&min_stat, &mechlist); 1692 return (maj_stat); 1693 } 1694 1695 principal_desc.value = (void *)pw->pw_name; 1696 principal_desc.length = strlen(pw->pw_name); 1697 maj_stat = gss_import_name(min_statp, &principal_desc, 1698 GSS_C_NT_USER_NAME, &name); 1699 if (maj_stat != GSS_S_COMPLETE) { 1700 gss_release_oid_set(&min_stat, &mechlist); 1701 return (maj_stat); 1702 } 1703 /* Acquire the credentials. */ 1704 maj_stat = gss_acquire_cred(min_statp, name, 0, mechlist, 1705 GSS_C_INITIATE, credp, NULL, NULL); 1706 gss_release_name(&min_stat, &name); 1707 gss_release_oid_set(&min_stat, &mechlist); 1708 return (maj_stat); 1709 } 1710 1711 void gssd_terminate(int sig __unused) 1712 { 1713 1714 if (hostbased_initiator_cred != 0) 1715 unlink(GSSD_CREDENTIAL_CACHE_FILE); 1716 exit(0); 1717 } 1718 1719