krb5 commit: Use kadm5_auth interface in kadmind

Greg Hudson ghudson at mit.edu
Thu Aug 17 12:46:23 EDT 2017


https://github.com/krb5/krb5/commit/92a1a7efe2fc43337416098f2227038a72f1e35a
commit 92a1a7efe2fc43337416098f2227038a72f1e35a
Author: Greg Hudson <ghudson at mit.edu>
Date:   Fri Jun 30 11:54:09 2017 -0400

    Use kadm5_auth interface in kadmind
    
    Convert the ACL code to a kadm5_auth module, and create a new module
    for self-service authorization.  Use the kadm5_auth consumer code
    instead of directly using the ACL code to authorize requests.
    
    Do not assume self-service authorization in the RPC stubs or in
    schpw_util_wrapper().  For key change requests, enforce the initial
    ticket requirement whenever a client changes its own keys, regardless
    of how it is authorized or which protocol it uses.  The initial ticket
    check for protocol version 1 in process_chpw_request() is redundant
    after this change, so remove it.
    
    The old kadmin-based password change client authenticates to
    kadmin/changepw and performs self-service get_principal, get_policy,
    and chpass requests.  Continue to allow these operations, enforcing
    the self-service requirement in addition to checking through the
    kadm5_auth interface.  For get_policy requests, always look up the
    client principal's policy name, for this check and for the
    authorization layer's use.
    
    The error messages for rename authorization failures are now more
    vague (because there is a specific rename operation check in the
    kadm5_auth interface, and we do not find out whether it failed due to
    missing add or delete privileges).  Adjust t_kadmin_acl.py
    accordingly.
    
    ticket: 8595

 doc/admin/conf_files/kadm5_acl.rst |   13 ++
 doc/admin/conf_files/kdc_conf.rst  |    7 +-
 doc/admin/conf_files/krb5_conf.rst |   20 ++
 src/kadmin/server/Makefile.in      |    8 +-
 src/kadmin/server/auth.c           |    7 +
 src/kadmin/server/auth.h           |    7 +
 src/kadmin/server/auth_acl.c       |  344 ++++++++++++++++++++++++++----------
 src/kadmin/server/auth_acl.h       |   74 --------
 src/kadmin/server/auth_self.c      |   77 ++++++++
 src/kadmin/server/deps             |   33 +++-
 src/kadmin/server/ipropd_svc.c     |    5 +-
 src/kadmin/server/misc.c           |  122 ++------------
 src/kadmin/server/misc.h           |   17 --
 src/kadmin/server/ovsec_kadmd.c    |    8 +-
 src/kadmin/server/schpw.c          |   12 +-
 src/kadmin/server/server_stubs.c   |  281 ++++++++++++++++--------------
 src/lib/kadm5/kadm_err.et          |    1 +
 src/lib/krb5/krb/plugin.c          |    3 +-
 src/tests/t_kadmin_acl.py          |   10 +-
 19 files changed, 591 insertions(+), 458 deletions(-)

diff --git a/doc/admin/conf_files/kadm5_acl.rst b/doc/admin/conf_files/kadm5_acl.rst
index 138a2d7..290bf0e 100644
--- a/doc/admin/conf_files/kadm5_acl.rst
+++ b/doc/admin/conf_files/kadm5_acl.rst
@@ -144,6 +144,19 @@ principals.
 any principal that it creates or modifies will not be able to get
 postdateable tickets or tickets with a life of longer than 9 hours.
 
+MODULE BEHAVIOR
+---------------
+
+The ACL file can coexist with other authorization modules in release
+1.16 and later, as configured in the :ref:`kadm5_auth` section of
+:ref:`krb5.conf(5)`.  The ACL file will positively authorize
+operations according to the rules above, but will never
+authoritatively deny an operation, so other modules can authorize
+operations in addition to those authorized by the ACL file.
+
+To operate without an ACL file, set the *acl_file* variable in
+:ref:`kdc.conf(5)` to the empty string with ``acl_file = ""``.
+
 SEE ALSO
 --------
 
diff --git a/doc/admin/conf_files/kdc_conf.rst b/doc/admin/conf_files/kdc_conf.rst
index 3c11e45..3af1c37 100644
--- a/doc/admin/conf_files/kdc_conf.rst
+++ b/doc/admin/conf_files/kdc_conf.rst
@@ -86,9 +86,10 @@ The following tags may be specified in a [realms] subsection:
 **acl_file**
     (String.)  Location of the access control list file that
     :ref:`kadmind(8)` uses to determine which principals are allowed
-    which permissions on the Kerberos database.  The default value is
-    |kdcdir|\ ``/kadm5.acl``.  For more information on Kerberos ACL
-    file see :ref:`kadm5.acl(5)`.
+    which permissions on the Kerberos database.  To operate without an
+    ACL file, set this relation to the empty string with ``acl_file =
+    ""``.  The default value is |kdcdir|\ ``/kadm5.acl``.  For more
+    information on Kerberos ACL file see :ref:`kadm5.acl(5)`.
 
 **database_module**
     (String.)  This relation indicates the name of the configuration
diff --git a/doc/admin/conf_files/krb5_conf.rst b/doc/admin/conf_files/krb5_conf.rst
index 3a09279..7951149 100644
--- a/doc/admin/conf_files/krb5_conf.rst
+++ b/doc/admin/conf_files/krb5_conf.rst
@@ -778,6 +778,26 @@ interface can be used to write a plugin to synchronize MIT Kerberos
 with another database such as Active Directory.  No plugins are built
 in for this interface.
 
+.. _kadm5_auth:
+
+kadm5_auth interface
+====================
+
+The kadm5_auth section (introduced in release 1.16) controls modules
+for the kadmin authorization interface, which determines whether a
+client principal is allowed to perform a kadmin operation.  The
+following built-in modules exist for this interface:
+
+**acl**
+    This module reads the :ref:`kadm5.acl(5)` file, and authorizes
+    operations which are allowed according to the rules in the file.
+
+**self**
+    This module authorizes self-service operations including password
+    changes, creation of new random keys, fetching the client's
+    principal record or string attributes, and fetching the policy
+    record associated with the client principal.
+
 .. _clpreauth:
 
 .. _kdcpreauth:
diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in
index 2f97ab9..16d5cc5 100644
--- a/src/kadmin/server/Makefile.in
+++ b/src/kadmin/server/Makefile.in
@@ -7,10 +7,10 @@ LOCALINCLUDES = -I$(top_srcdir)/lib/gssapi/generic \
 	-I$(BUILDTOP)/lib/gssapi/krb5 -I$(top_srcdir)/lib/kadm5/srv
 
 PROG = kadmind
-OBJS = auth.o auth_acl.o kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o schpw.o \
-	misc.o ipropd_svc.o
-SRCS = auth.o auth_acl.c kadm_rpc_svc.c server_stubs.c ovsec_kadmd.c schpw.c \
-	misc.c ipropd_svc.c
+OBJS = auth.o auth_acl.o auth_self.o kadm_rpc_svc.o server_stubs.o \
+	ovsec_kadmd.o schpw.o misc.o ipropd_svc.o
+SRCS = auth.o auth_acl.c auth_self.c kadm_rpc_svc.c server_stubs.c \
+	ovsec_kadmd.c schpw.c misc.c ipropd_svc.c
 
 all: $(PROG)
 
diff --git a/src/kadmin/server/auth.c b/src/kadmin/server/auth.c
index ccff0eb..081b20a 100644
--- a/src/kadmin/server/auth.c
+++ b/src/kadmin/server/auth.c
@@ -66,7 +66,14 @@ auth_init(krb5_context context, const char *acl_file)
     krb5_plugin_initvt_fn *modules = NULL, *mod;
     size_t count;
     auth_handle h = NULL;
+    const int intf = PLUGIN_INTERFACE_KADM5_AUTH;
 
+    ret = k5_plugin_register(context, intf, "acl", kadm5_auth_acl_initvt);
+    if (ret)
+        goto cleanup;
+    ret = k5_plugin_register(context, intf, "self", kadm5_auth_self_initvt);
+    if (ret)
+        goto cleanup;
     ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_KADM5_AUTH, &modules);
     if (ret)
         goto cleanup;
