2N/A/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2N/A
2N/A/*
2N/A * Copyright (c) 2002, 2011, Oracle and/or its affiliates. All rights reserved.
2N/A */
2N/A
2N/A/*
2N/A * lib/kadm5/srv/server_acl.c
2N/A *
2N/A * Copyright 1995-2004, 2007, 2008 by the Massachusetts Institute of Technology.
2N/A * All Rights Reserved.
2N/A *
2N/A * Export of this software from the United States of America may
2N/A * require a specific license from the United States Government.
2N/A * It is the responsibility of any person or organization contemplating
2N/A * export to obtain such a license before exporting.
2N/A *
2N/A * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
2N/A * distribute this software and its documentation for any purpose and
2N/A * without fee is hereby granted, provided that the above copyright
2N/A * notice appear in all copies and that both that copyright notice and
2N/A * this permission notice appear in supporting documentation, and that
2N/A * the name of M.I.T. not be used in advertising or publicity pertaining
2N/A * to distribution of the software without specific, written prior
2N/A * permission. Furthermore if you modify this software you must label
2N/A * your software as modified software and not distribute it in such a
2N/A * fashion that it might be confused with the original M.I.T. software.
2N/A * M.I.T. makes no representations about the suitability of
2N/A * this software for any purpose. It is provided "as is" without express
2N/A * or implied warranty.
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
2N/A *
2N/A * Openvision retains the copyright to derivative works of
2N/A * this source code. Do *NOT* create a derivative of this
2N/A * source code before consulting with your legal department.
2N/A * Do *NOT* integrate *ANY* of this source code into another
2N/A * product before consulting with your legal department.
2N/A *
2N/A * For further information, read the top-level Openvision
2N/A * copyright which is contained in the top-level MIT Kerberos
2N/A * copyright.
2N/A *
2N/A * WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING
2N/A *
2N/A */
2N/A
2N/A/*
2N/A * srv_acl.c - Handle Kerberos ACL related functions.
2N/A */
2N/A#include <stdio.h>
2N/A#include <syslog.h>
2N/A#include <sys/param.h>
2N/A#include "k5-int.h"
2N/A#include <gssapi_krb5.h>
2N/A#include <kadm5/server_internal.h>
2N/A#include <kadm5/admin.h>
2N/A#include <adm_proto.h> /* SUNWresync121 XXX */
2N/A#include "server_acl.h"
2N/A#include <ctype.h>
2N/A#include <libintl.h> /* SUNWresync121 XXX */
2N/A
2N/Atypedef struct _acl_op_table {
2N/A char ao_op;
2N/A krb5_int32 ao_mask;
2N/A} aop_t;
2N/A
2N/Atypedef struct _acl_entry {
2N/A struct _acl_entry *ae_next;
2N/A char *ae_name;
2N/A krb5_boolean ae_name_bad;
2N/A krb5_principal ae_principal;
2N/A krb5_int32 ae_op_allowed;
2N/A char *ae_target;
2N/A krb5_boolean ae_target_bad;
2N/A krb5_principal ae_target_princ;
2N/A char *ae_restriction_string;
2N/A /* eg: "-maxlife 3h -service +proxiable" */
2N/A krb5_boolean ae_restriction_bad;
2N/A restriction_t *ae_restrictions;
2N/A} aent_t;
2N/A
2N/Astatic const aop_t acl_op_table[] = {
2N/A { 'a', ACL_ADD },
2N/A { 'd', ACL_DELETE },
2N/A { 'm', ACL_MODIFY },
2N/A { 'c', ACL_CHANGEPW },
2N/A { 'i', ACL_INQUIRE },
2N/A { 'l', ACL_LIST },
2N/A { 'p', ACL_IPROP },
2N/A { 's', ACL_SETKEY },
2N/A { 'u', ACL_MIGRATE }, /* pam_krb5_migrate */
2N/A { 'x', ACL_ALL_MASK },
2N/A { '*', ACL_ALL_MASK },
2N/A { '\0', 0 }
2N/A};
2N/A
2N/Atypedef struct _wildstate {
2N/A int nwild;
2N/A krb5_data *backref[9];
2N/A} wildstate_t;
2N/A
2N/Astatic aent_t *acl_list_head = (aent_t *) NULL;
2N/Astatic aent_t *acl_list_tail = (aent_t *) NULL;
2N/A
2N/Astatic const char *acl_acl_file = (char *) NULL;
2N/Astatic int acl_inited = 0;
2N/Astatic int acl_debug_level = 0;
2N/A/*
2N/A * This is the catchall entry. If nothing else appropriate is found, or in
2N/A * the case where the ACL file is not present, this entry controls what can
2N/A * be done.
2N/A */
2N/Astatic const char *acl_catchall_entry = NULL;
2N/A
2N/A/* Solaris Kerberos */
2N/A#define acl_line2long_msg dgettext(TEXT_DOMAIN, \
2N/A "%s: line %d too long, truncated\n")
2N/A#define acl_op_bad_msg dgettext(TEXT_DOMAIN, \
2N/A "Unrecognized ACL operation '%c' in %s\n")
2N/A#define acl_syn_err_msg dgettext(TEXT_DOMAIN, \
2N/A "%s: syntax error at line %d <%10s...>\n")
2N/A#define acl_cantopen_msg dgettext(TEXT_DOMAIN, \
2N/A "\007cannot open ACL file")
2N/A
2N/A
2N/A/*
2N/A * kadm5int_acl_get_line() - Get a line from the ACL file.
2N/A * Lines ending with \ are continued on the next line
2N/A */
2N/Astatic char *
2N/Akadm5int_acl_get_line(fp, lnp)
2N/A FILE *fp;
2N/A int *lnp; /* caller should set to 1 before first call */
2N/A{
2N/A int i, domore;
2N/A static int line_incr = 0;
2N/A static char acl_buf[BUFSIZ];
2N/A
2N/A *lnp += line_incr;
2N/A line_incr = 0;
2N/A for (domore = 1; domore && !feof(fp); ) {
2N/A /* Copy in the line, with continuations */
2N/A for (i=0; ((i < sizeof acl_buf) && !feof(fp)); i++ ) {
2N/A int byte;
2N/A byte = fgetc(fp);
2N/A acl_buf[i] = byte;
2N/A if (byte == (char)EOF) {
2N/A if (i > 0 && acl_buf[i-1] == '\\')
2N/A i--;
2N/A break; /* it gets nulled-out below */
2N/A }
2N/A else if (acl_buf[i] == '\n') {
2N/A if (i == 0 || acl_buf[i-1] != '\\')
2N/A break; /* empty line or normal end of line */
2N/A else {
2N/A i -= 2; /* back up over "\\\n" and continue */
2N/A line_incr++;
2N/A }
2N/A }
2N/A }
2N/A /* Check if we exceeded our buffer size */
2N/A if (i == sizeof acl_buf && (i--, !feof(fp))) {
2N/A int c1 = acl_buf[i], c2;
2N/A
2N/A krb5_klog_syslog(LOG_ERR, acl_line2long_msg, acl_acl_file, *lnp);
2N/A while ((c2 = fgetc(fp)) != EOF) {
2N/A if (c2 == '\n') {
2N/A if (c1 != '\\')
2N/A break;
2N/A line_incr++;
2N/A }
2N/A c1 = c2;
2N/A }
2N/A }
2N/A acl_buf[i] = '\0';
2N/A if (acl_buf[0] == (char) EOF) /* ptooey */
2N/A acl_buf[0] = '\0';
2N/A else
2N/A line_incr++;
2N/A if ((acl_buf[0] != '#') && (acl_buf[0] != '\0'))
2N/A domore = 0;
2N/A }
2N/A if (domore || (strlen(acl_buf) == 0))
2N/A return((char *) NULL);
2N/A else
2N/A return(acl_buf);
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_parse_line() - Parse the contents of an ACL line.
2N/A */
2N/Astatic aent_t *
2N/Akadm5int_acl_parse_line(lp)
2N/A const char *lp;
2N/A{
2N/A static char acle_principal[BUFSIZ];
2N/A static char acle_ops[BUFSIZ];
2N/A static char acle_object[BUFSIZ];
2N/A static char acle_restrictions[BUFSIZ];
2N/A aent_t *acle;
2N/A char *op;
2N/A int t, found, opok, nmatch;
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("* kadm5int_acl_parse_line(line=%20s)\n", lp));
2N/A /*
2N/A * Format is still simple:
2N/A * entry ::= [<whitespace>] <principal> <whitespace> <opstring>
2N/A * [<whitespace> <target> [<whitespace> <restrictions>
2N/A * [<whitespace>]]]
2N/A */
2N/A acle = (aent_t *) NULL;
2N/A acle_object[0] = '\0';
2N/A nmatch = sscanf(lp, "%s %s %s %[^\n]", acle_principal, acle_ops,
2N/A acle_object, acle_restrictions);
2N/A if (nmatch >= 2) {
2N/A acle = (aent_t *) malloc(sizeof(aent_t));
2N/A if (acle) {
2N/A acle->ae_next = (aent_t *) NULL;
2N/A acle->ae_op_allowed = (krb5_int32) 0;
2N/A acle->ae_target =
2N/A (nmatch >= 3) ? strdup(acle_object) : (char *) NULL;
2N/A acle->ae_target_bad = 0;
2N/A acle->ae_target_princ = (krb5_principal) NULL;
2N/A opok = 1;
2N/A for (op=acle_ops; *op; op++) {
2N/A char rop;
2N/A
2N/A rop = (isupper((unsigned char) *op)) ? tolower((unsigned char) *op) : *op;
2N/A found = 0;
2N/A for (t=0; acl_op_table[t].ao_op; t++) {
2N/A if (rop == acl_op_table[t].ao_op) {
2N/A found = 1;
2N/A if (rop == *op)
2N/A acle->ae_op_allowed |= acl_op_table[t].ao_mask;
2N/A else
2N/A acle->ae_op_allowed &= ~acl_op_table[t].ao_mask;
2N/A }
2N/A }
2N/A if (!found) {
2N/A krb5_klog_syslog(LOG_ERR, acl_op_bad_msg, *op, lp);
2N/A opok = 0;
2N/A }
2N/A }
2N/A if (opok) {
2N/A acle->ae_name = strdup(acle_principal);
2N/A if (acle->ae_name) {
2N/A acle->ae_principal = (krb5_principal) NULL;
2N/A acle->ae_name_bad = 0;
2N/A DPRINT(DEBUG_ACL, acl_debug_level,
2N/A ("A ACL entry %s -> opmask %x\n",
2N/A acle->ae_name, acle->ae_op_allowed));
2N/A }
2N/A else {
2N/A if (acle->ae_target)
2N/A free(acle->ae_target);
2N/A free(acle);
2N/A acle = (aent_t *) NULL;
2N/A }
2N/A }
2N/A else {
2N/A if (acle->ae_target)
2N/A free(acle->ae_target);
2N/A free(acle);
2N/A acle = (aent_t *) NULL;
2N/A }
2N/A
2N/A if (acle) {
2N/A if ( nmatch >= 4 ) {
2N/A char *trailing;
2N/A
2N/A trailing = &acle_restrictions[strlen(acle_restrictions)-1];
2N/A while ( isspace((int) *trailing) )
2N/A trailing--;
2N/A trailing[1] = '\0';
2N/A acle->ae_restriction_string =
2N/A strdup(acle_restrictions);
2N/A }
2N/A else {
2N/A acle->ae_restriction_string = (char *) NULL;
2N/A }
2N/A acle->ae_restriction_bad = 0;
2N/A acle->ae_restrictions = (restriction_t *) NULL;
2N/A }
2N/A }
2N/A }
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("X kadm5int_acl_parse_line() = %x\n", (long) acle));
2N/A return(acle);
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_parse_restrictions() - Parse optional restrictions field
2N/A *
2N/A * Allowed restrictions are:
2N/A * [+-]flagname (recognized by krb5_string_to_flags)
2N/A * flag is forced to indicated value
2N/A * -clearpolicy policy is forced clear
2N/A * -policy pol policy is forced to be "pol"
2N/A * -{expire,pwexpire,maxlife,maxrenewlife} deltat
2N/A * associated value will be forced to
2N/A * MIN(deltat, requested value)
2N/A *
2N/A * Returns: 0 on success, or system errors
2N/A */
2N/Astatic krb5_error_code
2N/Akadm5int_acl_parse_restrictions(s, rpp)
2N/A char *s;
2N/A restriction_t **rpp;
2N/A{
2N/A char *sp = NULL, *tp, *ap, *save;
2N/A static const char *delims = "\t\n\f\v\r ,";
2N/A krb5_deltat dt;
2N/A krb5_flags flag;
2N/A krb5_error_code code;
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("* kadm5int_acl_parse_restrictions(s=%20s, rpp=0x%08x)\n", s, (long)rpp));
2N/A
2N/A *rpp = (restriction_t *) NULL;
2N/A code = 0;
2N/A if (s) {
2N/A if (!(sp = strdup(s)) /* Don't munge the original */
2N/A || !(*rpp = (restriction_t *) malloc(sizeof(restriction_t)))) {
2N/A code = ENOMEM;
2N/A } else {
2N/A memset(*rpp, 0, sizeof(**rpp));
2N/A for (tp = strtok_r(sp, delims, &save); tp;
2N/A tp = strtok_r(NULL, delims, &save)) {
2N/A flag = 0;
2N/A if (!krb5_string_to_flags(tp, "+", "-", &flag)) {
2N/A /* OK, but was it in the positive or negative sense? */
2N/A if (flag) {
2N/A (*rpp)->require_attrs |= flag;
2N/A } else {
2N/A flag = ~0;
2N/A (void) krb5_string_to_flags(tp, "+", "-", &flag);
2N/A (*rpp)->forbid_attrs |= ~flag;
2N/A }
2N/A (*rpp)->mask |= KADM5_ATTRIBUTES;
2N/A } else if (!strcmp(tp, "-clearpolicy")) {
2N/A (*rpp)->mask |= KADM5_POLICY_CLR;
2N/A } else {
2N/A /* everything else needs an argument ... */
2N/A if (!(ap = strtok_r(NULL, delims, &save))) {
2N/A code = EINVAL;
2N/A break;
2N/A }
2N/A if (!strcmp(tp, "-policy")) {
2N/A if (!((*rpp)->policy = strdup(ap))) {
2N/A code = ENOMEM;
2N/A break;
2N/A }
2N/A (*rpp)->mask |= KADM5_POLICY;
2N/A } else {
2N/A /* all other arguments must be a deltat ... */
2N/A if (krb5_string_to_deltat(ap, &dt)) {
2N/A code = EINVAL;
2N/A break;
2N/A }
2N/A if (!strcmp(tp, "-expire")) {
2N/A (*rpp)->princ_lifetime = dt;
2N/A (*rpp)->mask |= KADM5_PRINC_EXPIRE_TIME;
2N/A } else if (!strcmp(tp, "-pwexpire")) {
2N/A (*rpp)->pw_lifetime = dt;
2N/A (*rpp)->mask |= KADM5_PW_EXPIRATION;
2N/A } else if (!strcmp(tp, "-maxlife")) {
2N/A (*rpp)->max_life = dt;
2N/A (*rpp)->mask |= KADM5_MAX_LIFE;
2N/A } else if (!strcmp(tp, "-maxrenewlife")) {
2N/A (*rpp)->max_renewable_life = dt;
2N/A (*rpp)->mask |= KADM5_MAX_RLIFE;
2N/A } else {
2N/A code = EINVAL;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A }
2N/A if (sp)
2N/A free(sp);
2N/A if (*rpp && code) {
2N/A if ((*rpp)->policy)
2N/A free((*rpp)->policy);
2N/A free(*rpp);
2N/A *rpp = (restriction_t *) NULL;
2N/A }
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("X kadm5int_acl_parse_restrictions() = %d, mask=0x%08x\n",
2N/A code, (*rpp) ? (*rpp)->mask : 0));
2N/A return code;
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_impose_restrictions() - impose restrictions, modifying *recp, *maskp
2N/A *
2N/A * Returns: 0 on success;
2N/A * malloc or timeofday errors
2N/A */
2N/Akrb5_error_code
2N/Akadm5int_acl_impose_restrictions(kcontext, recp, maskp, rp)
2N/A krb5_context kcontext;
2N/A kadm5_principal_ent_rec *recp;
2N/A long *maskp;
2N/A restriction_t *rp;
2N/A{
2N/A krb5_error_code code;
2N/A krb5_int32 now;
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("* kadm5int_acl_impose_restrictions(..., *maskp=0x%08x, rp=0x%08x)\n",
2N/A *maskp, (long)rp));
2N/A if (!rp)
2N/A return 0;
2N/A if (rp->mask & (KADM5_PRINC_EXPIRE_TIME|KADM5_PW_EXPIRATION))
2N/A if ((code = krb5_timeofday(kcontext, &now)))
2N/A return code;
2N/A
2N/A if (rp->mask & KADM5_ATTRIBUTES) {
2N/A recp->attributes |= rp->require_attrs;
2N/A recp->attributes &= ~(rp->forbid_attrs);
2N/A *maskp |= KADM5_ATTRIBUTES;
2N/A }
2N/A if (rp->mask & KADM5_POLICY_CLR) {
2N/A *maskp &= ~KADM5_POLICY;
2N/A *maskp |= KADM5_POLICY_CLR;
2N/A } else if (rp->mask & KADM5_POLICY) {
2N/A if (recp->policy && strcmp(recp->policy, rp->policy)) {
2N/A free(recp->policy);
2N/A recp->policy = (char *) NULL;
2N/A }
2N/A if (!recp->policy) {
2N/A recp->policy = strdup(rp->policy); /* XDR will free it */
2N/A if (!recp->policy)
2N/A return ENOMEM;
2N/A }
2N/A *maskp |= KADM5_POLICY;
2N/A }
2N/A if (rp->mask & KADM5_PRINC_EXPIRE_TIME) {
2N/A if (!(*maskp & KADM5_PRINC_EXPIRE_TIME)
2N/A || (recp->princ_expire_time > (now + rp->princ_lifetime)))
2N/A recp->princ_expire_time = now + rp->princ_lifetime;
2N/A *maskp |= KADM5_PRINC_EXPIRE_TIME;
2N/A }
2N/A if (rp->mask & KADM5_PW_EXPIRATION) {
2N/A if (!(*maskp & KADM5_PW_EXPIRATION)
2N/A || (recp->pw_expiration > (now + rp->pw_lifetime)))
2N/A recp->pw_expiration = now + rp->pw_lifetime;
2N/A *maskp |= KADM5_PW_EXPIRATION;
2N/A }
2N/A if (rp->mask & KADM5_MAX_LIFE) {
2N/A if (!(*maskp & KADM5_MAX_LIFE)
2N/A || (recp->max_life > rp->max_life))
2N/A recp->max_life = rp->max_life;
2N/A *maskp |= KADM5_MAX_LIFE;
2N/A }
2N/A if (rp->mask & KADM5_MAX_RLIFE) {
2N/A if (!(*maskp & KADM5_MAX_RLIFE)
2N/A || (recp->max_renewable_life > rp->max_renewable_life))
2N/A recp->max_renewable_life = rp->max_renewable_life;
2N/A *maskp |= KADM5_MAX_RLIFE;
2N/A }
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("X kadm5int_acl_impose_restrictions() = 0, *maskp=0x%08x\n", *maskp));
2N/A return 0;
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_free_entries() - Free all ACL entries.
2N/A */
2N/Astatic void
2N/Akadm5int_acl_free_entries()
2N/A{
2N/A aent_t *ap;
2N/A aent_t *np;
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_free_entries()\n"));
2N/A for (ap=acl_list_head; ap; ap = np) {
2N/A if (ap->ae_name)
2N/A free(ap->ae_name);
2N/A if (ap->ae_principal)
2N/A krb5_free_principal((krb5_context) NULL, ap->ae_principal);
2N/A if (ap->ae_target)
2N/A free(ap->ae_target);
2N/A if (ap->ae_target_princ)
2N/A krb5_free_principal((krb5_context) NULL, ap->ae_target_princ);
2N/A if (ap->ae_restriction_string)
2N/A free(ap->ae_restriction_string);
2N/A if (ap->ae_restrictions) {
2N/A if (ap->ae_restrictions->policy)
2N/A free(ap->ae_restrictions->policy);
2N/A free(ap->ae_restrictions);
2N/A }
2N/A np = ap->ae_next;
2N/A free(ap);
2N/A }
2N/A acl_list_head = acl_list_tail = (aent_t *) NULL;
2N/A acl_inited = 0;
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_free_entries()\n"));
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_load_acl_file() - Open and parse the ACL file.
2N/A */
2N/Astatic int
2N/Akadm5int_acl_load_acl_file()
2N/A{
2N/A FILE *afp;
2N/A char *alinep;
2N/A aent_t **aentpp;
2N/A int alineno;
2N/A int retval = 1;
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_load_acl_file()\n"));
2N/A /* Open the ACL file for read */
2N/A afp = fopen(acl_acl_file, "rF"); /* Solaris Kerberos */
2N/A if (afp) {
2N/A set_cloexec_file(afp);
2N/A alineno = 1;
2N/A aentpp = &acl_list_head;
2N/A
2N/A /* Get a non-comment line */
2N/A while ((alinep = kadm5int_acl_get_line(afp, &alineno))) {
2N/A /* Parse it */
2N/A *aentpp = kadm5int_acl_parse_line(alinep);
2N/A /* If syntax error, then fall out */
2N/A if (!*aentpp) {
2N/A krb5_klog_syslog(LOG_ERR, acl_syn_err_msg,
2N/A acl_acl_file, alineno, alinep);
2N/A retval = 0;
2N/A break;
2N/A }
2N/A acl_list_tail = *aentpp;
2N/A aentpp = &(*aentpp)->ae_next;
2N/A }
2N/A
2N/A fclose(afp);
2N/A
2N/A if (acl_catchall_entry) {
2N/A *aentpp = kadm5int_acl_parse_line(acl_catchall_entry);
2N/A if (*aentpp) {
2N/A acl_list_tail = *aentpp;
2N/A }
2N/A else {
2N/A retval = 0;
2N/A DPRINT(DEBUG_OPERATION, acl_debug_level,
2N/A ("> catchall acl entry (%s) load failed\n",
2N/A acl_catchall_entry));
2N/A }
2N/A }
2N/A }
2N/A else {
2N/A krb5_klog_syslog(LOG_ERR, acl_cantopen_msg,
2N/A error_message(errno), acl_acl_file);
2N/A if (acl_catchall_entry &&
2N/A (acl_list_head = kadm5int_acl_parse_line(acl_catchall_entry))) {
2N/A acl_list_tail = acl_list_head;
2N/A }
2N/A else {
2N/A retval = 0;
2N/A DPRINT(DEBUG_OPERATION, acl_debug_level,
2N/A ("> catchall acl entry (%s) load failed\n",
2N/A acl_catchall_entry));
2N/A }
2N/A }
2N/A
2N/A if (!retval) {
2N/A kadm5int_acl_free_entries();
2N/A }
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("X kadm5int_acl_load_acl_file() = %d\n", retval));
2N/A return(retval);
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_match_data() - See if two data entries match.
2N/A *
2N/A * Wildcarding is only supported for a whole component.
2N/A */
2N/Astatic krb5_boolean
2N/Akadm5int_acl_match_data(e1, e2, targetflag, ws)
2N/A krb5_data *e1, *e2;
2N/A int targetflag;
2N/A wildstate_t *ws;
2N/A{
2N/A krb5_boolean retval;
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("* acl_match_entry(%s, %s)\n", e1->data, e2->data));
2N/A retval = 0;
2N/A if (!strncmp(e1->data, "*", e1->length)) {
2N/A retval = 1;
2N/A if (ws && !targetflag) {
2N/A if (ws->nwild >= 9) {
2N/A /* Solaris Kerberos */
2N/A DPRINT(DEBUG_ACL, acl_debug_level,
2N/A ("Too many wildcards in ACL entry %s\n", e1->data));
2N/A }
2N/A else
2N/A ws->backref[ws->nwild++] = e2;
2N/A }
2N/A }
2N/A else if (ws && targetflag && (e1->length == 2) && (e1->data[0] == '*') &&
2N/A (e1->data[1] >= '1') && (e1->data[1] <= '9')) {
2N/A int n = e1->data[1] - '1';
2N/A if (n >= ws->nwild) {
2N/A /* Solaris Kerberos */
2N/A DPRINT(DEBUG_ACL, acl_debug_level,
2N/A ("Too many backrefs in ACL entry %s\n", e1->data));
2N/A }
2N/A else if ((ws->backref[n]->length == e2->length) &&
2N/A (!strncmp(ws->backref[n]->data, e2->data, e2->length)))
2N/A retval = 1;
2N/A
2N/A }
2N/A else {
2N/A if ((e1->length == e2->length) &&
2N/A (!strncmp(e1->data, e2->data, e1->length)))
2N/A retval = 1;
2N/A }
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_match_entry()=%d\n",retval));
2N/A return(retval);
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_find_entry() - Find a matching entry.
2N/A */
2N/Astatic aent_t *
2N/Akadm5int_acl_find_entry(kcontext, principal, dest_princ)
2N/A krb5_context kcontext;
2N/A krb5_principal principal;
2N/A krb5_principal dest_princ;
2N/A{
2N/A aent_t *entry;
2N/A krb5_error_code kret;
2N/A int i;
2N/A int matchgood;
2N/A wildstate_t state;
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_find_entry()\n"));
2N/A memset(&state, 0, sizeof state);
2N/A for (entry=acl_list_head; entry; entry = entry->ae_next) {
2N/A if (entry->ae_name_bad)
2N/A continue;
2N/A if (!strcmp(entry->ae_name, "*")) {
2N/A DPRINT(DEBUG_ACL, acl_debug_level, ("A wildcard ACL match\n"));
2N/A matchgood = 1;
2N/A }
2N/A else {
2N/A if (!entry->ae_principal && !entry->ae_name_bad) {
2N/A kret = krb5_parse_name(kcontext,
2N/A entry->ae_name,
2N/A &entry->ae_principal);
2N/A if (kret)
2N/A entry->ae_name_bad = 1;
2N/A }
2N/A if (entry->ae_name_bad) {
2N/A DPRINT(DEBUG_ACL, acl_debug_level,
2N/A ("Bad ACL entry %s\n", entry->ae_name));
2N/A continue;
2N/A }
2N/A matchgood = 0;
2N/A if (kadm5int_acl_match_data(&entry->ae_principal->realm,
2N/A &principal->realm, 0, (wildstate_t *)0) &&
2N/A (entry->ae_principal->length == principal->length)) {
2N/A matchgood = 1;
2N/A for (i=0; i<principal->length; i++) {
2N/A if (!kadm5int_acl_match_data(&entry->ae_principal->data[i],
2N/A &principal->data[i], 0, &state)) {
2N/A matchgood = 0;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A }
2N/A if (!matchgood)
2N/A continue;
2N/A
2N/A /* We've matched the principal. If we have a target, then try it */
2N/A if (entry->ae_target && strcmp(entry->ae_target, "*")) {
2N/A if (!entry->ae_target_princ && !entry->ae_target_bad) {
2N/A kret = krb5_parse_name(kcontext, entry->ae_target,
2N/A &entry->ae_target_princ);
2N/A if (kret)
2N/A entry->ae_target_bad = 1;
2N/A }
2N/A if (entry->ae_target_bad) {
2N/A DPRINT(DEBUG_ACL, acl_debug_level,
2N/A ("Bad target in ACL entry for %s\n", entry->ae_name));
2N/A entry->ae_name_bad = 1;
2N/A continue;
2N/A }
2N/A if (!dest_princ)
2N/A matchgood = 0;
2N/A else if (entry->ae_target_princ && dest_princ) {
2N/A if (kadm5int_acl_match_data(&entry->ae_target_princ->realm,
2N/A &dest_princ->realm, 1, (wildstate_t *)0) &&
2N/A (entry->ae_target_princ->length == dest_princ->length)) {
2N/A for (i=0; i<dest_princ->length; i++) {
2N/A if (!kadm5int_acl_match_data(&entry->ae_target_princ->data[i],
2N/A &dest_princ->data[i], 1, &state)) {
2N/A matchgood = 0;
2N/A break;
2N/A }
2N/A }
2N/A }
2N/A else
2N/A matchgood = 0;
2N/A }
2N/A }
2N/A if (!matchgood)
2N/A continue;
2N/A
2N/A if (entry->ae_restriction_string
2N/A && !entry->ae_restriction_bad
2N/A && !entry->ae_restrictions
2N/A && kadm5int_acl_parse_restrictions(entry->ae_restriction_string,
2N/A &entry->ae_restrictions)) {
2N/A DPRINT(DEBUG_ACL, acl_debug_level,
2N/A ("Bad restrictions in ACL entry for %s\n", entry->ae_name));
2N/A entry->ae_restriction_bad = 1;
2N/A }
2N/A if (entry->ae_restriction_bad) {
2N/A entry->ae_name_bad = 1;
2N/A continue;
2N/A }
2N/A break;
2N/A }
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_find_entry()=%x\n",entry));
2N/A return(entry);
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_init() - Initialize ACL context.
2N/A */
2N/Akrb5_error_code
2N/Akadm5int_acl_init(kcontext, debug_level, acl_file)
2N/A krb5_context kcontext;
2N/A int debug_level;
2N/A char *acl_file;
2N/A{
2N/A krb5_error_code kret;
2N/A
2N/A kret = 0;
2N/A acl_debug_level = debug_level;
2N/A DPRINT(DEBUG_CALLS, acl_debug_level,
2N/A ("* kadm5int_acl_init(afile=%s)\n",
2N/A ((acl_file) ? acl_file : "(null)")));
2N/A acl_acl_file = (acl_file) ? acl_file : (char *) KRB5_DEFAULT_ADMIN_ACL;
2N/A acl_inited = kadm5int_acl_load_acl_file();
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_init() = %d\n", kret));
2N/A return(kret);
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_finish - Terminate ACL context.
2N/A */
2N/Avoid
2N/Akadm5int_acl_finish(kcontext, debug_level)
2N/A krb5_context kcontext;
2N/A int debug_level;
2N/A{
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("* kadm5int_acl_finish()\n"));
2N/A kadm5int_acl_free_entries();
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("X kadm5int_acl_finish()\n"));
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_check_krb() - Is this operation permitted for this principal?
2N/A */
2N/Akrb5_boolean
2N/Akadm5int_acl_check_krb(kcontext, caller_princ, opmask, principal, restrictions)
2N/A krb5_context kcontext;
2N/A krb5_const_principal caller_princ;
2N/A krb5_int32 opmask;
2N/A krb5_const_principal principal;
2N/A restriction_t **restrictions;
2N/A{
2N/A krb5_boolean retval;
2N/A aent_t *aentry;
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("* acl_op_permitted()\n"));
2N/A
2N/A retval = FALSE;
2N/A
2N/A aentry = kadm5int_acl_find_entry(kcontext, caller_princ, principal);
2N/A if (aentry) {
2N/A if ((aentry->ae_op_allowed & opmask) == opmask) {
2N/A retval = TRUE;
2N/A if (restrictions) {
2N/A *restrictions =
2N/A (aentry->ae_restrictions && aentry->ae_restrictions->mask)
2N/A ? aentry->ae_restrictions
2N/A : (restriction_t *) NULL;
2N/A }
2N/A }
2N/A }
2N/A
2N/A DPRINT(DEBUG_CALLS, acl_debug_level, ("X acl_op_permitted()=%d\n",
2N/A retval));
2N/A return retval;
2N/A}
2N/A
2N/A/*
2N/A * kadm5int_acl_check() - Is this operation permitted for this principal?
2N/A * this code used not to be based on gssapi. In order
2N/A * to minimize porting hassles, I've put all the
2N/A * gssapi hair in this function. This might not be
2N/A * the best medium-term solution. (The best long-term
2N/A * solution is, of course, a real authorization service.)
2N/A */
2N/Akrb5_boolean
2N/Akadm5int_acl_check(kcontext, caller, opmask, principal, restrictions)
2N/A krb5_context kcontext;
2N/A gss_name_t caller;
2N/A krb5_int32 opmask;
2N/A krb5_principal principal;
2N/A restriction_t **restrictions;
2N/A{
2N/A krb5_boolean retval;
2N/A gss_buffer_desc caller_buf;
2N/A OM_uint32 emaj, emin;
2N/A krb5_error_code code;
2N/A krb5_principal caller_princ;
2N/A
2N/A /* Solaris Kerberos */
2N/A if (restrictions)
2N/A *restrictions = NULL;
2N/A
2N/A /* Solaris Kerberos: fix a mem leak with OID arg that isn't needed */
2N/A if (GSS_ERROR(emaj = gss_display_name(&emin, caller, &caller_buf, NULL)))
2N/A return FALSE;
2N/A
2N/A code = krb5_parse_name(kcontext, (char *) caller_buf.value,
2N/A &caller_princ);
2N/A
2N/A gss_release_buffer(&emin, &caller_buf);
2N/A
2N/A if (code != 0)
2N/A return FALSE;
2N/A
2N/A retval = kadm5int_acl_check_krb(kcontext, caller_princ,
2N/A opmask, principal, restrictions);
2N/A
2N/A krb5_free_principal(kcontext, caller_princ);
2N/A
2N/A return retval;
2N/A}
2N/A
2N/Akadm5_ret_t
2N/Akadm5_get_privs(void *server_handle, long *privs)
2N/A{
2N/A CHECK_HANDLE(server_handle);
2N/A
2N/A /* this is impossible to do with the current interface. For now,
2N/A return all privs, which will confuse some clients, but not
2N/A deny any access to users of "smart" clients which try to cache */
2N/A
2N/A *privs = ~0;
2N/A
2N/A return KADM5_OK;
2N/A}
2N/A
2N/A/* SUNWresync121 (SEAM1.0) XXX */
2N/Akadm5_ret_t
2N/A__kadm5_get_priv(void *server_handle, long *privs, gss_name_t client)
2N/A{
2N/A
2N/A aent_t *aentry;
2N/A gss_buffer_desc caller_buff;
2N/A gss_OID caller_oid;
2N/A krb5_principal caller_principal;
2N/A OM_uint32 minor, major;
2N/A krb5_error_code k_error;
2N/A kadm5_ret_t retval = KADM5_FAILURE;
2N/A
2N/A kadm5_server_handle_t handle = server_handle;
2N/A
2N/A CHECK_HANDLE(server_handle);
2N/A
2N/A if (GSS_ERROR(major = gss_display_name(&minor, client, &caller_buff,
2N/A &caller_oid)))
2N/A return(retval);
2N/A k_error = krb5_parse_name(handle->context,
2N/A (char *) caller_buff.value,
2N/A &caller_principal);
2N/A gss_release_buffer(&minor, &caller_buff);
2N/A
2N/A if (k_error)
2N/A return(retval);
2N/A
2N/A if (aentry = kadm5int_acl_find_entry(handle->context, caller_principal,
2N/A (krb5_principal)NULL))
2N/A *privs = aentry->ae_op_allowed;
2N/A krb5_free_principal(handle->context, caller_principal);
2N/A
2N/A return (KADM5_OK);
2N/A}