1 /*
2  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3  * Use is subject to license terms.
4  */
5 
6 
7 /*
8  * lib/krb5/os/an_to_ln.c
9  *
10  * Copyright 1990,1991 by the Massachusetts Institute of Technology.
11  * All Rights Reserved.
12  *
13  * Export of this software from the United States of America may
14  *   require a specific license from the United States Government.
15  *   It is the responsibility of any person or organization contemplating
16  *   export to obtain such a license before exporting.
17  *
18  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19  * distribute this software and its documentation for any purpose and
20  * without fee is hereby granted, provided that the above copyright
21  * notice appear in all copies and that both that copyright notice and
22  * this permission notice appear in supporting documentation, and that
23  * the name of M.I.T. not be used in advertising or publicity pertaining
24  * to distribution of the software without specific, written prior
25  * permission.  Furthermore if you modify this software you must label
26  * your software as modified software and not distribute it in such a
27  * fashion that it might be confused with the original M.I.T. software.
28  * M.I.T. makes no representations about the suitability of
29  * this software for any purpose.  It is provided "as is" without express
30  * or implied warranty.
31  *
32  *
33  * krb5_aname_to_localname()
34  */
35 
36 /*
37  * We're only to AN_TO_LN rules at this point, and not doing the
38  * database lookup  (moved from configure script)
39  */
40 #define AN_TO_LN_RULES
41 
42 #include "k5-int.h"
43 #include <ctype.h>
44 #if	HAVE_REGEX_H
45 #include <regex.h>
46 #endif	/* HAVE_REGEX_H */
47 #include <string.h>
48 /*
49  * Use compile(3) if no regcomp present.
50  */
51 #if	!defined(HAVE_REGCOMP) && defined(HAVE_REGEXPR_H) && defined(HAVE_COMPILE)
52 #define	RE_BUF_SIZE	1024
53 #include <regexpr.h>
54 #endif	/* !HAVE_REGCOMP && HAVE_REGEXP_H && HAVE_COMPILE */
55 
56 #define	MAX_FORMAT_BUFFER	1024
57 #ifndef	min
58 #define	min(a,b)	((a>b) ? b : a)
59 #endif	/* min */
60 #ifdef ANAME_DB
61 /*
62  * Use standard DBM code.
63  */
64 #define	KDBM_OPEN(db, fl, mo)	dbm_open(db, fl, mo)
65 #define	KDBM_CLOSE(db)		dbm_close(db)
66 #define	KDBM_FETCH(db, key)	dbm_fetch(db, key)
67 #endif /*ANAME_DB*/
68 
69 /*
70  * Find the portion of the flattened principal name that we use for mapping.
71  */
72 static char *
aname_full_to_mapping_name(char * fprincname)73 aname_full_to_mapping_name(char *fprincname)
74 {
75     char	*atp;
76     size_t	mlen;
77     char	*mname;
78 
79     mname = (char *) NULL;
80     if (fprincname) {
81 	atp = strrchr(fprincname, '@');
82 	if (!atp)
83 	    atp = &fprincname[strlen(fprincname)];
84 	mlen = (size_t) (atp - fprincname);
85 
86 	if ((mname = (char *) malloc(mlen+1))) {
87 	    strncpy(mname, fprincname, mlen);
88 	    mname[mlen] = '\0';
89 	}
90     }
91     return(mname);
92 }
93 
94 #ifdef ANAME_DB
95 /*
96  * Implementation:  This version uses a DBM database, indexed by aname,
97  * to generate a lname.
98  *
99  * The entries in the database are normal C strings, and include the trailing
100  * null in the DBM datum.size.
101  */
102 static krb5_error_code
db_an_to_ln(context,dbname,aname,lnsize,lname)103 db_an_to_ln(context, dbname, aname, lnsize, lname)
104     krb5_context context;
105     char *dbname;
106     krb5_const_principal aname;
107     const unsigned int lnsize;
108     char *lname;
109 {
110 #if !defined(_WIN32)
111     DBM *db;
112     krb5_error_code retval;
113     datum key, contents;
114     char *princ_name;
115 
116     if ((retval = krb5_unparse_name(context, aname, &princ_name)))
117 	return(retval);
118     key.dptr = princ_name;
119     key.dsize = strlen(princ_name)+1;	/* need to store the NULL for
120 					   decoding */
121 
122     db = KDBM_OPEN(dbname, O_RDONLY, 0600);
123     if (!db) {
124 	krb5_xfree(princ_name);
125 	return KRB5_LNAME_CANTOPEN;
126     }
127 
128     contents = KDBM_FETCH(db, key);
129 
130     krb5_xfree(princ_name);
131 
132     if (contents.dptr == NULL) {
133 	retval = KRB5_LNAME_NOTRANS;
134     } else {
135 	strncpy(lname, contents.dptr, lnsize);
136 	if (lnsize < contents.dsize)
137 	    retval = KRB5_CONFIG_NOTENUFSPACE;
138 	else if (lname[contents.dsize-1] != '\0')
139 	    retval = KRB5_LNAME_BADFORMAT;
140 	else
141 	    retval = 0;
142     }
143     /* can't close until we copy the contents. */
144     (void) KDBM_CLOSE(db);
145     return retval;
146 #else	/* !_WIN32 && !MACINTOSH */
147     /*
148      * If we don't have support for a database mechanism, then we can't
149      * translate this now, can we?
150      */
151     return KRB5_LNAME_NOTRANS;
152 #endif	/* !_WIN32 && !MACINTOSH */
153 }
154 #endif /*ANAME_DB*/
155 
156 #ifdef	AN_TO_LN_RULES
157 /*
158  * Format and transform a principal name to a local name.  This is particularly
159  * useful when Kerberos principals and local user names are formatted to
160  * some particular convention.
161  *
162  * There are three parts to each rule:
163  * First part - formulate the string to perform operations on:  If not present
164  * then the string defaults to the fully flattened principal minus the realm
165  * name.  Otherwise the syntax is as follows:
166  *	"[" <ncomps> ":" <format> "]"
167  *		Where:
168  *			<ncomps> is the number of expected components for this
169  *			rule.  If the particular principal does not have this
170  *			many components, then this rule does not apply.
171  *
172  *			<format> is a string of <component> or verbatim
173  *			characters to be inserted.
174  *
175  *			<component> is of the form "$"<number> to select the
176  *			<number>th component.  <number> begins from 1.
177  *
178  * Second part - select rule validity:  If not present, then this rule may
179  * apply to all selections.  Otherwise the syntax is as follows:
180  *	"(" <regexp> ")"
181  *		Where:	<regexp> is a selector regular expression.  If this
182  *			regular expression matches the whole pattern generated
183  *			from the first part, then this rule still applies.
184  *
185  * Last part - Transform rule:  If not present, then the selection string
186  * is passed verbatim and is matched.  Otherwise, the syntax is as follows:
187  *	<rule> ...
188  *		Where:	<rule> is of the form:
189  *			"s/" <regexp> "/" <text> "/" ["g"]
190  *
191  * In order to be able to select rule validity, the native system must support
192  * one of compile(3), re_comp(3) or regcomp(3).  In order to be able to
193  * transform (e.g. substitute), the native system must support regcomp(3) or
194  * compile(3).
195  */
196 
197 /*
198  * aname_do_match() 	- Does our name match the parenthesized regular
199  *			  expression?
200  *
201  * Chew up the match portion of the regular expression and update *contextp.
202  * If no re_comp() or regcomp(), then always return a match.
203  */
204 static krb5_error_code
aname_do_match(char * string,char ** contextp)205 aname_do_match(char *string, char **contextp)
206 {
207     krb5_error_code	kret;
208     char		*regexp, *startp, *endp = 0;
209     size_t		regexlen;
210 #if	HAVE_REGCOMP
211     regex_t		match_exp;
212     regmatch_t		match_match;
213 #elif	HAVE_REGEXPR_H
214     char		regexp_buffer[RE_BUF_SIZE];
215 #endif	/* HAVE_REGEXP_H */
216 
217     kret = 0;
218     /*
219      * Is this a match expression?
220      */
221     if (**contextp == '(') {
222 	kret = KRB5_CONFIG_BADFORMAT;
223 	startp = (*contextp) + 1;
224 	endp = strchr(startp, ')');
225 	/* Find the end of the match expression. */
226 	if (endp) {
227 	    regexlen = (size_t) (endp - startp);
228 	    regexp = (char *) malloc((size_t) regexlen+1);
229 	    kret = ENOMEM;
230 	    if (regexp) {
231 		strncpy(regexp, startp, regexlen);
232 		regexp[regexlen] = '\0';
233 		kret = KRB5_LNAME_NOTRANS;
234 		/*
235 		 * Perform the match.
236 		 */
237 #if	HAVE_REGCOMP
238 		if (!regcomp(&match_exp, regexp, REG_EXTENDED) &&
239 		    !regexec(&match_exp, string, 1, &match_match, 0)) {
240 		    if ((match_match.rm_so == 0) &&
241 			(match_match.rm_eo == strlen(string)))
242 			kret = 0;
243 		}
244 		regfree(&match_exp);
245 #elif	HAVE_REGEXPR_H
246 		compile(regexp,
247 			regexp_buffer,
248 			&regexp_buffer[RE_BUF_SIZE]);
249 		if (step(string, regexp_buffer)) {
250 		    if ((loc1 == string) &&
251 			(loc2 == &string[strlen(string)]))
252 			kret = 0;
253 		}
254 #elif	HAVE_RE_COMP
255 		if (!re_comp(regexp) && re_exec(string))
256 		    kret = 0;
257 #else	/* HAVE_RE_COMP */
258 		kret = 0;
259 #endif	/* HAVE_RE_COMP */
260 		free(regexp);
261 	    }
262 	    endp++;
263 	}
264 	else
265 	    endp = startp;
266     }
267     *contextp = endp;
268     return(kret);
269 }
270 
271 /*
272  * do_replacement()	- Replace the regular expression with the specified
273  * 			  replacement.
274  *
275  * If "doall" is set, it's a global replacement, otherwise, just a oneshot
276  * deal.
277  * If no regcomp() then just return the input string verbatim in the output
278  * string.
279  */
280 #define use_bytes(x) \
281     out_used += (x); \
282     if (out_used > MAX_FORMAT_BUFFER) goto mem_err
283 
284 static int
do_replacement(char * regexp,char * repl,int doall,char * in,char * out)285 do_replacement(char *regexp, char *repl, int doall, char *in, char *out)
286 {
287     size_t out_used = 0;
288 #if	HAVE_REGCOMP
289     regex_t	match_exp;
290     regmatch_t	match_match;
291     int		matched;
292     char	*cp;
293     char	*op;
294 
295     if (!regcomp(&match_exp, regexp, REG_EXTENDED)) {
296 	cp = in;
297 	op = out;
298 	matched = 0;
299 	do {
300 	    if (!regexec(&match_exp, cp, 1, &match_match, 0)) {
301 		if (match_match.rm_so) {
302 		    use_bytes(match_match.rm_so);
303 		    strncpy(op, cp, match_match.rm_so);
304 		    op += match_match.rm_so;
305 		}
306 		use_bytes(strlen(repl));
307 		strncpy(op, repl, MAX_FORMAT_BUFFER - 1 - (op - out));
308 		op += strlen(op);
309 		cp += match_match.rm_eo;
310 		if (!doall) {
311 		    use_bytes(strlen(cp));
312 		    strncpy(op, cp, MAX_FORMAT_BUFFER - 1 - (op - out));
313 		}
314 		matched = 1;
315 	    }
316 	    else {
317 		use_bytes(strlen(cp));
318 		strncpy(op, cp, MAX_FORMAT_BUFFER - 1 - (op - out));
319 		matched = 0;
320 	    }
321 	} while (doall && matched);
322 	regfree(&match_exp);
323     }
324 #elif	HAVE_REGEXPR_H
325     int		matched;
326     char	*cp;
327     char	*op;
328     char	regexp_buffer[RE_BUF_SIZE];
329     size_t	sdispl, edispl;
330 
331     compile(regexp,
332 	    regexp_buffer,
333 	    &regexp_buffer[RE_BUF_SIZE]);
334     cp = in;
335     op = out;
336     matched = 0;
337     do {
338 	if (step(cp, regexp_buffer)) {
339 	    sdispl = (size_t) (loc1 - cp);
340 	    edispl = (size_t) (loc2 - cp);
341 	    if (sdispl) {
342 		use_bytes(sdispl);
343 		strncpy(op, cp, sdispl);
344 		op += sdispl;
345 	    }
346 	    use_bytes(strlen(repl));
347 	    strncpy(op, repl, MAX_FORMAT_BUFFER - 1 - (op - out));
348 	    op += strlen(repl);
349 	    cp += edispl;
350 	    if (!doall) {
351 		use_bytes(strlen(cp));
352 		strncpy(op, cp, MAX_FORMAT_BUFFER - 1 - (op - out));
353 	    }
354 	    matched = 1;
355 	}
356 	else {
357 	    use_bytes(strlen(cp));
358 	    strncpy(op, cp, MAX_FORMAT_BUFFER - 1 - (op - out));
359 	    matched = 0;
360 	}
361     } while (doall && matched);
362 #else	/* HAVE_REGEXP_H */
363     memcpy(out, in, MAX_FORMAT_BUFFER);
364 #endif	/* HAVE_REGCOMP */
365     return 1;
366  mem_err:
367 #ifdef HAVE_REGCMP
368     	regfree(&match_exp);
369 #endif
370 	return 0;
371 
372 }
373 #undef use_bytes
374 
375 /*
376  * aname_replacer()	- Perform the specified substitutions on the input
377  *			  string and return the result.
378  *
379  * This routine enforces the "s/<pattern>/<replacement>/[g]" syntax.
380  */
381 static krb5_error_code
aname_replacer(char * string,char ** contextp,char ** result)382 aname_replacer(char *string, char **contextp, char **result)
383 {
384     krb5_error_code	kret;
385     char		*in;
386     char		*out;
387     char		*cp, *ep, *tp;
388     char		*rule, *repl;
389     size_t		rule_size, repl_size;
390     int			doglobal;
391     char		sep;
392 
393     kret = ENOMEM;
394     *result = (char *) NULL;
395     /* Allocate the formatting buffers */
396     /* Solaris Kerberos */
397     if (((in = (char *) malloc(MAX_FORMAT_BUFFER)) != NULL) &&
398 	((out = (char *) malloc(MAX_FORMAT_BUFFER)) != NULL)) {
399 	/*
400 	 * Prime the buffers.  Copy input string to "out" to simulate it
401 	 * being the result of an initial iteration.
402 	 */
403 	strncpy(out, string, MAX_FORMAT_BUFFER - 1);
404 	out[MAX_FORMAT_BUFFER - 1] = '\0';
405 	in[0] = '\0';
406 	kret = 0;
407 	/*
408 	 * Pound through the expression until we're done.
409 	 */
410 	for (cp = *contextp; *cp; ) {
411 	    /* Skip leading whitespace */
412 	    while (isspace((int) (*cp)))
413 		cp++;
414 
415 	    /*
416 	     * Solaris Kerberos
417 	     * Find our separators.  First two characters must be "s<sep>"
418 	     * We must also find another "<sep>" followed by another * "<sep>".
419 	     */
420 	    if (cp[0] != 's') {
421 		/* Bad syntax */
422 		kret = KRB5_CONFIG_BADFORMAT;
423 		break;
424 	    }
425 	    if (strspn(cp + 1, ",/;|!%") < 1) {
426 		/* Bad syntax */
427 		kret = KRB5_CONFIG_BADFORMAT;
428 		break;
429 	    }
430 	    sep = cp[1];
431 
432 	    if (((ep = strchr(&cp[2], sep)) != NULL) &&
433 		((tp = strchr(&ep[1], sep)) != NULL)) {
434 
435 		/* Figure out sizes of strings and allocate them */
436 		rule_size = (size_t) (ep - &cp[2]);
437 		repl_size = (size_t) (tp - &ep[1]);
438 		/* Solaris Kerberos */
439 		if (((rule = (char *) malloc(rule_size+1)) != NULL) &&
440 		    ((repl = (char *) malloc(repl_size+1)) != NULL)) {
441 
442 		    /* Copy the strings */
443 		    strncpy(rule, &cp[2], rule_size);
444 		    strncpy(repl, &ep[1], repl_size);
445 		    rule[rule_size] = repl[repl_size] = '\0';
446 
447 		    /* Check for trailing "g" */
448 		    doglobal = (tp[1] == 'g') ? 1 : 0;
449 		    if (doglobal)
450 			tp++;
451 
452 		    /* Swap previous in and out buffers */
453 		    ep = in;
454 		    in = out;
455 		    out = ep;
456 
457 		    /* Do the replacemenbt */
458 		    memset(out, '\0', MAX_FORMAT_BUFFER);
459 		    if (!do_replacement(rule, repl, doglobal, in, out)) {
460 			free(rule);
461 		    free(repl);
462 			kret = KRB5_LNAME_NOTRANS;
463 			break;
464 		    }
465 		    free(rule);
466 		    free(repl);
467 
468 		    /* If we have no output buffer left, this can't be good */
469 		    if (strlen(out) == 0) {
470 			kret = KRB5_LNAME_NOTRANS;
471 			break;
472 		    }
473 		}
474 		else {
475 		    /* No memory for copies */
476 		    kret = ENOMEM;
477 		    break;
478 		}
479 	    }
480 	    else {
481 		/* Bad syntax */
482 		kret = KRB5_CONFIG_BADFORMAT;
483 		break;
484 	    }
485 	    /* Advance past trailer */
486 	    cp = &tp[1];
487 	}
488 	free(in);
489 	if (!kret)
490 	    *result = out;
491 	else
492 	    free(out);
493     }
494     return(kret);
495 }
496 
497 /*
498  * rule_an_to_ln()	- Handle aname to lname translations for RULE rules.
499  *
500  * The initial part of this routine handles the formulation of the strings from
501  * the principal name.
502  */
503 static krb5_error_code
rule_an_to_ln(krb5_context context,char * rule,krb5_const_principal aname,const unsigned int lnsize,char * lname)504 rule_an_to_ln(krb5_context context, char *rule, krb5_const_principal aname, const unsigned int lnsize, char *lname)
505 {
506     krb5_error_code	kret;
507     char		*current;
508     char		*fprincname;
509     char		*selstring = 0;
510     int			num_comps, compind;
511     size_t selstring_used;
512     char		*cout;
513     krb5_const krb5_data *datap;
514     char		*outstring;
515 
516     /*
517      * First flatten the name.
518      */
519     current = rule;
520     if (!(kret = krb5_unparse_name(context, aname, &fprincname))) {
521 	/*
522 	 * First part.
523 	 */
524 	if (*current == '[') {
525 	    if (sscanf(current+1,"%d:", &num_comps) == 1) {
526 		if (num_comps == aname->length) {
527 		    /*
528 		     * We have a match based on the number of components.
529 		     */
530 		    current = strchr(current, ':');
531 		    selstring = (char *) malloc(MAX_FORMAT_BUFFER);
532 		    selstring_used = 0;
533 		    if (current && selstring) {
534 			current++;
535 			cout = selstring;
536 			/*
537 			 * Plow through the string.
538 			 */
539 			while ((*current != ']') &&
540 			       (*current != '\0')) {
541 			    /*
542 			     * Expand to a component.
543 			     */
544 			    if (*current == '$') {
545 				if ((sscanf(current+1, "%d", &compind) == 1) &&
546 				    (compind <= num_comps) &&
547 				    (datap =
548 				     (compind > 0)
549 				     ? krb5_princ_component(context, aname,
550 							    compind-1)
551 				     : krb5_princ_realm(context, aname))
552 				    ) {
553 				    if ((datap->length < MAX_FORMAT_BUFFER)
554 					&&  (selstring_used+datap->length
555 					     < MAX_FORMAT_BUFFER)) {
556 					selstring_used += datap->length;
557 				    } else {
558 					kret = ENOMEM;
559 					goto errout;
560 				    }
561 				    strncpy(cout,
562 					    datap->data,
563 					    (unsigned) datap->length);
564 				    cout += datap->length;
565 				    *cout = '\0';
566 				    current++;
567 				    /* Point past number */
568 				    while (isdigit((int) (*current)))
569 					current++;
570 				}
571 				else
572 				    kret = KRB5_CONFIG_BADFORMAT;
573 			    }
574 			    else {
575 				/* Copy in verbatim. */
576 				*cout = *current;
577 				cout++;
578 				*cout = '\0';
579 				current++;
580 			    }
581 			}
582 
583 			/*
584 			 * Advance past separator if appropriate.
585 			 */
586 			if (*current == ']')
587 			    current++;
588 			else
589 			    kret = KRB5_CONFIG_BADFORMAT;
590 
591 			errout: if (kret)
592 			    free(selstring);
593 		    }
594 		}
595 		else
596 		    kret = KRB5_LNAME_NOTRANS;
597 	    }
598 	    else
599 		kret = KRB5_CONFIG_BADFORMAT;
600 	}
601 	else {
602 	    if (!(selstring = aname_full_to_mapping_name(fprincname)))
603 		kret = ENOMEM;
604 	}
605 	krb5_xfree(fprincname);
606     }
607     if (!kret) {
608 	/*
609 	 * Second part
610 	 */
611 	if (*current == '(')
612 	    kret = aname_do_match(selstring, &current);
613 
614 	/*
615 	 * Third part.
616 	 */
617 	if (!kret) {
618 	    outstring = (char *) NULL;
619 	    kret = aname_replacer(selstring, &current, &outstring);
620 	    if (outstring) {
621 		/* Copy out the value if there's enough room */
622 		if (strlen(outstring)+1 <= (size_t) lnsize)
623 		    strcpy(lname, outstring);
624 		else
625 		    kret = KRB5_CONFIG_NOTENUFSPACE;
626 		free(outstring);
627 	    }
628 	}
629 	free(selstring);
630     }
631 
632     return(kret);
633 }
634 #endif	/* AN_TO_LN_RULES */
635 
636 /*
637  * Solaris Kerberos
638  * Return true (1) if the princ's realm matches any of the
639  * 'auth_to_local_realm' relations in the default realm section.
640  */
641 static int
an_to_ln_realm_chk(krb5_context context,krb5_const_principal aname,char * def_realm,int realm_length)642 an_to_ln_realm_chk(
643 	krb5_context context,
644 	krb5_const_principal aname,
645 	char *def_realm,
646 	int realm_length)
647 {
648 	char        **values, **cpp;
649 	const char  *realm_names[4];
650 	krb5_error_code     retval;
651 
652 	realm_names[0] = "realms";
653 	realm_names[1] = def_realm;
654 	realm_names[2] = "auth_to_local_realm";
655 	realm_names[3] = 0;
656 
657 	if (context->profile == 0)
658 		return (0);
659 
660 	retval = profile_get_values(context->profile, realm_names,
661 				    &values);
662 	if (retval)
663 		return (0);
664 
665 	for (cpp = values; *cpp; cpp++) {
666 
667 		if (((size_t) realm_length == strlen(*cpp)) &&
668 		    (memcmp(*cpp, krb5_princ_realm(context, aname)->data,
669 			    realm_length) == 0)) {
670 
671 			profile_free_list(values);
672 			return (1); /* success */
673 		}
674 	}
675 
676 	profile_free_list(values);
677 	return (0);
678 }
679 
680 /*
681  * Implementation:  This version checks the realm to see if it is the local
682  * realm; if so, and there is exactly one non-realm component to the name,
683  * that name is returned as the lname.
684  */
685 static krb5_error_code
default_an_to_ln(krb5_context context,krb5_const_principal aname,const unsigned int lnsize,char * lname)686 default_an_to_ln(krb5_context context, krb5_const_principal aname, const unsigned int lnsize, char *lname)
687 {
688     krb5_error_code retval;
689     char *def_realm;
690     unsigned int realm_length;
691 
692     realm_length = krb5_princ_realm(context, aname)->length;
693 
694     if ((retval = krb5_get_default_realm(context, &def_realm))) {
695 	return(retval);
696     }
697     /* Solaris Kerberos */
698     /* compare against default realm and auth_to_local_realm(s) */
699     if ((((size_t) realm_length != strlen(def_realm)) ||
700         (memcmp(def_realm, krb5_princ_realm(context, aname)->data,
701 		realm_length))) &&
702 	!an_to_ln_realm_chk(context, aname, def_realm,
703 			    realm_length)) {
704         free(def_realm);
705         return KRB5_LNAME_NOTRANS;
706     }
707 
708     if (krb5_princ_size(context, aname) != 1) {
709         if (krb5_princ_size(context, aname) == 2 ) {
710            /* Check to see if 2nd component is the local realm. */
711            if ( strncmp(krb5_princ_component(context, aname,1)->data,def_realm,
712                         realm_length) ||
713                 realm_length != krb5_princ_component(context, aname,1)->length) {
714 		    /* XXX an_to_ln_realm_chk ? */
715 		/* Solaris Kerberos */
716 		free(def_realm);
717                 return KRB5_LNAME_NOTRANS;
718 	    }
719         }
720         else {
721            /* no components or more than one component to non-realm part of name
722            --no translation. */
723 	    /* Solaris Kerberos */
724 	    free(def_realm);
725             return KRB5_LNAME_NOTRANS;
726 	}
727     }
728 
729     free(def_realm);
730     strncpy(lname, krb5_princ_component(context, aname,0)->data,
731 	    min(krb5_princ_component(context, aname,0)->length,lnsize));
732     if (lnsize <= krb5_princ_component(context, aname,0)->length ) {
733 	retval = KRB5_CONFIG_NOTENUFSPACE;
734     } else {
735 	lname[krb5_princ_component(context, aname,0)->length] = '\0';
736 	retval = 0;
737     }
738     return retval;
739 }
740 
741 /*
742  Converts an authentication name to a local name suitable for use by
743  programs wishing a translation to an environment-specific name (e.g.
744  user account name).
745 
746  lnsize specifies the maximum length name that is to be filled into
747  lname.
748  The translation will be null terminated in all non-error returns.
749 
750  returns system errors, NOT_ENOUGH_SPACE
751 */
752 
753 krb5_error_code KRB5_CALLCONV
krb5_aname_to_localname(krb5_context context,krb5_const_principal aname,const int lnsize_in,char * lname)754 krb5_aname_to_localname(krb5_context context, krb5_const_principal aname, const int lnsize_in, char *lname)
755 {
756     krb5_error_code	kret;
757     char		*realm;
758     char		*pname;
759     char		*mname;
760     const char		*hierarchy[5];
761     char		**mapping_values;
762     int			i, nvalid;
763     char		*cp, *s;
764     char		*typep, *argp;
765     unsigned int        lnsize;
766 
767     if (lnsize_in < 0)
768       return KRB5_CONFIG_NOTENUFSPACE;
769 
770     lnsize = lnsize_in; /* Unsigned */
771 
772     /*
773      * First get the default realm.
774      */
775     if (!(kret = krb5_get_default_realm(context, &realm))) {
776 	/* Flatten the name */
777 	if (!(kret = krb5_unparse_name(context, aname, &pname))) {
778 	    if ((mname = aname_full_to_mapping_name(pname))) {
779 		/*
780 		 * Search first for explicit mappings of the form:
781 		 *
782 		 * [realms]->realm->"auth_to_local_names"->mapping_name
783 		 */
784 		hierarchy[0] = "realms";
785 		hierarchy[1] = realm;
786 		hierarchy[2] = "auth_to_local_names";
787 		hierarchy[3] = mname;
788 		hierarchy[4] = (char *) NULL;
789 		if (!(kret = profile_get_values(context->profile,
790 						hierarchy,
791 						&mapping_values))) {
792 		    /* We found one or more explicit mappings. */
793 		    for (nvalid=0; mapping_values[nvalid]; nvalid++);
794 
795 		    /* Just use the last one. */
796 		    /* Trim the value. */
797 		    s = mapping_values[nvalid-1];
798 		    cp = s + strlen(s);
799 		    while (cp > s) {
800 			cp--;
801 			if (!isspace((int)(*cp)))
802 			    break;
803 			*cp = '\0';
804 		    }
805 
806 		    /* Copy out the value if there's enough room */
807 		    if (strlen(mapping_values[nvalid-1])+1 <= (size_t) lnsize)
808 			strcpy(lname, mapping_values[nvalid-1]);
809 		    else
810 			kret = KRB5_CONFIG_NOTENUFSPACE;
811 
812 		    /* Free residue */
813 		    profile_free_list(mapping_values);
814 		}
815 		else {
816 		    /*
817 		     * OK - There's no explicit mapping.  Now check for
818 		     * general auth_to_local rules of the form:
819 		     *
820 		     * [realms]->realm->"auth_to_local"
821 		     *
822 		     * This can have one or more of the following kinds of
823 		     * values:
824 		     *	DB:<filename>	- Look up principal in aname database.
825 		     *	RULE:<sed-exp>	- Formulate lname from sed-exp.
826 		     *	DEFAULT		- Use default rule.
827 		     * The first rule to find a match is used.
828 		     */
829 		    hierarchy[0] = "realms";
830 		    hierarchy[1] = realm;
831 		    hierarchy[2] = "auth_to_local";
832 		    hierarchy[3] = (char *) NULL;
833 		    if (!(kret = profile_get_values(context->profile,
834 						    hierarchy,
835 						    &mapping_values))) {
836 			/*
837 			 * Loop through all the mapping values.
838 			 */
839 			for (i=0; mapping_values[i]; i++) {
840 			    typep = mapping_values[i];
841 			    argp = strchr(typep, ':');
842 			    if (argp) {
843 				*argp = '\0';
844 				argp++;
845 			    }
846 #ifdef ANAME_DB
847 			    if (!strcmp(typep, "DB") && argp) {
848 				kret = db_an_to_ln(context,
849 						   argp,
850 						   aname,
851 						   lnsize,
852 						   lname);
853 				if (kret != KRB5_LNAME_NOTRANS)
854 				    break;
855 			    }
856 			    else
857 #endif
858 #ifdef	AN_TO_LN_RULES
859 			    if (!strcmp(typep, "RULE") && argp) {
860 				kret = rule_an_to_ln(context,
861 						     argp,
862 						     aname,
863 						     lnsize,
864 						     lname);
865 				if (kret != KRB5_LNAME_NOTRANS)
866 				    break;
867 			    }
868 			    else
869 #endif	/* AN_TO_LN_RULES */
870 			    if (!strcmp(typep, "DEFAULT") && !argp) {
871 				kret = default_an_to_ln(context,
872 							aname,
873 							lnsize,
874 							lname);
875 				if (kret != KRB5_LNAME_NOTRANS)
876 				    break;
877 			    }
878 			    else {
879 				kret = KRB5_CONFIG_BADFORMAT;
880 				break;
881 			    }
882 			}
883 
884 			/* We're done, clean up the droppings. */
885 			profile_free_list(mapping_values);
886 		    }
887 		    else {
888 			/*
889 			 * No profile relation found, try default mapping.
890 			 */
891 			kret = default_an_to_ln(context,
892 						aname,
893 						lnsize,
894 						lname);
895 		    }
896 		}
897 		free(mname);
898 	    }
899 	    else
900 		kret = ENOMEM;
901 	    krb5_xfree(pname);
902 	}
903 	krb5_xfree(realm);
904     }
905     return(kret);
906 }
907 
908