diff --git a/src/kadmin/server/auth.h b/src/kadmin/server/auth.h
index 01e78cc..4d265ad 100644
--- a/src/kadmin/server/auth.h
+++ b/src/kadmin/server/auth.h
@@ -75,4 +75,11 @@ krb5_boolean auth_restrict(krb5_context context, int opcode,
 /* Notify modules that the most recent authorized operation has ended. */
 void auth_end(krb5_context context);
 
+/* initvt declarations for built-in modules */
+
+krb5_error_code kadm5_auth_acl_initvt(krb5_context context, int maj_ver,
+                                      int min_ver, krb5_plugin_vtable vtable);
+krb5_error_code kadm5_auth_self_initvt(krb5_context context, int maj_ver,
+                                       int min_ver, krb5_plugin_vtable vtable);
+
 #endif /* AUTH_H */
diff --git a/src/kadmin/server/auth_acl.c b/src/kadmin/server/auth_acl.c
index 389ff0b..1f804ba 100644
--- a/src/kadmin/server/auth_acl.c
+++ b/src/kadmin/server/auth_acl.c
@@ -1,5 +1,5 @@
 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* kadmin/server/auth_acl.c */
+/* kadmin/server/auth_acl.c - ACL kadm5_auth module */
 /*
  * Copyright 1995-2004, 2007, 2008, 2017 by the Massachusetts Institute of
  * Technology.  All Rights Reserved.
@@ -27,9 +27,33 @@
 #include "k5-int.h"
 #include <syslog.h>
 #include <kadm5/admin.h>
+#include <krb5/kadm5_auth_plugin.h>
 #include "adm_proto.h"
-#include "auth_acl.h"
 #include <ctype.h>
+#include "auth.h"
+
+/*
+ * Access control bits.
+ */
+#define ACL_ADD                 1
+#define ACL_DELETE              2
+#define ACL_MODIFY              4
+#define ACL_CHANGEPW            8
+/* #define ACL_CHANGE_OWN_PW    16 */
+#define ACL_INQUIRE             32
+#define ACL_EXTRACT             64
+#define ACL_LIST                128
+#define ACL_SETKEY              256
+#define ACL_IPROP               512
+
+#define ACL_ALL_MASK            (ACL_ADD        |       \
+                                 ACL_DELETE     |       \
+                                 ACL_MODIFY     |       \
+                                 ACL_CHANGEPW   |       \
+                                 ACL_INQUIRE    |       \
+                                 ACL_LIST       |       \
+                                 ACL_IPROP      |       \
+                                 ACL_SETKEY)
 
 struct acl_op_table {
     char op;
@@ -68,8 +92,6 @@ struct acl_state {
     struct acl_entry *list;
 };
 
-static struct acl_state aclstate;
-
 /*
  * Get a line from the ACL file.  Lines ending with \ are continued on the next
  * line.  The caller should set *lineno to 1 and *incr to 0 before the first
@@ -347,68 +369,6 @@ parse_line(krb5_context context, const char *line, const char *fname)
     return entry;
 }
 
-/* Impose restrictions, modifying *rec and *mask. */
-krb5_error_code
-acl_impose_restrictions(krb5_context context, kadm5_principal_ent_rec *rec,
-                        long *mask, struct kadm5_auth_restrictions *rs)
-{
-    krb5_error_code ret;
-    krb5_timestamp now;
-
-    if (rs == NULL)
-        return 0;
-    if (rs->mask & (KADM5_PRINC_EXPIRE_TIME | KADM5_PW_EXPIRATION)) {
-        ret = krb5_timeofday(context, &now);
-        if (ret)
-            return ret;
-    }
-
-    if (rs->mask & KADM5_ATTRIBUTES) {
-        rec->attributes |= rs->require_attrs;
-        rec->attributes &= rs->forbid_attrs;
-        *mask |= KADM5_ATTRIBUTES;
-    }
-    if (rs->mask & KADM5_POLICY_CLR) {
-        *mask &= ~KADM5_POLICY;
-        *mask |= KADM5_POLICY_CLR;
-    } else if (rs->mask & KADM5_POLICY) {
-        if (rec->policy != NULL && strcmp(rec->policy, rs->policy) != 0) {
-            free(rec->policy);
-            rec->policy = NULL;
-        }
-        if (rec->policy == NULL) {
-            rec->policy = strdup(rs->policy);  /* XDR will free it */
-            if (!rec->policy)
-                return ENOMEM;
-        }
-        *mask |= KADM5_POLICY;
-    }
-    if (rs->mask & KADM5_PRINC_EXPIRE_TIME) {
-        if (!(*mask & KADM5_PRINC_EXPIRE_TIME) ||
-            ts_after(rec->princ_expire_time, ts_incr(now, rs->princ_lifetime)))
-            rec->princ_expire_time = now + rs->princ_lifetime;
-        *mask |= KADM5_PRINC_EXPIRE_TIME;
-    }
-    if (rs->mask & KADM5_PW_EXPIRATION) {
-        if (!(*mask & KADM5_PW_EXPIRATION) ||
-            ts_after(rec->pw_expiration, ts_incr(now, rs->pw_lifetime)))
-            rec->pw_expiration = now + rs->pw_lifetime;
-        *mask |= KADM5_PW_EXPIRATION;
-    }
-    if (rs->mask & KADM5_MAX_LIFE) {
-        if (!(*mask & KADM5_MAX_LIFE) || rec->max_life > rs->max_life)
-            rec->max_life = rs->max_life;
-        *mask |= KADM5_MAX_LIFE;
-    }
-    if (rs->mask & KADM5_MAX_RLIFE) {
-        if (!(*mask & KADM5_MAX_RLIFE) ||
-            rec->max_renewable_life > rs->max_renewable_life)
-            rec->max_renewable_life = rs->max_renewable_life;
-        *mask |= KADM5_MAX_RLIFE;
-    }
-    return 0;
-}
-
 /* Free all ACL entries. */
 static void
 free_acl_entries(struct acl_state *state)
@@ -423,9 +383,10 @@ free_acl_entries(struct acl_state *state)
 }
 
 /* Open and parse the ACL file. */
-static void
+static krb5_error_code
 load_acl_file(krb5_context context, const char *fname, struct acl_state *state)
 {
+    krb5_error_code ret;
     FILE *fp;
     char *line;
     struct acl_entry **entry_slot;
@@ -438,7 +399,10 @@ load_acl_file(krb5_context context, const char *fname, struct acl_state *state)
     if (fp == NULL) {
         krb5_klog_syslog(LOG_ERR, _("%s while opening ACL file %s"),
                          error_message(errno), fname);
-        return;
+        ret = errno;
+        k5_setmsg(context, errno, _("Cannot open %s: %s"), fname,
+                  error_message(ret));
+        return ret;
     }
 
     set_cloexec_file(fp);
@@ -457,13 +421,17 @@ load_acl_file(krb5_context context, const char *fname, struct acl_state *state)
             free_acl_entries(state);
             free(line);
             fclose(fp);
-            return;
+            k5_setmsg(context, EINVAL,
+                      _("%s: syntax error at line %d <%.10s...>"),
+                      fname, lineno, line);
+            return EINVAL;
         }
         entry_slot = &(*entry_slot)->next;
         free(line);
     }
 
     fclose(fp);
+    return 0;
 }
 
 /*
@@ -552,25 +520,10 @@ find_entry(struct acl_state *state, krb5_const_principal client,
     return NULL;
 }
 
-/* Initialize the ACL context. */
-krb5_error_code
-acl_init(krb5_context context, const char *acl_file)
-{
-    load_acl_file(context, acl_file, &aclstate);
-    return 0;
-}
-
-/* Terminate the ACL context. */
-void
-acl_finish(krb5_context context)
-{
-    free_acl_entries(&aclstate);
-}
-
 /* Return true if op is permitted for this principal.  Set *rs_out (if not
  * NULL) according to any restrictions in the ACL entry. */
