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