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