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