1/* 2 * Copyright (c) 2006-2014 Apple Inc. All rights reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * The contents of this file constitute Original Code as defined in and 7 * are subject to the Apple Public Source License Version 1.1 (the 8 * "License"). You may not use this file except in compliance with the 9 * License. Please obtain a copy of the License at 10 * http://www.apple.com/publicsource and read it before using this file. 11 * 12 * This Original Code and all software distributed under the License are 13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER 14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the 17 * License for the specific language governing rights and limitations 18 * under the License. 19 * 20 * @APPLE_LICENSE_HEADER_END@ 21 */ 22 23/* 24 * gssd daemon. 25 * 26 * Gssd is used to proxy requests from the kernel to set up or accept GSS 27 * security contexts. The kernel makes up calls to these routines here via 28 * mach messaging as defined by gssd_mach.defs. Launchd is used to set up 29 * a task special port in both the start up context and in the per session 30 * context. The supplied plist that launchd uses for the start up context, 31 * /System/Library/LaunchDaemons/com.apple.gssd.plist, will set the program 32 * name to /usr/sbin/gssd, and in the per user session context, found at 33 * /System/Library/LaunchAgents/com.apple.gssd.plist, launchd will set the 34 * program name to gssd-agent. By using a special task port, we can fetch 35 * a send right from the task making a secure mount call in the kernel. 36 * Launchd will own the receive right and will thus start this daemon on 37 * demand as defined in the above plists. Since the daemon is invoked in 38 * the correct context, the GSS-API will be able to obtain the appropriate 39 * credentials with gss acquire cred. 40 * 41 * This daemon will set up the context and then wait for a spell (TIMEOUT below) 42 * to service any other requests. If no requests come we simply exit and 43 * let launchd restart us if necessary on the next mount request. In this way 44 * we are not using system resources unnecessarily and we're pretty well 45 * protected from any bad consequences of any resource leaks. 46 */ 47 48#include <bsm/audit.h> 49#include <bsm/libbsm.h> 50#include <libkern/OSAtomic.h> 51#include <sys/param.h> 52#include <sys/time.h> 53#include <mach/mach.h> 54#include <mach/mach_error.h> 55#include <servers/bootstrap.h> 56#include <uuid/uuid.h> 57 58#include <bootstrap_priv.h> 59#include <asl.h> 60#include <asl_private.h> 61#include <ctype.h> 62#include <errno.h> 63#include <grp.h> 64#include <membership.h> 65#include <netdb.h> 66#include <notify.h> 67#include <pthread.h> 68#include <pwd.h> 69#include <signal.h> 70#include <stdbool.h> 71#include <stdio.h> 72#include <stdlib.h> 73#include <string.h> 74#include <unistd.h> 75#include <vproc.h> 76#ifdef VDEBUG 77#include <time.h> 78#include "/usr/local/include/vproc_priv.h" 79#endif 80 81#include <Heimdal/com_err.h> 82#include <Heimdal/krb5.h> 83#include <GSS/gssapi.h> 84#include <GSS/gssapi_krb5.h> 85#include <GSS/gssapi_ntlm.h> 86#include <GSS/gssapi_spnego.h> 87#include <GSS/gssapi_spi.h> 88 89#include "gssd.h" 90#include "gssd/gssd_mach.h" 91#include "gssd_machServer.h" 92 93mach_port_t gssd_receive_right; 94 95union MaxMsgSize { 96 union __RequestUnion__gssd_mach_subsystem req; 97 union __ReplyUnion__gssd_mach_subsystem rep; 98}; 99 100#define MAX_GSSD_MSG_SIZE (sizeof (union MaxMsgSize) + MAX_TRAILER_SIZE) 101 102 103 104 105#define APPLE_PREFIX "com.apple." /* Bootstrap name prefix */ 106#define MAXLABEL 256 /* Max bootstrap name */ 107#define MAXTHREADS 64 /* Max number of service threads */ 108#define NOBODY (uint32_t)-2 /* Default nobody user/group id */ 109#define TIMEOUT 30 /* 30 seconds and then bye. */ 110#define SHUTDOWN_TIMEOUT 2 /* timeout gets set to this after TERM signal */ 111 112#define NFS_SERVICE "nfs" 113#define NFS_SERVICE_LEN 3 114#define IS_NFS_SERVICE(s) ((strncmp((s), NFS_SERVICE, NFS_SERVICE_LEN) == 0) && \ 115 ((s)[NFS_SERVICE_LEN] == '/' || (s)[NFS_SERVICE_LEN] == '@')) 116 117krb5_enctype NFS_ENCTYPES[] = { 118 ENCTYPE_DES_CBC_CRC, 119 ENCTYPE_DES_CBC_MD5, 120 ENCTYPE_DES_CBC_MD4, 121 ENCTYPE_DES3_CBC_SHA1 122}; 123 124#define NUM_NFS_ENCTYPES ((uint32_t)(sizeof(NFS_ENCTYPES)/sizeof(krb5_enctype))) 125extern int ctx_counter; 126 127static uint32_t uid_to_gss_name(uint32_t *, uid_t, gss_OID, gss_name_t *); 128static char * get_next_kerb_component(char *); 129static uint32_t gss_name_to_ucred(uint32_t *, gss_name_t, uid_t *, gid_t *, uint32_t *); 130static char * lowercase(char *); 131static char * canonicalize_host(const char *, char **); 132static uint32_t str_to_svc_names(uint32_t *, const char *, gss_name_t *, uint32_t *); 133static void gssd_init(void); 134static void * receive_message(void *); 135static void new_worker_thread(void); 136static void end_worker_thread(void); 137static void compute_new_timeout(struct timespec *); 138static void * shutdown_thread(void *); 139static void disable_timeout(int); 140static void * timeout_thread(void *); 141static void vm_alloc_buffer(gss_buffer_t, uint8_t **, uint32_t *); 142static uint32_t GetSessionKey(uint32_t *, gss_OID mech, gss_ctx_id_t, gssd_byte_buffer *, 143 mach_msg_type_number_t *); 144static uint32_t badcall(char *, uint32_t *, gssd_ctx *, gssd_cred *, uint32_t *, 145 gssd_byte_buffer *, mach_msg_type_number_t *, 146 gssd_byte_buffer *, mach_msg_type_number_t *); 147 148static time_t timeout = TIMEOUT; /* Seconds to wait before exiting */ 149static int die = 0; /* Simulate server death. Testing only */ 150static int bye = 0; /* Force clean shutdown flag. */ 151static int no_canon = 0; /* Don't canonicalize host names */ 152static int acquire_default = 0; /* Don't acquire default credentials in do_acquire_cred */ 153static int maxthreads = MAXTHREADS; /* Maximum number of service threads. */ 154static int numthreads = 0; /* Current number of service threads */ 155static int kernel_only = TRUE; /* Restricts mach_gss_lookup for kernel only */ 156static pthread_mutex_t numthreads_lock[1]; /* lock to protect above */ 157static pthread_cond_t numthreads_cv[1]; /* To signal when we're below max. */ 158static pthread_attr_t attr[1]; /* Needed to create detached threads */ 159static pthread_t timeout_thr; /* Thread sees if we've been inactive and exits */ 160static pthread_t shutdown_thr; /* Thread to handle signals */ 161 162/* Counters used in debugging for init and accept context */ 163static volatile int32_t initCnt = 0; 164static volatile int32_t initErr = 0; 165 166static volatile int32_t acceptCnt = 0; 167static volatile int32_t acceptErr = 0; 168 169uid_t NobodyUid = NOBODY; 170gid_t NobodyGid = NOBODY; 171 172char *local_host; /* our FQDN */ 173long GetPWMaxRSz; /* Storage size for password entry */ 174 175sigset_t waitset[1]; /* Signals that we wait for */ 176sigset_t contset[1]; /* Signals that we don't exit from */ 177 178/* 179 * OID table for supported mechs. This is index by the enumeration type mechtype 180 * found in gss_mach_types.h. 181 */ 182static gss_OID mechtab[] = { 183 NULL, /* Place holder for GSS_KRB5_MECHANISM */ 184 NULL, /* Place holder for GSS_SPNEGO_MECHANISM */ 185 NULL, /* Place holder for GSS_NTLM_MECHANISM */ 186 NULL, /* Place holder for GSS_IAKERB_MECHANISM */ 187 NULL 188}; 189 190 191/* 192 * Hopefully Heimdal will fix this in their library and this can go away. 193 */ 194 195#ifdef WIN2K_HACK 196static size_t 197derlen(uint8_t **dptr, uint8_t *eptr) 198{ 199 int i; 200 uint8_t *p = *dptr; 201 size_t len = 0; 202 203 if (*p & 0x80) { 204 for (i = *p & 0x7f; i > 0 && (eptr == NULL || (p < eptr)); i--) 205 len = (len << 8) + *++p; 206 } else 207 len = *p; 208 209 *dptr = p + 1; 210 211 return (len); 212} 213 214#define ADVANCE(p, l, e) do { \ 215 (p) += (l); \ 216 DEBUG(4, "Advancing %d bytes\n", (int)(l)); \ 217 if ((p) > (e)) { \ 218 DEBUG(4, "Defective p = %p e = %p\n", (p), (e)); \ 219 return (GSS_S_DEFECTIVE_TOKEN); \ 220 } \ 221 } while (0) 222 223#define CHK(p, v, e) (((p) >= (e) || *(p) != (v)) ? 0 : 1) 224 225static size_t 226encode_derlen(size_t len, size_t max, uint8_t *value) 227{ 228 size_t i; 229 size_t count, len_save = len; 230 231 if (len < 0x80) { 232 if (max > 0 && value) 233 value[0] = len; 234 return 1; 235 } 236 237 for (count = 0; len; count++) 238 len >>= 8; 239 240 len = len_save; 241 if (value && max > count) { 242 for (i = count; i > 0; i--, len >>= 8) { 243 value[i] = (len & 0xff ); 244 } 245 value[0] = (0x80 | count); 246 } 247 /* Extra octet to hold the count of length bytes */ 248 return (count + 1); 249} 250 251#define SEQUENCE 0x30 252#define CONTEXT 0xA0 253#define ENUM 0x0A 254#define OCTETSTRING 0x04 255 256/* 257 * Windows 2k is including a bogus MIC in the return token from the server 258 * which fails in the gss_init_sec_context call. The mic appears to always be 259 * another copy of the kerberos AP_REP token. Go figure. At any rate this 260 * routine takes the input token, ASN1 decodes it and if there is a bad Mic 261 * removes it and adjust the token so that it is valid again. We should move 262 * this into the kerberos library when we have enough experience that this routine 263 * covers all the w2k cases. 264 */ 265 266static uint32_t 267spnego_win2k_hack(gss_buffer_t token) 268{ 269 uint8_t *ptr, *eptr, *response, *start, *end; 270 size_t len, rlen, seqlen, seqlenbytes, negresplen, negresplenbytes, tlen; 271 272 ptr = token->value; 273 eptr = ptr + token->length; 274 275 DEBUG(3, "token value\n"); 276 HEXDUMP(e, token->value, token->length); 277 278 279 /* CHOICE [1] negTokenResp */ 280 if (!CHK(ptr, (CONTEXT | 1), eptr)) 281 return (GSS_S_DEFECTIVE_TOKEN); 282 ADVANCE(ptr, 1, eptr); 283 len = derlen(&ptr, eptr); 284 /* Sequence */ 285 if (!CHK(ptr, SEQUENCE, eptr)) 286 return (GSS_S_DEFECTIVE_TOKEN); 287 ADVANCE(ptr, 1, eptr); 288 len = derlen(&ptr, eptr); 289 /* Save start of first element in sequence [0] enum*/ 290 start = ptr; 291 if (!CHK(ptr, (CONTEXT | 0), eptr)) 292 return (GSS_S_DEFECTIVE_TOKEN); 293 ADVANCE(ptr, 1, eptr); 294 len = derlen(&ptr, eptr); 295 if (len != 3) 296 return (GSS_S_DEFECTIVE_TOKEN); 297 if (!CHK(ptr, ENUM, eptr)) 298 return (GSS_S_DEFECTIVE_TOKEN); 299 ADVANCE(ptr, 1, eptr); 300 len = derlen(&ptr, eptr); 301 if (len != 1) 302 return (GSS_S_DEFECTIVE_TOKEN); 303 if (!CHK(ptr, 0x0, eptr)) /* != ACCEPT_COMPLETE */ 304 return (GSS_S_DEFECTIVE_TOKEN); 305 ADVANCE(ptr, 1, eptr); 306 /* Get the mech type accepted */ 307 if (!CHK(ptr, (CONTEXT | 1), eptr)) 308 return (GSS_S_DEFECTIVE_TOKEN); 309 ADVANCE(ptr, 1, eptr); 310 len = derlen(&ptr, eptr); 311 /* Skip past the oid bytes -- should check for kerberos? */ 312 ADVANCE(ptr, len, eptr); 313 /* Check for the response token */ 314 if (!CHK(ptr, (CONTEXT | 2), eptr)) 315 return (GSS_S_DEFECTIVE_TOKEN); 316 ADVANCE(ptr, 1, eptr); 317 len = derlen(&ptr, eptr); 318 if (!CHK(ptr, OCTETSTRING, eptr)) 319 return (GSS_S_DEFECTIVE_TOKEN); 320 ADVANCE(ptr, 1, eptr); 321 rlen = derlen(&ptr, eptr); 322 response = ptr; 323 /* Skip rest of response token */ 324 ADVANCE(ptr, rlen, eptr); 325 if (ptr == eptr) 326 /* No mic part so nothing to do */ 327 return (GSS_S_COMPLETE); 328 end = ptr; /* Save the end of the token */ 329 /* See if we have a mechMic */ 330 if (!CHK(ptr, (CONTEXT | 3), eptr)) 331 return (GSS_S_DEFECTIVE_TOKEN); 332 ADVANCE(ptr, 1, eptr); 333 len = derlen(&ptr, eptr); 334 if (!CHK(ptr, OCTETSTRING, eptr)) 335 return (GSS_S_DEFECTIVE_TOKEN); 336 ADVANCE(ptr, 1, eptr); 337 len = derlen(&ptr, eptr); 338 if (len != rlen || ptr + rlen != eptr || memcmp(response, ptr, rlen) != 0) { 339 DEBUG(3, "Mic does not equal response %p %p %p len = %d rlen = %d\n", 340 ptr, ptr + rlen, eptr, (int)len, (int)rlen); 341 return (GSS_S_DEFECTIVE_TOKEN); 342 } 343 344 /* 345 * Ok we have a bogus mic, lets chop it off. This is the length value 346 * of the sequence in the negTokenResp 347 */ 348 seqlen = end - start; 349 350 /* Number of bytes to ecode the length */ 351 seqlenbytes = encode_derlen(seqlen, 0, 0); 352 /* 353 * Length of the sequence in the negToken response. Note we add one 354 * for the sequence tag itself 355 */ 356 negresplen = seqlen + seqlenbytes + 1; 357 negresplenbytes = encode_derlen(negresplen, 0, 0); 358 /* 359 * Total negTokenResp length 360 */ 361 tlen = negresplen + negresplenbytes + 1; /* One for the context 1 tag */ 362 /* 363 * Now we do surgery on the token, 364 */ 365 ptr = token->value; 366 *ptr++ = CONTEXT | 1; 367 encode_derlen(negresplen, negresplenbytes, ptr); 368 ptr += negresplenbytes; 369 *ptr++ = SEQUENCE; 370 encode_derlen(seqlen, seqlenbytes, ptr); 371 ptr += seqlenbytes; 372 memmove(ptr, start, seqlen); 373 token->length = tlen; 374 375 DEBUG(3, "Returning token"); 376 HEXDUMP(3, token->value, token->length); 377 378 return (GSS_S_COMPLETE); 379} 380#endif 381 382static kern_return_t 383checkin_or_register(char *service, mach_port_t *server_port) 384{ 385 kern_return_t kr; 386 387 /* 388 * Check in with launchd to get the receive right. 389 * N.B. Since we're using a task special port, if launchd 390 * does not have the receive right we can't get it. 391 * And since we should always be started by launchd 392 * this should always succeed. 393 */ 394 395 kr = bootstrap_check_in(bootstrap_port, service, server_port); 396 if (kr == BOOTSTRAP_SUCCESS) 397 return (KERN_SUCCESS); 398 399 Log("Could not checkin for receive right: %s\n", bootstrap_strerror(kr)); 400 401 /* This should never happen */ 402 403 kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, server_port); 404 if (kr != KERN_SUCCESS) { 405 Log("mach_port_allocation failed: %s\n", mach_error_string(kr)); 406 return (kr); 407 } 408 409 kr = mach_port_insert_right(mach_task_self(), *server_port, *server_port, MACH_MSG_TYPE_MAKE_SEND); 410 if (kr != KERN_SUCCESS) { 411 Log("mach_port_insert_right failed: %s\n", mach_error_string(kr)); 412 return (kr); 413 } 414 415 kr = bootstrap_register2(bootstrap_port, service, *server_port, 0); 416 if (kr != KERN_SUCCESS) { 417 Log("bootstrap_register2 failed: %s\n", mach_error_string(kr)); 418 return (kr); 419 } 420 421 return (kr); 422} 423 424static int 425uuidstr2sessioninfo(const char *uuid_str, uid_t *uid, au_asid_t *asid) 426{ 427 union { 428 uuid_t uuid; 429 struct { 430 uid_t uid; 431 au_asid_t asid; 432 } info; 433 } u; 434 435 if (uuid_parse(uuid_str, u.uuid)) 436 return (-1); 437 438 *uid = u.info.uid; 439 *asid = u.info.asid; 440 441 return (0); 442} 443 444static void 445sessioninfo2uuid(uid_t uid, au_asid_t asid, uuid_t uuid) 446{ 447 union { 448 uuid_t uuid; 449 struct { 450 uid_t uid; 451 au_asid_t asid; 452 } info; 453 } u; 454 455 uuid_clear(u.uuid); 456 u.info.uid = uid; 457 u.info.asid = asid; 458 uuid_copy(uuid, u.uuid); 459} 460 461static int 462join_session(au_asid_t asid, __unused const char *instance) 463{ 464 int err; 465 au_asid_t asid2; 466 mach_port_name_t session_port; 467 468 err = audit_session_port(asid, &session_port); 469 if (err) { 470 Log("Could not get audit session port for %d: %s", asid, strerror(errno)); 471 /* %%% we should see if we can unregister the sub-job? */ 472 return (-1); 473 } 474 475 asid2 = audit_session_join(session_port); 476 mach_port_deallocate(current_task(), session_port); 477 478 if (asid2 != asid) { 479 Log("Joined session %d but wound up in session %d", asid, asid2); 480 return (-1); 481 } 482 return (0); 483} 484 485/* 486 * Return TRUE if the audit session id is valid, FALSE otherwise 487 */ 488static int 489check_session(au_asid_t asid) 490{ 491 int err; 492 mach_port_name_t session_port; 493 494 if (asid == AU_DEFAUDITSID || asid == AU_ASSIGN_ASID) { 495 Info("Received special audit session id of %d", asid); 496 return (FALSE); 497 } 498 499 err = audit_session_port(asid, &session_port); 500 if (err) { 501 Log("Audit session id %d is in invalid: %s", asid, strerror(errno)); 502 return (FALSE); 503 } 504 505 mach_port_deallocate(current_task(), session_port); 506 return (TRUE); 507} 508 509au_asid_t my_asid = AU_DEFAUDITSID; 510 511static void 512set_identity(void) 513{ 514 const char *instance = getenv("LaunchInstanceID"); 515 auditinfo_addr_t ai; 516 au_asid_t asid = -1; 517 uid_t euid = geteuid(); 518 519 if (getaudit_addr(&ai, sizeof(auditinfo_addr_t))) 520 Debug("getaudit failed: %s", strerror(errno)); 521 else 522 asid = ai.ai_asid; 523 524 Debug("asid = %d euid = %d, instance = %s", ai.ai_asid, 525 euid, instance ? instance : "not set"); 526 if (instance && geteuid() == 0) { 527 uid_t uid; 528 529 if (uuidstr2sessioninfo(instance, &uid, &asid)) 530 Log("Could not parse LaunchInstanceID: %s", instance); 531 else { 532 if (join_session(asid, instance) == 0) 533 setuid(uid); 534 } 535 } 536 537 /* Get my actual audit session id for checkout */ 538 if (getaudit_addr(&ai, sizeof(auditinfo_addr_t))) 539 Log("getaudit failed: %s", strerror(errno)); 540 else 541 my_asid = ai.ai_asid; 542 if (asid != my_asid || getuid() != euid) 543 Info("My identity changed to asid = %d auid = %d uid = %d", ai.ai_asid, ai.ai_auid, getuid()); 544} 545 546static int 547check_audit(audit_token_t atok, int kernonly) 548{ 549 uid_t uid, euid, ruid; 550 gid_t egid, rgid; 551 pid_t pid; 552 au_asid_t asid; 553 int ok; 554 static audit_token_t kern_audit_token = KERNEL_AUDIT_TOKEN_VALUE; 555 556 audit_token_to_au32(atok, &uid, &euid, &egid, &ruid, &rgid, &pid, &asid, NULL); 557 DEBUG(9, "Received audit token: uid = %d, euid = %d, egid = %d, ruid = %d rgid = %d, pid = %d, asid = %d atid = %d", 558 uid, euid, egid, ruid, rgid, pid, asid, atok.val[7]); 559 560 ok = (memcmp(&atok, &kern_audit_token, sizeof (audit_token_t)) == 0); 561 if (!ok && !kernonly) { 562 Debug("gssd asid = %d gssd uid = %d remote pid = %d remote asid = %d remote euid = %d", 563 my_asid, getuid(), pid, asid, euid); 564 ok = (asid == my_asid || (euid && euid == getuid())); 565 } 566 if (!ok) 567 Log("Process %d in session %d as user %d was denied by gssd[%d] for session %d as user %d", pid, asid, euid, getpid(), my_asid, getuid()); 568 569 return (ok); 570} 571 572/* 573 * This daemon is to be started by launchd, as such it follows the following 574 * launchd rules: 575 * We don't: 576 * call daemon(3) 577 * call fork and having the parent process exit 578 * change uids or gids. 579 * set up the current working directory or chroot. 580 * set the session id 581 * change stdio to /dev/null. 582 * call setrusage(2) 583 * call setpriority(2) 584 * Ignore SIGTERM. 585 * We are launched on demand 586 * and we catch SIGTERM to exit cleanly. 587 * 588 * In practice daemonizing in the classic unix sense would probably be ok 589 * since we get invoke by traffic on a task_special_port, but we will play 590 * by the rules, its even easier to boot. 591 */ 592 593char label_buf[MAXLABEL]; 594char *bname = label_buf; 595 596int main(int argc, char *argv[]) 597{ 598 kern_return_t kr; 599 int error; 600 int ch; 601 int debug_opt = 0; 602 603 /* If launchd is redirecting these to files they'll be blocked */ 604 /* buffered. Probably not what you want. */ 605 setlinebuf(stdout); 606 setlinebuf(stderr); 607 608 /* Figure out our bootstrap name based on what we are called. */ 609 setprogname(argv[0]); 610 strlcpy(label_buf, APPLE_PREFIX, sizeof(label_buf)); 611 strlcat(label_buf, getprogname(), sizeof(label_buf)); 612 613 while ((ch = getopt(argc, argv, "b:Cdhm:n:t:DT")) != -1) { 614 switch (ch) { 615 case 'C': 616 no_canon = 1; 617 break; 618 case 'd': /* Debug */ 619 debug_opt++; 620 break; 621 case 'm': 622 maxthreads = atoi(optarg); 623 if (maxthreads < 1) 624 maxthreads = MAXTHREADS; 625 break; 626 case 'b': 627 case 'n': 628 bname = optarg; 629 break; 630 case 't': 631 timeout = atoi(optarg); 632 if (timeout < 10) 633 timeout = TIMEOUT; 634 break; 635 case 'D': 636 acquire_default = 1; 637 break; 638 case 'T': 639 kernel_only = FALSE; 640 break; 641 case 'h': 642 /* FALLTHROUGH */ 643 default: 644 Log("usage: %s [-Cdht] [-m threads] " 645 "[-n bootstrap name]\n", argv[0]); 646 exit(EXIT_FAILURE); 647 } 648 } 649 650/* 651 * Currently we don't do anything else with argc, argv. 652 * 653 * argc -= optind; 654 * argv += optind; 655 */ 656 kr = checkin_or_register(bname, &gssd_receive_right); 657 if (kr != KERN_SUCCESS) 658 exit(EXIT_FAILURE); 659 660 sigemptyset(waitset); 661 sigaddset(waitset, SIGQUIT); 662 if (!traced() && !in_foreground(2)) 663 sigaddset(waitset, SIGINT); 664 sigaddset(waitset, SIGHUP); 665 sigaddset(waitset, SIGUSR1); 666 sigaddset(waitset, SIGUSR2); 667 *contset = *waitset; 668 sigaddset(waitset, SIGTERM); 669 pthread_sigmask(SIG_BLOCK, waitset, NULL); 670 671 (void) pthread_mutex_init(numthreads_lock, NULL); 672 (void) pthread_cond_init(numthreads_cv, NULL); 673 (void) pthread_attr_init(attr); 674 (void) pthread_attr_setdetachstate(attr, PTHREAD_CREATE_DETACHED); 675 676 /* Allow set_debug_level to disable our timeout */ 677 set_debug_level_init(disable_timeout); 678 /* Set initial debug_level */ 679 set_debug_level(debug_opt); 680 /* Check to see if the master asl filter is set */ 681 set_debug_level(-1); 682 683 /* Set our session and uid if needed */ 684 set_identity(); 685 686 687 gssd_init(); 688 689 /* Create signal handling thread */ 690 error = pthread_create(&shutdown_thr, attr, shutdown_thread, NULL); 691 if (error) { 692 Log("unable to create shutdown thread: %s", strerror(error)); 693 exit(EXIT_FAILURE); 694 } 695 696 /* Create time out thread */ 697 error = pthread_create(&timeout_thr, NULL, timeout_thread, NULL); 698 if (error) { 699 Log("unable to create time out thread: %s", strerror(error)); 700 exit(EXIT_FAILURE); 701 } 702 703#ifdef VDEBUG 704 { 705 time_t now; 706 if (debug == 2) 707 vproc_transaction_begin(NULL); 708 709 now = time(NULL); 710 DEBUG(3, "starting %s with transaction count = %lu, " 711 "standby count = %lu\n", ctime(&now), 712 (unsigned long)_vproc_transaction_count(), 713 (unsigned long)_vproc_standby_count()); 714 } 715#endif 716 717 /* 718 * Kick off a thread to wait for a message. Shamelessly stolen from 719 * automountd. 720 */ 721 new_worker_thread(); 722 723 /* Wait for time out */ 724 pthread_join(timeout_thr, NULL); 725 726 DEBUG(3, "Time out exiting. Number of threads is %d\n", numthreads); 727 728 pthread_attr_destroy(attr); 729 730 DEBUG(2, "Total %d init_sec_context errors out of %d calls\n", initErr, initCnt); 731 DEBUG(2, "Total %d accept_sec_context errors out of %d calls\n", acceptErr, acceptCnt); 732 DEBUG(2, "Total entries left = %d\n", ctx_counter); 733#ifdef VDEBUG 734 DEBUG(3, "exiting with transaction count = %lu, " 735 "standby count = %lu\n", 736 (unsigned long) _vproc_transaction_count(), 737 (unsigned long) _vproc_standby_count()); 738#endif 739 740 return (0); 741} 742 743static int 744get_local_realms(krb5_realm **realms) 745{ 746 int error; 747 krb5_context kctx; 748 749 if (realms == NULL) 750 return (FALSE); 751 *realms = NULL; 752 error = krb5_init_context(&kctx); 753 if (error) { 754 Log("Could not get kerberos context"); 755 krb5_free_context(kctx); 756 return (FALSE); 757 } 758 error = krb5_get_default_realms(kctx, realms); 759 krb5_free_context(kctx); 760 if (error) { 761 Log("Could not get kerbose default realms"); 762 return (FALSE); 763 } 764 return (TRUE); 765} 766 767static void 768free_local_realms(krb5_realm *realms) 769{ 770 int error; 771 krb5_context kctx; 772 773 if (realms == NULL) 774 return; 775 776 error = krb5_init_context(&kctx); 777 if (error) { 778 Log("Could not get kerberos context"); 779 return; 780 } 781 (void )krb5_free_host_realm(kctx, realms); 782 krb5_free_context(kctx); 783} 784 785/* 786 * Given a uid and name type convert it to a gss_name_t 787 */ 788static uint32_t 789uid_to_gss_name(uint32_t *minor, uid_t uid, gss_OID oid, gss_name_t *name) 790{ 791 char pwbuf[GetPWMaxRSz]; 792 struct passwd *pwd, pwent; 793 char *princ_str; 794 gss_buffer_desc buf_name; 795 uint32_t major; 796 size_t len; 797 size_t realmlen; 798 krb5_realm *realms = NULL; 799 krb5_realm default_realm = NULL; 800 int rc; 801 802 *minor = 0; 803 804 rc = getpwuid_r(uid, &pwent, pwbuf, sizeof(pwbuf), &pwd); 805 if (rc != 0 || pwd == NULL) 806 return (GSS_S_UNAUTHORIZED); 807 808 if (get_local_realms(&realms)) 809 default_realm = *realms; 810 811 realmlen = default_realm ? strlen(default_realm) : 0; 812 len = strlen(pwd->pw_name) + 1 + realmlen + 1; 813 len = maximum(len, 10); /* max string rep for uids */ 814 len = maximum(len, 5 + strlen(local_host) + 1 + realmlen + 1); 815 if ((princ_str = malloc(len)) == NULL) { 816 free_local_realms(realms); 817 return (GSS_S_FAILURE); 818 } 819 if (gss_oid_equal(oid, GSS_KRB5_NT_PRINCIPAL_NAME)) { 820 if (pwd->pw_uid == 0) { 821 /* use the host principal */ 822 if (default_realm) 823 snprintf(princ_str, len, 824 "host/%s@%s", local_host, default_realm); 825 else 826 snprintf(princ_str, len, "host/%s", local_host); 827 } else { 828 if (default_realm) 829 snprintf(princ_str, len, 830 "%s@%s", pwd->pw_name, default_realm); 831 else 832 snprintf(princ_str, len, "%s", pwd->pw_name); 833 } 834 } 835 else if (gss_oid_equal(oid, GSS_C_NT_USER_NAME)) 836 snprintf(princ_str, len, "%s", pwd->pw_name); 837 else if (gss_oid_equal(oid, GSS_C_NT_STRING_UID_NAME)) 838 snprintf(princ_str, len, "%d", pwd->pw_uid); 839 else if (gss_oid_equal(oid, GSS_C_NT_MACHINE_UID_NAME)) 840 memcpy(princ_str, &pwd->pw_uid, sizeof(pwd->pw_uid)); 841 else if (gss_oid_equal(oid, GSS_C_NT_HOSTBASED_SERVICE) && pwd->pw_uid == 0) 842 snprintf(princ_str, len, "host@%s", local_host); 843 else { 844 free(princ_str); 845 free_local_realms(realms); 846 return (GSS_S_FAILURE); 847 } 848 849 str_to_buf(princ_str, &buf_name); 850 851 DEBUG(2, "importing name %s\n", princ_str); 852 853 major = gss_import_name(minor, &buf_name, oid, name); 854 855 free(princ_str); 856 free_local_realms(realms); 857 858 return (major); 859} 860 861/* 862 * get_next_kerb_component. Get the next kerberos component from string. 863 */ 864static char * 865get_next_kerb_component(char *str) 866{ 867 char *s, *p; 868 869 /* 870 * Its possible to include "/" and "@" in the leading 871 * components of a kerberos principal name if they 872 * are back slashed escaped, as in, fo\/o\@@realm. 873 */ 874 875 s = str; 876 do { 877 p = strpbrk(s, "/@\\"); 878 s = (p && *p == '\\' && *(p+1)) ? p + 2 : NULL; 879 } while (s); 880 881 return (p); 882} 883 884 885/* 886 * getucred: Given a user name return the corresponding uid and gid list. 887 * Note the first gid in the list is the principal (passwd entry) gid. 888 * 889 * Return: True on success of False on failure. Note on failure, *uid and *gid 890 * are set to nobody and *ngroups is set to 1. 891 */ 892static bool 893getucred(const char *uname, uid_t *uid, gid_t *gids, uint32_t *ngroups) 894{ 895 struct passwd *pwd, pwent; 896 char pwdbuf[GetPWMaxRSz]; 897 *uid = NobodyUid; 898 *gids = NobodyGid; 899 *ngroups = 1; 900 901 (void) getpwnam_r(uname, &pwent, pwdbuf, sizeof(pwdbuf), &pwd); 902 if (pwd) { 903 *uid = pwd->pw_uid; 904 *ngroups = NGROUPS_MAX; 905 if (getgrouplist(uname, pwd->pw_gid, 906 (int *)gids, (int *)ngroups) == -1) { 907 /* Best we can do is just return the principal gid */ 908 *gids = pwd->pw_gid; 909 *ngroups = 1; 910 } 911 return (true); 912 } 913 return (false); 914} 915 916/* 917 * Given a gss_name_t convert it to a local uid. We use an optional list 918 * of kerberos realm names to try if name can't be resolve to a passwd 919 * entry directly after converting it to a display name. 920 */ 921static uint32_t 922gss_name_to_ucred_1(uint32_t *minor, gss_name_t name, 923 uid_t *uid, gid_t *gids, uint32_t *ngroups) 924{ 925 uint32_t major; 926 char *name_str = NULL; 927 gss_buffer_desc buf; 928 gss_OID oid = GSS_C_NO_OID; 929 char **rlm, *this_realm, *uname; 930 bool gotname; 931 krb5_realm *realms = NULL; 932 933 *minor = 0; 934 935 /* 936 * Convert name to text string and fetch the name type. 937 */ 938 major = gss_display_name(minor, name, &buf, &oid); 939 if (major != GSS_S_COMPLETE) 940 return (major); 941 942 name_str = buf_to_str(&buf); 943 if (name_str == NULL) 944 return (GSS_S_FAILURE); 945 946 uname = name_str; 947 948 /* 949 * See if we get lucky and the string version of the name 950 * can be found. 951 */ 952 953 if ((gotname = getucred(uname, uid, gids, ngroups))) 954 goto out; 955 956 if (gss_oid_equal(oid, GSS_KRB5_NT_PRINCIPAL_NAME)) { 957 /* 958 * If we failed the above lookup and we're a kerberos name 959 * and if the realm of the name is one of our local realms, 960 * try looking up the first component and see if its a user we 961 * know. We ignore any instance part here, i.e., we assume 962 * user@realm and user/instance@realm are the same for all 963 * instances. 964 */ 965 this_realm = strrchr(name_str, '@'); 966 if (this_realm == NULL) 967 goto out; 968 this_realm++; 969 if (!get_local_realms(&realms)) 970 goto out; 971 for(rlm = realms; rlm && *rlm; rlm++) { 972 if (strncmp(this_realm, *rlm, buf.length) == 0) { 973 char *p; 974 975 p = get_next_kerb_component(name_str); 976 if (p) 977 *p = '\0'; 978 979 gotname = getucred(uname, uid, gids, ngroups); 980 goto out; 981 } 982 } 983 } 984out: 985 if (!gotname) 986 Log("Directory Service could not map %s to unix credentials. Directory Service problem?\n", uname); 987 else 988 Info("Directory Service mapped %s to uid %d", uname, *uid); 989 990 free(uname); 991 free_local_realms(realms); 992 993 return (uint32_t)(gotname ? GSS_S_COMPLETE : GSS_S_FAILURE); 994} 995 996/* 997 * Given a gss_name_t convert it to a local uid. 998 */ 999static uint32_t 1000gss_name_to_ucred(uint32_t *min, gss_name_t name, 1001 uid_t *uid, gid_t *gids, uint32_t *ngroups) 1002{ 1003 uint32_t maj, ms; 1004 gss_buffer_desc xname; 1005 uuid_t uu; 1006 int ret; 1007 int type; 1008 struct passwd *pwd, pwent; 1009 char pwdbuf[GetPWMaxRSz]; 1010 *uid = NobodyUid; 1011 *gids = NobodyGid; 1012 *ngroups = 1; 1013 1014 maj = gss_export_name(min, name, &xname); 1015 if (maj != GSS_S_COMPLETE) 1016 return (maj); 1017 1018 ret = mbr_identifier_to_uuid(ID_TYPE_GSS_EXPORT_NAME, xname.value, xname.length, uu); 1019 (void) gss_release_buffer(&ms, &xname); 1020 1021 if (ret) { 1022 DEBUG(2, "mbr_identifier_to_uid: failed to map export name to uuid: reason %d\n", ret); 1023 return (gss_name_to_ucred_1(min, name, uid, gids, ngroups)); 1024 } 1025 1026 ret = mbr_uuid_to_id(uu, uid, &type); 1027 if (ret || type != ID_TYPE_UID) { 1028 Info("gssapi: failed to turn uuid into uid: %d", ret); 1029 return (GSS_S_FAILURE); 1030 } 1031 1032 ret = getpwuid_r(*uid, &pwent, pwdbuf, sizeof(pwdbuf), &pwd); 1033 if (ret) { 1034 Info("Look up of uid %d failed. Reason %d: %s\n", *uid, errno, 1035 strerror(errno)); 1036 return (GSS_S_FAILURE); 1037 } 1038 1039 if (pwd) { 1040 *ngroups = NGROUPS_MAX; 1041 if (getgrouplist(pwd->pw_name, pwd->pw_gid, 1042 (int *)gids, (int *)ngroups) == -1) { 1043 /* Best we can do is just return the principal gid */ 1044 *gids = pwd->pw_gid; 1045 *ngroups = 1; 1046 } else { 1047 DEBUG(2, "getgrouplist failed.\n"); 1048 } 1049 } else { 1050 Log("Directory Service could not find uid %d.\n", *uid); 1051 return (GSS_S_FAILURE); 1052 } 1053 1054 return (GSS_S_COMPLETE); 1055} 1056 1057 1058static char * 1059lowercase(char *s) 1060{ 1061 char *t; 1062 1063 for (t = s; t && *t; t++) 1064 *t = tolower(*t); 1065 1066 return (s); 1067} 1068 1069/* 1070 * Turn a hostname into a FQDN if we can. Optionally do the reverse lookup 1071 * and return it as well in the rfqdn parameter if it is different from the 1072 * forward lookup. If that parameter is NULL, don't try the reverse lookup. 1073 * at all. If the foward lookup fails we return NULL. 1074 * If we succeed it is the caller's responsibility to free the results. 1075 */ 1076static char * 1077canonicalize_host(const char *host, char **rfqdn) 1078{ 1079 struct hostent *hp, *rhp; 1080 int h_err; 1081 char *fqdn; 1082 1083 if (rfqdn) 1084 *rfqdn = NULL; 1085 1086 hp = getipnodebyname(host, AF_INET6, AI_DEFAULT, &h_err); 1087 if (hp == NULL) { 1088 DEBUG(2, "host look up for %s returned %d\n", host, h_err); 1089 return (NULL); 1090 } 1091 fqdn = strdup(lowercase(hp->h_name)); 1092 if (fqdn == NULL) { 1093 Log("Could not allocat hostname in canonicalize_host\n"); 1094 return (NULL); 1095 } 1096 1097 if (rfqdn) { 1098 DEBUG(2, "Trying reverse lookup\n"); 1099 rhp = getipnodebyaddr(hp->h_addr_list[0], hp->h_length, AF_INET6, &h_err); 1100 if (rhp) { 1101 if (strncmp(fqdn, lowercase(rhp->h_name), MAXHOSTNAMELEN) != 0) { 1102 *rfqdn = strdup(rhp->h_name); 1103 if (*rfqdn == NULL) 1104 Log("Could not allocat hostname in canonicalize_host\n"); 1105 } 1106 freehostent(rhp); 1107 } 1108 else { 1109 DEBUG(2, "reversed host look up for %s returned %d\n", host, h_err); 1110 } 1111 } 1112 1113 freehostent(hp); 1114 1115 return (fqdn); 1116} 1117 1118/* 1119 * Given the service name, host name and realm, construct the kerberos gss 1120 * service name. 1121 */ 1122static uint32_t 1123construct_service_name(uint32_t *minor, const char *service, char *host, 1124 const char *realm, bool lcase, gss_name_t *svcname) 1125{ 1126 size_t len; 1127 char *s; 1128 gss_buffer_desc name_buf; 1129 uint32_t major; 1130 1131 if (lcase) 1132 lowercase(host); 1133 len = strlen(service) + strlen(host) + strlen(realm) + 3; 1134 s = malloc(len); 1135 if (s == NULL) { 1136 Log("Out of memory"); 1137 return (GSS_S_FAILURE); 1138 } 1139 strlcpy(s, service, len); 1140 strlcat(s, "/", len); 1141 strlcat(s, host, len); 1142 strlcat(s, "@", len); 1143 strlcat(s, realm, len); 1144 1145 str_to_buf(s, &name_buf); 1146 1147 Info("Importing kerberos principal service name %s\n", s); 1148 1149 major = gss_import_name(minor, &name_buf, 1150 GSS_KRB5_NT_PRINCIPAL_NAME, svcname); 1151 free(s); 1152 return (major); 1153} 1154 1155static uint32_t 1156construct_hostbased_service_name(uint32_t *minor, const char *service, const char *host, gss_name_t *svcname) 1157{ 1158 size_t len; 1159 char *s; 1160 gss_buffer_desc name_buf; 1161 uint32_t major; 1162 1163 len = strlen(service) + strlen(host) + 2; 1164 s = malloc(len); 1165 if (s == NULL) { 1166 Log("Out of memory"); 1167 return (GSS_S_FAILURE); 1168 } 1169 strlcpy(s, service, len); 1170 strlcat(s, "@", len); 1171 strlcat(s, host, len); 1172 1173 str_to_buf(s, &name_buf); 1174 1175 Info("Importing host based service name %s\n", s); 1176 1177 major = gss_import_name(minor, &name_buf, GSS_C_NT_HOSTBASED_SERVICE, svcname); 1178 1179 DEBUG(2, "gss_import_name returned %#K", major); 1180 1181 free(s); 1182 return (major); 1183} 1184 1185/* 1186 * str_to_svc_name: Given a string representation of a service name, convert it 1187 * into a set of gss service names of name type GSS_KRB5_NT_PRINCIPAL_NAME. 1188 * 1189 * We get up to three names, lowercase of the forward canonicalization of the 1190 * host name, lowercase of the host name itself, and the lowercase of the reverse 1191 * canonicalization of the host name. 1192 * 1193 * name_count is an in/out parameter that says what the size is of the svcname 1194 * array coming in and the number of gss names found coming out. 1195 * if name_count is one, no canonicalization is done 1196 * if name_count is two, return the lowercase of the forward canonicalization 1197 * followed by the non canonicalized host name 1198 * if name_count is three, the two elements above followed by the lowercase of 1199 * the reverse lookup. 1200 * 1201 * We return GSS_S_COMPLETE if we can produce at least one service name. 1202 */ 1203 1204#define LKDCPREFIX "LKDC:" 1205 1206static uint32_t 1207str_to_svc_names(uint32_t *minor, const char *svcstr, 1208 gss_name_t *svcname, uint32_t *name_count) 1209{ 1210 uint32_t major __unused /* To make the static analyser happy */, first_major; 1211 char *realm = NULL /* default_realm */, *host; 1212 char *s, *p, *service; 1213 char *fqdn = NULL, *rfqdn = NULL; 1214 uint32_t count = *name_count; 1215 int is_lkdc; 1216 krb5_realm *realms = NULL; 1217 1218 *minor = 0; 1219 major = GSS_S_FAILURE; 1220 *name_count = 0; 1221 1222 if (svcstr == NULL) { 1223 Log("Null service name string\n"); 1224 return (GSS_S_FAILURE); 1225 } 1226 DEBUG(3, "%s count = %d\n", svcstr, count); 1227 service = strdup(svcstr); 1228 if (service == NULL) { 1229 Log("Out of memory\n"); 1230 return (GSS_S_FAILURE); 1231 } 1232 1233 p = get_next_kerb_component(service); 1234 1235 /*set host part */ 1236 host = p + 1; 1237 1238 if (p == NULL || *p == '\0') { 1239 /* 1240 * We only have the service name so we (this host) 1241 * must be our instance. 1242 */ 1243 host = local_host; 1244 1245 } else if (*p == '@') { 1246 /* Have a host based service name */ 1247 /* Terminate service part of name */ 1248 *p = '\0'; 1249 1250 s = get_next_kerb_component(host); 1251 if (s != NULL) { 1252 Info("Invalid host name part %s\n", host); 1253 free(service); 1254 return (GSS_S_BAD_NAME); 1255 } 1256 major = construct_hostbased_service_name(minor, service, host, svcname); 1257 if (major == GSS_S_COMPLETE) 1258 *name_count = 1; 1259 return (major); 1260 } else if (*p == '/') { 1261 /* We have a kerberos instance thus a kerberos principal type */ 1262 /* Terminate service part of name */ 1263 *p = '\0'; 1264 1265 /* See if we have a realm */ 1266 s = host; 1267 do { 1268 s = get_next_kerb_component(s+1); 1269 if (s && (*s == '@')) { 1270 realm = s + 1; 1271 *s = '\0'; /* terminate host instance */ 1272 break; 1273 } 1274 } while (s); 1275 } else { 1276 /* Should never happen */ 1277 free(service); 1278 return (GSS_S_BAD_NAME); 1279 } 1280 1281 if (realm == NULL) { 1282 /* 1283 * Try this as a host based service name first, since 1284 * host base service name will get canonicalized, looked up in the domain realms 1285 * section and then tried for referrals 1286 */ 1287 major = construct_hostbased_service_name(minor, service, host, svcname); 1288 if (major == GSS_S_COMPLETE) { 1289 *name_count = 1; 1290 free(service); 1291 return (major); 1292 } 1293 /* Nope so set the realm to be the default and fall through */ 1294 if (get_local_realms(&realms)) 1295 realm = *realms; 1296 } 1297 if (realm == NULL) { 1298 free(service); 1299 /* 1300 * Force exit in SHUTDOWN_TIMEOUT. Perhaps 1301 * we'll pickup a default on next start up. 1302 */ 1303 kill(getpid(), SIGTERM); 1304 return (GSS_S_BAD_NAME); 1305 } 1306 1307 1308 is_lkdc = (strncmp(realm, LKDCPREFIX, strlen(LKDCPREFIX)) == 0); 1309 /* Don't lowercase an LKDC instance */ 1310 major = construct_service_name(minor, service, host, realm, !is_lkdc, &svcname[*name_count]); 1311 if (major == GSS_S_COMPLETE) 1312 *name_count += 1; 1313 first_major = major; 1314 1315 /* Don't waste time trying to canonicalize local KDCs */ 1316 if (count == 1 || is_lkdc) 1317 goto done; 1318 1319 fqdn = canonicalize_host(host, (count == 3) ? &rfqdn : NULL); 1320 if (fqdn) { 1321 if (strncmp(fqdn, host, MAXHOSTNAMELEN) != 0) { 1322 major = construct_service_name(minor, service, fqdn, realm, true, &svcname[*name_count]); 1323 if (major == GSS_S_COMPLETE) 1324 *name_count += 1; 1325 } else { 1326 free(fqdn); 1327 } 1328 } 1329 1330 if (rfqdn) { 1331 if (*name_count < count) { 1332 major = construct_service_name(minor, service, rfqdn, realm, true, &svcname[*name_count]); 1333 if (major == GSS_S_COMPLETE) 1334 *name_count += 1; 1335 } else { 1336 free(rfqdn); 1337 } 1338 } 1339 1340done: 1341 free(service); 1342 free_local_realms(realms); 1343 1344 return (*name_count ? GSS_S_COMPLETE : first_major); 1345} 1346 1347/* 1348 * Given the name and name type, convert the name to a gss_name_t. If the name type 1349 * is a mechanism specific (currently one of the kerberos or NTLM name types), we will set the mechtype 1350 * passed in to be that mechanism type. We do this so that we will acquire that mechanism specific 1351 * credential in do_acquire_cred. This is important when the mechanism being used is SPNEGO and 1352 * we end up trying to use the wrong credential. 1353 */ 1354static uint32_t 1355blob_to_name(uint32_t *min, gssd_nametype nt, gssd_byte_buffer name, uint32_t size, gssd_mechtype *mech, char **strrep, char **oidnt, gss_name_t *gname) 1356{ 1357 uint32_t maj; 1358 gss_buffer_desc name_buf = { size, name }; 1359 gss_OID name_type; 1360 *min = GSS_S_COMPLETE; 1361 1362 switch (nt) { 1363 case GSSD_EXPORT: 1364 name_type = GSS_C_NT_EXPORT_NAME; 1365 break; 1366 case GSSD_ANONYMOUS: 1367 name_type = GSS_C_NT_ANONYMOUS; 1368 if (*mech == GSSD_SPNEGO_MECH) 1369 *mech = GSSD_NTLM_MECH; 1370 break; 1371 case GSSD_HOSTBASED: 1372 name_type = GSS_C_NT_HOSTBASED_SERVICE; 1373 break; 1374 case GSSD_USER: 1375 name_type = GSS_C_NT_USER_NAME; 1376 break; 1377 case GSSD_MACHINE_UID: 1378 name_type = GSS_C_NT_MACHINE_UID_NAME; 1379 break; 1380 case GSSD_STRING_UID: 1381 name_type = GSS_C_NT_STRING_UID_NAME; 1382 break; 1383 case GSSD_KRB5_PRINCIPAL: 1384 name_type = GSS_KRB5_NT_PRINCIPAL_NAME; 1385 *mech = GSSD_KRB5_MECH; 1386 break; 1387 case GSSD_UUID: 1388 name_type = GSS_C_NT_UUID; 1389 *mech = GSSD_IAKERB_MECH; 1390 break; 1391 case GSSD_KRB5_REFERRAL: 1392 name_type = GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL; 1393 *mech = GSSD_KRB5_MECH; 1394 break; 1395 case GSSD_NTLM_PRINCIPAL: 1396 name_type = GSS_C_NT_NTLM; 1397 *mech = GSSD_NTLM_MECH; 1398 break; 1399 case GSSD_NTLM_BLOB: 1400 default: 1401 return (GSS_S_BAD_NAMETYPE); 1402 } 1403 1404 maj = gss_import_name(min, &name_buf, name_type, gname); 1405 1406 if (maj != GSS_S_COMPLETE || get_debug_level() > 1) { 1407 char *ntstr = oid_name(name_type); 1408 Info("gss_import_name returned %#K; %#k for %.*s using %s name type", 1409 maj, mechtab[*mech], *min, size, name, ntstr); 1410 free(ntstr); 1411 } 1412 if (maj == GSS_S_COMPLETE && strrep) { 1413 uint32_t dmaj, dmin; 1414 gss_buffer_desc dbuf; 1415 gss_OID oid; 1416 1417 dmaj = gss_display_name(&dmin, *gname, &dbuf, &oid); 1418 DEBUG(3, "gss_display_name returned %#K", dmaj); 1419 *strrep = (dmaj == GSS_S_COMPLETE) ? buf_to_str(&dbuf) : strdup("unknown"); 1420 if (oidnt) 1421 *oidnt = oid_name(oid); 1422 } 1423 1424 return (maj); 1425} 1426 1427static uint32_t 1428blob_to_svcnames(uint32_t *min, gssd_nametype nt, gssd_byte_buffer svc_princ, uint32_t size, 1429 gssd_mechtype mech, gss_name_t *svcname, uint32_t *name_count) 1430{ 1431 *min = GSS_S_COMPLETE; 1432 1433 switch (nt) { 1434 case GSSD_STRING_NAME: 1435 return (str_to_svc_names(min, (char *)svc_princ, svcname, name_count)); 1436 default: 1437 *name_count = 1; 1438 return (blob_to_name(min, nt, svc_princ, size, &mech, NULL, NULL, svcname)); 1439 } 1440} 1441 1442static int 1443is_nfs_service(gss_name_t svcname) 1444{ 1445 uint32_t maj, min; 1446 gss_buffer_desc nbuf; 1447 gss_name_t canon; 1448 char *str = NULL; 1449 int is_nfs = 0; 1450 1451 maj = gss_canonicalize_name(&min, svcname, mechtab[GSSD_KRB5_MECH], &canon); 1452 if (maj != GSS_S_COMPLETE) 1453 return (0); 1454 1455 maj = gss_display_name(&min, canon, &nbuf, NULL); 1456 if (maj != GSS_S_COMPLETE) 1457 goto done; 1458 1459 str = buf_to_str(&nbuf); 1460 1461 DEBUG(3, "is_nfs_service principal is %s\n", str ? str : ""); 1462 1463 if (str) 1464 is_nfs = IS_NFS_SERVICE(str); 1465 1466done: 1467 gss_release_name(&min, &canon); 1468 free(str); 1469 1470 return (is_nfs); 1471} 1472 1473/* 1474 * Figure out who nobody is and how big a buffer we need to fetch password entries. 1475 * If we're logging at a debug level print out the default realm if we can. 1476 */ 1477static void 1478gssd_init(void) 1479{ 1480 struct passwd *pwent; 1481 struct group *grent; 1482 char hostbuf[MAXHOSTNAMELEN]; 1483 1484 /* Set up mech table */ 1485 mechtab[GSSD_KRB5_MECH] = GSS_KRB5_MECHANISM; 1486 mechtab[GSSD_SPNEGO_MECH] = GSS_SPNEGO_MECHANISM; 1487 mechtab[GSSD_NTLM_MECH] = GSS_NTLM_MECHANISM; 1488 mechtab[GSSD_IAKERB_MECH] = GSS_IAKERB_MECHANISM; 1489 1490 /* 1491 * Turn off home directory access during startup. 1492 * XXX Will need a more flexible policy to handle 1493 * apps that may want home dir access. 1494 */ 1495 krb5_set_home_dir_access(NULL, FALSE); 1496 1497 pwent = getpwnam("nobody"); 1498 NobodyUid = pwent ? pwent->pw_uid : NOBODY; 1499 grent = getgrnam("nobody"); 1500 NobodyGid = grent ? grent->gr_gid : NOBODY; 1501 1502 gethostname(hostbuf, MAXHOSTNAMELEN); 1503 local_host = canonicalize_host(hostbuf, NULL); 1504 if ( local_host == NULL) { 1505 Info("Could not canonicalize our host name in gssd_init\n"); 1506 local_host = strdup(lowercase(hostbuf)); 1507 } 1508 1509 /* Figure out how big a buffer we need for getting pwd entries */ 1510 GetPWMaxRSz = sysconf(_SC_GETPW_R_SIZE_MAX); 1511 GetPWMaxRSz = (GetPWMaxRSz == -1) ? 512 : GetPWMaxRSz; 1512 1513 DEBUG(2, "Starting with pid = %d\n\n\n", getpid()); 1514 if (get_debug_level()) { 1515 krb5_realm *realms = NULL; 1516 krb5_realm drealm = NULL; 1517 1518 if (get_local_realms(&realms)) 1519 drealm = *realms; 1520 Info("Kerberos default realm is %s for %s\n\n", 1521 drealm ? drealm : "No realm", local_host); 1522 free_local_realms(realms); 1523 } 1524} 1525 1526/* 1527 * Receive one message. Note that mach_msg_server_once will call 1528 * the appropriate dispatch routine, which in turn will call new_worker_thread() 1529 * and that will fire us up again to wait for the next message. 1530 */ 1531static void * 1532receive_message(void *arg __attribute__((unused))) 1533{ 1534 kern_return_t kr; 1535 1536 1537#ifdef VDEBUG 1538 DEBUG(3, "Enter receive_message %p with transaction count = %lu, " 1539 "standby count = %lu\n", pthread_self(), 1540 _vproc_transaction_count(), _vproc_standby_count()); 1541#endif 1542 pthread_setname_np("mach_msg_server thread"); 1543 kr = mach_msg_server_once(gssd_mach_server, MAX_GSSD_MSG_SIZE, 1544 gssd_receive_right, 1545 MACH_RCV_TRAILER_ELEMENTS(MACH_RCV_TRAILER_AUDIT) | 1546 MACH_RCV_TRAILER_TYPE(MACH_MSG_TRAILER_FORMAT_0)); 1547 1548 1549#ifdef VDEBUG 1550 DEBUG(3, "Leaving receive_message %p with transaction count = %lu, " 1551 "standby count = %lu\n", pthread_self(), 1552 _vproc_transaction_count(), _vproc_standby_count()); 1553#endif 1554 1555 if (kr != KERN_SUCCESS) { 1556 Info("mach_msg_server(mp): %s\n", mach_error_string(kr)); 1557 exit(1); 1558 } 1559 1560 return (NULL); 1561} 1562 1563 1564/* 1565 * Wait until we have fewer than the maximum number of worker threads, 1566 * and then create one running receive_message() thread. 1567 * 1568 * Called by the dispatch routines just before processing a message, 1569 * so we're listening for messages even while processing a message, 1570 * as long as we aren't out of threads. 1571 */ 1572#define MAXTHREADNAME 24 1573 1574static void 1575new_worker_thread(void) 1576{ 1577 pthread_t thread; 1578 char thread_name[MAXTHREADNAME]; 1579 int error; 1580 1581 (void) pthread_mutex_lock(numthreads_lock); 1582 1583 while (bye == 0 && numthreads >= maxthreads) { 1584 (void) pthread_cond_wait(numthreads_cv, numthreads_lock); 1585 } 1586 if (bye) 1587 goto out; 1588 numthreads++; 1589 error = pthread_create(&thread, attr, receive_message, NULL); 1590 if (error) { 1591 Info("unable to create worker thread: %s", strerror(error)); 1592 numthreads--; 1593 } 1594 1595out: 1596 1597 snprintf(thread_name, sizeof (thread_name), "worker thread %d", numthreads); 1598 thread_name[MAXTHREADNAME - 1] = '\0'; 1599 pthread_setname_np(thread_name); 1600 DEBUG(3, "Starting %s\n", thread_name); 1601 1602 (void) pthread_mutex_unlock(numthreads_lock); 1603} 1604 1605/* 1606 * This worker thread is terminating; reduce the count of worker threads, 1607 * and, if it's dropped below the maximum, wake up anybody waiting for 1608 * it to drop below the maximum. 1609 * 1610 * Called by the dispatch routines just before returning. 1611 */ 1612static void 1613end_worker_thread(void) 1614{ 1615 (void) pthread_mutex_lock(numthreads_lock); 1616 numthreads--; 1617 if (numthreads < maxthreads) 1618 pthread_cond_signal(numthreads_cv); 1619 1620 if (get_debug_level() > 2) { 1621 char thread_name[MAXTHREADNAME]; 1622 pthread_getname_np(pthread_self(), thread_name, sizeof thread_name); 1623 DEBUG(3, "Ending %s. Number of worker threads running is %d\n", thread_name, numthreads); 1624 } 1625 1626 (void) pthread_mutex_unlock(numthreads_lock); 1627} 1628 1629 1630/* 1631 * Thread that handles signals for us and will tell the timeout thread to 1632 * shut us down if we get a signal that we don't continue for. We set a global 1633 * variable bye and the timeout value to SHUTDOWN_TIMEOUT and wake every 1634 * body up. Threads block in new_worker_thread will see bye is set and exit. 1635 * We set timeout to SHUTDOWN_TIMEOUT for the timeout thread, so that threads 1636 * executing dispatch routines have an opportunity to finish. 1637 */ 1638 1639static void* 1640shutdown_thread(void *arg __attribute__((unused))) 1641{ 1642 int sig; 1643 int status; 1644 int remote_token; 1645 int master_token; 1646 sigset_t quitset[1]; 1647 char *notify_name = asl_remote_notify_name(); 1648 1649 pthread_setname_np("Signal thread"); 1650 1651 sigemptyset(quitset); 1652 sigaddset(quitset, SIGQUIT); 1653 1654 status = notify_register_signal(notify_name, SIGUSR2, &remote_token); 1655 if (status != NOTIFY_STATUS_OK) 1656 Log("Could not register for asl notifications: %s\n", asl_remote_notify_name()); 1657 status = notify_register_signal(NOTIFY_SYSTEM_MASTER, SIGUSR2, &master_token); 1658 if (status != NOTIFY_STATUS_OK) 1659 Log("Could not register for asl notifications: %s\n", NOTIFY_SYSTEM_MASTER); 1660 1661 /* 1662 * N.B. From the man page: "A true indication is returned the first time notify_check 1663 * is called for a token. Subsequent calls give a true indication when notifications have 1664 * been posted for the name associated with the notification token." 1665 * 1666 * So we do a notify_check here for the above tokens to get the true status when processing 1667 * SIGUSR2 below. 1668 */ 1669 (void)notify_check(remote_token, &status); 1670 (void)notify_check(master_token, &status); 1671 1672 do { 1673 int asl_notification = 0; 1674 int debug_level = get_debug_level(); 1675 1676 if (sigwait(waitset, &sig)) 1677 Log("sigwait failed %s", strerror(errno)); 1678 1679 DEBUG(2, "Received signal %d\n", sig); 1680 switch (sig) { 1681 case SIGQUIT: 1682 if (get_debug_level() > 1) 1683 die = 1; 1684 else { 1685 pthread_sigmask(SIG_UNBLOCK, quitset, NULL); 1686 raise(SIGQUIT); 1687 } 1688 break; 1689 case SIGUSR1: 1690 debug_level++; 1691 break; 1692 case SIGUSR2: 1693 status = notify_check(master_token, &asl_notification); 1694 if (status != NOTIFY_STATUS_OK) 1695 Log("Could not retreive notification for %s", NOTIFY_SYSTEM_MASTER); 1696 if (asl_notification == 0) { 1697 status = notify_check(remote_token, &asl_notification); 1698 if (status != NOTIFY_STATUS_OK ) 1699 Log("Could not retreive notification for %s", asl_remote_notify_name()); 1700 if (asl_notification == 0) { 1701 if (debug_level) 1702 debug_level--; 1703 } 1704 } 1705 break; 1706 case SIGHUP: 1707 debug_level = !debug_level; 1708 break; 1709 } 1710 if (asl_notification) { 1711 set_debug_level(-1); 1712 Info("Debug set to %d by syslog\n", get_debug_level()); 1713 } else { 1714 set_debug_level(debug_level); 1715 Info("Debug level set to %d", get_debug_level()); 1716 } 1717 } while (sigismember(contset, sig) || sig == 0); 1718 1719 pthread_mutex_lock(numthreads_lock); 1720 bye = 1; 1721 /* 1722 * Wait a little bit for dispatch threads to complete. 1723 */ 1724 timeout = SHUTDOWN_TIMEOUT; 1725 /* 1726 * Force the timeout_thread and all the rest to to wake up and exit. 1727 */ 1728 pthread_cond_broadcast(numthreads_cv); 1729 pthread_mutex_unlock(numthreads_lock); 1730 free(notify_name); 1731 1732 return (NULL); 1733} 1734 1735static void 1736compute_new_timeout(struct timespec *new) 1737{ 1738 struct timeval current; 1739 1740 gettimeofday(¤t, NULL); 1741 new->tv_sec = current.tv_sec + timeout; 1742 new->tv_nsec = 1000 * current.tv_usec; 1743} 1744 1745static int no_timeout; 1746 1747static void 1748disable_timeout(int disable) 1749{ 1750 pthread_mutex_lock(numthreads_lock); 1751 no_timeout = disable; 1752 pthread_mutex_unlock(numthreads_lock); 1753} 1754 1755static void* 1756timeout_thread(void *arg __attribute__((unused))) 1757{ 1758 int rv = 0; 1759 struct timespec exittime; 1760 1761 pthread_setname_np("Timeout thread"); 1762 (void) pthread_mutex_lock(numthreads_lock); 1763 1764 /* 1765 * Note that we have an extra thread running waiting for a mach message, 1766 * the first of which was started in main. Hence we have the test below for 1767 * greater than one instead of zero. 1768 */ 1769 while (bye ? (rv == 0 && numthreads > 1) : (rv == 0 || no_timeout || numthreads > 1)) { 1770 if (bye < 2) 1771 compute_new_timeout(&exittime); 1772 /* 1773 * If the shutdown thread has told us to exit (bye == 1), 1774 * then increment bye so that we will exit after at most 1775 * SHUTDOWN_TIMEOUT from the time we were signaled. When 1776 * we come back around the loop bye will be greater or 1777 * equal to two and we will not update our absolute exit time. 1778 */ 1779 if (bye) 1780 bye++; 1781 rv = pthread_cond_timedwait(numthreads_cv, 1782 numthreads_lock, &exittime); 1783 1784 DEBUG(4, "timeout_thread: rv = %s %d\n", 1785 rv ? strerror(rv) : "signaled", numthreads); 1786 } 1787 1788 (void) pthread_mutex_unlock(numthreads_lock); 1789 1790 1791 return (NULL); 1792} 1793 1794/* 1795 * vm_alloc_buffer: Copy the contents of the gss_buf_t to vm_allocated 1796 * memory at *value. The mig routines will automatically deallocate this 1797 * memory. 1798 */ 1799 1800static void 1801vm_alloc_buffer(gss_buffer_t buf, uint8_t **value, uint32_t *len) 1802{ 1803 kern_return_t kr; 1804 1805 *value = NULL; 1806 *len = 0; 1807 1808 if (buf->length == 0) 1809 return; 1810 kr = vm_allocate(mach_task_self(), 1811 (vm_address_t *)value, buf->length, VM_FLAGS_ANYWHERE); 1812 if (kr != KERN_SUCCESS) { 1813 Log("Could not allocate vm in vm_alloc_buffer\n"); 1814 return; 1815 } 1816 *len = (uint32_t) buf->length; 1817 memcpy(*value, buf->value, *len); 1818} 1819 1820/* 1821 * Extract the session key from a completed gss context. Currently the only 1822 * supported mechanism is kerberos and NTLM. Note the extracted key has been vm_allocated 1823 * and will be released by mig. (See gssd_mach.defs) 1824 * XXX this is extraordinarily yuckie. 1825 */ 1826 1827 1828static gss_OID kerb_mechs[] = { 1829 GSS_KRB5_MECHANISM, 1830 GSS_IAKERB_MECHANISM, 1831 GSS_PKU2U_MECHANISM, 1832 NULL 1833}; 1834 1835static bool 1836is_kerberos_key_mech(gss_const_OID mech) 1837{ 1838 gss_OID *p; 1839 1840 for (p = kerb_mechs; p; p++) { 1841 if (gss_oid_equal(mech, *p)) 1842 return (true); 1843 } 1844 1845 return (false); 1846} 1847 1848static uint32_t 1849GetSessionKey(uint32_t *minor, gss_OID mech, gss_ctx_id_t ctx, 1850 gssd_byte_buffer *skey, mach_msg_type_number_t *skeyCnt) 1851{ 1852 gss_krb5_lucid_context_v1_t *lucid_ctx = NULL; 1853 gss_krb5_lucid_key_t *key; 1854 void *some_lucid_ctx; 1855 uint32_t maj_stat, min_stat; 1856 uint32_t vers; 1857 gss_buffer_desc buf; 1858 1859 *skey = NULL; 1860 *skeyCnt = 0; 1861 *minor = 0; 1862 1863 if (gss_oid_equal(mech, GSS_NTLM_MECHANISM)) { 1864 gss_buffer_set_t keys; 1865 maj_stat = gss_inquire_sec_context_by_oid(minor, ctx, GSS_NTLM_GET_SESSION_KEY_X, &keys); 1866 if (maj_stat != GSS_S_COMPLETE) 1867 return (maj_stat); 1868 1869 if (keys->count) { 1870 if (keys->count > 1) 1871 Info("GetSessionKey received multiple keys. Using first key of %d keys\n", (uint32_t)keys->count); 1872 vm_alloc_buffer(&keys->elements[0], skey, skeyCnt); 1873 if (skey == NULL) { 1874 Log("Out of memory in GetSessionKey\n"); 1875 return (GSS_S_FAILURE); 1876 } 1877 } 1878 (void)gss_release_buffer_set(&min_stat, &keys); 1879 return (GSS_S_COMPLETE); 1880 1881 } else if (is_kerberos_key_mech(mech)) { 1882 DEBUG(4, "Calling gss_krb5_export_lucid_sec_context\n"); 1883 maj_stat = gss_krb5_export_lucid_sec_context(minor, &ctx, 1884 1, &some_lucid_ctx); 1885 DEBUG(3, "gss_krb5_export_lucid_sec_context returned %#K; %#k", maj_stat, mech, *minor); 1886 1887 if (maj_stat != GSS_S_COMPLETE) { 1888 return (maj_stat); 1889 } 1890 1891 vers = ((gss_krb5_lucid_context_version_t *)some_lucid_ctx)->version; 1892 switch (vers) { 1893 case 1: 1894 lucid_ctx = (gss_krb5_lucid_context_v1_t *)some_lucid_ctx; 1895 break; 1896 default: 1897 Log("Lucid version %d is unsupported\n", vers); 1898 (void) gss_krb5_free_lucid_sec_context(&min_stat, lucid_ctx); 1899 return (GSS_S_UNAVAILABLE); 1900 } 1901 1902 DEBUG(4, "vers = %d, protocol = %d\n", vers, lucid_ctx->protocol); 1903 1904 switch (lucid_ctx->protocol) { 1905 case 0: 1906 DEBUG(4, "Got rfc1964\n"); 1907 key = &lucid_ctx->rfc1964_kd.ctx_key; 1908 break; 1909 case 1: 1910 key = lucid_ctx->cfx_kd.have_acceptor_subkey ? 1911 &lucid_ctx->cfx_kd.acceptor_subkey : 1912 &lucid_ctx->cfx_kd.ctx_key; 1913 break; 1914 default: 1915 (void) gss_krb5_free_lucid_sec_context(&min_stat, lucid_ctx); 1916 return (GSS_S_CALL_BAD_STRUCTURE); /* should never happen. */ 1917 } 1918 1919 DEBUG(4, "lucid key type = %d\n", key->type); 1920 buf.length = key->length; 1921 buf.value = key->data; 1922 1923 vm_alloc_buffer(&buf, skey, skeyCnt); 1924 if (skey == NULL) { 1925 Log("Out of memory in GetSessionKey\n"); 1926 return (GSS_S_FAILURE); 1927 } 1928 1929 (void) gss_krb5_free_lucid_sec_context(&min_stat, lucid_ctx); 1930 return (GSS_S_COMPLETE); 1931 } 1932 1933 maj_stat = gss_oid_to_str(&min_stat, mech, &buf); 1934 if (maj_stat == GSS_S_COMPLETE) { 1935 char *oidstr = buf_to_str(&buf); 1936 Info("Unsupported mechanism for key extraction: %s\n", oidstr); 1937 free(oidstr); 1938 } else { 1939 Info("Unsupported mechanism for key extraction.\n"); 1940 } 1941 1942 return (GSS_S_COMPLETE); 1943} 1944 1945/* 1946 * If we get a call and the verifier does not match, clear out the args for 1947 * the client. 1948 */ 1949static uint32_t 1950badcall(char *rtn, uint32_t *minor_stat, 1951 gssd_ctx *gss_context, gssd_cred *cred_handle, uint32_t *gssd_flags, 1952 gssd_byte_buffer *skey, mach_msg_type_number_t *skeyCnt, 1953 gssd_byte_buffer *otoken, mach_msg_type_number_t *otokenCnt) 1954{ 1955 1956 if (!gssd_check(CAST(void *, *gss_context))) 1957 Info("Bad context found %p\n", (void *)(uintptr_t)*gss_context); 1958 if (!gssd_check(CAST(void *, *cred_handle))) 1959 Info("Bad cred handle found %p\n", (void *)(uintptr_t)*cred_handle); 1960 Log("%s request not addressed to us\n", rtn); 1961 *minor_stat = 0; 1962 *gss_context = CAST(gssd_ctx, GSS_C_NO_CONTEXT); 1963 *cred_handle = CAST(gssd_cred, GSS_C_NO_CREDENTIAL); 1964 *gssd_flags = 0; 1965 *skey = NULL; 1966 *skeyCnt = 0; 1967 *otoken = NULL; 1968 *otokenCnt = 0; 1969 1970 return (GSS_S_CALL_BAD_STRUCTURE); 1971} 1972 1973/* 1974 * Convert a gss_name_t to a krb5_principal 1975 */ 1976static uint32_t 1977gss_name_to_kprinc(uint32_t *minor, gss_name_t name, krb5_principal *princ, krb5_context kctx) 1978{ 1979 uint32_t major, m; 1980 gss_name_t kname = GSS_C_NO_NAME; 1981 gss_buffer_desc dname; 1982 char *strname = NULL; 1983 1984 *minor = 0; 1985 major = gss_canonicalize_name(minor, name, GSS_KRB5_MECHANISM, &kname); 1986 if (major != GSS_S_COMPLETE) 1987 return (major); 1988 1989 major = gss_display_name(minor, kname, &dname, NULL); 1990 (void) gss_release_name(&m, &kname); 1991 if (major != GSS_S_COMPLETE) 1992 return (major); 1993 1994 strname = buf_to_str(&dname); 1995 if (strname == NULL) { 1996 return (GSS_S_FAILURE); 1997 } 1998 1999 DEBUG(3, "parsing %s\n", strname); 2000 *minor = krb5_parse_name(kctx, strname, princ); 2001 2002 major = (uint32_t) (*minor ? GSS_S_FAILURE : GSS_S_COMPLETE); 2003 free(strname); 2004 2005 return (major); 2006} 2007 2008/* 2009 * krb5_find_cache_name(krb5_principal princ) 2010 * 2011 * Given a kerberos principal find the best cache name to use. 2012 */ 2013 2014#define KFCN_ALIVE 1 2015#define KFCN_EXPIRED 2 2016 2017static char* 2018krb5_find_cache_name(krb5_context kcontext, krb5_principal sprinc, int *flags) 2019{ 2020 krb5_error_code error, err; 2021 krb5_cc_cache_cursor cursor; 2022 krb5_ccache ccache; 2023 krb5_principal ccache_princ; 2024 char *cname = NULL; 2025 char *kname = NULL; 2026 time_t ltime; 2027 const char *msg = NULL; 2028 int cnt = 0; 2029 *flags = 0; 2030 2031 err = krb5_cc_cache_get_first(kcontext, NULL, &cursor); 2032 if (err) { 2033 msg = krb5_get_error_message(kcontext, err); 2034 Info("Could not get cache collection cursor %s\n", msg); 2035 krb5_free_error_message(kcontext, msg); 2036 return (NULL); 2037 } 2038 while (!(error = krb5_cc_cache_next(kcontext, cursor, &ccache))) { 2039 int isdead = 0; 2040 cnt += 1; 2041 err = krb5_cc_get_full_name(kcontext, ccache, &cname); 2042 if (err) { 2043 msg = krb5_get_error_message(kcontext, err); 2044 Info("krb5_cc_get_full_name error: %s\n", msg); 2045 krb5_free_error_message(kcontext, msg); 2046 krb5_cc_close(kcontext, ccache); 2047 if (cname) /* Shouldn't happen */ 2048 free(cname); 2049 cname = NULL; 2050 continue; 2051 } 2052 err = krb5_cc_get_principal(kcontext, ccache, &ccache_princ); 2053 if (err) { 2054 krb5_cc_close(kcontext, ccache); 2055 msg = krb5_get_error_message(kcontext, err); 2056 Info("krb5_cc_get_principal error: %s\n", msg); 2057 krb5_free_error_message(kcontext, msg); 2058 free(cname); 2059 cname = NULL; 2060 continue; 2061 } 2062 2063 err = krb5_cc_get_lifetime(kcontext, ccache, <ime); 2064 2065 if (ltime <= 0) { 2066 if (err && err != KRB5_CC_END) { 2067 msg = krb5_get_error_message(kcontext, err); 2068 Info("krb5_cc_get_lifetime error: %s\n", msg); 2069 krb5_free_error_message(kcontext, msg); 2070 } 2071 isdead = 1; 2072 } else { 2073 *flags |= KFCN_ALIVE; 2074 } 2075 2076 if (krb5_realm_compare(kcontext, sprinc, ccache_princ)) { 2077 (void) krb5_unparse_name(kcontext, ccache_princ, &kname); 2078 krb5_free_principal(kcontext, ccache_princ); 2079 Info("Found cache %d: %s for %s lifetime %ld\n", 2080 cnt, cname, kname ? kname : "could not get principal name", ltime); 2081 free(kname); 2082 2083 if (!isdead) { 2084 krb5_cc_close(kcontext, ccache); 2085 *flags &= ~KFCN_EXPIRED; 2086 break; 2087 } else { 2088 *flags |= KFCN_EXPIRED; 2089 } 2090 } else { 2091 (void) krb5_free_principal(kcontext, ccache_princ); 2092 } 2093 2094 krb5_cc_close(kcontext, ccache); 2095 free(cname); 2096 cname = NULL; 2097 } 2098 if (error != KRB5_CC_END) { 2099 msg = krb5_get_error_message(kcontext, error); 2100 Log("Could not iterate through cache collections: %s\n", msg); 2101 krb5_free_error_message(kcontext, msg); 2102 } 2103 (void) krb5_cc_cache_end_seq_get(kcontext, cursor); 2104 2105 return (cname); 2106} 2107 2108/* 2109 * set_principal_identity: 2110 * Given a service principal try and set the default identity so that 2111 * calls to gss_init_sec_context will work. 2112 * Currently this only groks kerberos. 2113 */ 2114static uint32_t 2115set_principal_identity(gss_name_t sname, uint32_t *minor) 2116{ 2117 krb5_principal sprinc; 2118 uint32_t major; 2119 char *cname; 2120 krb5_context kctx; 2121 int error, flags; 2122 2123 *minor = 0; 2124 error = krb5_init_context(&kctx); 2125 if (error) { 2126 Log("Can't get kerberos context"); 2127 return (GSS_S_FAILURE); 2128 } 2129 2130 major = gss_name_to_kprinc(minor, sname, &sprinc, kctx); 2131 if (major != GSS_S_COMPLETE) { 2132 krb5_free_context(kctx); 2133 DEBUG(2, "Could not convert gss name to kerberos principal %#K %#k\n", major, GSS_KRB5_MECHANISM, *minor); 2134 return (major); 2135 } 2136 2137 cname = krb5_find_cache_name(kctx, sprinc, &flags); 2138 krb5_free_principal(kctx, sprinc); 2139 krb5_free_context(kctx); 2140 Debug("Using ccache <%s> flags = %d\n", cname ? cname : "Default", flags); 2141 if (flags == KFCN_EXPIRED) 2142 return (GSS_S_CREDENTIALS_EXPIRED); 2143 if (cname) { 2144 major = gss_krb5_ccache_name(minor, cname, NULL); 2145 DEBUG(3, "gss_krb5_ccache_name returned %#K; %#k\n", major, GSS_KRB5_MECHANISM, minor); 2146 free(cname); 2147 } 2148 2149 return (GSS_S_COMPLETE); 2150} 2151 2152 2153static uint32_t 2154do_acquire_cred_v1(uint32_t *minor, char *principal, gssd_mechtype mech, gss_name_t sname, uint32_t uid, 2155 gssd_cred *cred_handle, uint32_t flags) 2156{ 2157 uint32_t major = GSS_S_FAILURE, mstat; 2158 gss_buffer_desc buf_name; 2159 gss_name_t clnt_gss_name; 2160 gss_OID_set mechset = GSS_C_NULL_OID_SET; 2161 gss_OID name_type = GSS_KRB5_NT_PRINCIPAL_NAME; 2162 2163 major = set_principal_identity(sname, minor); 2164 if (major) 2165 return (major); 2166 major = gss_create_empty_oid_set(minor, &mechset); 2167 if (major != GSS_S_COMPLETE) 2168 goto done; 2169 major = gss_add_oid_set_member(minor, mechtab[mech], &mechset); 2170 if (major != GSS_S_COMPLETE) 2171 goto done; 2172 2173 /* 2174 * If we've been passed a principal name then try that first with Kerberos. 2175 * Since using GSS_C_NT_USER_NAME might work, but throw away instance and realm 2176 * info. It seems easier just to try and not call gss_inquire_names_for_mech 2177 */ 2178 if (principal && *principal) { 2179 str_to_buf(principal, &buf_name); 2180 2181 Info("importing name %s with Kerberos\n", principal); 2182 2183 retry: 2184 major = gss_import_name(minor, &buf_name, name_type, &clnt_gss_name); 2185 if (major == GSS_S_COMPLETE) { 2186 char *nt_oid; 2187 major = gss_acquire_cred( 2188 minor, 2189 clnt_gss_name, 2190 GSS_C_INDEFINITE, 2191 mechset, 2192 GSS_C_INITIATE, 2193 (gss_cred_id_t *) cred_handle, 2194 NULL, NULL); 2195 nt_oid = oid_name(name_type); 2196 Info("gss_acuire_cred for %s using %s, returned: %K; %#k", principal, nt_oid, major, mechtab[mech], *minor); 2197 free(nt_oid); 2198 if (major == GSS_S_COMPLETE) { 2199 /* Done with the name */ 2200 (void) gss_release_name(&mstat, &clnt_gss_name); 2201 goto done; 2202 } 2203 } 2204 2205 /* 2206 * We could call gss_inquire_names_for_mech and try all supported name types 2207 * but it seems likely the only name type of interest would be GSS_C_NT_USER_NAME. 2208 */ 2209 if (name_type == GSS_KRB5_NT_PRINCIPAL_NAME) { 2210 name_type = GSS_C_NT_USER_NAME; 2211 goto retry; 2212 } 2213 } 2214 2215 if (!(flags & GSSD_NO_DEFAULT)) { 2216 /* Try default */ 2217 major = gss_acquire_cred( 2218 minor, 2219 GSS_C_NO_NAME, 2220 GSS_C_INDEFINITE, 2221 mechset, 2222 GSS_C_INITIATE, 2223 (gss_cred_id_t *) cred_handle, 2224 NULL, NULL); 2225 2226 if (major == GSS_S_COMPLETE) { 2227 Info("Using default credential %p\n", *(gss_cred_id_t *)cred_handle); 2228 goto done; 2229 } 2230 } 2231 2232 /* See if uid will work */ 2233 major = uid_to_gss_name(minor, (uid_t) uid, 2234 GSS_C_NT_USER_NAME, &clnt_gss_name); 2235 if (major != GSS_S_COMPLETE) 2236 return (major); 2237 2238 major = gss_acquire_cred( 2239 minor, 2240 clnt_gss_name, 2241 GSS_C_INDEFINITE, 2242 mechset, 2243 GSS_C_INITIATE, 2244 (gss_cred_id_t *) cred_handle, 2245 NULL, NULL); 2246 Info("Trying to aquire cred with uid %d. Returned %#K; %#k", uid, major, mechtab[mech], *minor); 2247 2248 /* Done with the name */ 2249 (void) gss_release_name(&mstat, &clnt_gss_name); 2250done: 2251 if (mechset != GSS_C_NULL_OID_SET) 2252 gss_release_oid_set(&mstat, &mechset); 2253 2254 return (major); 2255} 2256 2257static uint32_t 2258do_acquire_cred(uint32_t *minor_stat, gssd_nametype nt, gssd_byte_buffer name, uint32_t size, 2259 gssd_mechtype mech, gss_cred_id_t *handle) 2260{ 2261 uint32_t maj, min, nmaj; 2262 gss_OID_set mechset = GSS_C_NULL_OID_SET; 2263 gss_name_t gname = GSS_C_NO_NAME; 2264 char *mech_name= NULL; 2265 char *princ_name = NULL; 2266 char *oid_nt = NULL; 2267 2268 *minor_stat = GSS_S_COMPLETE; 2269 2270 if (handle == NULL) 2271 return (GSS_S_CALL_INACCESSIBLE_READ | GSS_S_CALL_INACCESSIBLE_WRITE); 2272 2273 maj = gss_create_empty_oid_set(minor_stat, &mechset); 2274 if (maj != GSS_S_COMPLETE) 2275 return (maj); 2276 2277 /* 2278 * Convert the name blob to a gss_name_t, giving back the string representation for 2279 * the name and the name type oid passed in. In addition if the name type was a 2280 * mech specific name type adjust the mech to reflect that. That mechanism will 2281 * then be added as the only member to the mech set below, and thus we will only 2282 * acquire credentials for that mech. This is important for SPNEGO, if we don't do 2283 * that, then SPNEGO may try mechanism we are not interested in. 2284 */ 2285 nmaj = blob_to_name(minor_stat, nt, name, size, &mech, &princ_name, &oid_nt, &gname); 2286 2287 maj = gss_add_oid_set_member(minor_stat, mechtab[mech], &mechset); 2288 if (maj != GSS_S_COMPLETE) 2289 goto done; 2290 2291 /* If we can't convert to a gss_name_t try the default with the possibly adjusted mech type */ 2292 if (nmaj != GSS_S_COMPLETE) 2293 goto do_default; 2294 2295 mech_name = oid_name(mechtab[mech]); 2296 Info("Acquiring credentials for %s with %s name type using %s mechanism", 2297 princ_name, oid_nt, mech_name ? mech_name : "Unknown"); 2298 free(mech_name); 2299 2300 maj = gss_acquire_cred(minor_stat, 2301 gname, 2302 GSS_C_INDEFINITE, 2303 mechset, 2304 GSS_C_INITIATE, 2305 handle, 2306 NULL, NULL); 2307 2308 (void)gss_release_name(&min, &gname); 2309 Info("Acquiring passed in credentials %K; %#k", maj, mechtab[mech], *minor_stat); 2310 if (maj == GSS_S_COMPLETE) 2311 goto done; 2312 2313do_default: 2314 if (!acquire_default) 2315 goto done; 2316 2317 /* Use the default in gss_init_sec_context */ 2318 maj = gss_acquire_cred( 2319 minor_stat, 2320 GSS_C_NO_NAME, 2321 GSS_C_INDEFINITE, 2322 mechset, 2323 GSS_C_INITIATE, 2324 handle, 2325 NULL, NULL); 2326 2327 if (maj == GSS_S_COMPLETE) { 2328 Info("Using default credential %p\n", (void *) *handle); 2329 } else { 2330 Info("Using null credential\n"); 2331 *handle = GSS_C_NO_CREDENTIAL; 2332 maj = GSS_S_COMPLETE; 2333 } 2334done: 2335 if (mechset != GSS_C_NULL_OID_SET) 2336 (void) gss_release_oid_set(&min, &mechset); 2337 if (gname != GSS_C_NO_NAME) 2338 (void) gss_release_name(&min, &gname); 2339 free(princ_name); 2340 free(oid_nt); 2341 2342 return (maj); 2343} 2344 2345/* 2346 * gssd_context type and routines to hold the underlying gss context as well 2347 * as the service name 2348 * 2349 * The reason we do this is on the initial call to gss_init_sec_context is that the 2350 * service name can generate up to two extra service names to try. 2351 * See str_to_svc_names above. Now we need to store the found name 2352 * where we can retrieve it on the next call if we return CONTINUE_NEEDED and 2353 * an easy way to do that is to construct our own context data structure to wrap 2354 * the real gss context and the service name used. 2355 * 2356 * You might be wondering why not just call str_to_svc_names again and not 2357 * worry about another level of context wrapping. Apart from the added work 2358 * of generating the candidate names and finding the "right" name again when we go 2359 * through the loop calling gss_init_sec_context, it won't work unless the first 2360 * name is the chosen name. When we pass in the address of the context to 2361 * gss_init_sec_context, on error gss will happily delete the context and set 2362 * our context now to be GSS_C_NO_CONTEXT. 2363 * 2364 * So let us say we generate 3 candidate service names and the second one will actually 2365 * work. The first time around gss_init_sec_context will fail and set our passed 2366 * in context to GSS_C_NO_CONTEXT and on the second call succeed, but 2367 * gss_init_sec_context will think this is an initial context (since the context 2368 * is NULL) and create a new one and return to the caller CONTINUE_NEEDED. Oops 2369 * we're in an infinite loop at this point, since the server will receive a valid 2370 * initial token and around we go. 2371 */ 2372typedef struct { 2373 gss_ctx_id_t gss_cntx; 2374 gss_name_t svc_name; 2375 vproc_transaction_t trans_handle; 2376} gssd_context, *gssd_context_t; 2377 2378static gssd_ctx 2379gssd_set_context(gss_ctx_id_t ctx, gss_name_t svc_name) 2380{ 2381 gssd_context_t g; 2382 2383 g = malloc(sizeof (gssd_context)); 2384 if (g == NULL) 2385 return (CAST(gssd_ctx, GSS_C_NO_CONTEXT)); 2386 gssd_enter(g); 2387 2388 g->gss_cntx = ctx; 2389 g->svc_name = svc_name; 2390 g->trans_handle = vproc_transaction_begin(NULL); 2391 2392 return (CAST(gssd_ctx, g)); 2393} 2394 2395static gss_ctx_id_t 2396gssd_get_context(gssd_ctx ctx, gss_name_t *svc_name) 2397{ 2398 gssd_context_t g; 2399 gss_ctx_id_t gss_context; 2400 2401 if (!ctx) { 2402 if (svc_name) 2403 *svc_name = GSS_C_NO_NAME; 2404 return (GSS_C_NO_CONTEXT); 2405 } 2406 g = CAST(gssd_context_t, ctx); 2407 if (svc_name) 2408 *svc_name = g->svc_name; 2409 gss_context = g->gss_cntx; 2410 vproc_transaction_end(NULL, g->trans_handle); 2411 gssd_remove(g); 2412 free(g); 2413 2414 return (gss_context); 2415} 2416 2417#define MAX_SVC_NAMES 3 2418 2419static uint32_t 2420svc_mach_gss_init_sec_context_common( 2421 gssd_mechtype mech, 2422 gssd_byte_buffer itoken, mach_msg_type_number_t itokenCnt, 2423 gss_name_t svcid, 2424 uint32_t flags, 2425 uint32_t *gssd_flags, 2426 gss_ctx_id_t *context, 2427 gss_cred_id_t cred_handle, 2428 uint32_t *ret_flags, 2429 gssd_byte_buffer *skey, mach_msg_type_number_t *skeyCnt, 2430 gssd_byte_buffer *otoken, mach_msg_type_number_t *otokenCnt, 2431 gssd_dstring displayname, 2432 uint32_t *minor_stat) 2433{ 2434 gss_buffer_desc intoken = {itokenCnt, itoken}; 2435 gss_buffer_desc outtoken = {0, NULL}; 2436 gss_buffer_desc name_buf; 2437 gss_name_t source; 2438 gss_OID mech_oid; 2439 uint32_t major_stat; 2440 uint32_t major, minor; 2441 uint32_t __unused in_gssd_flags = *gssd_flags; 2442 2443 DEBUG(2, "Using mech = %d\n", mech); 2444 DEBUG(3, "\tcred_handle = %p\n", cred_handle); 2445 DEBUG(3, "\tgss_context = %p\n", context); 2446 DEBUG(2, "itokenCnt = %d\n", itokenCnt); 2447 HEXDUMP(2, (char *)itoken, (itokenCnt > 80) ? 80 : itokenCnt); 2448 if (die) { 2449 DEBUG(2, "Forced server death\n"); 2450 _exit(0); 2451 } 2452 2453 *gssd_flags = 0; 2454 2455#ifdef WIN2K_HACK 2456 if ((in_gssd_flags & GSSD_WIN2K_HACK) && itokenCnt > 0) 2457 spnego_win2k_hack(&intoken); 2458#endif 2459 2460 major_stat = gss_init_sec_context( 2461 minor_stat, 2462 cred_handle, /* User's credential handle */ 2463 context, /* Context handle */ 2464 svcid, /* Target name */ 2465 mechtab[mech], /* Use the requested mech */ 2466 flags, /* Request flag bits */ 2467 0, /* Time requirement */ 2468 NULL, /* Channel bindings */ 2469 &intoken, /* Token from context acceptor */ 2470 &mech_oid, /* Actual mech types */ 2471 &outtoken, /* Token for the context acceptor */ 2472 ret_flags, /* Returned flag bits */ 2473 NULL); /* Time valid */ 2474 2475 vm_alloc_buffer(&outtoken, otoken, otokenCnt); 2476 gss_release_buffer(&minor, &outtoken); 2477 2478 if (major_stat == GSS_S_COMPLETE) { 2479 /* 2480 * If requeseted return a display representation to the caller. 2481 */ 2482 if (displayname) { 2483 major = gss_inquire_context(&minor, *context, &source, 2484 NULL, NULL, NULL, NULL, NULL, NULL); 2485 if (major == GSS_S_COMPLETE) { 2486 major = gss_display_name(&minor, source, &name_buf, NULL); 2487 if (major == GSS_S_COMPLETE) { 2488 char *s = buf_to_str(&name_buf); 2489 strlcpy(displayname, s, MAX_DISPLAY_STR); 2490 free(s); 2491 } 2492 gss_release_name(&minor, &source); 2493 } 2494 } 2495 2496 if (gss_oid_equal(mech_oid, GSS_NTLM_MECHANISM)) { 2497 gss_buffer_set_t data; 2498 2499 major = gss_inquire_sec_context_by_oid(&minor, *context, GSS_C_NTLM_GUEST, &data); 2500 if (major == GSS_S_COMPLETE) { 2501 uint32_t guest_flag = *(uint32_t *)data->elements->value; 2502 if (guest_flag) { 2503 *gssd_flags |= GSSD_GUEST_ONLY; 2504 DEBUG(3, "\tContext is NTLM simple file sharing %x\n", guest_flag); 2505 } else { 2506 DEBUG(3, "\tContext is NOT NTLM simple file sharing\n"); 2507 } 2508 (void) gss_release_buffer_set(&minor, &data); 2509 } else { 2510 Info("gss_inquire_sec_context_by_oid returned %K; %#k", major, mechtab[mech], minor); 2511 } 2512 } 2513 2514 /* 2515 * Fetch the (sub)session key from the context 2516 */ 2517 major_stat = GetSessionKey(minor_stat, mech_oid, *context, 2518 skey, skeyCnt); 2519 2520 DEBUG(2, "Client key: length = %d\n", *skeyCnt); 2521 HEXDUMP(2, (char *) *skey, *skeyCnt); 2522 } 2523 2524 2525 OSAtomicIncrement32(&initCnt); 2526 if (major_stat != GSS_S_CONTINUE_NEEDED && major_stat != GSS_S_COMPLETE) 2527 OSAtomicIncrement32(&initErr); 2528 2529 DEBUG(3, "cred = %p\n", cred_handle); 2530 DEBUG(3, "\tgss_context = %p\n", *context); 2531 DEBUG(2, "%sotokenCnt = %d\n", get_debug_level() > 2 ? "\t" : "", *otokenCnt); 2532 HEXDUMP(2, (char *)*otoken, (*otokenCnt > 80) ? 80 : *otokenCnt); 2533 DEBUG(3, "Returning from init %d errors out of a total %d calls\n", initErr, initCnt); 2534 2535 2536 2537 return (major_stat); 2538} 2539 2540/* 2541 * Mig dispatch routine for gss_init_sec_context. 2542 */ 2543kern_return_t 2544svc_mach_gss_init_sec_context( 2545 mach_port_t server, 2546 gssd_mechtype mech, 2547 gssd_byte_buffer itoken, mach_msg_type_number_t itokenCnt, 2548 uint32_t uid, 2549 gssd_string princ_namestr, 2550 gssd_string svc_namestr, 2551 uint32_t flags, 2552 uint32_t gssd_flags, 2553 gssd_ctx *gss_context, 2554 gssd_cred *cred_handle, 2555 audit_token_t atok, 2556 uint32_t *ret_flags, 2557 gssd_byte_buffer *skey, mach_msg_type_number_t *skeyCnt, 2558 gssd_byte_buffer *otoken, mach_msg_type_number_t *otokenCnt, 2559 uint32_t *major_stat, 2560 uint32_t *minor_stat) 2561{ 2562 kern_return_t kstat; 2563 2564 kstat = svc_mach_gss_init_sec_context_v2(server, 2565 mech, 2566 itoken, 2567 itokenCnt, 2568 uid, 2569 GSSD_STRING_NAME, 2570 (gssd_byte_buffer) princ_namestr, 2571 (uint32_t) strlen(princ_namestr) + 1, 2572 GSSD_STRING_NAME, 2573 (gssd_byte_buffer) svc_namestr, 2574 (uint32_t) strlen(svc_namestr) + 1, 2575 flags, 2576 &gssd_flags, 2577 gss_context, 2578 cred_handle, 2579 atok, 2580 ret_flags, 2581 skey, 2582 skeyCnt, 2583 otoken, 2584 otokenCnt, 2585 NULL, 2586 major_stat, 2587 minor_stat); 2588 return (kstat); 2589} 2590 2591kern_return_t 2592svc_mach_gss_init_sec_context_v2( 2593 mach_port_t server __attribute__((unused)), 2594 gssd_mechtype mech, 2595 gssd_byte_buffer itoken, 2596 mach_msg_type_number_t itokenCnt, 2597 uint32_t uid, 2598 gssd_nametype clnt_nt, 2599 gssd_byte_buffer clnt_princ, 2600 mach_msg_type_number_t clnt_princCnt, 2601 gssd_nametype svc_nt, 2602 gssd_byte_buffer svc_princ, 2603 mach_msg_type_number_t svc_princCnt, 2604 uint32_t flags, 2605 uint32_t *gssd_flags, 2606 gssd_ctx *gss_context, 2607 gssd_cred *cred_handle, 2608 audit_token_t atok, 2609 uint32_t *ret_flags, 2610 gssd_byte_buffer *skey, 2611 mach_msg_type_number_t *skeyCnt, 2612 gssd_byte_buffer *otoken, 2613 mach_msg_type_number_t *otokenCnt, 2614 gssd_dstring displayname, 2615 uint32_t *major_stat, 2616 uint32_t *minor_stat) 2617{ 2618 gss_name_t svc_gss_name[MAX_SVC_NAMES]; 2619 gss_ctx_id_t g_cntx = GSS_C_NO_CONTEXT; 2620 uint32_t i, gnames = MAX_SVC_NAMES, name_index = MAX_SVC_NAMES; 2621 uint32_t mstat; /* Minor status for cleaning up. */ 2622 vproc_transaction_t gssd_vproc_handle; 2623 uint32_t only_1des = ((*gssd_flags & GSSD_NFS_1DES) != 0); 2624 kern_return_t kr = KERN_SUCCESS; 2625 2626 DEBUG(2, "Enter"); 2627 2628 gssd_vproc_handle = vproc_transaction_begin(NULL); 2629 new_worker_thread(); 2630 2631 if (!check_audit(atok, FALSE)) { 2632 kr = KERN_NO_ACCESS; 2633 goto out; 2634 } 2635 2636 krb5_set_home_dir_access(NULL, (*gssd_flags & GSSD_HOME_ACCESS_OK) ? 1 : 0); 2637 2638 if (displayname) 2639 *displayname = '\0'; 2640 2641 if (!gssd_check(CAST(void *, *gss_context)) || !gssd_check(CAST(void *, *cred_handle))) { 2642 *major_stat = badcall("svc_mach_gss_init_context", 2643 minor_stat, gss_context, cred_handle, 2644 gssd_flags, 2645 skey, skeyCnt, 2646 otoken, otokenCnt); 2647 2648 kr = KERN_SUCCESS; 2649 goto out; 2650 } 2651 2652 /* 2653 * Below currently doesn't do anything since the mach defs file has 2654 * the major_stat as an out parameter, so *major_stat is always going 2655 * to be 0 (GSS_S_COMPLETE). If we ever rev the protocol we should change 2656 * that. It's not so bad since gss_init_sec_context will note that the 2657 * context is invalid and we will destroy the context on returning. 2658 */ 2659 if (*major_stat != GSS_S_CONTINUE_NEEDED && *major_stat != GSS_S_COMPLETE) { 2660 kr = KERN_SUCCESS; 2661 g_cntx = gssd_get_context(*gss_context, svc_gss_name); 2662 goto done; 2663 } 2664 2665 if (*gss_context == CAST(gssd_ctx, GSS_C_NO_CONTEXT)) { 2666 2667 if (no_canon || (*gssd_flags & GSSD_NO_CANON)) 2668 gnames = 1; 2669 *major_stat = blob_to_svcnames(minor_stat, svc_nt, svc_princ, svc_princCnt, 2670 mech, svc_gss_name, &gnames); 2671 2672 if (*major_stat != GSS_S_COMPLETE) { 2673 Info("Could not determine service principal name: %#K", *major_stat); 2674 goto done; 2675 } 2676 2677 if (gnames > 1) 2678 Info("Trying the following server principal names:"); 2679 for (i = 0; i < gnames; i++) { 2680 char *dname; 2681 gss_buffer_desc bufname; 2682 uint32_t maj, min; 2683 gss_OID oid; 2684 char *oname; 2685 2686 maj = gss_display_name(&min, svc_gss_name[i], &bufname, &oid); 2687 if (maj != GSS_S_COMPLETE) 2688 Info("Cannot determine target name: %K", maj); 2689 else { 2690 dname = buf_to_str(&bufname); 2691 oname = oid_name(oid); 2692 Info("%s %s as %s", (gnames > 1)? "\t" : "Server principal name", dname, oname); 2693 free(dname); 2694 free(oname); 2695 } 2696 } 2697 2698 } 2699 else { 2700 gnames = 1; 2701 g_cntx = gssd_get_context(*gss_context, svc_gss_name); 2702 if ((*gssd_flags & GSSD_RESTART) && g_cntx != GSS_C_NO_CONTEXT) 2703 (void) gss_delete_sec_context(&mstat, &g_cntx, GSS_C_NO_BUFFER); 2704 } 2705 if (*cred_handle && (*gssd_flags & GSSD_RESTART)) { 2706 gssd_remove(CAST(void *, *cred_handle)); 2707 (void) gss_release_cred(&mstat, (gss_cred_id_t *) cred_handle); 2708 } 2709 if (CAST(gss_cred_id_t, *cred_handle) == GSS_C_NO_CREDENTIAL || (*gssd_flags & GSSD_RESTART)) { 2710 if (clnt_nt == GSSD_STRING_NAME) 2711 *major_stat = do_acquire_cred_v1(minor_stat, (char *)clnt_princ, mech, 2712 *svc_gss_name, uid, cred_handle, *gssd_flags); 2713 else { 2714 *major_stat = do_acquire_cred(minor_stat, clnt_nt, 2715 clnt_princ, clnt_princCnt, 2716 mech, (gss_cred_id_t *) cred_handle); 2717 } 2718 if (*major_stat != GSS_S_COMPLETE) 2719 goto done; 2720 /* ??? 2721 * Currently NFS only supports a subset of the Kerberos enctypes 2722 * and only suports the kerberos mech. If using a non kerberos 2723 * credential, gss_krb5_set_allowable_enctypes will fail. 2724 */ 2725 if (is_nfs_service(*svc_gss_name)) { 2726 *major_stat = gss_krb5_set_allowable_enctypes 2727 (minor_stat, *(gss_cred_id_t *)cred_handle, 2728 NUM_NFS_ENCTYPES - only_1des, NFS_ENCTYPES); 2729 if (*major_stat != GSS_S_COMPLETE) { 2730 Log("Could not set enctypes for NFS\n"); 2731 goto done; 2732 } 2733 } 2734 gssd_enter(CAST(void *, *cred_handle)); 2735 } 2736 2737 *major_stat = GSS_S_BAD_NAME; 2738 for (i = 0; i < gnames; i++) { 2739 2740 *major_stat = svc_mach_gss_init_sec_context_common( 2741 mech, 2742 itoken, 2743 itokenCnt, 2744 svc_gss_name[i], 2745 flags, 2746 gssd_flags, 2747 &g_cntx, 2748 CAST(gss_cred_id_t, *cred_handle), 2749 ret_flags, 2750 skey, 2751 skeyCnt, 2752 otoken, 2753 otokenCnt, 2754 displayname, 2755 minor_stat); 2756 2757 if (*major_stat == GSS_S_COMPLETE || 2758 *major_stat == GSS_S_CONTINUE_NEEDED) 2759 break; 2760 } 2761 name_index = i; 2762 2763 /* Done with the names */ 2764 for (i = 0; i < gnames; i++) 2765 if (i != name_index) 2766 (void)gss_release_name(&mstat, &svc_gss_name[i]); 2767 2768 if (*major_stat == GSS_S_CONTINUE_NEEDED) { 2769 *gss_context = gssd_set_context(g_cntx, svc_gss_name[name_index]); 2770 if (*gss_context == 0) 2771 *major_stat = GSS_S_FAILURE; 2772 } 2773 2774done: 2775 Info("svc_mach_gss_init_sec_context_common %K; %#k", *major_stat, mechtab[mech], *minor_stat); 2776 if (*major_stat != GSS_S_CONTINUE_NEEDED) { 2777 /* We're done so free what we allocated */ 2778 gssd_remove(CAST(void *, *cred_handle)); 2779 (void) gss_release_cred(&mstat, (gss_cred_id_t *) cred_handle); 2780 if (g_cntx != GSS_C_NO_CONTEXT) 2781 (void) gss_delete_sec_context(&mstat, &g_cntx, GSS_C_NO_BUFFER); 2782 2783 if (name_index < gnames) 2784 (void)gss_release_name(&mstat, &svc_gss_name[name_index]); 2785 } 2786 2787out: 2788 end_worker_thread(); 2789 vproc_transaction_end(NULL, gssd_vproc_handle); 2790 2791 DEBUG(2, "Exit"); 2792 2793 return (kr); 2794 2795} 2796 2797/* 2798 * Mig dispatch routine for gss_accept_sec_context. 2799 */ 2800kern_return_t 2801svc_mach_gss_accept_sec_context( 2802 mach_port_t test_port, 2803 gssd_byte_buffer itoken, mach_msg_type_number_t itokenCnt, 2804 gssd_string svc_namestr, 2805 uint32_t gssd_flags, 2806 gssd_ctx *gss_context, 2807 gssd_cred *cred_handle, 2808 audit_token_t atok, 2809 uint32_t *ret_flags, 2810 uint32_t *uid, 2811 gssd_gid_list gids, mach_msg_type_number_t *gidsCnt, 2812 gssd_byte_buffer *skey, mach_msg_type_number_t *skeyCnt, 2813 gssd_byte_buffer *otoken, mach_msg_type_number_t *otokenCnt, 2814 uint32_t *major_stat, 2815 uint32_t *minor_stat) 2816{ 2817 kern_return_t kr; 2818 2819 kr = svc_mach_gss_accept_sec_context_v2(test_port, 2820 itoken, 2821 itokenCnt, 2822 GSSD_STRING_NAME, 2823 (gssd_byte_buffer)svc_namestr, 2824 (uint32_t) strlen(svc_namestr) + 1, 2825 &gssd_flags, 2826 gss_context, 2827 cred_handle, 2828 atok, 2829 ret_flags, 2830 uid, 2831 gids, 2832 gidsCnt, 2833 skey, 2834 skeyCnt, 2835 otoken, 2836 otokenCnt, 2837 major_stat, 2838 minor_stat); 2839 return (kr); 2840} 2841 2842kern_return_t 2843svc_mach_gss_accept_sec_context_v2( 2844 mach_port_t server __attribute__((unused)), 2845 gssd_byte_buffer itoken, 2846 mach_msg_type_number_t itokenCnt, 2847 gssd_nametype svc_nt __attribute__((unused)), 2848 gssd_byte_buffer svc_princ __attribute__((unused)), 2849 mach_msg_type_number_t svc_princCnt __attribute__((unused)), 2850 uint32_t *inout_gssd_flags __attribute__((unused)), 2851 gssd_ctx *gss_context, 2852 gssd_cred *cred_handle, 2853 audit_token_t atok, 2854 uint32_t *ret_flags, 2855 uint32_t *uid, 2856 gssd_gid_list gids, 2857 mach_msg_type_number_t *gidsCnt, 2858 gssd_byte_buffer *skey, 2859 mach_msg_type_number_t *skeyCnt, 2860 gssd_byte_buffer *otoken, 2861 mach_msg_type_number_t *otokenCnt, 2862 uint32_t *major_stat, 2863 uint32_t *minor_stat) 2864{ 2865 gss_ctx_id_t g_cntx = GSS_C_NO_CONTEXT; 2866 gss_name_t princ; 2867 gss_OID oid; 2868 uint32_t mstat; /* Minor status to clean up with. */ 2869 kern_return_t kr = KERN_SUCCESS; 2870 vproc_transaction_t gssd_vproc_handle; 2871 2872 DEBUG(2, "Enter"); 2873 gssd_vproc_handle = vproc_transaction_begin(NULL); 2874 new_worker_thread(); 2875 2876 if (!check_audit(atok, FALSE)) { 2877 kr = KERN_NO_ACCESS; 2878 goto out; 2879 } 2880 2881 krb5_set_home_dir_access(NULL, ((*inout_gssd_flags) & GSSD_HOME_ACCESS_OK) ? 1 : 0); 2882 2883 *inout_gssd_flags = 0; 2884 2885 /* Set the uid to nobody to be safe */ 2886 *uid = NobodyUid; 2887 2888 if (die) { 2889 DEBUG(2, "Forced server death\n"); 2890 _exit(0); 2891 } 2892 2893 if (!gssd_check(CAST(void *, *gss_context)) || !gssd_check(CAST(void *, *cred_handle))) { 2894 *major_stat = badcall("svc_mach_gss_accept_sec_context", 2895 minor_stat, gss_context, cred_handle, 2896 inout_gssd_flags, 2897 skey, skeyCnt, otoken, otokenCnt); 2898 2899 end_worker_thread(); 2900 vproc_transaction_end(NULL, gssd_vproc_handle); 2901 2902 return (KERN_SUCCESS); 2903 } 2904 2905 g_cntx = gssd_get_context(*gss_context, NULL); 2906 gss_buffer_desc intoken = {itokenCnt, itoken}; 2907 gss_buffer_desc outtoken = {0, NULL};; 2908 *major_stat = 0; 2909 *minor_stat = 0; 2910 2911 DEBUG(4, "minor_stat = %d\n", (int) *minor_stat); 2912 DEBUG(4, "\tcred = %p\n", (void *)(uintptr_t)*cred_handle); 2913 DEBUG(4, "\tgss_context = %p\n", g_cntx); 2914 DEBUG(3, "itokenCnt = %d\n", itokenCnt); 2915 HEXDUMP(3, (char *)itoken, (itokenCnt > 80) ? 80 : itokenCnt); 2916 2917 *major_stat = gss_accept_sec_context( 2918 minor_stat, 2919 &g_cntx, // Context handle 2920 CAST(gss_cred_id_t, *cred_handle), // Acceptor's credential handle 2921 &intoken, // Token from context initiator 2922 GSS_C_NO_CHANNEL_BINDINGS, // Channel bindings 2923 &princ, // Context initiator's name 2924 &oid, // Mech types 2925 &outtoken, // Token for context initiator 2926 ret_flags, // Flags out 2927 NULL, // Time requirement 2928 NULL); // Delegated creds 2929 2930 vm_alloc_buffer(&outtoken, otoken, otokenCnt); 2931 gss_release_buffer(&mstat, &outtoken); 2932 2933 if (*major_stat == GSS_S_COMPLETE ) { 2934 /* 2935 * Turn the principal name into UNIX creds 2936 */ 2937 *major_stat = gss_name_to_ucred(minor_stat, princ, 2938 uid, gids, gidsCnt); 2939 if (*major_stat != GSS_S_COMPLETE) { 2940 kr = KERN_FAILURE; 2941 goto done; 2942 } 2943 /* 2944 * Fetch the (sub)session key from the context 2945 */ 2946 *major_stat = GetSessionKey(minor_stat, oid, g_cntx, 2947 skey, skeyCnt); 2948 2949 DEBUG(2, "Server key length = %d\n", *skeyCnt); 2950 HEXDUMP(2, (char *) *skey, *skeyCnt); 2951 } else if (*major_stat == GSS_S_CONTINUE_NEEDED) { 2952 *gss_context = gssd_set_context(g_cntx, NULL); 2953 if (*gss_context == 0) 2954 *major_stat = GSS_S_FAILURE; 2955 2956 /* 2957 * Register our context handle 2958 */ 2959 gssd_enter(CAST(void *, *gss_context)); 2960 } 2961 if (*major_stat == GSS_S_COMPLETE || *major_stat == GSS_S_CONTINUE_NEEDED) { 2962 DEBUG(3, "otokenCnt = %d", *otokenCnt); 2963 HEXDUMP(3, (char *)*otoken, (*otokenCnt > 80) ? 80 : *otokenCnt); 2964 } 2965done: 2966 gss_release_name(&mstat, &princ); 2967 if (*major_stat != GSS_S_CONTINUE_NEEDED) { 2968 gssd_remove(CAST(void *, *cred_handle)); 2969 (void)gss_release_cred(&mstat, (gss_cred_id_t *) cred_handle); 2970 if (g_cntx != GSS_C_NO_CONTEXT) 2971 (void) gss_delete_sec_context(&mstat, &g_cntx, GSS_C_NO_BUFFER); 2972 } 2973 2974 OSAtomicIncrement32(&acceptCnt); 2975 if (*major_stat != GSS_S_CONTINUE_NEEDED && *major_stat != GSS_S_COMPLETE) 2976 OSAtomicIncrement32(&acceptErr); 2977 DEBUG(3, "Returning from accept %d erros of of %d total calls\n", acceptErr, acceptCnt); 2978 2979 Info("gss_accept_sec_context %K; %#k", *major_stat, oid, *minor_stat); 2980out: 2981 end_worker_thread(); 2982 vproc_transaction_end(NULL, gssd_vproc_handle); 2983 2984 DEBUG(2, "Exit"); 2985 2986 return (kr); 2987} 2988 2989 2990#define MSG(f, ...) do {\ 2991 if (f) { \ 2992 Debug(__VA_ARGS__); \ 2993 } else { \ 2994 Log(__VA_ARGS__); \ 2995 } \ 2996} while (0) 2997 2998 2999 3000/* 3001 * Mig dispatch routine to log GSS-API errors 3002 */ 3003kern_return_t 3004svc_mach_gss_log_error( 3005 mach_port_t test_port __attribute__((unused)), 3006 gssd_string mnt, 3007 uint32_t uid, 3008 gssd_string source, 3009 uint32_t major, 3010 uint32_t minor, 3011 audit_token_t atok) 3012{ 3013 OM_uint32 msg_context = 0; 3014 OM_uint32 min_stat = 0; 3015 OM_uint32 maj_stat = 0; 3016 gss_buffer_desc errBuf; 3017 char msgbuf[1024]; 3018 char *errStr; 3019 int full = 0; 3020 vproc_transaction_t gssd_vproc_handle; 3021 kern_return_t kr = KERN_SUCCESS; 3022 3023 DEBUG(2, "Enter"); 3024 gssd_vproc_handle = vproc_transaction_begin(NULL); 3025 new_worker_thread(); 3026 3027 if (!check_audit(atok, FALSE)) { 3028 kr = KERN_NO_ACCESS; 3029 goto out; 3030 } 3031 3032 (void) snprintf(msgbuf, sizeof(msgbuf), "nfs %s Kerberos: %s, uid=%d", 3033 source, mnt, uid); 3034 3035 /* 3036 * Start with the major error string(s) 3037 * The strings are concatenated into a fixed size log 3038 * message buffer. If the messages exceed the buffer 3039 * size then we truncate. 3040 */ 3041 do { 3042 if (major == GSS_S_FAILURE) // more info in minor msg 3043 break; 3044 maj_stat = gss_display_status(&min_stat, major, GSS_C_GSS_CODE, 3045 GSS_C_NULL_OID, &msg_context, &errBuf); 3046 errStr = buf_to_str(&errBuf); 3047 if (maj_stat != GSS_S_COMPLETE) 3048 goto done; 3049 full = strlcat(msgbuf, " - ", sizeof(msgbuf)) >= sizeof(msgbuf) || 3050 strlcat(msgbuf, errStr, sizeof(msgbuf)) >= sizeof(msgbuf); 3051 free(errStr); 3052 if (full) 3053 goto done; 3054 } while (msg_context != 0); 3055 3056 /* 3057 * Append any minor error string(s) 3058 */ 3059 msg_context = 0; 3060 do { 3061 maj_stat = gss_display_status (&min_stat, minor, GSS_C_MECH_CODE, 3062 GSS_C_NULL_OID, &msg_context, &errBuf); 3063 errStr = buf_to_str(&errBuf); 3064 if (maj_stat != GSS_S_COMPLETE) 3065 goto done; 3066 full = strlcat(msgbuf, " - ", sizeof(msgbuf)) >= sizeof(msgbuf) || 3067 strlcat(msgbuf, errStr, sizeof(msgbuf)) >= sizeof(msgbuf); 3068 free(errStr); 3069 if (full) 3070 goto done; 3071 } while (msg_context != 0); 3072 3073done: 3074 MSG((major == GSS_S_NO_CRED), "%s", msgbuf); 3075 3076out: 3077 end_worker_thread(); 3078 vproc_transaction_end(NULL, gssd_vproc_handle); 3079 3080 return (kr); 3081} 3082 3083kern_return_t 3084svc_mach_gss_hold_cred(mach_port_t server __unused, 3085 gssd_mechtype mech, 3086 gssd_nametype nt, 3087 gssd_byte_buffer princ, 3088 mach_msg_type_number_t princCnt, 3089 audit_token_t atok, 3090 uint32_t *major_stat, 3091 uint32_t *minor_stat) 3092{ 3093 gss_cred_id_t cred = NULL; 3094 uint32_t m; 3095 vproc_transaction_t gssd_vproc_handle; 3096 kern_return_t kr = KERN_SUCCESS; 3097 3098 DEBUG(2, "Enter"); 3099 gssd_vproc_handle = vproc_transaction_begin(NULL); 3100 new_worker_thread(); 3101 3102 if (!check_audit(atok, FALSE)) { 3103 kr = KERN_NO_ACCESS; 3104 goto out; 3105 } 3106 3107 *major_stat = do_acquire_cred(minor_stat, nt, princ, princCnt, mech, &cred); 3108 if (*major_stat != GSS_S_COMPLETE) 3109 goto out; 3110 *major_stat = gss_cred_hold(minor_stat, cred); 3111 (void) gss_release_cred(&m, &cred); 3112 3113out: 3114 end_worker_thread(); 3115 vproc_transaction_end(NULL, gssd_vproc_handle); 3116 3117 return (kr); 3118} 3119 3120kern_return_t 3121svc_mach_gss_unhold_cred(mach_port_t server __unused, 3122 gssd_mechtype mech, 3123 gssd_nametype nt, 3124 gssd_byte_buffer princ, 3125 mach_msg_type_number_t princCnt, 3126 audit_token_t atok, 3127 uint32_t *major_stat, 3128 uint32_t *minor_stat) 3129{ 3130 gss_cred_id_t cred = NULL; 3131 uint32_t m; 3132 vproc_transaction_t gssd_vproc_handle; 3133 kern_return_t kr = KERN_SUCCESS; 3134 3135 DEBUG(2, "Enter"); 3136 gssd_vproc_handle = vproc_transaction_begin(NULL); 3137 new_worker_thread(); 3138 3139 if (!check_audit(atok, FALSE)) { 3140 kr = KERN_NO_ACCESS; 3141 goto out; 3142 } 3143 3144 *major_stat = do_acquire_cred(minor_stat, nt, princ, princCnt, mech, &cred); 3145 if (*major_stat != GSS_S_COMPLETE) 3146 goto out; 3147 *major_stat = gss_cred_unhold(minor_stat, cred); 3148 (void) gss_release_cred(&m, &cred); 3149 3150out: 3151 end_worker_thread(); 3152 vproc_transaction_end(NULL, gssd_vproc_handle); 3153 return (kr); 3154} 3155 3156kern_return_t 3157svc_mach_gss_lookup(mach_port_t server, 3158 uint32_t uid, 3159 int32_t asid, 3160 audit_token_t atok, 3161 mach_port_t *gssd_port) 3162{ 3163 kern_return_t kr = KERN_SUCCESS; 3164 uuid_t uuid; 3165 uuid_string_t uuidstr; 3166 vproc_transaction_t gssd_vproc_handle; 3167 3168 DEBUG(2, "Enter"); 3169 gssd_vproc_handle = vproc_transaction_begin(NULL); 3170 new_worker_thread(); 3171 3172 if (!check_audit(atok, kernel_only)) { 3173 kr = KERN_NO_ACCESS; 3174 goto out; 3175 } 3176 3177 *gssd_port = MACH_PORT_NULL; 3178 if (!check_session(asid)) { 3179 *gssd_port = server; 3180 } else { 3181 sessioninfo2uuid((uid_t)uid, (au_asid_t)asid, uuid); 3182 uuid_unparse(uuid, uuidstr); 3183 DEBUG(2, "Looking up %s for %d %d as instance %s", bname, uid, asid, uuidstr); 3184 3185 kr = bootstrap_look_up3(bootstrap_port, bname, gssd_port, 0, uuid, BOOTSTRAP_SPECIFIC_INSTANCE); 3186 if (kr != KERN_SUCCESS) 3187 Log("Could not lookup per instance port %d: %s", kr, bootstrap_strerror(kr)); 3188 3189 DEBUG(2, "bootstap_look_up3 = %d port = %d, server port = %d", kr, *gssd_port, server); 3190 } 3191out: 3192 end_worker_thread(); 3193 vproc_transaction_end(NULL, gssd_vproc_handle); 3194 return (kr); 3195} 3196