root/usr/src/lib/gss_mechs/mech_krb5/krb5/os/an_to_ln.c
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */


/*
 * lib/krb5/os/an_to_ln.c
 *
 * Copyright 1990,1991 by the Massachusetts Institute of Technology.
 * All Rights Reserved.
 *
 * Export of this software from the United States of America may
 *   require a specific license from the United States Government.
 *   It is the responsibility of any person or organization contemplating
 *   export to obtain such a license before exporting.
 *
 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
 * distribute this software and its documentation for any purpose and
 * without fee is hereby granted, provided that the above copyright
 * notice appear in all copies and that both that copyright notice and
 * this permission notice appear in supporting documentation, and that
 * the name of M.I.T. not be used in advertising or publicity pertaining
 * to distribution of the software without specific, written prior
 * permission.  Furthermore if you modify this software you must label
 * your software as modified software and not distribute it in such a
 * fashion that it might be confused with the original M.I.T. software.
 * M.I.T. makes no representations about the suitability of
 * this software for any purpose.  It is provided "as is" without express
 * or implied warranty.
 *
 *
 * krb5_aname_to_localname()
 */

/*
 * We're only to AN_TO_LN rules at this point, and not doing the
 * database lookup  (moved from configure script)
 */
#define AN_TO_LN_RULES

#include "k5-int.h"
#include <ctype.h>
#if     HAVE_REGEX_H
#include <regex.h>
#endif  /* HAVE_REGEX_H */
#include <string.h>
/*
 * Use compile(3) if no regcomp present.
 */
#if     !defined(HAVE_REGCOMP) && defined(HAVE_REGEXPR_H) && defined(HAVE_COMPILE)
#define RE_BUF_SIZE     1024
#include <regexpr.h>
#endif  /* !HAVE_REGCOMP && HAVE_REGEXP_H && HAVE_COMPILE */

#define MAX_FORMAT_BUFFER       1024
#ifndef min
#define min(a,b)        ((a>b) ? b : a)
#endif  /* min */
#ifdef ANAME_DB
/*
 * Use standard DBM code.
 */
#define KDBM_OPEN(db, fl, mo)   dbm_open(db, fl, mo)
#define KDBM_CLOSE(db)          dbm_close(db)
#define KDBM_FETCH(db, key)     dbm_fetch(db, key)
#endif /*ANAME_DB*/

/*
 * Find the portion of the flattened principal name that we use for mapping.
 */
static char *
aname_full_to_mapping_name(char *fprincname)
{
    char        *atp;
    size_t      mlen;
    char        *mname;

    mname = (char *) NULL;
    if (fprincname) {
        atp = strrchr(fprincname, '@');
        if (!atp)
            atp = &fprincname[strlen(fprincname)];
        mlen = (size_t) (atp - fprincname);

        if ((mname = (char *) malloc(mlen+1))) {
            strncpy(mname, fprincname, mlen);
            mname[mlen] = '\0';
        }
    }
    return(mname);
}

#ifdef ANAME_DB
/*
 * Implementation:  This version uses a DBM database, indexed by aname,
 * to generate a lname.
 *
 * The entries in the database are normal C strings, and include the trailing
 * null in the DBM datum.size.
 */
