Imported Upstream version 1.20.1
[platform/upstream/krb5.git] / src / kadmin / server / server_stubs.c
index 0de627f..ef7e809 100644 (file)
@@ -4,18 +4,18 @@
  *
  */
 
-#include <k5-platform.h>
+#include <k5-int.h>
+#include <socket-utils.h>
 #include <gssapi/gssapi.h>
 #include <gssapi/gssapi_krb5.h> /* for gss_nt_krb5_name */
 #include <krb5.h>
 #include <kadm5/admin.h>
 #include <kadm5/kadm_rpc.h>
 #include <kadm5/server_internal.h>
-#include <kadm5/server_acl.h>
 #include <syslog.h>
-#include <arpa/inet.h>  /* inet_ntoa */
 #include <adm_proto.h>  /* krb5_klog_syslog */
 #include "misc.h"
+#include "auth.h"
 
 extern gss_name_t                       gss_changepw_name;
 extern gss_name_t                       gss_oldchangepw_name;
@@ -39,10 +39,10 @@ gss_name_t rqst2name(struct svc_req *rqstp);
 
 static int cmp_gss_names(gss_name_t n1, gss_name_t n2)
 {
-    OM_uint32 emaj, emin;
+    OM_uint32 emin;
     int equal;
 
-    if (GSS_ERROR(emaj = gss_compare_name(&emin, n1, n2, &equal)))
+    if (GSS_ERROR(gss_compare_name(&emin, n1, n2, &equal)))
         return(0);
 
     return(equal);
@@ -143,6 +143,24 @@ static void free_server_handle(kadm5_server_handle_t handle)
     free(handle);
 }
 
+/* Result is stored in a static buffer and is invalidated by the next call. */
+const char *
+client_addr(SVCXPRT *xprt)
+{
+    static char abuf[128];
+    struct sockaddr_storage ss;
+    socklen_t len = sizeof(ss);
+    const char *p = NULL;
+
+    if (getpeername(xprt->xp_sock, ss2sa(&ss), &len) != 0)
+        return "(unknown)";
+    if (ss2sa(&ss)->sa_family == AF_INET)
+        p = inet_ntop(AF_INET, &ss2sin(&ss)->sin_addr, abuf, sizeof(abuf));
+    else if (ss2sa(&ss)->sa_family == AF_INET6)
+        p = inet_ntop(AF_INET6, &ss2sin6(&ss)->sin6_addr, abuf, sizeof(abuf));
+    return (p == NULL) ? "(unknown)" : p;
+}
+
 /*
  * Function: setup_gss_names
  *
@@ -198,31 +216,24 @@ 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)
 {
-    OM_uint32 status, minor_stat;
+    krb5_error_code ret;
+    OM_uint32 minor_stat;
     gss_buffer_desc gss_str;
-    gss_OID gss_type;
     int success;
+    char *s;
 
-    status = gss_display_name(&minor_stat, gss_name, &gss_str, &gss_type);
-    if ((status != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name))
+    if (gss_name_to_string(gss_name, &gss_str) != 0)
+        return 0;
+    s = k5memdup0(gss_str.value, gss_str.length, &ret);
+    if (s == NULL) {
+        gss_release_buffer(&minor_stat, &gss_str);
         return 0;
-    success = (krb5_parse_name(handle->context, gss_str.value, princ) == 0);
+    }
+    success = (krb5_parse_name(handle->context, s, princ) == 0);
+    free(s);
     gss_release_buffer(&minor_stat, &gss_str);
     return success;
 }
@@ -232,13 +243,136 @@ gss_name_to_string(gss_name_t gss_name, gss_buffer_desc *str)
 {
     OM_uint32 status, minor_stat;
     gss_OID gss_type;
+    const char pref[] = KRB5_WELLKNOWN_NAMESTR "/" KRB5_ANONYMOUS_PRINCSTR "@";
+    const size_t preflen = sizeof(pref) - 1;
 
     status = gss_display_name(&minor_stat, gss_name, str, &gss_type);
-    if ((status != GSS_S_COMPLETE) || (gss_type != gss_nt_krb5_name))
+    if (status != GSS_S_COMPLETE)
         return 1;
+    if (gss_oid_equal(gss_type, GSS_C_NT_ANONYMOUS)) {
+        /* Guard against non-krb5 mechs with different anonymous displays. */
+        if (str->length < preflen || memcmp(str->value, pref, preflen) != 0)
+            return 1;
+    } else if (!gss_oid_equal(gss_type, GSS_KRB5_NT_PRINCIPAL_NAME)) {
+        return 1;
+    }
     return 0;
 }
 