-krb5_boolean
-acl_check(krb5_context context, krb5_const_principal client, uint32_t op,
+static krb5_error_code
+acl_check(kadm5_auth_moddata data, uint32_t op, krb5_const_principal client,
           krb5_const_principal target, struct kadm5_auth_restrictions **rs_out)
 {
     struct acl_entry *entry;
@@ -578,14 +531,225 @@ acl_check(krb5_context context, krb5_const_principal client, uint32_t op,
     if (rs_out != NULL)
         *rs_out = NULL;
 
-    entry = find_entry(&aclstate, client, target);
+    entry = find_entry((struct acl_state *)data, client, target);
     if (entry == NULL)
-        return FALSE;
+        return KRB5_PLUGIN_NO_HANDLE;
     if (!(entry->op_allowed & op))
-        return FALSE;
+        return KRB5_PLUGIN_NO_HANDLE;
 
     if (rs_out != NULL && entry->rs != NULL && entry->rs->mask)
         *rs_out = entry->rs;
 
-    return TRUE;
+    return 0;
+}
+
+static krb5_error_code
+acl_init(krb5_context context, const char *acl_file,
+         kadm5_auth_moddata *data_out)
+{
+    krb5_error_code ret;
+    struct acl_state *state;
+
+    *data_out = NULL;
+    if (acl_file == NULL)
+        return KRB5_PLUGIN_NO_HANDLE;
+    state = malloc(sizeof(*state));
+    state->list = NULL;
+    ret = load_acl_file(context, acl_file, state);
+    if (ret) {
+        free(state);
+        return ret;
+    }
+    *data_out = (kadm5_auth_moddata)state;
+    return 0;
+}
+
+static void
+acl_fini(krb5_context context, kadm5_auth_moddata data)
+{
+    if (data == NULL)
+        return;
+    free_acl_entries((struct acl_state *)data);
+    free(data);
+}
+
+static krb5_error_code
+acl_addprinc(krb5_context context, kadm5_auth_moddata data,
+             krb5_const_principal client, krb5_const_principal target,
+             const struct _kadm5_principal_ent_t *ent, long mask,
+             struct kadm5_auth_restrictions **rs_out)
+{
+    return acl_check(data, ACL_ADD, client, target, rs_out);
+}
+
+static krb5_error_code
+acl_modprinc(krb5_context context, kadm5_auth_moddata data,
+             krb5_const_principal client, krb5_const_principal target,
+             const struct _kadm5_principal_ent_t *ent, long mask,
+             struct kadm5_auth_restrictions **rs_out)
+{
+    return acl_check(data, ACL_MODIFY, client, target, rs_out);
+}
+
+static krb5_error_code
+acl_setstr(krb5_context context, kadm5_auth_moddata data,
+           krb5_const_principal client, krb5_const_principal target,
+           const char *key, const char *value)
+{
+    return acl_check(data, ACL_MODIFY, client, target, NULL);
+}
+
+static krb5_error_code
+acl_cpw(krb5_context context, kadm5_auth_moddata data,
+        krb5_const_principal client, krb5_const_principal target)
+{
+    return acl_check(data, ACL_CHANGEPW, client, target, NULL);
+}
+
+static krb5_error_code
+acl_chrand(krb5_context context, kadm5_auth_moddata data,
+           krb5_const_principal client, krb5_const_principal target)
+{
+    return acl_check(data, ACL_CHANGEPW, client, target, NULL);
+}
+
+static krb5_error_code
+acl_setkey(krb5_context context, kadm5_auth_moddata data,
+           krb5_const_principal client, krb5_const_principal target)
+{
+    return acl_check(data, ACL_SETKEY, client, target, NULL);
+}
+
+static krb5_error_code
+acl_purgekeys(krb5_context context, kadm5_auth_moddata data,
+              krb5_const_principal client, krb5_const_principal target)
+{
+    return acl_check(data, ACL_MODIFY, client, target, NULL);
+}
+
+static krb5_error_code
+acl_delprinc(krb5_context context, kadm5_auth_moddata data,
+             krb5_const_principal client, krb5_const_principal target)
+{
+    return acl_check(data, ACL_DELETE, client, target, NULL);
+}
+
+static krb5_error_code
+acl_renprinc(krb5_context context, kadm5_auth_moddata data,
+             krb5_const_principal client, krb5_const_principal src,
+             krb5_const_principal dest)
+{
+    struct kadm5_auth_restrictions *rs;
+
+    if (acl_check(data, ACL_DELETE, client, src, NULL) == 0 &&
+        acl_check(data, ACL_ADD, client, dest, &rs) == 0 && rs == NULL)
+        return 0;
+    return KRB5_PLUGIN_NO_HANDLE;
+}
+
+static krb5_error_code
+acl_getprinc(krb5_context context, kadm5_auth_moddata data,
+             krb5_const_principal client, krb5_const_principal target)
+{
+    return acl_check(data, ACL_INQUIRE, client, target, NULL);
+}
+
+static krb5_error_code
+acl_getstrs(krb5_context context, kadm5_auth_moddata data,
+            krb5_const_principal client, krb5_const_principal target)
+{
+    return acl_check(data, ACL_INQUIRE, client, target, NULL);
+}
+
+static krb5_error_code
+acl_extract(krb5_context context, kadm5_auth_moddata data,
+            krb5_const_principal client, krb5_const_principal target)
+{
+    return acl_check(data, ACL_EXTRACT, client, target, NULL);
+}
+
+static krb5_error_code
+acl_listprincs(krb5_context context, kadm5_auth_moddata data,
+               krb5_const_principal client)
+{
+    return acl_check(data, ACL_LIST, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_addpol(krb5_context context, kadm5_auth_moddata data,
+           krb5_const_principal client, const char *policy,
+           const struct _kadm5_policy_ent_t *ent, long mask)
+{
+    return acl_check(data, ACL_ADD, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_modpol(krb5_context context, kadm5_auth_moddata data,
+           krb5_const_principal client, const char *policy,
+           const struct _kadm5_policy_ent_t *ent, long mask)
+{
+    return acl_check(data, ACL_MODIFY, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_delpol(krb5_context context, kadm5_auth_moddata data,
+           krb5_const_principal client, const char *policy)
+{
+    return acl_check(data, ACL_DELETE, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_getpol(krb5_context context, kadm5_auth_moddata data,
+           krb5_const_principal client, const char *policy,
+           const char *client_policy)
+{
+    return acl_check(data, ACL_INQUIRE, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_listpols(krb5_context context, kadm5_auth_moddata data,
+             krb5_const_principal client)
+{
+    return acl_check(data, ACL_LIST, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_iprop(krb5_context context, kadm5_auth_moddata data,
+          krb5_const_principal client)
+{
+    return acl_check(data, ACL_IPROP, client, NULL, NULL);
+}
+
+krb5_error_code
+kadm5_auth_acl_initvt(krb5_context context, int maj_ver, int min_ver,
+                      krb5_plugin_vtable vtable)
+{
+    kadm5_auth_vtable vt;
+
+    if (maj_ver != 1)
+        return KRB5_PLUGIN_VER_NOTSUPP;
+    vt = (kadm5_auth_vtable)vtable;
+    vt->name = "acl";
+    vt->init = acl_init;
+    vt->fini = acl_fini;
+    vt->addprinc = acl_addprinc;
+    vt->modprinc = acl_modprinc;
+    vt->setstr = acl_setstr;
+    vt->cpw = acl_cpw;
+    vt->chrand = acl_chrand;
+    vt->setkey = acl_setkey;
+    vt->purgekeys = acl_purgekeys;
+    vt->delprinc = acl_delprinc;
+    vt->renprinc = acl_renprinc;
+    vt->getprinc = acl_getprinc;
+    vt->getstrs = acl_getstrs;
+    vt->extract = acl_extract;
+    vt->listprincs = acl_listprincs;
+    vt->addpol = acl_addpol;
+    vt->modpol = acl_modpol;
+    vt->delpol = acl_delpol;
+    vt->getpol = acl_getpol;
+    vt->listpols = acl_listpols;
+    vt->iprop = acl_iprop;
+    return 0;
 }
diff --git a/src/kadmin/server/auth_acl.h b/src/kadmin/server/auth_acl.h
deleted file mode 100644
index 55a8662..0000000
--- a/src/kadmin/server/auth_acl.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
-/* kadmin/server/auth_acl.h */
-/*
- * Copyright 1995-2004, 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.
- */
-
-#ifndef SERVER_ACL_H__
-#define SERVER_ACL_H__
-
-/*
- * Access control bits.
- */
-#define ACL_ADD                 1
-#define ACL_DELETE              2
-#define ACL_MODIFY              4
-#define ACL_CHANGEPW            8
-/* #define ACL_CHANGE_OWN_PW    16 */
-#define ACL_INQUIRE             32
-#define ACL_EXTRACT             64
-#define ACL_LIST                128
-#define ACL_SETKEY              256
-#define ACL_IPROP               512
-
-#define ACL_ALL_MASK            (ACL_ADD        |       \
-                                 ACL_DELETE     |       \
-                                 ACL_MODIFY     |       \
-                                 ACL_CHANGEPW   |       \
-                                 ACL_INQUIRE    |       \
-                                 ACL_LIST       |       \
-                                 ACL_IPROP      |       \
-                                 ACL_SETKEY)
-
-struct kadm5_auth_restrictions {
-    long mask;
-    krb5_flags require_attrs;
-    krb5_flags forbid_attrs;
-    krb5_deltat princ_lifetime;
-    krb5_deltat pw_lifetime;
-    krb5_deltat max_life;
-    krb5_deltat max_renewable_life;
-    char *policy;
-};
-
-krb5_error_code acl_init(krb5_context context, const char *acl_file);
-void acl_finish(krb5_context);
-krb5_boolean acl_check(krb5_context context, krb5_const_principal client,
-                       uint32_t op, krb5_const_principal target,
-                       struct kadm5_auth_restrictions **rs_out);
-krb5_error_code acl_impose_restrictions(krb5_context context,
-                                        kadm5_principal_ent_rec *rec,
-                                        long *mask,
-                                        struct kadm5_auth_restrictions *rs);
-
-#endif  /* SERVER_ACL_H__ */
diff --git a/src/kadmin/server/auth_self.c b/src/kadmin/server/auth_self.c
new file mode 100644
index 0000000..253d4bc
--- /dev/null
+++ b/src/kadmin/server/auth_self.c
@@ -0,0 +1,77 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/server/auth_self.c - self-service kadm5_auth module */
+/*
+ * Copyright (C) 2017 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 <kadm5/admin.h>
+#include <krb5/kadm5_auth_plugin.h>
+#include "auth.h"
+
+/* Authorize a principal to operate on itself.  Applies to cpw, chrand,
+ * purgekeys, getprinc, and getstrs. */
+static krb5_error_code
+self_compare(krb5_context context, kadm5_auth_moddata data,
+             krb5_const_principal client, krb5_const_principal target)
+{
+    if (krb5_principal_compare(context, client, target))
+        return 0;
+    return KRB5_PLUGIN_NO_HANDLE;
+}
+
+/* Authorize a principal to get the policy record for its own policy. */
+static krb5_error_code
+self_getpol(krb5_context context, kadm5_auth_moddata data,
+            krb5_const_principal client, const char *policy,
+            const char *client_policy)
+{
+    if (client_policy != NULL && strcmp(policy, client_policy) == 0)
+        return 0;
+    return KRB5_PLUGIN_NO_HANDLE;
+}
+
+krb5_error_code
+kadm5_auth_self_initvt(krb5_context context, int maj_ver, int min_ver,
+                       krb5_plugin_vtable vtable)
+{
+    kadm5_auth_vtable vt;
+
+    if (maj_ver != 1)
+        return KRB5_PLUGIN_VER_NOTSUPP;
+    vt = (kadm5_auth_vtable)vtable;
+    vt->name = "self";
+    vt->cpw = self_compare;
+    vt->chrand = self_compare;
+    vt->purgekeys = self_compare;
+    vt->getprinc = self_compare;
+    vt->getstrs = self_compare;
+    vt->getpol = self_getpol;
+    return 0;
+}
diff --git a/src/kadmin/server/deps b/src/kadmin/server/deps
index e7cd3e6..99aef75 100644
--- a/src/kadmin/server/deps
+++ b/src/kadmin/server/deps
@@ -17,9 +17,28 @@ $(OUTPRE)auth_acl.$(OBJEXT): $(BUILDTOP)/include/autoconf.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/plugin.h \
-  $(top_srcdir)/include/port-sockets.h $(top_srcdir)/include/socket-utils.h \
-  auth_acl.c auth_acl.h
+  $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/kadm5_auth_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h auth.h auth_acl.c
+$(OUTPRE)auth_self.$(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/kadm5_auth_plugin.h \
+  $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/port-sockets.h \
+  $(top_srcdir)/include/socket-utils.h auth.h auth_self.c
 $(OUTPRE)kadm_rpc_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
   $(BUILDTOP)/include/gssapi/gssapi_krb5.h $(BUILDTOP)/include/gssrpc/types.h \
@@ -59,7 +78,7 @@ $(OUTPRE)server_stubs.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/kdb.h \
   $(top_srcdir)/include/krb5.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/net-server.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h auth_acl.h misc.h \
+  $(top_srcdir)/include/socket-utils.h auth.h misc.h \
   server_stubs.c
 $(OUTPRE)ovsec_kadmd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_alloc.h \
@@ -89,7 +108,7 @@ $(OUTPRE)ovsec_kadmd.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/lib/gssapi/generic/gssapiP_generic.h \
   $(top_srcdir)/lib/gssapi/generic/gssapi_ext.h $(top_srcdir)/lib/gssapi/generic/gssapi_generic.h \
   $(top_srcdir)/lib/gssapi/krb5/gssapiP_krb5.h $(top_srcdir)/lib/gssapi/krb5/gssapi_krb5.h \
-  auth_acl.h misc.h ovsec_kadmd.c
+  auth.h misc.h ovsec_kadmd.c
 $(OUTPRE)schpw.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssrpc/types.h \
   $(BUILDTOP)/include/kadm5/admin.h $(BUILDTOP)/include/kadm5/admin_internal.h \
@@ -130,7 +149,7 @@ $(OUTPRE)misc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/kdb.h $(top_srcdir)/include/krb5.h \
   $(top_srcdir)/include/krb5/authdata_plugin.h $(top_srcdir)/include/krb5/plugin.h \
   $(top_srcdir)/include/net-server.h $(top_srcdir)/include/port-sockets.h \
-  $(top_srcdir)/include/socket-utils.h auth_acl.h misc.c \
+  $(top_srcdir)/include/socket-utils.h auth.h misc.c \
   misc.h
 $(OUTPRE)ipropd_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(BUILDTOP)/include/gssapi/gssapi.h $(BUILDTOP)/include/gssapi/gssapi_ext.h \
@@ -149,5 +168,5 @@ $(OUTPRE)ipropd_svc.$(OBJEXT): $(BUILDTOP)/include/autoconf.h \
   $(top_srcdir)/include/k5-thread.h $(top_srcdir)/include/kdb.h \
   $(top_srcdir)/include/kdb_log.h $(top_srcdir)/include/krb5.h \
   $(top_srcdir)/include/krb5/plugin.h $(top_srcdir)/include/net-server.h \
-  $(top_srcdir)/lib/gssapi/krb5/gssapi_krb5.h auth_acl.h \
+  $(top_srcdir)/lib/gssapi/krb5/gssapi_krb5.h auth.h \
   ipropd_svc.c misc.h
diff --git a/src/kadmin/server/ipropd_svc.c b/src/kadmin/server/ipropd_svc.c
index c552ed0..e6e1901 100644
--- a/src/kadmin/server/ipropd_svc.c
+++ b/src/kadmin/server/ipropd_svc.c
@@ -24,7 +24,7 @@
 #include <arpa/inet.h>
 #include <netdb.h>
 #include <kdb_log.h>
-#include "auth_acl.h"
+#include "auth.h"
 #include "misc.h"
 #include "osconf.h"
 
@@ -137,7 +137,8 @@ iprop_acl_check(krb5_context context, const char *client_name)
 
     if (krb5_parse_name(context, client_name, &client_princ) != 0)
 	return FALSE;
-    result = acl_check(context, client_princ, ACL_IPROP, NULL, NULL);
+    result = auth(context, OP_IPROP, client_princ,
+		  NULL, NULL, NULL, NULL, NULL, 0);
     krb5_free_principal(context, client_princ);
     return result;
 }
diff --git a/src/kadmin/server/misc.c b/src/kadmin/server/misc.c
index 1975af0..6b258a6 100644
--- a/src/kadmin/server/misc.c
+++ b/src/kadmin/server/misc.c
@@ -8,95 +8,8 @@
 #include    <kdb.h>
 #include    <kadm5/server_internal.h>
 #include    "misc.h"
-#include    "auth_acl.h"
+#include    "auth.h"
 #include    "net-server.h"
-
-/*
- * Function: chpass_principal_wrapper_3
- *
- * Purpose: wrapper to kadm5_chpass_principal that checks to see if
- *          pw_min_life has been reached. if not it returns an error.
- *          otherwise it calls kadm5_chpass_principal
- *
- * Arguments:
- *      principal       (input) krb5_principals whose password we are
- *                              changing
- *      keepold         (input) whether to preserve old keys
- *      n_ks_tuple      (input) the number of key-salt tuples in ks_tuple
- *      ks_tuple        (input) array of tuples indicating the caller's
- *                              requested enctypes/salttypes
- *      password        (input) password we are going to change to.
- *      <return value>  0 on success error code on failure.
- *
- * Requires:
- *      kadm5_init to have been run.
- *
- * Effects:
- *      calls kadm5_chpass_principal which changes the kdb and the
- *      the admin db.
- *
- */
-kadm5_ret_t
-chpass_principal_wrapper_3(void *server_handle,
-                           krb5_principal principal,
-                           krb5_boolean keepold,
-                           int n_ks_tuple,
-                           krb5_key_salt_tuple *ks_tuple,
-                           char *password)
-{
-    kadm5_ret_t                 ret;
-
-    ret = check_min_life(server_handle, principal, NULL, 0);
-    if (ret)
-        return ret;
-
-    return kadm5_chpass_principal_3(server_handle, principal,
-                                    keepold, n_ks_tuple, ks_tuple,
-                                    password);
-}
-
-
-/*
- * Function: randkey_principal_wrapper_3
- *
- * Purpose: wrapper to kadm5_randkey_principal which checks the
- *          password's min. life.
- *
- * Arguments:
- *      principal           (input) krb5_principal whose password we are
- *                                  changing
- *      keepold         (input) whether to preserve old keys
- *      n_ks_tuple      (input) the number of key-salt tuples in ks_tuple
- *      ks_tuple        (input) array of tuples indicating the caller's
- *                              requested enctypes/salttypes
- *      key                 (output) new random key
- *      <return value>      0, error code on error.
- *
- * Requires:
- *      kadm5_init       needs to be run
- *
- * Effects:
- *      calls kadm5_randkey_principal
- *
- */
-kadm5_ret_t
-randkey_principal_wrapper_3(void *server_handle,
-                            krb5_principal principal,
-                            krb5_boolean keepold,
-                            int n_ks_tuple,
-                            krb5_key_salt_tuple *ks_tuple,
-                            krb5_keyblock **keys, int *n_keys)
-{
-    kadm5_ret_t                 ret;
-
-    ret = check_min_life(server_handle, principal, NULL, 0);
-    if (ret)
-        return ret;
-    return kadm5_randkey_principal_3(server_handle, principal,
-                                     keepold, n_ks_tuple, ks_tuple,
-                                     keys, n_keys);
-}
-
 kadm5_ret_t
 schpw_util_wrapper(void *server_handle,
                    krb5_principal client,
@@ -107,8 +20,6 @@ schpw_util_wrapper(void *server_handle,
 {
     kadm5_ret_t                 ret;
     kadm5_server_handle_t       handle = server_handle;
-    krb5_boolean                access_granted;
-    krb5_boolean                self;
 
     /*
      * If no target is explicitly provided, then the target principal
@@ -117,31 +28,22 @@ schpw_util_wrapper(void *server_handle,
     if (target == NULL)
         target = client;
 
-    /*
-     * A principal can always change its own password, as long as it
-     * has an initial ticket and meets the minimum password lifetime
-     * requirement.
-     */
-    self = krb5_principal_compare(handle->context, client, target);
-    if (self) {
+    /* If the client is changing its own password, require it to use an initial
+     * ticket, and enforce the policy min_life. */
+    if (krb5_principal_compare(handle->context, client, target)) {
+        if (!initial_flag) {
+            strlcpy(msg_ret, "Ticket must be derived from a password",
+                    msg_len);
+            return KADM5_AUTH_INITIAL;
+        }
+
         ret = check_min_life(server_handle, target, msg_ret, msg_len);
         if (ret != 0)
             return ret;
-
-        access_granted = initial_flag;
-    } else
-        access_granted = FALSE;
-
-    if (!access_granted &&
-        acl_check(handle->context, client, ACL_CHANGEPW, target, NULL)) {
-        /*
-         * Otherwise, principals with appropriate privileges can change
-         * any password
-         */
-        access_granted = TRUE;
     }
 
-    if (access_granted) {
+    if (auth(handle->context, OP_CPW, client, target,
+             NULL, NULL, NULL, NULL, 0)) {
         ret = kadm5_chpass_principal_util(server_handle,
                                           target,
                                           new_pw, ret_pw,
diff --git a/src/kadmin/server/misc.h b/src/kadmin/server/misc.h
index ea0fc7d..3a112a0 100644
--- a/src/kadmin/server/misc.h
+++ b/src/kadmin/server/misc.h
@@ -13,23 +13,6 @@ int
 setup_gss_names(struct svc_req *, gss_buffer_desc *,
                 gss_buffer_desc *);
 
-
-kadm5_ret_t
-chpass_principal_wrapper_3(void *server_handle,
-                           krb5_principal principal,
-                           krb5_boolean keepold,
-                           int n_ks_tuple,
-                           krb5_key_salt_tuple *ks_tuple,
-                           char *password);
-
-kadm5_ret_t
-randkey_principal_wrapper_3(void *server_handle,
-                            krb5_principal principal,
-                            krb5_boolean keepold,
-                            int n_ks_tuple,
-                            krb5_key_salt_tuple *ks_tuple,
-                            krb5_keyblock **keys, int *n_keys);
-
 kadm5_ret_t
 schpw_util_wrapper(void *server_handle, krb5_principal client,
                    krb5_principal target, krb5_boolean initial_flag,
diff --git a/src/kadmin/server/ovsec_kadmd.c b/src/kadmin/server/ovsec_kadmd.c
index 498c7cd..6c87590 100644
--- a/src/kadmin/server/ovsec_kadmd.c
+++ b/src/kadmin/server/ovsec_kadmd.c
@@ -58,7 +58,7 @@
 #include <kdb_log.h>
 
 #include "misc.h"
-#include "auth_acl.h"
+#include "auth.h"
 
 #if defined(NEED_DAEMON_PROTO)
 int daemon(int, int);
@@ -356,6 +356,7 @@ main(int argc, char *argv[])
     verto_ctx *vctx;
     const char *pid_file = NULL;
     char **db_args = NULL, **tmpargs;
+    const char *acl_file;
     int ret, i, db_args_size = 0, strong_random = 1, proponly = 0;
 
     setlocale(LC_ALL, "");
@@ -505,7 +506,8 @@ main(int argc, char *argv[])
     if (svcauth_gss_set_svc_name(GSS_C_NO_NAME) != TRUE)
         fail_to_start(0, _("Cannot initialize GSSAPI service name"));
 
-    ret = acl_init(context, params.acl_file);
+    acl_file = (*params.acl_file != '\0') ? params.acl_file : NULL;
+    ret = auth_init(context, acl_file);
     if (ret)
         fail_to_start(ret, _("initializing ACL file"));
 
@@ -550,7 +552,7 @@ main(int argc, char *argv[])
     svcauth_gssapi_unset_names();
     kadm5_destroy(global_server_handle);
     loop_free(vctx);
-    acl_finish(context);
+    auth_fini(context);
     (void)gss_release_name(&minor_status, &gss_changepw_name);
     (void)gss_release_name(&minor_status, &gss_oldchangepw_name);
     for (i = 0; i < 4; i++)
diff --git a/src/kadmin/server/schpw.c b/src/kadmin/server/schpw.c
index d462616..491cba9 100644
--- a/src/kadmin/server/schpw.c
+++ b/src/kadmin/server/schpw.c
@@ -205,15 +205,6 @@ process_chpw_request(krb5_context context, void *server_handle, char *realm,
         goto chpwfail;
     }
 
-    /* for cpw, verify that this is an AS_REQ ticket */
-    if (vno == 1 &&
-        (ticket->enc_part2->flags & TKT_FLG_INITIAL) == 0) {
-        numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
-        strlcpy(strresult, "Ticket must be derived from a password",
-                sizeof(strresult));
-        goto chpwfail;
-    }
-
     /* change the password */
 
     ptr = k5memdup0(clear.data, clear.length, &ret);
@@ -292,6 +283,9 @@ process_chpw_request(krb5_context context, void *server_handle, char *realm,
     case KADM5_AUTH_CHANGEPW:
         numresult = KRB5_KPASSWD_ACCESSDENIED;
         break;
+    case KADM5_AUTH_INITIAL:
+        numresult = KRB5_KPASSWD_INITIAL_FLAG_NEEDED;
+        break;
     case KADM5_PASS_Q_TOOSHORT:
     case KADM5_PASS_REUSE:
     case KADM5_PASS_Q_CLASS:
diff --git a/src/kadmin/server/server_stubs.c b/src/kadmin/server/server_stubs.c
index 84b877a..cfef97f 100644
--- a/src/kadmin/server/server_stubs.c
+++ b/src/kadmin/server/server_stubs.c
@@ -15,7 +15,7 @@
 #include <syslog.h>
 #include <adm_proto.h>  /* krb5_klog_syslog */
 #include "misc.h"
-#include "auth_acl.h"
+#include "auth.h"
 
 extern gss_name_t                       gss_changepw_name;
 extern gss_name_t                       gss_oldchangepw_name;
@@ -216,19 +216,6 @@ static gss_name_t acceptor_name(gss_ctx_id_t context)
     return name;
 }
 
-static int cmp_gss_krb5_name(kadm5_server_handle_t handle,
-                             gss_name_t gss_name, krb5_principal princ)
-{
-    krb5_principal princ2;
-    int status;
-
-    if (! gss_to_krb5_name(handle, gss_name, &princ2))
-        return 0;
-    status = krb5_principal_compare(handle->context, princ, princ2);
-    krb5_free_principal(handle->context, princ2);
-    return status;
-}
-
 static int gss_to_krb5_name(kadm5_server_handle_t handle,
                             gss_name_t gss_name, krb5_principal *princ)
 {
@@ -314,6 +301,7 @@ stub_cleanup(kadm5_server_handle_t handle, char *princ_str,
 {
     OM_uint32 minor_stat;
 
+    auth_end(handle->context);
     free_server_handle(handle);
     free(princ_str);
     gss_release_buffer(&minor_stat, client_name);
@@ -321,12 +309,66 @@ stub_cleanup(kadm5_server_handle_t handle, char *princ_str,
 }
 
 static krb5_boolean
-stub_acl_check(kadm5_server_handle_t handle, uint32_t op,
-               krb5_const_principal target,
-               struct kadm5_auth_restrictions **rs_out)
+stub_auth(kadm5_server_handle_t handle, int opcode, krb5_const_principal p1,
+          krb5_const_principal p2, const char *s1, const char *s2)
+{
+    return auth(handle->context, opcode, handle->current_caller, p1, p2,
+                s1, s2, NULL, 0);
+}
+
+static krb5_boolean
+stub_auth_pol(kadm5_server_handle_t handle, int opcode, const char *policy,
+              const kadm5_policy_ent_rec *polent, long mask)
+{
+    return auth(handle->context, opcode, handle->current_caller, NULL, NULL,
+                policy, NULL, polent, mask);
+}
+
+static krb5_boolean
+stub_auth_restrict(kadm5_server_handle_t handle, int opcode,
+                   kadm5_principal_ent_t ent, long *mask)
+{
+    return auth_restrict(handle->context, opcode, handle->current_caller,
+                         ent, mask);
+}
+
+/* Return true if the client authenticated to kadmin/changepw and princ is not
+ * the client principal. */
+static krb5_boolean
+changepw_not_self(kadm5_server_handle_t handle, struct svc_req *rqstp,
+                  krb5_const_principal princ)
+{
+    return CHANGEPW_SERVICE(rqstp) &&
+        !krb5_principal_compare(handle->context, handle->current_caller,
+                                princ);
+}
+
+static krb5_boolean
+ticket_is_initial(struct svc_req *rqstp)
+{
+    OM_uint32 status, minor_stat;
+    krb5_flags flags;
+
+    status = gss_krb5_get_tkt_flags(&minor_stat, rqstp->rq_svccred, &flags);
+    if (status != GSS_S_COMPLETE)
+        return 0;
+    return (flags & TKT_FLG_INITIAL) != 0;
+}
+
+/* If a key change request is for the client's own principal, verify that the
+ * client used an initial ticket and enforce the policy min_life. */
+static kadm5_ret_t
+check_self_keychange(kadm5_server_handle_t handle, struct svc_req *rqstp,
+                     krb5_principal princ)
 {
-    return acl_check(handle->context, handle->current_caller, op, target,
-                     rs_out);
+    if (!krb5_principal_compare(handle->context, handle->current_caller,
+                                princ))
+        return 0;
+
+    if (!ticket_is_initial(rqstp))
+        return KADM5_AUTH_INITIAL;
+
+    return check_min_life(handle, princ, NULL, 0);
 }
 
 static int
@@ -396,7 +438,6 @@ create_principal_2_svc(cprinc_arg *arg, generic_ret *ret,
     gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
     gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t       handle;
-    struct kadm5_auth_restrictions *rp;
     const char                  *errmsg = NULL;
 
     ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
@@ -406,8 +447,7 @@ create_principal_2_svc(cprinc_arg *arg, generic_ret *ret,
         goto exit_func;
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_ADD, arg->rec.principal, &rp) ||
-        acl_impose_restrictions(handle->context, &arg->rec, &arg->mask, rp)) {
+        !stub_auth_restrict(handle, OP_ADDPRINC, &arg->rec, &arg->mask)) {
         ret->code = KADM5_AUTH_ADD;
         log_unauth("kadm5_create_principal", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -438,7 +478,6 @@ create_principal3_2_svc(cprinc3_arg *arg, generic_ret *ret,
     gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
     gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t       handle;
-    struct kadm5_auth_restrictions *rp;
     const char                  *errmsg = NULL;
 
     ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
@@ -448,8 +487,7 @@ create_principal3_2_svc(cprinc3_arg *arg, generic_ret *ret,
         goto exit_func;
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_ADD, arg->rec.principal, &rp) ||
-        acl_impose_restrictions(handle->context, &arg->rec, &arg->mask, rp)) {
+        !stub_auth_restrict(handle, OP_ADDPRINC, &arg->rec, &arg->mask)) {
         ret->code = KADM5_AUTH_ADD;
         log_unauth("kadm5_create_principal", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -504,7 +542,7 @@ delete_principal_2_svc(dprinc_arg *arg, generic_ret *ret,
         goto exit_func;
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_DELETE, arg->princ, NULL)) {
+        !stub_auth(handle, OP_DELPRINC, arg->princ, NULL, NULL, NULL)) {
         ret->code = KADM5_AUTH_DELETE;
         log_unauth("kadm5_delete_principal", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -544,7 +582,6 @@ modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret,
     gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
     gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
-    struct kadm5_auth_restrictions *rp;
     const char                      *errmsg = NULL;
 
     ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
@@ -554,8 +591,7 @@ modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret,
         goto exit_func;
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_MODIFY, arg->rec.principal, &rp) ||
-        acl_impose_restrictions(handle->context, &arg->rec, &arg->mask, rp)) {
+        !stub_auth_restrict(handle, OP_MODPRINC, &arg->rec, &arg->mask)) {
         ret->code = KADM5_AUTH_MODIFY;
         log_unauth("kadm5_modify_principal", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -594,7 +630,6 @@ rename_principal_2_svc(rprinc_arg *arg, generic_ret *ret,
     gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
     gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t       handle;
-    struct kadm5_auth_restrictions *rp;
     const char                  *errmsg = NULL;
     size_t                      tlen1, tlen2, clen, slen;
     char                        *tdots1, *tdots2, *cdots, *sdots;
@@ -619,27 +654,19 @@ rename_principal_2_svc(rprinc_arg *arg, generic_ret *ret,
     slen = service_name.length;
     trunc_name(&slen, &sdots);
 
-    ret->code = KADM5_OK;
-    if (! CHANGEPW_SERVICE(rqstp)) {
-        if (!stub_acl_check(handle, ACL_DELETE, arg->src, NULL))
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !stub_auth(handle, OP_RENPRINC, arg->src, arg->dest, NULL, NULL)) {
+        ret->code = KADM5_AUTH_INSUFFICIENT;
+        log_unauth("kadm5_rename_principal", prime_arg1, &client_name,
+                   &service_name, rqstp);
+    } else {
+        ret->code = check_lockdown_keys(handle, arg->src);
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_rename_principal", prime_arg1, &client_name,
+                       &service_name, rqstp);
             ret->code = KADM5_AUTH_DELETE;
-        /* any restrictions at all on the ADD kills the RENAME */
-        if (!stub_acl_check(handle, ACL_ADD, arg->dest, &rp) || rp != NULL) {
-            if (ret->code == KADM5_AUTH_DELETE)
-                ret->code = KADM5_AUTH_INSUFFICIENT;
-            else
-                ret->code = KADM5_AUTH_ADD;
         }
-        if (ret->code == KADM5_OK) {
-            ret->code = check_lockdown_keys(handle, arg->src);
-            if (ret->code == KADM5_PROTECT_KEYS) {
-                log_unauth("kadm5_rename_principal", prime_arg1, &client_name,
-                           &service_name, rqstp);
-                ret->code = KADM5_AUTH_DELETE;
-            }
-        }
-    } else
-        ret->code = KADM5_AUTH_INSUFFICIENT;
+    }
     if (ret->code != KADM5_OK) {
         /* okay to cast lengths to int because trunc_name limits max value */
         krb5_klog_syslog(LOG_NOTICE,
@@ -696,9 +723,8 @@ get_principal_2_svc(gprinc_arg *arg, gprinc_ret *ret, struct svc_req *rqstp)
 
     funcname = "kadm5_get_principal";
 
-    if (! cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) &&
-        (CHANGEPW_SERVICE(rqstp) ||
-         !stub_acl_check(handle, ACL_INQUIRE, arg->princ, NULL))) {
+    if (changepw_not_self(handle, rqstp, arg->princ) ||
+        !stub_auth(handle, OP_GETPRINC, arg->princ, NULL, NULL, NULL)) {
         ret->code = KADM5_AUTH_GET;
         log_unauth(funcname, prime_arg,
                    &client_name, &service_name, rqstp);
@@ -741,7 +767,7 @@ get_princs_2_svc(gprincs_arg *arg, gprincs_ret *ret, struct svc_req *rqstp)
         prime_arg = "*";
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_LIST, NULL, NULL)) {
+        !stub_auth(handle, OP_LISTPRINCS, NULL, NULL, NULL, NULL)) {
         ret->code = KADM5_AUTH_LIST;
         log_unauth("kadm5_get_principals", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -787,16 +813,15 @@ chpass_principal_2_svc(chpass_arg *arg, generic_ret *ret,
                        &service_name, rqstp);
             ret->code = KADM5_AUTH_CHANGEPW;
         }
-    } else if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
-        ret->code = chpass_principal_wrapper_3(handle, arg->princ, FALSE, 0,
-                                               NULL, arg->pass);
-    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               stub_acl_check(handle, ACL_CHANGEPW, arg->princ, NULL)) {
-        ret->code = kadm5_chpass_principal(handle, arg->princ, arg->pass);
-    } else {
+    } else if (changepw_not_self(handle, rqstp, arg->princ) ||
+               !stub_auth(handle, OP_CPW, arg->princ, NULL, NULL, NULL)) {
+        ret->code = KADM5_AUTH_CHANGEPW;
         log_unauth("kadm5_chpass_principal", prime_arg,
                    &client_name, &service_name, rqstp);
-        ret->code = KADM5_AUTH_CHANGEPW;
+    } else {
+        ret->code = check_self_keychange(handle, rqstp, arg->princ);
+        if (!ret->code)
+            ret->code = kadm5_chpass_principal(handle, arg->princ, arg->pass);
     }
 
     if (ret->code != KADM5_AUTH_CHANGEPW) {
@@ -838,19 +863,18 @@ chpass_principal3_2_svc(chpass3_arg *arg, generic_ret *ret,
                        &service_name, rqstp);
             ret->code = KADM5_AUTH_CHANGEPW;
         }
-    } else if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
-        ret->code = chpass_principal_wrapper_3(handle, arg->princ,
-                                               arg->keepold, arg->n_ks_tuple,
-                                               arg->ks_tuple, arg->pass);
-    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               stub_acl_check(handle, ACL_CHANGEPW, arg->princ, NULL)) {
-        ret->code = kadm5_chpass_principal_3(handle, arg->princ, arg->keepold,
-                                             arg->n_ks_tuple, arg->ks_tuple,
-                                             arg->pass);
-    } else {
+    } else if (changepw_not_self(handle, rqstp, arg->princ) ||
+               !stub_auth(handle, OP_CPW, arg->princ, NULL, NULL, NULL)) {
+        ret->code = KADM5_AUTH_CHANGEPW;
         log_unauth("kadm5_chpass_principal", prime_arg,
                    &client_name, &service_name, rqstp);
-        ret->code = KADM5_AUTH_CHANGEPW;
+    } else  {
+        ret->code = check_self_keychange(handle, rqstp, arg->princ);
+        if (!ret->code) {
+            ret->code = kadm5_chpass_principal_3(handle, arg->princ,
+                                                 arg->keepold, arg->n_ks_tuple,
+                                                 arg->ks_tuple, arg->pass);
+        }
     }
 
     if (ret->code != KADM5_AUTH_CHANGEPW) {
@@ -893,7 +917,7 @@ setv4key_principal_2_svc(setv4key_arg *arg, generic_ret *ret,
             ret->code = KADM5_AUTH_SETKEY;
         }
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               stub_acl_check(handle, ACL_SETKEY, arg->princ, NULL)) {
+               stub_auth(handle, OP_SETKEY, arg->princ, NULL, NULL, NULL)) {
         ret->code = kadm5_setv4key_principal(handle, arg->princ,
                                              arg->keyblock);
     } else {
@@ -943,7 +967,7 @@ setkey_principal_2_svc(setkey_arg *arg, generic_ret *ret,
             ret->code = KADM5_AUTH_SETKEY;
         }
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               stub_acl_check(handle, ACL_SETKEY, arg->princ, NULL)) {
+               stub_auth(handle, OP_SETKEY, arg->princ, NULL, NULL, NULL)) {
         ret->code = kadm5_setkey_principal(handle, arg->princ, arg->keyblocks,
                                            arg->n_keys);
     } else {
@@ -992,7 +1016,7 @@ setkey_principal3_2_svc(setkey3_arg *arg, generic_ret *ret,
             ret->code = KADM5_AUTH_SETKEY;
         }
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               stub_acl_check(handle, ACL_SETKEY, arg->princ, NULL)) {
+               stub_auth(handle, OP_SETKEY, arg->princ, NULL, NULL, NULL)) {
         ret->code = kadm5_setkey_principal_3(handle, arg->princ, arg->keepold,
                                              arg->n_ks_tuple, arg->ks_tuple,
                                              arg->keyblocks, arg->n_keys);
@@ -1042,7 +1066,7 @@ setkey_principal4_2_svc(setkey4_arg *arg, generic_ret *ret,
             ret->code = KADM5_AUTH_SETKEY;
         }
     } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               stub_acl_check(handle, ACL_SETKEY, arg->princ, NULL)) {
+               stub_auth(handle, OP_SETKEY, arg->princ, NULL, NULL, NULL)) {
         ret->code = kadm5_setkey_principal_4(handle, arg->princ, arg->keepold,
                                              arg->key_data, arg->n_key_data);
     } else {
@@ -1107,16 +1131,17 @@ chrand_principal_2_svc(chrand_arg *arg, chrand_ret *ret, struct svc_req *rqstp)
 
     funcname = "kadm5_randkey_principal";
 
-    if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
-        ret->code = randkey_principal_wrapper_3(handle, arg->princ, FALSE, 0,
-                                                NULL, &k, &nkeys);
-    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               stub_acl_check(handle, ACL_CHANGEPW, arg->princ, NULL)) {
-        ret->code = kadm5_randkey_principal(handle, arg->princ, &k, &nkeys);
-    } else {
+    if (changepw_not_self(handle, rqstp, arg->princ) ||
+        !stub_auth(handle, OP_CHRAND, arg->princ, NULL, NULL, NULL)) {
+        ret->code = KADM5_AUTH_CHANGEPW;
         log_unauth(funcname, prime_arg,
                    &client_name, &service_name, rqstp);
-        ret->code = KADM5_AUTH_CHANGEPW;
+    } else {
+        ret->code = check_self_keychange(handle, rqstp, arg->princ);
+        if (!ret->code) {
+            ret->code = kadm5_randkey_principal(handle, arg->princ,
+                                                &k, &nkeys);
+        }
     }
 
     if (ret->code == KADM5_OK) {
@@ -1163,19 +1188,19 @@ chrand_principal3_2_svc(chrand3_arg *arg, chrand_ret *ret,
 
     funcname = "kadm5_randkey_principal";
 
-    if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
-        ret->code = randkey_principal_wrapper_3(handle, arg->princ,
-                                                arg->keepold, arg->n_ks_tuple,
-                                                arg->ks_tuple, &k, &nkeys);
-    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               stub_acl_check(handle, ACL_CHANGEPW, arg->princ, NULL)) {
-        ret->code = kadm5_randkey_principal_3(handle, arg->princ, arg->keepold,
-                                              arg->n_ks_tuple, arg->ks_tuple,
-                                              &k, &nkeys);
-    } else {
+    if (changepw_not_self(handle, rqstp, arg->princ) ||
+        !stub_auth(handle, OP_CHRAND, arg->princ, NULL, NULL, NULL)) {
+        ret->code = KADM5_AUTH_CHANGEPW;
         log_unauth(funcname, prime_arg,
                    &client_name, &service_name, rqstp);
-        ret->code = KADM5_AUTH_CHANGEPW;
+    } else {
+        ret->code = check_self_keychange(handle, rqstp, arg->princ);
+        if (!ret->code) {
+            ret->code = kadm5_randkey_principal_3(handle, arg->princ,
+                                                  arg->keepold,
+                                                  arg->n_ks_tuple,
+                                                  arg->ks_tuple, &k, &nkeys);
+        }
     }
 
     if (ret->code == KADM5_OK) {
@@ -1220,7 +1245,8 @@ create_policy_2_svc(cpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
     prime_arg = arg->rec.policy;
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_ADD, NULL, NULL)) {
+        !stub_auth_pol(handle, OP_ADDPOL, arg->rec.policy,
+                       &arg->rec, arg->mask)) {
         ret->code = KADM5_AUTH_ADD;
         log_unauth("kadm5_create_policy", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -1261,7 +1287,7 @@ delete_policy_2_svc(dpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
     prime_arg = arg->name;
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_DELETE, NULL, NULL)) {
+        !stub_auth(handle, OP_DELPOL, NULL, NULL, arg->name, NULL)) {
         log_unauth("kadm5_delete_policy", prime_arg,
                    &client_name, &service_name, rqstp);
         ret->code = KADM5_AUTH_DELETE;
@@ -1301,7 +1327,8 @@ modify_policy_2_svc(mpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
     prime_arg = arg->rec.policy;
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_MODIFY, NULL, NULL)) {
+        !stub_auth_pol(handle, OP_MODPOL, arg->rec.policy,
+                       &arg->rec, arg->mask)) {
         log_unauth("kadm5_modify_policy", prime_arg,
                    &client_name, &service_name, rqstp);
         ret->code = KADM5_AUTH_MODIFY;
@@ -1332,7 +1359,9 @@ get_policy_2_svc(gpol_arg *arg, gpol_ret *ret, struct svc_req *rqstp)
     kadm5_ret_t         ret2;
     kadm5_principal_ent_rec     caller_ent;
     kadm5_server_handle_t       handle;
-    const char                  *errmsg = NULL;
+    const char                  *errmsg = NULL, *cpolicy = NULL;
+
+    memset(&caller_ent, 0, sizeof(caller_ent));
 
     ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
                            &ret->api_version, &client_name, &service_name,
@@ -1344,30 +1373,20 @@ get_policy_2_svc(gpol_arg *arg, gpol_ret *ret, struct svc_req *rqstp)
 
     prime_arg = arg->name;
 
+    /* Look up the client principal's policy value. */
+    ret2 = kadm5_get_principal(handle->lhandle, handle->current_caller,
+                               &caller_ent, KADM5_PRINCIPAL_NORMAL_MASK);
+    if (ret2 == KADM5_OK && (caller_ent.aux_attributes & KADM5_POLICY))
+        cpolicy = caller_ent.policy;
+
     ret->code = KADM5_AUTH_GET;
-    if (!CHANGEPW_SERVICE(rqstp) &&
-        stub_acl_check(handle, ACL_INQUIRE, NULL, NULL)) {
-        ret->code = KADM5_OK;
+    if ((CHANGEPW_SERVICE(rqstp) &&
+         (cpolicy == NULL || strcmp(cpolicy, arg->name) != 0)) ||
+        !stub_auth(handle, OP_GETPOL, NULL, NULL, arg->name, cpolicy)) {
+        ret->code = KADM5_AUTH_GET;
+        log_unauth(funcname, prime_arg, &client_name, &service_name, rqstp);
     } else {
-        ret->code = kadm5_get_principal(handle->lhandle,
-                                        handle->current_caller, &caller_ent,
-                                        KADM5_PRINCIPAL_NORMAL_MASK);
-        if (ret->code == KADM5_OK) {
-            if (caller_ent.aux_attributes & KADM5_POLICY &&
-                strcmp(caller_ent.policy, arg->name) == 0) {
-                ret->code = KADM5_OK;
-            } else {
-                ret->code = KADM5_AUTH_GET;
-            }
-            ret2 = kadm5_free_principal_ent(handle->lhandle,
-                                            &caller_ent);
-            ret->code = ret->code ? ret->code : ret2;
-        }
-    }
-
-    if (ret->code == KADM5_OK) {
         ret->code = kadm5_get_policy(handle, arg->name, &ret->rec);
-
         if (ret->code != 0)
             errmsg = krb5_get_error_message(handle->context, ret->code);
 
@@ -1376,13 +1395,10 @@ get_policy_2_svc(gpol_arg *arg, gpol_ret *ret, struct svc_req *rqstp)
                  &client_name, &service_name, rqstp);
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
-
-    } else {
-        log_unauth(funcname, prime_arg,
-                   &client_name, &service_name, rqstp);
     }
 
 exit_func:
+    (void)kadm5_free_principal_ent(handle->lhandle, &caller_ent);
     stub_cleanup(handle, NULL, &client_name, &service_name);
     return TRUE;
 }
@@ -1407,7 +1423,7 @@ get_pols_2_svc(gpols_arg *arg, gpols_ret *ret, struct svc_req *rqstp)
         prime_arg = "*";
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_LIST, NULL, NULL)) {
+        !stub_auth(handle, OP_LISTPOLS, NULL, NULL, NULL, NULL)) {
         ret->code = KADM5_AUTH_LIST;
         log_unauth("kadm5_get_policies", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -1475,9 +1491,8 @@ purgekeys_2_svc(purgekeys_arg *arg, generic_ret *ret, struct svc_req *rqstp)
 
     funcname = "kadm5_purgekeys";
 
-    if (!cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) &&
-        (CHANGEPW_SERVICE(rqstp) ||
-         !stub_acl_check(handle, ACL_MODIFY, arg->princ, NULL))) {
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !stub_auth(handle, OP_PURGEKEYS, arg->princ, NULL, NULL, NULL)) {
         ret->code = KADM5_AUTH_MODIFY;
         log_unauth(funcname, prime_arg, &client_name, &service_name, rqstp);
     } else {
@@ -1512,9 +1527,8 @@ get_strings_2_svc(gstrings_arg *arg, gstrings_ret *ret, struct svc_req *rqstp)
     if (ret->code)
         goto exit_func;
 
-    if (! cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) &&
-        (CHANGEPW_SERVICE(rqstp) ||
-         !stub_acl_check(handle, ACL_INQUIRE, arg->princ, NULL))) {
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !stub_auth(handle, OP_GETSTRS, arg->princ, NULL, NULL, NULL)) {
         ret->code = KADM5_AUTH_GET;
         log_unauth("kadm5_get_strings", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -1552,7 +1566,8 @@ set_string_2_svc(sstring_arg *arg, generic_ret *ret, struct svc_req *rqstp)
         goto exit_func;
 
     if (CHANGEPW_SERVICE(rqstp) ||
-        !stub_acl_check(handle, ACL_MODIFY, arg->princ, NULL)) {
+        !stub_auth(handle, OP_SETSTR, arg->princ, NULL,
+                   arg->key, arg->value)) {
         ret->code = KADM5_AUTH_MODIFY;
         log_unauth("kadm5_mod_strings", prime_arg,
                    &client_name, &service_name, rqstp);
@@ -1641,7 +1656,7 @@ get_principal_keys_2_svc(getpkeys_arg *arg, getpkeys_ret *ret,
         goto exit_func;
 
     if (!(CHANGEPW_SERVICE(rqstp)) &&
-        stub_acl_check(handle, ACL_EXTRACT, arg->princ, NULL)) {
+        stub_auth(handle, OP_EXTRACT, arg->princ, NULL, NULL, NULL)) {
         ret->code = kadm5_get_principal_keys(handle, arg->princ, arg->kvno,
                                              &ret->key_data, &ret->n_key_data);
     } else {
diff --git a/src/lib/kadm5/kadm_err.et b/src/lib/kadm5/kadm_err.et
index 71b0534..892a6fa 100644
--- a/src/lib/kadm5/kadm_err.et
+++ b/src/lib/kadm5/kadm_err.et
@@ -66,4 +66,5 @@ error_code KADM5_BAD_KEYSALTS, "Invalid key/salt tuples"
 error_code KADM5_SETKEY_BAD_KVNO, "Invalid multiple or duplicate kvnos in setkey operation"
 error_code KADM5_AUTH_EXTRACT, "Operation requires ``extract-keys'' privilege"
 error_code KADM5_PROTECT_KEYS, "Principal keys are locked down"
+error_code KADM5_AUTH_INITIAL, "Operation requires initial ticket"
 end
diff --git a/src/lib/krb5/krb/plugin.c b/src/lib/krb5/krb/plugin.c
index 17dd6bd..ea4c069 100644
--- a/src/lib/krb5/krb/plugin.c
+++ b/src/lib/krb5/krb/plugin.c
@@ -58,7 +58,8 @@ const char *interface_names[] = {
     "audit",
     "tls",
     "kdcauthdata",
-    "certauth"
+    "certauth",
+    "kadm5_auth"
 };
 
 /* Return the context's interface structure for id, or NULL if invalid. */
diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
index bbbbae9..f7c04cf 100755
--- a/src/tests/t_kadmin_acl.py
+++ b/src/tests/t_kadmin_acl.py
@@ -213,16 +213,16 @@ realm.run([kadminl, 'renprinc', 'to', 'from'])
 kadmin_as(some_rename, ['renprinc', 'from', 'to'])
 realm.run([kadminl, 'renprinc', 'to', 'from'])
 kadmin_as(all_add, ['renprinc', 'from', 'to'], expected_code=1,
-          expected_msg="Operation requires ``delete'' privilege")
+          expected_msg="Insufficient authorization for operation")
 kadmin_as(all_delete, ['renprinc', 'from', 'to'], expected_code=1,
-          expected_msg="Operation requires ``add'' privilege")
+          expected_msg="Insufficient authorization for operation")
 kadmin_as(some_rename, ['renprinc', 'from', 'notto'], expected_code=1,
-          expected_msg="Operation requires ``add'' privilege")
+          expected_msg="Insufficient authorization for operation")
 realm.run([kadminl, 'renprinc', 'from', 'notfrom'])
 kadmin_as(some_rename, ['renprinc', 'notfrom', 'to'], expected_code=1,
-          expected_msg="Operation requires ``delete'' privilege")
+          expected_msg="Insufficient authorization for operation")
 kadmin_as(restricted_rename, ['renprinc', 'notfrom', 'to'], expected_code=1,
-          expected_msg="Operation requires ``add'' privilege")
+          expected_msg="Insufficient authorization for operation")
 realm.run([kadminl, 'delprinc', 'notfrom'])
 
 realm.addprinc('selected', 'pw')


More information about the cvs-krb5 mailing list