1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 /* lib/krb5/os/init_os_ctx.c */ 3 /* 4 * Copyright 1994, 2007, 2008, 2009 by the Massachusetts Institute of Technology. 5 * All Rights Reserved. 6 * 7 * Export of this software from the United States of America may 8 * require a specific license from the United States Government. 9 * It is the responsibility of any person or organization contemplating 10 * export to obtain such a license before exporting. 11 * 12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 13 * distribute this software and its documentation for any purpose and 14 * without fee is hereby granted, provided that the above copyright 15 * notice appear in all copies and that both that copyright notice and 16 * this permission notice appear in supporting documentation, and that 17 * the name of M.I.T. not be used in advertising or publicity pertaining 18 * to distribution of the software without specific, written prior 19 * permission. Furthermore if you modify this software you must label 20 * your software as modified software and not distribute it in such a 21 * fashion that it might be confused with the original M.I.T. software. 22 * M.I.T. makes no representations about the suitability of 23 * this software for any purpose. It is provided "as is" without express 24 * or implied warranty. 25 */ 26 27 #define NEED_WINDOWS 28 29 #include "k5-int.h" 30 #include "os-proto.h" 31 #include "../krb/int-proto.h" 32 #include "prof_int.h" /* XXX for profile_copy, not public yet */ 33 34 #if defined(_WIN32) 35 #include <winsock.h> 36 #include <Shlobj.h> 37 38 static krb5_error_code 39 get_from_windows_dir( 40 char **pname 41 ) 42 { 43 UINT size = GetWindowsDirectory(0, 0); 44 *pname = malloc(size + strlen(DEFAULT_PROFILE_FILENAME) + 2); 45 if (*pname) { 46 GetWindowsDirectory(*pname, size); 47 strcat(*pname, "\\"); 48 strcat(*pname, DEFAULT_PROFILE_FILENAME); 49 return 0; 50 } else { 51 return KRB5_CONFIG_CANTOPEN; 52 } 53 } 54 55 static krb5_error_code 56 get_from_module_dir( 57 char **pname 58 ) 59 { 60 const DWORD size = 1024; /* fixed buffer */ 61 int found = 0; 62 char *p = NULL; 63 char *name = NULL; 64 struct _stat s; 65 66 *pname = 0; 67 68 name = malloc(size); 69 if (!name) 70 return ENOMEM; 71 72 #ifdef _WIN64 73 if (!GetModuleFileName(GetModuleHandle("krb5_64"), name, size)) 74 #else 75 if (!GetModuleFileName(GetModuleHandle("krb5_32"), name, size)) 76 #endif 77 goto cleanup; 78 79 p = name + strlen(name); 80 while ((p >= name) && (*p != '\\') && (*p != '/')) p--; 81 if (p < name) 82 goto cleanup; 83 p++; 84 strncpy(p, DEFAULT_PROFILE_FILENAME, size - (p - name)); 85 name[size - 1] = 0; 86 found = !_stat(name, &s); 87 88 cleanup: 89 if (found) 90 *pname = name; 91 else 92 free(name); 93 return 0; 94 } 95 96 /* 97 * get_from_registry 98 * 99 * This will find a profile in the registry. *pbuffer != 0 if we 100 * found something. Make sure to free(*pbuffer) when done. It will 101 * return an error code if there is an error the user should know 102 * about. We maintain the invariant: return value != 0 => 103 * *pbuffer == 0. 104 */ 105 static krb5_error_code 106 get_from_registry( 107 char** pbuffer, 108 HKEY hBaseKey 109 ) 110 { 111 HKEY hKey = 0; 112 LONG rc = 0; 113 DWORD size = 0; 114 krb5_error_code retval = 0; 115 const char *key_path = "Software\\MIT\\Kerberos5"; 116 const char *value_name = "config"; 117 118 assert(pbuffer != NULL); 119 *pbuffer = NULL; 120 121 if ((rc = RegOpenKeyEx(hBaseKey, key_path, 0, KEY_QUERY_VALUE, 122 &hKey)) != ERROR_SUCCESS) { 123 /* not a real error */ 124 goto cleanup; 125 } 126 rc = RegQueryValueEx(hKey, value_name, 0, 0, 0, &size); 127 if ((rc != ERROR_SUCCESS) && (rc != ERROR_MORE_DATA)) { 128 /* not a real error */ 129 goto cleanup; 130 } 131 *pbuffer = malloc(size); 132 if (!*pbuffer) { 133 retval = ENOMEM; 134 goto cleanup; 135 } 136 if ((rc = RegQueryValueEx(hKey, value_name, 0, 0, *pbuffer, &size)) != 137 ERROR_SUCCESS) { 138 /* 139 * Let's not call it a real error in case it disappears, but 140 * we need to free so that we say we did not find anything. 141 */ 142 free(*pbuffer); 143 *pbuffer = 0; 144 goto cleanup; 145 } 146 cleanup: 147 if (hKey) 148 RegCloseKey(hKey); 149 if (retval && *pbuffer) { 150 free(*pbuffer); 151 /* Let's say we did not find anything: */ 152 *pbuffer = 0; 153 } 154 return retval; 155 } 156 157 /* 158 * get_from_known_folder 159 * 160 * This will find a profile in the specified known folder (e.g. CSIDL_APPDATA). 161 * *pbuffer != 0 if we found something. Make sure to free(*pbuffer) when done. 162 * It will return an error code if there is an error the user should know 163 * about. We maintain the invariant: return value != 0 => 164 * *pbuffer == 0. 165 */ 166 static krb5_error_code 167 get_from_known_folder( 168 int folderId, 169 char** pbuffer 170 ) 171 { 172 char szPath[MAX_PATH]; 173 const char * software_suffix = "\\MIT\\Kerberos5"; 174 krb5_error_code retval = 0; 175 size_t size; 176 struct _stat s; 177 178 assert(pbuffer); 179 *pbuffer = NULL; 180 181 if (SUCCEEDED(SHGetFolderPath(NULL, 182 folderId /*|CSIDL_FLAG_CREATE*/, 183 NULL, 184 SHGFP_TYPE_CURRENT, 185 szPath))) { 186 size = strlen(software_suffix) + strlen("\\" DEFAULT_PROFILE_FILENAME) + strlen(szPath); 187 if ((size + 1) >= sizeof(szPath)) { 188 goto cleanup; 189 } 190 strlcat(szPath, software_suffix, sizeof(szPath)); 191 strlcat(szPath, "\\", sizeof(szPath)); 192 strlcat(szPath, DEFAULT_PROFILE_FILENAME, sizeof(szPath)); 193 } else { 194 /* Might want to deliberate a bit better why we failed. 195 But for the time being this is not an error */ 196 goto cleanup; 197 } 198 199 if (_stat(szPath, &s)) { 200 goto cleanup; 201 } 202 203 *pbuffer = malloc(size + 1); 204 if (!*pbuffer) { 205 retval = ENOMEM; 206 goto cleanup; 207 } 208 209 strlcpy (*pbuffer, szPath, size + 1); 210 211 cleanup: 212 if (retval && *pbuffer) { 213 free(*pbuffer); 214 /* Let's say we did not find anything: */ 215 *pbuffer = 0; 216 } 217 return retval; 218 } 219 220 #endif /* _WIN32 */ 221 222 static void 223 free_filespecs(profile_filespec_t *files) 224 { 225 char **cp; 226 227 if (files == 0) 228 return; 229 230 for (cp = files; *cp; cp++) 231 free(*cp); 232 free(files); 233 } 234 235 /* This function is needed by KfM's KerberosPreferences API 236 * because it needs to be able to specify "secure" */ 237 static krb5_error_code 238 os_get_default_config_files(profile_filespec_t **pfiles, krb5_boolean secure) 239 { 240 profile_filespec_t* files; 241 #if defined(_WIN32) 242 krb5_error_code retval = 0; 243 char *name = 0; 244 245 if (!secure) { 246 char *env = secure_getenv("KRB5_CONFIG"); 247 if (env) { 248 name = strdup(env); 249 if (!name) return ENOMEM; 250 } 251 } 252 if (!name && !secure) { 253 /* HKCU */ 254 retval = get_from_registry(&name, HKEY_CURRENT_USER); 255 if (retval) return retval; 256 } 257 if (!name) { 258 /* HKLM */ 259 retval = get_from_registry(&name, HKEY_LOCAL_MACHINE); 260 if (retval) return retval; 261 } 262 263 if (!name && !secure) { 264 retval = get_from_known_folder(CSIDL_APPDATA, &name); 265 if (retval) return retval; 266 } 267 268 if (!name) { 269 retval = get_from_known_folder(CSIDL_COMMON_APPDATA, &name); 270 if (retval) return retval; 271 } 272 273 if (!name && !secure) { 274 /* module dir */ 275 retval = get_from_module_dir(&name); 276 if (retval) return retval; 277 } 278 if (!name) { 279 /* windows dir */ 280 retval = get_from_windows_dir(&name); 281 } 282 if (retval) 283 return retval; 284 if (!name) 285 return KRB5_CONFIG_CANTOPEN; /* should never happen */ 286 287 files = malloc(2 * sizeof(char *)); 288 if (!files) 289 return ENOMEM; 290 files[0] = name; 291 files[1] = 0; 292 #else /* !_WIN32 */ 293 char* filepath = 0; 294 int n_entries, i; 295 unsigned int ent_len; 296 const char *s, *t; 297 298 if (secure) { 299 filepath = DEFAULT_SECURE_PROFILE_PATH; 300 } else { 301 filepath = secure_getenv("KRB5_CONFIG"); 302 if (!filepath) filepath = DEFAULT_PROFILE_PATH; 303 } 304 305 /* count the distinct filename components */ 306 for(s = filepath, n_entries = 1; *s; s++) { 307 if (*s == ':') 308 n_entries++; 309 } 310 311 /* the array is NULL terminated */ 312 files = (char**) malloc((n_entries+1) * sizeof(char*)); 313 if (files == 0) 314 return ENOMEM; 315 316 /* measure, copy, and skip each one */ 317 for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++) { 318 ent_len = t-s; 319 files[i] = (char*) malloc(ent_len + 1); 320 if (files[i] == 0) { 321 /* if malloc fails, free the ones that worked */ 322 while(--i >= 0) free(files[i]); 323 free(files); 324 return ENOMEM; 325 } 326 strncpy(files[i], s, ent_len); 327 files[i][ent_len] = 0; 328 if (*t == 0) { 329 i++; 330 break; 331 } 332 } 333 /* cap the array */ 334 files[i] = 0; 335 #endif /* !_WIN32 */ 336 *pfiles = (profile_filespec_t *)files; 337 return 0; 338 } 339 340 static krb5_error_code 341 add_kdc_config_file(profile_filespec_t **pfiles) 342 { 343 char *file = NULL; 344 size_t count = 0; 345 profile_filespec_t *newfiles; 346 347 file = secure_getenv(KDC_PROFILE_ENV); 348 if (file == NULL) 349 file = DEFAULT_KDC_PROFILE; 350 351 for (count = 0; (*pfiles)[count]; count++) 352 ; 353 count += 2; 354 newfiles = malloc(count * sizeof(*newfiles)); 355 if (newfiles == NULL) 356 return ENOMEM; 357 memcpy(newfiles + 1, *pfiles, (count-1) * sizeof(*newfiles)); 358 newfiles[0] = strdup(file); 359 if (newfiles[0] == NULL) { 360 int e = ENOMEM; 361 free(newfiles); 362 return e; 363 } 364 free(*pfiles); 365 *pfiles = newfiles; 366 return 0; 367 } 368 369 370 /* Set the profile paths in the context. If secure is set to TRUE 371 then do not include user paths (from environment variables, etc). 372 If kdc is TRUE, include kdc.conf from wherever we expect to find 373 it. */ 374 static krb5_error_code 375 os_init_paths(krb5_context ctx, krb5_boolean kdc) 376 { 377 krb5_error_code retval = 0; 378 profile_filespec_t *files = 0; 379 krb5_boolean secure = ctx->profile_secure; 380 381 retval = os_get_default_config_files(&files, secure); 382 383 if (retval == 0 && kdc) 384 retval = add_kdc_config_file(&files); 385 386 if (!retval) { 387 retval = profile_init_flags((const_profile_filespec_t *) files, 388 PROFILE_INIT_ALLOW_MODULE, &ctx->profile); 389 390 /* If none of the filenames can be opened, use an empty profile. */ 391 if (retval == ENOENT) 392 retval = profile_init(NULL, &ctx->profile); 393 } 394 395 if (files) 396 free_filespecs(files); 397 398 if (retval) 399 ctx->profile = 0; 400 401 if (retval == ENOENT) 402 return KRB5_CONFIG_CANTOPEN; 403 404 if ((retval == PROF_SECTION_NOTOP) || 405 (retval == PROF_SECTION_SYNTAX) || 406 (retval == PROF_RELATION_SYNTAX) || 407 (retval == PROF_EXTRA_CBRACE) || 408 (retval == PROF_MISSING_OBRACE)) 409 return KRB5_CONFIG_BADFORMAT; 410 411 return retval; 412 } 413 414 krb5_error_code 415 k5_os_init_context(krb5_context ctx, profile_t profile, krb5_flags flags) 416 { 417 krb5_os_context os_ctx; 418 krb5_error_code retval = 0; 419 #ifdef _WIN32 420 WORD wVersionRequested; 421 WSADATA wsaData; 422 #endif /* _WIN32 */ 423 424 os_ctx = &ctx->os_context; 425 os_ctx->magic = KV5M_OS_CONTEXT; 426 os_ctx->time_offset = 0; 427 os_ctx->usec_offset = 0; 428 os_ctx->os_flags = 0; 429 os_ctx->default_ccname = 0; 430 431 PLUGIN_DIR_INIT(&ctx->libkrb5_plugins); 432 ctx->preauth_context = NULL; 433 434 /* Use the profile we were handed, or create one from config files. */ 435 if (profile) 436 retval = profile_copy(profile, &ctx->profile); 437 else 438 retval = os_init_paths(ctx, (flags & KRB5_INIT_CONTEXT_KDC) != 0); 439 if (retval) 440 return retval; 441 442 #ifdef _WIN32 443 /* We initialize winsock to version 1.1 but 444 * we do not care if we succeed or fail. 445 */ 446 wVersionRequested = 0x0101; 447 WSAStartup (wVersionRequested, &wsaData); 448 #endif /* _WIN32 */ 449 450 return 0; 451 } 452 453 krb5_error_code KRB5_CALLCONV 454 krb5_get_profile (krb5_context ctx, profile_t *profile) 455 { 456 return profile_copy (ctx->profile, profile); 457 } 458 459 krb5_error_code 460 krb5_set_config_files(krb5_context ctx, const char **filenames) 461 { 462 krb5_error_code retval = 0; 463 profile_t profile; 464 465 retval = profile_init_flags(filenames, PROFILE_INIT_ALLOW_MODULE, 466 &profile); 467 if (retval) 468 return retval; 469 470 if (ctx->profile) 471 profile_release(ctx->profile); 472 ctx->profile = profile; 473 474 return 0; 475 } 476 477 krb5_error_code KRB5_CALLCONV 478 krb5_get_default_config_files(char ***pfilenames) 479 { 480 if (!pfilenames) 481 return EINVAL; 482 return os_get_default_config_files(pfilenames, FALSE); 483 } 484 485 void KRB5_CALLCONV 486 krb5_free_config_files(char **filenames) 487 { 488 free_filespecs(filenames); 489 } 490 491 void 492 k5_os_free_context(krb5_context ctx) 493 { 494 krb5_os_context os_ctx; 495 496 os_ctx = &ctx->os_context; 497 498 if (os_ctx->default_ccname) { 499 free(os_ctx->default_ccname); 500 os_ctx->default_ccname = 0; 501 } 502 503 os_ctx->magic = 0; 504 505 if (ctx->profile) { 506 profile_release(ctx->profile); 507 ctx->profile = 0; 508 } 509 510 if (ctx->preauth_context) { 511 k5_free_preauth_context(ctx); 512 ctx->preauth_context = NULL; 513 } 514 krb5int_close_plugin_dirs (&ctx->libkrb5_plugins); 515 516 #ifdef _WIN32 517 WSACleanup(); 518 #endif /* _WIN32 */ 519 } 520