+/*
+ * Perform common initialization for server stub functions.  A subset of the
+ * output arguments may be set on failure; the caller is responsible for
+ * initializing outputs and calling stub_cleanup() on success or failure.
+ * princ and princ_str_out may be NULL to omit unparsing a principal name.
+ */
+static kadm5_ret_t
+stub_setup(krb5_ui_4 api_version, struct svc_req *rqstp, krb5_principal princ,
+           kadm5_server_handle_t *handle_out, krb5_ui_4 *api_version_out,
+           gss_buffer_t client_name_out, gss_buffer_t service_name_out,
+           char **princ_str_out)
+{
+    kadm5_ret_t ret;
+
+    ret = new_server_handle(api_version, rqstp, handle_out);
+    if (ret)
+        return ret;
+
+    ret = check_handle(*handle_out);
+    if (ret)
+        return ret;
+
+    *api_version_out = (*handle_out)->api_version;
+
+    if (setup_gss_names(rqstp, client_name_out, service_name_out) < 0)
+        return KADM5_FAILURE;
+
+    if (princ_str_out != NULL) {
+        if (princ == NULL)
+            return KADM5_BAD_PRINCIPAL;
+        if (krb5_unparse_name((*handle_out)->context, princ, princ_str_out))
+            return KADM5_BAD_PRINCIPAL;
+    }
+
+    return KADM5_OK;
+}
+
+/* Perform common cleanup for server stub functions. */
+static void
+stub_cleanup(kadm5_server_handle_t handle, char *princ_str,
+             gss_buffer_t client_name, gss_buffer_t service_name)
+{
+    OM_uint32 minor_stat;
+
+    auth_end(handle->context);
+    free_server_handle(handle);
+    free(princ_str);
+    gss_release_buffer(&minor_stat, client_name);
+    gss_release_buffer(&minor_stat, service_name);
+}
+
+static krb5_boolean
+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)
+{
+    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
 log_unauth(
     char *op,
@@ -264,7 +398,7 @@ log_unauth(
                             op, (int)tlen, target, tdots,
                             (int)clen, (char *)client->value, cdots,
                             (int)slen, (char *)server->value, sdots,
-                            inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+                            client_addr(rqstp->rq_xprt));
 }
 
 static int
@@ -295,54 +429,36 @@ log_done(
                             op, (int)tlen, target, tdots, errmsg,
                             (int)clen, (char *)client->value, cdots,
                             (int)slen, (char *)server->value, sdots,
-                            inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+                            client_addr(rqstp->rq_xprt));
 }
 
-generic_ret *
-create_principal_2_svc(cprinc_arg *arg, struct svc_req *rqstp)
+bool_t
+create_principal_2_svc(cprinc_arg *arg, generic_ret *ret,
+                       struct svc_req *rqstp)
 {
-    static generic_ret          ret;
-    char                        *prime_arg;
-    gss_buffer_desc             client_name, service_name;
-    OM_uint32                   minor_stat;
+    char                        *prime_arg = NULL;
+    gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t       handle;
-    restriction_t               *rp;
     const char                  *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
+                           &handle, &ret->api_version, &client_name,
+                           &service_name, &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (CHANGEPW_SERVICE(rqstp)
-        || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_ADD,
-                               arg->rec.principal, &rp)
-        || kadm5int_acl_impose_restrictions(handle->context,
-                                            &arg->rec, &arg->mask, rp)) {
-        ret.code = KADM5_AUTH_ADD;
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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);
     } else {
-        ret.code = kadm5_create_principal((void *)handle,
-                                          &arg->rec, arg->mask,
-                                          arg->passwd);
+        ret->code = kadm5_create_principal(handle, &arg->rec, arg->mask,
+                                           arg->passwd);
 
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_create_principal", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -350,61 +466,39 @@ create_principal_2_svc(cprinc_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-create_principal3_2_svc(cprinc3_arg *arg, struct svc_req *rqstp)
+bool_t
+create_principal3_2_svc(cprinc3_arg *arg, generic_ret *ret,
+                        struct svc_req *rqstp)
 {
-    static generic_ret          ret;
-    char                        *prime_arg;
-    gss_buffer_desc             client_name, service_name;
-    OM_uint32                   minor_stat;
+    char                        *prime_arg = NULL;
+    gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t       handle;
-    restriction_t               *rp;
     const char                  *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
+    ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
+                           &handle, &ret->api_version, &client_name,
+                           &service_name, &prime_arg);
+    if (ret->code)
         goto exit_func;
-    }
 
-    if (CHANGEPW_SERVICE(rqstp)
-        || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_ADD,
-                               arg->rec.principal, &rp)
-        || kadm5int_acl_impose_restrictions(handle->context,
-                                            &arg->rec, &arg->mask, rp)) {
-        ret.code = KADM5_AUTH_ADD;
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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);
     } else {
-        ret.code = kadm5_create_principal_3((void *)handle,
-                                            &arg->rec, arg->mask,
-                                            arg->n_ks_tuple,
-                                            arg->ks_tuple,
-                                            arg->passwd);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_create_principal_3(handle, &arg->rec, arg->mask,
+                                             arg->n_ks_tuple, arg->ks_tuple,
+                                             arg->passwd);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_create_principal", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -412,55 +506,62 @@ create_principal3_2_svc(cprinc3_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-delete_principal_2_svc(dprinc_arg *arg, struct svc_req *rqstp)
+/* Return KADM5_PROTECT_KEYS if KRB5_KDB_LOCKDOWN_KEYS is set for princ. */
+static kadm5_ret_t
+check_lockdown_keys(kadm5_server_handle_t handle, krb5_principal princ)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    kadm5_principal_ent_rec rec;
+    kadm5_ret_t ret;
+
+    ret = kadm5_get_principal(handle, princ, &rec, KADM5_ATTRIBUTES);
+    if (ret)
+        return ret;
+    ret = (rec.attributes & KRB5_KDB_LOCKDOWN_KEYS) ? KADM5_PROTECT_KEYS : 0;
+    kadm5_free_principal_ent(handle, &rec);
+    return ret;
+}
+
+bool_t
+delete_principal_2_svc(dprinc_arg *arg, generic_ret *ret,
+                       struct svc_req *rqstp)
+{
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (CHANGEPW_SERVICE(rqstp)
-        || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_DELETE,
-                               arg->princ, NULL)) {
-        ret.code = KADM5_AUTH_DELETE;
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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);
     } else {
-        ret.code = kadm5_delete_principal((void *)handle, arg->princ);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = check_lockdown_keys(handle, arg->princ);
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_delete_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret->code = KADM5_AUTH_DELETE;
+        }
+    }
+
+    if (ret->code == KADM5_OK)
+        ret->code = kadm5_delete_principal(handle, arg->princ);
+    if (ret->code != KADM5_AUTH_DELETE) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_delete_principal", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -469,57 +570,47 @@ delete_principal_2_svc(dprinc_arg *arg, struct svc_req *rqstp)
             krb5_free_error_message(handle->context, errmsg);
 
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-modify_principal_2_svc(mprinc_arg *arg, struct svc_req *rqstp)
+bool_t
+modify_principal_2_svc(mprinc_arg *arg, generic_ret *ret,
+                       struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
-    restriction_t                   *rp;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->rec.principal, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
+    ret->code = stub_setup(arg->api_version, rqstp, arg->rec.principal,
+                           &handle, &ret->api_version, &client_name,
+                           &service_name, &prime_arg);
+    if (ret->code)
         goto exit_func;
-    }
 