static krb5_error_code
db_an_to_ln(context, dbname, aname, lnsize, lname)
    krb5_context context;
    char *dbname;
    krb5_const_principal aname;
    const unsigned int lnsize;
    char *lname;
{
#if !defined(_WIN32)
    DBM *db;
    krb5_error_code retval;
    datum key, contents;
    char *princ_name;

    if ((retval = krb5_unparse_name(context, aname, &princ_name)))
        return(retval);
    key.dptr = princ_name;
    key.dsize = strlen(princ_name)+1;   /* need to store the NULL for
                                           decoding */

    db = KDBM_OPEN(dbname, O_RDONLY, 0600);
    if (!db) {
        krb5_xfree(princ_name);
        return KRB5_LNAME_CANTOPEN;
    }

    contents = KDBM_FETCH(db, key);

    krb5_xfree(princ_name);

    if (contents.dptr == NULL) {
        retval = KRB5_LNAME_NOTRANS;
    } else {
        strncpy(lname, contents.dptr, lnsize);
        if (lnsize < contents.dsize)
            retval = KRB5_CONFIG_NOTENUFSPACE;
        else if (lname[contents.dsize-1] != '\0')
            retval = KRB5_LNAME_BADFORMAT;
        else
            retval = 0;
    }
    /* can't close until we copy the contents. */
    (void) KDBM_CLOSE(db);
    return retval;
#else   /* !_WIN32 && !MACINTOSH */
    /*
     * If we don't have support for a database mechanism, then we can't
     * translate this now, can we?
     */
    return KRB5_LNAME_NOTRANS;
#endif  /* !_WIN32 && !MACINTOSH */
}
#endif /*ANAME_DB*/

#ifdef  AN_TO_LN_RULES
/*
 * Format and transform a principal name to a local name.  This is particularly
 * useful when Kerberos principals and local user names are formatted to
 * some particular convention.
 *
 * There are three parts to each rule:
 * First part - formulate the string to perform operations on:  If not present
 * then the string defaults to the fully flattened principal minus the realm
 * name.  Otherwise the syntax is as follows:
 *      "[" <ncomps> ":" <format> "]"
 *              Where:
 *                      <ncomps> is the number of expected components for this
 *                      rule.  If the particular principal does not have this
 *                      many components, then this rule does not apply.
 *
 *                      <format> is a string of <component> or verbatim
 *                      characters to be inserted.
 *
 *                      <component> is of the form "$"<number> to select the
 *                      <number>th component.  <number> begins from 1.
 *
 * Second part - select rule validity:  If not present, then this rule may
 * apply to all selections.  Otherwise the syntax is as follows:
 *      "(" <regexp> ")"
 *              Where:  <regexp> is a selector regular expression.  If this
 *                      regular expression matches the whole pattern generated
 *                      from the first part, then this rule still applies.
 *
 * Last part - Transform rule:  If not present, then the selection string
 * is passed verbatim and is matched.  Otherwise, the syntax is as follows:
 *      <rule> ...
 *              Where:  <rule> is of the form:
 *                      "s/" <regexp> "/" <text> "/" ["g"]
 *
 * In order to be able to select rule validity, the native system must support
 * one of compile(3), re_comp(3) or regcomp(3).  In order to be able to
 * transform (e.g. substitute), the native system must support regcomp(3) or
 * compile(3).
 */

/*
 * aname_do_match()     - Does our name match the parenthesized regular
 *                        expression?
 *
 * Chew up the match portion of the regular expression and update *contextp.
 * If no re_comp() or regcomp(), then always return a match.
 */
static krb5_error_code
aname_do_match(char *string, char **contextp)
{
    krb5_error_code     kret;
    char                *regexp, *startp, *endp = 0;
    size_t              regexlen;
#if     HAVE_REGCOMP
    regex_t             match_exp;
    regmatch_t          match_match;
#elif   HAVE_REGEXPR_H
    char                regexp_buffer[RE_BUF_SIZE];
#endif  /* HAVE_REGEXP_H */

    kret = 0;
    /*
     * Is this a match expression?
     */
    if (**contextp == '(') {
        kret = KRB5_CONFIG_BADFORMAT;
        startp = (*contextp) + 1;
        endp = strchr(startp, ')');
        /* Find the end of the match expression. */
        if (endp) {
            regexlen = (size_t) (endp - startp);
            regexp = (char *) malloc((size_t) regexlen+1);
            kret = ENOMEM;
            if (regexp) {
                strncpy(regexp, startp, regexlen);
                regexp[regexlen] = '\0';
                kret = KRB5_LNAME_NOTRANS;
                /*
                 * Perform the match.
                 */
#if     HAVE_REGCOMP
                if (!regcomp(&match_exp, regexp, REG_EXTENDED) &&
                    !regexec(&match_exp, string, 1, &match_match, 0)) {
                    if ((match_match.rm_so == 0) &&
                        (match_match.rm_eo == strlen(string)))
                        kret = 0;
                }
                regfree(&match_exp);
#elif   HAVE_REGEXPR_H
                compile(regexp,
                        regexp_buffer,
                        &regexp_buffer[RE_BUF_SIZE]);
                if (step(string, regexp_buffer)) {
                    if ((loc1 == string) &&
                        (loc2 == &string[strlen(string)]))
                        kret = 0;
                }
#elif   HAVE_RE_COMP
                if (!re_comp(regexp) && re_exec(string))
                    kret = 0;
#else   /* HAVE_RE_COMP */
                kret = 0;
#endif  /* HAVE_RE_COMP */
                free(regexp);
            }
            endp++;
        }
        else
            endp = startp;
    }
    *contextp = endp;
    return(kret);
}

