1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * prof_solaris.c:
28  * Abstracted contract private interfaces for configuring krb5.conf(4).
29  */
30 
31 #include <ctype.h>
32 #include "prof_int.h"
33 #include "k5-int.h"
34 
35 errcode_t
36 __profile_iter_name_value(profile_t profile, char *section, char *key,
37 	char ***retvals)
38 {
39 	const char	*hierarchy[4];
40 	errcode_t	code, code2;
41 	char		*name = NULL, *value = NULL, **ret_values = NULL;
42 	void		*state = NULL;
43 	struct profile_string_list values;
44 	boolean_t	found = FALSE;
45 
46 	hierarchy[0] = section;
47 	hierarchy[1] = NULL;
48 
49 	if (code = init_list(&values))
50 		return (code);
51 
52 	code = profile_iterator_create(profile, hierarchy,
53 	    PROFILE_ITER_LIST_SECTION, &state);
54 	while (code == 0) {
55 		code = profile_iterator(&state, &name, &value);
56 		if (code == 0 && name != NULL) {
57 			if ((key == NULL) || (strcmp(value, key) == 0)) {
58 				code2 = add_to_list(&values, name);
59 				if (code2 != 0) {
60 					end_list(&values, &ret_values);
61 					profile_free_list(ret_values);
62 					code2 = code;
63 					goto cleanup;
64 				}
65 				found = TRUE;
66 			}
67 		}
68 		if (name != NULL) {
69 			profile_release_string(name);
70 			name = NULL;
71 		}
72 		if (value != NULL) {
73 			profile_release_string(value);
74 			value = NULL;
75 		}
76 	}
77 	code = 0;
78 	if (found == TRUE)
79 		end_list(&values, &ret_values);
80 
81 cleanup:
82 
83 	if (state != NULL)
84 		profile_iterator_free(&state);
85 	if (name != NULL)
86 		profile_release_string(name);
87 	if (value != NULL)
88 		profile_release_string(value);
89 
90 	*retvals = ret_values;
91 
92 	return (code);
93 }
94 
95 errcode_t
96 __profile_get_domain_realm(profile_t profile, char *realm, char ***domains)
97 {
98 	if (profile == NULL || realm == NULL || domains == NULL)
99 		return (EINVAL);
100 
101 	return (__profile_iter_name_value(profile, "domain_realm", realm,
102 	    domains));
103 }
104 
105 errcode_t
106 __profile_set_appdefaults(profile_t profile)
107 {
108 	const char	*hierarchy[4];
109 	errcode_t	code;
110 
111 	if (profile == NULL)
112 		return (EINVAL);
113 
114 	hierarchy[0] = "appdefaults";
115 	hierarchy[1] = "kinit";
116 	hierarchy[3] = NULL;
117 
118 	hierarchy[2] = "renewable";
119 
120 	/*
121 	 * Not fatal if this fails, continue on.
122 	 */
123 	(void) profile_clear_relation(profile, hierarchy);
124 
125 	code = profile_add_relation(profile, hierarchy, "true");
126 	if (code != 0)
127 		return (code);
128 
129 	hierarchy[2] = "forwardable";
130 
131 	(void) profile_clear_relation(profile, hierarchy);
132 
133 	code = profile_add_relation(profile, hierarchy, "true");
134 
135 	return (code);
136 }
137 
138 errcode_t
139 __profile_set_logging(profile_t profile)
140 {
141 	const char	*hierarchy[4];
142 	errcode_t	code;
143 
144 	if (profile == NULL)
145 		return (EINVAL);
146 
147 	hierarchy[0] = "logging";
148 	hierarchy[2] = NULL;
149 	hierarchy[3] = NULL;
150 
151 	hierarchy[1] = "default";
152 
153 	/*
154 	 * Not fatal if this fails, continue on.
155 	 */
156 	(void) profile_clear_relation(profile, hierarchy);
157 
158 	code = profile_add_relation(profile, hierarchy,
159 	    "FILE:/var/krb5/kdc.log");
160 	if (code != 0)
161 		return (code);
162 
163 	hierarchy[1] = "kdc";
164 
165 	(void) profile_clear_relation(profile, hierarchy);
166 
167 	code = profile_add_relation(profile, hierarchy,
168 	    "FILE:/var/krb5/kdc.log");
169 	if (code != 0)
170 		return (code);
171 
172 	hierarchy[1] = "kdc_rotate";
173 
174 	hierarchy[2] = "period";
175 
176 	(void) profile_clear_relation(profile, hierarchy);
177 
178 	code = profile_add_relation(profile, hierarchy, "1d");
179 	if (code != 0)
180 		return (code);
181 
182 	hierarchy[2] = "versions";
183 
184 	(void) profile_clear_relation(profile, hierarchy);
185 
186 	code = profile_add_relation(profile, hierarchy, "10");
187 
188 	return (code);
189 }
190 
191 errcode_t
192 __profile_set_libdefaults(profile_t profile, char *realm)
193 {
194 	const char	*hierarchy[4];
195 	errcode_t	code;
196 
197 	if (profile == NULL || realm == NULL)
198 		return (EINVAL);
199 
200 	hierarchy[0] = "libdefaults";
201 	hierarchy[1] = "default_realm";
202 	hierarchy[2] = NULL;
203 
204 	/*
205 	 * Not fatal if this fails, continue on.
206 	 */
207 	(void) profile_clear_relation(profile, hierarchy);
208 
209 	code = profile_add_relation(profile, hierarchy, realm);
210 
211 	return (code);
212 }
213 
214 errcode_t
215 __profile_set_kdc(profile_t profile, char *realm, char *kdc,
216 	boolean_t overwrite)
217 {
218 	const char	*hierarchy[4];
219 	errcode_t	code;
220 
221 	if (profile == NULL || realm == NULL || kdc == NULL)
222 		return (EINVAL);
223 
224 	hierarchy[0] = "realms";
225 	hierarchy[1] = realm;
226 	hierarchy[3] = NULL;
227 
228 	hierarchy[2] = "kdc";
229 
230 	if (overwrite == TRUE) {
231 		/*
232 		 * Not fatal if this fails, continue on.
233 		 */
234 		(void) profile_clear_relation(profile, hierarchy);
235 	}
236 
237 	code = profile_add_relation(profile, hierarchy, kdc);
238 
239 	return (code);
240 }
241 
242 /*
243  * errcode_t __profile_release(profile_t profile)
244  *
245  * where profile was the pointer passed back by __profile_init
246  * Note: used to commit the associated profile to the backing store
247  * (e.g. file) and free profile memory
248  * Note: that this function returns an error code which profile_release
249  * does not.  With the error code, the application can determine if they
250  * need to free the resulting profile information in memory
251  */
252 errcode_t
253 __profile_release(profile_t profile)
254 {
255 	prf_file_t	p, next;
256 	errcode_t	code;
257 
258 	if (profile == NULL || profile->magic != PROF_MAGIC_PROFILE)
259 		return (EINVAL);
260 
261 	for (p = profile->first_file; p; p = next) {
262 		next = p->next;
263 		if ((code = profile_close_file(p)) != 0)
264 			return (code);
265 	}
266 	profile->magic = 0;
267 	free(profile);
268 
269 	return (0);
270 }
271 
272 /*
273  * void __profile_abandon(profile_t profile)
274  *
275  * where profile was the pointer passed back by __profile_init
276  * Note: used to free any profile information in memory.  Typically can
277  * be used in conjunction with __profile_release upon error
278  */
279 void
280 __profile_abandon(profile_t profile)
281 {
282 	profile_abandon(profile);
283 }
284 
285 /*
286  * errcode_t __profile_add_domain_mapping(profile_t profile, char *domain,
287  *	char *realm)
288  *
289  * where profile was the pointer passed back by __profile_init
290  * where domain is the domain name of the associated realm name
291  * where realm is the corresponding realm name for the domain
292  */
293 errcode_t
294 __profile_add_domain_mapping(profile_t profile, char *domain, char *realm)
295 {
296 	const char	*hierarchy[4];
297 	errcode_t	code = 0;
298 
299 	if (profile == NULL || domain == NULL || realm == NULL)
300 		return (EINVAL);
301 
302 	hierarchy[0] = "domain_realm";
303 	hierarchy[1] = domain;
304 	hierarchy[2] = NULL;
305 
306 	/*
307 	 * Not fatal if relation can't be cleared, continue on.
308 	 */
309 	(void) profile_clear_relation(profile, hierarchy);
310 
311 	code = profile_add_relation(profile, hierarchy, realm);
312 
313 	return (code);
314 }
315 
316 /*
317  * errcode_t __profile_remove_domain_mapping(profile_t profile,	char *realm)
318  *
319  * where profile was the pointer passed back by __profile_init
320  * where domain is the domain name of the associated realm name
321  * where realm is the corresponding realm name for the domain
322  * Note: for the remove function, all matching domain - realm mappings
323  * will be removed for realm
324  */
325 errcode_t
326 __profile_remove_domain_mapping(profile_t profile, char *realm)
327 {
328 	const char	*hierarchy[4];
329 	errcode_t	code;
330 	char		**domains = NULL, **domain = NULL;
331 
332 	if (profile == NULL || realm == NULL)
333 		return (EINVAL);
334 
335 	hierarchy[0] = "domain_realm";
336 	hierarchy[1] = NULL;
337 	hierarchy[2] = NULL;
338 
339 	code = __profile_get_domain_realm(profile, realm, &domains);
340 	if (code == 0 && domains != NULL) {
341 		for (domain = domains; *domain; domain++) {
342 			hierarchy[1] = *domain;
343 			code = profile_clear_relation(profile, hierarchy);
344 			if (code != 0)
345 				goto error;
346 		}
347 	}
348 
349 error:
350 	if (domains != NULL)
351 		profile_free_list(domains);
352 
353 	return (code);
354 }
355 
356 /*
357  * errcode_t __profile_get_realm_entry(profile_t profile, char *realm,
358  *	char *name, char ***ret_value)
359  *
360  * where profile was the pointer passed back by __profile_init
361  * where realm is the target realm for lookup
362  * where name is the name in the realm section requested
363  * where value is a string array of any matching values assigned to name.
364  * The array is terminated with a NULL pointer.
365  * Note: if no name has been configured and a profile does exist
366  * then value is set to NULL
367  */
368 errcode_t
369 __profile_get_realm_entry(profile_t profile, char *realm, char *name,
370 	char ***ret_value)
371 {
372 	const char	*hierarchy[4];
373 	errcode_t	code;
374 	char		**values = NULL;
375 
376 	if (profile == NULL || realm == NULL || name == NULL ||
377 	    ret_value == NULL)
378 		return (EINVAL);
379 
380 	hierarchy[0] = "realms";
381 	hierarchy[1] = realm;
382 	hierarchy[2] = name;
383 	hierarchy[3] = NULL;
384 
385 	code = profile_get_values(profile, hierarchy, &values);
386 	if (code == 0 && values != NULL)
387 		*ret_value = values;
388 
389 	code = PROF_NO_RELATION ? 0 : code;
390 
391 	return (code);
392 }
393 
394 /*
395  * errcode_t __profile_add_realm_entry(profile_t profile, char *realm,
396  *	char *name, char **value)
397  *
398  * where profile was the pointer passed back by __profile_init
399  * where realm is the target realm for the name-value pair
400  * where name is the name in the realm subsection to add
401  * where value is a string array values to assigned to name.  The array is
402  * terminated with a NULL pointer.
403  * Note: if the realm subsection does no exist then an error is returned
404  * Note: if the name already exists the set is overwritten with the values
405  * passed
406  */
407 errcode_t
408 __profile_add_realm_entry(profile_t profile, char *realm, char *name,
409 	char **values)
410 {
411 	const char	*hierarchy[4];
412 	errcode_t	code;
413 	char		**tvalue = NULL;
414 
415 	if (profile == NULL || realm == NULL || name == NULL || values == NULL)
416 		return (EINVAL);
417 
418 	hierarchy[0] = "realms";
419 	hierarchy[1] = realm;
420 	hierarchy[2] = name;
421 	hierarchy[3] = NULL;
422 
423 	/*
424 	 * Not fatal if this fails, continue on.
425 	 */
426 	(void) profile_clear_relation(profile, hierarchy);
427 
428 	for (tvalue = values; *tvalue; tvalue++) {
429 
430 		code = profile_add_relation(profile, hierarchy, *tvalue);
431 		if (code != 0)
432 			return (code);
433 	}
434 
435 	return (0);
436 }
437 
438 /*
439  * errcode_t __profile_get_default_realm(profile_t profile, char **realm)
440  *
441  * where profile was the pointer passed back by __profile_init
442  * where realm is the default_realm configured for the system
443  * Note: if no default_realm has been configured and a profile does exist
444  * then realm is set to NULL
445  */
446 errcode_t
447 __profile_get_default_realm(profile_t profile, char **realm)
448 {
449 	errcode_t	code;
450 	char		*value = NULL;
451 
452 	if (profile == NULL || realm == NULL)
453 		return (EINVAL);
454 
455 	code = profile_get_string(profile, "libdefaults", "default_realm", 0, 0,
456 	    &value);
457 	if (code == 0 && value != NULL)
458 		*realm = value;
459 
460 	code = PROF_NO_RELATION ? 0 : code;
461 
462 	return (code);
463 }
464 
465 /*
466  * errcode_t __profile_get_realms(profile_t profile, char ***realms)
467  *
468  * where profile was the pointer passed back by __profile_init
469  * where realms is a string array of realm names currently configured.
470  * The array is terminated with a NULL pointer.
471  * Note: if no realms have been configured and a profile does exist then
472  * realms is set to NULL
473  */
474 errcode_t
475 __profile_get_realms(profile_t profile, char ***realms)
476 {
477 
478 	if (profile == NULL || realms == NULL)
479 		return (EINVAL);
480 
481 	return (__profile_iter_name_value(profile, "realms", NULL, realms));
482 }
483 
484 /*
485  * errcode_t __profile_add_realm(profile_t profile, char *realm,
486  *	char *master, char **kdcs, boolean_t set_change, boolean_t
487  *	default_realm)
488  *
489  * where profile was the pointer passed back by __profile_init
490  * where realm is the realm name associated with the configuration
491  * where master is the server that is assigned to admin_server
492  * where kdcs is a string array of KDCs used to populate the kdc set.
493  * The array is terminated with a NULL pointer.
494  * where set_change, if set, will use the SET_CHANGE protocol for password
495  * modifications.  RPCSEC_GSS is set by default
496  * where default_realm, if set, will assign the realm to default_realm
497  * Note: the ordering of kdcs is determined by the server's position in the
498  * array
499  * Note: kdcs must be assigned a value, even if it is the same value as the
500  * master.
501  */
502 errcode_t
503 __profile_add_realm(profile_t profile, char *realm, char *master, char **kdcs,
504 	boolean_t set_change, boolean_t default_realm)
505 {
506 	const char	*hierarchy[4];
507 	errcode_t	code;
508 	boolean_t	ow = TRUE;
509 	char		**tkdcs;
510 
511 	if (profile == NULL || realm == NULL || master == NULL || kdcs == NULL)
512 		return (EINVAL);
513 
514 	/*
515 	 * Sets the default realm to realm if default_realm flag is set.
516 	 */
517 	if (default_realm == TRUE) {
518 		if (code = __profile_set_libdefaults(profile, realm))
519 			return (code);
520 	}
521 
522 	hierarchy[0] = "realms";
523 	hierarchy[1] = realm;
524 	hierarchy[3] = NULL;
525 
526 	hierarchy[2] = "admin_server";
527 
528 	/*
529 	 * Not fatal if this fails, therefore return code is not checked.
530 	 */
531 	(void) profile_clear_relation(profile, hierarchy);
532 
533 	if (code = profile_add_relation(profile, hierarchy, master))
534 		return (code);
535 
536 	/*
537 	 * If not set then defaults to undefined, which defaults to RPCSEC_GSS.
538 	 */
539 	if (set_change == TRUE) {
540 		hierarchy[2] = "kpasswd_protocol";
541 
542 		(void) profile_clear_relation(profile, hierarchy);
543 
544 		code = profile_add_relation(profile, hierarchy, "SET_CHANGE");
545 		if (code != 0)
546 			return (code);
547 	}
548 
549 	for (tkdcs = kdcs; *tkdcs; tkdcs++) {
550 		if (code = __profile_set_kdc(profile, realm, *tkdcs, ow))
551 			return (code);
552 		ow = FALSE;
553 	}
554 
555 	code = __profile_set_logging(profile);
556 	if (code != 0)
557 		return (code);
558 
559 	code = __profile_set_appdefaults(profile);
560 
561 	return (code);
562 }
563 
564 /*
565  * errcode_t __profile_remove_xrealm_mapping(profile_t profile, char *realm)
566  *
567  * where profile was the pointer passed back by __profile_init
568  * where source is the source realm for the capath
569  * where target is the target realm for the capath
570  * where inter is the intermediate realm between the source and target
571  * realms.  If the source and target share x-realm keys then this set to "."
572  * Note: for the remove function, all associated source, target, and
573  * intermediate entries will be removed matching the realm name
574  */
575 errcode_t
576 __profile_remove_xrealm_mapping(profile_t profile, char *realm)
577 {
578 	const char	*hierarchy[4];
579 	errcode_t	code, code2, code3;
580 	void		*state = NULL, *state2 = NULL;
581 	char		*source = NULL, *dummy_val = NULL, *target = NULL;
582 	char		*inter = NULL;
583 
584 	if (profile == NULL || realm == NULL)
585 		return (EINVAL);
586 
587 	hierarchy[0] = "capaths";
588 	hierarchy[1] = realm;
589 	hierarchy[2] = NULL;
590 	hierarchy[3] = NULL;
591 
592 	/*
593 	 * Not fatal if this fails, continue on.
594 	 */
595 	code = profile_rename_section(profile, hierarchy, NULL);
596 
597 	hierarchy[1] = NULL;
598 	code = profile_iterator_create(profile, hierarchy,
599 	    PROFILE_ITER_LIST_SECTION, &state);
600 	while (code == 0) {
601 		code = profile_iterator(&state, &source, &dummy_val);
602 		if (code == 0 && source != NULL) {
603 			hierarchy[1] = source;
604 			code2 = profile_iterator_create(profile, hierarchy,
605 			    PROFILE_ITER_LIST_SECTION, &state2);
606 			while (code2 == 0) {
607 				code2 = profile_iterator(&state2, &target,
608 				    &inter);
609 				if (code2 == 0 && target != NULL &&
610 				    inter != NULL) {
611 					if (strcmp(realm, target) == 0 ||
612 					    strcmp(realm, inter) == 0) {
613 						hierarchy[2] = target;
614 						code3 =
615 						    profile_clear_relation(
616 						    profile, hierarchy);
617 						if (code3 != 0) {
618 							code = code3;
619 							goto error;
620 						}
621 					}
622 				}
623 				if (target != NULL) {
624 					profile_release_string(target);
625 					target = NULL;
626 				}
627 				if (inter != NULL) {
628 					profile_release_string(inter);
629 					inter = NULL;
630 				}
631 			}
632 		}
633 		if (source != NULL) {
634 			profile_release_string(source);
635 			source = NULL;
636 		}
637 		if (dummy_val != NULL) {
638 			profile_release_string(dummy_val);
639 			dummy_val = NULL;
640 		}
641 	}
642 	code = 0;
643 
644 error:
645 	if (state != NULL)
646 		profile_iterator_free(&state);
647 	if (state2 != NULL)
648 		profile_iterator_free(&state2);
649 	if (target != NULL)
650 		profile_release_string(target);
651 	if (inter != NULL)
652 		profile_release_string(inter);
653 	if (source != NULL)
654 		profile_release_string(source);
655 	if (dummy_val != NULL)
656 		profile_release_string(dummy_val);
657 
658 	return (code);
659 }
660 
661 /*
662  * errcode_t __profile_remove_realm(profile_t profile, char *realm)
663  *
664  * where profile was the pointer passed back by __profile_init
665  * where realm is the target realm for removal
666  * Note: the function removes the matching realm in the realms section,
667  * the default_realm, relevant domain_realm mappings with the realm name,
668  * and matching capaths source realm subsection.
669  */
670 errcode_t
671 __profile_remove_realm(profile_t profile, char *realm)
672 {
673 	const char	*hierarchy[4];
674 	errcode_t	code;
675 	char		*drealm;
676 
677 	if (profile == NULL || realm == NULL)
678 		return (EINVAL);
679 
680 	/*
681 	 * Remove the default realm.
682 	 */
683 	hierarchy[0] = "libdefaults";
684 	hierarchy[1] = "default_realm";
685 	hierarchy[2] = NULL;
686 
687 	code = __profile_get_default_realm(profile, &drealm);
688 	if (code != 0)
689 		return (code);
690 	else if (drealm != NULL) {
691 		if (strcmp(drealm, realm) == 0) {
692 			code = profile_clear_relation(profile, hierarchy);
693 			if (code != 0)
694 				return (code);
695 		}
696 		free(drealm);
697 	}
698 
699 	hierarchy[0] = "realms";
700 	hierarchy[1] = realm;
701 	hierarchy[2] = NULL;
702 
703 	code = profile_rename_section(profile, hierarchy, NULL);
704 	if (code != 0)
705 		return (code);
706 
707 	code = __profile_remove_domain_mapping(profile, realm);
708 	if (code != 0)
709 		return (code);
710 
711 	code = __profile_remove_xrealm_mapping(profile, realm);
712 	if (code != 0)
713 		return (code);
714 
715 	/*
716 	 * Not fatal even if realm wasn't available to remove.
717 	 */
718 	return (0);
719 }
720 
721 /*
722  * errcode_t __profile_add_xrealm_mapping(profile_t profile, char *source,
723  *	char *target, char *inter)
724  *
725  * where profile was the pointer passed back by __profile_init
726  * where source is the source realm for the capath
727  * where target is the target realm for the capath
728  * where inter is the intermediate realm between the source and target
729  * realms.  If the source and target share x-realm keys then this set to "."
730  * Note: if the section does not exist one will be created
731  */
732 errcode_t
733 __profile_add_xrealm_mapping(profile_t profile, char *source, char *target,
734 	char *inter)
735 {
736 	const char	*hierarchy[4];
737 	errcode_t	code;
738 
739 	if (profile == NULL || source == NULL || target == NULL ||
740 	    inter == NULL)
741 		return (EINVAL);
742 
743 	hierarchy[0] = "capaths";
744 	hierarchy[1] = source;
745 	hierarchy[2] = target;
746 	hierarchy[3] = NULL;
747 
748 	/*
749 	 * Not fatal if this fails, continue on.
750 	 */
751 	(void) profile_clear_relation(profile, hierarchy);
752 
753 	code = profile_add_relation(profile, hierarchy, inter);
754 
755 	return (code);
756 }
757 
758 /*
759  * errcode_t __profile_validate(profile_t profile, int *val_err, char **val)
760  *
761  * where profile was the pointer passed back by __profile_init
762  * where val_err is a function specific error code of the following values:
763  *	0 No errors detected in profile
764  *	1 default realm is in lower-case (val returns realm)
765  *	2 realm in realms section is in lower-case (val returns realm)
766  *	3 default realm is not found in realms section
767  *		(val returns realm not found)
768  *	4 default realm does not exist
769  *	5 no realm found in realms section
770  *	6 no domain realm mapping entry found corresponding to a realm
771  *		in the realms section (val returns realm name)
772  *	7 kdc relation-value does not exist in realm
773  *		(val returns realm name)
774  *	8 admin_server relation-value does not exist in realm
775  *		(val returns realm name)
776  * where val is the associated errant value, associated with val_err.  This
777  * value is returned as is from the profile
778  * Note: function infers the following:
779  *	1. REALM should be in upper-case
780  *	2. all required entries are present
781  *	3. all relations are defined between default realm, realm, and
782  *		domain - realm mappings
783  * Caution: This function could return false positives on valid
784  * configurations and should only be used by the CIFS team for
785  * specific purposes.
786  */
787 errcode_t
788 __profile_validate(profile_t profile, int *val_err, char **val)
789 {
790 	errcode_t	code;
791 	register int	c;
792 	boolean_t	found = FALSE;
793 	char		*default_realm = NULL, **realms = NULL, *tr = NULL;
794 	char		**trealms = NULL, **domains = NULL, **ret_vals = NULL;
795 
796 	if (profile == NULL || val_err == NULL || val == NULL)
797 		return (EINVAL);
798 
799 	*val_err = 0;
800 	*val = NULL;
801 
802 	code = __profile_get_default_realm(profile, &default_realm);
803 	if (code == 0 && default_realm != NULL) {
804 		tr = default_realm;
805 
806 		while ((c = *tr++) != NULL) {
807 			if (islower(c)) {
808 				*val_err = 1;
809 				*val = strdup(default_realm);
810 				if (*val == NULL)
811 					code = ENOMEM;
812 				goto cleanup;
813 			}
814 		}
815 	} else if (code == 0 && default_realm == NULL) {
816 		*val_err = 4;
817 		goto cleanup;
818 	} else
819 		goto cleanup;
820 
821 	code = __profile_get_realms(profile, &realms);
822 	if (code == 0 && realms != NULL) {
823 		for (trealms = realms; *trealms; trealms++) {
824 
825 			tr = *trealms;
826 			while ((c = *tr++) != NULL) {
827 				if (islower(c)) {
828 					*val_err = 2;
829 					*val = strdup(*trealms);
830 					if (*val == NULL)
831 						code = ENOMEM;
832 					goto cleanup;
833 				}
834 			}
835 
836 			if (strcmp(default_realm, *trealms) == 0)
837 				found = TRUE;
838 
839 			code = __profile_get_domain_realm(profile, *trealms,
840 			    &domains);
841 			if (code == 0 && domains != NULL) {
842 				profile_free_list(domains);
843 				domains = NULL;
844 			} else if (code == 0 && domains == NULL) {
845 				*val_err = 6;
846 				*val = strdup(*trealms);
847 				if (*val == NULL)
848 					code = ENOMEM;
849 				goto cleanup;
850 			} else
851 				goto cleanup;
852 
853 			code = __profile_get_realm_entry(profile, *trealms,
854 			    "kdc", &ret_vals);
855 			if (code == 0 && ret_vals != NULL) {
856 				profile_free_list(ret_vals);
857 				ret_vals = NULL;
858 			} else if (code == 0 && ret_vals == NULL) {
859 				*val_err = 7;
860 				*val = strdup(*trealms);
861 				if (*val == NULL)
862 					code = ENOMEM;
863 				goto cleanup;
864 			} else
865 				goto cleanup;
866 
867 			code = __profile_get_realm_entry(profile, *trealms,
868 			    "admin_server", &ret_vals);
869 			if (code == 0 && ret_vals != NULL) {
870 				profile_free_list(ret_vals);
871 				ret_vals = NULL;
872 			} else if (code == 0 && ret_vals == NULL) {
873 				*val_err = 8;
874 				*val = strdup(*trealms);
875 				if (*val == NULL)
876 					code = ENOMEM;
877 				goto cleanup;
878 			} else
879 				goto cleanup;
880 		}
881 
882 		if (found == FALSE) {
883 			*val_err = 3;
884 			*val = strdup(default_realm);
885 			if (*val == NULL)
886 				code = ENOMEM;
887 			goto cleanup;
888 		}
889 	} else if (code == 0 && realms == NULL)
890 		*val_err = 5;
891 
892 cleanup:
893 
894 	if (realms != NULL)
895 		profile_free_list(realms);
896 	if (ret_vals != NULL)
897 		profile_free_list(ret_vals);
898 	if (default_realm != NULL)
899 		profile_release_string(default_realm);
900 	if (domains != NULL)
901 		profile_free_list(domains);
902 
903 	return (code);
904 }
905 
906 /*
907  * errcode_t __profile_init(char *filename, profile_t *profile)
908  *
909  * where filename is the specified profile location.  If filename is NULL
910  * then function uses the system default name, /etc/krb5/krb5.conf
911  * where profile is pointer passed to caller upon success
912  * Note: if the file does not exist then one will be created
913  * Note: if the file does exist then any existing profile information will
914  * be in profile
915  * Note: profile_release() should be used by the caller to free profile
916  */
917 errcode_t
918 __profile_init(char *filename, profile_t *profile)
919 {
920 	profile_filespec_t	*filenames = NULL;
921 	krb5_error_code		ret = 0;
922 	errcode_t		code = 0;
923 	int			err = 0, fd;
924 	mode_t			mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
925 
926 	if (profile == NULL)
927 		return (EINVAL);
928 
929 	if (filename != NULL) {
930 		filenames = malloc(2 * sizeof (char *));
931 		if (filenames == NULL)
932 			return (ENOMEM);
933 		filenames[0] = strdup(filename);
934 		if (filenames[0] == NULL) {
935 			free(filenames);
936 			return (ENOMEM);
937 		}
938 		filenames[1] = NULL;
939 	} else {
940 		ret = krb5_get_default_config_files(&filenames);
941 		if (ret != 0)
942 			return (ret);
943 	}
944 
945 	/*
946 	 * If file does not exist then create said file.
947 	 */
948 	fd = open(*filenames, O_RDWR|O_CREAT|O_NOFOLLOW|O_NOLINKS, mode);
949 	if (fd < 0) {
950 		err = errno;
951 		krb5_free_config_files(filenames);
952 		return (err);
953 	} else
954 		close(fd);
955 
956 	/*
957 	 * Specify non-null for specific file (to load any existing profile)
958 	 */
959 	code = profile_init((const_profile_filespec_t *)filenames, profile);
960 
961 	krb5_free_config_files(filenames);
962 
963 	return (code);
964 }
965