krb5 commit: Add localauth pluggable interface

Greg Hudson ghudson at MIT.EDU
Sat Mar 9 02:13:19 EST 2013


https://github.com/krb5/krb5/commit/4216fb5b0e0abb80a3ccd8251abddc18435d81f3
commit 4216fb5b0e0abb80a3ccd8251abddc18435d81f3
Author: Greg Hudson <ghudson at mit.edu>
Date:   Wed Feb 13 15:29:48 2013 -0500

    Add localauth pluggable interface
    
    Add a new pluggable interface for local authorization, and replace the
    existing krb5_aname_to_localname and krb5_kuserok implementations with
    implementations based on the pluggable interface.
    
    ticket: 7583 (new)

 .gitignore                          |    2 -
 src/include/k5-int.h                |    7 +-
 src/include/k5-trace.h              |    8 +
 src/include/krb5/localauth_plugin.h |  139 ++++++
 src/lib/krb5/krb/plugin.c           |    3 +-
 src/lib/krb5/os/Makefile.in         |   73 +---
 src/lib/krb5/os/an_to_ln.c          |  784 -----------------------------------
 src/lib/krb5/os/deps                |  132 +++++--
 src/lib/krb5/os/kuserok.c           |  203 ---------
 src/lib/krb5/os/localauth.c         |  455 ++++++++++++++++++++
 src/lib/krb5/os/localauth_an2ln.c   |   59 +++
 src/lib/krb5/os/localauth_k5login.c |  183 ++++++++
 src/lib/krb5/os/localauth_names.c   |  102 +++++
 src/lib/krb5/os/localauth_rule.c    |  338 +++++++++++++++
 src/lib/krb5/os/os-proto.h          |    9 +
 15 files changed, 1426 insertions(+), 1071 deletions(-)

diff --git a/.gitignore b/.gitignore
index fbb9252..697807f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -206,9 +206,7 @@ testlog
 /src/lib/krb5/krb/t_walk_rtree
 /src/lib/krb5/krb/t_response_items
 
-/src/lib/krb5/os/t_an_to_ln
 /src/lib/krb5/os/t_expand_path