/*
 * do_replacement()     - Replace the regular expression with the specified
 *                        replacement.
 *
 * If "doall" is set, it's a global replacement, otherwise, just a oneshot
 * deal.
 * If no regcomp() then just return the input string verbatim in the output
 * string.
 */
#define use_bytes(x) \
    out_used += (x); \
    if (out_used > MAX_FORMAT_BUFFER) goto mem_err

static int
do_replacement(char *regexp, char *repl, int doall, char *in, char *out)
{
    size_t out_used = 0;
#if     HAVE_REGCOMP
    regex_t     match_exp;
    regmatch_t  match_match;
    int         matched;
    char        *cp;
    char        *op;

    if (!regcomp(&match_exp, regexp, REG_EXTENDED)) {
        cp = in;
        op = out;
        matched = 0;
        do {
            if (!regexec(&match_exp, cp, 1, &match_match, 0)) {
                if (match_match.rm_so) {
                    use_bytes(match_match.rm_so);
                    strncpy(op, cp, match_match.rm_so);
                    op += match_match.rm_so;
                }
                use_bytes(strlen(repl));
                strncpy(op, repl, MAX_FORMAT_BUFFER - 1 - (op - out));
                op += strlen(op);
                cp += match_match.rm_eo;
                if (!doall) {
                    use_bytes(strlen(cp));
                    strncpy(op, cp, MAX_FORMAT_BUFFER - 1 - (op - out));
                }
                matched = 1;
            }
            else {
                use_bytes(strlen(cp));
                strncpy(op, cp, MAX_FORMAT_BUFFER - 1 - (op - out));
                matched = 0;
            }
        } while (doall && matched);
        regfree(&match_exp);
    }
#elif   HAVE_REGEXPR_H
    int         matched;
    char        *cp;
    char        *op;
    char        regexp_buffer[RE_BUF_SIZE];
    size_t      sdispl, edispl;

    compile(regexp,
            regexp_buffer,
            &regexp_buffer[RE_BUF_SIZE]);
    cp = in;
    op = out;
    matched = 0;
    do {
        if (step(cp, regexp_buffer)) {
            sdispl = (size_t) (loc1 - cp);
            edispl = (size_t) (loc2 - cp);
            if (sdispl) {
                use_bytes(sdispl);
                strncpy(op, cp, sdispl);
                op += sdispl;
            }
            use_bytes(strlen(repl));
            strncpy(op, repl, MAX_FORMAT_BUFFER - 1 - (op - out));
            op += strlen(repl);
            cp += edispl;
            if (!doall) {
                use_bytes(strlen(cp));
                strncpy(op, cp, MAX_FORMAT_BUFFER - 1 - (op - out));
            }
            matched = 1;
        }
        else {
            use_bytes(strlen(cp));
            strncpy(op, cp, MAX_FORMAT_BUFFER - 1 - (op - out));
            matched = 0;
        }
    } while (doall && matched);
#else   /* HAVE_REGEXP_H */
    memcpy(out, in, MAX_FORMAT_BUFFER);
#endif  /* HAVE_REGCOMP */
    return 1;
 mem_err:
#ifdef HAVE_REGCMP
        regfree(&match_exp);
#endif
        return 0;

}
#undef use_bytes

