xref: /freebsd/crypto/krb5/src/lib/krb5/os/init_os_ctx.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
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