1/* 2 * Copyright (c) 2009 Kungliga Tekniska Högskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Portions Copyright (c) 2010 Apple Inc. All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * 3. Neither the name of the Institute nor the names of its contributors 20 * may be used to endorse or promote products derived from this software 21 * without specific prior written permission. 22 * 23 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33 * SUCH DAMAGE. 34 */ 35 36#define HEIMDAL_PRINTF_ATTRIBUTE(x) 37 38#include "mech_locl.h" 39#include "heim_threads.h" 40#include "heimbase.h" 41 42#include <asl.h> 43#include <CoreFoundation/CoreFoundation.h> 44#include <krb5.h> 45 46struct mg_thread_ctx { 47 gss_OID mech; 48 OM_uint32 min_stat; 49 gss_buffer_desc min_error; 50 aslclient asl; 51 aslmsg msg; 52}; 53 54static HEIMDAL_MUTEX context_mutex = HEIMDAL_MUTEX_INITIALIZER; 55static int created_key; 56static HEIMDAL_thread_key context_key; 57 58 59static void 60destroy_context(void *ptr) 61{ 62 struct mg_thread_ctx *mg = ptr; 63 OM_uint32 junk; 64 65 if (mg == NULL) 66 return; 67 68 gss_release_buffer(&junk, &mg->min_error); 69 70 if (mg->msg) 71 asl_free(mg->msg); 72 if (mg->asl) 73 asl_close(mg->asl); 74 75 free(mg); 76} 77 78 79static struct mg_thread_ctx * 80_gss_mechglue_thread(void) 81{ 82 struct mg_thread_ctx *ctx; 83 int ret = 0; 84 85 HEIMDAL_MUTEX_lock(&context_mutex); 86 87 if (!created_key) { 88 HEIMDAL_key_create(&context_key, destroy_context, ret); 89 if (ret) { 90 HEIMDAL_MUTEX_unlock(&context_mutex); 91 return NULL; 92 } 93 created_key = 1; 94 } 95 HEIMDAL_MUTEX_unlock(&context_mutex); 96 97 ctx = HEIMDAL_getspecific(context_key); 98 if (ctx == NULL) { 99 100 ctx = calloc(1, sizeof(*ctx)); 101 if (ctx == NULL) 102 return NULL; 103 HEIMDAL_setspecific(context_key, ctx, ret); 104 if (ret) { 105 free(ctx); 106 return NULL; 107 } 108 109 ctx->asl = asl_open(getprogname(), NULL, 0); 110 ctx->msg = asl_new(ASL_TYPE_MSG); 111 asl_set(ctx->msg, "org.h5l.asl", "gssapi"); 112 } 113 return ctx; 114} 115 116OM_uint32 117_gss_mg_get_error(const gss_OID mech, OM_uint32 value, gss_buffer_t string) 118{ 119 struct mg_thread_ctx *mg; 120 121 mg = _gss_mechglue_thread(); 122 if (mg == NULL) 123 return GSS_S_BAD_STATUS; 124 125 if (value != mg->min_stat || mg->min_error.length == 0) { 126 _mg_buffer_zero(string); 127 return GSS_S_BAD_STATUS; 128 } 129 string->value = malloc(mg->min_error.length); 130 if (string->value == NULL) { 131 _mg_buffer_zero(string); 132 return GSS_S_FAILURE; 133 } 134 string->length = mg->min_error.length; 135 memcpy(string->value, mg->min_error.value, mg->min_error.length); 136 return GSS_S_COMPLETE; 137} 138 139void 140_gss_mg_error(gssapi_mech_interface m, OM_uint32 min) 141{ 142 OM_uint32 major_status, minor_status; 143 OM_uint32 message_content = 0; 144 struct mg_thread_ctx *mg; 145 146 /* 147 * Mechs without gss_display_status() does 148 * gss_mg_collect_error() by themself. 149 */ 150 if (m->gm_display_status == NULL) 151 return ; 152 153 mg = _gss_mechglue_thread(); 154 if (mg == NULL) 155 return; 156 157 gss_release_buffer(&minor_status, &mg->min_error); 158 159 mg->mech = &m->gm_mech_oid; 160 mg->min_stat = min; 161 162 major_status = m->gm_display_status(&minor_status, 163 min, 164 GSS_C_MECH_CODE, 165 &m->gm_mech_oid, 166 &message_content, 167 &mg->min_error); 168 if (major_status != GSS_S_COMPLETE) { 169 _mg_buffer_zero(&mg->min_error); 170 } else { 171 _gss_mg_log(5, "_gss_mg_error: captured %.*s (%d) from underlaying mech %s", 172 (int)mg->min_error.length, (const char *)mg->min_error.value, 173 (int)min, m->gm_name); 174 } 175} 176 177void 178gss_mg_collect_error(gss_OID mech, OM_uint32 maj, OM_uint32 min) 179{ 180 gssapi_mech_interface m = __gss_get_mechanism(mech); 181 if (m == NULL) 182 return; 183 _gss_mg_error(m, min); 184} 185 186OM_uint32 187gss_mg_set_error_string(gss_OID mech, 188 OM_uint32 maj, OM_uint32 min, 189 const char *fmt, ...) 190 HEIMDAL_PRINTF_ATTRIBUTE((printf, 4, 5)) 191{ 192 struct mg_thread_ctx *mg; 193 char *str = NULL; 194 OM_uint32 junk; 195 va_list ap; 196 197 mg = _gss_mechglue_thread(); 198 if (mg == NULL) 199 return maj; 200 201 va_start(ap, fmt); 202 vasprintf(&str, fmt, ap); 203 va_end(ap); 204 205 if (str) { 206 gss_release_buffer(&junk, &mg->min_error); 207 208 mg->mech = mech; 209 mg->min_stat = min; 210 211 mg->min_error.value = str; 212 mg->min_error.length = strlen(str); 213 214 _gss_mg_log(5, "gss_mg_set_error_string: %.*s (%d/%d)", 215 (int)mg->min_error.length, (const char *)mg->min_error.value, 216 (int)maj, (int)min); 217 } 218 return maj; 219} 220 221#ifdef __APPLE__ 222 223#include <CoreFoundation/CoreFoundation.h> 224 225CFErrorRef 226_gss_mg_cferror(OM_uint32 major_status, 227 OM_uint32 minor_status, 228 gss_const_OID mech) 229{ 230 struct mg_thread_ctx *mg; 231 CFErrorRef e; 232#define NUM_ERROR_DESC 5 233 void const *keys[NUM_ERROR_DESC] = { 234 CFSTR("kGSSMajorErrorCode"), 235 CFSTR("kGSSMinorErrorCode"), 236 CFSTR("kGSSMechanismOID"), 237 CFSTR("kGSSMechanism"), 238 kCFErrorDescriptionKey 239 }; 240 void const *values[NUM_ERROR_DESC] = { 0 }; 241 gss_buffer_desc oid; 242 const char *name; 243 OM_uint32 junk; 244 size_t n; 245 246 values[0] = CFNumberCreate(NULL, kCFNumberSInt32Type, &major_status); 247 values[1] = CFNumberCreate(NULL, kCFNumberSInt32Type, &minor_status); 248 249 if (mech && gss_oid_to_str(&junk, (gss_OID)mech, &oid) == GSS_S_COMPLETE) { 250 values[2] = CFStringCreateWithFormat(NULL, NULL, CFSTR("%.*s"), (int)oid.length, (char *)oid.value); 251 gss_release_buffer(&junk, &oid); 252 } else { 253 values[2] = CFStringCreateWithFormat(NULL, NULL, CFSTR("no-mech")); 254 } 255 256 if (mech && (name = gss_oid_to_name(mech)) != NULL) { 257 values[3] = CFStringCreateWithFormat(NULL, NULL, CFSTR("%s"), name);; 258 } else { 259 values[3] = CFStringCreateWithFormat(NULL, NULL, CFSTR("no mech given")); 260 } 261 262 mg = _gss_mechglue_thread(); 263 if (mg && minor_status == mg->min_stat && mg->min_error.length != 0) { 264 values[4] = CFStringCreateWithFormat(NULL, NULL, CFSTR("%.*s"), 265 (int)mg->min_error.length, 266 mg->min_error.value); 267 } else { 268 values[4] = CFStringCreateWithFormat(NULL, NULL, CFSTR("Unknown minor status: %d"), (int)minor_status); 269 } 270 271 e = CFErrorCreateWithUserInfoKeysAndValues(NULL, 272 CFSTR("org.h5l.GSS"), 273 (CFIndex)major_status, 274 keys, 275 values, 276 NUM_ERROR_DESC); 277 for (n = 0; n < sizeof(values) / sizeof(values[0]); n++) 278 CFRelease(values[n]); 279 280 return e; 281} 282 283 284 285static CFTypeRef 286CopyKeyFromFile(CFStringRef domain, CFStringRef key) 287{ 288 CFReadStreamRef s; 289 CFDictionaryRef d; 290 CFStringRef file; 291 CFErrorRef e; 292 CFURLRef url; 293 CFTypeRef val; 294 295 file = CFStringCreateWithFormat(NULL, 0, CFSTR("/Library/Preferences/%@.plist"), domain); 296 if (file == NULL) 297 return NULL; 298 299 url = CFURLCreateWithFileSystemPath(kCFAllocatorDefault, file, kCFURLPOSIXPathStyle, false); 300 CFRelease(file); 301 if (url == NULL) 302 return NULL; 303 304 s = CFReadStreamCreateWithFile(kCFAllocatorDefault, url); 305 CFRelease(url); 306 if (s == NULL) 307 return NULL; 308 309 if (!CFReadStreamOpen(s)) { 310 CFRelease(s); 311 return NULL; 312 } 313 314 d = (CFDictionaryRef)CFPropertyListCreateWithStream (kCFAllocatorDefault, s, 0, kCFPropertyListImmutable, NULL, &e); 315 CFRelease(s); 316 if (d == NULL) 317 return NULL; 318 319 if (CFGetTypeID(d) != CFDictionaryGetTypeID()) { 320 CFRelease(d); 321 return NULL; 322 } 323 324 val = CFDictionaryGetValue(d, key); 325 if (val) 326 CFRetain(val); 327 CFRelease(d); 328 return val; 329} 330 331 332CFTypeRef 333_gss_mg_copy_key(CFStringRef domain, CFStringRef key) 334{ 335 CFTypeRef val; 336 337 /* 338 * First prefer system file, then user copy if we are allowed to 339 * touch user home directory. 340 */ 341 342 val = CopyKeyFromFile(domain, key); 343 344 if (val == NULL && krb5_homedir_access(NULL)) { 345 val = CFPreferencesCopyAppValue(key, domain); 346 if (val == NULL) 347 val = CFPreferencesCopyValue(key, domain, kCFPreferencesAnyUser, kCFPreferencesAnyHost); 348 } 349 return val; 350} 351 352#endif 353 354static int log_level = 1; 355static int asl_level = ASL_LEVEL_DEBUG; 356 357static void 358init_log(void *ptr) 359{ 360 CFTypeRef val; 361 362 val = _gss_mg_copy_key(CFSTR("com.apple.GSS"), CFSTR("DebugLevel")); 363 if (val == NULL) 364 return; 365 366 if (CFGetTypeID(val) == CFBooleanGetTypeID()) 367 log_level = CFBooleanGetValue(val) ? 1 : 0; 368 else if (CFGetTypeID(val) == CFNumberGetTypeID()) 369 CFNumberGetValue(val, kCFNumberIntType, &log_level); 370 else 371 /* ignore other types */; 372 373 CFRelease(val); 374 375 if (log_level > 0) 376 asl_level = ASL_LEVEL_NOTICE; 377} 378 379int 380_gss_mg_log_level(int level) 381{ 382 static heim_base_once_t once = HEIM_BASE_ONCE_INIT; 383 384 heim_base_once_f(&once, NULL, init_log); 385 386 return (level > log_level) ? 0 : 1; 387} 388 389void 390_gss_mg_log(int level, const char *fmt, ...) 391 HEIMDAL_PRINTF_ATTRIBUTE((printf, 2, 3)) 392{ 393 struct mg_thread_ctx *mg; 394 va_list ap; 395 396 if (!_gss_mg_log_level(level)) 397 return; 398 399 mg = _gss_mechglue_thread(); 400 if (mg == NULL) 401 return; 402 403 va_start(ap, fmt); 404 asl_vlog(mg->asl, mg->msg, asl_level, fmt, ap); 405 va_end(ap); 406} 407 408void 409_gss_mg_log_name(int level, struct _gss_name *name, gss_OID mech_type, const char *fmt, ...) 410 HEIMDAL_PRINTF_ATTRIBUTE((printf, 4, 5)) 411{ 412 struct _gss_mechanism_name *mn = NULL; 413 gssapi_mech_interface m; 414 OM_uint32 junk; 415 416 if (!_gss_mg_log_level(level)) 417 return; 418 419 m = __gss_get_mechanism(mech_type); 420 if (m == NULL) 421 return; 422 423 if (_gss_find_mn(&junk, name, mech_type, &mn) == GSS_S_COMPLETE) { 424 OM_uint32 maj_stat = GSS_S_COMPLETE; 425 gss_buffer_desc namebuf; 426 427 if (mn == NULL) { 428 namebuf.value = "no name"; 429 namebuf.length = strlen((char *)namebuf.value); 430 } else { 431 maj_stat = m->gm_display_name(&junk, mn->gmn_name, 432 &namebuf, NULL); 433 } 434 if (maj_stat == GSS_S_COMPLETE) { 435 char *str = NULL; 436 va_list ap; 437 438 va_start(ap, fmt); 439 vasprintf(&str, fmt, ap); 440 va_end(ap); 441 442 if (str) 443 _gss_mg_log(level, "%s %.*s", str, 444 (int)namebuf.length, (char *)namebuf.value); 445 free(str); 446 if (mn != NULL) 447 gss_release_buffer(&junk, &namebuf); 448 } 449 } 450 451} 452 453void 454_gss_mg_log_cred(int level, struct _gss_cred *cred, const char *fmt, ...) 455 HEIMDAL_PRINTF_ATTRIBUTE((printf, 3, 4)) 456{ 457 struct _gss_mechanism_cred *mc; 458 char *str; 459 va_list ap; 460 461 if (!_gss_mg_log_level(level)) 462 return; 463 464 va_start(ap, fmt); 465 vasprintf(&str, fmt, ap); 466 va_end(ap); 467 468 if (cred) { 469 HEIM_SLIST_FOREACH(mc, &cred->gc_mc, gmc_link) { 470 _gss_mg_log(1, "%s: %s", str, mc->gmc_mech->gm_name); 471 } 472 } else { 473 _gss_mg_log(1, "%s: GSS_C_NO_CREDENTIAL", str); 474 } 475 free(str); 476} 477 478static const char *paths[] = { 479 "/Library/KerberosPlugins/GSSAPI", 480 "/System/Library/KerberosPlugins/GSSAPI", 481 NULL 482}; 483 484static void 485load_plugins(void *ptr) 486{ 487 krb5_context context; 488 if (krb5_init_context(&context)) 489 return; 490 krb5_load_plugins(context, "gss", paths); 491 krb5_free_context(context); 492} 493 494 495void 496_gss_load_plugins(void) 497{ 498 static heim_base_once_t once = HEIM_BASE_ONCE_INIT; 499 heim_base_once_f(&once, NULL, load_plugins); 500} 501