/*
 * aname_replacer()     - Perform the specified substitutions on the input
 *                        string and return the result.
 *
 * This routine enforces the "s/<pattern>/<replacement>/[g]" syntax.
 */
static krb5_error_code
aname_replacer(char *string, char **contextp, char **result)
{
    krb5_error_code     kret;
    char                *in;
    char                *out;
    char                *cp, *ep, *tp;
    char                *rule, *repl;
    size_t              rule_size, repl_size;
    int                 doglobal;
    char                sep;

    kret = ENOMEM;
    *result = (char *) NULL;
    /* Allocate the formatting buffers */
    /* Solaris Kerberos */
    if (((in = (char *) malloc(MAX_FORMAT_BUFFER)) != NULL) &&
        ((out = (char *) malloc(MAX_FORMAT_BUFFER)) != NULL)) {
        /*
         * Prime the buffers.  Copy input string to "out" to simulate it
         * being the result of an initial iteration.
         */
        strncpy(out, string, MAX_FORMAT_BUFFER - 1);
        out[MAX_FORMAT_BUFFER - 1] = '\0';
        in[0] = '\0';
        kret = 0;
        /*
         * Pound through the expression until we're done.
         */
        for (cp = *contextp; *cp; ) {
            /* Skip leading whitespace */
            while (isspace((int) (*cp)))
                cp++;

            /*
             * Solaris Kerberos
             * Find our separators.  First two characters must be "s<sep>"
             * We must also find another "<sep>" followed by another * "<sep>".
             */
            if (cp[0] != 's') {
                /* Bad syntax */
                kret = KRB5_CONFIG_BADFORMAT;
                break;
            }
            if (strspn(cp + 1, ",/;|!%") < 1) {
                /* Bad syntax */
                kret = KRB5_CONFIG_BADFORMAT;
                break;
            }
            sep = cp[1];

            if (((ep = strchr(&cp[2], sep)) != NULL) &&
                ((tp = strchr(&ep[1], sep)) != NULL)) {

                /* Figure out sizes of strings and allocate them */
                rule_size = (size_t) (ep - &cp[2]);
                repl_size = (size_t) (tp - &ep[1]);
                /* Solaris Kerberos */
                if (((rule = (char *) malloc(rule_size+1)) != NULL) &&
                    ((repl = (char *) malloc(repl_size+1)) != NULL)) {

                    /* Copy the strings */
                    strncpy(rule, &cp[2], rule_size);
                    strncpy(repl, &ep[1], repl_size);
                    rule[rule_size] = repl[repl_size] = '\0';

                    /* Check for trailing "g" */
                    doglobal = (tp[1] == 'g') ? 1 : 0;
                    if (doglobal)
                        tp++;

                    /* Swap previous in and out buffers */
                    ep = in;
                    in = out;
                    out = ep;

                    /* Do the replacemenbt */
                    memset(out, '\0', MAX_FORMAT_BUFFER);
                    if (!do_replacement(rule, repl, doglobal, in, out)) {
                        free(rule);
                    free(repl);
                        kret = KRB5_LNAME_NOTRANS;
                        break;
                    }
                    free(rule);
                    free(repl);

                    /* If we have no output buffer left, this can't be good */
                    if (strlen(out) == 0) {
                        kret = KRB5_LNAME_NOTRANS;
                        break;
                    }
                }
                else {
                    /* No memory for copies */
                    kret = ENOMEM;
                    break;
                }
            }
            else {
                /* Bad syntax */
                kret = KRB5_CONFIG_BADFORMAT;
                break;
            }
            /* Advance past trailer */
            cp = &tp[1];
        }
        free(in);
        if (!kret)
            *result = out;
        else
            free(out);
    }
    return(kret);
}

