| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593 |
- /** BEGIN COPYRIGHT BLOCK
- * This Program is free software; you can redistribute it and/or modify it under
- * the terms of the GNU General Public License as published by the Free Software
- * Foundation; version 2 of the License.
- *
- * This Program is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
- * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along with
- * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * In addition, as a special exception, Red Hat, Inc. gives You the additional
- * right to link the code of this Program with code not covered under the GNU
- * General Public License ("Non-GPL Code") and to distribute linked combinations
- * including the two, subject to the limitations in this paragraph. Non-GPL Code
- * permitted under this exception must only link to the code of this Program
- * through those well defined interfaces identified in the file named EXCEPTION
- * found in the source code files (the "Approved Interfaces"). The files of
- * Non-GPL Code may instantiate templates or use macros or inline functions from
- * the Approved Interfaces without causing the resulting work to be covered by
- * the GNU General Public License. Only Red Hat, Inc. may make changes or
- * additions to the list of Approved Interfaces. You must obey the GNU General
- * Public License in all respects for all of the Program code and other code used
- * in conjunction with the Program except the Non-GPL Code covered by this
- * exception. If you modify this file, you may extend this exception to your
- * version of the file, but you are not obligated to do so. If you do not wish to
- * provide this exception without modification, you must delete this exception
- * statement from your version and license this file solely under the GPL without
- * exception.
- *
- *
- * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
- * Copyright (C) 2005 Red Hat, Inc.
- * All rights reserved.
- * END COPYRIGHT BLOCK **/
- #ifdef HAVE_CONFIG_H
- # include <config.h>
- #endif
- /*
- * Description (acleval.c)
- *
- * This module provides functions for evaluating Access Control List
- * (ACL) structures in memory.
- *
- */
- #include "base/systems.h"
- #include "netsite.h"
- #include "libaccess/symbols.h"
- #include "libaccess/aclerror.h"
- #include "libaccess/acleval.h"
- #include <assert.h>
- /*
- * Description (RLMEQUIV)
- *
- * Macro for realm comparison. Both realm pointers must be non-null.
- * The realms are equivalent if the pointers are equal, or if the
- * authentication methods and database names are the same. The
- * prompt string is not considered.
- */
- #define RLMEQUIV(rlm1, rlm2) (((rlm1) != 0) && ((rlm2) != 0) && \
- (((rlm1) == (rlm2)) || \
- (((rlm1)->rlm_ameth == (rlm2)->rlm_ameth) && \
- ((rlm1)->rlm_dbname != 0) && \
- ((rlm2)->rlm_dbname != 0) && \
- !strcmp((rlm1)->rlm_dbname, \
- (rlm2)->rlm_dbname))))
- int aclDNSLookup(DNSFilter_t * dnf, const char * dnsspec, int fqdn, const char **match)
- {
- const char * subdns; /* suffix of client DNS name */
- void * table; /* hash table pointer */
- Symbol_t * sym; /* DNS spec symbol pointer */
- int rv; /* result value */
- fqdn = (fqdn) ? 1 : 0;
- if (match) *match = 0;
- /* Handle null or empty filter */
- if ((dnf == 0) || (dnf->dnf_hash == 0)) {
- return ACL_NOMATCH;
- }
- /* Got the client's DNS name? */
- if (!dnsspec || !*dnsspec) {
- /* No, use special one */
- dnsspec = "unknown";
- }
-
- /* Get hash table pointer */
- table = dnf->dnf_hash;
- /*
- * Look up each possible suffix for the client domain name,
- * starting with the entire string, and working toward the
- * last component.
- */
- subdns = dnsspec;
- while (subdns != 0) {
- /* Look up the domain name suffix in the hash table */
- rv = symTableFindSym(table, subdns, fqdn, (void **)&sym);
- if (rv == 0) break;
- /* Step to the next level */
- if (subdns[0] == '.') subdns += 1;
- subdns = strchr(subdns, '.');
- /* If it was fully qualified, now it's not */
- fqdn = 0;
- }
- /* One more possibility if nothing found yet... */
- if (rv) {
- rv = symTableFindSym(table, "*", 0, (void **)&sym);
- }
- if (rv == 0) {
- if (match) *match = sym->sym_name;
- rv = ACL_DNMATCH;
- }
- else rv = ACL_NOMATCH;
- return rv;
- }
- int aclIPLookup(IPFilter_t * ipf, IPAddr_t ipaddr, void **match)
- {
- IPLeaf_t * leaf; /* radix tree leaf pointer */
- IPAddr_t bitmask; /* bit mask for current node */
- IPNode_t * ipn; /* current internal node */
- IPNode_t * lastipn; /* last internal node seen in search */
- IPNode_t * mipn; /* ipn_masked subtree root pointer */
- if (match) *match = 0;
- /* Handle null or empty IP filter */
- if ((ipf == 0) || (ipf->ipf_tree == 0)) goto fail;
- lastipn = NULL;
- ipn = ipf->ipf_tree;
- /*
- * The tree traversal first works down the tree, under the assumption
- * that all of the bits in the given IP address may be significant.
- * The internal nodes of the tree will cause particular bits of the
- * IP address to be tested, and the ipn_clear or ipn_set link to
- * a descendant followed accordingly. The internal nodes are arranged
- * in such a way that high-order bits are tested before low-order bits.
- * Usually some bits are skipped, as they are not needed to distinguish
- * the entries in the tree.
- *
- * At the bottom of the tree, a leaf node may be found, or the last
- * descendant link may be NULL. If a leaf node is found, it is
- * tested for a match against the given IP address. If it doesn't
- * match, or the link was NULL, backtracking begins, as described
- * below.
- *
- * Backtracking follows the ipn_parent links back up the tree from
- * the last internal node, looking for internal nodes with ipn_masked
- * descendants. The subtrees attached to these links are traversed
- * downward, as before, with the same processing at the bottom as
- * the first downward traversal. Following the ipn_masked links is
- * essentially examining the possibility that the IP address bit
- * associated with the internal node may be masked out by the
- * ipl_netmask in a leaf at the bottom of such a subtree. Since
- * the ipn_masked links are examined from the bottom of the tree
- * to the top, this looks at the low-order bits first.
- */
- while (ipn != NULL) {
- /*
- * Work down the tree testing bits in the IP address indicated
- * by the internal nodes. Exit the loop when there are no more
- * internal nodes.
- */
- while ((ipn != NULL) && (ipn->ipn_type == IPN_NODE)) {
- /* Save pointer to internal node */
- lastipn = ipn;
- /* Get a mask for the bit this node tests */
- bitmask = (IPAddr_t) 1<<ipn->ipn_bit;
- /* Select link to follow for this IP address */
- ipn = (bitmask & ipaddr) ? ipn->ipn_set : ipn->ipn_clear;
- }
- /* Did we end up with a non-NULL node pointer? */
- if (ipn != NULL) {
- /* It must be a leaf node */
- assert(ipn->ipn_type == IPN_LEAF);
- leaf = (IPLeaf_t *)ipn;
- /* Is it a matching leaf? */
- if (leaf->ipl_ipaddr == (ipaddr & leaf->ipl_netmask)) goto win;
- }
- /*
- * Backtrack, starting at lastipn. Search each subtree
- * emanating from an ipn_masked link. Step up the tree
- * until the ipn_masked link of the node referenced by
- * "ipf->ipf_tree" has been considered.
- */
- for (ipn = lastipn; ipn != NULL; ipn = ipn->ipn_parent) {
- /*
- * Look for a node with a non-NULL masked link, but don't
- * go back to the node we just came from.
- */
- if ((ipn->ipn_masked != NULL) && (ipn->ipn_masked != lastipn)) {
- /* Get the root of this subtree */
- mipn = ipn->ipn_masked;
- /* If this is an internal node, start downward traversal */
- if (mipn->ipn_type == IPN_NODE) {
- ipn = mipn;
- break;
- }
- /* Otherwise it's a leaf */
- assert(mipn->ipn_type == IPN_LEAF);
- leaf = (IPLeaf_t *)mipn;
- /* Is it a matching leaf? */
- if (leaf->ipl_ipaddr == (ipaddr & leaf->ipl_netmask)) goto win;
- }
- /* Don't consider nodes above the given root */
- if (ipn == ipf->ipf_tree) goto fail;
- lastipn = ipn;
- }
- }
- fail:
- /* No matching entry found */
- return ACL_NOMATCH;
- win:
- /* Found a match in leaf */
- if (match) *match = (void *)leaf;
- return ACL_IPMATCH;
- }
- int aclUserLookup(UidUser_t * uup, UserObj_t * uoptr)
- {
- int gl1cnt; /* elements left in uup->uu_group list */
- int gl2cnt; /* elements left in uoptr->uo_groups list */
- USI_t * gl1ptr; /* pointer to next group in uup->uu_group */
- USI_t * gl2ptr; /* pointer to next group in uoptr->uo_groups */
- /* Try for a direct match on the user id */
- if (usiPresent(&uup->uu_user, uoptr->uo_uid)) {
- return ACL_USMATCH;
- }
- /*
- * Now we want to see if there are any matches between the
- * uup->uu_group group id list and the list of groups in the
- * user object.
- */
- gl1cnt = UILCOUNT(&uup->uu_group);
- gl1ptr = UILLIST(&uup->uu_group);
- gl2cnt = UILCOUNT(&uoptr->uo_groups);
- gl2ptr = UILLIST(&uoptr->uo_groups);
- while ((gl1cnt > 0) && (gl2cnt > 0)) {
- if (*gl1ptr == *gl2ptr) {
- return ACL_GRMATCH;
- }
- if (*gl1ptr < *gl2ptr) {
- ++gl1ptr;
- --gl1cnt;
- }
- else {
- ++gl2ptr;
- --gl2cnt;
- }
- }
- return ACL_NOMATCH;
- }
- /*
- * Description (aclEvaluate)
- *
- * This function evaluates a given ACL against specified client
- * information and a particular access right that is needed to
- * service the client. It can optionally return the ACL directive
- * number which allows or denies the client's access.
- *
- * Arguments:
- *
- * acl - pointer to ACL to evaluate
- * arid - desired access right id value
- * clauth - pointer to client authentication information
- * padn - pointer to returned ACL directive number
- * (may be null)
- *
- * Returns:
- *
- * A return value of zero indicates that the given ACL does not
- * control the desired access right, or contains no directives which
- * match the specified client. A positive return value contains a
- * value of ACD_ALLOW, ACD_DENY, or ACD_AUTH, and may also have the
- * ACD_ALWAYS bit flag set. The value indicates whether the client
- * should be allowed or denied access, or whether authentication is
- * needed. The ACD_ALWAYS flag indicates if the action should occur
- * immediately, terminating any further ACL evaluation. An error
- * is indicated by a negative error code (ACLERRxxxx - see aclerror.h).
- */
- int aclEvaluate(ACL_t * acl, USI_t arid, ClAuth_t * clauth, int * padn)
- {
- ACDirective_t * acd; /* current ACL directive pointer */
- RightSpec_t * rsp; /* pointer to rights controlled by ACL */
- ACClients_t * csp; /* pointer to clients specification */
- HostSpec_t * hsp; /* pointer to host specification */
- UserSpec_t * usp; /* pointer to user specification */
- Realm_t * rlm = 0; /* current authentication realm pointer */
- Realm_t * authrlm = 0; /* realm to be used for authentication */
- int ndir; /* ACL directive number */
- int rv; /* result value */
- int decision = 0; /* current access control decision */
- int result = 0; /* function return value */
- int mdn = 0; /* matching directive number */
- if (padn) *padn = 0;
- /* Does this ACL control the desired access right? */
- rsp = acl->acl_rights;
- if ((rsp == 0) || !usiPresent(&rsp->rs_list, arid)) {
- /* No, nothing to do */
- return 0;
- }
- ndir = 0;
- /* Loop on each ACL directive */
- for (acd = acl->acl_dirf; acd != 0; acd = acd->acd_next) {
- /* Bump directive number */
- ++ndir;
- /* Dispatch on directive action code */
- switch (acd->acd_action) {
- case ACD_ALLOW:
- case ACD_DENY:
- /* Loop to process list of client specifications */
- for (csp = acd->acd_cl; csp != 0; csp = csp->cl_next) {
- /* Is there a host list? */
- hsp = csp->cl_host;
- if (hsp != 0) {
- /* An empty host list will not match */
- rv = 0;
- /* Yes, is there an IP address filter? */
- if (hsp->hs_host.inh_ipf.ipf_tree != 0) {
- /*
- * Yes, see if the the client's IP address
- * matches anything in the IP filter.
- */
- rv = aclIPLookup(&hsp->hs_host.inh_ipf,
- clauth->cla_ipaddr, 0);
- }
- /* If no IP match, is there a DNS filter? */
- if (!rv && (hsp->hs_host.inh_dnf.dnf_hash != 0)) {
- /* Yes, try for a DNS match */
- rv = aclDNSLookup(&hsp->hs_host.inh_dnf,
- clauth->cla_dns, 1, 0);
- }
- /*
- * Does the client match the host list? If not, skip
- * to the next client specification.
- */
- if (!rv) continue;
- }
- /* Is there a user list? */
- usp = csp->cl_user;
- if (usp != 0) {
- /* Yes, has the client user been authenticated yet? */
- if ((clauth->cla_realm != 0) && (clauth->cla_uoptr != 0)) {
- /*
- * Yes, has the client user been authenticated in the
- * realm associated with this user list?
- */
- if (RLMEQUIV(rlm, clauth->cla_realm)) {
- /*
- * Yes, does the user spec allow all
- * authenticated users?
- */
- rv = (usp->us_flags & ACL_USALL) ? ACL_GRMATCH : 0;
- if (!rv) {
- /*
- * No, need to check client user against list.
- */
- rv = aclUserLookup(&usp->us_user,
- clauth->cla_uoptr);
- }
- /* Got a match yet? */
- if (rv) {
- /*
- * Yes, update the the access control decision,
- * clearing any pending authentication request
- * flag.
- */
- authrlm = 0;
- decision = acd->acd_action;
- /* Copy the "always" flag to the result */
- result = (acd->acd_flags & ACD_ALWAYS);
- mdn = ndir;
- }
- }
- else {
- /*
- * The client has been authenticated already,
- * but not in the realm used by this directive.
- * Since directives in a given ACL are not
- * independent policy statements, it seems that
- * the proper thing to do here is to reject
- * this ACL in its entirity. This case is not
- * an authentication failure per se, but rather
- * an inability to evaluate this particular
- * ACL directive which requires authentication.
- */
- return 0;
- }
- }
- else {
- /*
- * The client user has not been authenticated in this
- * realm yet, but could potentially be one of the
- * users on this user list. This directive is
- * therefore "potentially matching". The question
- * is: would it change the current decision to allow
- * or deny the client if the client user actually did
- * match the user list?
- */
- if ((authrlm == 0) && (decision != acd->acd_action)) {
- /*
- * Yes, set the "request authentication" flag,
- * along with ACD_ALWAYS if it is set in the
- * directive.
- */
- authrlm = rlm;
- decision = ACD_AUTH;
- result = (acd->acd_flags & ACD_ALWAYS);
- mdn = ndir;
- }
- }
- }
- else {
- /*
- * There is no user list. Therefore any user,
- * authenticated or not, is considered a match.
- * Update the decision, and clear the
- * "authentication requested" flag.
- */
- authrlm = 0;
- decision = acd->acd_action;
- result = (acd->acd_flags & ACD_ALWAYS);
- mdn = ndir;
- }
- /*
- * If we hit a client specification that requires
- * immediate action, exit the loop.
- */
- if (result & ACD_ALWAYS) break;
- }
- break;
- case ACD_AUTH:
- /* Got a pointer to a realm specification? */
- if (acd->acd_auth.au_realm != 0) {
- /* Yes, update the current realm pointer */
- rlm = &acd->acd_auth.au_realm->rs_realm;
- /* Has the client already successfully authenticated? */
- if ((clauth->cla_realm == 0) || (clauth->cla_uoptr == 0)) {
- /*
- * No, if this is an "always" directive, override any
- * previously selected realm and request authentication.
- */
- if ((acd->acd_flags & ACD_ALWAYS) != 0) {
- /* Set decision to request authentication */
- authrlm = rlm;
- decision = ACD_AUTH;
- result = ACD_ALWAYS;
- mdn = ndir;
- }
- }
- }
- break;
- case ACD_EXEC:
- /* Conditionally terminate ACL evaluation */
- switch (decision) {
- case ACD_ALLOW:
- if (acd->acd_flags & ACD_EXALLOW) {
- result = (acd->acd_flags & ACD_ALWAYS);
- goto out;
- }
- break;
- case ACD_DENY:
- if (acd->acd_flags & ACD_EXDENY) {
- result = (acd->acd_flags & ACD_ALWAYS);
- goto out;
- }
- break;
- case ACD_AUTH:
- if (acd->acd_flags & ACD_EXAUTH) {
- result = (acd->acd_flags & ACD_ALWAYS);
- goto out;
- }
- break;
- default:
- break;
- }
- break;
-
- default:
- break;
- }
- /*
- * If we hit a directive that requires immediate action, exit
- * the loop.
- */
- if (result & ACD_ALWAYS) break;
- }
- out:
- /* If the decision is to request authentication, set the desired realm */
- if (decision == ACD_AUTH) {
- clauth->cla_realm = authrlm;
- }
- /* Combine decision with flags already in result */
- result |= decision;
- /* Return matching directive number if desired */
- if (padn) *padn = mdn;
- return result;
- }
|