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