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