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
get_from_windows_dir(char ** pname)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
get_from_module_dir(char ** pname)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
get_from_registry(char ** pbuffer,HKEY hBaseKey)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
get_from_known_folder(int folderId,char ** pbuffer)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
free_filespecs(profile_filespec_t * files)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
os_get_default_config_files(profile_filespec_t ** pfiles,krb5_boolean secure)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
add_kdc_config_file(profile_filespec_t ** pfiles)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
os_init_paths(krb5_context ctx,krb5_boolean kdc)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
k5_os_init_context(krb5_context ctx,profile_t profile,krb5_flags flags)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
krb5_get_profile(krb5_context ctx,profile_t * profile)454 krb5_get_profile (krb5_context ctx, profile_t *profile)
455 {
456 return profile_copy (ctx->profile, profile);
457 }
458
459 krb5_error_code
krb5_set_config_files(krb5_context ctx,const char ** filenames)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
krb5_get_default_config_files(char *** pfilenames)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
krb5_free_config_files(char ** filenames)486 krb5_free_config_files(char **filenames)
487 {
488 free_filespecs(filenames);
489 }
490
491 void
k5_os_free_context(krb5_context ctx)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