krb5 commit: Add kadm5_auth pluggable interface

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/d92114795fee2bdfa855263797aea7eaa47e0fc0
commit d92114795fee2bdfa855263797aea7eaa47e0fc0
Author: Greg Hudson <[email protected]>
Date:   Fri Jun 30 11:51:42 2017 -0400

    Add kadm5_auth pluggable interface
    
    Add a pluggable interface for authorizing kadmin operations, and the
    consumer interface code in kadmind.
    
    ticket: 8595

 doc/plugindev/index.rst              |    1 +
 doc/plugindev/kadm5_auth.rst         |   35 ++++
 src/include/Makefile.in              |    1 +
 src/include/k5-int.h                 |    3 +-
 src/include/k5-trace.h               |    7 +
 src/include/krb5/kadm5_auth_plugin.h |  306 +++++++++++++++++++++++++++++++++
 src/kadmin/server/Makefile.in        |    8 +-
 src/kadmin/server/auth.c             |  307 ++++++++++++++++++++++++++++++++++
 src/kadmin/server/auth.h             |   78 +++++++++
 9 files changed, 741 insertions(+), 5 deletions(-)

diff --git a/doc/plugindev/index.rst b/doc/plugindev/index.rst
index 67dbc27..9c27461 100644
--- a/doc/plugindev/index.rst
+++ b/doc/plugindev/index.rst
@@ -25,6 +25,7 @@ Contents
    ccselect.rst
    pwqual.rst
    kadm5_hook.rst
+   kadm5_auth.rst
    hostrealm.rst
    localauth.rst
    locate.rst
diff --git a/doc/plugindev/kadm5_auth.rst b/doc/plugindev/kadm5_auth.rst
new file mode 100644
index 0000000..b483961
--- /dev/null
+++ b/doc/plugindev/kadm5_auth.rst
@@ -0,0 +1,35 @@
+.. _kadm5_auth_plugin:
+
+kadmin authorization interface (kadm5_auth)
+===========================================
+
+The kadm5_auth interface (new in release 1.16) allows modules to
+determine whether a client principal is authorized to perform an
+operation in the kadmin protocol, and to apply restrictions to
+principal operations.  For a detailed description of the kadm5_auth
+interface, see the header file ``<krb5/kadm5_auth_plugin.h>``.
+
+A module can create and destroy per-process state objects by
+implementing the **init** and **fini** methods.  State objects have
+the type kadm5_auth_modinfo, which is an abstract pointer type.  A
+module should typically cast this to an internal type for the state
+object.
+
+The kadm5_auth interface has one method for each kadmin operation,
+with parameters specific to the operation.  Each method can return
+either 0 to authorize access, KRB5_PLUGIN_NO_HANDLE to defer the
+decision to other modules, or another error (canonically EPERM) to
+authoritatively deny access.  Access is granted if at least one module
+grants access and no module authoritatively denies access.
+
+The **addprinc** and **modprinc** methods can also impose restrictions
+on the principal operation by returning a ``struct
+kadm5_auth_restrictions`` object.  The module should also implement
+the **free_restrictions** method if it dynamically allocates
+restrictions objects for principal operations.
+
+kadm5_auth modules can optionally inspect principal or policy objects.
+To do this, the module must also include ``<kadm5/admin.h>`` to gain
+access to the structure definitions for those objects.  As the kadmin
+interface is explicitly not as stable as other public interfaces,
+modules which do this may not retain compatibility across releases.
diff --git a/src/include/Makefile.in b/src/include/Makefile.in
index 0239338..d5a50a3 100644
--- a/src/include/Makefile.in
+++ b/src/include/Makefile.in
@@ -150,6 +150,7 @@ install-headers-unix install: krb5/krb5.h profile.h
 	$(INSTALL_DATA) $(srcdir)/krb5/plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)plugin.h
 	$(INSTALL_DATA) $(srcdir)/krb5/preauth_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)preauth_plugin.h
 	$(INSTALL_DATA) $(srcdir)/krb5/pwqual_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)pwqual_plugin.h