/*
 * rule_an_to_ln()      - Handle aname to lname translations for RULE rules.
 *
 * The initial part of this routine handles the formulation of the strings from
 * the principal name.
 */
static krb5_error_code
rule_an_to_ln(krb5_context context, char *rule, krb5_const_principal aname, const unsigned int lnsize, char *lname)
{
    krb5_error_code     kret;
    char                *current;
    char                *fprincname;
    char                *selstring = 0;
    int                 num_comps, compind;
    size_t selstring_used;
    char                *cout;
    krb5_const krb5_data *datap;
    char                *outstring;

    /*
     * First flatten the name.
     */
    current = rule;
    if (!(kret = krb5_unparse_name(context, aname, &fprincname))) {
        /*
         * First part.
         */
        if (*current == '[') {
            if (sscanf(current+1,"%d:", &num_comps) == 1) {
                if (num_comps == aname->length) {
                    /*
                     * We have a match based on the number of components.
                     */
                    current = strchr(current, ':');
                    selstring = (char *) malloc(MAX_FORMAT_BUFFER);
                    selstring_used = 0;
                    if (current && selstring) {
                        current++;
                        cout = selstring;
                        /*
                         * Plow through the string.
                         */
                        while ((*current != ']') &&
                               (*current != '\0')) {
                            /*
                             * Expand to a component.
                             */
                            if (*current == '$') {
                                if ((sscanf(current+1, "%d", &compind) == 1) &&
                                    (compind <= num_comps) &&
                                    (datap =
                                     (compind > 0)
                                     ? krb5_princ_component(context, aname,
                                                            compind-1)
                                     : krb5_princ_realm(context, aname))
                                    ) {
                                    if ((datap->length < MAX_FORMAT_BUFFER)
                                        &&  (selstring_used+datap->length
                                             < MAX_FORMAT_BUFFER)) {
                                        selstring_used += datap->length;
                                    } else {
                                        kret = ENOMEM;
                                        goto errout;
                                    }
                                    strncpy(cout,
                                            datap->data,
                                            (unsigned) datap->length);
                                    cout += datap->length;
                                    *cout = '\0';
                                    current++;
                                    /* Point past number */
                                    while (isdigit((int) (*current)))
                                        current++;
                                }
                                else
                                    kret = KRB5_CONFIG_BADFORMAT;
                            }
                            else {
                                /* Copy in verbatim. */
                                *cout = *current;
                                cout++;
                                *cout = '\0';
                                current++;
                            }
                        }

                        /*
                         * Advance past separator if appropriate.
                         */
                        if (*current == ']')
                            current++;
                        else
                            kret = KRB5_CONFIG_BADFORMAT;

                        errout: if (kret)
                            free(selstring);
                    }
                }
                else
                    kret = KRB5_LNAME_NOTRANS;
            }
            else
                kret = KRB5_CONFIG_BADFORMAT;
        }
        else {
            if (!(selstring = aname_full_to_mapping_name(fprincname)))
                kret = ENOMEM;
        }
        krb5_xfree(fprincname);
    }
    if (!kret) {
        /*
         * Second part
         */
        if (*current == '(')
            kret = aname_do_match(selstring, &current);

        /*
         * Third part.
         */
        if (!kret) {
            outstring = (char *) NULL;
            kret = aname_replacer(selstring, &current, &outstring);
            if (outstring) {
                /* Copy out the value if there's enough room */
                if (strlen(outstring)+1 <= (size_t) lnsize)
                    strcpy(lname, outstring);
                else
                    kret = KRB5_CONFIG_NOTENUFSPACE;
                free(outstring);
            }
        }
        free(selstring);
    }

    return(kret);
}
#endif  /* AN_TO_LN_RULES */

/*
 * Solaris Kerberos
 * Return true (1) if the princ's realm matches any of the
 * 'auth_to_local_realm' relations in the default realm section.
 */
