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