-    if (CHANGEPW_SERVICE(rqstp)
-        || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY,
-                               arg->rec.principal, &rp)
-        || kadm5int_acl_impose_restrictions(handle->context,
-                                            &arg->rec, &arg->mask, rp)) {
-        ret.code = KADM5_AUTH_MODIFY;
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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);
-    } else {
-        ret.code = kadm5_modify_principal((void *)handle, &arg->rec,
-                                          arg->mask);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+    } else if ((arg->mask & KADM5_ATTRIBUTES) &&
+               (!(arg->rec.attributes & KRB5_KDB_LOCKDOWN_KEYS))) {
+        ret->code = check_lockdown_keys(handle, arg->rec.principal);
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_modify_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret->code = KADM5_AUTH_MODIFY;
+        }
+    }
+
+    if (ret->code == KADM5_OK) {
+        ret->code = kadm5_modify_principal(handle, &arg->rec, arg->mask);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_modify_principal", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -527,44 +618,33 @@ modify_principal_2_svc(mprinc_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-rename_principal_2_svc(rprinc_arg *arg, struct svc_req *rqstp)
+bool_t
+rename_principal_2_svc(rprinc_arg *arg, generic_ret *ret,
+                       struct svc_req *rqstp)
 {
-    static generic_ret          ret;
-    char                        *prime_arg1,
-        *prime_arg2;
-    gss_buffer_desc             client_name,
-        service_name;
-    OM_uint32                   minor_stat;
+    char                        *prime_arg1 = NULL, *prime_arg2 = NULL;
+    gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t       handle;
-    restriction_t               *rp;
     const char                  *errmsg = NULL;
     size_t                      tlen1, tlen2, clen, slen;
     char                        *tdots1, *tdots2, *cdots, *sdots;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           NULL);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
     if (krb5_unparse_name(handle->context, arg->src, &prime_arg1) ||
         krb5_unparse_name(handle->context, arg->dest, &prime_arg2)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
+        ret->code = KADM5_BAD_PRINCIPAL;
         goto exit_func;
     }
     tlen1 = strlen(prime_arg1);
@@ -576,22 +656,20 @@ rename_principal_2_svc(rprinc_arg *arg, struct svc_req *rqstp)
     slen = service_name.length;
     trunc_name(&slen, &sdots);
 
-    ret.code = KADM5_OK;
-    if (! CHANGEPW_SERVICE(rqstp)) {
-        if (!kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                                ACL_DELETE, arg->src, NULL))
-            ret.code = KADM5_AUTH_DELETE;
-        /* any restrictions at all on the ADD kills the RENAME */
-        if (!kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                                ACL_ADD, arg->dest, &rp) || rp) {
-            if (ret.code == KADM5_AUTH_DELETE)
-                ret.code = KADM5_AUTH_INSUFFICIENT;
-            else
-                ret.code = KADM5_AUTH_ADD;
+    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;
         }
-    } else
-        ret.code = KADM5_AUTH_INSUFFICIENT;
-    if (ret.code != KADM5_OK) {
+    }
+    if (ret->code != KADM5_OK) {
         /* okay to cast lengths to int because trunc_name limits max value */
         krb5_klog_syslog(LOG_NOTICE,
                          _("Unauthorized request: kadm5_rename_principal, "
@@ -601,12 +679,11 @@ rename_principal_2_svc(rprinc_arg *arg, struct svc_req *rqstp)
                          (int)tlen2, prime_arg2, tdots2,
                          (int)clen, (char *)client_name.value, cdots,
                          (int)slen, (char *)service_name.value, sdots,
-                         inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+                         client_addr(rqstp->rq_xprt));
     } else {
-        ret.code = kadm5_rename_principal((void *)handle, arg->src,
-                                          arg->dest);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_rename_principal(handle, arg->src, arg->dest);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         /* okay to cast lengths to int because trunc_name limits max value */
         krb5_klog_syslog(LOG_NOTICE,
@@ -618,68 +695,47 @@ rename_principal_2_svc(rprinc_arg *arg, struct svc_req *rqstp)
                          errmsg ? errmsg : _("success"),
                          (int)clen, (char *)client_name.value, cdots,
                          (int)slen, (char *)service_name.value, sdots,
-                         inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr));
+                         client_addr(rqstp->rq_xprt));
 
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
 
     }
+exit_func:
     free(prime_arg1);
     free(prime_arg2);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
-exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
-gprinc_ret *
-get_principal_2_svc(gprinc_arg *arg, struct svc_req *rqstp)
+bool_t
+get_principal_2_svc(gprinc_arg *arg, gprinc_ret *ret, struct svc_req *rqstp)
 {
-    static gprinc_ret               ret;
-    char                            *prime_arg, *funcname;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *funcname, *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_gprinc_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    ret.api_version = handle->api_version;
-
     funcname = "kadm5_get_principal";
 
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (! cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) &&
-        (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
-                                                        rqst2name(rqstp),
-                                                        ACL_INQUIRE,
-                                                        arg->princ,
-                                                        NULL))) {
-        ret.code = KADM5_AUTH_GET;
+    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);
     } else {
-        ret.code = kadm5_get_principal(handle, arg->princ, &ret.rec,
-                                       arg->mask);
+        ret->code = kadm5_get_principal(handle, arg->princ, &ret->rec,
+                                        arg->mask);
 
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done(funcname, prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -687,57 +743,41 @@ get_principal_2_svc(gprinc_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-gprincs_ret *
-get_princs_2_svc(gprincs_arg *arg, struct svc_req *rqstp)
+bool_t
+get_princs_2_svc(gprincs_arg *arg, gprincs_ret *ret, struct svc_req *rqstp)
 {
-    static gprincs_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_gprincs_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           NULL);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
     prime_arg = arg->exp;
     if (prime_arg == NULL)
         prime_arg = "*";
 
-    if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
-                                                       rqst2name(rqstp),
-                                                       ACL_LIST,
-                                                       NULL,
-                                                       NULL)) {
-        ret.code = KADM5_AUTH_LIST;
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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);
     } else {
-        ret.code  = kadm5_get_principals((void *)handle,
-                                         arg->exp, &ret.princs,
-                                         &ret.count);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_get_principals(handle, arg->exp, &ret->princs,
+                                         &ret->count);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_get_principals", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -746,60 +786,49 @@ get_princs_2_svc(gprincs_arg *arg, struct svc_req *rqstp)
             krb5_free_error_message(handle->context, errmsg);
 
     }
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-chpass_principal_2_svc(chpass_arg *arg, struct svc_req *rqstp)
+bool_t
+chpass_principal_2_svc(chpass_arg *arg, generic_ret *ret,
+                       struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
-        ret.code = chpass_principal_wrapper_3((void *)handle, arg->princ,
-                                              FALSE, 0, NULL, arg->pass);
-    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                                  ACL_CHANGEPW, arg->princ, NULL)) {
-        ret.code = kadm5_chpass_principal((void *)handle, arg->princ,
-                                          arg->pass);
-    } else {
+    ret->code = check_lockdown_keys(handle, arg->princ);
+    if (ret->code != KADM5_OK) {
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_chpass_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret->code = KADM5_AUTH_CHANGEPW;
+        }
+    } 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) {
-        if (ret.code != 0)
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+    if (ret->code != KADM5_AUTH_CHANGEPW) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_chpass_principal", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -808,67 +837,51 @@ chpass_principal_2_svc(chpass_arg *arg, struct svc_req *rqstp)
             krb5_free_error_message(handle->context, errmsg);
     }
 
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-chpass_principal3_2_svc(chpass3_arg *arg, struct svc_req *rqstp)
+bool_t
+chpass_principal3_2_svc(chpass3_arg *arg, generic_ret *ret,
+                        struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
-        ret.code = chpass_principal_wrapper_3((void *)handle, arg->princ,
-                                              arg->keepold,
-                                              arg->n_ks_tuple,
-                                              arg->ks_tuple,
-                                              arg->pass);
-    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                                  ACL_CHANGEPW, arg->princ, NULL)) {
-        ret.code = kadm5_chpass_principal_3((void *)handle, arg->princ,
-                                            arg->keepold,
-                                            arg->n_ks_tuple,
-                                            arg->ks_tuple,
-                                            arg->pass);
-    } else {
+    ret->code = check_lockdown_keys(handle, arg->princ);
+    if (ret->code != KADM5_OK) {
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_chpass_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret->code = KADM5_AUTH_CHANGEPW;
+        }
+    } 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) {
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+    if (ret->code != KADM5_AUTH_CHANGEPW) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_chpass_principal", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -877,118 +890,97 @@ chpass_principal3_2_svc(chpass3_arg *arg, struct svc_req *rqstp)
             krb5_free_error_message(handle->context, errmsg);
     }
 
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-setv4key_principal_2_svc(setv4key_arg *arg, struct svc_req *rqstp)
+bool_t
+setkey_principal_2_svc(setkey_arg *arg, generic_ret *ret,
+                       struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
-    }
 
