summaryrefslogtreecommitdiffstats
path: root/lib/libaccess/aclbuild.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libaccess/aclbuild.cpp')
-rw-r--r--lib/libaccess/aclbuild.cpp1360
1 files changed, 1360 insertions, 0 deletions
diff --git a/lib/libaccess/aclbuild.cpp b/lib/libaccess/aclbuild.cpp
new file mode 100644
index 00000000..8c4647dc
--- /dev/null
+++ b/lib/libaccess/aclbuild.cpp
@@ -0,0 +1,1360 @@
+/** BEGIN COPYRIGHT BLOCK
+ * Copyright 2001 Sun Microsystems, Inc.
+ * Portions copyright 1999, 2001-2003 Netscape Communications Corporation.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+/*
+ * Description (aclbuild.c)
+ *
+ * This module provides functions for building Access Control List
+ * (ACL) structures in memory.
+ *
+ */
+
+#include <assert.h>
+#include "base/systems.h"
+#include "netsite.h"
+#include "libaccess/nsauth.h"
+#include "libaccess/nsuser.h"
+#include "libaccess/nsgroup.h"
+#include "libaccess/nsadb.h"
+#include "libaccess/aclerror.h"
+#include "libaccess/aclstruct.h"
+#include "libaccess/aclbuild.h"
+#include "libaccess/aclparse.h"
+#include "libaccess/acleval.h"
+#include "libaccess/usi.h"
+
+char * ACL_Program = "NSACL"; /* ACL facility name */
+
+/*
+ * Description (accCreate)
+ *
+ * This function creates a new access control context, which
+ * provides context information for a set of ACL definitions.
+ * The caller also provides a handle for a symbol table to be
+ * used to store definitions of ACL and rights names.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * stp - symbol table handle (may be null)
+ * pacc - pointer to returned context handle
+ *
+ * Returns:
+ *
+ * If the context is created successfully, the return value is zero.
+ * Otherwise it is a negative error code (ACLERRxxxx - see aclerror.h),
+ * and an error frame will be generated if an error list is provided.
+ */
+
+int accCreate(NSErr_t * errp, void * stp, ACContext_t **pacc)
+{
+ ACContext_t * acc; /* pointer to new context */
+ int rv; /* result value */
+ int eid; /* error id */
+
+ *pacc = 0;
+
+ /* Do we need to create a symbol table? */
+ if (stp == 0) {
+
+ /* Yes, create a symbol table for ACL, rights, etc. names */
+ rv = symTableNew(&stp);
+ if (rv < 0) goto err_nomem1;
+ }
+
+ /* Allocate the context structure */
+ acc = (ACContext_t *)MALLOC(sizeof(ACContext_t));
+ if (acc == 0) goto err_nomem2;
+
+ /* Initialize it */
+ acc->acc_stp = stp;
+ acc->acc_acls = 0;
+ acc->acc_rights = 0;
+ acc->acc_refcnt = 0;
+
+ *pacc = acc;
+ return 0;
+
+ err_nomem1:
+ rv = ACLERRNOMEM;
+ eid = ACLERR3000;
+ goto err_ret;
+
+ err_nomem2:
+ rv = ACLERRNOMEM;
+ eid = ACLERR3020;
+
+ err_ret:
+ nserrGenerate(errp, rv, eid, ACL_Program, 0);
+ return rv;
+}
+
+/*
+ * Description (accDestroy)
+ *
+ * This function destroys a set of ACL data structures referenced
+ * by a specified ACContext_t structure, including the ACContext_t
+ * itself.
+ *
+ * Arguments:
+ *
+ * acc - pointer to ACContext_t structure
+ * flags - bit flags (unused - must be zero)
+ */
+
+void accDestroy(ACContext_t * acc, int flags)
+{
+ ACL_t * acl;
+
+ if (acc != 0) {
+
+ /*
+ * First destroy all ACLs and any unnamed structures they reference.
+ * Note that aclDestroy() modifies the acc_acls list.
+ */
+ while ((acl = acc->acc_acls) != 0) {
+
+ aclDelete(acl);
+ }
+
+ /* If there's a symbol table, destroy everything it references */
+ if (acc->acc_stp != 0) {
+ symTableEnumerate(acc->acc_stp, 0, accDestroySym);
+
+ /* Destroy the symbol table itself */
+ symTableDestroy(acc->acc_stp, 0);
+ }
+
+ /* Free the ACContext_t structure */
+ FREE(acc);
+ }
+}
+
+/*
+ * Description (accDestroySym)
+ *
+ * This function is called to destroy the data structure associated
+ * with a specified Symbol_t symbol table entry. It examines the
+ * type of the symbol and calls the appropriate destructor.
+ *
+ * Arguments:
+ *
+ * sym - pointer to symbol table entry
+ * argp - unused - must be zero
+ *
+ * Returns:
+ *
+ * The return value is SYMENUMREMOVE.
+ */
+
+int accDestroySym(Symbol_t * sym, void * argp)
+{
+ switch (sym->sym_type) {
+ case ACLSYMACL: /* ACL */
+ aclDestroy((ACL_t *)sym);
+ break;
+
+ case ACLSYMRIGHT: /* access right */
+ {
+ RightDef_t * rdp = (RightDef_t *)sym;
+
+ if (rdp->rd_sym.sym_name != 0) {
+ FREE(rdp->rd_sym.sym_name);
+ }
+ FREE(rdp);
+ }
+ break;
+
+ case ACLSYMRDEF: /* access rights list */
+ aclRightSpecDestroy((RightSpec_t *)sym);
+ break;
+
+ case ACLSYMREALM: /* realm name */
+ aclRealmSpecDestroy((RealmSpec_t *)sym);
+ break;
+
+ case ACLSYMHOST: /* host specifications */
+ aclHostSpecDestroy((HostSpec_t *)sym);
+ break;
+
+ case ACLSYMUSER: /* user/group list */
+ aclUserSpecDestroy((UserSpec_t *)sym);
+ break;
+ }
+
+ return SYMENUMREMOVE;
+}
+
+/*
+ * Description (accReadFile)
+ *
+ * This function reads a specfied file containing ACL definitions
+ * and creates data structures in memory to represent the ACLs.
+ * The caller may provide a pointer to an existing ACContext_t
+ * structure which will serve as the root of the ACL structures,
+ * or else a new one will be created.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * aclfile - pointer to the ACL filename string
+ * pacc - value/result ACContext_t
+ *
+ * Returns:
+ *
+ * If the ACL file is read successfully, the return value is zero.
+ * Otherwise it is a negative error code (ACLERRxxxx - see aclerror.h),
+ * and an error frame will be generated if an error list is provided.
+ */
+
+int accReadFile(NSErr_t * errp, char * aclfile, ACContext_t **pacc)
+{
+ ACContext_t * acc = *pacc; /* pointer to ACL root structure */
+ ACLFile_t * acf = 0; /* pointer to ACL file handle */
+ void * stp = 0; /* ACL symbol table handle */
+ int rv; /* result value */
+ int eid; /* error id value */
+
+ /* Initialize the ACL parser */
+ rv = aclParseInit();
+ if (rv < 0) goto err_init;
+
+ /* Do we need to create a new ACContext_t structure? */
+ if (acc == 0) {
+
+ /* Yes, create a symbol table for ACL, rights, etc. names */
+ rv = symTableNew(&stp);
+ if (rv < 0) goto err_crsym;
+
+ /* Create a root structure for the ACLs, including the symbol table */
+ rv = accCreate(errp, stp, &acc);
+ if (rv < 0) goto err_ret2;
+ }
+
+ /* Open the ACL definition file */
+ rv = aclFileOpen(errp, aclfile, 0, &acf);
+ if (rv < 0) goto err_ret3;
+
+ /* Parse the ACL definitions, building ACL structures in memory */
+ rv = aclACLParse(errp, acf, acc, 0);
+ if (rv < 0) goto err_ret4;
+
+ aclFileClose(acf, 0);
+
+ if (pacc) *pacc = acc;
+
+ return rv;
+
+ err_init:
+ eid = ACLERR3100;
+ goto err_ret;
+
+ err_crsym:
+ eid = ACLERR3120;
+ rv = ACLERRNOMEM;
+ goto err_ret;
+
+ err_ret4:
+ aclFileClose(acf, 0);
+ err_ret3:
+ /* Destroy the ACContext_t if we just created it */
+ if (acc != *pacc) {
+ accDestroy(acc, 0);
+ }
+ goto err_ret;
+
+ err_ret2:
+ symTableDestroy(stp, 0);
+
+ err_ret:
+ return rv;
+}
+
+/*
+ * Description (aclAuthDNSAdd)
+ *
+ * This function adds a DNS name specification to the DNS filter
+ * associated with a given host list. The DNS name specification is
+ * either a fully-qualified domain name or a domain name suffix,
+ * indicated by a leading ".", e.g. (".mcom.com"). The name
+ * components included in a suffix must be complete. For example,
+ * ".scape.com" will not match names ending in ".netscape.com".
+ *
+ * Arguments:
+ *
+ * hspp - pointer to host list pointer
+ * dnsspec - DNS name or suffix string pointer
+ * fqdn - non-zero if dnsspec is fully qualified
+ *
+ * Returns:
+ *
+ * If successful, the return code is zero.
+ * An error is indicated by a negative return code (ACLERRxxxx
+ * - see aclerror.h).
+ */
+
+int aclAuthDNSAdd(HostSpec_t **hspp, char * dnsspec, int fqdn)
+{
+ HostSpec_t * hsp; /* host list pointer */
+ void * table; /* access control hash table pointer */
+ Symbol_t * sym; /* hash table entry pointer */
+ int rv; /* result value */
+
+ fqdn = (fqdn) ? 1 : 0;
+
+ /* Create the HostSpec_t if it doesn't exist */
+ hsp = *hspp;
+ if (hsp == 0) {
+
+ hsp = (HostSpec_t *)MALLOC(sizeof(HostSpec_t));
+ if (hsp == 0) goto err_nomem;
+ memset((void *)hsp, 0, sizeof(HostSpec_t));
+ hsp->hs_sym.sym_type = ACLSYMHOST;
+ }
+
+ /* Get pointer to hash table used for DNS filter */
+ table = hsp->hs_host.inh_dnf.dnf_hash;
+ if (table == 0) {
+
+ /* None there yet, so create one */
+ rv = symTableNew(&table);
+ if (rv < 0) goto punt;
+ hsp->hs_host.inh_dnf.dnf_hash = table;
+ }
+
+ /* See if the DNS spec is already in the table */
+ rv = symTableFindSym(table, dnsspec, fqdn, (void **)&sym);
+ if (rv < 0) {
+ if (rv != SYMERRNOSYM) goto punt;
+
+ /* It's not there, so add it */
+ sym = (Symbol_t *)MALLOC(sizeof(Symbol_t));
+ sym->sym_name = STRDUP(dnsspec);
+ sym->sym_type = fqdn;
+
+ rv = symTableAddSym(table, sym, (void *)sym);
+ if (rv < 0) goto err_nomem;
+ }
+
+ *hspp = hsp;
+
+ punt:
+ return rv;
+
+ err_nomem:
+ rv = ACLERRNOMEM;
+ goto punt;
+}
+
+/*
+ * Description (aclAuthIPAdd)
+ *
+ * This function adds an IP address specification to the IP filter
+ * associated with a given host list. The IP address specification
+ * consists of an IP host or network address and an IP netmask.
+ * For host addresses the netmask value is 255.255.255.255.
+ *
+ * Arguments:
+ *
+ * hspp - pointer to host list pointer
+ * ipaddr - IP host or network address
+ * netmask - IP netmask value
+ *
+ * Returns:
+ *
+ * If successful, the return code is zero.
+ * An error is indicated by a negative return code (ACLERRxxxx
+ * - see aclerror.h).
+ */
+
+int aclAuthIPAdd(HostSpec_t **hspp, IPAddr_t ipaddr, IPAddr_t netmask)
+{
+ HostSpec_t * hsp; /* host list pointer */
+ IPFilter_t * ipf; /* IP filter pointer */
+ IPNode_t * ipn; /* current node pointer */
+ IPNode_t * lastipn; /* last (lower) node pointer */
+ IPLeaf_t * leaf; /* leaf node pointer */
+ IPAddr_t bitmask; /* bit mask for current node */
+ int lastbit; /* number of last bit set in netmask */
+ int i; /* loop index */
+
+ /* Create the HostSpec_t if it doesn't exist */
+ hsp = *hspp;
+ if (hsp == 0) {
+
+ hsp = (HostSpec_t *)MALLOC(sizeof(HostSpec_t));
+ if (hsp == 0) goto err_nomem;
+ memset((void *)hsp, 0, sizeof(HostSpec_t));
+ hsp->hs_sym.sym_type = ACLSYMHOST;
+ }
+
+ ipf = &hsp->hs_host.inh_ipf;
+
+ /* If the filter doesn't have a root node yet, create it */
+ if (ipf->ipf_tree == 0) {
+
+ /* Allocate node */
+ ipn = (IPNode_t *)MALLOC(sizeof(IPNode_t));
+ if (ipn == 0) goto err_nomem;
+
+ /* Initialize it to test bit 31, but without any descendants */
+ ipn->ipn_type = IPN_NODE;
+ ipn->ipn_bit = 31;
+ ipn->ipn_parent = NULL;
+ ipn->ipn_clear = NULL;
+ ipn->ipn_set = NULL;
+ ipn->ipn_masked = NULL;
+
+ /* Set it as the root node in the radix tree */
+ ipf->ipf_tree = ipn;
+ }
+
+ /* First we search the tree to see where this IP specification fits */
+
+ lastipn = NULL;
+
+ for (ipn = ipf->ipf_tree; (ipn != NULL) && (ipn->ipn_type == IPN_NODE); ) {
+
+ /* Get a mask for the bit this node tests */
+ bitmask = (IPAddr_t) 1<<ipn->ipn_bit;
+
+ /* Save pointer to last internal node */
+ lastipn = ipn;
+
+ /* Is this a bit we care about? */
+ if (bitmask & netmask) {
+
+ /* Yes, get address of set or clear descendant pointer */
+ ipn = (bitmask & ipaddr) ? ipn->ipn_set : ipn->ipn_clear;
+ }
+ else {
+ /* No, get the address of the masked descendant pointer */
+ ipn = ipn->ipn_masked;
+ }
+ }
+
+ /* Did we end up at a leaf node? */
+ if (ipn == NULL) {
+
+ /*
+ * No, well, we need to find a leaf node if possible. The
+ * reason is that we need an IP address and netmask to compare
+ * to the IP address and netmask we're inserting. We know that
+ * they're the same up to the bit tested by the lastipn node,
+ * but we need to know the *highest* order bit that's different.
+ * Any leaf node below lastipn will do.
+ */
+
+ leaf = NULL;
+ ipn = lastipn;
+
+ while (ipn != NULL) {
+
+ /* Look for any non-null child link of the current node */
+ for (i = 0; i < IPN_NLINKS; ++i) {
+ if (ipn->ipn_links[i]) break;
+ }
+
+ /*
+ * Fail search for leaf if no non-null child link found.
+ * This should only happen on the root node of the tree
+ * when the tree is empty.
+ */
+ if (i >= IPN_NLINKS) {
+ assert(ipn == ipf->ipf_tree);
+ break;
+ }
+
+ /* Step to the child node */
+ ipn = ipn->ipn_links[i];
+
+ /* Is it a leaf? */
+ if (ipn->ipn_type == IPN_LEAF) {
+
+ /* Yes, search is over */
+ leaf = (IPLeaf_t *)ipn;
+ ipn = NULL;
+ break;
+ }
+ }
+ }
+ else {
+
+ /* Yes, loop terminated on a leaf node */
+ assert(ipn->ipn_type == IPN_LEAF);
+ leaf = (IPLeaf_t *)ipn;
+ }
+
+ /* Got a leaf yet? */
+ if (leaf != NULL) {
+
+ /* Combine the IP address and netmask differences */
+ bitmask = (leaf->ipl_ipaddr ^ ipaddr) | (leaf->ipl_netmask ^ netmask);
+
+ /* Are both the IP address and the netmask the same? */
+ if (bitmask == 0) {
+
+ /* Yes, duplicate entry */
+ return 0;
+ }
+
+ /* Find the bit number of the first different bit */
+ for (lastbit = 31;
+ (bitmask & 0x80000000) == 0; --lastbit, bitmask <<= 1) ;
+
+ /* Generate a bit mask with just that bit */
+ bitmask = (IPAddr_t) (1 << lastbit);
+
+ /*
+ * Go up the tree from lastipn, looking for an internal node
+ * that tests lastbit. Stop if we get to a node that tests
+ * a higher bit number first.
+ */
+ for (ipn = lastipn, lastipn = (IPNode_t *)leaf;
+ ipn != NULL; ipn = ipn->ipn_parent) {
+
+ if (ipn->ipn_bit >= lastbit) {
+ if (ipn->ipn_bit == lastbit) {
+ /* Need to add a leaf off ipn node */
+ lastipn = NULL;
+ }
+ break;
+ }
+ lastipn = ipn;
+ }
+
+ assert(ipn != NULL);
+ }
+ else {
+
+ /* Just hang a leaf off the lastipn node if no leaf */
+ ipn = lastipn;
+ lastipn = NULL;
+ lastbit = ipn->ipn_bit;
+ }
+
+ /*
+ * If lastipn is not NULL at this point, the new leaf will hang
+ * off an internal node inserted between the upper node, referenced
+ * by ipn, and the lower node, referenced by lastipn. The lower
+ * node may be an internal node or a leaf.
+ */
+ if (lastipn != NULL) {
+ IPNode_t * parent = ipn; /* parent of the new node */
+
+ assert((lastipn->ipn_type == IPN_LEAF) ||
+ (ipn == lastipn->ipn_parent));
+
+ /* Allocate space for the internal node */
+ ipn = (IPNode_t *)MALLOC(sizeof(IPNode_t));
+ if (ipn == NULL) goto err_nomem;
+
+ ipn->ipn_type = IPN_NODE;
+ ipn->ipn_bit = lastbit;
+ ipn->ipn_parent = parent;
+ ipn->ipn_clear = NULL;
+ ipn->ipn_set = NULL;
+ ipn->ipn_masked = NULL;
+
+ bitmask = (IPAddr_t) (1 << lastbit);
+
+ /*
+ * The values in the leaf we found above determine which
+ * descendant link of the new internal node will reference
+ * the subtree that we just ascended.
+ */
+ if (leaf->ipl_netmask & bitmask) {
+ if (leaf->ipl_ipaddr & bitmask) {
+ ipn->ipn_set = lastipn;
+ }
+ else {
+ ipn->ipn_clear = lastipn;
+ }
+ }
+ else {
+ ipn->ipn_masked = lastipn;
+ }
+
+ /* Allocate space for the new leaf */
+ leaf = (IPLeaf_t *)MALLOC(sizeof(IPLeaf_t));
+ if (leaf == NULL) {
+ FREE((void *)ipn);
+ goto err_nomem;
+ }
+
+ /* Insert internal node in tree */
+
+ /* First the downward link from the parent to the new node */
+ for (i = 0; i < IPN_NLINKS; ++i) {
+ if (parent->ipn_links[i] == lastipn) {
+ parent->ipn_links[i] = ipn;
+ break;
+ }
+ }
+
+ /* Then the upward link from the child (if it's not a leaf) */
+ if (lastipn->ipn_type == IPN_NODE) {
+ lastipn->ipn_parent = ipn;
+ }
+ }
+ else {
+ /* Allocate space for a leaf node only */
+ leaf = (IPLeaf_t *)MALLOC(sizeof(IPLeaf_t));
+ if (leaf == NULL) goto err_nomem;
+ }
+
+ /* Initialize the new leaf */
+ leaf->ipl_type = IPN_LEAF;
+ leaf->ipl_ipaddr = ipaddr;
+ leaf->ipl_netmask = netmask;
+
+ /*
+ * Select the appropriate descendant link of the internal node
+ * and point it at the new leaf.
+ */
+ bitmask = (IPAddr_t) (1 << ipn->ipn_bit);
+ if (bitmask & netmask) {
+ if (bitmask & ipaddr) {
+ assert(ipn->ipn_set == NULL);
+ ipn->ipn_set = (IPNode_t *)leaf;
+ }
+ else {
+ assert(ipn->ipn_clear == NULL);
+ ipn->ipn_clear = (IPNode_t *)leaf;
+ }
+ }
+ else {
+ assert(ipn->ipn_masked == NULL);
+ ipn->ipn_masked = (IPNode_t *)leaf;
+ }
+
+ *hspp = hsp;
+
+ /* Successful completion */
+ return 0;
+
+ err_nomem:
+ return ACLERRNOMEM;
+}
+
+/*
+ * Description (aclAuthNameAdd)
+ *
+ * This function adds a user or group to a given user list,
+ * in the context of a specified ACL that is being created. The
+ * name of the user or group is provided by the caller, and is
+ * looked up in the authentication database associated with the
+ * specified user list. The return value indicates whether the name
+ * matched a user or group name, and whether the corresponding user
+ * or group id was already present in the given user list.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * usp - pointer to user list specification
+ * rlm - pointer to current authentication realm
+ * name - pointer to user or group name string
+ *
+ * Returns:
+ *
+ * The return value is zero if the name is not found in the
+ * authentication database. If the name is found, the return value
+ * is a positive value containing bit flags:
+ *
+ * AIF_GROUP - name matches a group name
+ * AIF_USER - name matches a user name
+ * AIF_DUP - name was already represented in the
+ * specified user list
+ *
+ * An error is indicated by a negative return code (ACLERRxxxx
+ * - see aclerror.h), and an error frame will be generated if
+ * an error list is provided.
+ */
+
+int aclAuthNameAdd(NSErr_t * errp, UserSpec_t * usp,
+ Realm_t * rlm, char * name)
+{
+ void * guoptr; /* group or user object pointer */
+ int irv; /* insert result value */
+ int eid; /* error id */
+ int rv; /* result value */
+
+ /* There must be a realm specified in order to handle users */
+ if (rlm == 0) goto err_norealm;
+
+ /* Open the authentication database if it's not already */
+ if (rlm->rlm_authdb == 0) {
+
+ if (rlm->rlm_aif == 0) {
+ rlm->rlm_aif = &NSADB_AuthIF;
+ }
+
+ rv = (*rlm->rlm_aif->aif_open)(errp,
+ rlm->rlm_dbname, 0, &rlm->rlm_authdb);
+ if (rv < 0) goto err_open;
+ }
+
+ /* Look up the name in the authentication DB */
+ rv = (*rlm->rlm_aif->aif_findname)(errp, rlm->rlm_authdb, name,
+ (AIF_USER|AIF_GROUP), (void **)&guoptr);
+ if (rv <= 0) {
+ if (rv < 0) goto err_adb;
+
+ /* The name was not found in the database */
+ return 0;
+ }
+
+ /* The name was found. Was it a user name? */
+ if (rv == AIF_USER) {
+
+ /* Yes, add the user id to the user list */
+ irv = usiInsert(&usp->us_user.uu_user, ((UserObj_t *)guoptr)->uo_uid);
+ rv = ANA_USER;
+ }
+ else {
+
+ /* No, must be a group name. Add group id to an_groups list. */
+ irv = usiInsert(&usp->us_user.uu_group,
+ ((GroupObj_t *)guoptr)->go_gid);
+ rv = ANA_GROUP;
+ }
+
+ /* Examine the result of the insert operation */
+ if (irv <= 0) {
+ if (irv < 0) goto err_ins;
+
+ /* Id was already in the list */
+ rv |= ANA_DUP;
+ }
+
+ punt:
+ return rv;
+
+ err_norealm:
+ eid = ACLERR3400;
+ rv = ACLERRNORLM;
+ nserrGenerate(errp, rv, eid, ACL_Program, 1, name);
+ goto punt;
+
+ err_open:
+ eid = ACLERR3420;
+ rv = ACLERROPEN;
+ nserrGenerate(errp, rv, eid, ACL_Program,
+ 2, rlm->rlm_dbname, system_errmsg());
+ goto punt;
+
+ err_adb:
+ /* Error accessing authentication database. */
+ eid = ACLERR3440;
+ rv = ACLERRADB;
+ nserrGenerate(errp, rv, eid, ACL_Program, 2, rlm->rlm_dbname, name);
+ goto punt;
+
+ err_ins:
+ /* Error on insert operation. Must be lack of memory. */
+ eid = ACLERR3460;
+ rv = ACLERRNOMEM;
+ nserrGenerate(errp, rv, eid, ACL_Program, 0);
+ goto punt;
+}
+
+/*
+ * Description (aclClientsDirCreate)
+ *
+ * This function allocates and initializes a new ACClients_t
+ * ACL directive.
+ *
+ * Arguments:
+ *
+ * None.
+ *
+ * Returns:
+ *
+ * If successful, a pointer to the new ACClients_t is returned.
+ * A shortage of dynamic memory is indicated by a null return value.
+ */
+
+ACClients_t * aclClientsDirCreate()
+{
+ ACClients_t * acd; /* pointer to new ACClients_t */
+
+ acd = (ACClients_t *)MALLOC(sizeof(ACClients_t));
+ if (acd != 0) {
+ memset((void *)acd, 0, sizeof(ACClients_t));
+ }
+
+ return acd;
+}
+
+/*
+ * Description (aclCreate)
+ *
+ * This function creates a new ACL root structure. The caller
+ * specifies the name to be associated with the ACL. The ACL handle
+ * returned by this function is passed to other functions in this
+ * module when adding information to the ACL.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * acc - pointer to an access control context
+ * aclname - pointer to ACL name string
+ * pacl - pointer to returned ACL handle
+ *
+ * Returns:
+ *
+ * The return value is zero if the ACL is created successfully.
+ * Otherwise it is a negative error code (ACLERRxxxx - see aclerror.h),
+ * and an error frame will be generated if an error list is provided.
+ */
+
+int aclCreate(NSErr_t * errp, ACContext_t * acc, char * aclname, ACL_t **pacl)
+{
+ ACL_t * acl; /* pointer to created ACL */
+ int rv; /* result value */
+ int eid; /* error id */
+
+ *pacl = 0;
+
+ /* Allocate the ACL_t structure */
+ acl = (ACL_t *) MALLOC(sizeof(ACL_t));
+ if (acl == 0) goto err_nomem;
+
+ /* Initialize the structure */
+ memset((void *)acl, 0, sizeof(ACL_t));
+ acl->acl_sym.sym_name = STRDUP(aclname);
+ acl->acl_sym.sym_type = ACLSYMACL;
+ acl->acl_acc = acc;
+ acl->acl_refcnt = 1;
+
+ /* Add it to the symbol table for the specified context */
+ rv = symTableAddSym(acc->acc_stp, &acl->acl_sym, (void *)acl);
+ if (rv < 0) goto err_addsym;
+
+ /* Add it to the list of ACLs for the specified context */
+ acl->acl_next = acc->acc_acls;
+ acc->acc_acls = acl;
+ acc->acc_refcnt += 1;
+
+ *pacl = acl;
+ return 0;
+
+ err_nomem:
+ rv = ACLERRNOMEM;
+ eid = ACLERR3200;
+ nserrGenerate(errp, rv, eid, ACL_Program, 0);
+ goto done;
+
+ err_addsym:
+ FREE(acl);
+ rv = ACLERRDUPSYM;
+ eid = ACLERR3220;
+ nserrGenerate(errp, rv, eid, ACL_Program, 1, aclname);
+
+ done:
+ return rv;
+}
+
+/*
+ * Description (aclDestroy)
+ *
+ * This function destroys an ACL structure and its sub-structures.
+ * It does not free the ACContext_t referenced by the ACL.
+ *
+ * Arguments:
+ *
+ * acl - pointer to ACL_t structure
+ */
+
+void aclDestroy(ACL_t * acl)
+{
+ ACL_t **pacl; /* ACL list link pointer */
+ ACDirective_t * acd; /* ACL directive pointer */
+ ACDirective_t * nacd; /* next ACL directive pointer */
+
+ /* Is there an ACContext_t structure? */
+ if (acl->acl_acc != 0) {
+
+ /* Remove this ACL from the list in the ACContext_t structure */
+ for (pacl = &acl->acl_acc->acc_acls;
+ *pacl != 0; pacl = &(*pacl)->acl_next) {
+
+ if (*pacl == acl) {
+ *pacl = acl->acl_next;
+ acl->acl_acc->acc_refcnt -= 1;
+ break;
+ }
+ }
+ }
+
+ /* Destroy each ACL directive */
+ for (acd = acl->acl_dirf; acd != 0; acd = nacd) {
+ nacd = acd->acd_next;
+ aclDirectiveDestroy(acd);
+ }
+
+ /* Free the ACL rights list if it is unnamed */
+ if ((acl->acl_rights != 0) && (acl->acl_rights->rs_sym.sym_name == 0)) {
+ aclRightSpecDestroy(acl->acl_rights);
+ }
+
+ /* Free the ACL name string, if any */
+ if (acl->acl_sym.sym_name != 0) {
+ FREE(acl->acl_sym.sym_name);
+ }
+
+ /* Free the ACL itself */
+ FREE(acl);
+}
+
+/*
+ * Description (aclDelete)
+ *
+ * This function removes a specified ACL from the symbol table
+ * associated with its ACL context, and then destroys the ACL
+ * structure and any unnamed objects it references (other than
+ * the ACL context).
+ *
+ * Arguments:
+ *
+ * acl - pointer to the ACL
+ */
+
+void aclDelete(ACL_t * acl)
+{
+ ACContext_t * acc = acl->acl_acc;
+
+ if ((acc != 0) && (acl->acl_sym.sym_name != 0)) {
+ symTableRemoveSym(acc->acc_stp, &acl->acl_sym);
+ }
+
+ aclDestroy(acl);
+}
+
+/*
+ * Description (aclDirectiveAdd)
+ *
+ * This function adds a given directive to a specified ACL.
+ *
+ * Arguments:
+ *
+ * acl - pointer to the ACL
+ * acd - pointer to the directive to be added
+ *
+ * Returns:
+ *
+ * If successful, the return value is zero. An error is indicated
+ * by a negative return value.
+ */
+
+int aclDirectiveAdd(ACL_t * acl, ACDirective_t * acd)
+{
+ /* Add the directive to the end of the ACL's directive list */
+ acd->acd_next = 0;
+
+ if (acl->acl_dirl == 0) {
+ /* First entry in empty list */
+ acl->acl_dirf = acd;
+ }
+ else {
+ /* Append to end of list */
+ acl->acl_dirl->acd_next = acd;
+ }
+
+ acl->acl_dirl = acd;
+
+ return 0;
+}
+
+/*
+ * Description (aclDirectiveCreate)
+ *
+ * This function allocates and initializes a new ACDirective_t
+ * structure, representing an ACL directive.
+ *
+ * Arguments:
+ *
+ * None.
+ *
+ * Returns:
+ *
+ * If successful, the return value is a pointer to a new ACDirective_t.
+ * Otherwise the return value is null.
+ */
+
+ACDirective_t * aclDirectiveCreate()
+{
+ ACDirective_t * acd;
+
+ acd = (ACDirective_t *) MALLOC(sizeof(ACDirective_t));
+ if (acd != 0) {
+ memset((void *)acd, 0, sizeof(ACDirective_t));
+ }
+
+ return acd;
+}
+
+/*
+ * Description (aclDirectiveDestroy)
+ *
+ * This function destroys an ACL directive structure.
+ *
+ * Arguments:
+ *
+ * acd - pointer to ACL directive structure
+ */
+
+void aclDirectiveDestroy(ACDirective_t * acd)
+{
+ switch (acd->acd_action) {
+ case ACD_ALLOW:
+ case ACD_DENY:
+ {
+ ACClients_t * acp;
+ ACClients_t * nacp;
+
+ /* Free a list of ACClients_t structures */
+ for (acp = acd->acd_cl; acp != 0; acp = nacp) {
+ nacp = acp->cl_next;
+
+ /* Free the HostSpec_t if it's there and unnamed */
+ if ((acp->cl_host != 0) &&
+ (acp->cl_host->hs_sym.sym_name == 0)) {
+ aclHostSpecDestroy(acp->cl_host);
+ }
+
+ /* Free the UserSpec_t if it's there and unnamed */
+ if ((acp->cl_user != 0) &&
+ (acp->cl_user->us_sym.sym_name == 0)) {
+ aclUserSpecDestroy(acp->cl_user);
+ }
+ }
+ }
+ break;
+
+ case ACD_AUTH:
+ {
+ RealmSpec_t * rsp = acd->acd_auth.au_realm;
+
+ /* Destroy the RealmSpec_t if it's unnamed */
+ if ((rsp != 0) && (rsp->rs_sym.sym_name == 0)) {
+ aclRealmSpecDestroy(rsp);
+ }
+ }
+ break;
+ }
+
+ FREE(acd);
+}
+
+/*
+ * Description (aclDNSSpecDestroy)
+ *
+ * This function destroys an entry in a DNS filter. It is intended
+ * mainly to be used by aclHostSpecDestroy().
+ *
+ * Arguments:
+ *
+ * sym - pointer to Symbol_t for DNS filter entry
+ * argp - unused (must be zero)
+ *
+ * Returns:
+ *
+ * The return value is SYMENUMREMOVE.
+ */
+
+int aclDNSSpecDestroy(Symbol_t * sym, void * argp)
+{
+ if (sym != 0) {
+
+ /* Free the DNS specification string if any */
+ if (sym->sym_name != 0) {
+ FREE(sym->sym_name);
+ }
+
+ /* Free the Symbol_t structure */
+ FREE(sym);
+ }
+
+ /* Indicate that the symbol table entry should be removed */
+ return SYMENUMREMOVE;
+}
+
+/*
+ * Description (aclHostSpecDestroy)
+ *
+ * This function destroys a HostSpec_t structure and its sub-structures.
+ *
+ * Arguments:
+ *
+ * hsp - pointer to HostSpec_t structure
+ */
+
+void aclHostSpecDestroy(HostSpec_t * hsp)
+{
+ if (hsp == 0) return;
+
+ /* Free the IP filter if any */
+ if (hsp->hs_host.inh_ipf.ipf_tree != 0) {
+ IPNode_t * ipn; /* current node pointer */
+ IPNode_t * parent; /* parent node pointer */
+ int i;
+
+ /* Traverse tree, freeing nodes */
+ for (parent = hsp->hs_host.inh_ipf.ipf_tree; parent != NULL; ) {
+
+ /* Look for a link to a child node */
+ for (i = 0; i < IPN_NLINKS; ++i) {
+ ipn = parent->ipn_links[i];
+ if (ipn != NULL) break;
+ }
+
+ /* Any children for the parent node? */
+ if (ipn == NULL) {
+
+ /* Otherwise back up the tree */
+ ipn = parent;
+ parent = ipn->ipn_parent;
+
+ /* Free the lower node */
+ FREE(ipn);
+ continue;
+ }
+
+ /*
+ * Found a child node for the current parent.
+ * NULL out the downward link and check it out.
+ */
+ parent->ipn_links[i] = NULL;
+
+ /* Is it a leaf? */
+ if (ipn->ipn_type == IPN_LEAF) {
+ /* Yes, free it */
+ FREE(ipn);
+ continue;
+ }
+
+ /* No, step down the tree */
+ parent = ipn;
+ }
+ }
+
+ /* Free the DNS filter if any */
+ if (hsp->hs_host.inh_dnf.dnf_hash != 0) {
+
+ /* Destroy each entry in the symbol table */
+ symTableEnumerate(hsp->hs_host.inh_dnf.dnf_hash, 0,
+ aclDNSSpecDestroy);
+
+ /* Destroy the symbol table itself */
+ symTableDestroy(hsp->hs_host.inh_dnf.dnf_hash, 0);
+ }
+
+ /* Free the symbol name if any */
+ if (hsp->hs_sym.sym_name != 0) {
+ FREE(hsp->hs_sym.sym_name);
+ }
+
+ /* Free the HostSpec_t structure */
+ FREE(hsp);
+}
+
+/*
+ * Description (aclRealmSpecDestroy)
+ *
+ * This function destroys a RealmSpec_t structure.
+ *
+ * Arguments:
+ *
+ * rsp - pointer to RealmSpec_t structure
+ */
+
+void aclRealmSpecDestroy(RealmSpec_t * rsp)
+{
+ /* Close the realm authentication database if it appears open */
+ if ((rsp->rs_realm.rlm_aif != 0) &&
+ (rsp->rs_realm.rlm_authdb != 0)) {
+ (*rsp->rs_realm.rlm_aif->aif_close)(rsp->rs_realm.rlm_authdb, 0);
+ }
+
+ /* Free the prompt string if any */
+ if (rsp->rs_realm.rlm_prompt != 0) {
+ FREE(rsp->rs_realm.rlm_prompt);
+ }
+
+ /* Free the database filename string if any */
+ if (rsp->rs_realm.rlm_dbname != 0) {
+ FREE(rsp->rs_realm.rlm_dbname);
+ }
+
+ /* Free the realm specification name if any */
+ if (rsp->rs_sym.sym_name != 0) {
+ FREE(rsp->rs_sym.sym_name);
+ }
+
+ /* Free the RealmSpec_t structure */
+ FREE(rsp);
+}
+
+/*
+ * Description (aclRightDef)
+ *
+ * This function find or creates an access right with a specified
+ * name in a given ACL context. If a new access right definition
+ * is created, it assigns a unique integer identifier to the the
+ * right, adds it to the ACL context symbol table and to the
+ * list of all access rights for the context. Note that access
+ * right names are case-insensitive.
+ *
+ * Arguments:
+ *
+ * errp - error frame list pointer (may be null)
+ * acc - pointer to an access control context
+ * rname - access right name (e.g. "GET")
+ * prd - pointer to returned RightDef_t pointer
+ * (may be null)
+ *
+ * Returns:
+ *
+ * The return value is zero if the access right definition already
+ * existed or one if it was created successfully. Otherwise it is
+ * a negative error code (ACLERRxxxx - see aclerror.h), and an error
+ * frame will be generated if an error list is provided.
+ */
+
+int aclRightDef(NSErr_t * errp,
+ ACContext_t * acc, char * rname, RightDef_t **prd)
+{
+ RightDef_t * rdp; /* pointer to right definition */
+ int eid; /* error id code */
+ int rv; /* result value */
+ static int last_rid = 0; /* last assigned right id */
+
+ /* See if there's already a symbol table entry for it */
+ rv = symTableFindSym(acc->acc_stp, rname, ACLSYMRIGHT, (void **)&rdp);
+ if (rv) {
+
+ /* No, create an entry */
+
+ /* Allocate a right definition structure and initialize it */
+ rdp = (RightDef_t *)MALLOC(sizeof(RightDef_t));
+ if (rdp == 0) goto err_nomem;
+
+ rdp->rd_sym.sym_name = STRDUP(rname);
+ rdp->rd_sym.sym_type = ACLSYMRIGHT;
+ rdp->rd_next = acc->acc_rights;
+ rdp->rd_id = ++last_rid;
+
+ /* Add the right name to the symbol table for the ACL context */
+ rv = symTableAddSym(acc->acc_stp, &rdp->rd_sym, (void *)rdp);
+ if (rv) goto err_stadd;
+
+ /* Add the right definition to the list for the ACL context */
+ acc->acc_rights = rdp;
+
+ /* Indicate a new right definition was created */
+ rv = 1;
+ }
+
+ /* Return a pointer to the RightDef_t structure if indicated */
+ if (prd != 0) *prd = rdp;
+
+ return rv;
+
+ err_nomem:
+ eid = ACLERR3600;
+ rv = ACLERRNOMEM;
+ nserrGenerate(errp, rv, eid, ACL_Program, 0);
+ goto punt;
+
+ err_stadd:
+ FREE(rdp->rd_sym.sym_name);
+ FREE(rdp);
+ eid = ACLERR3620;
+ rv = ACLERRDUPSYM;
+ nserrGenerate(errp, rv, eid, ACL_Program, 1, rname);
+
+ punt:
+ return rv;
+}
+
+/*
+ * Description (aclRightSpecDestroy)
+ *
+ * This function destroys a RightSpec_t structure.
+ *
+ * Arguments:
+ *
+ * rsp - pointer to RightSpec_t structure
+ */
+
+void aclRightSpecDestroy(RightSpec_t * rsp)
+{
+ if (rsp != 0) {
+
+ UILFREE(&rsp->rs_list);
+
+ if (rsp->rs_sym.sym_name != 0) {
+ FREE(rsp->rs_sym.sym_name);
+ }
+
+ FREE(rsp);
+ }
+}
+
+/*
+ * Description (aclUserSpecCreate)
+ *
+ * This function allocates and initializes a new UserSpec_t
+ * structure, representing a list of users and groups.
+ *
+ * Arguments:
+ *
+ * None.
+ *
+ * Returns:
+ *
+ * If successful, the return value is a pointer to a new UserSpec_t.
+ * Otherwise the return value is null.
+ */
+
+UserSpec_t * aclUserSpecCreate()
+{
+ UserSpec_t * usp;
+
+ usp = (UserSpec_t *) MALLOC(sizeof(UserSpec_t));
+ if (usp != 0) {
+ memset((void *)usp, 0, sizeof(UserSpec_t));
+ usp->us_sym.sym_type = ACLSYMUSER;
+ }
+
+ return usp;
+}
+
+/*
+ * Description (aclUserSpecDestroy)
+ *
+ * This function destroys a UserSpec_t structure.
+ *
+ * Arguments:
+ *
+ * usp - pointer to UserSpec_t structure
+ */
+
+void aclUserSpecDestroy(UserSpec_t * usp)
+{
+ if (usp != 0) {
+
+ UILFREE(&usp->us_user.uu_user);
+ UILFREE(&usp->us_user.uu_group);
+
+ if (usp->us_sym.sym_name != 0) {
+ FREE(usp->us_sym.sym_name);
+ }
+
+ FREE(usp);
+ }
+}