-/src/lib/krb5/os/t_kuserok
 /src/lib/krb5/os/t_locate_kdc
 /src/lib/krb5/os/t_std_conf
 /src/lib/krb5/os/t_trace
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index 74b12af..c60035b 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -1308,7 +1308,8 @@ struct plugin_interface {
 #define PLUGIN_INTERFACE_CLPREAUTH   2
 #define PLUGIN_INTERFACE_KDCPREAUTH  3
 #define PLUGIN_INTERFACE_CCSELECT    4
-#define PLUGIN_NUM_INTERFACES        5
+#define PLUGIN_INTERFACE_LOCALAUTH   5
+#define PLUGIN_NUM_INTERFACES        6
 
 /* Retrieve the plugin module of type interface_id and name modname,
  * storing the result into module. */
@@ -1349,6 +1350,7 @@ typedef struct _kdb5_dal_handle kdb5_dal_handle;
 struct _kdb_log_context;
 typedef struct krb5_preauth_context_st krb5_preauth_context;
 struct ccselect_module_handle;
+struct localauth_module_handle;
 struct _krb5_context {
     krb5_magic      magic;
     krb5_enctype    *in_tkt_etypes;
@@ -1392,6 +1394,9 @@ struct _krb5_context {
     /* cache module stuff */
     struct ccselect_module_handle **ccselect_handles;
 
+    /* localauth module stuff */
+    struct localauth_module_handle **localauth_handles;
+
     /* error detail info */
     struct errinfo err;
 
diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
index 53dcba5..1aa09ce 100644
--- a/src/include/k5-trace.h
+++ b/src/include/k5-trace.h
@@ -219,6 +219,14 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
     TRACE(c, "Retrieving {princ} from {keytab} (vno {int}, enctype {etype}) " \
           "with result: {kerr}", princ, keytab, (int) vno, enctype, err)
 
+#define TRACE_LOCALAUTH_INIT_CONFLICT(c, type, oldname, newname)        \
+    TRACE(c, "Ignoring localauth module {str} because it conflicts "    \
+          "with an2ln type {str} from module {str}", newname, type, oldname)
+#define TRACE_LOCALAUTH_VTINIT_FAIL(c, ret)                             \
+    TRACE(c, "localauth module failed to init vtable: {kerr}", ret)
+#define TRACE_LOCALAUTH_INIT_FAIL(c, name, ret)                         \
+    TRACE(c, "localauth module {str} failed to init: {kerr}", name, ret)
+
 #define TRACE_MK_REP(c, ctime, cusec, subkey, seqnum)                   \
     TRACE(c, "Creating AP-REP, time {long}.{int}, subkey {keyblock}, "  \
           "seqnum {int}", (long) ctime, (int) cusec, subkey, (int) seqnum)
diff --git a/src/include/krb5/localauth_plugin.h b/src/include/krb5/localauth_plugin.h
new file mode 100644
index 0000000..1c6165b
--- /dev/null
+++ b/src/include/krb5/localauth_plugin.h
@@ -0,0 +1,139 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Declarations for localauth plugin module implementors.
+ *
+ * The localauth pluggable interface currently has only one supported major
+ * version, which is 1.  Major version 1 has a current minor version number of
+ * 1.
+ *
+ * Localauth plugin modules should define a function named
+ * localauth_<modulename>_initvt, matching the signature:
+ *
+ *   krb5_error_code
+ *   localauth_modname_initvt(krb5_context context, int maj_ver, int min_ver,
+ *                            krb5_plugin_vtable vtable);
+ *
+ * The initvt function should:
+ *
+ * - Check that the supplied maj_ver number is supported by the module, or
+ *   return KRB5_PLUGIN_VER_NOTSUPP if it is not.
+ *
+ * - Cast the vtable pointer as appropriate for maj_ver:
+ *     maj_ver == 1: Cast to krb5_localauth_vtable
+ *
+ * - Initialize the methods of the vtable, stopping as appropriate for the
+ *   supplied min_ver.  Optional methods may be left uninitialized.
+ *
+ * Memory for the vtable is allocated by the caller, not by the module.
+ */
+
+#ifndef KRB5_LOCALAUTH_PLUGIN_H
+#define KRB5_LOCALAUTH_PLUGIN_H
+
+#include <krb5/krb5.h>
+#include <krb5/plugin.h>
+#include <kadm5/admin.h>
+
+/* An abstract type for localauth module data. */
+typedef struct krb5_localauth_moddata_st *krb5_localauth_moddata;
+
+/*** Method type declarations ***/
+
+/* Optional: Initialize module data. */
+typedef krb5_error_code
+(*krb5_localauth_init_fn)(krb5_context context,
+                          krb5_localauth_moddata *data);
+
+/* Optional: Release resources used by module data. */
+typedef void
+(*krb5_localauth_fini_fn)(krb5_context context, krb5_localauth_moddata data);
+
+/*
+ * Optional: Determine whether aname is authorized to log in as the local
+ * account lname.  Return 0 if aname is authorized, EPERM if aname is
+ * authoritatively not authorized, KRB5_PLUGIN_NO_HANDLE if the module cannot
+ * determine whether aname is authorized, and any other error code for a
+ * serious failure to process the request.  aname will be considered authorized
+ * if at least one module returns 0 and all other modules return
+ * KRB5_PLUGIN_NO_HANDLE.
+ */
+typedef krb5_error_code
+(*krb5_localauth_userok_fn)(krb5_context context, krb5_localauth_moddata data,
+                            krb5_const_principal aname, const char *lname);
+
+/*
+ * Optional (mandatory if an2ln_types is set): Determine the local account name
+ * corresponding to aname.  Return 0 and set *lname_out if a mapping can be
+ * determined; the contents of *lname_out will later be released with a call to
+ * the module's free_string method.  Return KRB5_LNAME_NOTRANS if no mapping
+ * can be determined.  Return any other error code for a serious failure to
+ * process the request; this will halt the krb5_aname_to_localname operation.
+ *
+ * If the module's an2ln_types field is set, this method will only be invoked
+ * when a profile "auth_to_local" value references one of the module's types.
+ * type and residual will be set to the type and residual of the auth_to_local
+ * value.
+ *
+ * If the module's an2ln_types field is not set but the an2ln method is
+ * implemented, this method will be invoked independently of the profile's
+ * auth_to_local settings, with type and residual set to NULL.  If multiple
+ * modules are registered with an2ln methods but no an2ln_types field, the
+ * order of invocation is not defined, but all such modules will be consulted
+ * before the built-in mechanisms are tried.
+ */
+typedef krb5_error_code
+(*krb5_localauth_an2ln_fn)(krb5_context context, krb5_localauth_moddata data,
+                           const char *type, const char *residual,
+                           krb5_const_principal aname, char **lname_out);
+
+/*
+ * Optional (mandatory if an2ln is implemented): Release the memory returned by
+ * an invocation of an2ln.
+ */
+typedef void
+(*krb5_localauth_free_string_fn)(krb5_context context,
+                                 krb5_localauth_moddata data, char *str);
+
+/* localauth vtable for major version 1. */
+typedef struct krb5_localauth_vtable_st {
+    const char *name;           /* Mandatory: name of module. */
+    const char **an2ln_types;   /* Optional: uppercase auth_to_local types */
+    krb5_localauth_init_fn init;
+    krb5_localauth_fini_fn fini;
+    krb5_localauth_userok_fn userok;
+    krb5_localauth_an2ln_fn an2ln;
+    krb5_localauth_free_string_fn free_string;
+    /* Minor version 1 ends here. */
+} *krb5_localauth_vtable;
+
+#endif /* KRB5_LOCALAUTH_PLUGIN_H */
diff --git a/src/lib/krb5/krb/plugin.c b/src/lib/krb5/krb/plugin.c
index 9b2328b..12945b4 100644
--- a/src/lib/krb5/krb/plugin.c
+++ b/src/lib/krb5/krb/plugin.c
@@ -31,7 +31,8 @@ const char *interface_names[] = {
     "kadm5_hook",
     "clpreauth",
     "kdcpreauth",
-    "ccselect"
+    "ccselect",
+    "localauth"
 };
 
 /* Return the context's interface structure for id, or NULL if invalid. */
diff --git a/src/lib/krb5/os/Makefile.in b/src/lib/krb5/os/Makefile.in
index 0ffbe08..bf61557 100644
--- a/src/lib/krb5/os/Makefile.in
+++ b/src/lib/krb5/os/Makefile.in
@@ -14,7 +14,6 @@ LOCALINCLUDES=-I$(top_srcdir)/util/profile
 
 STLIBOBJS= \
 	accessor.o	\
-	an_to_ln.o	\
 	c_ustime.o	\
 	def_realm.o	\
 	ccdefname.o	\
@@ -34,9 +33,13 @@ STLIBOBJS= \
 	init_os_ctx.o	\
 	krbfileio.o	\
 	ktdefname.o	\
-	kuserok.o	\
 	mk_faddr.o	\
 	localaddr.o	\
+	localauth.o	\
+	localauth_an2ln.o \
+	localauth_k5login.o \
+	localauth_names.o \
+	localauth_rule.o \
 	locate_kdc.o	\
 	lock_file.o	\
 	net_read.o	\
@@ -59,7 +62,6 @@ STLIBOBJS= \
 
 OBJS= \
 	$(OUTPRE)accessor.$(OBJEXT)	\
-	$(OUTPRE)an_to_ln.$(OBJEXT)	\
 	$(OUTPRE)c_ustime.$(OBJEXT)	\
 	$(OUTPRE)def_realm.$(OBJEXT)	\
 	$(OUTPRE)ccdefname.$(OBJEXT)	\
@@ -79,9 +81,13 @@ OBJS= \
 	$(OUTPRE)init_os_ctx.$(OBJEXT)	\
 	$(OUTPRE)krbfileio.$(OBJEXT)	\
 	$(OUTPRE)ktdefname.$(OBJEXT)	\
-	$(OUTPRE)kuserok.$(OBJEXT)	\
 	$(OUTPRE)mk_faddr.$(OBJEXT)	\
 	$(OUTPRE)localaddr.$(OBJEXT)	\
+	$(OUTPRE)localauth.$(OBJEXT)	\
+	$(OUTPRE)localauth_an2ln.$(OBJEXT) \
+	$(OUTPRE)localauth_k5login.$(OBJEXT) \
+	$(OUTPRE)localauth_names.$(OBJEXT) \
+	$(OUTPRE)localauth_rule.$(OBJEXT) \
 	$(OUTPRE)locate_kdc.$(OBJEXT)	\
 	$(OUTPRE)lock_file.$(OBJEXT)	\
 	$(OUTPRE)net_read.$(OBJEXT)	\
@@ -104,7 +110,6 @@ OBJS= \
 
 SRCS= \
 	$(srcdir)/accessor.c    \
-	$(srcdir)/an_to_ln.c	\
 	$(srcdir)/c_ustime.c	\
 	$(srcdir)/def_realm.c	\
 	$(srcdir)/ccdefname.c	\
@@ -124,9 +129,13 @@ SRCS= \
 	$(srcdir)/init_os_ctx.c	\
 	$(srcdir)/krbfileio.c	\
 	$(srcdir)/ktdefname.c	\
-	$(srcdir)/kuserok.c	\
 	$(srcdir)/mk_faddr.c	\
 	$(srcdir)/localaddr.c	\
+	$(srcdir)/localauth.c	\
+	$(srcdir)/localauth_an2ln.c \
+	$(srcdir)/localauth_k5login.c \
+	$(srcdir)/localauth_names.c \
+	$(srcdir)/localauth_rule.c \
 	$(srcdir)/locate_kdc.c	\
 	$(srcdir)/lock_file.c	\
 	$(srcdir)/net_read.c	\
@@ -148,8 +157,7 @@ SRCS= \
 	$(srcdir)/write_msg.c
 
 EXTRADEPSRCS = \
-	t_an_to_ln.c t_expand_path.c t_gifconf.c t_kuserok.c t_locate_kdc.c \
-	t_std_conf.c t_trace.c
+	t_expand_path.c t_gifconf.c t_locate_kdc.c t_std_conf.c t_trace.c
 
 ##DOS##LIBOBJS = $(OBJS)
 
@@ -159,25 +167,15 @@ clean-unix:: clean-libobjs
 shared:
 	mkdir shared
 
-TEST_PROGS= t_std_conf t_an_to_ln t_kuserok t_locate_kdc t_trace t_expand_path
+TEST_PROGS= t_std_conf t_locate_kdc t_trace t_expand_path
 
 T_STD_CONF_OBJS= t_std_conf.o 
 
-T_AN_TO_LN_OBJS = t_an_to_ln.o an_to_ln.o 
-
-T_KUSEROK_OBJS = t_kuserok.o
-
 T_TRACE_OBJS = t_trace.o
 
 t_std_conf: $(T_STD_CONF_OBJS) $(KRB5_BASE_DEPLIBS)
 	$(CC_LINK) -o t_std_conf $(T_STD_CONF_OBJS) $(KRB5_BASE_LIBS)
 
-t_an_to_ln: $(T_AN_TO_LN_OBJS) $(KRB5_BASE_DEPLIBS)
-	$(CC_LINK) -o t_an_to_ln $(T_AN_TO_LN_OBJS) $(KRB5_BASE_LIBS)
-
-t_kuserok: $(T_KUSEROK_OBJS) $(KRB5_BASE_DEPLIBS)
-	$(CC_LINK) -o t_kuserok $(T_KUSEROK_OBJS) $(KRB5_BASE_LIBS)
-
 t_localaddr: localaddr.c
 	$(CC_LINK) $(ALL_CFLAGS) -DTEST -o t_localaddr $(srcdir)/localaddr.c $(KRB5_BASE_LIBS) $(LIBS)
 
@@ -204,8 +202,8 @@ lclint-localaddr: localaddr.c
 	$(LCLINT) $(LCLINTOPTS) $(CPPFLAGS) $(LOCALINCLUDES) $(DEFS) \
 		-DTEST $(srcdir)/localaddr.c
 
-check-unix:: check-unix-stdconf check-unix-locate check-unix-antoln \
-	check-unix-trace check-unix-expand t_kuserok
+check-unix:: check-unix-stdconf check-unix-locate check-unix-trace \
+	check-unix-expand
 
 check-unix-stdconf:: t_std_conf
 	KRB5_CONFIG=$(srcdir)/td_krb5.conf ; export KRB5_CONFIG ;\
@@ -238,35 +236,6 @@ check-unix-locate:: t_locate_kdc
 		echo '*** WARNING: skipped t_locate_kdc test: OFFLINE'; \
 	fi
 
-#
-# Do some aname-to-lname testing.
-#
-check-unix-antoln:: t_an_to_ln
-	echo '[libdefaults]' > ./t_an.conf
-	echo '	default_realm = r' >> ./t_an.conf
-	echo '[realms]' >> ./t_an.conf
-	echo 'r = {' >> ./t_an.conf
-#	if test -r ../../../admin/aname/kdb5_anadd ; then \
-#		$(KRB5_RUN_ENV) $(VALGRIND) ../../../admin/aname/kdb5_anadd -a -n ./t_an p/i/i/i at r piii; \
-#		../../../admin/aname/kdb5_anadd -a -n ./t_an p/a/b/c at r pabc; \
-#		echo 'auth_to_local = DB:./t_an' >> ./t_an.conf; \
-#	fi
-	echo 'auth_to_local = RULE:[3:$$1$$3$$2](rule.*)s/rule//g' \
-		>> ./t_an.conf
-	echo 'auth_to_local = RULE:[4:wi$$1ma]s/x/l/g' \
-		>> ./t_an.conf
-	echo 'auth_to_local = DEFAULT' >> ./t_an.conf
-	echo '}' >> ./t_an.conf
-#	if test -r ../../../admin/aname/kdb5_anadd ; then \
-#		KRB5_CONFIG=./t_an.conf ; export KRB5_CONFIG ; \
-#		$(KRB5_RUN_ENV) $(VALGRIND) ./t_an_to_ln p/i/i/i at r p/a/b/c at r; \
-#	fi
-	KRB5_CONFIG=./t_an.conf ; export KRB5_CONFIG ; \
-	$(KRB5_RUN_ENV) $(VALGRIND) ./t_an_to_ln rul/helpme/e at r ru/123/le at r
-	KRB5_CONFIG=./t_an.conf ; export KRB5_CONFIG ; \
-	$(KRB5_RUN_ENV) $(VALGRIND) ./t_an_to_ln fred/r at r barney/r at r x/r/r/r at r
-	$(RM) ./t_an.*
-
 check-unix-trace:: t_trace
 	rm -f t_trace.out
 	KRB5_TRACE=t_trace.out ; export KRB5_TRACE ; \
@@ -283,8 +252,8 @@ check-unix-expand:: t_expand_path
 		'the frogs on the pads'
 
 clean:: 
-	$(RM) $(TEST_PROGS) test.out t_std_conf.o t_an_to_ln.o t_locate_kdc.o
-	$(RM) t_kuserok.o t_trace.o t_expand_path.o
+	$(RM) $(TEST_PROGS) test.out t_std_conf.o t_locate_kdc.o t_trace.o
+	$(RM) t_expand_path.o
 
 @libobj_frag@
 
diff --git a/src/lib/krb5/os/an_to_ln.c b/src/lib/krb5/os/an_to_ln.c
deleted file mode 100644
index 54b98f6..0000000
--- a/src/lib/krb5/os/an_to_ln.c
+++ /dev/null
@@ -1,784 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* lib/krb5/os/an_to_ln.c */
-/*
- * Copyright 1990,1991,2007,2008 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.
- */
-
-/*
- * 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       ((size_t)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) {
-        free(princ_name);
-        return KRB5_LNAME_CANTOPEN;
-    }
-
-    contents = KDBM_FETCH(db, key);
-
-    free(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) &&
-                        ((unsigned int) 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 = NULL, *out = NULL, *rule = NULL, *repl = NULL;
-    char                *cp, *ep, *tp;
-    int                 doglobal;
-
-    *result = NULL;
-
-    /* Allocate the formatting buffers */
-    in = malloc(MAX_FORMAT_BUFFER);
-    if (!in)
-        return ENOMEM;
-    out = malloc(MAX_FORMAT_BUFFER);
-    if (!out) {
-        kret = ENOMEM;
-        goto cleanup;
-    }
-
-    /*
-     * Prime the buffers.  Copy input string to "out" to simulate it
-     * being the result of an initial iteration.
-     */
-    strlcpy(out, string, MAX_FORMAT_BUFFER);
-    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++;
-
-        /*
-         * Find our separators.  First two characters must be "s/"
-         * We must also find another "/" followed by another "/".
-         */
-        if (!((cp[0] == 's') &&
-              (cp[1] == '/') &&
-              (ep = strchr(&cp[2], '/')) &&
-              (tp = strchr(&ep[1], '/')))) {
-            /* Bad syntax */
-            kret = KRB5_CONFIG_BADFORMAT;
-            goto cleanup;
-        }
-
-        /* Copy the rule and replacement strings. */
-        rule = k5memdup0(&cp[2], ep - &cp[2], &kret);
-        if (rule == NULL)
-            goto cleanup;
-        repl = k5memdup0(&ep[1], tp - &ep[1], &kret);
-        if (repl == NULL)
-            goto cleanup;
-
-        /* 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)) {
-            kret = KRB5_LNAME_NOTRANS;
-            goto cleanup;
-        }
-        free(rule);
-        free(repl);
-        rule = repl = NULL;
-
-        /* If we have no output buffer left, this can't be good */
-        if (strlen(out) == 0) {
-            kret = KRB5_LNAME_NOTRANS;
-            goto cleanup;
-        }
-
-        /* Advance past trailer */
-        cp = &tp[1];
-    }
-    free(in);
-    *result = out;
-    return 0;
-cleanup:
-    free(in);
-    free(out);
-    free(repl);
-    free(rule);
-    return kret;
-}
-
-/*
- * Compute selection string for RULE rules.
- *
- * Advance *contextp to the string position after the selectring
- * string part if present, and set *result to the selection string.
- */
-static krb5_error_code
-aname_get_selstring(krb5_context context, krb5_const_principal aname,
-                    char **contextp, char **result)
-{
-    krb5_error_code kret;
-    char *fprincname, *current, *str;
-    long num_comps, compind;
-    const krb5_data *datap;
-    struct k5buf selstring;
-    size_t nlit;
-
-    *result = NULL;
-    if (**contextp != '[') {
-        /* No selstring part; use the full flattened principal name. */
-        kret = krb5_unparse_name(context, aname, &fprincname);
-        if (kret)
-            return kret;
-        str = aname_full_to_mapping_name(fprincname);
-        free(fprincname);
-        if (!str)
-            return ENOMEM;
-        *result = str;
-        return 0;
-    }
-
-    /* Advance past the '[' and read the number of components. */
-    current = *contextp + 1;
-    errno = 0;
-    num_comps = strtol(current, &current, 10);
-    if (errno != 0 || num_comps < 0 || *current != ':')
-        return KRB5_CONFIG_BADFORMAT;
-    if (num_comps != aname->length)
-        return KRB5_LNAME_NOTRANS;
-    current++;
-
-    k5_buf_init_dynamic(&selstring);
-    while (1) {
-        /* Copy in literal characters up to the next $ or ]. */
-        nlit = strcspn(current, "$]");
-        k5_buf_add_len(&selstring, current, nlit);
-        current += nlit;
-        if (*current != '$')
-            break;
-
-        /* Expand $ substitution to a principal component. */
-        errno = 0;
-        compind = strtol(current + 1, &current, 10);
-        if (errno || compind > num_comps)
-            break;
-        datap = (compind > 0)
-            ? krb5_princ_component(context, aname, compind - 1)
-            : krb5_princ_realm(context, aname);
-        if (!datap)
-            break;
-        k5_buf_add_len(&selstring, datap->data, datap->length);
-    }
-
-    /* Check that we hit a ']' and not the end of the string. */
-    if (*current != ']') {
-        k5_free_buf(&selstring);
-        return KRB5_CONFIG_BADFORMAT;
-    }
-
-    str = k5_buf_data(&selstring);
-    if (str == NULL)
-        return ENOMEM;
-
-    *contextp = current + 1;
-    *result = str;
-    return 0;
-}
-
-/* Handle aname to lname translations for RULE rules. */
-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, *selstring = 0, *outstring = 0;
-
-    /* Compute the selection string. */
-    current = rule;
-    kret = aname_get_selstring(context, aname, &current, &selstring);
-    if (kret)
-        return kret;
-
-    /* Check the selection string against the regexp, if present. */
-    if (*current == '(') {
-        kret = aname_do_match(selstring, &current);
-        if (kret)
-            goto cleanup;
-    }
-
-    /* Perform the substitution. */
-    outstring = NULL;
-    kret = aname_replacer(selstring, &current, &outstring);
-    if (kret)
-        goto cleanup;
-
-    /* Copy out the value if there's enough room. */
-    if (strlcpy(lname, outstring, lnsize) >= lnsize)
-        kret = KRB5_CONFIG_NOTENUFSPACE;
-
-cleanup:
-    free(selstring);
-    free(outstring);
-    return kret;
-}
-#endif  /* AN_TO_LN_RULES */
-
-/*
- * 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 ret;
-    char *def_realm;
-
-    ret = krb5_get_default_realm(context, &def_realm);
-    if (ret)
-        return ret;
-
-    if (!data_eq_string(aname->realm, def_realm)) {
-        ret = KRB5_LNAME_NOTRANS;
-    } else if (aname->length == 2) {
-        /* Check to see if second component is the local realm. */
-        if (!data_eq_string(aname->data[1], def_realm))
-            ret = KRB5_LNAME_NOTRANS;
-    } else if (aname->length != 1) {
-        ret = KRB5_LNAME_NOTRANS;
-    }
-    free(def_realm);
-    if (ret)
-        return ret;
-
-    if (aname->data[0].length >= lnsize)
-        return KRB5_CONFIG_NOTENUFSPACE;
-    memcpy(lname, aname->data[0].data, aname->data[0].length);
-    lname[aname->data[0].length] = '\0';
-    return 0;
-}
-
-/*
-  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, 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] = KRB5_CONF_REALMS;
-                hierarchy[1] = realm;
-                hierarchy[2] = KRB5_CONF_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 (strlcpy(lname, mapping_values[nvalid-1],
-                                lnsize) >= lnsize)
-                        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] = KRB5_CONF_REALMS;
-                    hierarchy[1] = realm;
-                    hierarchy[2] = KRB5_CONF_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;
-            free(pname);
-        }
-        free(realm);
-    }
-    return(kret);
-}
diff --git a/src/lib/krb5/os/deps b/src/lib/krb5/os/deps
index 130c38b..a870f50 100644
--- a/src/lib/krb5/os/deps
+++ b/src/lib/krb5/os/deps
@@ -13,17 +13,6 @@ accessor.so accessor.po $(OUTPRE)accessor.$(OBJEXT): \
   $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   accessor.c os-proto.h
-an_to_ln.so an_to_ln.po $(OUTPRE)an_to_ln.$(OBJEXT): \
-  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
-  $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
-  $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h $(top_srcdir)/include/k5-err.h \
-  $(top_srcdir)/include/k5-gmt_mktime.h $(top_srcdir)/include/k5-int-pkinit.h \
-  $(top_srcdir)/include/k5-int.h $(top_srcdir)/include/k5-platform.h \
-  $(top_srcdir)/include/k5-plugin.h $(top_srcdir)/include/k5-thread.h \
-  $(top_srcdir)/include/k5-trace.h $(top_srcdir)/include/krb5.h \
-  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/clpreauth_plugin.h \
-  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h an_to_ln.c
 c_ustime.so c_ustime.po $(OUTPRE)c_ustime.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -252,17 +241,6 @@ ktdefname.so ktdefname.po $(OUTPRE)ktdefname.$(OBJEXT): \
   $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   ktdefname.c os-proto.h
-kuserok.so kuserok.po $(OUTPRE)kuserok.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
-  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
-  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/k5-buf.h \
-  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
-  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
-  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
-  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
-  $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/authdata_plugin.h \
-  $(top_srcdir)/include/krb5/clpreauth_plugin.h $(top_srcdir)/include/krb5/plugin.h \
-  $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-  kuserok.c
 mk_faddr.so mk_faddr.po $(OUTPRE)mk_faddr.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -287,6 +265,110 @@ localaddr.so localaddr.po $(OUTPRE)localaddr.$(OBJEXT): \
   $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/clpreauth_plugin.h \
   $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
   $(top_srcdir)/include/socket-utils.h localaddr.c
+localauth.so localauth.po $(OUTPRE)localauth.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/kadm5/admin.h \
+  $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+  $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+  $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+  $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+  $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+  $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+  $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/clpreauth_plugin.h \
+  $(top_srcdir)/include/krb5/localauth_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h localauth.c os-proto.h
+localauth_an2ln.so localauth_an2ln.po $(OUTPRE)localauth_an2ln.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/kadm5/admin.h \
+  $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+  $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+  $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+  $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+  $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+  $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+  $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/clpreauth_plugin.h \
+  $(top_srcdir)/include/krb5/localauth_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h localauth_an2ln.c \
+  os-proto.h
+localauth_k5login.so localauth_k5login.po $(OUTPRE)localauth_k5login.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/kadm5/admin.h \
+  $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+  $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+  $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+  $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+  $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+  $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+  $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/clpreauth_plugin.h \
+  $(top_srcdir)/include/krb5/localauth_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h localauth_k5login.c \
+  os-proto.h
+localauth_names.so localauth_names.po $(OUTPRE)localauth_names.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/kadm5/admin.h \
+  $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+  $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+  $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+  $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+  $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+  $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+  $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/clpreauth_plugin.h \
+  $(top_srcdir)/include/krb5/localauth_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h localauth_names.c \
+  os-proto.h
+localauth_rule.so localauth_rule.po $(OUTPRE)localauth_rule.$(OBJEXT): \
+  $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/gssapi/gssapi.h \
+  $(BUILDTOP)/include/gssrpc/types.h $(BUILDTOP)/include/kadm5/admin.h \
+  $(BUILDTOP)/include/kadm5/chpass_util_strings.h $(BUILDTOP)/include/kadm5/kadm_err.h \
+  $(BUILDTOP)/include/krb5/krb5.h $(BUILDTOP)/include/osconf.h \
+  $(BUILDTOP)/include/profile.h $(COM_ERR_DEPS) $(top_srcdir)/include/gssrpc/auth.h \
+  $(top_srcdir)/include/gssrpc/auth_gss.h $(top_srcdir)/include/gssrpc/auth_unix.h \
+  $(top_srcdir)/include/gssrpc/clnt.h $(top_srcdir)/include/gssrpc/rename.h \
+  $(top_srcdir)/include/gssrpc/rpc.h $(top_srcdir)/include/gssrpc/rpc_msg.h \
+  $(top_srcdir)/include/gssrpc/svc.h $(top_srcdir)/include/gssrpc/svc_auth.h \
+  $(top_srcdir)/include/gssrpc/xdr.h $(top_srcdir)/include/k5-buf.h \
+  $(top_srcdir)/include/k5-err.h $(top_srcdir)/include/k5-gmt_mktime.h \
+  $(top_srcdir)/include/k5-int-pkinit.h $(top_srcdir)/include/k5-int.h \
+  $(top_srcdir)/include/k5-platform.h $(top_srcdir)/include/k5-plugin.h \
+  $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/k5-trace.h \
+  $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
+  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/clpreauth_plugin.h \
+  $(top_srcdir)/include/krb5/localauth_plugin.h $(top_srcdir)/include/krb5/locate_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h localauth_rule.c \
+  os-proto.h
 locate_kdc.so locate_kdc.po $(OUTPRE)locate_kdc.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -504,9 +586,6 @@ write_msg.so write_msg.po $(OUTPRE)write_msg.$(OBJEXT): \
   $(top_srcdir)/include/krb5/locate_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
   os-proto.h write_msg.c
-t_an_to_ln.so t_an_to_ln.po $(OUTPRE)t_an_to_ln.$(OBJEXT): \
-  $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
-  t_an_to_ln.c
 t_expand_path.so t_expand_path.po $(OUTPRE)t_expand_path.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
@@ -521,9 +600,6 @@ t_expand_path.so t_expand_path.po $(OUTPRE)t_expand_path.$(OBJEXT): \
   os-proto.h t_expand_path.c
 t_gifconf.so t_gifconf.po $(OUTPRE)t_gifconf.$(OBJEXT): \
   t_gifconf.c
-t_kuserok.so t_kuserok.po $(OUTPRE)t_kuserok.$(OBJEXT): \
-  $(BUILDTOP)/include/krb5/krb5.h $(COM_ERR_DEPS) $(top_srcdir)/include/krb5.h \
-  t_kuserok.c
 t_locate_kdc.so t_locate_kdc.po $(OUTPRE)t_locate_kdc.$(OBJEXT): \
   $(BUILDTOP)/include/autoconf.h $(BUILDTOP)/include/krb5/krb5.h \
   $(BUILDTOP)/include/osconf.h $(BUILDTOP)/include/profile.h \
diff --git a/src/lib/krb5/os/kuserok.c b/src/lib/krb5/os/kuserok.c
deleted file mode 100644
index 51471ae..0000000
--- a/src/lib/krb5/os/kuserok.c
+++ /dev/null
@@ -1,203 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* lib/krb5/os/kuserok.c */
-/*
- * Copyright 1990,1993,2007 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.
- */
-
-#include "k5-int.h"
-#if !defined(_WIN32)            /* Not yet for Windows */
-#include <stdio.h>
-#include <pwd.h>
-
-#if defined(_AIX) && defined(_IBMR2)
-#include <sys/access.h>
-/* xlc has a bug with "const" */
-#define getpwnam(user) getpwnam((char *)user)
-#endif
-
-#define MAX_USERNAME 65
-
-#if defined(__APPLE__) && defined(__MACH__)
-#include <hfs/hfs_mount.h>      /* XXX */
-#define FILE_OWNER_OK(UID)  ((UID) == 0 || (UID) == UNKNOWNUID)
-#else
-#define FILE_OWNER_OK(UID)  ((UID) == 0)
-#endif
-
-enum result { ACCEPT, REJECT, PASS };
-
-/*
- * Find the k5login filename for luser, either in the user's homedir or in a
- * configured directory under the username.
- */
-static krb5_error_code
-get_k5login_filename(krb5_context context, const char *luser,
-                     const char *homedir, char **filename_out)
-{
-    krb5_error_code ret;
-    char *dir, *filename;
-
-    *filename_out = NULL;
-    ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
-                             KRB5_CONF_K5LOGIN_DIRECTORY, NULL, NULL, &dir);
-    if (ret != 0)
-        return ret;
-
-    if (dir == NULL) {
-        /* Look in the user's homedir. */
-        if (asprintf(&filename, "%s/.k5login", homedir) < 0)
-            return ENOMEM;
-    } else {
-        /* Look in the configured directory. */
-        if (asprintf(&filename, "%s/%s", dir, luser) < 0)
-            ret = ENOMEM;
-        profile_release_string(dir);
-        if (ret)
-            return ret;
-    }
-    *filename_out = filename;
-    return 0;
-}
-
-/*
- * Determine whether principal is authorized to log in as luser according to
- * the user's k5login file.  Return ACCEPT if the k5login file authorizes the
- * principal, PASS if the k5login file does not exist, or REJECT if the k5login
- * file exists but does not authorize the principal.  If k5login files are
- * configured to be non-authoritative, pass instead of rejecting.
- */
-static enum result
-k5login_ok(krb5_context context, krb5_principal principal, const char *luser)
-{
-    int authoritative = TRUE, gobble;
-    enum result result = REJECT;
-    char *filename = NULL, *princname = NULL;
-    char *newline, linebuf[BUFSIZ], pwbuf[BUFSIZ];
-    struct stat sbuf;
-    struct passwd pwx, *pwd;
-    FILE *fp = NULL;
-
-    if (profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
-                            KRB5_CONF_K5LOGIN_AUTHORITATIVE, NULL, TRUE,
-                            &authoritative) != 0)
-        goto cleanup;
-
-    /* Get the local user's homedir and uid. */
-    if (k5_getpwnam_r(luser, &pwx, pwbuf, sizeof(pwbuf), &pwd) != 0)
-        goto cleanup;
-
-    if (get_k5login_filename(context, luser, pwd->pw_dir, &filename) != 0)
-        goto cleanup;
-
-    if (access(filename, F_OK) != 0) {
-        result = PASS;
-        goto cleanup;
-    }
-
-    if (krb5_unparse_name(context, principal, &princname) != 0)
-        goto cleanup;
-
-    fp = fopen(filename, "r");
-    if (fp == NULL)
-        goto cleanup;
-    set_cloexec_file(fp);
-
-    /* For security reasons, the .k5login file must be owned either by
-     * the user or by root. */
-    if (fstat(fileno(fp), &sbuf))
-        goto cleanup;
-    if (sbuf.st_uid != pwd->pw_uid && !FILE_OWNER_OK(sbuf.st_uid))
-        goto cleanup;
-
-    /* Check each line. */
-    while (result != ACCEPT && (fgets(linebuf, sizeof(linebuf), fp) != NULL)) {
-        newline = strrchr(linebuf, '\n');
-        if (newline != NULL)
-            *newline = '\0';
-        if (strcmp(linebuf, princname) == 0)
-            result = ACCEPT;
-        /* Clean up the rest of the line if necessary. */
-        if (newline == NULL)
-            while (((gobble = getc(fp)) != EOF) && gobble != '\n');
-    }
-
-cleanup:
-    free(princname);
-    free(filename);
-    if (fp != NULL)
-        fclose(fp);
-    /* If k5login files are non-authoritative, never reject. */
-    return (!authoritative && result == REJECT) ? PASS : result;
-}
-
-/*
- * Determine whether principal is authorized to log in as luser according to
- * aname-to-localname translation.  Return ACCEPT if principal translates to
- * luser or PASS if it does not.
- */
-static enum result
-an2ln_ok(krb5_context context, krb5_principal principal, const char *luser)
-{
-    krb5_error_code ret;
-    char kuser[MAX_USERNAME];
-
-    ret = krb5_aname_to_localname(context, principal, sizeof(kuser), kuser);
-    if (ret != 0)
-        return PASS;
-    return (strcmp(kuser, luser) == 0) ? ACCEPT : PASS;
-}
-
-krb5_boolean KRB5_CALLCONV
-krb5_kuserok(krb5_context context, krb5_principal principal, const char *luser)
-{
-    enum result result;
-
-    result = k5login_ok(context, principal, luser);
-    if (result == PASS)
-        result = an2ln_ok(context, principal, luser);
-    return (result == ACCEPT) ? TRUE : FALSE;
-}
-
-#else /* _WIN32 */
-
-/*
- * If the given Kerberos name "server" translates to the same name as "luser"
- * (using * krb5_aname_to_lname()), returns TRUE.
- */
-krb5_boolean KRB5_CALLCONV
-krb5_kuserok(context, principal, luser)
-    krb5_context context;
-    krb5_principal principal;
-    const char *luser;
-{
-    char kuser[50];
-
-    if (krb5_aname_to_localname(context, principal, sizeof(kuser), kuser))
-        return FALSE;
-
-    if (strcmp(kuser, luser) == 0)
-        return TRUE;
-
-    return FALSE;
-}
-#endif /* _WIN32 */
diff --git a/src/lib/krb5/os/localauth.c b/src/lib/krb5/os/localauth.c
new file mode 100644
index 0000000..e48b3a9
--- /dev/null
+++ b/src/lib/krb5/os/localauth.c
@@ -0,0 +1,455 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/os/localauth.c - Authorize access to local accounts */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "os-proto.h"
+#include <krb5/localauth_plugin.h>
+
+struct localauth_module_handle {
+    struct krb5_localauth_vtable_st vt;
+    krb5_localauth_moddata data;
+};
+
+static krb5_error_code
+localauth_auth_to_local_initvt(krb5_context context, int maj_ver, int min_ver,
+                               krb5_plugin_vtable vtable);
+static krb5_error_code
+localauth_default_initvt(krb5_context context, int maj_ver, int min_ver,
+                         krb5_plugin_vtable vtable);
+
+/* Release a list of localauth module handles. */
+static void
+free_handles(krb5_context context, struct localauth_module_handle **handles)
+{
+    struct localauth_module_handle *h, **hp;
+
+    if (handles == NULL)
+        return;
+    for (hp = handles; *hp != NULL; hp++) {
+        h = *hp;
+        if (h->vt.fini != NULL)
+            h->vt.fini(context, h->data);
+        free(h);
+    }
+    free(handles);
+}
+
+/* Find a localauth module whose an2ln_types contains a match for type. */
+static struct localauth_module_handle *
+find_typed_module(struct localauth_module_handle **handles, const char *type)
+{
+    struct localauth_module_handle **hp, *h;
+    const char **tp;
+
+    for (hp = handles; *hp != NULL; hp++) {
+        h = *hp;
+        for (tp = h->vt.an2ln_types; tp != NULL && *tp != NULL; tp++) {
+            if (strcmp(*tp, type) == 0)
+                return h;
+        }
+    }
+    return NULL;
+}
+
+/* Generate a trace log and return 1 if the an2ln_types of handle conflicts
+ * with any of the handles in list.  Return 0 otherwise. */
+static int
+check_conflict(krb5_context context, struct localauth_module_handle **list,
+               struct localauth_module_handle *handle)
+{
+    struct localauth_module_handle *conflict;
+    const char **tp;
+
+    for (tp = handle->vt.an2ln_types; tp != NULL && *tp != NULL; tp++) {
+        conflict = find_typed_module(list, *tp);
+        if (conflict != NULL) {
+            TRACE_LOCALAUTH_INIT_CONFLICT(context, *tp, handle->vt.name,
+                                          conflict->vt.name);
+            return 1;
+        }
+    }
+    return 0;
+}
+
+/* If mod is in list, move it to the back. */
+static void
+move_to_back(krb5_plugin_initvt_fn *list, krb5_plugin_initvt_fn mod)
+{
+    for (; *list != NULL && *list != mod; list++);
+    if (*list == NULL)
+        return;
+    for (; *list != NULL; list++)
+        *list = *(list + 1);
+    *(list - 1) = mod;
+}
+
+/* Get the registered localauth modules including all built-in modules, in the
+ * proper order. */
+static krb5_error_code
+get_modules(krb5_context context, krb5_plugin_initvt_fn **modules_out)
+{
+    krb5_error_code ret;
+    const int intf = PLUGIN_INTERFACE_LOCALAUTH;
+
+    *modules_out = NULL;
+
+    /* Register built-in modules. */
+    ret = k5_plugin_register(context, intf, "auth_to_local",
+                             localauth_auth_to_local_initvt);
+    if (ret)
+        return ret;
+    ret = k5_plugin_register(context, intf, "names", localauth_names_initvt);
+    if (ret)
+        return ret;
+    ret = k5_plugin_register(context, intf, "default",
+                             localauth_default_initvt);
+    if (ret)
+        return ret;
+    ret = k5_plugin_register(context, intf, "rule", localauth_rule_initvt);
+    if (ret)
+        return ret;
+    ret = k5_plugin_register(context, intf, "k5login",
+                             localauth_k5login_initvt);
+    if (ret)
+        return ret;
+    ret = k5_plugin_register(context, intf, "an2ln", localauth_an2ln_initvt);
+    if (ret)
+        return ret;
+
+    ret = k5_plugin_load_all(context, intf, modules_out);
+    if (ret)
+        return ret;
+
+    /* Move built-in userok and untyped an2ln localauth modules to back so we
+     * try loaded modules first. */
+    move_to_back(*modules_out, localauth_names_initvt);
+    move_to_back(*modules_out, localauth_auth_to_local_initvt);
+    move_to_back(*modules_out, localauth_k5login_initvt);
+    move_to_back(*modules_out, localauth_an2ln_initvt);
+
+    return 0;
+}
+
+/* Initialize context->localauth_handles with a list of module handles. */
+static krb5_error_code
+load_localauth_modules(krb5_context context)
+{
+    krb5_error_code ret;
+    struct localauth_module_handle **list = NULL, *handle;
+    krb5_plugin_initvt_fn *modules = NULL, *mod;
+    size_t count;
+
+    ret = get_modules(context, &modules);
+    if (ret != 0)
+        goto cleanup;
+
+    /* Allocate a large enough list of handles. */
+    for (count = 0; modules[count] != NULL; count++);
+    list = k5alloc((count + 1) * sizeof(*list), &ret);
+    if (list == NULL)
+        goto cleanup;
+
+    /* Initialize each module, ignoring ones that fail. */
+    count = 0;
+    for (mod = modules; *mod != NULL; mod++) {
+        handle = k5alloc(sizeof(*handle), &ret);
+        if (handle == NULL)
+            goto cleanup;
+        ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&handle->vt);
+        if (ret != 0) {
+            TRACE_LOCALAUTH_VTINIT_FAIL(context, ret);
+            free(handle);
+            continue;
+        }
+
+        if (check_conflict(context, list, handle))
+            continue;
+
+        handle->data = NULL;
+        if (handle->vt.init != NULL) {
+            ret = handle->vt.init(context, &handle->data);
+            if (ret != 0) {
+                TRACE_LOCALAUTH_INIT_FAIL(context, handle->vt.name, ret);
+                free(handle);
+                continue;
+            }
+        }
+        list[count++] = handle;
+        list[count] = NULL;
+    }
+    list[count] = NULL;
+
+    ret = 0;
+    context->localauth_handles = list;
+    list = NULL;
+
+cleanup:
+    k5_plugin_free_modules(context, modules);
+    free_handles(context, list);
+    return ret;
+}
+
+/* Invoke a module's userok method, if it has one. */
+static krb5_error_code
+userok(krb5_context context, struct localauth_module_handle *h,
+       krb5_const_principal aname, const char *lname)
+{
+    if (h->vt.userok == NULL)
+        return KRB5_PLUGIN_NO_HANDLE;
+    return h->vt.userok(context, h->data, aname, lname);
+}
+
+/* Invoke a module's an2ln method, if it has one. */
+static krb5_error_code
+an2ln(krb5_context context, struct localauth_module_handle *h,
+      const char *type, const char *residual, krb5_const_principal aname,
+      char **lname_out)
+{
+    if (h->vt.an2ln == NULL)
+        return KRB5_LNAME_NOTRANS;
+    return h->vt.an2ln(context, h->data, type, residual, aname, lname_out);
+}
+
+/* Invoke a module's free_string method. */
+static void
+free_lname(krb5_context context, struct localauth_module_handle *h, char *str)
+{
+    h->vt.free_string(context, h->data, str);
+}
+
+/* Parse a TYPE or TYPE:residual string into its components. */
+static krb5_error_code
+parse_mapping_value(const char *value, char **type_out, char **residual_out)
+{
+    krb5_error_code ret;
+    const char *p;
+    char *type, *residual;
+
+    *type_out = NULL;
+    *residual_out = NULL;
+    p = strchr(value, ':');
+    if (p == NULL) {
+        type = strdup(value);
+        if (type == NULL)
+            return ENOMEM;
+        residual = NULL;
+    } else {
+        type = k5memdup0(value, p - value, &ret);
+        if (type == NULL)
+            return ret;
+        residual = strdup(p + 1);
+        if (residual == NULL) {
+            free(type);
+            return ENOMEM;
+        }
+    }
+    *type_out = type;
+    *residual_out = residual;
+    return 0;
+}
+
+/* Apply the default an2ln method, which translates name at defaultrealm or
+ * name/defaultrealm at defaultrealm to name. */
+static krb5_error_code
+an2ln_default(krb5_context context, krb5_localauth_moddata data,
+              const char *type, const char *residual,
+              krb5_const_principal aname, char **lname_out)
+{
+    krb5_error_code ret;
+    char *def_realm;
+
+    *lname_out = NULL;
+
+    ret = krb5_get_default_realm(context, &def_realm);
+    if (ret)
+        return KRB5_LNAME_NOTRANS;
+
+    if (!data_eq_string(aname->realm, def_realm)) {
+        ret = KRB5_LNAME_NOTRANS;
+    } else if (aname->length == 2) {
+        /* Allow a second component if it is the local realm. */
+        if (!data_eq_string(aname->data[1], def_realm))
+            ret = KRB5_LNAME_NOTRANS;
+    } else if (aname->length != 1) {
+        ret = KRB5_LNAME_NOTRANS;
+    }
+    free(def_realm);
+    if (ret)
+        return ret;
+
+    *lname_out = k5memdup0(aname->data[0].data, aname->data[0].length, &ret);
+    return ret;
+}
+
+/*
+ * Perform aname-to-lname translation using the auth_to_local values in the
+ * default realm's profile section.  If no values exist, fall back to the
+ * default method.
+ */
+static krb5_error_code
+an2ln_auth_to_local(krb5_context context, krb5_localauth_moddata data,
+                    const char *type_arg, const char *residual_arg,
+                    krb5_const_principal aname, char **lname_out)
+{
+    krb5_error_code ret;
+    struct localauth_module_handle *h;
+    char *realm = NULL, **mapping_values = NULL, *type, *residual, *lname;
+    const char *hierarchy[4];
+    size_t i;
+
+    *lname_out = NULL;
+
+    /* Fetch the profile values for realms-><defaultrealm>->auth_to_local. */
+    ret = krb5_get_default_realm(context, &realm);
+    if (ret)
+        return KRB5_LNAME_NOTRANS;
+    hierarchy[0] = KRB5_CONF_REALMS;
+    hierarchy[1] = realm;
+    hierarchy[2] = KRB5_CONF_AUTH_TO_LOCAL;
+    hierarchy[3] = NULL;
+    ret = profile_get_values(context->profile, hierarchy, &mapping_values);
+    if (ret) {
+        /* Use default method if there are no auth_to_local values. */
+        ret = an2ln_default(context, data, NULL, NULL, aname, lname_out);
+        goto cleanup;
+    }
+
+    ret = KRB5_LNAME_NOTRANS;
+    for (i = 0; mapping_values[i] != NULL && ret == KRB5_LNAME_NOTRANS; i++) {
+        ret = parse_mapping_value(mapping_values[i], &type, &residual);
+        if (ret)
+            goto cleanup;
+        h = find_typed_module(context->localauth_handles, type);
+        if (h != NULL) {
+            ret = an2ln(context, h, type, residual, aname, &lname);
+            if (ret == 0) {
+                *lname_out = strdup(lname);
+                if (*lname_out == NULL)
+                    ret = ENOMEM;
+                free_lname(context, h, lname);
+            }
+        } else {
+            ret = KRB5_CONFIG_BADFORMAT;
+        }
+        free(type);
+        free(residual);
+    }
+
+cleanup:
+    free(realm);
+    profile_free_list(mapping_values);
+    return ret;
+}
+
+static void
+freestr(krb5_context context, krb5_localauth_moddata data, char *str)
+{
+    free(str);
+}
+
+static krb5_error_code
+localauth_auth_to_local_initvt(krb5_context context, int maj_ver, int min_ver,
+                               krb5_plugin_vtable vtable)
+{
+    krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
+
+    vt->name = "auth_to_local";
+    vt->an2ln = an2ln_auth_to_local;
+    vt->free_string = freestr;
+    return 0;
+}
+
+static krb5_error_code
+localauth_default_initvt(krb5_context context, int maj_ver, int min_ver,
+                         krb5_plugin_vtable vtable)
+{
+    krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
+    static const char *types[] = { "DEFAULT", NULL };
+
+    vt->name = "default";
+    vt->an2ln_types = types;
+    vt->an2ln = an2ln_default;
+    vt->free_string = freestr;
+    return 0;
+}
+
+krb5_boolean KRB5_CALLCONV
+krb5_kuserok(krb5_context context, krb5_principal aname, const char *lname)
+{
+    krb5_error_code ret;
+    struct localauth_module_handle **hp;
+    krb5_boolean accepted = FALSE;
+
+    if (context->localauth_handles == NULL && load_localauth_modules(context))
+        return FALSE;
+
+    /* If any module denies access, return false immediately.  Otherwise,
+     * consult all modules and return true if one of them allows access. */
+    for (hp = context->localauth_handles; *hp != NULL; hp++) {
+        ret = userok(context, *hp, aname, lname);
+        if (ret == 0)
+            accepted = TRUE;
+        else if (ret != KRB5_PLUGIN_NO_HANDLE)
+            return FALSE;
+    }
+    return accepted;
+}
+
+krb5_error_code KRB5_CALLCONV
+krb5_aname_to_localname(krb5_context context, krb5_const_principal aname,
+                        int lnsize, char *lname_out)
+{
+    krb5_error_code ret;
+    struct localauth_module_handle **hp;
+    char *lname;
+    size_t sz;
+
+    if (context->localauth_handles == NULL) {
+        ret = load_localauth_modules(context);
+        if (ret)
+            return ret;
+    }
+
+    for (hp = context->localauth_handles; *hp != NULL; hp++) {
+        if ((*hp)->vt.an2ln_types != NULL)
+            continue;
+        ret = an2ln(context, *hp, NULL, NULL, aname, &lname);
+        if (ret == 0) {
+            sz = strlcpy(lname_out, lname, lnsize);
+            free_lname(context, *hp, lname);
+            return sz >= (size_t)lnsize ? KRB5_CONFIG_NOTENUFSPACE : 0;
+        } else if (ret != KRB5_LNAME_NOTRANS) {
+            return ret;
+        }
+    }
+    return KRB5_LNAME_NOTRANS;
+}
diff --git a/src/lib/krb5/os/localauth_an2ln.c b/src/lib/krb5/os/localauth_an2ln.c
new file mode 100644
index 0000000..0d29667
--- /dev/null
+++ b/src/lib/krb5/os/localauth_an2ln.c
@@ -0,0 +1,59 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/os/localauth_an2ln.c - an2ln localauth module */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "os-proto.h"
+#include <krb5/localauth_plugin.h>
+
+#define MAX_USERNAME 65
+
+static krb5_error_code
+an2ln_userok(krb5_context context, krb5_localauth_moddata data,
+             krb5_const_principal aname, const char *lname)
+{
+    krb5_error_code ret;
+    char kuser[MAX_USERNAME];
+
+    ret = krb5_aname_to_localname(context, aname, sizeof(kuser), kuser);
+    return (ret == 0 && strcmp(kuser, lname) == 0) ? 0 : KRB5_PLUGIN_NO_HANDLE;
+}
+
+krb5_error_code
+localauth_an2ln_initvt(krb5_context context, int maj_ver, int min_ver,
+                       krb5_plugin_vtable vtable)
+{
+    krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
+
+    vt->name = "an2ln";
+    vt->userok = an2ln_userok;
+    return 0;
+}
diff --git a/src/lib/krb5/os/localauth_k5login.c b/src/lib/krb5/os/localauth_k5login.c
new file mode 100644
index 0000000..27dfca0
--- /dev/null
+++ b/src/lib/krb5/os/localauth_k5login.c
@@ -0,0 +1,183 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/os/localauth_k5login.c - k5login localauth module */
+/*
+ * Copyright (C) 1990,1993,2007,2013 by the Massachusetts Institute
+ * of Technology.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "os-proto.h"
+#include <krb5/localauth_plugin.h>
+
+#if !defined(_WIN32)            /* Not yet for Windows */
+#include <pwd.h>
+
+#if defined(__APPLE__) && defined(__MACH__)
+#include <hfs/hfs_mount.h>
+#define FILE_OWNER_OK(UID)  ((UID) == 0 || (UID) == UNKNOWNUID)
+#else
+#define FILE_OWNER_OK(UID)  ((UID) == 0)
+#endif
+
+/*
+ * Find the k5login filename for luser, either in the user's homedir or in a
+ * configured directory under the username.
+ */
+static krb5_error_code
+get_k5login_filename(krb5_context context, const char *lname,
+                     const char *homedir, char **filename_out)
+{
+    krb5_error_code ret;
+    char *dir, *filename;
+
+    *filename_out = NULL;
+    ret = profile_get_string(context->profile, KRB5_CONF_LIBDEFAULTS,
+                             KRB5_CONF_K5LOGIN_DIRECTORY, NULL, NULL, &dir);
+    if (ret != 0)
+        return ret;
+
+    if (dir == NULL) {
+        /* Look in the user's homedir. */
+        if (asprintf(&filename, "%s/.k5login", homedir) < 0)
+            return ENOMEM;
+    } else {
+        /* Look in the configured directory. */
+        if (asprintf(&filename, "%s/%s", dir, lname) < 0)
+            ret = ENOMEM;
+        profile_release_string(dir);
+        if (ret)
+            return ret;
+    }
+    *filename_out = filename;
+    return 0;
+}
+
+/* Determine whether aname is authorized to log in as lname according to the
+ * user's k5login file. */
+static krb5_error_code
+userok_k5login(krb5_context context, krb5_localauth_moddata data,
+               krb5_const_principal aname, const char *lname)
+{
+    krb5_error_code ret;
+    int authoritative = TRUE, gobble;
+    char *filename = NULL, *princname = NULL;
+    char *newline, linebuf[BUFSIZ], pwbuf[BUFSIZ];
+    struct stat sbuf;
+    struct passwd pwx, *pwd;
+    FILE *fp = NULL;
+
+    ret = profile_get_boolean(context->profile, KRB5_CONF_LIBDEFAULTS,
+                              KRB5_CONF_K5LOGIN_AUTHORITATIVE, NULL, TRUE,
+                              &authoritative);
+    if (ret)
+        goto cleanup;
+
+    /* Get the local user's .k5login filename. */
+    ret = k5_getpwnam_r(lname, &pwx, pwbuf, sizeof(pwbuf), &pwd);
+    if (ret) {
+        ret = EPERM;
+        goto cleanup;
+    }
+    ret = get_k5login_filename(context, lname, pwd->pw_dir, &filename);
+    if (ret)
+        goto cleanup;
+
+    if (access(filename, F_OK) != 0) {
+        ret = KRB5_PLUGIN_NO_HANDLE;
+        goto cleanup;
+    }
+
+    ret = krb5_unparse_name(context, aname, &princname);
+    if (ret)
+        goto cleanup;
+
+    fp = fopen(filename, "r");
+    if (fp == NULL) {
+        ret = errno;
+        goto cleanup;
+    }
+    set_cloexec_file(fp);
+
+    /* For security reasons, the .k5login file must be owned either by
+     * the user or by root. */
+    if (fstat(fileno(fp), &sbuf)) {
+        ret = errno;
+        goto cleanup;
+    }
+    if (sbuf.st_uid != pwd->pw_uid && !FILE_OWNER_OK(sbuf.st_uid)) {
+        ret = EPERM;
+        goto cleanup;
+    }
+
+    /* Check each line. */
+    while (fgets(linebuf, sizeof(linebuf), fp) != NULL) {
+        newline = strrchr(linebuf, '\n');
+        if (newline != NULL)
+            *newline = '\0';
+        if (strcmp(linebuf, princname) == 0) {
+            ret = 0;
+            goto cleanup;
+        }
+        /* Clean up the rest of the line if necessary. */
+        if (newline == NULL)
+            while ((gobble = getc(fp)) != EOF && gobble != '\n');
+    }
+
+    /* We didn't find it. */
+    ret = EPERM;
+
+cleanup:
+    free(princname);
+    free(filename);
+    if (fp != NULL)
+        fclose(fp);
+    /* If k5login files are non-authoritative, never reject. */
+    return (!authoritative && ret) ? KRB5_PLUGIN_NO_HANDLE : ret;
+}
+
+#else /* _WIN32 */
+
+static krb5_error_code
+userok_k5login(krb5_context context, krb5_localauth_moddata data,
+               krb5_const_principal aname, const char *lname)
+{
+    return KRB5_PLUGIN_NO_HANDLE;
+}
+
+#endif
+
+krb5_error_code
+localauth_k5login_initvt(krb5_context context, int maj_ver, int min_ver,
+                         krb5_plugin_vtable vtable)
+{
+    krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
+
+    vt->name = "k5login";
+    vt->userok = userok_k5login;
+    return 0;
+}
diff --git a/src/lib/krb5/os/localauth_names.c b/src/lib/krb5/os/localauth_names.c
new file mode 100644
index 0000000..53ec2d9
--- /dev/null
+++ b/src/lib/krb5/os/localauth_names.c
@@ -0,0 +1,102 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/os/localauth_names.c - names localauth module */
+/*
+ * Copyright (C) 2013 by the Massachusetts Institute of Technology.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "k5-int.h"
+#include "os-proto.h"
+#include <krb5/localauth_plugin.h>
+
+static krb5_error_code
+an2ln_names(krb5_context context, krb5_localauth_moddata data,
+            const char *type, const char *residual, krb5_const_principal aname,
+            char **lname_out)
+{
+    krb5_error_code ret;
+    char *realm = NULL, *pname = NULL, **mapping_values = NULL;
+    const char *hierarchy[5];
+    size_t count;
+
+    *lname_out = NULL;
+
+    /*
+     * Fetch the profile values for realms-><defaultrealm>->
+     * auth_to_local_names-><princname>.  Use the principal name without realm;
+     * this is problematic in many multiple-realm environments, but is how
+     * we've historically done it.
+     */
+    ret = krb5_get_default_realm(context, &realm);
+    if (ret)
+        return KRB5_LNAME_NOTRANS;
+    ret = krb5_unparse_name_flags(context, aname,
+                                  KRB5_PRINCIPAL_UNPARSE_NO_REALM, &pname);
+    if (ret)
+        goto cleanup;
+    hierarchy[0] = KRB5_CONF_REALMS;
+    hierarchy[1] = realm;
+    hierarchy[2] = KRB5_CONF_AUTH_TO_LOCAL_NAMES;
+    hierarchy[3] = pname;
+    hierarchy[4] = NULL;
+    ret = profile_get_values(context->profile, hierarchy, &mapping_values);
+    if (ret) {
+        ret = KRB5_LNAME_NOTRANS;
+        goto cleanup;
+    }
+
+    /* We found one or more explicit mappings.  Use the last one. */
+    for (count = 0; mapping_values[count] != NULL; count++);
+    *lname_out = strdup(mapping_values[count - 1]);
+    if (*lname_out == NULL)
+        ret = ENOMEM;
+
+cleanup:
+    free(realm);
+    free(pname);
+    profile_free_list(mapping_values);
+    return ret;
+}
+
+static void
+freestr(krb5_context context, krb5_localauth_moddata data, char *str)
+{
+    free(str);
+}
+
+krb5_error_code
+localauth_names_initvt(krb5_context context, int maj_ver, int min_ver,
+                       krb5_plugin_vtable vtable)
+{
+    krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
+
+    vt->name = "names";
+    vt->an2ln = an2ln_names;
+    vt->free_string = freestr;
+    return 0;
+}
diff --git a/src/lib/krb5/os/localauth_rule.c b/src/lib/krb5/os/localauth_rule.c
new file mode 100644
index 0000000..bf4b21d
--- /dev/null
+++ b/src/lib/krb5/os/localauth_rule.c
@@ -0,0 +1,338 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/krb5/os/localauth_rule.c - rule localauth module */
+/*
+ * Copyright (C) 1990,1991,2007,2008,2013 by the Massachusetts
+ * Institute of Technology.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * This module implements the RULE type for auth_to_local processing.
+ *
+ * There are three parts to each rule.  The first part, if present, determines
+ * the selection string.  If this is not present, the selection string defaults
+ * to the unparsed principal name without realm (which can be dangerous in
+ * multi-realm environments, but is our historical behavior).  The selection
+ * string syntax is:
+ *
+ *     "[" <ncomps> ":" <format> "]"
+ *
+ *         <ncomps> is the number of expected components for this rule.  If the
+ *         principal does not have this many components, then this rule does
+ *         not apply.
+ *
+ *         <format> determines the selection string.  Within <format>, $0 will
+ *         be substituted with the principal's realm, $1 with its first
+ *         component, $2 with its second component, and so forth.
+ *
+ * The second part is an optional regular expression surrounded by parentheses.
+ * If present, the rule will only apply if the selection string matches the
+ * regular expression.  At present, the regular expression may not contain a
+ * ')' character.
+ *
+ * The third part is a sequence of zero or more transformation rules, using
+ * the syntax:
+ *
+ *     "s/" <regexp> "/" <text> "/" ["g"]
+ *
+ * No substitutions are allowed within <text>.  A "g" indicates that the
+ * substitution should be performed globally; otherwise it will be performed at
+ * most once.
+ */
+
+#include "k5-int.h"
+#include "os-proto.h"
+#include <krb5/localauth_plugin.h>
+#include <ctype.h>
+
+#ifdef HAVE_REGEX_H
+#include <regex.h>
+
+/* Process the match portion of a rule and update *contextp.  Return
+ * KRB5_LNAME_NOTRANS if selstring doesn't match the regexp. */
+static krb5_error_code
+aname_do_match(const char *selstring, const char **contextp)
+{
+    krb5_error_code ret;
+    const char *startp, *endp;
+    char *regstr;
+    regex_t re;
+    regmatch_t m;
+
+    /* If no regexp is present, leave *contextp alone and return success. */
+    if (**contextp != '(')
+        return 0;
+
+    /* Find the end of the regexp and make a copy of it. */
+    startp = *contextp + 1;
+    endp = strchr(startp, ')');
+    if (endp == NULL)
+        return KRB5_CONFIG_BADFORMAT;
+    regstr = k5memdup0(startp, endp - startp, &ret);
+    if (regstr == NULL)
+        return ret;
+
+    /* Perform the match. */
+    ret = (regcomp(&re, regstr, REG_EXTENDED) == 0 &&
+           regexec(&re, selstring, 1, &m, 0) == 0 &&
+           m.rm_so == 0 && (size_t)m.rm_eo == strlen(selstring)) ? 0 :
+        KRB5_LNAME_NOTRANS;
+    regfree(&re);
+    free(regstr);
+    *contextp = endp + 1;
+    return ret;
+}
+
+/* Replace regular expression matches of regstr with repl in instr, producing
+ * *outstr.  If doall is true, replace all matches for regstr. */
+static krb5_error_code
+do_replacement(const char *regstr, const char *repl, krb5_boolean doall,
+               const char *instr, char **outstr)
+{
+    struct k5buf buf;
+    regex_t re;
+    regmatch_t m;
+
+    *outstr = NULL;
+    if (regcomp(&re, regstr, REG_EXTENDED))
+        return KRB5_LNAME_NOTRANS;
+    k5_buf_init_dynamic(&buf);
+    while (regexec(&re, instr, 1, &m, 0) == 0) {
+        k5_buf_add_len(&buf, instr, m.rm_so);
+        k5_buf_add(&buf, repl);
+        instr += m.rm_eo;
+        if (!doall)
+            break;
+    }
+    regfree(&re);
+    k5_buf_add(&buf, instr);
+    *outstr = k5_buf_data(&buf);
+    return *outstr == NULL ? ENOMEM : 0;
+}
+
+/*
+ * Perform any substitutions specified by *contextp, and advance *contextp past
+ * the substitution expressions.  Place the result of the substitutions in
+ * *result.
+ */
+static krb5_error_code
+aname_replacer(const char *string, const char **contextp, char **result)
+{
+    krb5_error_code ret = 0;
+    const char *cp, *ep, *tp;
+    char *current, *newstr, *rule = NULL, *repl = NULL;
+    krb5_boolean doglobal;
+
+    *result = NULL;
+
+    current = strdup(string);
+    if (current == NULL)
+        return ENOMEM;
+
+    /* Iterate over replacement expressions, updating current for each one. */
+    cp = *contextp;
+    while (*cp != '\0') {
+        /* Skip leading whitespace */
+        while (isspace((unsigned char)*cp))
+            cp++;
+
+        /* Find the separators for an s/rule/repl/ expression. */
+        if (!(cp[0] == 's' && cp[1] == '/' && (ep = strchr(cp + 2, '/')) &&
+              (tp = strchr(ep + 1, '/')))) {
+            ret = KRB5_CONFIG_BADFORMAT;
+            goto cleanup;
+        }
+
+        /* Copy the rule and replacement strings. */
+        free(rule);
+        rule = k5memdup0(cp + 2, ep - (cp + 2), &ret);
+        if (rule == NULL)
+            goto cleanup;
+        free(repl);
+        repl = k5memdup0(ep + 1, tp - (ep + 1), &ret);
+        if (repl == NULL)
+            goto cleanup;
+
+        /* Advance past expression and check for trailing "g". */
+        cp = tp + 1;
+        doglobal = (*cp == 'g');
+        if (doglobal)
+            cp++;
+
+        ret = do_replacement(rule, repl, doglobal, current, &newstr);
+        if (ret)
+            goto cleanup;
+        free(current);
+        current = newstr;
+    }
+    *result = current;
+
+cleanup:
+    free(repl);
+    free(rule);
+    return ret;
+}
+
+/*
+ * Compute selection string for RULE rules.  Advance *contextp to the string
+ * position after the selstring part if present, and set *result to the
+ * selection string.
+ */
+static krb5_error_code
+aname_get_selstring(krb5_context context, krb5_const_principal aname,
+                    const char **contextp, char **selstring_out)
+{
+    const char *current;
+    char *end, *str;
+    long num_comps, ind;
+    const krb5_data *datap;
+    struct k5buf selstring;
+    size_t nlit;
+
+    *selstring_out = NULL;
+    if (**contextp != '[') {
+        /*
+         * No selstring part; use the principal name without realm.  This is
+         * problematic in many multiple-realm environments, but is how we've
+         * historically done it.
+         */
+        return krb5_unparse_name_flags(context, aname,
+                                       KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+                                       selstring_out);
+    }
+
+    /* Advance past the '[' and read the number of components. */
+    current = *contextp + 1;
+    errno = 0;
+    num_comps = strtol(current, &end, 10);
+    if (errno != 0 || num_comps < 0 || *end != ':')
+        return KRB5_CONFIG_BADFORMAT;
+    current = end;
+    if (num_comps != aname->length)
+        return KRB5_LNAME_NOTRANS;
+    current++;
+
+    k5_buf_init_dynamic(&selstring);
+    while (TRUE) {
+        /* Copy in literal characters up to the next $ or ]. */
+        nlit = strcspn(current, "$]");
+        k5_buf_add_len(&selstring, current, nlit);
+        current += nlit;
+        if (*current != '$')
+            break;
+
+        /* Expand $ substitution to a principal component. */
+        errno = 0;
+        ind = strtol(current + 1, &end, 10);
+        if (errno || ind > num_comps)
+            break;
+        current = end;
+        datap = ind > 0 ? krb5_princ_component(context, aname, ind - 1) :
+            krb5_princ_realm(context, aname);
+        if (!datap)
+            break;
+        k5_buf_add_len(&selstring, datap->data, datap->length);
+    }
+
+    /* Check that we hit a ']' and not the end of the string. */
+    if (*current != ']') {
+        k5_free_buf(&selstring);
+        return KRB5_CONFIG_BADFORMAT;
+    }
+
+    str = k5_buf_data(&selstring);
+    if (str == NULL)
+        return ENOMEM;
+
+    *contextp = current + 1;
+    *selstring_out = str;
+    return 0;
+}
+
+static krb5_error_code
+an2ln_rule(krb5_context context, krb5_localauth_moddata data, const char *type,
+           const char *rule, krb5_const_principal aname, char **lname_out)
+{
+    krb5_error_code ret;
+    const char *current;
+    char *selstring = NULL;
+
+    *lname_out = NULL;
+    if (rule == NULL)
+        return KRB5_CONFIG_BADFORMAT;
+
+    /* Compute the selection string. */
+    current = rule;
+    ret = aname_get_selstring(context, aname, &current, &selstring);
+    if (ret)
+        return ret;
+
+    /* Check the selection string against the regexp, if present. */
+    if (*current == '(') {
+        ret = aname_do_match(selstring, &current);
+        if (ret)
+            goto cleanup;
+    }
+
+    /* Perform the substitution. */
+    ret = aname_replacer(selstring, &current, lname_out);
+
+cleanup:
+    free(selstring);
+    return ret;
+}
+
+#else /* HAVE_REGEX_H */
+
+static krb5_error_code
+an2ln_rule(krb5_context context, krb5_localauth_moddata data, const char *type,
+           const char *rule, krb5_const_principal aname, char **lname_out)
+{
+    return KRB5_LNAME_NOTRANS;
+}
+
+#endif
+
+static void
+freestr(krb5_context context, krb5_localauth_moddata data, char *str)
+{
+    free(str);
+}
+
+krb5_error_code
+localauth_rule_initvt(krb5_context context, int maj_ver, int min_ver,
+                      krb5_plugin_vtable vtable)
+{
+    krb5_localauth_vtable vt = (krb5_localauth_vtable)vtable;
+    static const char *types[] = { "RULE", NULL };
+
+    vt->name = "rule";
+    vt->an2ln_types = types;
+    vt->an2ln = an2ln_rule;
+    vt->free_string = freestr;
+    return 0;
+}
diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h
index a0fa37e..f0071d6 100644
--- a/src/lib/krb5/os/os-proto.h
+++ b/src/lib/krb5/os/os-proto.h
@@ -118,5 +118,14 @@ extern unsigned int krb5_skdc_timeout_shift;
 extern unsigned int krb5_skdc_timeout_1;
 extern unsigned int krb5_max_dgram_size;
 
+krb5_error_code localauth_names_initvt(krb5_context context, int maj_ver,
+                                       int min_ver, krb5_plugin_vtable vtable);
+krb5_error_code localauth_rule_initvt(krb5_context context, int maj_ver,
+                                      int min_ver, krb5_plugin_vtable vtable);
+krb5_error_code localauth_k5login_initvt(krb5_context context, int maj_ver,
+                                         int min_ver,
+                                         krb5_plugin_vtable vtable);
+krb5_error_code localauth_an2ln_initvt(krb5_context context, int maj_ver,
+                                       int min_ver, krb5_plugin_vtable vtable);
 
 #endif /* KRB5_LIBOS_INT_PROTO__ */


More information about the cvs-krb5 mailing list