static int
an_to_ln_realm_chk(
        krb5_context context,
        krb5_const_principal aname,
        char *def_realm,
        int realm_length)
{
        char        **values, **cpp;
        const char  *realm_names[4];
        krb5_error_code     retval;

        realm_names[0] = "realms";
        realm_names[1] = def_realm;
        realm_names[2] = "auth_to_local_realm";
        realm_names[3] = 0;

        if (context->profile == 0)
                return (0);

        retval = profile_get_values(context->profile, realm_names,
                                    &values);
        if (retval)
                return (0);

        for (cpp = values; *cpp; cpp++) {

                if (((size_t) realm_length == strlen(*cpp)) &&
                    (memcmp(*cpp, krb5_princ_realm(context, aname)->data,
                            realm_length) == 0)) {

                        profile_free_list(values);
                        return (1); /* success */
                }
        }

        profile_free_list(values);
        return (0);
}

/*
 * Implementation:  This version checks the realm to see if it is the local
 * realm; if so, and there is exactly one non-realm component to the name,
 * that name is returned as the lname.
 */
static krb5_error_code
default_an_to_ln(krb5_context context, krb5_const_principal aname, const unsigned int lnsize, char *lname)
{
    krb5_error_code retval;
    char *def_realm;
    unsigned int realm_length;

    realm_length = krb5_princ_realm(context, aname)->length;

    if ((retval = krb5_get_default_realm(context, &def_realm))) {
        return(retval);
    }
    /* Solaris Kerberos */
    /* compare against default realm and auth_to_local_realm(s) */
    if ((((size_t) realm_length != strlen(def_realm)) ||
        (memcmp(def_realm, krb5_princ_realm(context, aname)->data,
                realm_length))) &&
        !an_to_ln_realm_chk(context, aname, def_realm,
                            realm_length)) {
        free(def_realm);
        return KRB5_LNAME_NOTRANS;
    }

    if (krb5_princ_size(context, aname) != 1) {
        if (krb5_princ_size(context, aname) == 2 ) {
           /* Check to see if 2nd component is the local realm. */
           if ( strncmp(krb5_princ_component(context, aname,1)->data,def_realm,
                        realm_length) ||
                realm_length != krb5_princ_component(context, aname,1)->length) {
                    /* XXX an_to_ln_realm_chk ? */
                /* Solaris Kerberos */
                free(def_realm);
                return KRB5_LNAME_NOTRANS;
            }
        }
        else {
           /* no components or more than one component to non-realm part of name
           --no translation. */
            /* Solaris Kerberos */
            free(def_realm);
            return KRB5_LNAME_NOTRANS;
        }
    }

    free(def_realm);
    strncpy(lname, krb5_princ_component(context, aname,0)->data,
            min(krb5_princ_component(context, aname,0)->length,lnsize));
    if (lnsize <= krb5_princ_component(context, aname,0)->length ) {
        retval = KRB5_CONFIG_NOTENUFSPACE;
    } else {
        lname[krb5_princ_component(context, aname,0)->length] = '\0';
        retval = 0;
    }
    return retval;
}

/*
 Converts an authentication name to a local name suitable for use by
 programs wishing a translation to an environment-specific name (e.g.
 user account name).

 lnsize specifies the maximum length name that is to be filled into
 lname.
 The translation will be null terminated in all non-error returns.

 returns system errors, NOT_ENOUGH_SPACE
*/