-    if (!(CHANGEPW_SERVICE(rqstp)) &&
-        kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                           ACL_SETKEY, arg->princ, NULL)) {
-        ret.code = kadm5_setv4key_principal((void *)handle, arg->princ,
-                                            arg->keyblock);
+    ret->code = check_lockdown_keys(handle, arg->princ);
+    if (ret->code != KADM5_OK) {
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret->code = KADM5_AUTH_SETKEY;
+        }
+    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+               stub_auth(handle, OP_SETKEY, arg->princ, NULL, NULL, NULL)) {
+        ret->code = kadm5_setkey_principal(handle, arg->princ, arg->keyblocks,
+                                           arg->n_keys);
     } else {
-        log_unauth("kadm5_setv4key_principal", prime_arg,
+        log_unauth("kadm5_setkey_principal", prime_arg,
                    &client_name, &service_name, rqstp);
-        ret.code = KADM5_AUTH_SETKEY;
+        ret->code = KADM5_AUTH_SETKEY;
     }
 
-    if(ret.code != KADM5_AUTH_SETKEY) {
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+    if (ret->code != KADM5_AUTH_SETKEY) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
-        log_done("kadm5_setv4key_principal", prime_arg, errmsg,
+        log_done("kadm5_setkey_principal", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
 
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
 
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-setkey_principal_2_svc(setkey_arg *arg, struct svc_req *rqstp)
+bool_t
+setkey_principal3_2_svc(setkey3_arg *arg, generic_ret *ret,
+                        struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (!(CHANGEPW_SERVICE(rqstp)) &&
-        kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                           ACL_SETKEY, arg->princ, NULL)) {
-        ret.code = kadm5_setkey_principal((void *)handle, arg->princ,
-                                          arg->keyblocks, arg->n_keys);
+    ret->code = check_lockdown_keys(handle, arg->princ);
+    if (ret->code != KADM5_OK) {
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret->code = KADM5_AUTH_SETKEY;
+        }
+    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+               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);
     } else {
         log_unauth("kadm5_setkey_principal", prime_arg,
                    &client_name, &service_name, rqstp);
-        ret.code = KADM5_AUTH_SETKEY;
+        ret->code = KADM5_AUTH_SETKEY;
     }
 
-    if(ret.code != KADM5_AUTH_SETKEY) {
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+    if (ret->code != KADM5_AUTH_SETKEY) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_setkey_principal", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -997,134 +989,124 @@ setkey_principal_2_svc(setkey_arg *arg, struct svc_req *rqstp)
             krb5_free_error_message(handle->context, errmsg);
     }
 
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-setkey_principal3_2_svc(setkey3_arg *arg, struct svc_req *rqstp)
+bool_t
+setkey_principal4_2_svc(setkey4_arg *arg, generic_ret *ret,
+                        struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
 
-    if (!(CHANGEPW_SERVICE(rqstp)) &&
-        kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                           ACL_SETKEY, arg->princ, NULL)) {
-        ret.code = kadm5_setkey_principal_3((void *)handle, arg->princ,
-                                            arg->keepold,
-                                            arg->n_ks_tuple,
-                                            arg->ks_tuple,
-                                            arg->keyblocks, arg->n_keys);
+    ret->code = check_lockdown_keys(handle, arg->princ);
+    if (ret->code != KADM5_OK) {
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+                       &service_name, rqstp);
+            ret->code = KADM5_AUTH_SETKEY;
+        }
+    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
+               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 {
-        log_unauth("kadm5_setkey_principal", prime_arg,
-                   &client_name, &service_name, rqstp);
-        ret.code = KADM5_AUTH_SETKEY;
+        log_unauth("kadm5_setkey_principal", prime_arg, &client_name,
+                   &service_name, rqstp);
+        ret->code = KADM5_AUTH_SETKEY;
     }
 
-    if(ret.code != KADM5_AUTH_SETKEY) {
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+    if (ret->code != KADM5_AUTH_SETKEY) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
-        log_done("kadm5_setkey_principal", prime_arg, errmsg,
-                 &client_name, &service_name, rqstp);
+        log_done("kadm5_setkey_principal", prime_arg, errmsg, &client_name,
+                 &service_name, rqstp);
 
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
 
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-chrand_ret *
-chrand_principal_2_svc(chrand_arg *arg, struct svc_req *rqstp)
+/* Empty out *keys / *nkeys if princ is protected with the lockdown
+ * attribute, or if we fail to check. */
+static kadm5_ret_t
+chrand_check_lockdown(kadm5_server_handle_t handle, krb5_principal princ,
+                      krb5_keyblock **keys, int *nkeys)
 {
-    static chrand_ret           ret;
+    kadm5_ret_t ret;
+    int i;
+
+    ret = check_lockdown_keys(handle, princ);
+    if (!ret)
+        return 0;
+
+    for (i = 0; i < *nkeys; i++)
+        krb5_free_keyblock_contents(handle->context, &((*keys)[i]));
+    free(*keys);
+    *keys = NULL;
+    *nkeys = 0;
+    return (ret == KADM5_PROTECT_KEYS) ? KADM5_OK : ret;
+}
+
+bool_t
+chrand_principal_2_svc(chrand_arg *arg, chrand_ret *ret, struct svc_req *rqstp)
+{
+    char                        *funcname, *prime_arg = NULL;
+    gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     krb5_keyblock               *k;
     int                         nkeys;
-    char                        *prime_arg, *funcname;
-    gss_buffer_desc             client_name,
-        service_name;
-    OM_uint32                   minor_stat;
     kadm5_server_handle_t       handle;
     const char                  *errmsg = NULL;
 
-    xdr_free(xdr_chrand_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
     funcname = "kadm5_randkey_principal";
 
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
-        ret.code = randkey_principal_wrapper_3((void *)handle, arg->princ,
-                                               FALSE, 0, NULL, &k, &nkeys);
-    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                                  ACL_CHANGEPW, arg->princ, NULL)) {
-        ret.code = kadm5_randkey_principal((void *)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) {
-        ret.keys = k;
-        ret.n_keys = nkeys;
+    if (ret->code == KADM5_OK) {
+        ret->code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys);
+        if (ret->code == KADM5_PROTECT_KEYS)
+            ret->code = KADM5_OK;
+        ret->keys = k;
+        ret->n_keys = nkeys;
     }
 
-    if(ret.code != KADM5_AUTH_CHANGEPW) {
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+    if (ret->code != KADM5_AUTH_CHANGEPW) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done(funcname, prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -1132,76 +1114,58 @@ chrand_principal_2_svc(chrand_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-chrand_ret *
-chrand_principal3_2_svc(chrand3_arg *arg, struct svc_req *rqstp)
+bool_t
+chrand_principal3_2_svc(chrand3_arg *arg, chrand_ret *ret,
+                        struct svc_req *rqstp)
 {
-    static chrand_ret           ret;
+    char                        *funcname, *prime_arg = NULL;
+    gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     krb5_keyblock               *k;
     int                         nkeys;
-    char                        *prime_arg, *funcname;
-    gss_buffer_desc             client_name,
-        service_name;
-    OM_uint32                   minor_stat;
     kadm5_server_handle_t       handle;
     const char                  *errmsg = NULL;
 
-    xdr_free(xdr_chrand_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
     funcname = "kadm5_randkey_principal";
 
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ)) {
-        ret.code = randkey_principal_wrapper_3((void *)handle, arg->princ,
-                                               arg->keepold,
-                                               arg->n_ks_tuple,
-                                               arg->ks_tuple,
-                                               &k, &nkeys);
-    } else if (!(CHANGEPW_SERVICE(rqstp)) &&
-               kadm5int_acl_check(handle->context, rqst2name(rqstp),
-                                  ACL_CHANGEPW, arg->princ, NULL)) {
-        ret.code = kadm5_randkey_principal_3((void *)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) {
-        ret.keys = k;
-        ret.n_keys = nkeys;
+    if (ret->code == KADM5_OK) {
+        ret->code = chrand_check_lockdown(handle, arg->princ, &k, &nkeys);
+        if (ret->code == KADM5_PROTECT_KEYS)
+            ret->code = KADM5_OK;
+        ret->keys = k;
+        ret->n_keys = nkeys;
     }
 
-    if(ret.code != KADM5_AUTH_CHANGEPW) {
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+    if (ret->code != KADM5_AUTH_CHANGEPW) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done(funcname, prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -1209,53 +1173,40 @@ chrand_principal3_2_svc(chrand3_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-create_policy_2_svc(cpol_arg *arg, struct svc_req *rqstp)
+bool_t
+create_policy_2_svc(cpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           NULL);
+    if (ret->code)
         goto exit_func;
 
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
     prime_arg = arg->rec.policy;
 
-    if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
-                                                       rqst2name(rqstp),
-                                                       ACL_ADD, NULL, NULL)) {
-        ret.code = KADM5_AUTH_ADD;
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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);
 
     } else {
-        ret.code = kadm5_create_policy((void *)handle, &arg->rec,
-                                       arg->mask);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_create_policy(handle, &arg->rec, arg->mask);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_create_policy",
                  ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg,
@@ -1264,50 +1215,38 @@ create_policy_2_svc(cpol_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-delete_policy_2_svc(dpol_arg *arg, struct svc_req *rqstp)
+bool_t
+delete_policy_2_svc(dpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           NULL);
+    if (ret->code)
         goto exit_func;
 
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
     prime_arg = arg->name;
 
-    if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
-                                                       rqst2name(rqstp),
-                                                       ACL_DELETE, NULL, NULL)) {
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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;
+        ret->code = KADM5_AUTH_DELETE;
     } else {
-        ret.code = kadm5_delete_policy((void *)handle, arg->name);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_delete_policy(handle, arg->name);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_delete_policy",
                  ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg,
@@ -1316,51 +1255,39 @@ delete_policy_2_svc(dpol_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-modify_policy_2_svc(mpol_arg *arg, struct svc_req *rqstp)
+bool_t
+modify_policy_2_svc(mpol_arg *arg, generic_ret *ret, struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           NULL);
+    if (ret->code)
         goto exit_func;
 
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
     prime_arg = arg->rec.policy;
 
-    if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
-                                                       rqst2name(rqstp),
-                                                       ACL_MODIFY, NULL, NULL)) {
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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;
+        ret->code = KADM5_AUTH_MODIFY;
     } else {
-        ret.code = kadm5_modify_policy((void *)handle, &arg->rec,
-                                       arg->mask);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_modify_policy(handle, &arg->rec, arg->mask);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_modify_policy",
                  ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg,
@@ -1369,130 +1296,94 @@ modify_policy_2_svc(mpol_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
-gpol_ret *
-get_policy_2_svc(gpol_arg *arg, struct svc_req *rqstp)
+bool_t
+get_policy_2_svc(gpol_arg *arg, gpol_ret *ret, struct svc_req *rqstp)
 {
-    static gpol_ret             ret;
+    char                        *funcname, *prime_arg = NULL;
+    gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     kadm5_ret_t         ret2;
-    char                        *prime_arg, *funcname;
-    gss_buffer_desc             client_name,
-        service_name;
-    OM_uint32                   minor_stat;
     kadm5_principal_ent_rec     caller_ent;
     kadm5_server_handle_t       handle;
-    const char                  *errmsg = NULL;
+    const char                  *errmsg = NULL, *cpolicy = NULL;
 
-    xdr_free(xdr_gpol_ret,  &ret);
+    memset(&caller_ent, 0, sizeof(caller_ent));
 
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           NULL);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
     funcname = "kadm5_get_policy";
 
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
     prime_arg = arg->name;
 
-    ret.code = KADM5_AUTH_GET;
-    if (!CHANGEPW_SERVICE(rqstp) && kadm5int_acl_check(handle->context,
-                                                       rqst2name(rqstp),
-                                                       ACL_INQUIRE, NULL, NULL))
-        ret.code = KADM5_OK;
-    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);
+    /* 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) &&
+         (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_policy(handle, arg->name, &ret->rec);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done(funcname,
                  ((prime_arg == NULL) ? "(null)" : prime_arg), errmsg,
                  &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);
     }
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
-exit_func:
-    free_server_handle(handle);
-    return &ret;
 
+exit_func:
+    (void)kadm5_free_principal_ent(handle->lhandle, &caller_ent);
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
-gpols_ret *
-get_pols_2_svc(gpols_arg *arg, struct svc_req *rqstp)
+bool_t
+get_pols_2_svc(gpols_arg *arg, gpols_ret *ret, struct svc_req *rqstp)
 {
-    static gpols_ret                ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_gpols_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, NULL, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           NULL);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
     prime_arg = arg->exp;
     if (prime_arg == NULL)
         prime_arg = "*";
 
-    if (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
-                                                       rqst2name(rqstp),
-                                                       ACL_LIST, NULL, NULL)) {
-        ret.code = KADM5_AUTH_LIST;
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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);
     } else {
-        ret.code  = kadm5_get_policies((void *)handle,
-                                       arg->exp, &ret.pols,
-                                       &ret.count);
-        if( ret.code != 0 )
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_get_policies(handle, arg->exp, &ret->pols,
+                                       &ret->count);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_get_policies", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -1500,39 +1391,28 @@ get_pols_2_svc(gpols_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
-getprivs_ret * get_privs_2_svc(krb5_ui_4 *arg, struct svc_req *rqstp)
+bool_t
+get_privs_2_svc(krb5_ui_4 *arg, getprivs_ret *ret, struct svc_req *rqstp)
 {
-    static getprivs_ret            ret;
-    gss_buffer_desc                client_name, service_name;
-    OM_uint32                      minor_stat;
+    gss_buffer_desc                client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t          handle;
     const char                     *errmsg = NULL;
 
-    xdr_free(xdr_getprivs_ret, &ret);
-
-    if ((ret.code = new_server_handle(*arg, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
+    ret->code = stub_setup(*arg, rqstp, NULL, &handle, &ret->api_version,
+                           &client_name, &service_name, NULL);
+    if (ret->code)
         goto exit_func;
-    }
 
-    ret.code = kadm5_get_privs((void *)handle, &ret.privs);
-    if( ret.code != 0 )
-        errmsg = krb5_get_error_message(handle->context, ret.code);
+    ret->code = kadm5_get_privs(handle, &ret->privs);
+    if (ret->code != 0)
+        errmsg = krb5_get_error_message(handle->context, ret->code);
 
     log_done("kadm5_get_privs", client_name.value, errmsg,
              &client_name, &service_name, rqstp);
@@ -1540,55 +1420,37 @@ getprivs_ret * get_privs_2_svc(krb5_ui_4 *arg, struct svc_req *rqstp)
     if (errmsg != NULL)
         krb5_free_error_message(handle->context, errmsg);
 
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-purgekeys_2_svc(purgekeys_arg *arg, struct svc_req *rqstp)
+bool_t
+purgekeys_2_svc(purgekeys_arg *arg, generic_ret *ret, struct svc_req *rqstp)
 {
-    static generic_ret          ret;
-    char                        *prime_arg, *funcname;
-    gss_buffer_desc             client_name, service_name;
-    OM_uint32                   minor_stat;
+    char                        *funcname, *prime_arg = NULL;
+    gss_buffer_desc             client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc             service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t       handle;
 
     const char                  *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    ret.api_version = handle->api_version;
-
     funcname = "kadm5_purgekeys";
 
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (CHANGEPW_SERVICE(rqstp)
-        || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY,
-                               arg->princ, NULL)) {
-        ret.code = KADM5_AUTH_MODIFY;
+    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 {
-        ret.code = kadm5_purgekeys((void *)handle, arg->princ,
-                                   arg->keepkvno);
-        if (ret.code != 0)
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_purgekeys(handle, arg->princ, arg->keepkvno);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done(funcname, prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -1596,58 +1458,37 @@ purgekeys_2_svc(purgekeys_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-gstrings_ret *
-get_strings_2_svc(gstrings_arg *arg, struct svc_req *rqstp)
+bool_t
+get_strings_2_svc(gstrings_arg *arg, gstrings_ret *ret, struct svc_req *rqstp)
 {
-    static gstrings_ret             ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_gstrings_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
 
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
-        goto exit_func;
-    }
-
-    if (! cmp_gss_krb5_name(handle, rqst2name(rqstp), arg->princ) &&
-        (CHANGEPW_SERVICE(rqstp) || !kadm5int_acl_check(handle->context,
-                                                        rqst2name(rqstp),
-                                                        ACL_INQUIRE,
-                                                        arg->princ,
-                                                        NULL))) {
-        ret.code = KADM5_AUTH_GET;
+    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);
     } else {
-        ret.code = kadm5_get_strings((void *)handle, arg->princ, &ret.strings,
-                                     &ret.count);
-        if (ret.code != 0)
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_get_strings(handle, arg->princ, &ret->strings,
+                                      &ret->count);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_get_strings", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -1655,55 +1496,37 @@ get_strings_2_svc(gstrings_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *
-set_string_2_svc(sstring_arg *arg, struct svc_req *rqstp)
+bool_t
+set_string_2_svc(sstring_arg *arg, generic_ret *ret, struct svc_req *rqstp)
 {
-    static generic_ret              ret;
-    char                            *prime_arg;
-    gss_buffer_desc                 client_name,
-        service_name;
-    OM_uint32                       minor_stat;
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t           handle;
     const char                      *errmsg = NULL;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(arg->api_version, rqstp, &handle)))
-        goto exit_func;
-
-    if ((ret.code = check_handle((void *)handle)))
-        goto exit_func;
-
-    ret.api_version = handle->api_version;
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-    if (krb5_unparse_name(handle->context, arg->princ, &prime_arg)) {
-        ret.code = KADM5_BAD_PRINCIPAL;
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
         goto exit_func;
-    }
 
-    if (CHANGEPW_SERVICE(rqstp)
-        || !kadm5int_acl_check(handle->context, rqst2name(rqstp), ACL_MODIFY,
-                               arg->princ, NULL)) {
-        ret.code = KADM5_AUTH_MODIFY;
+    if (CHANGEPW_SERVICE(rqstp) ||
+        !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);
     } else {
-        ret.code = kadm5_set_string((void *)handle, arg->princ, arg->key,
-                                    arg->value);
-        if (ret.code != 0)
-            errmsg = krb5_get_error_message(handle->context, ret.code);
+        ret->code = kadm5_set_string(handle, arg->princ, arg->key, arg->value);
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
 
         log_done("kadm5_mod_strings", prime_arg, errmsg,
                  &client_name, &service_name, rqstp);
@@ -1711,42 +1534,29 @@ set_string_2_svc(sstring_arg *arg, struct svc_req *rqstp)
         if (errmsg != NULL)
             krb5_free_error_message(handle->context, errmsg);
     }
-    free(prime_arg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+
 exit_func:
-    free_server_handle(handle);
-    return &ret;
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
 }
 
-generic_ret *init_2_svc(krb5_ui_4 *arg, struct svc_req *rqstp)
+bool_t
+init_2_svc(krb5_ui_4 *arg, generic_ret *ret, struct svc_req *rqstp)
 {
-    static generic_ret         ret;
-    gss_buffer_desc            client_name,
-        service_name;
+    gss_buffer_desc            client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc            service_name = GSS_C_EMPTY_BUFFER;
     kadm5_server_handle_t      handle;
-    OM_uint32                  minor_stat;
     const char                 *errmsg = NULL;
     size_t clen, slen;
     char *cdots, *sdots;
 
-    xdr_free(xdr_generic_ret, &ret);
-
-    if ((ret.code = new_server_handle(*arg, rqstp, &handle)))
+    ret->code = stub_setup(*arg, rqstp, NULL, &handle, &ret->api_version,
+                           &client_name, &service_name, NULL);
+    if (ret->code)
         goto exit_func;
-    if (! (ret.code = check_handle((void *)handle))) {
-        ret.api_version = handle->api_version;
-    }
 
-    free_server_handle(handle);
-
-    if (setup_gss_names(rqstp, &client_name, &service_name) < 0) {
-        ret.code = KADM5_FAILURE;
-        goto exit_func;
-    }
-
-    if (ret.code != 0)
-        errmsg = krb5_get_error_message(NULL, ret.code);
+    if (ret->code != 0)
+        errmsg = krb5_get_error_message(handle->context, ret->code);
 
     clen = client_name.length;
     trunc_name(&clen, &cdots);
@@ -1760,16 +1570,15 @@ generic_ret *init_2_svc(krb5_ui_4 *arg, struct svc_req *rqstp)
                      errmsg ? errmsg : _("success"),
                      (int)clen, (char *)client_name.value, cdots,
                      (int)slen, (char *)service_name.value, sdots,
-                     inet_ntoa(rqstp->rq_xprt->xp_raddr.sin_addr),
-                     ret.api_version & ~(KADM5_API_VERSION_MASK),
+                     client_addr(rqstp->rq_xprt),
+                     ret->api_version & ~(KADM5_API_VERSION_MASK),
                      rqstp->rq_cred.oa_flavor);
     if (errmsg != NULL)
-        krb5_free_error_message(NULL, errmsg);
-    gss_release_buffer(&minor_stat, &client_name);
-    gss_release_buffer(&minor_stat, &service_name);
+        krb5_free_error_message(handle->context, errmsg);
 
 exit_func:
-    return(&ret);
+    stub_cleanup(handle, NULL, &client_name, &service_name);
+    return TRUE;
 }
 
 gss_name_t
@@ -1781,3 +1590,60 @@ rqst2name(struct svc_req *rqstp)
     else
         return rqstp->rq_clntcred;
 }
+
+bool_t
+get_principal_keys_2_svc(getpkeys_arg *arg, getpkeys_ret *ret,
+                         struct svc_req *rqstp)
+{
+    char                            *prime_arg = NULL;
+    gss_buffer_desc                 client_name = GSS_C_EMPTY_BUFFER;
+    gss_buffer_desc                 service_name = GSS_C_EMPTY_BUFFER;
+    kadm5_server_handle_t           handle;
+    const char                      *errmsg = NULL;
+
+    ret->code = stub_setup(arg->api_version, rqstp, arg->princ, &handle,
+                           &ret->api_version, &client_name, &service_name,
+                           &prime_arg);
+    if (ret->code)
+        goto exit_func;
+
+    if (!(CHANGEPW_SERVICE(rqstp)) &&
+        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 {
+        log_unauth("kadm5_get_principal_keys", prime_arg,
+                   &client_name, &service_name, rqstp);
+        ret->code = KADM5_AUTH_EXTRACT;
+    }
+
+    if (ret->code == KADM5_OK) {
+        ret->code = check_lockdown_keys(handle, arg->princ);
+        if (ret->code != KADM5_OK) {
+            kadm5_free_kadm5_key_data(handle->context, ret->n_key_data,
+                                      ret->key_data);
+            ret->key_data = NULL;
+            ret->n_key_data = 0;
+        }
+        if (ret->code == KADM5_PROTECT_KEYS) {
+            log_unauth("kadm5_get_principal_keys", prime_arg,
+                       &client_name, &service_name, rqstp);
+            ret->code = KADM5_AUTH_EXTRACT;
+        }
+    }
+
+    if (ret->code != KADM5_AUTH_EXTRACT) {
+        if (ret->code != 0)
+            errmsg = krb5_get_error_message(handle->context, ret->code);
+
+        log_done("kadm5_get_principal_keys", prime_arg, errmsg,
+                 &client_name, &service_name, rqstp);
+
+        if (errmsg != NULL)
+            krb5_free_error_message(handle->context, errmsg);
+    }
+
+exit_func:
+    stub_cleanup(handle, prime_arg, &client_name, &service_name);
+    return TRUE;
+}