1 /*
2  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 #pragma ident	"%Z%%M%	%I%	%E% SMI"
7 
8 /*
9  * lib/krb5/os/init_ctx.c
10  *
11  * Copyright 1994 by the Massachusetts Institute of Technology.
12  * All Rights Reserved.
13  *
14  * Export of this software from the United States of America may
15  *   require a specific license from the United States Government.
16  *   It is the responsibility of any person or organization contemplating
17  *   export to obtain such a license before exporting.
18  *
19  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
20  * distribute this software and its documentation for any purpose and
21  * without fee is hereby granted, provided that the above copyright
22  * notice appear in all copies and that both that copyright notice and
23  * this permission notice appear in supporting documentation, and that
24  * the name of M.I.T. not be used in advertising or publicity pertaining
25  * to distribution of the software without specific, written prior
26  * permission.  Furthermore if you modify this software you must label
27  * your software as modified software and not distribute it in such a
28  * fashion that it might be confused with the original M.I.T. software.
29  * M.I.T. makes no representations about the suitability of
30  * this software for any purpose.  It is provided "as is" without express
31  * or implied warranty.
32  *
33  * krb5_init_contex()
34  */
35 
36 #define NEED_WINDOWS
37 #include <k5-int.h>
38 #ifndef _KERNEL
39 #include "os-proto.h"
40 #endif
41 
42 /* SUNW14resync: Solaris kerb does not need this feature in this file */
43 #ifdef USE_LOGIN_LIBRARY
44 #undef USE_LOGIN_LIBRARY
45 #endif
46 
47 #ifdef USE_LOGIN_LIBRARY
48 #include "KerberosLoginPrivate.h"
49 #endif
50 
51 #if defined(_WIN32)
52 
53 static krb5_error_code
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
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
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
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
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 (secure || !__KLAllowHomeDirectoryAccess ()) {
259 #else
260     if (secure) {
261 #endif
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 
306 /* Set the profile paths in the context. If secure is set to TRUE then
307    do not include user paths (from environment variables, etc.)
308 */
309 static krb5_error_code
310 os_init_paths(krb5_context ctx)
311 {
312     krb5_error_code	retval = 0;
313     profile_filespec_t *files = 0;
314     krb5_boolean secure = ctx->profile_secure;
315 
316 #ifdef KRB5_DNS_LOOKUP
317     ctx->profile_in_memory = 0;
318 #endif /* KRB5_DNS_LOOKUP */
319 
320     retval = os_get_default_config_files(&files, secure);
321 
322     if (!retval) {
323         retval = profile_init((const_profile_filespec_t *) files,
324 			      &ctx->profile);
325 
326 #ifdef KRB5_DNS_LOOKUP
327         /* if none of the filenames can be opened use an empty profile */
328         if (retval == ENOENT) {
329             retval = profile_init(NULL, &ctx->profile);
330             if (!retval)
331                 ctx->profile_in_memory = 1;
332         }
333 #endif /* KRB5_DNS_LOOKUP */
334     }
335 
336     if (files)
337         free_filespecs(files);
338 
339     if (retval)
340         ctx->profile = 0;
341 
342     if (retval == ENOENT)
343         return KRB5_CONFIG_CANTOPEN;
344 
345     if ((retval == PROF_SECTION_NOTOP) ||
346         (retval == PROF_SECTION_SYNTAX) ||
347         (retval == PROF_RELATION_SYNTAX) ||
348         (retval == PROF_EXTRA_CBRACE) ||
349         (retval == PROF_MISSING_OBRACE))
350         return KRB5_CONFIG_BADFORMAT;
351 
352     return retval;
353 }
354 #endif /* !_KERNEL */
355 
356 krb5_error_code
357 krb5_os_init_context(krb5_context ctx)
358 {
359 	krb5_os_context os_ctx;
360 	krb5_error_code	retval = 0;
361 
362 	os_ctx = ctx->os_context;
363 	os_ctx->magic = KV5M_OS_CONTEXT;
364 
365 	os_ctx->time_offset = 0;
366 	os_ctx->usec_offset = 0;
367 	os_ctx->os_flags = 0;
368 	os_ctx->default_ccname = 0;
369 
370 #ifndef _KERNEL
371 	krb5_cc_set_default_name(ctx, NULL);
372 
373 	retval = os_init_paths(ctx);
374 #endif
375 	/*
376 	 * If there's an error in the profile, return an error.  Just
377 	 * ignoring the error is a Bad Thing (tm).
378 	 */
379 
380 	return retval;
381 }
382 
383 #ifndef _KERNEL
384 
385 krb5_error_code KRB5_CALLCONV
386 krb5_get_profile (krb5_context ctx, profile_t *profile)
387 {
388     krb5_error_code	retval = 0;
389     profile_filespec_t *files = 0;
390 
391     retval = os_get_default_config_files(&files, ctx->profile_secure);
392 
393     if (!retval) {
394 	retval = profile_init((const_profile_filespec_t *) files,
395                                profile);
396     }
397 
398     if (files)
399         free_filespecs(files);
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 #endif
415 
416 #ifndef _KERNEL
417 
418 krb5_error_code
419 krb5_set_config_files(krb5_context ctx, const char **filenames)
420 {
421 	krb5_error_code retval;
422 	profile_t	profile;
423 
424 	retval = profile_init(filenames, &profile);
425 	if (retval)
426 		return retval;
427 
428 	if (ctx->profile)
429 		profile_release(ctx->profile);
430 	ctx->profile = profile;
431 
432 	return 0;
433 }
434 
435 krb5_error_code KRB5_CALLCONV
436 krb5_get_default_config_files(char ***pfilenames)
437 {
438     if (!pfilenames)
439         return EINVAL;
440     return os_get_default_config_files(pfilenames, FALSE);
441 }
442 
443 void KRB5_CALLCONV
444 krb5_free_config_files(char **filenames)
445 {
446     free_filespecs(filenames);
447 }
448 
449 #endif /* _KERNEL */
450 
451 #ifndef _KERNEL
452 
453 krb5_error_code
454 krb5_secure_config_files(krb5_context ctx)
455 {
456 	/* Obsolete interface; always return an error.
457 
458 	   This function should be removed next time a major version
459 	   number change happens.  */
460 	krb5_error_code retval;
461 
462 	if (ctx->profile) {
463 		profile_release(ctx->profile);
464 		ctx->profile = 0;
465 	}
466 
467 	ctx->profile_secure = TRUE;
468 	retval = os_init_paths(ctx);
469 	if (retval)
470 		return retval;
471 
472 	return KRB5_OBSOLETE_FN;
473 }
474 
475 #endif /* _KERNEL */
476 
477 void
478 krb5_os_free_context(krb5_context ctx)
479 {
480 	krb5_os_context os_ctx;
481 
482 	os_ctx = ctx->os_context;
483 
484         if (os_ctx->default_ccname) {
485 		FREE(os_ctx->default_ccname,
486 			strlen(os_ctx->default_ccname) + 1);
487                 os_ctx->default_ccname = 0;
488         }
489 
490 	os_ctx->magic = 0;
491 
492 #ifndef _KERNEL
493 	if (ctx->profile) {
494 	    profile_release(ctx->profile);
495 	    ctx->profile = 0;
496 	}
497 #endif
498 }
499