xref: /illumos-gate/usr/src/uts/common/gssapi/mechs/krb5/krb5/os/init_os_ctx.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1 /*
2  * Copyright 2001-2003 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 
39 #ifdef macintosh
40 #include <PreferencesLib.h>
41 #endif /* macintosh */
42 
43 #if defined(_MSDOS) || defined(_WIN32)
44 
45 static krb5_error_code
46 get_from_windows_dir(
47     char **pname
48     )
49 {
50     UINT size = GetWindowsDirectory(0, 0);
51     *pname = malloc(size + 1 +
52                     strlen(DEFAULT_PROFILE_FILENAME) + 1);
53     if (*pname)
54     {
55         GetWindowsDirectory(*pname, size);
56         strcat(*pname, "\\");
57         strcat(*pname, DEFAULT_PROFILE_FILENAME);
58         return 0;
59     } else {
60         return KRB5_CONFIG_CANTOPEN;
61     }
62 }
63 
64 static krb5_error_code
65 get_from_module_dir(
66     char **pname
67     )
68 {
69     const DWORD size = 1024; /* fixed buffer */
70     int found = 0;
71     char *p;
72     char *name;
73     struct _stat s;
74 
75     *pname = 0;
76 
77     name = MALLOC(size);
78     if (!name)
79         return ENOMEM;
80 
81     if (!GetModuleFileName(GetModuleHandle("krb5_32"), name, size))
82         goto cleanup;
83 
84     p = name + strlen(name);
85     while ((p >= name) && (*p != '\\') && (*p != '/')) p--;
86     if (p < name)
87         goto cleanup;
88     p++;
89     strncpy(p, DEFAULT_PROFILE_FILENAME, size - (p - name));
90     name[size - 1] = 0;
91     found = !_stat(name, &s);
92 
93  cleanup:
94     if (found)
95         *pname = name;
96     else
97         if (name) FREE(name, size);
98     return 0;
99 }
100 
101 /*
102  * get_from_registry
103  *
104  * This will find a profile in the registry.  *pbuffer != 0 if we
105  * found something.  Make sure to free(*pbuffer) when done.  It will
106  * return an error code if there is an error the user should know
107  * about.  We maintain the invariant: return value != 0 =>
108  * *pbuffer == 0.
109  */
110 static krb5_error_code
111 get_from_registry(
112     char** pbuffer,
113     HKEY hBaseKey
114     )
115 {
116     HKEY hKey = 0;
117     LONG rc = 0;
118     DWORD size = 0;
119     krb5_error_code retval = 0;
120     const char *key_path = "Software\\MIT\\Kerberos5";
121     const char *value_name = "config";
122 
123     /* a wannabe assertion */
124     if (!pbuffer)
125     {
126         /*
127          * We have a programming error!  For now, we segfault :)
128          * There is no good mechanism to deal.
129          */
130     }
131     *pbuffer = 0;
132 
133     if ((rc = RegOpenKeyEx(hBaseKey, key_path, 0, KEY_QUERY_VALUE,
134                            &hKey)) != ERROR_SUCCESS)
135     {
136         /* not a real error */
137         goto cleanup;
138     }
139     rc = RegQueryValueEx(hKey, value_name, 0, 0, 0, &size);
140     if ((rc != ERROR_SUCCESS) &&  (rc != ERROR_MORE_DATA))
141     {
142         /* not a real error */
143         goto cleanup;
144     }
145     *pbuffer = MALLOC(size);
146     if (!*pbuffer)
147     {
148         retval = ENOMEM;
149         goto cleanup;
150     }
151     if ((rc = RegQueryValueEx(hKey, value_name, 0, 0, *pbuffer, &size)) !=
152         ERROR_SUCCESS)
153     {
154         /*
155          * Let's not call it a real error in case it disappears, but
156          * we need to free so that we say we did not find anything.
157          */
158         FREE(*pbuffer, size);
159         *pbuffer = 0;
160         goto cleanup;
161     }
162  cleanup:
163     if (hKey)
164         RegCloseKey(hKey);
165     if (retval && *pbuffer)
166     {
167         FREE(*pbuffer, size);
168         /* Let's say we did not find anything: */
169         *pbuffer = 0;
170     }
171     return retval;
172 }
173 
174 #endif /* _MSDOS || _WIN32 */
175 
176 #ifndef _KERNEL
177 static void
178 free_filespecs(files)
179 	profile_filespec_t *files;
180 {
181 #ifndef macintosh
182     char **cp;
183 
184     if (files == 0)
185         return;
186 
187     for (cp = files; *cp; cp++)
188 	free(*cp);
189 #endif
190     free(files);
191 }
192 
193 static krb5_error_code
194 os_get_default_config_files(pfiles, secure)
195 	profile_filespec_t ** pfiles;
196 	krb5_boolean secure;
197 {
198     profile_filespec_t* files;
199 #ifdef macintosh
200 	FSSpec*	preferencesFiles = nil;
201 	UInt32	numPreferencesFiles;
202 	FSSpec*	preferencesFilesToInit = nil;
203 	UInt32	numPreferencesFilesToInit;
204 	UInt32 i;
205 	Boolean foundPreferences = false;
206 	Boolean writtenPreferences = false;
207 	SInt16 refNum = -1;
208 	SInt32 length = 0;
209 
210 	OSErr err = KPGetListOfPreferencesFiles (
211 		secure ? kpSystemPreferences : kpUserPreferences | kpSystemPreferences,
212 		&preferencesFiles,
213 		&numPreferencesFiles);
214 
215 	if (err == noErr) {
216 		/* After we get the list of files, check whether any of them contain any useful information */
217 		for (i = 0; i < numPreferencesFiles; i++) {
218 			if (KPPreferencesFileIsReadable (&preferencesFiles [i]) == noErr) {
219 				/* It's readable, check if it has anything in the data fork */
220 				err = FSpOpenDF (&preferencesFiles [i], fsRdPerm, &refNum);
221 				if (err == noErr) {
222 					err = GetEOF (refNum, &length);
223 				}
224 
225 				if (refNum != -1) {
226 					FSClose (refNum);
227 				}
228 
229 				if (length != 0) {
230 					foundPreferences = true;
231 					break;
232 				}
233 			}
234 		}
235 
236 		if (!foundPreferences) {
237 			/* We found no profile data in any of those files; try to initialize one */
238 			/* If we are running "secure" do not try to initialize preferences */
239 			if (!secure) {
240 				err = KPGetListOfPreferencesFiles (kpUserPreferences, &preferencesFilesToInit, &numPreferencesFilesToInit);
241 				if (err == noErr) {
242 					for (i = 0; i < numPreferencesFilesToInit; i++) {
243 						if (KPPreferencesFileIsWritable (&preferencesFilesToInit [i]) == noErr) {
244 							err = noErr;
245 							/* If not readable, create it */
246 							if (KPPreferencesFileIsReadable (&preferencesFilesToInit [i]) != noErr) {
247 								err = KPCreatePreferencesFile (&preferencesFilesToInit [i]);
248 							}
249 							/* Initialize it */
250 							if (err == noErr) {
251 								err = KPInitializeWithDefaultKerberosLibraryPreferences (&preferencesFilesToInit [i]);
252 							}
253 							break;
254 						}
255 					}
256 				}
257 			}
258 		}
259 	}
260 
261 	if (err == noErr) {
262 		files = malloc ((numPreferencesFiles + 1) * sizeof (FSSpec));
263 		if (files == NULL)
264 			err = memFullErr;
265 	}
266 
267 	if (err == noErr) {
268     	for (i = 0; i < numPreferencesFiles; i++) {
269     		files [i] = preferencesFiles [i];
270     	}
271 
272     	files [numPreferencesFiles].vRefNum = 0;
273     	files [numPreferencesFiles].parID = 0;
274     	files [numPreferencesFiles].name[0] = '\0';
275 	}
276 
277 	if (preferencesFiles != nil)
278 		KPFreeListOfPreferencesFiles (preferencesFiles);
279 
280 	if (preferencesFilesToInit != nil)
281 		KPFreeListOfPreferencesFiles (preferencesFilesToInit);
282 
283 	if (err == memFullErr)
284 		return ENOMEM;
285 	else if (err != noErr)
286 		return ENOENT;
287 
288 #else /* !macintosh */
289 #if defined(_MSDOS) || defined(_WIN32)
290     krb5_error_code retval = 0;
291     char *name = 0;
292 
293     if (!secure)
294     {
295         char *env = getenv("KRB5_CONFIG");
296         if (env)
297         {
298             name = malloc(strlen(env) + 1);
299             if (!name) return ENOMEM;
300             strcpy(name, env);
301         }
302     }
303     if (!name && !secure)
304     {
305         /* HKCU */
306         retval = get_from_registry(&name, HKEY_CURRENT_USER);
307         if (retval) return retval;
308     }
309     if (!name)
310     {
311         /* HKLM */
312         retval = get_from_registry(&name, HKEY_LOCAL_MACHINE);
313         if (retval) return retval;
314     }
315     if (!name && !secure)
316     {
317         /* module dir */
318         retval = get_from_module_dir(&name);
319         if (retval) return retval;
320     }
321     if (!name)
322     {
323         /* windows dir */
324         retval = get_from_windows_dir(&name);
325     }
326     if (retval)
327         return retval;
328     if (!name)
329         return KRB5_CONFIG_CANTOPEN; /* should never happen */
330 
331     files = malloc(2 * sizeof(char *));
332     files[0] = name;
333     files[1] = 0;
334 #else /* !_MSDOS && !_WIN32 */
335     char* filepath = 0;
336     int n_entries, i;
337     int ent_len;
338     const char *s, *t;
339 
340     if (!secure) filepath = getenv("KRB5_CONFIG");
341     if (!filepath) filepath = DEFAULT_PROFILE_PATH;
342 
343     /* count the distinct filename components */
344     for(s = filepath, n_entries = 1; *s; s++) {
345         if (*s == ':')
346             n_entries++;
347     }
348 
349     /* the array is NULL terminated */
350     files = (char**) MALLOC((n_entries+1) * sizeof(char*));
351     if (files == 0)
352         return ENOMEM;
353 
354     /* measure, copy, and skip each one */
355     /*LINTED*/
356     for(s = filepath, i=0; (t = strchr(s, ':')) || (t=s+strlen(s)); s=t+1, i++)
357     {
358         ent_len = t-s;
359         files[i] = (char*) malloc(ent_len + 1);
360         if (files[i] == 0) {
361             /* if malloc fails, free the ones that worked */
362             while(--i >= 0) free(files[i]);
363             free(files);
364             return ENOMEM;
365         }
366         strncpy(files[i], s, ent_len);
367         files[i][ent_len] = 0;
368         if (*t == 0) {
369             i++;
370             break;
371         }
372     }
373     /* cap the array */
374     files[i] = 0;
375 #endif /* !_MSDOS && !_WIN32 */
376 #endif /* !macintosh */
377     *pfiles = files;
378     return 0;
379 }
380 
381 
382 /* Set the profile paths in the context. If secure is set to TRUE then
383    do not include user paths (from environment variables, etc.)
384 */
385 static krb5_error_code
386 os_init_paths(ctx)
387 	krb5_context ctx;
388 {
389     krb5_error_code	retval = 0;
390     profile_filespec_t *files = 0;
391     krb5_boolean secure = ctx->profile_secure;
392 
393 #ifdef KRB5_DNS_LOOKUP
394     ctx->profile_in_memory = 0;
395 #endif /* KRB5_DNS_LOOKUP */
396 
397     retval = os_get_default_config_files(&files, secure);
398 
399     if (!retval) {
400         retval = profile_init((const_profile_filespec_t *) files,
401 			      &ctx->profile);
402 #ifdef KRB5_DNS_LOOKUP
403         /* if none of the filenames can be opened use an empty profile */
404         if (retval == ENOENT) {
405             retval = profile_init(NULL, &ctx->profile);
406             if (!retval)
407                 ctx->profile_in_memory = 1;
408         }
409 #endif /* KRB5_DNS_LOOKUP */
410     }
411 
412     if (files)
413         free_filespecs(files);
414 
415     if (retval)
416         ctx->profile = 0;
417 
418     if (retval == ENOENT)
419         return KRB5_CONFIG_CANTOPEN;
420 
421     if ((retval == PROF_SECTION_NOTOP) ||
422         (retval == PROF_SECTION_SYNTAX) ||
423         (retval == PROF_RELATION_SYNTAX) ||
424         (retval == PROF_EXTRA_CBRACE) ||
425         (retval == PROF_MISSING_OBRACE))
426         return KRB5_CONFIG_BADFORMAT;
427 
428     return retval;
429 }
430 #endif /* !_KERNEL */
431 
432 krb5_error_code
433 krb5_os_init_context(ctx)
434 	krb5_context ctx;
435 {
436 	krb5_os_context os_ctx;
437 	krb5_error_code	retval = 0;
438 
439 	if (ctx->os_context)
440 		return 0;
441 
442 	os_ctx = MALLOC(sizeof(struct _krb5_os_context));
443 	if (!os_ctx)
444 		return ENOMEM;
445 	(void) memset(os_ctx, 0, sizeof(struct _krb5_os_context));
446 	os_ctx->magic = KV5M_OS_CONTEXT;
447 
448 	ctx->os_context = (void *) os_ctx;
449 
450 	os_ctx->time_offset = 0;
451 	os_ctx->usec_offset = 0;
452 	os_ctx->os_flags = 0;
453 	os_ctx->default_ccname = 0;
454 	os_ctx->default_ccprincipal = 0;
455 
456 #ifndef _KERNEL
457 	krb5_cc_set_default_name(ctx, NULL);
458 	retval = os_init_paths(ctx);
459 #endif
460 	/*
461 	 * If there's an error in the profile, return an error.  Just
462 	 * ignoring the error is a Bad Thing (tm).
463 	 */
464 
465 	return retval;
466 }
467 
468 #ifndef _KERNEL
469 
470 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
471 krb5_get_profile (ctx, profile)
472 	krb5_context ctx;
473 	profile_t* profile;
474 {
475     krb5_error_code	retval = 0;
476     profile_filespec_t *files = 0;
477 
478     retval = os_get_default_config_files(&files, ctx->profile_secure);
479 
480     if (!retval)
481         retval = profile_init((const_profile_filespec_t *) files, profile);
482 
483     if (files)
484         free_filespecs(files);
485 
486     if (retval == ENOENT)
487         return KRB5_CONFIG_CANTOPEN;
488 
489     if ((retval == PROF_SECTION_NOTOP) ||
490         (retval == PROF_SECTION_SYNTAX) ||
491         (retval == PROF_RELATION_SYNTAX) ||
492         (retval == PROF_EXTRA_CBRACE) ||
493         (retval == PROF_MISSING_OBRACE))
494         return KRB5_CONFIG_BADFORMAT;
495 
496     return retval;
497 }
498 
499 #endif
500 
501 #ifndef macintosh
502 #ifndef _KERNEL
503 
504 krb5_error_code
505 krb5_set_config_files(ctx, filenames)
506 	krb5_context ctx;
507 	const char **filenames;
508 {
509 	krb5_error_code retval;
510 	profile_t	profile;
511 
512 	retval = profile_init(filenames, &profile);
513 	if (retval)
514 		return retval;
515 
516 	if (ctx->profile)
517 		profile_release(ctx->profile);
518 	ctx->profile = profile;
519 
520 	return 0;
521 }
522 
523 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
524 krb5_get_default_config_files(pfilenames)
525 	char ***pfilenames;
526 {
527     if (!pfilenames)
528         return EINVAL;
529     return os_get_default_config_files(pfilenames, FALSE);
530 }
531 
532 KRB5_DLLIMP void KRB5_CALLCONV
533 krb5_free_config_files(filenames)
534 	char **filenames;
535 {
536     free_filespecs(filenames);
537 }
538 
539 #endif /* _KERNEL */
540 #endif /* macintosh */
541 
542 #ifndef _KERNEL
543 
544 krb5_error_code
545 krb5_secure_config_files(ctx)
546 	krb5_context ctx;
547 {
548 	/* Obsolete interface; always return an error.
549 
550 	   This function should be removed next time a major version
551 	   number change happens.  */
552 	krb5_error_code retval;
553 
554 	if (ctx->profile) {
555 		profile_release(ctx->profile);
556 		ctx->profile = 0;
557 	}
558 
559 	ctx->profile_secure = TRUE;
560 	retval = os_init_paths(ctx);
561 	if (retval)
562 		return retval;
563 
564 	return KRB5_OBSOLETE_FN;
565 }
566 
567 #endif /* _KERNEL */
568 
569 void
570 krb5_os_free_context(ctx)
571 	krb5_context	ctx;
572 {
573 	krb5_os_context os_ctx;
574 
575 	os_ctx = ctx->os_context;
576 
577 	if (!os_ctx)
578 		return;
579 
580         if (os_ctx->default_ccname) {
581 		FREE(os_ctx->default_ccname,
582 			strlen(os_ctx->default_ccname) + 1);
583                 os_ctx->default_ccname = 0;
584         }
585 
586 	if (os_ctx->default_ccprincipal) {
587 		krb5_free_principal (ctx, os_ctx->default_ccprincipal);
588 		os_ctx->default_ccprincipal = 0;
589 	}
590 
591 	os_ctx->magic = 0;
592 	FREE(os_ctx, sizeof(struct _krb5_os_context));
593 	ctx->os_context = 0;
594 
595 #ifndef _KERNEL
596 	if (ctx->profile) {
597 	    profile_release(ctx->profile);
598 	    ctx->profile = 0;
599 	}
600 #endif
601 }
602