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