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#include <stdio.h> 24#include <string.h> 25#include <stdlib.h> 26#include <errno.h> 27#include <search.h> 28#include <stdint.h> 29#include <pthread.h> 30#include <stdarg.h> 31#include <regex.h> 32#include <asl.h> 33#include <asl_private.h> 34#include <unistd.h> 35#include <sys/sysctl.h> 36#include <GSS/gssapi.h> 37#include <GSS/gssapi_krb5.h> 38#include <GSS/gssapi_ntlm.h> 39#include <GSS/gssapi_spnego.h> 40#include <GSS/gssapi_netlogon.h> 41#include "gssd.h" 42 43/* 44 * Limit the number of contexts that we can have. 45 * If a kernel thread gets a context from us with 46 * CONTINUE NEEDED, but never finish the context we 47 * and then loops before we idle out we can end up 48 * consuming a large amount of memory. 49 */ 50#define MAX_GSS_CONTEXTS 100 51 52static void *rootp = (void *)0; 53static pthread_mutex_t smutex; 54static pthread_once_t sonce = PTHREAD_ONCE_INIT; 55int ctx_counter = 0; 56 57 58static void 59init(void) 60{ 61 pthread_mutex_init(&smutex, NULL); 62} 63 64static int 65compare(const void *p1, const void *p2) 66{ 67 uintptr_t v1 = (uintptr_t)p1; 68 uintptr_t v2 = (uintptr_t)p2; 69 if (v1 == v2) 70 return (0); 71 else if (v1 < v2) 72 return (-1); 73 return (1); 74} 75 76void 77gssd_enter(void *ptr) 78{ 79 if (ptr == NULL) 80 return; 81 82 pthread_once(&sonce, init); 83 (void) pthread_mutex_lock(&smutex); 84 if (ctx_counter > MAX_GSS_CONTEXTS) { 85 Log("To many contexes. Exiting\n"); 86 /* Will exit with 0 so lanchd will start as again */ 87 exit(0); 88 } 89 90 if ((tfind(ptr, &rootp, compare) == (void *)0)) { 91 if (tsearch(ptr, &rootp, compare)) 92 ctx_counter++; 93 } 94 (void) pthread_mutex_unlock(&smutex); 95} 96 97void 98gssd_remove(void *ptr) 99{ 100 if (ptr == NULL) 101 return; 102 103 pthread_once(&sonce, init); 104 (void) pthread_mutex_lock(&smutex); 105 if (tdelete(ptr, &rootp, compare)) 106 ctx_counter--; 107 (void) pthread_mutex_unlock(&smutex); 108} 109 110int 111gssd_check(void *ptr) 112{ 113 int rc; 114 if (ptr == (void *)0) 115 return (1); 116 117 pthread_once(&sonce, init); 118 (void) pthread_mutex_lock(&smutex); 119 rc = (tfind(ptr, &rootp, compare) != (void *)0); 120 (void) pthread_mutex_unlock(&smutex); 121 return (rc); 122} 123 124char * 125buf_to_str(gss_buffer_t buf) 126{ 127 char *s = malloc(buf->length + 1); 128 uint32_t min; 129 130 if (s) { 131 memcpy(s, buf->value, buf->length); 132 s[buf->length] = '\0'; 133 } 134 (void) gss_release_buffer(&min, buf); 135 136 return (s); 137} 138 139int 140traced() 141{ 142 struct kinfo_proc kp; 143 int mib[4]; 144 size_t len; 145 146 /* Fill out the first three components of the mib */ 147 len = 4; 148 sysctlnametomib("kern.proc.pid", mib, &len); 149 mib[3] = getpid(); 150 len = sizeof(kp); 151 if (sysctl(mib, 4, &kp, &len, NULL, 0) == -1) { 152 gssd_log(ASL_LEVEL_ERR, "sysctl: %s", strerror(errno)); 153 return (FALSE); 154 } 155 156 return ((kp.kp_proc.p_flag & P_TRACED) == P_TRACED); 157} 158 159static void 160regerr(int rerr, regex_t *re) 161{ 162 char errbuff[60]; 163 164 (void)regerror(rerr, re, errbuff, sizeof(errbuff)); 165 Fatal("regex error %s\n", errbuff); 166} 167 168/* 169 * Convert the oid in to a sane name if possible, if not return 170 * the string constant of unkown. 171 * 172 */ 173 174struct oid_name_entry { 175 gss_OID oid; 176 const char *name; 177} oid_name_tbl[] = { 178 { GSS_C_NT_USER_NAME, "user name" }, 179 { GSS_C_NT_MACHINE_UID_NAME, "uid" }, 180 { GSS_C_NT_STRING_UID_NAME, "uid string" }, 181 { GSS_C_NT_HOSTBASED_SERVICE_X, "host based service" }, 182 { GSS_C_NT_HOSTBASED_SERVICE, "host based service" }, 183 { GSS_C_NT_ANONYMOUS, "anonymous" }, 184 { GSS_C_NT_EXPORT_NAME, "export name" }, 185 { GSS_C_NT_DN, "distinguished name" }, 186 { GSS_SASL_DIGEST_MD5_MECHANISM, "sasl md5 mech" }, 187 { GSS_NETLOGON_MECHANISM, "netlogon mech" }, 188 { GSS_C_INQ_SSPI_SESSION_KEY, "session key" }, 189 { GSS_C_INQ_WIN2K_PAC_X, "Win2k PAC" }, 190 { GSS_KRB5_NT_PRINCIPAL_NAME, "KRB5 principal" }, 191 { GSS_KRB5_NT_PRINCIPAL_NAME_REFERRAL, "KRB5 principal referral" }, 192 { GSS_KRB5_NT_PRINCIPAL, "KRB5 principal" }, 193 { GSS_KRB5_NT_USER_NAME, "KRB5 user name" }, 194 { GSS_KRB5_NT_MACHINE_UID_NAME, "KRB5 uid" }, 195 { GSS_KRB5_NT_STRING_UID_NAME, "KRB5 uid string" }, 196 { GSS_KRB5_MECHANISM, "KRB5 mech" }, 197 { GSS_PKU2U_MECHANISM, "PKU2U mech" }, 198 { GSS_IAKERB_MECHANISM, "IA Kerb mech" }, 199 /* OTHER gssapi_krb5.h oids here */ 200 { GSS_NTLM_MECHANISM, "NTLM mech" }, 201 { GSS_C_NT_NTLM, "NTLM name" }, 202 { GSS_C_NTLM_GUEST, "NTLM guest" }, 203 { GSS_NTLM_GET_SESSION_KEY_X, "NTLM session key" }, 204 { GSS_SPNEGO_MECHANISM, "SPNEGO mech" }, 205 { GSS_C_NT_UUID, "UUID name" }, 206 { NULL, NULL } 207}; 208 209char * 210oid_name(gss_OID oid) 211{ 212 uint32_t maj, min; 213 gss_buffer_desc buf; 214 struct oid_name_entry *oep; 215 const char *name = NULL; 216 char *rn; 217 218 for (oep = oid_name_tbl; oep->name != NULL; oep++) { 219 if (gss_oid_equal(oid, oep->oid)) { 220 name = oep->name; 221 break; 222 } 223 } 224 if (oid == NULL) 225 name = "default (null) oid"; 226 if (name == NULL) { 227 maj = gss_oid_to_str(&min, oid, &buf); 228 if (maj != GSS_S_COMPLETE) 229 rn = strdup("Bad oid"); 230 else 231 rn = buf_to_str(&buf); 232 } else 233 rn = strdup(name); 234 235 return (rn); 236} 237 238/* 239 * GSSAPI ERRORS 240 * 241 */ 242static const char *gss_call_err_names[] = { 243 "GSS_S_CALL_INACCESSIBLE_READ", 244 "GSS_S_CALL_INACCESSIBLE_WRITE", 245 "GSS_S_CALL_BAD_STRUCTURE" 246}; 247#define CALL_ERR_SIZE (sizeof(gss_call_err_names)/sizeof(const char *)) 248 249static const char *gss_err_names[] = { 250 "GSS_S_BAD_MECH", 251 "GSS_S_BAD_NAME", 252 "GSS_S_BAD_NAMETYPE", 253 "GSS_S_BAD_BINDINGS", 254 "GSS_S_BAD_STATUS", 255 "GSS_S_BAD_MIC", 256 "GSS_S_NO_CRED", 257 "GSS_S_NO_CONTEXT", 258 "GSS_S_DEFECTIVE_TOKEN", 259 "GSS_S_DEFECTIVE_CREDENTIAL", 260 "GSS_S_CREDENTIALS_EXPIRED", 261 "GSS_S_CONTEXT_EXPIRED", 262 "GSS_S_FAILURE", 263 "GSS_S_BAD_QOP", 264 "GSS_S_UNAUTHORIZED", 265 "GSS_S_UNAVAILABLE", 266 "GSS_S_DUPLICATE_ELEMENT", 267 "GSS_S_NAME_NOT_MN", 268}; 269#define ERR_SIZE (sizeof(gss_err_names)/sizeof(const char *)) 270 271static const char *gss_sup_names[] = { 272 "GSS_CONTINUE_NEEDED", 273 "GSS_S_DUPLICATE_TOKEN", 274 "GSS_S_OLD_TOKEN", 275 "GSS_S_UNSEQ_TOKEN", 276 "GSS_S_GAP_TOKEN", 277}; 278#define SUP_SIZE (sizeof(gss_sup_names)/sizeof(const char *)) 279#define SUP_STRING_SIZE 128 280 281static const char* 282gss_error(uint32_t status) 283{ 284 status >>= GSS_C_ROUTINE_ERROR_OFFSET; 285 status &= (uint32_t)GSS_C_ROUTINE_ERROR_MASK; 286 if (status == 0) 287 return (NULL); 288 if (status > ERR_SIZE) 289 return ("GSS_UNKOWN_ERROR"); 290 return (gss_err_names[status - 1]); 291} 292 293static const char* 294gss_call_error(uint32_t status) 295{ 296 status >>= GSS_C_CALLING_ERROR_OFFSET; 297 status &= (uint32_t)GSS_C_CALLING_ERROR_MASK; 298 if (status == 0) 299 return (NULL); 300 if (status > ERR_SIZE) 301 return ("GSS_UNKOWN_CALL_ERROR"); 302 return (gss_call_err_names[status - 1]); 303} 304 305static char* 306gss_sup_info(uint32_t status) 307{ 308 309 int previous = 0; 310 size_t i; 311 char *str; 312 313 status >>= GSS_C_SUPPLEMENTARY_OFFSET; 314 status &= (uint32_t) GSS_C_SUPPLEMENTARY_MASK; 315 if (status == 0) 316 return (NULL); 317 318 str = malloc(SUP_STRING_SIZE); 319 if (str == NULL) 320 Fatal("Gssd, gssd-agent out of memory"); 321 322 for (i = 0, *str = '\0'; status && i < SUP_SIZE; i++, status >>= 1) { 323 if (status & 1) { 324 if (previous) 325 strlcat(str, " ", SUP_STRING_SIZE); 326 strlcat(str, gss_sup_names[i], SUP_STRING_SIZE); 327 previous = 1; 328 } 329 } 330 if (status) 331 strlcat(str, previous ? " GSS_UNKOWN_INFO" : "GSS_UNKOWN_INFO", SUP_STRING_SIZE); 332 333 return (str); 334} 335 336static char * 337gss_alt_error(uint32_t status) 338{ 339 const char *error, *call_err; 340 char *ret, *sup_info; 341 342 if (status == GSS_S_COMPLETE) 343 return (strdup("GSS_S_COMPLETE")); 344 345 error = gss_error(status); 346 call_err = gss_call_error(status); 347 sup_info = gss_sup_info(status); 348 349 if (error && call_err && sup_info) 350 asprintf(&ret, "%s (%s): %s", error, call_err, sup_info); 351 else if (error && call_err && sup_info == NULL) 352 asprintf(&ret, "%s (%s)", error, call_err); 353 else if (error && call_err == NULL && sup_info) 354 asprintf(&ret, "%s: %s", error, sup_info); 355 else if (error && call_err == NULL && sup_info == NULL) 356 ret = strdup(error); 357 else if (error == NULL && call_err && sup_info) 358 asprintf(&ret, "(%s): %s", call_err, sup_info); 359 else if (error == NULL && call_err && sup_info == NULL) 360 ret = strdup(call_err); 361 else { 362 ret = sup_info; 363 sup_info = NULL; 364 } 365 366 if (sup_info) 367 free(sup_info); 368 369 return (ret); 370} 371 372 373/* 374 * Convert the major and minor error codes for a given id into a string. 375 * 376 * GSS API has a function, gss_display_status, that will do this and that can handle 377 * very long messages by repeatedly calling the routine with a handle returned 378 * from the first call. This is too long to be useful and I have never seen a 379 * case where the entire message was not retrieved on the first call. If we 380 * should find more text to extract will notate that by appending a '<' code value '>' 381 * at the end of the returned string. The caller will be responsible to free 382 * the result. If the OID passed in is GSS_C_NO_OID then we will look up the 383 * major status, else we will return the string for the minor status. If a code 384 * does not map to a string we'll return the string representation of the code 385 * value. 386 */ 387 388/* 389 * gss_display_status for minor codes that are not from the last failure on the 390 * current thread are displayed unknown mech-code <blah> from mech <string of numbers> 391 * for the oid in the mech in question. In the abbreviated format, "#" specifier, 392 * will strip that of, since we are printing a nice readable name and its redundant. 393 */ 394 395static char *nomechexp = " for mech ([0-9]+ )*[0-9]+$"; 396static pthread_once_t gonce = PTHREAD_ONCE_INIT; 397static regex_t mre; 398 399static void 400gss_strerror_init(void) 401{ 402 int rerr = regcomp(&mre, nomechexp, REG_EXTENDED); 403 if (rerr) 404 regerr(rerr, &mre); 405} 406 407char * 408gss_strerror(gss_OID oid, uint32_t code, uint32_t flag) 409{ 410 uint32_t maj, min; 411 uint32_t display_ctx = 0; 412 int code_space; 413 gss_buffer_desc msg_buf; 414 char *ret_msg = NULL; 415 char *tmp_msg; 416 char *oidstr; 417 regmatch_t match[1]; 418 int rerr; 419 420 pthread_once(&gonce, gss_strerror_init); 421 code_space = (oid == GSS_C_NO_OID) ? GSS_C_GSS_CODE : GSS_C_MECH_CODE; 422 oidstr = (oid == GSS_C_NO_OID) ? strdup("GSSAPI") : oid_name(oid); 423 if (oidstr == NULL) 424 Fatal("Gssd or gssd-agent out of memory -- exiting\n"); 425 426 if (oid == GSS_C_NO_OID && flag) { 427 free(oidstr); 428 return (gss_alt_error(code)); 429 } 430 431 maj = gss_display_status(&min, code, code_space, oid, &display_ctx, &msg_buf); 432 if (maj != GSS_S_COMPLETE) { 433 asprintf(&ret_msg, "%s status %d", oidstr, code); 434 free(oidstr); 435 return (ret_msg); 436 } else { 437 tmp_msg = buf_to_str(&msg_buf); 438 if (tmp_msg == NULL) 439 Fatal("Gssd or gssd-agent out of memory -- exiting\n"); 440 if (flag && (oid != GSS_C_NO_OID)) { 441 442 rerr = regexec(&mre, tmp_msg, 1, match, 0); 443 if (rerr == REG_NOMATCH) 444 goto done; 445 else if (rerr) 446 regerr(rerr, &mre); 447 else 448 tmp_msg[match[0].rm_so] = '\0'; 449 } 450 } 451 452done: 453 if (flag) { 454 if (display_ctx) 455 asprintf(&ret_msg, "%s: %s <%d>", oidstr, tmp_msg, code); 456 else 457 asprintf(&ret_msg, "%s: %s", oidstr, tmp_msg); 458 } else { 459 if (display_ctx) 460 asprintf(&ret_msg, "%s status: %s <%d>", oidstr, tmp_msg, code); 461 else 462 asprintf(&ret_msg, "%s status: %s", oidstr, tmp_msg); 463 } 464 free(oidstr); 465 free(tmp_msg); 466 467 return (ret_msg); 468} 469 470static int foreground; 471static int istraced; 472 473#include <fcntl.h> 474int 475in_foreground(int ttyfd) 476{ 477 int ttyfd2 = -1; 478 pid_t tpg; 479 480 if (ttyfd == -1) { 481 ttyfd2 = open("/dev/tty", O_WRONLY); 482 if (ttyfd2 == -1) 483 return (FALSE); 484 ttyfd = ttyfd2; 485 } 486 tpg = tcgetpgrp(ttyfd); 487 488 if (ttyfd2 > -1) 489 close(ttyfd2); 490 491 if (tpg == -1) 492 return (FALSE); 493 494 return (tpg == getpgid(getpid())); 495} 496 497/* 498 * Regular expression for printf fields: 499 * Regexec will match an array of regmatch_t's as follows: 500 * index 0 is the whole expression 501 * skip the optional accessor specification 502 * index 1 is the optional flags fields 503 * skip the optional AltiVec/SSE vector value separator 504 * index 2 is the optional minimum field width 505 * index 3 is the optional precision field. (also max digits or characters to print) 506 * index 4 is the optional sub-field from above; the characters after the '.' 507 * index 5 is the intergral lenght type modifier (diouxXn) 508 * index is the type conversion character 509 */ 510#define PRF_WHOLE_MATCH 0 511#define PRF_ACCESSOR -1 512#define PRF_FLAGS 1 513#define PRF_VSEP -1 514#define PRF_WIDTH 2 515#define PRF_PREC 3 516#define PRF_PREC_SPEC 4 517#define PRF_LENGTH 5 518#define PRF_TYPE 6 519 520#define PRF_FIELDS 7 521 522//static char *prfrexp = "%([0-9]+\\$)?([-#0 +']+)?([,;:_])?(\\*|[0-9]+)?(.(\\*|[0-9]*)?)?(hh?|ll?|j|t|z|q|L)?([diouxXDOUeEfFgGaACcSsp%kK])"; 523static char *prfrexp = "%([-#0 +']+)?(\\*|[0-9]+)?(.(\\*|[0-9]*)?)?(hh?|ll?|j|t|z|q|L)?([diouxXDOUeEfFgGaACcSsp%kK])"; 524static regex_t pre; 525 526#ifndef GSSD_LOG_DEBUG 527static char *gssrexp = "%([0-9]+\\$)?([-#0 +']+)?([,;:_])?(\\*|[0-9]+)?(.(\\*|[0-9]*)?)?(hh?|ll?|j|t|z|q|L)?([kK])"; 528static regex_t gre; 529#endif 530 531static pthread_once_t ronce = PTHREAD_ONCE_INIT; 532 533#define GSSD_FACILITY "com.apple.gssd" 534static aslclient asl = NULL; 535 536#define ASL_INIT_FILTER ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE) 537 538static void 539gssd_log_init(void) 540{ 541 int rerr; 542 543 544 /* Check if were in the forground */ 545 foreground = in_foreground(2); 546 istraced = traced(); 547 548 asl = asl_open(getprogname(), GSSD_FACILITY, 0); 549 asl_set_filter(asl, ASL_INIT_FILTER); 550 551 rerr = regcomp(&pre, prfrexp, REG_EXTENDED); 552 if (rerr) 553 regerr(rerr, &pre); 554#ifndef GSSD_LOG_DEBUG 555 rerr = regcomp(&gre, gssrexp, REG_EXTENDED | REG_NOSUB); 556 if (rerr) 557 regerr(rerr, &gre); 558#endif 559} 560 561void 562fatal(const char *fmt, ...) 563{ 564 va_list ap; 565 566 va_start(ap, fmt); 567 asl_vlog(asl, NULL, ASL_LEVEL_ERR, fmt, ap); 568 va_end(ap); 569 exit(1); 570} 571 572static int debug = 0; 573static void (*disable_timeout)(int); 574 575/* 576 * set a callback to disable the processes timeout. 577 * If the argument is zero reenable the time out 578 * else disable the timeout. 579 */ 580void set_debug_level_init(void (*dto)(int)) 581{ 582 disable_timeout = dto; 583} 584 585int get_debug_level(void) 586{ 587 return (debug); 588} 589 590/* 591 * Set the debug level and filter mask to allow asl debug messages. 592 * This routine is called at startup and from the signal handler thread. 593 * That thread registers for notifications from syslog the master or remote 594 * filter have been install/removed. 595 * 596 * - debug level 0 - turn off debugging and do not send messages syslogd 597 * - debug level 1 - set debugging to 1 and log to syslog at info level 598 * - debug level >= 2 set the debugging level and log to syslogd at debug level 599 * 600 * - debug level -1, set the debug level to what the asl log level allows. 601 * N.B. If the active asl log level is debug and the current debug level is 602 * greater than 1, log at the current debug level. If the previous active filter 603 * was remote and the current active filter is local turn off debug. This would 604 * indicate that someone explicitly is turning off debuging with syslog -c gssd off. 605 * 606 * Normally seting a debug level greater than zero will stop gssd from timing out. 607 * The exception is that we are turning debugging on because of a master filter notification. 608 * in that case we leave the time out setting alone. As long as the master filter is 609 * active and is greater than notice, we will log to syslog in each startup of gssd. 610 * 611 * Limitations: Only one filter is active at a time. The filter priorities are 612 * remote > master > local. 613 * Raising the debug level with SIGUSR1 will have no apparent effect if the 614 * remote or master filter is the active filter and if the filter does not have 615 * debug set. If info is set you will see only info messages. 616 */ 617void 618set_debug_level(int debug_level) 619{ 620 int filter, local, master, remote, active = 0; 621 int status; 622 static int last_active = 0; 623 624 pthread_once(&ronce, gssd_log_init); 625 if (debug_level < 0) { 626 /* 627 * We've got notified by syslog that our filter mask have changed. 628 * Use that to determine the debug level. 629 */ 630 status = asl_get_filter(asl, &local, &master, &remote, &active); 631 if (status) { 632 Log("asl_get_filter failed\n"); 633 return; 634 } 635 636 //Log("l = %x m = %x r = %x active = %d, last_active = %d", local, master, remote, active, last_active); 637 switch (active) { 638 case 0: filter = local; 639 break; 640 case 1: filter = master; 641 /* 642 * If someone turns on global debugging we don't 643 * alter whether we time out or not. If global 644 * debugging was in effect when gssd started, we 645 * will continue to send debug output as long as 646 * interest in global debugging is in effect. 647 * If global debugging is enabled after explicit debugging 648 * was enabled either with a remote filter or signal then 649 * we should keep the current timout behavor until the user 650 * turns off explicit debugging. 651 */ 652 break; 653 case 2: filter = remote; 654 break; 655 default: 656 Log("Unkown active ASL filter %d", active); 657 return; 658 } 659 if (ASL_FILTER_MASK(ASL_LEVEL_DEBUG) & filter) 660 debug_level = maximum(debug, 2); 661 else if (ASL_FILTER_MASK(ASL_LEVEL_INFO) & filter) 662 debug_level = 1; 663 else 664 debug_level = 0; 665 if (last_active == 2 && active == 0) { 666 /* 667 * We got here because the user gave the command 668 * syslog -c gssd off, so the user as explicitly 669 * told us, she is no longer interested in debugging. 670 * turn debugging off. 671 */ 672 debug_level = 0; 673 } 674 last_active = active; 675 } 676 if (debug == debug_level) 677 return; /* Nothing has changed. */ 678 679 if (active == 0) { 680 switch (debug_level) { 681 case 0: /* Debug has been turned off. */ 682 local = ASL_FILTER_MASK_UPTO(ASL_LEVEL_NOTICE); 683 break; 684 case 1: /* Debug hs been set to 1 turn on INFO level loging. */ 685 local = ASL_FILTER_MASK_UPTO(ASL_LEVEL_INFO) | ASL_FILTER_MASK_TUNNEL; 686 break; 687 default: /* Anything else turn on DEBUG level logging. */ 688 local = ASL_FILTER_MASK_UPTO(ASL_LEVEL_DEBUG) | ASL_FILTER_MASK_TUNNEL; 689 break; 690 } 691 asl_set_filter(asl, local); 692 } 693 694 debug = debug_level; 695 if (debug == 0) 696 gssd_log(ASL_LEVEL_NOTICE, "Leaving debug mode"); 697 if (active != 1 && disable_timeout) 698 disable_timeout(debug != 0); 699} 700 701static void 702g_vlog(int level, const char *fmt, va_list ap) 703{ 704 int saved_errno = errno; 705 706 if (foreground) { 707 vfprintf(stderr, fmt, ap); 708 fflush(stderr); 709 } else { 710 if (istraced && isatty(1)) { 711 va_list ap2; 712 va_copy(ap2, ap); 713 vprintf(fmt, ap2); 714 fflush(stdout); 715 va_end(ap2); 716 } 717 asl_vlog(asl, NULL, level, fmt, ap); 718 } 719 720 errno = saved_errno; 721} 722 723static void 724g_log(int level, const char *fmt, ...) 725{ 726 va_list ap; 727 728 va_start(ap, fmt); 729 g_vlog(level, fmt, ap); 730 va_end(ap); 731} 732 733#ifndef GSSD_LOG_DEBUG 734static int 735has_gss_conv(const char *fmt) 736{ 737 int rerr; 738 739 rerr = regexec(&gre, fmt, 0, NULL, 0); 740 741 if (rerr == REG_NOMATCH) 742 return (FALSE); 743 if (rerr) 744 regerr(rerr, &gre); 745 746 return (TRUE); 747} 748#endif 749 750static int 751vslprintf(char *buf, size_t size, const char *fmt, va_list ap) 752{ 753 size_t n = strlen(buf); 754 int rv; 755 756 if (n >= size) 757 return (0); 758 759 size -= n; 760 rv = vsnprintf(&buf[n], size, fmt, ap); 761 return (((size_t)rv > size) ? (int)size : rv); 762} 763 764static int 765slprintf(char *buf, size_t size, const char *fmt, ...) 766{ 767 va_list ap; 768 int rv; 769 770 va_start(ap, fmt); 771 rv = vslprintf(buf, size, fmt, ap); 772 va_end(ap); 773 774 return (rv); 775} 776 777static size_t 778strlncat(char *buf, const char *s, size_t bufsize, size_t slen) 779{ 780 char *d; 781 size_t n = strlen(buf); 782 size_t i; 783 784 if (slen + n >= bufsize) 785 Fatal("copy to big\n"); 786 787 d = &buf[n]; 788 for (i = 0; i < slen && *s && (n + i < bufsize - 1); i++) { 789 *d++ = *s++; 790 } 791 *d = '\0'; 792 793 return (d - buf); 794} 795 796static size_t 797strlncpy(char *buf, const char *s, size_t bufsize, size_t slen) 798{ 799 *buf = '\0'; 800 return (strlncat(buf, s, bufsize, slen)); 801} 802 803#define MAX_FMT 256 804#define MAX_LOG_MESSAGE 1024 805 806/* 807 * Advance the va_list given a printf style format. 808 * We will make a copy of our argment list and pass that to slprintf. 809 * We then need to advance ap by the same amount. 810 */ 811static void 812va_next(va_list *ap, const char *fmt, regmatch_t match[PRF_FIELDS]) 813{ 814 char ftype; 815 char tlen[3]; 816 regoff_t i, j; 817 818 ftype = fmt[match[PRF_TYPE].rm_so]; 819 for (i = match[PRF_LENGTH].rm_so, j = 0; i < match[PRF_LENGTH].rm_eo; i++, j++) { 820 tlen[j] = fmt[i]; 821 } 822 tlen[j] = '\0'; 823 if (match[PRF_WIDTH].rm_eo - match[PRF_WIDTH].rm_so == 1 && 824 fmt[match[PRF_WIDTH].rm_so] == '*') 825 va_arg(*ap, int); 826 if (match[PRF_PREC_SPEC].rm_eo - match[PRF_PREC_SPEC].rm_so == 1 && 827 fmt[match[PRF_PREC_SPEC].rm_so] == '*') 828 va_arg(*ap, int); 829 830 switch(ftype) { 831 /* Shamelessly assume unsigned sizes are the same size as signed ones. */ 832 case 'd': 833 case 'i': 834 case 'o': 835 case 'u': 836 case 'x': 837 case 'X': 838 if (tlen[0] == 'h' && tlen[1] == 'h') 839 va_arg(*ap, int); 840 else if (tlen[0] == 'l' && tlen[1] == 'l') 841 va_arg(*ap, long long); 842 else if (*tlen == 'l') 843 va_arg(*ap, long); 844 else if (*tlen == 'j') 845 va_arg(*ap, intmax_t); 846 else if (*tlen == 't') 847 va_arg(*ap, ptrdiff_t); 848 else if (*tlen == 'z') 849 va_arg(*ap, size_t); 850 else if (*tlen == 'q') 851 va_arg(*ap, quad_t); 852 else 853 /* tlen in NULL or invalid and ignored */ 854 va_arg(*ap, int); 855 break; 856 case 'D': 857 case 'O': 858 case 'U': 859 va_arg(*ap, long); 860 break; 861 case 'a': 862 case 'A': 863 case 'e': 864 case 'E': 865 case 'f': 866 case 'F': 867 case 'g': 868 case 'G': 869 if (*tlen == 'L') 870 va_arg(*ap, long double); 871 else 872 va_arg(*ap, double); 873 break; 874 case 'c': 875 if (*tlen == 'l' && tlen[1] == '\0') 876 va_arg(*ap, wchar_t); 877 else 878 va_arg(*ap, int); 879 break; 880 case 's': 881 if (*tlen == 'l' && tlen[1] == '\0') 882 va_arg(*ap, wchar_t *); 883 else 884 va_arg(*ap, char *); 885 break; 886 case 'C': 887 va_arg(*ap, wchar_t); 888 break; 889 case 'S': 890 va_arg(*ap, wchar_t); 891 break; 892 case 'p': 893 va_arg(*ap, void *); 894 break; 895 case '%': 896 default: 897 break; 898 } 899} 900 901static const char * 902fmt_parse(char *out_buffer, char **obp, const char *ofp, va_list *ap) 903{ 904 int rerr; 905 regmatch_t match[PRF_FIELDS]; 906 char tconv; 907 size_t mlen; 908 const char *mstr; 909 char kfmt[MAX_FMT]; 910 911 rerr = regexec(&pre, ofp, PRF_FIELDS, match, 0); 912 if (rerr == REG_NOMATCH) 913 return (NULL); /* Invalid format return NULL */ 914 else if (rerr) 915 regerr(rerr, &pre); /* Fatal error doesn't return, should never happen */ 916 917 tconv = ofp[match[PRF_TYPE].rm_so]; 918 mlen = (size_t)(match[PRF_WHOLE_MATCH].rm_eo - match[PRF_WHOLE_MATCH].rm_so); 919 mstr = &ofp[match[PRF_WHOLE_MATCH].rm_so]; 920 if (mlen >= MAX_FMT) 921 return (NULL); 922 if (tconv == 'k' || tconv == 'K') { 923 regoff_t i; 924 gss_OID oid; 925 char *gss_error_string; 926 uint32_t flag = 0; 927 uint32_t code; 928 int prec = -1, width = -1; 929 930 // handle 'k|K' format stream fromt ap to output buffer 931 for (i = match[PRF_FLAGS].rm_so; i < match[PRF_FLAGS].rm_eo; i++) { 932 if (ofp[i] == '#') { 933 flag++; 934 break; 935 } 936 } 937 if (match[PRF_WIDTH].rm_eo - match[PRF_WIDTH].rm_so == 1 && 938 ofp[match[PRF_WIDTH].rm_so] == '*') 939 width = va_arg(*ap, int); 940 941 if (match[PRF_PREC_SPEC].rm_eo - match[PRF_PREC_SPEC].rm_so == 1 && 942 ofp[match[PRF_PREC_SPEC].rm_so] == '*') 943 prec = va_arg(*ap, int); 944 945 oid = (tconv == 'k') ? va_arg(*ap, gss_OID) : GSS_C_NO_OID; 946 code = va_arg(*ap, uint32_t); 947 gss_error_string = gss_strerror(oid, code, flag); 948 kfmt[0] = '\0'; 949 strlncpy(kfmt, mstr, MAX_FMT, mlen); 950 // Change the 'k' to 's' 951 kfmt[strnlen(kfmt, MAX_FMT) - 1 ] = 's'; 952 if (width > -1 && prec > -1) 953 *obp += slprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, width, prec, gss_error_string); 954 else if (width > -1) 955 *obp += slprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, width, gss_error_string); 956 else if (prec > -1) 957 *obp += slprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, prec, gss_error_string); 958 else 959 *obp += slprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, gss_error_string); 960 free(gss_error_string); 961 } else { 962 va_list ap2; 963 va_copy(ap2, *ap); 964 strlncpy(kfmt, mstr, MAX_FMT, mlen); 965 *obp += vslprintf(out_buffer, MAX_LOG_MESSAGE, kfmt, ap2); 966 va_end(ap2); 967 va_next(ap, ofp, match); /* Eat the arguments printed */ 968 } 969 ofp += match[PRF_WHOLE_MATCH].rm_eo; 970 971 return (ofp); 972} 973 974void 975gssd_log(int log_level, const char *fmt, ...) 976{ 977 int saved_errno = errno; 978 const char *ofp = fmt; 979 char output_buffer[MAX_LOG_MESSAGE]; 980 char *obp = output_buffer; 981 va_list ap; 982 983 pthread_once(&ronce, gssd_log_init); 984 985 va_start(ap, fmt); 986#ifndef GSSD_LOG_DEBUG 987 if (!has_gss_conv(fmt) && 0) { 988 g_vlog(log_level, fmt, ap); 989 va_end(ap); 990 return; 991 } 992#endif 993 994 *obp = '\0'; 995 while (*ofp) { 996 if (*ofp != '%' ) { 997 if (obp - output_buffer < MAX_LOG_MESSAGE - 1) { 998 *obp++ = *ofp++; 999 *obp = '\0'; 1000 } 1001 } else { 1002 ofp = fmt_parse(output_buffer, &obp, ofp, &ap); 1003 if (ofp == NULL) { 1004 g_log(ASL_LEVEL_ERR, "Invalid log format %s skipping ...\n", fmt); 1005 return; 1006 } 1007 } 1008 } 1009 1010 va_end(ap); 1011 1012 if (*output_buffer) { 1013 size_t len = strlen(output_buffer); 1014 g_log(log_level, output_buffer[len -1] == '\n' ? "%s" : "%s\n", output_buffer); 1015 } 1016 1017 errno = saved_errno; 1018} 1019 1020#if 0 1021/* 1022 * Display the major and minor GSS return codes from routine. 1023 */ 1024void 1025display_GSS_err(char* rtnName, gss_OID mech, OM_uint32 maj, OM_uint32 min, int display_debug) 1026{ 1027 OM_uint32 msg_context = 0; 1028 OM_uint32 min_stat = 0; 1029 OM_uint32 maj_stat = 0; 1030 gss_buffer_desc errBuf; 1031 char *str; 1032 int count = 1; 1033 1034 if (maj == GSS_S_NO_CRED) 1035 display_debug = 1; 1036 1037 MSG(display_debug, "Error returned by %s:\n", rtnName); 1038 do { 1039 maj_stat = gss_display_status(&min_stat, maj, GSS_C_GSS_CODE, 1040 mech, &msg_context, &errBuf); 1041 str = (maj_stat == GSS_S_COMPLETE) ? buf_to_str(&errBuf) : NULL; 1042 if (count == 1) 1043 MSG(display_debug, "\tMajor error = %d: %s\n", maj, str ? str : ""); 1044 else 1045 MSG(display_debug, "\t\t%s\n", str ? str : ""); 1046 free(str); 1047 ++count; 1048 } while (msg_context != 0); 1049 1050 count = 1; 1051 msg_context = 0; 1052 do { 1053 maj_stat = gss_display_status (&min_stat, min, GSS_C_MECH_CODE, 1054 mech, &msg_context, &errBuf); 1055 str = (maj_stat == GSS_S_COMPLETE) ? buf_to_str(&errBuf) : NULL; 1056 if (count == 1) 1057 MSG(display_debug, "\tMinor error = %d: %s\n", min, str ? str : ""); 1058 else 1059 MSG(display_debug, "\t\t%s\n", str ? str : ""); 1060 free(str); 1061 ++count; 1062 } while (msg_context != 0); 1063} 1064#endif 1065 1066static const char HexChars[16] = { 1067 '0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F' 1068}; 1069 1070/* 1071 * Dump 16 bytes or bufSize bytes (<16) to line buf in hex followed by 1072 * character representation. 1073 */ 1074static void 1075HexLine(const char *buf, size_t *bufSize, char linebuf[80]) 1076{ 1077 const char *bptr = buf; 1078 size_t limit; 1079 size_t i; 1080 char *cptr = linebuf; 1081 1082 memset(linebuf, 0, 80); 1083 1084 limit = (*bufSize > 16) ? 16 : *bufSize; 1085 *bufSize -= limit; 1086 1087 for(i = 0; i < 16; i++) 1088 { 1089 if(i < limit) 1090 { 1091 *cptr++ = HexChars[(*bptr >> 4) & 0x0f]; 1092 *cptr++ = HexChars[*bptr & 0x0f]; 1093 *cptr++ = ' '; 1094 bptr++; 1095 } else { 1096 *cptr++ = ' '; 1097 *cptr++ = ' '; 1098 *cptr++ = ' '; 1099 1100 } 1101 } 1102 bptr = buf; 1103 *cptr++ = ' '; 1104 *cptr++ = ' '; 1105 *cptr++ = ' '; 1106 for(i = 0; i < limit; i++) 1107 { 1108 *cptr++ = (char) (((*bptr > 0x1f) && (*bptr < 0x7f)) ? *bptr : '.'); 1109 bptr++; 1110 } 1111 *cptr++ = '\n'; 1112 *cptr = '\0'; 1113} 1114 1115/* 1116 * Dump the supplied buffer in hex. 1117 */ 1118void 1119HexDump(const char *inBuffer, size_t inLength) 1120{ 1121 size_t currentSize = inLength; 1122 char linebuf[80]; 1123 1124 while(currentSize > 0) 1125 { 1126 HexLine(inBuffer, ¤tSize, linebuf); 1127 gssd_log(ASL_LEVEL_DEBUG, "\t%s", linebuf); 1128 inBuffer += 16; 1129 } 1130} 1131 1132#if 0 1133/* LogToMessageTracer. 1134 * Currently not used, but we may want this in the future. 1135 * At any rate this apparently is how it is done. 1136 */ 1137 1138void LogToMessageTracer(const char *domain, const char *signature, 1139 const char *optResult, const char *optValue, 1140 const char *fmt,...) 1141{ 1142 aslmsg m; 1143 va_list ap; 1144 1145 if ( (domain == NULL) || (signature == NULL) || (fmt == NULL) ) { 1146 /* domain, signature and msg are required */ 1147 return; 1148 } 1149 1150 m = asl_new(ASL_TYPE_MSG); 1151 asl_set(m, "com.apple.message.domain", domain); 1152 asl_set(m, "com.apple.message.signature", signature); 1153 1154 if (optResult != NULL) { 1155 asl_set(m, "com.apple.message.result", optResult); 1156 } 1157 if (optValue != NULL) { 1158 asl_set(m, "com.apple.message.value", optValue); 1159 } 1160 1161 va_start(ap, fmt); 1162 asl_vlog(NULL, m, ASL_LEVEL_NOTICE, fmt, ap); 1163 va_end(ap); 1164 1165 asl_free(m); 1166} 1167#endif 1168