/** 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 #endif /* lasdns.c * This file contains the DNS LAS code. */ #include #include #ifdef XP_WIN32 /* #include */ #include #else #include #include #include #include #include #endif #include extern "C" { #include } #include #include #include #include #include #include "aclpriv.h" #include #include #include "lasdns.h" #include "aclutil.h" #include "aclcache.h" #include "permhash.h" #include #include #include "access_plhash.h" extern "C" { #include } #ifdef UTEST extern int LASDnsGetDns(char **dnsv); #endif /* LASDnsMatch * Given an array of fully-qualified dns names, tries to match them * against a given hash table. * INPUT * dns DNS string * context pointer to an LAS DNS context structure * * In our usage, this context is derived from * an acllist cache, which is a representation of * of some global acis--in other words it's a global thing * shared between threads--hence the use * of the "read-only" hash lookup routines here. */ int LASDnsMatch(char *token, LASDnsContext_t *context) { /* Test for the unusual case where "*" is allowed */ if (ACL_HashTableLookup_const(context->Table, "*")) return LAS_EVAL_TRUE; /* Start with the full name. Then strip off each component * leaving the remainder starting with a period. E.g. * splash.mcom.com * .mcom.com * .com * Search the hash table for each remaining portion. Remember that * wildcards were put in with the leading period intact. */ do { if (ACL_HashTableLookup_const(context->Table, token)) return LAS_EVAL_TRUE; token = strchr(&token[1], '.'); } while (token != NULL); return LAS_EVAL_FALSE; } /* LASDNSBuild * Builds a hash table of all the hostnames provided (plus their aliases * if aliasflg is true). Wildcards are only permitted in the leftmost * field. They're represented in the hash table by a leading period. * E.g. ".mcom.com". * * RETURNS Zero on success, else LAS_EVAL_INVALID */ int LASDnsBuild(NSErr_t *errp, char *attr_pattern, LASDnsContext_t *context, int aliasflg) { size_t delimiter; /* length of valid token */ char token[256]; /* max length dns name */ int i; int ipcnt; char **p; unsigned long *ipaddrs=0; pool_handle_t *pool; PRStatus error=PR_SUCCESS; char buffer[PR_NETDB_BUF_SIZE]; #ifdef UTEST struct hostent *he, host; #else PRHostEnt *he, host; #endif context->Table = PR_NewHashTable(0, PR_HashCaseString, PR_CompareCaseStrings, PR_CompareValues, &ACLPermAllocOps, NULL); pool = pool_create(); context->pool = pool; if ((!context->Table) || (!context->pool)) { nserrGenerate(errp, ACLERRNOMEM, ACLERR4700, ACL_Program, 1, XP_GetAdminStr(DBT_lasdnsbuildUnableToAllocateHashT_)); return LAS_EVAL_INVALID; } do { size_t maxsize = sizeof(token); /* Get a single hostname from the pattern string */ delimiter = strcspn(attr_pattern, ", \t"); if (delimiter >= maxsize) { delimiter = maxsize-1; } PL_strncpyz(token, attr_pattern, delimiter + 1); token[delimiter] = '\0'; /* Skip any white space after the token */ attr_pattern += delimiter; attr_pattern += strspn(attr_pattern, ", \t"); /* If there's a wildcard, strip it off but leave the "." * Can't have aliases for a wildcard pattern. * Treat "*" as a special case. If so, go ahead and hash it. */ if (token[0] == '*') { if (token[1] != '\0') { if (!PR_HashTableAdd(context->Table, pool_strdup(pool, &token[1]), (void *)-1)) { nserrGenerate(errp, ACLERRFAIL, ACLERR4710, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); return LAS_EVAL_INVALID; } } else { if (!PR_HashTableAdd(context->Table, pool_strdup(pool, token), (void *)-1)) { nserrGenerate(errp, ACLERRFAIL, ACLERR4720, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); return LAS_EVAL_INVALID; } } } else { /* This is a single hostname add it to the hash table */ if (!PR_HashTableAdd(context->Table, pool_strdup(pool, &token[0]), (void *)-1)) { nserrGenerate(errp, ACLERRFAIL, ACLERR4730, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), token); return LAS_EVAL_INVALID; } if (aliasflg) { /* memset(buffer, '\0', PR_NETDB_BUF_SIZE); */ #ifdef UTEST he = gethostbyname(token); #else error = PR_GetHostByName(token, buffer, PR_NETDB_BUF_SIZE, &host); if (error == PR_SUCCESS) he = &host; else he = NULL; #endif if (he) { /* Make a copy of the list of IP addresses if any */ if (he->h_addr_list && he->h_addr_list[0]) { /* Count the IP addresses */ for (p = he->h_addr_list, ipcnt = 0; *p; ++p) { ++ipcnt; } /* Allocate space */ ipaddrs = (unsigned long *)PERM_MALLOC(ipcnt * sizeof(unsigned long)); /* Copy IP addresses */ for (i = 0; i < ipcnt; ++i) { ipaddrs[i] = 0; memcpy((void *)&ipaddrs[i], he->h_addr_list[i], 4); } } /* Add each of the aliases to the list */ if (he->h_aliases && he->h_aliases[0]) { for (p = he->h_aliases; *p; ++p) { /* Add it to the hash table */ if (!PR_HashTableAdd(context->Table, pool_strdup(pool, *p), (void*)-1)) { nserrGenerate(errp, ACLERRFAIL, ACLERR4740, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), *p); PERM_FREE(ipaddrs); return LAS_EVAL_INVALID; } } } for (i = 0; i < ipcnt; ++i) { #ifdef UTEST he = gethostbyaddr((char *)&ipaddrs[i], 4, AF_INET); #else error = PR_GetHostByAddr((PRNetAddr *)&ipaddrs[i], buffer, PR_NETDB_BUF_SIZE, &host); if (error == PR_SUCCESS) he = &host; else he = NULL; #endif if (he) { if (he->h_name) { /* Add it to the hash table */ if (!PR_HashTableAdd(context->Table, pool_strdup(pool, he->h_name), (void *)-1)) { nserrGenerate(errp, ACLERRFAIL, ACLERR4750, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), he->h_name); PERM_FREE(ipaddrs); return LAS_EVAL_INVALID; } } if (he->h_aliases && he->h_aliases[0]) { for (p = he->h_aliases; *p; ++p) { /* Add it to the hash table */ if (!PR_HashTableAdd(context->Table, pool_strdup(pool, *p), (void *)-1)) { nserrGenerate(errp, ACLERRFAIL, ACLERR4760, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsbuildUnableToAddKeySN_), *p); PERM_FREE(ipaddrs); return LAS_EVAL_INVALID; } } } } } PERM_FREE(ipaddrs); } /* if he */ } /* if aliasflg */ } /* else - single hostname */ } while ((attr_pattern != NULL) && (attr_pattern[0] != '\0') && (delimiter != 0)); return 0; } /* LASDnsFlush * Given the address of a las_cookie for a DNS expression entry, frees up * all allocated memory for it. This includes the hash table, plus the * context structure. */ void LASDnsFlush(void **las_cookie) { if (*las_cookie == NULL) return; pool_destroy(((LASDnsContext_t *)*las_cookie)->pool); PR_HashTableDestroy(((LASDnsContext_t *)*las_cookie)->Table); PERM_FREE(*las_cookie); *las_cookie = NULL; return; } /* * LASDnsEval * INPUT * attr_name The string "dns" - in lower case. * comparator CMP_OP_EQ or CMP_OP_NE only * attr_pattern A comma-separated list of DNS names * Any segment(s) in a DNS name can be wildcarded using * "*". Note that this is not a true Regular Expression * form. * *cachable Always set to ACL_INDEF_CACHE * subject Subject property list * resource Resource property list * auth_info Authentication info, if any * RETURNS * ret code The usual LAS return codes. */ int LASDnsEval(NSErr_t *errp, char *attr_name, CmpOp_t comparator, char *attr_pattern, ACLCachable_t *cachable, void **LAS_cookie, PList_t subject, PList_t resource, PList_t auth_info, PList_t global_auth) { int result; int aliasflg; char *my_dns; LASDnsContext_t *context; int rv; *cachable = ACL_INDEF_CACHABLE; if (strcmp(attr_name, "dns") == 0) aliasflg = 0; else if (strcmp(attr_name, "dnsalias") == 0) aliasflg = 1; else { nserrGenerate(errp, ACLERRINVAL, ACLERR4800, ACL_Program, 2, XP_GetAdminStr(DBT_lasDnsBuildReceivedRequestForAtt_), attr_name); return LAS_EVAL_INVALID; } if ((comparator != CMP_OP_EQ) && (comparator != CMP_OP_NE)) { nserrGenerate(errp, ACLERRINVAL, ACLERR4810, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsevalIllegalComparatorDN_), comparator_string(comparator)); return LAS_EVAL_INVALID; } /* If this is the first time through, build the pattern tree first. */ if (*LAS_cookie == NULL) { ACL_CritEnter(); if (*LAS_cookie == NULL) { /* Must check again */ *LAS_cookie = context = (LASDnsContext_t *)PERM_MALLOC(sizeof(LASDnsContext_t)); if (context == NULL) { nserrGenerate(errp, ACLERRNOMEM, ACLERR4820, ACL_Program, 1, XP_GetAdminStr(DBT_lasdnsevalUnableToAllocateContex_)); ACL_CritExit(); return LAS_EVAL_FAIL; } context->Table = NULL; LASDnsBuild(errp, attr_pattern, context, aliasflg); } ACL_CritExit(); } else context = (LASDnsContext *) *LAS_cookie; /* Call the DNS attribute getter */ #ifdef UTEST LASDnsGetDns(&my_dns); /* gets stuffed on return */ #else rv = ACL_GetAttribute(errp, ACL_ATTR_DNS, (void **)&my_dns, subject, resource, auth_info, global_auth); if (rv != LAS_EVAL_TRUE) { if (subject || resource) { char rv_str[16]; /* Don't ereport if called from ACL_CachableAclList */ sprintf(rv_str, "%d", rv); nserrGenerate(errp, ACLERRINVAL, ACLERR4830, ACL_Program, 2, XP_GetAdminStr(DBT_lasdnsevalUnableToGetDnsErrorDN_), rv_str); } return LAS_EVAL_FAIL; } #endif result = LASDnsMatch(my_dns, context); if (comparator == CMP_OP_NE) { if (result == LAS_EVAL_FALSE) return LAS_EVAL_TRUE; else if (result == LAS_EVAL_TRUE) return LAS_EVAL_FALSE; } return (result); }