+	$(INSTALL_DATA) $(srcdir)/krb5/kadm5_auth_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)kadm5_auth_plugin.h
 	$(INSTALL_DATA) $(srcdir)/krb5/kadm5_hook_plugin.h $(DESTDIR)$(KRB5_INCDIR)$(S)krb5$(S)kadm5_hook_plugin.h
 	$(INSTALL_DATA) profile.h $(DESTDIR)$(KRB5_INCDIR)$(S)profile.h
 	$(INSTALL_DATA) $(srcdir)/gssapi.h $(DESTDIR)$(KRB5_INCDIR)$(S)gssapi.h
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index d834495..483c273 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -1157,7 +1157,8 @@ struct plugin_interface {
 #define PLUGIN_INTERFACE_TLS         8
 #define PLUGIN_INTERFACE_KDCAUTHDATA 9
 #define PLUGIN_INTERFACE_CERTAUTH    10
-#define PLUGIN_NUM_INTERFACES        11
+#define PLUGIN_INTERFACE_KADM5_AUTH  11
+#define PLUGIN_NUM_INTERFACES        12
 
 /* Retrieve the plugin module of type interface_id and name modname,
  * storing the result into module. */
diff --git a/src/include/k5-trace.h b/src/include/k5-trace.h
index 0a08c06..4b39ae1 100644
--- a/src/include/k5-trace.h
+++ b/src/include/k5-trace.h
@@ -253,6 +253,13 @@ void krb5int_trace(krb5_context context, const char *fmt, ...);
 #define TRACE_INIT_CREDS_SERVICE(c, service)                    \
     TRACE(c, "Setting initial creds service to {str}", service)
 
+#define TRACE_KADM5_AUTH_VTINIT_FAIL(c, ret)                            \
+    TRACE(c, "kadm5_auth module failed to init vtable: {kerr}", ret)
+#define TRACE_KADM5_AUTH_INIT_FAIL(c, name, ret)                        \
+    TRACE(c, "kadm5_auth module {str} failed to init: {kerr}", ret)
+#define TRACE_KADM5_AUTH_INIT_SKIP(c, name)                             \
+    TRACE(c, "kadm5_auth module {str} declined to initialize", name)
+
 #define TRACE_KT_GET_ENTRY(c, keytab, princ, vno, enctype, err)         \
     TRACE(c, "Retrieving {princ} from {keytab} (vno {int}, enctype {etype}) " \
           "with result: {kerr}", princ, keytab, (int) vno, enctype, err)
diff --git a/src/include/krb5/kadm5_auth_plugin.h b/src/include/krb5/kadm5_auth_plugin.h
new file mode 100644
index 0000000..d514e99
--- /dev/null
+++ b/src/include/krb5/kadm5_auth_plugin.h
@@ -0,0 +1,306 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+ * 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.
+ */
+
+/*
+ * Declarations for kadm5_auth plugin module implementors.
+ *
+ * The kadm5_auth pluggable interface currently has only one supported major
+ * version, which is 1.  Major version 1 has a current minor version number of
+ * 1.
+ *
+ * kadm5_auth plugin modules should define a function named
+ * kadm5_auth_<modulename>_initvt, matching the signature:
+ *
+ *   krb5_error_code
+ *   kadm5_auth_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_kadm5_auth_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_KADM5_AUTH_PLUGIN_H
+#define KRB5_KADM5_AUTH_PLUGIN_H
+
+#include <krb5/krb5.h>
+#include <krb5/plugin.h>
+
+/* An abstract type for kadm5_auth module data. */
+typedef struct kadm5_auth_moddata_st *kadm5_auth_moddata;
+
+/*
+ * A module can optionally include <kadm5/admin.h> to inspect principal or
+ * policy records from requests that add or modify principals or policies.
+ * Note that fields of principal and policy structures are only valid if the
+ * corresponding bit is set in the accompanying mask parameter.
+ */
+struct _kadm5_principal_ent_t;
+struct _kadm5_policy_ent_t;
+
+/*
+ * A module can optionally generate restrictions when checking permissions for
+ * adding or modifying a principal entry.  Restriction fields will only be
+ * honored if the corresponding mask bit is set.  The operable mask bits are
+ * defined in <kadmin/admin.h> and are:
+ *
+ * - KADM5_ATTRIBUTES for require_attrs, forbid_attrs
+ * - KADM5_POLICY for policy
+ * - KADM5_POLICY_CLR to require that policy be unset
+ * - KADM5_PRINC_EXPIRE_TIME for princ_lifetime
+ * - KADM5_PW_EXPIRATION for pw_lifetime
+ * - KADM5_MAX_LIFE for max_life
+ * - KADM5_MAX_RLIFE for max_renewable_life
+ */
+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;
+};
+
+/*** Method type declarations ***/
+
+/*
+ * Optional: Initialize module data.  acl_file is the realm's configured ACL
+ * file, or NULL if none was configured.  Return 0 on success,
+ * KRB5_PLUGIN_NO_HANDLE if the module is inoperable (due to configuration, for
+ * example), and any other error code to abort kadmind startup.  Optionally set
+ * *data_out to a module data object to be passed to future calls.
+ */
+typedef krb5_error_code
+(*kadm5_auth_init_fn)(krb5_context context, const char *acl_file,
+                      kadm5_auth_moddata *data_out);
+
+/* Optional: Release resources used by module data. */
+typedef void
+(*kadm5_auth_fini_fn)(krb5_context context, kadm5_auth_moddata data);
+
+/*
+ * Each check method below should return 0 to explicitly authorize the request,
+ * KRB5_PLUGIN_NO_HANDLE to neither authorize nor deny the request, and any
+ * other error code (such as EPERM) to explicitly deny the request.  If a check
+ * method is not defined, the module will neither authorize nor deny the
+ * request.  A request succeeds if at least one kadm5_auth module explicitly
+ * authorizes the request and none of the modules explicitly deny it.
+ */
+
+/* Optional: authorize an add-principal operation, and optionally generate
+ * restrictions. */
+typedef krb5_error_code
+(*kadm5_auth_addprinc_fn)(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);
+
+/* Optional: authorize a modify-principal operation, and optionally generate
+ * restrictions. */
+typedef krb5_error_code
+(*kadm5_auth_modprinc_fn)(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);
+
+/* Optional: authorize a set-string operation. */
+typedef krb5_error_code
+(*kadm5_auth_setstr_fn)(krb5_context context, kadm5_auth_moddata data,
+                        krb5_const_principal client,
+                        krb5_const_principal target,
+                        const char *key, const char *value);
+
+/* Optional: authorize a change-password operation. */
+typedef krb5_error_code
+(*kadm5_auth_cpw_fn)(krb5_context context, kadm5_auth_moddata data,
+                     krb5_const_principal client, krb5_const_principal target);
+
+/* Optional: authorize a randomize-keys operation. */
+typedef krb5_error_code
+(*kadm5_auth_chrand_fn)(krb5_context context, kadm5_auth_moddata data,
+                        krb5_const_principal client,
+                        krb5_const_principal target);
+
+/* Optional: authorize a set-key operation. */
+typedef krb5_error_code
+(*kadm5_auth_setkey_fn)(krb5_context context, kadm5_auth_moddata data,
+                        krb5_const_principal client,
+                        krb5_const_principal target);
+
+/* Optional: authorize a purgekeys operation. */
+typedef krb5_error_code
+(*kadm5_auth_purgekeys_fn)(krb5_context context, kadm5_auth_moddata data,
+                           krb5_const_principal client,
+                           krb5_const_principal target);
+
+/* Optional: authorize a delete-principal operation. */
+typedef krb5_error_code
+(*kadm5_auth_delprinc_fn)(krb5_context context, kadm5_auth_moddata data,
+                          krb5_const_principal client,
+                          krb5_const_principal target);
+
+/* Optional: authorize a rename-principal operation. */
+typedef krb5_error_code
+(*kadm5_auth_renprinc_fn)(krb5_context context, kadm5_auth_moddata data,
+                          krb5_const_principal client,
+                          krb5_const_principal src,
+                          krb5_const_principal dest);
+
+/* Optional: authorize a get-principal operation. */
+typedef krb5_error_code
+(*kadm5_auth_getprinc_fn)(krb5_context context, kadm5_auth_moddata data,
+                          krb5_const_principal client,
+                          krb5_const_principal target);
+
+/* Optional: authorize a get-strings operation. */
+typedef krb5_error_code
+(*kadm5_auth_getstrs_fn)(krb5_context context, kadm5_auth_moddata data,
+                         krb5_const_principal client,
+                         krb5_const_principal target);
+
+/* Optional: authorize an extract-keys operation. */
+typedef krb5_error_code
+(*kadm5_auth_extract_fn)(krb5_context context, kadm5_auth_moddata data,
+                         krb5_const_principal client,
+                         krb5_const_principal target);
+
+/* Optional: authorize a list-principals operation. */
+typedef krb5_error_code
+(*kadm5_auth_listprincs_fn)(krb5_context context, kadm5_auth_moddata data,
+                            krb5_const_principal client);
+
+/* Optional: authorize an add-policy operation. */
+typedef krb5_error_code
+(*kadm5_auth_addpol_fn)(krb5_context context, kadm5_auth_moddata data,
+                        krb5_const_principal client, const char *policy,
+                        const struct _kadm5_policy_ent_t *ent, long mask);
+
+/* Optional: authorize a modify-policy operation. */
+typedef krb5_error_code
+(*kadm5_auth_modpol_fn)(krb5_context context, kadm5_auth_moddata data,
+                        krb5_const_principal client, const char *policy,
+                        const struct _kadm5_policy_ent_t *ent, long mask);
+
+/* Optional: authorize a delete-policy operation. */
+typedef krb5_error_code
+(*kadm5_auth_delpol_fn)(krb5_context context, kadm5_auth_moddata data,
+                        krb5_const_principal client, const char *policy);
+
+/* Optional: authorize a get-policy operation.  client_policy is the client
+ * principal's policy name, or NULL if it does not have one. */
+typedef krb5_error_code
+(*kadm5_auth_getpol_fn)(krb5_context context, kadm5_auth_moddata data,
+                        krb5_const_principal client, const char *policy,
+                        const char *client_policy);
+
+/* Optional: authorize a list-policies operation. */
+typedef krb5_error_code
+(*kadm5_auth_listpols_fn)(krb5_context context, kadm5_auth_moddata data,
+                          krb5_const_principal client);
+
+/* Optional: authorize an iprop operation. */
+typedef krb5_error_code
+(*kadm5_auth_iprop_fn)(krb5_context context, kadm5_auth_moddata data,
+                       krb5_const_principal client);
+
+/*
+ * Optional: receive a notification that the most recent authorized operation
+ * has ended.  If a kadm5_auth module is also a KDB module, it can assume that
+ * all KDB methods invoked between a kadm5_auth authorization method invocation
+ * and a kadm5_auth end invocation are performed as part of the authorized
+ * operation.
+ *
+ * The end method may be invoked without a preceding authorization method in
+ * some cases; the module must be prepared to ignore such calls.
+ */
+typedef void
+(*kadm5_auth_end_fn)(krb5_context context, kadm5_auth_moddata data);
+
+/*
+ * Optional: free a restrictions object.  This method does not need to be
+ * defined if the module does not generate restrictions objects, or if it
+ * returns aliases to restrictions objects contained from within the module
+ * data.
+ */
+typedef void
+(*kadm5_auth_free_restrictions_fn)(krb5_context context,
+                                   kadm5_auth_moddata data,
+                                   struct kadm5_auth_restrictions *rs);
+
+/* kadm5_auth vtable for major version 1. */
+typedef struct kadm5_auth_vtable_st {
+    const char *name;           /* Mandatory: name of module. */
+    kadm5_auth_init_fn init;
+    kadm5_auth_fini_fn fini;
+
+    kadm5_auth_addprinc_fn addprinc;
+    kadm5_auth_modprinc_fn modprinc;
+    kadm5_auth_setstr_fn setstr;
+    kadm5_auth_cpw_fn cpw;
+    kadm5_auth_chrand_fn chrand;
+    kadm5_auth_setkey_fn setkey;
+    kadm5_auth_purgekeys_fn purgekeys;
+    kadm5_auth_delprinc_fn delprinc;
+    kadm5_auth_renprinc_fn renprinc;
+
+    kadm5_auth_getprinc_fn getprinc;
+    kadm5_auth_getstrs_fn getstrs;
+    kadm5_auth_extract_fn extract;
+    kadm5_auth_listprincs_fn listprincs;
+
+    kadm5_auth_addpol_fn addpol;
+    kadm5_auth_modpol_fn modpol;
+    kadm5_auth_delpol_fn delpol;
+    kadm5_auth_getpol_fn getpol;
+    kadm5_auth_listpols_fn listpols;
+
+    kadm5_auth_iprop_fn iprop;
+
+    kadm5_auth_end_fn end;
+
+    kadm5_auth_free_restrictions_fn free_restrictions;
+    /* Minor version 1 ends here. */
+} *kadm5_auth_vtable;
+
+#endif /* KRB5_KADM5_AUTH_PLUGIN_H */
diff --git a/src/kadmin/server/Makefile.in b/src/kadmin/server/Makefile.in
index 1ef5c66..2f97ab9 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_acl.o kadm_rpc_svc.o server_stubs.o ovsec_kadmd.o schpw.o misc.o \
-	ipropd_svc.o
-SRCS = 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 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
 
 all: $(PROG)
 
