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(5).
29  */
30 
31 #include <ctype.h>
32 #include "prof_int.h"
33 #include "k5-int.h"
34 
35 errcode_t
__profile_iter_name_value(profile_t profile,char * section,char * key,char *** retvals)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
__profile_get_domain_realm(profile_t profile,char * realm,char *** domains)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
__profile_set_appdefaults(profile_t profile)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
__profile_set_logging(profile_t profile)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
__profile_set_libdefaults(profile_t profile,char * realm)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
__profile_set_kdc(profile_t profile,char * realm,char * kdc,boolean_t overwrite)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
__profile_release(profile_t profile)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
__profile_abandon(profile_t profile)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
__profile_add_domain_mapping(profile_t profile,char * domain,char * realm)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
__profile_remove_domain_mapping(profile_t profile,char * realm)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
__profile_get_realm_entry(profile_t profile,char * realm,char * name,char *** ret_value)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 	if (code == PROF_NO_RELATION)
390 		code = 0;
391 
392 	return (code);
393 }
394 
395 /*
396  * errcode_t __profile_add_realm_entry(profile_t profile, char *realm,
397  *	char *name, char **value)
398  *
399  * where profile was the pointer passed back by __profile_init
400  * where realm is the target realm for the name-value pair
401  * where name is the name in the realm subsection to add
402  * where value is a string array values to assigned to name.  The array is
403  * terminated with a NULL pointer.
404  * Note: if the realm subsection does no exist then an error is returned
405  * Note: if the name already exists the set is overwritten with the values
406  * passed
407  */
408 errcode_t
__profile_add_realm_entry(profile_t profile,char * realm,char * name,char ** values)409 __profile_add_realm_entry(profile_t profile, char *realm, char *name,
410 	char **values)
411 {
412 	const char	*hierarchy[4];
413 	errcode_t	code;
414 	char		**tvalue = NULL;
415 
416 	if (profile == NULL || realm == NULL || name == NULL || values == NULL)
417 		return (EINVAL);
418 
419 	hierarchy[0] = "realms";
420 	hierarchy[1] = realm;
421 	hierarchy[2] = name;
422 	hierarchy[3] = NULL;
423 
424 	/*
425 	 * Not fatal if this fails, continue on.
426 	 */
427 	(void) profile_clear_relation(profile, hierarchy);
428 
429 	for (tvalue = values; *tvalue; tvalue++) {
430 
431 		code = profile_add_relation(profile, hierarchy, *tvalue);
432 		if (code != 0)
433 			return (code);
434 	}
435 
436 	return (0);
437 }
438 
439 /*
440  * errcode_t __profile_get_default_realm(profile_t profile, char **realm)
441  *
442  * where profile was the pointer passed back by __profile_init
443  * where realm is the default_realm configured for the system
444  * Note: if no default_realm has been configured and a profile does exist
445  * then realm is set to NULL
446  */
447 errcode_t
__profile_get_default_realm(profile_t profile,char ** realm)448 __profile_get_default_realm(profile_t profile, char **realm)
449 {
450 	errcode_t	code;
451 	char		*value = NULL;
452 
453 	if (profile == NULL || realm == NULL)
454 		return (EINVAL);
455 
456 	code = profile_get_string(profile, "libdefaults", "default_realm", 0, 0,
457 	    &value);
458 	if (code == 0 && value != NULL)
459 		*realm = value;
460 
461 	if (code == PROF_NO_RELATION)
462 		code = 0;
463 
464 	return (code);
465 }
466 
467 /*
468  * errcode_t __profile_get_realms(profile_t profile, char ***realms)
469  *
470  * where profile was the pointer passed back by __profile_init
471  * where realms is a string array of realm names currently configured.
472  * The array is terminated with a NULL pointer.
473  * Note: if no realms have been configured and a profile does exist then
474  * realms is set to NULL
475  */
476 errcode_t
__profile_get_realms(profile_t profile,char *** realms)477 __profile_get_realms(profile_t profile, char ***realms)
478 {
479 
480 	if (profile == NULL || realms == NULL)
481 		return (EINVAL);
482 
483 	return (__profile_iter_name_value(profile, "realms", NULL, realms));
484 }
485 
486 /*
487  * errcode_t __profile_add_realm(profile_t profile, char *realm,
488  *	char *master, char **kdcs, boolean_t set_change, boolean_t
489  *	default_realm)
490  *
491  * where profile was the pointer passed back by __profile_init
492  * where realm is the realm name associated with the configuration
493  * where master is the server that is assigned to admin_server
494  * where kdcs is a string array of KDCs used to populate the kdc set.
495  * The array is terminated with a NULL pointer.
496  * where set_change, if set, will use the SET_CHANGE protocol for password
497  * modifications.  RPCSEC_GSS is set by default
498  * where default_realm, if set, will assign the realm to default_realm
499  * Note: the ordering of kdcs is determined by the server's position in the
500  * array
501  * Note: kdcs must be assigned a value, even if it is the same value as the
502  * master.
503  */
504 errcode_t
__profile_add_realm(profile_t profile,char * realm,char * master,char ** kdcs,boolean_t set_change,boolean_t default_realm)505 __profile_add_realm(profile_t profile, char *realm, char *master, char **kdcs,
506 	boolean_t set_change, boolean_t default_realm)
507 {
508 	const char	*hierarchy[4];
509 	errcode_t	code;
510 	boolean_t	ow = TRUE;
511 	char		**tkdcs;
512 
513 	if (profile == NULL || realm == NULL || master == NULL || kdcs == NULL)
514 		return (EINVAL);
515 
516 	/*
517 	 * Sets the default realm to realm if default_realm flag is set.
518 	 */
519 	if (default_realm == TRUE) {
520 		if (code = __profile_set_libdefaults(profile, realm))
521 			return (code);
522 	}
523 
524 	hierarchy[0] = "realms";
525 	hierarchy[1] = realm;
526 	hierarchy[3] = NULL;
527 
528 	hierarchy[2] = "admin_server";
529 
530 	/*
531 	 * Not fatal if this fails, therefore return code is not checked.
532 	 */
533 	(void) profile_clear_relation(profile, hierarchy);
534 
535 	if (code = profile_add_relation(profile, hierarchy, master))
536 		return (code);
537 
538 	/*
539 	 * If not set then defaults to undefined, which defaults to RPCSEC_GSS.
540 	 */
541 	if (set_change == TRUE) {
542 		hierarchy[2] = "kpasswd_protocol";
543 
544 		(void) profile_clear_relation(profile, hierarchy);
545 
546 		code = profile_add_relation(profile, hierarchy, "SET_CHANGE");
547 		if (code != 0)
548 			return (code);
549 	}
550 
551 	for (tkdcs = kdcs; *tkdcs; tkdcs++) {
552 		if (code = __profile_set_kdc(profile, realm, *tkdcs, ow))
553 			return (code);
554 		ow = FALSE;
555 	}
556 
557 	code = __profile_set_logging(profile);
558 	if (code != 0)
559 		return (code);
560 
561 	code = __profile_set_appdefaults(profile);
562 
563 	return (code);
564 }
565 
566 /*
567  * errcode_t __profile_remove_xrealm_mapping(profile_t profile, char *realm)
568  *
569  * where profile was the pointer passed back by __profile_init
570  * where source is the source realm for the capath
571  * where target is the target realm for the capath
572  * where inter is the intermediate realm between the source and target
573  * realms.  If the source and target share x-realm keys then this set to "."
574  * Note: for the remove function, all associated source, target, and
575  * intermediate entries will be removed matching the realm name
576  */
577 errcode_t
__profile_remove_xrealm_mapping(profile_t profile,char * realm)578 __profile_remove_xrealm_mapping(profile_t profile, char *realm)
579 {
580 	const char	*hierarchy[4];
581 	errcode_t	code, code2, code3;
582 	void		*state = NULL, *state2 = NULL;
583 	char		*source = NULL, *dummy_val = NULL, *target = NULL;
584 	char		*inter = NULL;
585 
586 	if (profile == NULL || realm == NULL)
587 		return (EINVAL);
588 
589 	hierarchy[0] = "capaths";
590 	hierarchy[1] = realm;
591 	hierarchy[2] = NULL;
592 	hierarchy[3] = NULL;
593 
594 	/*
595 	 * Not fatal if this fails, continue on.
596 	 */
597 	code = profile_rename_section(profile, hierarchy, NULL);
598 
599 	hierarchy[1] = NULL;
600 	code = profile_iterator_create(profile, hierarchy,
601 	    PROFILE_ITER_LIST_SECTION, &state);
602 	while (code == 0) {
603 		code = profile_iterator(&state, &source, &dummy_val);
604 		if (code == 0 && source != NULL) {
605 			hierarchy[1] = source;
606 			code2 = profile_iterator_create(profile, hierarchy,
607 			    PROFILE_ITER_LIST_SECTION, &state2);
608 			while (code2 == 0) {
609 				code2 = profile_iterator(&state2, &target,
610 				    &inter);
611 				if (code2 == 0 && target != NULL &&
612 				    inter != NULL) {
613 					if (strcmp(realm, target) == 0 ||
614 					    strcmp(realm, inter) == 0) {
615 						hierarchy[2] = target;
616 						code3 =
617 						    profile_clear_relation(
618 						    profile, hierarchy);
619 						if (code3 != 0) {
620 							code = code3;
621 							goto error;
622 						}
623 					}
624 				}
625 				if (target != NULL) {
626 					profile_release_string(target);
627 					target = NULL;
628 				}
629 				if (inter != NULL) {
630 					profile_release_string(inter);
631 					inter = NULL;
632 				}
633 			}
634 		}
635 		if (source != NULL) {
636 			profile_release_string(source);
637 			source = NULL;
638 		}
639 		if (dummy_val != NULL) {
640 			profile_release_string(dummy_val);
641 			dummy_val = NULL;
642 		}
643 	}
644 	code = 0;
645 
646 error:
647 	if (state != NULL)
648 		profile_iterator_free(&state);
649 	if (state2 != NULL)
650 		profile_iterator_free(&state2);
651 	if (target != NULL)
652 		profile_release_string(target);
653 	if (inter != NULL)
654 		profile_release_string(inter);
655 	if (source != NULL)
656 		profile_release_string(source);
657 	if (dummy_val != NULL)
658 		profile_release_string(dummy_val);
659 
660 	return (code);
661 }
662 
663 /*
664  * errcode_t __profile_remove_realm(profile_t profile, char *realm)
665  *
666  * where profile was the pointer passed back by __profile_init
667  * where realm is the target realm for removal
668  * Note: the function removes the matching realm in the realms section,
669  * the default_realm, relevant domain_realm mappings with the realm name,
670  * and matching capaths source realm subsection.
671  */
672 errcode_t
__profile_remove_realm(profile_t profile,char * realm)673 __profile_remove_realm(profile_t profile, char *realm)
674 {
675 	const char	*hierarchy[4];
676 	errcode_t	code;
677 	char		*drealm = NULL;
678 
679 	if (profile == NULL || realm == NULL)
680 		return (EINVAL);
681 
682 	/*
683 	 * Remove the default realm.
684 	 */
685 	hierarchy[0] = "libdefaults";
686 	hierarchy[1] = "default_realm";
687 	hierarchy[2] = NULL;
688 
689 	code = __profile_get_default_realm(profile, &drealm);
690 	if (code != 0)
691 		return (code);
692 	else if (drealm != NULL) {
693 		if (strcmp(drealm, realm) == 0) {
694 			code = profile_clear_relation(profile, hierarchy);
695 			if (code != 0) {
696 				free(drealm);
697 				return (code);
698 			}
699 		}
700 		free(drealm);
701 	}
702 
703 	hierarchy[0] = "realms";
704 	hierarchy[1] = realm;
705 	hierarchy[2] = NULL;
706 
707 	code = profile_rename_section(profile, hierarchy, NULL);
708 	if (code != 0)
709 		return (code);
710 
711 	code = __profile_remove_domain_mapping(profile, realm);
712 	if (code != 0)
713 		return (code);
714 
715 	code = __profile_remove_xrealm_mapping(profile, realm);
716 	if (code != 0)
717 		return (code);
718 
719 	/*
720 	 * Not fatal even if realm wasn't available to remove.
721 	 */
722 	return (0);
723 }
724 
725 /*
726  * errcode_t __profile_add_xrealm_mapping(profile_t profile, char *source,
727  *	char *target, char *inter)
728  *
729  * where profile was the pointer passed back by __profile_init
730  * where source is the source realm for the capath
731  * where target is the target realm for the capath
732  * where inter is the intermediate realm between the source and target
733  * realms.  If the source and target share x-realm keys then this set to "."
734  * Note: if the section does not exist one will be created
735  */
736 errcode_t
__profile_add_xrealm_mapping(profile_t profile,char * source,char * target,char * inter)737 __profile_add_xrealm_mapping(profile_t profile, char *source, char *target,
738 	char *inter)
739 {
740 	const char	*hierarchy[4];
741 	errcode_t	code;
742 
743 	if (profile == NULL || source == NULL || target == NULL ||
744 	    inter == NULL)
745 		return (EINVAL);
746 
747 	hierarchy[0] = "capaths";
748 	hierarchy[1] = source;
749 	hierarchy[2] = target;
750 	hierarchy[3] = NULL;
751 
752 	/*
753 	 * Not fatal if this fails, continue on.
754 	 */
755 	(void) profile_clear_relation(profile, hierarchy);
756 
757 	code = profile_add_relation(profile, hierarchy, inter);
758 
759 	return (code);
760 }
761 
762 /*
763  * errcode_t __profile_validate(profile_t profile, int *val_err, char **val)
764  *
765  * where profile was the pointer passed back by __profile_init
766  * where val_err is a function specific error code of the following values:
767  *	0 No errors detected in profile
768  *	1 default realm is in lower-case (val returns realm)
769  *	2 realm in realms section is in lower-case (val returns realm)
770  *	3 default realm is not found in realms section
771  *		(val returns realm not found)
772  *	4 default realm does not exist
773  *	5 no realm found in realms section
774  *	6 no domain realm mapping entry found corresponding to a realm
775  *		in the realms section (val returns realm name)
776  *	7 kdc relation-value does not exist in realm
777  *		(val returns realm name)
778  *	8 admin_server relation-value does not exist in realm
779  *		(val returns realm name)
780  * where val is the associated errant value, associated with val_err.  This
781  * value is returned as is from the profile
782  * Note: function infers the following:
783  *	1. REALM should be in upper-case
784  *	2. all required entries are present
785  *	3. all relations are defined between default realm, realm, and
786  *		domain - realm mappings
787  *
788  * Note: The return value of this function is based on the error code returned
789  * by the framework/mechanism.  The function could return zero with the
790  * validation error code set to non-zero if the profile is invalid in any way.
791  *
792  * Caution: This function could return false positives on valid
793  * configurations and should only be used by the CIFS team for
794  * specific purposes.
795  */
796 errcode_t
__profile_validate(profile_t profile,int * val_err,char ** val)797 __profile_validate(profile_t profile, int *val_err, char **val)
798 {
799 	errcode_t	code;
800 	int		c;
801 	boolean_t	found = FALSE;
802 	char		*default_realm = NULL, **realms = NULL, *tr = NULL;
803 	char		**trealms = NULL, **domains = NULL, **ret_vals = NULL;
804 
805 	if (profile == NULL || val_err == NULL || val == NULL)
806 		return (EINVAL);
807 
808 	*val_err = 0;
809 	*val = NULL;
810 
811 	code = __profile_get_default_realm(profile, &default_realm);
812 	if (code == 0 && default_realm != NULL) {
813 		tr = default_realm;
814 
815 		while ((c = *tr++) != 0) {
816 			if (islower(c)) {
817 				*val_err = 1;
818 				*val = strdup(default_realm);
819 				if (*val == NULL)
820 					code = ENOMEM;
821 				goto cleanup;
822 			}
823 		}
824 	} else if (code == 0 && default_realm == NULL) {
825 		*val_err = 4;
826 		goto cleanup;
827 	} else
828 		goto cleanup;
829 
830 	code = __profile_get_realms(profile, &realms);
831 	if (code == 0 && realms != NULL) {
832 		for (trealms = realms; *trealms; trealms++) {
833 
834 			tr = *trealms;
835 			while ((c = *tr++) != 0) {
836 				if (islower(c)) {
837 					*val_err = 2;
838 					*val = strdup(*trealms);
839 					if (*val == NULL)
840 						code = ENOMEM;
841 					goto cleanup;
842 				}
843 			}
844 
845 			if (strcmp(default_realm, *trealms) == 0)
846 				found = TRUE;
847 
848 			code = __profile_get_domain_realm(profile, *trealms,
849 			    &domains);
850 			if (code == 0 && domains != NULL) {
851 				profile_free_list(domains);
852 				domains = NULL;
853 			} else if (code == 0 && domains == NULL) {
854 				*val_err = 6;
855 				*val = strdup(*trealms);
856 				if (*val == NULL)
857 					code = ENOMEM;
858 				goto cleanup;
859 			} else
860 				goto cleanup;
861 
862 			code = __profile_get_realm_entry(profile, *trealms,
863 			    "kdc", &ret_vals);
864 			if (code == 0 && ret_vals != NULL) {
865 				profile_free_list(ret_vals);
866 				ret_vals = NULL;
867 			} else if (code == 0 && ret_vals == NULL) {
868 				*val_err = 7;
869 				*val = strdup(*trealms);
870 				if (*val == NULL)
871 					code = ENOMEM;
872 				goto cleanup;
873 			} else
874 				goto cleanup;
875 
876 			code = __profile_get_realm_entry(profile, *trealms,
877 			    "admin_server", &ret_vals);
878 			if (code == 0 && ret_vals != NULL) {
879 				profile_free_list(ret_vals);
880 				ret_vals = NULL;
881 			} else if (code == 0 && ret_vals == NULL) {
882 				*val_err = 8;
883 				*val = strdup(*trealms);
884 				if (*val == NULL)
885 					code = ENOMEM;
886 				goto cleanup;
887 			} else
888 				goto cleanup;
889 		}
890 
891 		if (found == FALSE) {
892 			*val_err = 3;
893 			*val = strdup(default_realm);
894 			if (*val == NULL)
895 				code = ENOMEM;
896 			goto cleanup;
897 		}
898 	} else if (code == 0 && realms == NULL)
899 		*val_err = 5;
900 
901 cleanup:
902 
903 	if (realms != NULL)
904 		profile_free_list(realms);
905 	if (ret_vals != NULL)
906 		profile_free_list(ret_vals);
907 	if (default_realm != NULL)
908 		profile_release_string(default_realm);
909 	if (domains != NULL)
910 		profile_free_list(domains);
911 
912 	return (code);
913 }
914 
915 /*
916  * errcode_t __profile_init(char *filename, profile_t *profile)
917  *
918  * where filename is the specified profile location.  If filename is NULL
919  * then function uses the system default name, /etc/krb5/krb5.conf
920  * where profile is pointer passed to caller upon success
921  * Note: if the file does not exist then one will be created
922  * Note: if the file does exist then any existing profile information will
923  * be in profile
924  * Note: profile_release() should be used by the caller to free profile
925  */
926 errcode_t
__profile_init(char * filename,profile_t * profile)927 __profile_init(char *filename, profile_t *profile)
928 {
929 	profile_filespec_t	*filenames = NULL;
930 	krb5_error_code		ret = 0;
931 	errcode_t		code = 0;
932 	int			err = 0, fd;
933 	mode_t			mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
934 
935 	if (profile == NULL)
936 		return (EINVAL);
937 
938 	if (filename != NULL) {
939 		filenames = malloc(2 * sizeof (char *));
940 		if (filenames == NULL)
941 			return (ENOMEM);
942 		filenames[0] = strdup(filename);
943 		if (filenames[0] == NULL) {
944 			free(filenames);
945 			return (ENOMEM);
946 		}
947 		filenames[1] = NULL;
948 	} else {
949 		ret = krb5_get_default_config_files(&filenames);
950 		if (ret != 0)
951 			return (ret);
952 	}
953 
954 	/*
955 	 * If file does not exist then create said file.
956 	 */
957 	fd = open(*filenames, O_RDWR|O_CREAT|O_NOFOLLOW|O_NOLINKS, mode);
958 	if (fd < 0) {
959 		err = errno;
960 		krb5_free_config_files(filenames);
961 		return (err);
962 	} else
963 		close(fd);
964 
965 	/*
966 	 * Specify non-null for specific file (to load any existing profile)
967 	 */
968 	code = profile_init((const_profile_filespec_t *)filenames, profile);
969 
970 	krb5_free_config_files(filenames);
971 
972 	return (code);
973 }
974