summaryrefslogtreecommitdiff
path: root/src/kadmin/server/auth_acl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/kadmin/server/auth_acl.c')
-rw-r--r--src/kadmin/server/auth_acl.c755
1 files changed, 755 insertions, 0 deletions
diff --git a/src/kadmin/server/auth_acl.c b/src/kadmin/server/auth_acl.c
new file mode 100644
index 000000000000..efe9c6961bbb
--- /dev/null
+++ b/src/kadmin/server/auth_acl.c
@@ -0,0 +1,755 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* kadmin/server/auth_acl.c - ACL kadm5_auth module */
+/*
+ * Copyright 1995-2004, 2007, 2008, 2017 by the Massachusetts Institute of
+ * Technology. All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include <syslog.h>
+#include <kadm5/admin.h>
+#include <krb5/kadm5_auth_plugin.h>
+#include "adm_proto.h"
+#include <ctype.h>
+#include "auth.h"
+
+/*
+ * Access control bits.
+ */
+#define ACL_ADD 1
+#define ACL_DELETE 2
+#define ACL_MODIFY 4
+#define ACL_CHANGEPW 8
+/* #define ACL_CHANGE_OWN_PW 16 */
+#define ACL_INQUIRE 32
+#define ACL_EXTRACT 64
+#define ACL_LIST 128
+#define ACL_SETKEY 256
+#define ACL_IPROP 512
+
+#define ACL_ALL_MASK (ACL_ADD | \
+ ACL_DELETE | \
+ ACL_MODIFY | \
+ ACL_CHANGEPW | \
+ ACL_INQUIRE | \
+ ACL_LIST | \
+ ACL_IPROP | \
+ ACL_SETKEY)
+
+struct acl_op_table {
+ char op;
+ uint32_t mask;
+};
+
+struct acl_entry {
+ struct acl_entry *next;
+ krb5_principal client;
+ uint32_t op_allowed;
+ krb5_principal target;
+ struct kadm5_auth_restrictions *rs;
+};
+
+static const struct acl_op_table acl_op_table[] = {
+ { 'a', ACL_ADD },
+ { 'd', ACL_DELETE },
+ { 'm', ACL_MODIFY },
+ { 'c', ACL_CHANGEPW },
+ { 'i', ACL_INQUIRE },
+ { 'l', ACL_LIST },
+ { 'p', ACL_IPROP },
+ { 's', ACL_SETKEY },
+ { 'x', ACL_ALL_MASK },
+ { '*', ACL_ALL_MASK },
+ { 'e', ACL_EXTRACT },
+ { '\0', 0 }
+};
+
+struct wildstate {
+ int nwild;
+ const krb5_data *backref[9];
+};
+
+struct acl_state {
+ struct acl_entry *list;
+};
+
+/*
+ * Get a line from the ACL file. Lines ending with \ are continued on the next
+ * line. The caller should set *lineno to 1 and *incr to 0 before the first
+ * call. On successful return, *lineno will be the line number of the line
+ * read. Return a pointer to the line on success, or NULL on end of file or
+ * read failure.
+ */
+static char *
+get_line(FILE *fp, const char *fname, int *lineno, int *incr)
+{
+ const int chunksize = 128;
+ struct k5buf buf;
+ size_t old_len;
+ char *p;
+
+ /* Increment *lineno by the number of newlines from the last line. */
+ *lineno += *incr;
+ *incr = 0;
+
+ k5_buf_init_dynamic(&buf);
+ for (;;) {
+ /* Read at least part of a line into the buffer. */
+ old_len = buf.len;
+ p = k5_buf_get_space(&buf, chunksize);
+ if (p == NULL)
+ return NULL;
+
+ if (fgets(p, chunksize, fp) == NULL) {
+ /* We reached the end. Return a final unterminated line, if there
+ * is one and it's not a comment. */
+ k5_buf_truncate(&buf, old_len);
+ if (buf.len > 0 && *(char *)buf.data != '#')
+ return buf.data;
+ k5_buf_free(&buf);
+ return NULL;
+ }
+
+ /* Set the buffer length based on the actual amount read. */
+ k5_buf_truncate(&buf, old_len + strlen(p));
+
+ p = buf.data;
+ if (buf.len > 0 && p[buf.len - 1] == '\n') {
+ /* We have a complete raw line in the buffer. */
+ (*incr)++;
+ k5_buf_truncate(&buf, buf.len - 1);
+ if (buf.len > 0 && p[buf.len - 1] == '\\') {
+ /* This line has a continuation marker; keep reading. */
+ k5_buf_truncate(&buf, buf.len - 1);
+ } else if (buf.len == 0 || *p == '#') {
+ /* This line is empty or a comment. Start over. */
+ *lineno += *incr;
+ *incr = 0;
+ k5_buf_truncate(&buf, 0);
+ } else {
+ return buf.data;
+ }
+ }
+ }
+}
+
+/*
+ * Parse a restrictions field. Return NULL on failure.
+ *
+ * Allowed restrictions are:
+ * [+-]flagname (recognized by krb5_flagspec_to_mask)
+ * flag is forced to indicated value
+ * -clearpolicy policy is forced clear
+ * -policy pol policy is forced to be "pol"
+ * -{expire,pwexpire,maxlife,maxrenewlife} deltat
+ * associated value will be forced to
+ * MIN(deltat, requested value)
+ */
+static struct kadm5_auth_restrictions *
+parse_restrictions(const char *str, const char *fname)
+{
+ char *copy = NULL, *token, *arg, *save;
+ const char *delims = "\t\n\f\v\r ,";
+ krb5_deltat delta;
+ struct kadm5_auth_restrictions *rs;
+
+ copy = strdup(str);
+ if (copy == NULL)
+ return NULL;
+
+ rs = calloc(1, sizeof(*rs));
+ if (rs == NULL) {
+ free(copy);
+ return NULL;
+ }
+
+ rs->forbid_attrs = ~(krb5_flags)0;
+ for (token = strtok_r(copy, delims, &save); token != NULL;
+ token = strtok_r(NULL, delims, &save)) {
+
+ if (krb5_flagspec_to_mask(token, &rs->require_attrs,
+ &rs->forbid_attrs) == 0) {
+ rs->mask |= KADM5_ATTRIBUTES;
+ continue;
+ }
+
+ if (strcmp(token, "-clearpolicy") == 0) {
+ rs->mask |= KADM5_POLICY_CLR;
+ continue;
+ }
+
+ /* Everything else needs an argument. */
+ arg = strtok_r(NULL, delims, &save);
+ if (arg == NULL)
+ goto error;
+
+ if (strcmp(token, "-policy") == 0) {
+ if (rs->policy != NULL)
+ goto error;
+ rs->policy = strdup(arg);
+ if (rs->policy == NULL)
+ goto error;
+ rs->mask |= KADM5_POLICY;
+ continue;
+ }
+
+ /* All other arguments must be a deltat. */
+ if (krb5_string_to_deltat(arg, &delta) != 0)
+ goto error;
+
+ if (strcmp(token, "-expire") == 0) {
+ rs->princ_lifetime = delta;
+ rs->mask |= KADM5_PRINC_EXPIRE_TIME;
+ } else if (strcmp(token, "-pwexpire") == 0) {
+ rs->pw_lifetime = delta;
+ rs->mask |= KADM5_PW_EXPIRATION;
+ } else if (strcmp(token, "-maxlife") == 0) {
+ rs->max_life = delta;
+ rs->mask |= KADM5_MAX_LIFE;
+ } else if (strcmp(token, "-maxrenewlife") == 0) {
+ rs->max_renewable_life = delta;
+ rs->mask |= KADM5_MAX_RLIFE;
+ } else {
+ goto error;
+ }
+ }
+
+ free(copy);
+ return rs;
+
+error:
+ krb5_klog_syslog(LOG_ERR, _("%s: invalid restrictions: %s"), fname, str);
+ free(copy);
+ free(rs->policy);
+ free(rs);
+ return NULL;
+}
+
+static void
+free_acl_entry(struct acl_entry *entry)
+{
+ krb5_free_principal(NULL, entry->client);
+ krb5_free_principal(NULL, entry->target);
+ if (entry->rs != NULL) {
+ free(entry->rs->policy);
+ free(entry->rs);
+ }
+ free(entry);
+}
+
+/* Parse the four fields of an ACL entry and return a structure representing
+ * it. Log a message and return NULL on error. */
+static struct acl_entry *
+parse_entry(krb5_context context, const char *client, const char *ops,
+ const char *target, const char *rs, const char *line,
+ const char *fname)
+{
+ struct acl_entry *entry;
+ const char *op;
+ char rop;
+ int t;
+
+ entry = calloc(1, sizeof(*entry));
+ if (entry == NULL)
+ return NULL;
+
+ for (op = ops; *op; op++) {
+ rop = isupper((unsigned char)*op) ? tolower((unsigned char)*op) : *op;
+ for (t = 0; acl_op_table[t].op; t++) {
+ if (rop == acl_op_table[t].op) {
+ if (rop == *op)
+ entry->op_allowed |= acl_op_table[t].mask;
+ else
+ entry->op_allowed &= ~acl_op_table[t].mask;
+ break;
+ }
+ }
+ if (!acl_op_table[t].op) {
+ krb5_klog_syslog(LOG_ERR,
+ _("Unrecognized ACL operation '%c' in %s"),
+ *op, line);
+ goto error;
+ }
+ }
+
+ if (strcmp(client, "*") != 0) {
+ if (krb5_parse_name(context, client, &entry->client) != 0) {
+ krb5_klog_syslog(LOG_ERR, _("Cannot parse client principal '%s'"),
+ client);
+ goto error;
+ }
+ }
+
+ if (target != NULL && strcmp(target, "*") != 0) {
+ if (krb5_parse_name(context, target, &entry->target) != 0) {
+ krb5_klog_syslog(LOG_ERR, _("Cannot parse target principal '%s'"),
+ target);
+ goto error;
+ }
+ }
+
+ if (rs != NULL) {
+ entry->rs = parse_restrictions(rs, fname);
+ if (entry->rs == NULL)
+ goto error;
+ }
+
+ return entry;
+
+error:
+ free_acl_entry(entry);
+ return NULL;
+}
+
+/* Parse the contents of an ACL line. */
+static struct acl_entry *
+parse_line(krb5_context context, const char *line, const char *fname)
+{
+ struct acl_entry *entry = NULL;
+ char *copy;
+ char *client, *client_end, *ops, *ops_end, *target, *target_end, *rs, *end;
+ const char *ws = "\t\n\f\v\r ,";
+
+ /*
+ * Format:
+ * entry ::= [<whitespace>] <principal> <whitespace> <opstring>
+ * [<whitespace> <target> [<whitespace> <restrictions>
+ * [<whitespace>]]]
+ */
+
+ /* Make a copy and remove any trailing whitespace. */
+ copy = strdup(line);
+ if (copy == NULL)
+ return NULL;
+ end = copy + strlen(copy);
+ while (end > copy && isspace(end[-1]))
+ *--end = '\0';
+
+ /* Find the beginning and end of each field. The end of restrictions is
+ * the end of copy. */
+ client = copy + strspn(copy, ws);
+ client_end = client + strcspn(client, ws);
+ ops = client_end + strspn(client_end, ws);
+ ops_end = ops + strcspn(ops, ws);
+ target = ops_end + strspn(ops_end, ws);
+ target_end = target + strcspn(target, ws);
+ rs = target_end + strspn(target_end, ws);
+
+ /* Terminate the first three fields. */
+ *client_end = *ops_end = *target_end = '\0';
+
+ /* The last two fields are optional; represent them as NULL if not present.
+ * The first two fields are required. */
+ if (*target == '\0')
+ target = NULL;
+ if (*rs == '\0')
+ rs = NULL;
+ if (*client != '\0' && *ops != '\0')
+ entry = parse_entry(context, client, ops, target, rs, line, fname);
+ free(copy);
+ return entry;
+}
+
+/* Free all ACL entries. */
+static void
+free_acl_entries(struct acl_state *state)
+{
+ struct acl_entry *entry, *next;
+
+ for (entry = state->list; entry != NULL; entry = next) {
+ next = entry->next;
+ free_acl_entry(entry);
+ }
+ state->list = NULL;
+}
+
+/* Open and parse the ACL file. */
+static krb5_error_code
+load_acl_file(krb5_context context, const char *fname, struct acl_state *state)
+{
+ krb5_error_code ret;
+ FILE *fp;
+ char *line;
+ struct acl_entry **entry_slot;
+ int lineno, incr;
+
+ state->list = NULL;
+
+ /* Open the ACL file for reading. */
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ krb5_klog_syslog(LOG_ERR, _("%s while opening ACL file %s"),
+ error_message(errno), fname);
+ ret = errno;
+ k5_setmsg(context, errno, _("Cannot open %s: %s"), fname,
+ error_message(ret));
+ return ret;
+ }
+
+ set_cloexec_file(fp);
+ lineno = 1;
+ incr = 0;
+ entry_slot = &state->list;
+
+ /* Get a non-comment line. */
+ while ((line = get_line(fp, fname, &lineno, &incr)) != NULL) {
+ /* Parse it. Fail out on syntax error. */
+ *entry_slot = parse_line(context, line, fname);
+ if (*entry_slot == NULL) {
+ krb5_klog_syslog(LOG_ERR,
+ _("%s: syntax error at line %d <%.10s...>"),
+ fname, lineno, line);
+ k5_setmsg(context, EINVAL,
+ _("%s: syntax error at line %d <%.10s...>"),
+ fname, lineno, line);
+ free_acl_entries(state);
+ free(line);
+ fclose(fp);
+ return EINVAL;
+ }
+ entry_slot = &(*entry_slot)->next;
+ free(line);
+ }
+
+ fclose(fp);
+ return 0;
+}
+
+/*
+ * See if two data entries match. If e1 is a wildcard (matching a whole
+ * component only) and targetflag is false, save an alias to e2 into
+ * ws->backref. If e1 is a back-reference and targetflag is true, compare the
+ * appropriate entry in ws->backref to e2. If ws is NULL, do not store or
+ * match back-references.
+ */
+static krb5_boolean
+match_data(const krb5_data *e1, const krb5_data *e2, krb5_boolean targetflag,
+ struct wildstate *ws)
+{
+ int n;
+
+ if (data_eq_string(*e1, "*")) {
+ if (ws != NULL && !targetflag) {
+ if (ws->nwild < 9)
+ ws->backref[ws->nwild++] = e2;
+ }
+ return TRUE;
+ }
+
+ if (ws != NULL && targetflag && e1->length == 2 && e1->data[0] == '*' &&
+ e1->data[1] >= '1' && e1->data[1] <= '9') {
+ n = e1->data[1] - '1';
+ if (n >= ws->nwild)
+ return FALSE;
+ return data_eq(*e2, *ws->backref[n]);
+ } else {
+ return data_eq(*e2, *e1);
+ }
+}
+
+/* Return true if p1 matches p2. p1 may contain wildcards if targetflag is
+ * false, or backreferences if it is true. */
+static krb5_boolean
+match_princ(krb5_const_principal p1, krb5_const_principal p2,
+ krb5_boolean targetflag, struct wildstate *ws)
+{
+ int i;
+
+ /* The principals must be of the same length. */
+ if (p1->length != p2->length)
+ return FALSE;
+
+ /* The realm must match, and does not interact with wildcard state. */
+ if (!match_data(&p1->realm, &p2->realm, targetflag, NULL))
+ return FALSE;
+
+ /* All components of the principals must match. */
+ for (i = 0; i < p1->length; i++) {
+ if (!match_data(&p1->data[i], &p2->data[i], targetflag, ws))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* Find an ACL entry matching principal and target_principal. Return NULL if
+ * none is found. */
+static struct acl_entry *
+find_entry(struct acl_state *state, krb5_const_principal client,
+ krb5_const_principal target)
+{
+ struct acl_entry *entry;
+ struct wildstate ws;
+
+ for (entry = state->list; entry != NULL; entry = entry->next) {
+ memset(&ws, 0, sizeof(ws));
+ if (entry->client != NULL) {
+ if (!match_princ(entry->client, client, FALSE, &ws))
+ continue;
+ }
+
+ if (entry->target != NULL) {
+ if (target == NULL)
+ continue;
+ if (!match_princ(entry->target, target, TRUE, &ws))
+ continue;
+ }
+
+ return entry;
+ }
+
+ return NULL;
+}
+
+/* Return true if op is permitted for this principal. Set *rs_out (if not
+ * NULL) according to any restrictions in the ACL entry. */
+static krb5_error_code
+acl_check(kadm5_auth_moddata data, uint32_t op, krb5_const_principal client,
+ krb5_const_principal target, struct kadm5_auth_restrictions **rs_out)
+{
+ struct acl_entry *entry;
+
+ if (rs_out != NULL)
+ *rs_out = NULL;
+
+ entry = find_entry((struct acl_state *)data, client, target);
+ if (entry == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+ if (!(entry->op_allowed & op))
+ return KRB5_PLUGIN_NO_HANDLE;
+
+ if (rs_out != NULL && entry->rs != NULL && entry->rs->mask)
+ *rs_out = entry->rs;
+
+ return 0;
+}
+
+static krb5_error_code
+acl_init(krb5_context context, const char *acl_file,
+ kadm5_auth_moddata *data_out)
+{
+ krb5_error_code ret;
+ struct acl_state *state;
+
+ *data_out = NULL;
+ if (acl_file == NULL)
+ return KRB5_PLUGIN_NO_HANDLE;
+ state = malloc(sizeof(*state));
+ state->list = NULL;
+ ret = load_acl_file(context, acl_file, state);
+ if (ret) {
+ free(state);
+ return ret;
+ }
+ *data_out = (kadm5_auth_moddata)state;
+ return 0;
+}
+
+static void
+acl_fini(krb5_context context, kadm5_auth_moddata data)
+{
+ if (data == NULL)
+ return;
+ free_acl_entries((struct acl_state *)data);
+ free(data);
+}
+
+static krb5_error_code
+acl_addprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const struct _kadm5_principal_ent_t *ent, long mask,
+ struct kadm5_auth_restrictions **rs_out)
+{
+ return acl_check(data, ACL_ADD, client, target, rs_out);
+}
+
+static krb5_error_code
+acl_modprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const struct _kadm5_principal_ent_t *ent, long mask,
+ struct kadm5_auth_restrictions **rs_out)
+{
+ return acl_check(data, ACL_MODIFY, client, target, rs_out);
+}
+
+static krb5_error_code
+acl_setstr(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target,
+ const char *key, const char *value)
+{
+ return acl_check(data, ACL_MODIFY, client, target, NULL);
+}
+
+static krb5_error_code
+acl_cpw(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return acl_check(data, ACL_CHANGEPW, client, target, NULL);
+}
+
+static krb5_error_code
+acl_chrand(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return acl_check(data, ACL_CHANGEPW, client, target, NULL);
+}
+
+static krb5_error_code
+acl_setkey(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return acl_check(data, ACL_SETKEY, client, target, NULL);
+}
+
+static krb5_error_code
+acl_purgekeys(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return acl_check(data, ACL_MODIFY, client, target, NULL);
+}
+
+static krb5_error_code
+acl_delprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return acl_check(data, ACL_DELETE, client, target, NULL);
+}
+
+static krb5_error_code
+acl_renprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal src,
+ krb5_const_principal dest)
+{
+ struct kadm5_auth_restrictions *rs;
+
+ if (acl_check(data, ACL_DELETE, client, src, NULL) == 0 &&
+ acl_check(data, ACL_ADD, client, dest, &rs) == 0 && rs == NULL)
+ return 0;
+ return KRB5_PLUGIN_NO_HANDLE;
+}
+
+static krb5_error_code
+acl_getprinc(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return acl_check(data, ACL_INQUIRE, client, target, NULL);
+}
+
+static krb5_error_code
+acl_getstrs(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return acl_check(data, ACL_INQUIRE, client, target, NULL);
+}
+
+static krb5_error_code
+acl_extract(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, krb5_const_principal target)
+{
+ return acl_check(data, ACL_EXTRACT, client, target, NULL);
+}
+
+static krb5_error_code
+acl_listprincs(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client)
+{
+ return acl_check(data, ACL_LIST, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_addpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const struct _kadm5_policy_ent_t *ent, long mask)
+{
+ return acl_check(data, ACL_ADD, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_modpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const struct _kadm5_policy_ent_t *ent, long mask)
+{
+ return acl_check(data, ACL_MODIFY, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_delpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy)
+{
+ return acl_check(data, ACL_DELETE, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_getpol(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client, const char *policy,
+ const char *client_policy)
+{
+ return acl_check(data, ACL_INQUIRE, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_listpols(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client)
+{
+ return acl_check(data, ACL_LIST, client, NULL, NULL);
+}
+
+static krb5_error_code
+acl_iprop(krb5_context context, kadm5_auth_moddata data,
+ krb5_const_principal client)
+{
+ return acl_check(data, ACL_IPROP, client, NULL, NULL);
+}
+
+krb5_error_code
+kadm5_auth_acl_initvt(krb5_context context, int maj_ver, int min_ver,
+ krb5_plugin_vtable vtable)
+{
+ kadm5_auth_vtable vt;
+
+ if (maj_ver != 1)
+ return KRB5_PLUGIN_VER_NOTSUPP;
+ vt = (kadm5_auth_vtable)vtable;
+ vt->name = "acl";
+ vt->init = acl_init;
+ vt->fini = acl_fini;
+ vt->addprinc = acl_addprinc;
+ vt->modprinc = acl_modprinc;
+ vt->setstr = acl_setstr;
+ vt->cpw = acl_cpw;
+ vt->chrand = acl_chrand;
+ vt->setkey = acl_setkey;
+ vt->purgekeys = acl_purgekeys;
+ vt->delprinc = acl_delprinc;
+ vt->renprinc = acl_renprinc;
+ vt->getprinc = acl_getprinc;
+ vt->getstrs = acl_getstrs;
+ vt->extract = acl_extract;
+ vt->listprincs = acl_listprincs;
+ vt->addpol = acl_addpol;
+ vt->modpol = acl_modpol;
+ vt->delpol = acl_delpol;
+ vt->getpol = acl_getpol;
+ vt->listpols = acl_listpols;
+ vt->iprop = acl_iprop;
+ return 0;
+}