krb5_error_code KRB5_CALLCONV
krb5_aname_to_localname(krb5_context context, krb5_const_principal aname, const int lnsize_in, char *lname)
{
    krb5_error_code     kret;
    char                *realm;
    char                *pname;
    char                *mname;
    const char          *hierarchy[5];
    char                **mapping_values;
    int                 i, nvalid;
    char                *cp, *s;
    char                *typep, *argp;
    unsigned int        lnsize;

    if (lnsize_in < 0)
      return KRB5_CONFIG_NOTENUFSPACE;

    lnsize = lnsize_in; /* Unsigned */

    /*
     * First get the default realm.
     */
    if (!(kret = krb5_get_default_realm(context, &realm))) {
        /* Flatten the name */
        if (!(kret = krb5_unparse_name(context, aname, &pname))) {
            if ((mname = aname_full_to_mapping_name(pname))) {
                /*
                 * Search first for explicit mappings of the form:
                 *
                 * [realms]->realm->"auth_to_local_names"->mapping_name
                 */
                hierarchy[0] = "realms";
                hierarchy[1] = realm;
                hierarchy[2] = "auth_to_local_names";
                hierarchy[3] = mname;
                hierarchy[4] = (char *) NULL;
                if (!(kret = profile_get_values(context->profile,
                                                hierarchy,
                                                &mapping_values))) {
                    /* We found one or more explicit mappings. */
                    for (nvalid=0; mapping_values[nvalid]; nvalid++);

                    /* Just use the last one. */
                    /* Trim the value. */
                    s = mapping_values[nvalid-1];
                    cp = s + strlen(s);
                    while (cp > s) {
                        cp--;
                        if (!isspace((int)(*cp)))
                            break;
                        *cp = '\0';
                    }

                    /* Copy out the value if there's enough room */
                    if (strlen(mapping_values[nvalid-1])+1 <= (size_t) lnsize)
                        strcpy(lname, mapping_values[nvalid-1]);
                    else
                        kret = KRB5_CONFIG_NOTENUFSPACE;

                    /* Free residue */
                    profile_free_list(mapping_values);
                }
                else {
                    /*
                     * OK - There's no explicit mapping.  Now check for
                     * general auth_to_local rules of the form:
                     *
                     * [realms]->realm->"auth_to_local"
                     *
                     * This can have one or more of the following kinds of
                     * values:
                     *  DB:<filename>   - Look up principal in aname database.
                     *  RULE:<sed-exp>  - Formulate lname from sed-exp.
                     *  DEFAULT         - Use default rule.
                     * The first rule to find a match is used.
                     */
                    hierarchy[0] = "realms";
                    hierarchy[1] = realm;
                    hierarchy[2] = "auth_to_local";
                    hierarchy[3] = (char *) NULL;
                    if (!(kret = profile_get_values(context->profile,
                                                    hierarchy,
                                                    &mapping_values))) {
                        /*
                         * Loop through all the mapping values.
                         */
                        for (i=0; mapping_values[i]; i++) {
                            typep = mapping_values[i];
                            argp = strchr(typep, ':');
                            if (argp) {
                                *argp = '\0';
                                argp++;
                            }
#ifdef ANAME_DB
                            if (!strcmp(typep, "DB") && argp) {
                                kret = db_an_to_ln(context,
                                                   argp,
                                                   aname,
                                                   lnsize,
                                                   lname);
                                if (kret != KRB5_LNAME_NOTRANS)
                                    break;
                            }
                            else
#endif
#ifdef  AN_TO_LN_RULES
                            if (!strcmp(typep, "RULE") && argp) {
                                kret = rule_an_to_ln(context,
                                                     argp,
                                                     aname,
                                                     lnsize,
                                                     lname);
                                if (kret != KRB5_LNAME_NOTRANS)
                                    break;
                            }
                            else
#endif  /* AN_TO_LN_RULES */
                            if (!strcmp(typep, "DEFAULT") && !argp) {
                                kret = default_an_to_ln(context,
                                                        aname,
                                                        lnsize,
                                                        lname);
                                if (kret != KRB5_LNAME_NOTRANS)
                                    break;
                            }
                            else {
                                kret = KRB5_CONFIG_BADFORMAT;
                                break;
                            }
                        }

                        /* We're done, clean up the droppings. */
                        profile_free_list(mapping_values);
                    }
                    else {
                        /*
                         * No profile relation found, try default mapping.
                         */
                        kret = default_an_to_ln(context,
                                                aname,
                                                lnsize,
                                                lname);
                    }
                }
                free(mname);
            }
            else
                kret = ENOMEM;
            krb5_xfree(pname);
        }
        krb5_xfree(realm);
    }
    return(kret);
}