summaryrefslogtreecommitdiff
path: root/src/lib/kadm5/srv/server_acl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/kadm5/srv/server_acl.c')
-rw-r--r--src/lib/kadm5/srv/server_acl.c823
1 files changed, 823 insertions, 0 deletions
diff --git a/src/lib/kadm5/srv/server_acl.c b/src/lib/kadm5/srv/server_acl.c
new file mode 100644
index 000000000000..59ed0b975472
--- /dev/null
+++ b/src/lib/kadm5/srv/server_acl.c
@@ -0,0 +1,823 @@
+/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/* lib/kadm5/srv/server_acl.c */
+/*
+ * Copyright 1995-2004, 2007, 2008 by the Massachusetts Institute of Technology.
+ * All Rights Reserved.
+ *
+ * Export of this software from the United States of America may
+ * require a specific license from the United States Government.
+ * It is the responsibility of any person or organization contemplating
+ * export to obtain such a license before exporting.
+ *
+ * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
+ * distribute this software and its documentation for any purpose and
+ * without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both that copyright notice and
+ * this permission notice appear in supporting documentation, and that
+ * the name of M.I.T. not be used in advertising or publicity pertaining
+ * to distribution of the software without specific, written prior
+ * permission. Furthermore if you modify this software you must label
+ * your software as modified software and not distribute it in such a
+ * fashion that it might be confused with the original M.I.T. software.
+ * M.I.T. makes no representations about the suitability of
+ * this software for any purpose. It is provided "as is" without express
+ * or implied warranty.
+ */
+
+#include "k5-int.h"
+#include <syslog.h>
+#include <sys/param.h>
+#include <gssapi/gssapi_generic.h>
+#include <kadm5/server_internal.h>
+#include <kadm5/admin.h>
+#include "adm_proto.h"
+#include "server_acl.h"
+#include <ctype.h>
+
+typedef struct _acl_op_table {
+ char ao_op;
+ krb5_int32 ao_mask;
+} aop_t;
+
+typedef struct _acl_entry {
+ struct _acl_entry *ae_next;
+ char *ae_name;
+ krb5_boolean ae_name_bad;
+ krb5_principal ae_principal;
+ krb5_int32 ae_op_allowed;
+ char *ae_target;
+ krb5_boolean ae_target_bad;
+ krb5_principal ae_target_princ;
+ char *ae_restriction_string;
+ /* eg: "-maxlife 3h -service +proxiable" */
+ krb5_boolean ae_restriction_bad;
+ restriction_t *ae_restrictions;
+} aent_t;
+
+static const aop_t 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 }
+};
+
+typedef struct _wildstate {
+ int nwild;
+ const krb5_data *backref[9];
+} wildstate_t;
+
+static aent_t *acl_list_head = (aent_t *) NULL;
+static aent_t *acl_list_tail = (aent_t *) NULL;
+
+static const char *acl_acl_file = (char *) NULL;
+static int acl_inited = 0;
+static int acl_debug_level = 0;
+/*
+ * This is the catchall entry. If nothing else appropriate is found, or in
+ * the case where the ACL file is not present, this entry controls what can
+ * be done.
+ */
+static const char *acl_catchall_entry = NULL;
+
+static const char *acl_line2long_msg = N_("%s: line %d too long, truncated");
+static const char *acl_op_bad_msg = N_("Unrecognized ACL operation '%c' in "
+ "%s");
+static const char *acl_syn_err_msg = N_("%s: syntax error at line %d "
+ "<%10s...>");
+static const char *acl_cantopen_msg = N_("%s while opening ACL file %s");
+
+/*
+ * kadm5int_acl_get_line() - Get a line from the ACL file.
+ * Lines ending with \ are continued on the next line
+ */
+static char *
+kadm5int_acl_get_line(fp, lnp)
+ FILE *fp;
+ int *lnp; /* caller should set to 1 before first call */
+{
+ int i, domore;
+ static int line_incr = 0;
+ static char acl_buf[BUFSIZ];
+
+ *lnp += line_incr;
+ line_incr = 0;
+ for (domore = 1; domore && !feof(fp); ) {
+ /* Copy in the line, with continuations */
+ for (i = 0; ((i < BUFSIZ) && !feof(fp)); i++) {
+ int byte;
+ byte = fgetc(fp);
+ acl_buf[i] = byte;
+ if (byte == EOF) {
+ if (i > 0 && acl_buf[i-1] == '\\')
+ i--;
+ break; /* it gets nulled-out below */
+ }
+ else if (acl_buf[i] == '\n') {
+ if (i == 0 || acl_buf[i-1] != '\\')
+ break; /* empty line or normal end of line */
+ else {
+ i -= 2; /* back up over "\\\n" and continue */
+ line_incr++;
+ }
+ }
+ }
+ /* Check if we exceeded our buffer size */
+ if (i == sizeof acl_buf && (i--, !feof(fp))) {
+ int c1 = acl_buf[i], c2;
+
+ krb5_klog_syslog(LOG_ERR, _(acl_line2long_msg), acl_acl_file,
+ *lnp);
+ while ((c2 = fgetc(fp)) != EOF) {
+ if (c2 == '\n') {
+ if (c1 != '\\')
+ break;
+ line_incr++;
+ }
+ c1 = c2;
+ }
+ }
+ acl_buf[i] = '\0';
+ if (acl_buf[0] == (char) EOF) /* ptooey */
+ acl_buf[0] = '\0';
+ else
+ line_incr++;
+ if ((acl_buf[0] != '#') && (acl_buf[0] != '\0'))
+ domore = 0;
+ }
+ if (domore || (strlen(acl_buf) == 0))
+ return((char *) NULL);
+ else
+ return(acl_buf);
+}
+
+/*
+ * kadm5int_acl_parse_line() - Parse the contents of an ACL line.
+ */
+static aent_t *
+kadm5int_acl_parse_line(lp)
+ const char *lp;
+{
+ static char acle_principal[BUFSIZ];
+ static char acle_ops[BUFSIZ];
+ static char acle_object[BUFSIZ];
+ static char acle_restrictions[BUFSIZ];
+ aent_t *acle;
+ char *op;
+ int t, found, opok, nmatch;
+
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("* kadm5int_acl_parse_line(line=%20s)\n", lp));
+ /*
+ * Format is still simple:
+ * entry ::= [<whitespace>] <principal> <whitespace> <opstring>
+ * [<whitespace> <target> [<whitespace> <restrictions>
+ * [<whitespace>]]]
+ */
+ acle = (aent_t *) NULL;
+ acle_object[0] = '\0';
+ nmatch = sscanf(lp, "%s %s %s %[^\n]", acle_principal, acle_ops,
+ acle_object, acle_restrictions);
+ if (nmatch >= 2) {
+ acle = (aent_t *) malloc(sizeof(aent_t));
+ if (acle) {
+ acle->ae_next = (aent_t *) NULL;
+ acle->ae_op_allowed = (krb5_int32) 0;
+ acle->ae_target =
+ (nmatch >= 3) ? strdup(acle_object) : (char *) NULL;
+ acle->ae_target_bad = 0;
+ acle->ae_target_princ = (krb5_principal) NULL;
+ opok = 1;
+ for (op=acle_ops; *op; op++) {
+ char rop;
+
+ rop = (isupper((unsigned char) *op)) ? tolower((unsigned char) *op) : *op;
+ found = 0;
+ for (t=0; acl_op_table[t].ao_op; t++) {
+ if (rop == acl_op_table[t].ao_op) {
+ found = 1;
+ if (rop == *op)
+ acle->ae_op_allowed |= acl_op_table[t].ao_mask;
+ else
+ acle->ae_op_allowed &= ~acl_op_table[t].ao_mask;
+ }
+ }
+ if (!found) {
+ krb5_klog_syslog(LOG_ERR, _(acl_op_bad_msg), *op, lp);
+ opok = 0;
+ }
+ }
+ if (opok) {
+ acle->ae_name = strdup(acle_principal);
+ if (acle->ae_name) {
+ acle->ae_principal = (krb5_principal) NULL;
+ acle->ae_name_bad = 0;
+ DPRINT(DEBUG_ACL, acl_debug_level,
+ ("A ACL entry %s -> opmask %x\n",
+ acle->ae_name, acle->ae_op_allowed));
+ }
+ else {
+ if (acle->ae_target)
+ free(acle->ae_target);
+ free(acle);
+ acle = (aent_t *) NULL;
+ }
+ }
+ else {
+ if (acle->ae_target)
+ free(acle->ae_target);
+ free(acle);
+ acle = (aent_t *) NULL;
+ }
+
+ if (acle) {
+ if ( nmatch >= 4 ) {
+ char *trailing;
+
+ trailing = &acle_restrictions[strlen(acle_restrictions)-1];
+ while ( isspace((int) *trailing) )
+ trailing--;
+ trailing[1] = '\0';
+ acle->ae_restriction_string =
+ strdup(acle_restrictions);
+ }
+ else {
+ acle->ae_restriction_string = (char *) NULL;
+ }
+ acle->ae_restriction_bad = 0;
+ acle->ae_restrictions = (restriction_t *) NULL;
+ }
+ }
+ }
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("X kadm5int_acl_parse_line() = %x\n", (long) acle));
+ return(acle);
+}
+
+/*
+ * kadm5int_acl_parse_restrictions() - Parse optional restrictions field
+ *
+ * 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)
+ *
+ * Returns: 0 on success, or system errors
+ */
+static krb5_error_code
+kadm5int_acl_parse_restrictions(s, rpp)
+ char *s;
+ restriction_t **rpp;
+{
+ char *sp = NULL, *tp, *ap, *save;
+ static const char *delims = "\t\n\f\v\r ,";
+ krb5_deltat dt;
+ krb5_error_code code;
+
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("* kadm5int_acl_parse_restrictions(s=%20s, rpp=0x%08x)\n", s, (long)rpp));
+
+ *rpp = (restriction_t *) NULL;
+ code = 0;
+ if (s) {
+ if (!(sp = strdup(s)) /* Don't munge the original */
+ || !(*rpp = (restriction_t *) malloc(sizeof(restriction_t)))) {
+ code = ENOMEM;
+ } else {
+ memset(*rpp, 0, sizeof(**rpp));
+ (*rpp)->forbid_attrs = ~(krb5_flags)0;
+ for (tp = strtok_r(sp, delims, &save); tp;
+ tp = strtok_r(NULL, delims, &save)) {
+ if (!krb5_flagspec_to_mask(tp, &(*rpp)->require_attrs,
+ &(*rpp)->forbid_attrs)) {
+ (*rpp)->mask |= KADM5_ATTRIBUTES;
+ } else if (!strcmp(tp, "-clearpolicy")) {
+ (*rpp)->mask |= KADM5_POLICY_CLR;
+ } else {
+ /* everything else needs an argument ... */
+ if (!(ap = strtok_r(NULL, delims, &save))) {
+ code = EINVAL;
+ break;
+ }
+ if (!strcmp(tp, "-policy")) {
+ if (!((*rpp)->policy = strdup(ap))) {
+ code = ENOMEM;
+ break;
+ }
+ (*rpp)->mask |= KADM5_POLICY;
+ } else {
+ /* all other arguments must be a deltat ... */
+ if (krb5_string_to_deltat(ap, &dt)) {
+ code = EINVAL;
+ break;
+ }
+ if (!strcmp(tp, "-expire")) {
+ (*rpp)->princ_lifetime = dt;
+ (*rpp)->mask |= KADM5_PRINC_EXPIRE_TIME;
+ } else if (!strcmp(tp, "-pwexpire")) {
+ (*rpp)->pw_lifetime = dt;
+ (*rpp)->mask |= KADM5_PW_EXPIRATION;
+ } else if (!strcmp(tp, "-maxlife")) {
+ (*rpp)->max_life = dt;
+ (*rpp)->mask |= KADM5_MAX_LIFE;
+ } else if (!strcmp(tp, "-maxrenewlife")) {
+ (*rpp)->max_renewable_life = dt;
+ (*rpp)->mask |= KADM5_MAX_RLIFE;
+ } else {
+ code = EINVAL;
+ break;
+ }
+ }
+ }
+ }
+ if (code) {
+ krb5_klog_syslog(LOG_ERR, _("%s: invalid restrictions: %s"),
+ acl_acl_file, s);
+ }
+ }
+ }
+ if (sp)
+ free(sp);
+ if (*rpp && code) {
+ if ((*rpp)->policy)
+ free((*rpp)->policy);
+ free(*rpp);
+ *rpp = (restriction_t *) NULL;
+ }
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("X kadm5int_acl_parse_restrictions() = %d, mask=0x%08x\n",
+ code, (*rpp) ? (*rpp)->mask : 0));
+ return code;
+}
+
+/*
+ * kadm5int_acl_impose_restrictions() - impose restrictions, modifying *recp, *maskp
+ *
+ * Returns: 0 on success;
+ * malloc or timeofday errors
+ */
+krb5_error_code
+kadm5int_acl_impose_restrictions(kcontext, recp, maskp, rp)
+ krb5_context kcontext;
+ kadm5_principal_ent_rec *recp;
+ long *maskp;
+ restriction_t *rp;
+{
+ krb5_error_code code;
+ krb5_int32 now;
+
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("* kadm5int_acl_impose_restrictions(..., *maskp=0x%08x, rp=0x%08x)\n",
+ *maskp, (long)rp));
+ if (!rp)
+ return 0;
+ if (rp->mask & (KADM5_PRINC_EXPIRE_TIME|KADM5_PW_EXPIRATION))
+ if ((code = krb5_timeofday(kcontext, &now)))
+ return code;
+
+ if (rp->mask & KADM5_ATTRIBUTES) {
+ recp->attributes |= rp->require_attrs;
+ recp->attributes &= rp->forbid_attrs;
+ *maskp |= KADM5_ATTRIBUTES;
+ }
+ if (rp->mask & KADM5_POLICY_CLR) {
+ *maskp &= ~KADM5_POLICY;
+ *maskp |= KADM5_POLICY_CLR;
+ } else if (rp->mask & KADM5_POLICY) {
+ if (recp->policy && strcmp(recp->policy, rp->policy)) {
+ free(recp->policy);
+ recp->policy = (char *) NULL;
+ }
+ if (!recp->policy) {
+ recp->policy = strdup(rp->policy); /* XDR will free it */
+ if (!recp->policy)
+ return ENOMEM;
+ }
+ *maskp |= KADM5_POLICY;
+ }
+ if (rp->mask & KADM5_PRINC_EXPIRE_TIME) {
+ if (!(*maskp & KADM5_PRINC_EXPIRE_TIME)
+ || (recp->princ_expire_time > (now + rp->princ_lifetime)))
+ recp->princ_expire_time = now + rp->princ_lifetime;
+ *maskp |= KADM5_PRINC_EXPIRE_TIME;
+ }
+ if (rp->mask & KADM5_PW_EXPIRATION) {
+ if (!(*maskp & KADM5_PW_EXPIRATION)
+ || (recp->pw_expiration > (now + rp->pw_lifetime)))
+ recp->pw_expiration = now + rp->pw_lifetime;
+ *maskp |= KADM5_PW_EXPIRATION;
+ }
+ if (rp->mask & KADM5_MAX_LIFE) {
+ if (!(*maskp & KADM5_MAX_LIFE)
+ || (recp->max_life > rp->max_life))
+ recp->max_life = rp->max_life;
+ *maskp |= KADM5_MAX_LIFE;
+ }
+ if (rp->mask & KADM5_MAX_RLIFE) {
+ if (!(*maskp & KADM5_MAX_RLIFE)
+ || (recp->max_renewable_life > rp->max_renewable_life))
+ recp->max_renewable_life = rp->max_renewable_life;
+ *maskp |= KADM5_MAX_RLIFE;
+ }
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("X kadm5int_acl_impose_restrictions() = 0, *maskp=0x%08x\n", *maskp));
+ return 0;
+}
+
+/*
+ * kadm5int_acl_free_entries() - Free all ACL entries.
+ */
+static void
+kadm5int_acl_free_entries()
+{
+ aent_t *ap;
+ aent_t *np;
+
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_free_entries()\n"));
+ for (ap=acl_list_head; ap; ap = np) {
+ if (ap->ae_name)
+ free(ap->ae_name);
+ if (ap->ae_principal)
+ krb5_free_principal((krb5_context) NULL, ap->ae_principal);
+ if (ap->ae_target)
+ free(ap->ae_target);
+ if (ap->ae_target_princ)
+ krb5_free_principal((krb5_context) NULL, ap->ae_target_princ);
+ if (ap->ae_restriction_string)
+ free(ap->ae_restriction_string);
+ if (ap->ae_restrictions) {
+ if (ap->ae_restrictions->policy)
+ free(ap->ae_restrictions->policy);
+ free(ap->ae_restrictions);
+ }
+ np = ap->ae_next;
+ free(ap);
+ }
+ acl_list_head = acl_list_tail = (aent_t *) NULL;
+ acl_inited = 0;
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_free_entries()\n"));
+}
+
+/*
+ * kadm5int_acl_load_acl_file() - Open and parse the ACL file.
+ */
+static int
+kadm5int_acl_load_acl_file()
+{
+ FILE *afp;
+ char *alinep;
+ aent_t **aentpp;
+ int alineno;
+ int retval = 1;
+
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_load_acl_file()\n"));
+ /* Open the ACL file for read */
+ afp = fopen(acl_acl_file, "r");
+ if (afp) {
+ set_cloexec_file(afp);
+ alineno = 1;
+ aentpp = &acl_list_head;
+
+ /* Get a non-comment line */
+ while ((alinep = kadm5int_acl_get_line(afp, &alineno))) {
+ /* Parse it */
+ *aentpp = kadm5int_acl_parse_line(alinep);
+ /* If syntax error, then fall out */
+ if (!*aentpp) {
+ krb5_klog_syslog(LOG_ERR, _(acl_syn_err_msg),
+ acl_acl_file, alineno, alinep);
+ retval = 0;
+ break;
+ }
+ acl_list_tail = *aentpp;
+ aentpp = &(*aentpp)->ae_next;
+ }
+
+ fclose(afp);
+
+ if (acl_catchall_entry) {
+ *aentpp = kadm5int_acl_parse_line(acl_catchall_entry);
+ if (*aentpp) {
+ acl_list_tail = *aentpp;
+ }
+ else {
+ retval = 0;
+ DPRINT(DEBUG_OPERATION, acl_debug_level,
+ ("> catchall acl entry (%s) load failed\n",
+ acl_catchall_entry));
+ }
+ }
+ }
+ else {
+ krb5_klog_syslog(LOG_ERR, _(acl_cantopen_msg),
+ error_message(errno), acl_acl_file);
+ if (acl_catchall_entry &&
+ (acl_list_head = kadm5int_acl_parse_line(acl_catchall_entry))) {
+ acl_list_tail = acl_list_head;
+ }
+ else {
+ retval = 0;
+ DPRINT(DEBUG_OPERATION, acl_debug_level,
+ ("> catchall acl entry (%s) load failed\n",
+ acl_catchall_entry));
+ }
+ }
+
+ if (!retval) {
+ kadm5int_acl_free_entries();
+ }
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("X kadm5int_acl_load_acl_file() = %d\n", retval));
+ return(retval);
+}
+
+/*
+ * kadm5int_acl_match_data() - See if two data entries match.
+ *
+ * Wildcarding is only supported for a whole component.
+ */
+static krb5_boolean
+kadm5int_acl_match_data(const krb5_data *e1, const krb5_data *e2,
+ int targetflag, wildstate_t *ws)
+{
+ krb5_boolean retval;
+
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("* acl_match_entry(%s, %s)\n", e1->data, e2->data));
+ retval = 0;
+ if (!strncmp(e1->data, "*", e1->length)) {
+ retval = 1;
+ if (ws && !targetflag) {
+ if (ws->nwild >= 9) {
+ DPRINT(DEBUG_ACL, acl_debug_level,
+ ("Too many wildcards in ACL entry.\n"));
+ }
+ else
+ ws->backref[ws->nwild++] = e2;
+ }
+ }
+ else if (ws && targetflag && (e1->length == 2) && (e1->data[0] == '*') &&
+ (e1->data[1] >= '1') && (e1->data[1] <= '9')) {
+ int n = e1->data[1] - '1';
+ if (n >= ws->nwild) {
+ DPRINT(DEBUG_ACL, acl_debug_level,
+ ("Too many backrefs in ACL entry.\n"));
+ }
+ else if ((ws->backref[n]->length == e2->length) &&
+ (!strncmp(ws->backref[n]->data, e2->data, e2->length)))
+ retval = 1;
+
+ }
+ else {
+ if ((e1->length == e2->length) &&
+ (!strncmp(e1->data, e2->data, e1->length)))
+ retval = 1;
+ }
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval));
+ return(retval);
+}
+
+/*
+ * kadm5int_acl_find_entry() - Find a matching entry.
+ */
+static aent_t *
+kadm5int_acl_find_entry(krb5_context kcontext, krb5_const_principal principal,
+ krb5_const_principal dest_princ)
+{
+ aent_t *entry;
+ krb5_error_code kret;
+ int i;
+ int matchgood;
+ wildstate_t state;
+
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_find_entry()\n"));
+ for (entry=acl_list_head; entry; entry = entry->ae_next) {
+ memset(&state, 0, sizeof(state));
+ if (entry->ae_name_bad)
+ continue;
+ if (!strcmp(entry->ae_name, "*")) {
+ DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n"));
+ matchgood = 1;
+ }
+ else {
+ if (!entry->ae_principal && !entry->ae_name_bad) {
+ kret = krb5_parse_name(kcontext,
+ entry->ae_name,
+ &entry->ae_principal);
+ if (kret)
+ entry->ae_name_bad = 1;
+ }
+ if (entry->ae_name_bad) {
+ DPRINT(DEBUG_ACL, acl_debug_level,
+ ("Bad ACL entry %s\n", entry->ae_name));
+ continue;
+ }
+ matchgood = 0;
+ if (kadm5int_acl_match_data(&entry->ae_principal->realm,
+ &principal->realm, 0, (wildstate_t *)0) &&
+ (entry->ae_principal->length == principal->length)) {
+ matchgood = 1;
+ for (i=0; i<principal->length; i++) {
+ if (!kadm5int_acl_match_data(&entry->ae_principal->data[i],
+ &principal->data[i], 0, &state)) {
+ matchgood = 0;
+ break;
+ }
+ }
+ }
+ }
+ if (!matchgood)
+ continue;
+
+ /* We've matched the principal. If we have a target, then try it */
+ if (entry->ae_target && strcmp(entry->ae_target, "*")) {
+ if (!entry->ae_target_princ && !entry->ae_target_bad) {
+ kret = krb5_parse_name(kcontext, entry->ae_target,
+ &entry->ae_target_princ);
+ if (kret)
+ entry->ae_target_bad = 1;
+ }
+ if (entry->ae_target_bad) {
+ DPRINT(DEBUG_ACL, acl_debug_level,
+ ("Bad target in ACL entry for %s\n", entry->ae_name));
+ entry->ae_name_bad = 1;
+ continue;
+ }
+ if (!dest_princ)
+ matchgood = 0;
+ else if (entry->ae_target_princ && dest_princ) {
+ if (kadm5int_acl_match_data(&entry->ae_target_princ->realm,
+ &dest_princ->realm, 1, (wildstate_t *)0) &&
+ (entry->ae_target_princ->length == dest_princ->length)) {
+ for (i=0; i<dest_princ->length; i++) {
+ if (!kadm5int_acl_match_data(&entry->ae_target_princ->data[i],
+ &dest_princ->data[i], 1, &state)) {
+ matchgood = 0;
+ break;
+ }
+ }
+ }
+ else
+ matchgood = 0;
+ }
+ }
+ if (!matchgood)
+ continue;
+
+ if (entry->ae_restriction_string
+ && !entry->ae_restriction_bad
+ && !entry->ae_restrictions
+ && kadm5int_acl_parse_restrictions(entry->ae_restriction_string,
+ &entry->ae_restrictions)) {
+ DPRINT(DEBUG_ACL, acl_debug_level,
+ ("Bad restrictions in ACL entry for %s\n", entry->ae_name));
+ entry->ae_restriction_bad = 1;
+ }
+ if (entry->ae_restriction_bad) {
+ entry->ae_name_bad = 1;
+ continue;
+ }
+ break;
+ }
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_find_entry()=%x\n",entry));
+ return(entry);
+}
+
+/*
+ * kadm5int_acl_init() - Initialize ACL context.
+ */
+krb5_error_code
+kadm5int_acl_init(kcontext, debug_level, acl_file)
+ krb5_context kcontext;
+ int debug_level;
+ char *acl_file;
+{
+ krb5_error_code kret;
+
+ kret = 0;
+ acl_debug_level = debug_level;
+ DPRINT(DEBUG_CALLS, acl_debug_level,
+ ("* kadm5int_acl_init(afile=%s)\n",
+ ((acl_file) ? acl_file : "(null)")));
+ acl_acl_file = (acl_file) ? acl_file : (char *) KRB5_DEFAULT_ADMIN_ACL;
+ acl_inited = kadm5int_acl_load_acl_file();
+
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_init() = %d\n", kret));
+ return(kret);
+}
+
+/*
+ * kadm5int_acl_finish - Terminate ACL context.
+ */
+void
+kadm5int_acl_finish(kcontext, debug_level)
+ krb5_context kcontext;
+ int debug_level;
+{
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_finish()\n"));
+ kadm5int_acl_free_entries();
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_finish()\n"));
+}
+
+/*
+ * kadm5int_acl_check_krb() - Is this operation permitted for this principal?
+ */
+krb5_boolean
+kadm5int_acl_check_krb(kcontext, caller_princ, opmask, principal, restrictions)
+ krb5_context kcontext;
+ krb5_const_principal caller_princ;
+ krb5_int32 opmask;
+ krb5_const_principal principal;
+ restriction_t **restrictions;
+{
+ krb5_boolean retval;
+ aent_t *aentry;
+
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n"));
+
+ retval = FALSE;
+
+ aentry = kadm5int_acl_find_entry(kcontext, caller_princ, principal);
+ if (aentry) {
+ if ((aentry->ae_op_allowed & opmask) == opmask) {
+ retval = TRUE;
+ if (restrictions) {
+ *restrictions =
+ (aentry->ae_restrictions && aentry->ae_restrictions->mask)
+ ? aentry->ae_restrictions
+ : (restriction_t *) NULL;
+ }
+ }
+ }
+
+ DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n",
+ retval));
+ return retval;
+}
+
+/*
+ * kadm5int_acl_check() - Is this operation permitted for this principal?
+ * this code used not to be based on gssapi. In order
+ * to minimize porting hassles, I've put all the
+ * gssapi hair in this function. This might not be
+ * the best medium-term solution. (The best long-term
+ * solution is, of course, a real authorization service.)
+ */
+krb5_boolean
+kadm5int_acl_check(kcontext, caller, opmask, principal, restrictions)
+ krb5_context kcontext;
+ gss_name_t caller;
+ krb5_int32 opmask;
+ krb5_principal principal;
+ restriction_t **restrictions;
+{
+ krb5_boolean retval;
+ gss_buffer_desc caller_buf;
+ gss_OID caller_oid;
+ OM_uint32 emin;
+ krb5_error_code code;
+ krb5_principal caller_princ;
+
+ if (GSS_ERROR(gss_display_name(&emin, caller, &caller_buf, &caller_oid)))
+ return FALSE;
+
+ code = krb5_parse_name(kcontext, (char *) caller_buf.value,
+ &caller_princ);
+
+ gss_release_buffer(&emin, &caller_buf);
+
+ if (code != 0)
+ return FALSE;
+
+ retval = kadm5int_acl_check_krb(kcontext, caller_princ,
+ opmask, principal, restrictions);
+
+ krb5_free_principal(kcontext, caller_princ);
+
+ return retval;
+}
+
+kadm5_ret_t
+kadm5_get_privs(void *server_handle, long *privs)
+{
+ CHECK_HANDLE(server_handle);
+
+ /* this is impossible to do with the current interface. For now,
+ return all privs, which will confuse some clients, but not
+ deny any access to users of "smart" clients which try to cache */
+
+ *privs = ~0;
+
+ return KADM5_OK;
+}