diff --git a/src/kadmin/server/auth.c b/src/kadmin/server/auth.c
new file mode 100644
index 0000000..ccff0eb
--- /dev/null
+++ b/src/kadmin/server/auth.c
@@ -0,0 +1,307 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/server/auth.c - kadm5_auth pluggable interface consumer */
+/*
+ * 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"
+
+typedef struct {
+    struct kadm5_auth_vtable_st vt;
+    kadm5_auth_moddata data;
+} *auth_handle;
+
+static auth_handle *handles;
+
+void
+auth_fini(krb5_context context)
+{
+    auth_handle *hp, h;
+
+    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);
+    handles = NULL;
+}
+
+krb5_error_code
+auth_init(krb5_context context, const char *acl_file)
+{
+    krb5_error_code ret;
+    krb5_plugin_initvt_fn *modules = NULL, *mod;
+    size_t count;
+    auth_handle h = NULL;
+
+    ret = k5_plugin_load_all(context, PLUGIN_INTERFACE_KADM5_AUTH, &modules);
+    if (ret)
+        goto cleanup;
+
+    /* Allocate a large enough list of handles. */
+    for (count = 0; modules[count] != NULL; count++);
+    handles = k5calloc(count + 1, sizeof(*handles), &ret);
+    if (handles == NULL)
+        goto cleanup;
+
+    /* For each module, allocate a handle, initialize its vtable, and
+     * initialize its module data. */
+    count = 0;
+    for (mod = modules; *mod != NULL; mod++) {
+        h = k5alloc(sizeof(*h), &ret);
+        if (h == NULL)
+            goto cleanup;
+        ret = (*mod)(context, 1, 1, (krb5_plugin_vtable)&h->vt);
+        if (ret) {              /* Failed vtable init is non-fatal. */
+            TRACE_KADM5_AUTH_VTINIT_FAIL(context, ret);
+            free(h);
+            h = NULL;
+            continue;
+        }
+        h->data = NULL;
+        if (h->vt.init != NULL) {
+            ret = h->vt.init(context, acl_file, &h->data);
+            if (ret == KRB5_PLUGIN_NO_HANDLE) {
+                TRACE_KADM5_AUTH_INIT_SKIP(context, h->vt.name);
+                free(h);
+                h = NULL;
+                continue;
+            }
+            if (ret) {
+                TRACE_KADM5_AUTH_INIT_FAIL(context, h->vt.name, ret);
+                goto cleanup;
+            }
+        }
+        handles[count++] = h;
+        handles[count] = NULL;
+        h = NULL;
+    }
+
+    ret = 0;
+
+cleanup:
+    if (ret)
+        auth_fini(context);
+    free(h);
+    k5_plugin_free_modules(context, modules);
+    return ret;
+}
+
+/* Invoke the appropriate method from h->vt for opcode, passing client and the
+ * correct subset of p1, p2, s1, s2, polent, and mask for the method. */
+static krb5_error_code
+call_module(krb5_context context, auth_handle h, int opcode,
+            krb5_const_principal client, krb5_const_principal p1,
+            krb5_const_principal p2, const char *s1, const char *s2,
+            const kadm5_policy_ent_rec *polent, long mask)
+{
+    /* addprinc and modprinc are handled through auth_restrict(). */
+    assert(opcode != OP_ADDPRINC && opcode != OP_MODPRINC);
+
+    if (opcode == OP_SETSTR && h->vt.setstr != NULL)
+        return h->vt.setstr(context, h->data, client, p1, s1, s2);
+    else if (opcode == OP_CPW && h->vt.cpw != NULL)
+        return h->vt.cpw(context, h->data, client, p1);
+    else if (opcode == OP_CHRAND && h->vt.chrand != NULL)
+        return h->vt.chrand(context, h->data, client, p1);
+    else if (opcode == OP_SETKEY && h->vt.setkey != NULL)
+        return h->vt.setkey(context, h->data, client, p1);
+    else if (opcode == OP_PURGEKEYS && h->vt.purgekeys != NULL)
+        return h->vt.purgekeys(context, h->data, client, p1);
+    else if (opcode == OP_DELPRINC && h->vt.delprinc != NULL)
+        return h->vt.delprinc(context, h->data, client, p1);
+    else if (opcode == OP_RENPRINC && h->vt.renprinc != NULL)
+        return h->vt.renprinc(context, h->data, client, p1, p2);
+    else if (opcode == OP_GETPRINC && h->vt.getprinc != NULL)
+        return h->vt.getprinc(context, h->data, client, p1);
+    else if (opcode == OP_GETSTRS && h->vt.getstrs != NULL)
+        return h->vt.getstrs(context, h->data, client, p1);
+    else if (opcode == OP_EXTRACT && h->vt.extract != NULL)
+        return h->vt.extract(context, h->data, client, p1);
+    else if (opcode == OP_LISTPRINCS && h->vt.listprincs != NULL)
+        return h->vt.listprincs(context, h->data, client);
+    else if (opcode == OP_ADDPOL && h->vt.addpol != NULL)
+        return h->vt.addpol(context, h->data, client, s1, polent, mask);
+    else if (opcode == OP_MODPOL && h->vt.modpol != NULL)
+        return h->vt.modpol(context, h->data, client, s1, polent, mask);
+    else if (opcode == OP_DELPOL && h->vt.delpol != NULL)
+        return h->vt.delpol(context, h->data, client, s1);
+    else if (opcode == OP_GETPOL && h->vt.getpol != NULL)
+        return h->vt.getpol(context, h->data, client, s1, s2);
+    else if (opcode == OP_LISTPOLS && h->vt.listpols != NULL)
+        return h->vt.listpols(context, h->data, client);
+    else if (opcode == OP_IPROP && h->vt.iprop != NULL)
+        return h->vt.iprop(context, h->data, client);
+
+    return KRB5_PLUGIN_NO_HANDLE;
+}
+
+krb5_boolean
+auth(krb5_context context, int opcode, krb5_const_principal client,
+     krb5_const_principal p1, krb5_const_principal p2, const char *s1,
+     const char *s2, const kadm5_policy_ent_rec *polent, long mask)
+{
+    krb5_error_code ret;
+    krb5_boolean authorized = FALSE;
+    auth_handle *hp, h;
+
+    for (hp = handles; *hp != NULL; hp++) {
+        h = *hp;
+
+        ret = call_module(context, h, opcode, client, p1, p2, s1, s2,
+                          polent, mask);
+        if (!ret)
+            authorized = TRUE;
+        else if (ret != KRB5_PLUGIN_NO_HANDLE)
+            return FALSE;
+    }
+
+    return authorized;
+}
+
+/* Impose restrictions, modifying *ent and *mask. */
+static krb5_error_code
+impose_restrictions(krb5_context context,
+                    const struct kadm5_auth_restrictions *rs,
+                    kadm5_principal_ent_t ent, long *mask)
+{
+    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) {
+        ent->attributes |= rs->require_attrs;
+        ent->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 (ent->policy != NULL && strcmp(ent->policy, rs->policy) != 0) {
+            free(ent->policy);
+            ent->policy = NULL;
+        }
+        if (ent->policy == NULL) {
+            ent->policy = strdup(rs->policy);
+            if (ent->policy == NULL)
+                return ENOMEM;
+        }
+        *mask |= KADM5_POLICY;
+    }
+    if (rs->mask & KADM5_PRINC_EXPIRE_TIME) {
+        if (!(*mask & KADM5_PRINC_EXPIRE_TIME) ||
+            ts_after(ent->princ_expire_time, ts_incr(now, rs->princ_lifetime)))
+            ent->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(ent->pw_expiration, ts_incr(now, rs->pw_lifetime)))
+            ent->pw_expiration = now + rs->pw_lifetime;
+        *mask |= KADM5_PW_EXPIRATION;
+    }
+    if (rs->mask & KADM5_MAX_LIFE) {
+        if (!(*mask & KADM5_MAX_LIFE) || ent->max_life > rs->max_life)
+            ent->max_life = rs->max_life;
+        *mask |= KADM5_MAX_LIFE;
+    }
+    if (rs->mask & KADM5_MAX_RLIFE) {
+        if (!(*mask & KADM5_MAX_RLIFE) ||
+            ent->max_renewable_life > rs->max_renewable_life)
+            ent->max_renewable_life = rs->max_renewable_life;
+        *mask |= KADM5_MAX_RLIFE;
+    }
+    return 0;
+}
+
+krb5_boolean
+auth_restrict(krb5_context context, int opcode, krb5_const_principal client,
+              kadm5_principal_ent_t ent, long *mask)
+{
+    auth_handle *hp, h;
+    krb5_boolean authorized = FALSE;
+    krb5_error_code ret, rs_ret;
+    krb5_const_principal target = ent->principal;
+    struct kadm5_auth_restrictions *rs;
+
+    assert(opcode == OP_ADDPRINC || opcode == OP_MODPRINC);
+    for (hp = handles; *hp != NULL; hp++) {
+        h = *hp;
+
+        ret = KRB5_PLUGIN_NO_HANDLE;
+        rs = NULL;
+        if (opcode == OP_ADDPRINC && h->vt.addprinc != NULL) {
+            ret = h->vt.addprinc(context, h->data, client, target, ent, *mask,
+                                 &rs);
+        } else if (opcode == OP_MODPRINC && h->vt.modprinc != NULL) {
+            ret = h->vt.modprinc(context, h->data, client, target, ent, *mask,
+                                 &rs);
+        }
+        if (rs != NULL) {
+            rs_ret = impose_restrictions(context, rs, ent, mask);
+            if (h->vt.free_restrictions != NULL)
+                h->vt.free_restrictions(context, h->data, rs);
+            if (rs_ret)
+                return FALSE;
+        }
+        if (!ret)
+            authorized = TRUE;
+        else if (ret != KRB5_PLUGIN_NO_HANDLE)
+            return FALSE;
+    }
+
+    return authorized;
+}
+
+void
+auth_end(krb5_context context)
+{
+    auth_handle *hp, h;
+
+    for (hp = handles; *hp != NULL; hp++) {
+        h = *hp;
+        if (h->vt.end != NULL)
+            h->vt.end(context, h->data);
+    }
+}
diff --git a/src/kadmin/server/auth.h b/src/kadmin/server/auth.h
new file mode 100644
index 0000000..01e78cc
--- /dev/null
+++ b/src/kadmin/server/auth.h
@@ -0,0 +1,78 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/server/auth.h - kadmin authorization declarations */
+/*
+ * 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.
+ */
+
+#ifndef AUTH_H
+#define AUTH_H
+
+#define OP_ADDPRINC     1
+#define OP_MODPRINC     2
+#define OP_SETSTR       3
+#define OP_CPW          4
+#define OP_CHRAND       5
+#define OP_SETKEY       6
+#define OP_PURGEKEYS    7
+#define OP_DELPRINC     8
+#define OP_RENPRINC     9
+#define OP_GETPRINC    10
+#define OP_GETSTRS     11
+#define OP_EXTRACT     12
+#define OP_LISTPRINCS  13
+#define OP_ADDPOL      14
+#define OP_MODPOL      15
+#define OP_DELPOL      16
+#define OP_GETPOL      17
+#define OP_LISTPOLS    18
+#define OP_IPROP       19
+
+/* Initialize all authorization modules. */
+krb5_error_code auth_init(krb5_context context, const char *acl_file);
+
+/* Release authorization module state. */
+void auth_fini(krb5_context context);
+
+/* Authorize the operation given by opcode, using the appropriate subset of p1,
+ * p2, s1, s2, polent, and mask. */
+krb5_boolean auth(krb5_context context, int opcode,
+                  krb5_const_principal client, krb5_const_principal p1,
+                  krb5_const_principal p2, const char *s1, const char *s2,
+                  const kadm5_policy_ent_rec *polent, long mask);
+
+/* Authorize an add-principal or modify-principal operation, and apply
+ * restrictions to ent and mask if any modules supply them. */
+krb5_boolean auth_restrict(krb5_context context, int opcode,
+                           krb5_const_principal client,
+                           kadm5_principal_ent_t ent, long *mask);
+
+/* Notify modules that the most recent authorized operation has ended. */
+void auth_end(krb5_context context);
+
+#endif /* AUTH_H */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.