X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=support%2Fmisc%2Ftcpwrapper.c;h=6f65c1375c4b1cfa5e2b18654604f20af649ce9f;hp=ba76864cb84a19adf5005c378f9f68d3feb4bd01;hb=90c944c9cc1fde845caa29b98c2864eb32660403;hpb=5591654c71e7e2e5959c8718a7e880516b9081e8 diff --git a/support/misc/tcpwrapper.c b/support/misc/tcpwrapper.c index ba76864..6f65c13 100644 --- a/support/misc/tcpwrapper.c +++ b/support/misc/tcpwrapper.c @@ -25,234 +25,204 @@ * authorized by the /etc/hosts.{allow,deny} files. The local system is * always treated as an authorized host. The access control tables are never * consulted for requests from the local system, and are always consulted - * for requests from other hosts. Access control is based on IP addresses - * only; attempts to map an address to a host name might cause the - * portmapper to hang. + * for requests from other hosts. * * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and * Computing Science, Eindhoven University of Technology, The Netherlands. */ -#include "tcpwrapper.h" +#ifdef HAVE_CONFIG_H +#include +#endif +#ifdef HAVE_LIBWRAP #include #include #include #include -#include #include #include #include #include +#include +#include +#include + +#include "tcpwrapper.h" +#include "xlog.h" + #ifdef SYSV40 #include #include #endif -static void logit(); -static void toggle_verboselog(); -int verboselog = 0; -int allow_severity = LOG_INFO; -int deny_severity = LOG_WARNING; - -/* A handful of macros for "readability". */ +static int check_files(void); -/* coming from libwrap.a (tcp_wrappers) */ -extern int hosts_ctl(char *daemon, char *name, char *addr, char *user); +#define ALLOW 1 +#define DENY 0 -#define legal_port(a,p) \ - (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED) +typedef struct _haccess_t { + TAILQ_ENTRY(_haccess_t) list; + int access; + struct in_addr addr; +} haccess_t; -#define log_bad_port(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request from unprivileged port") +#define HASH_TABLE_SIZE 1021 +typedef struct _hash_head { + TAILQ_HEAD(host_list, _haccess_t) h_head; +} hash_head; +hash_head haccess_tbl[HASH_TABLE_SIZE]; +static haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long); +static void haccess_add(struct sockaddr_in *addr, u_long, int); -#define log_bad_host(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request from unauthorized host") - -#define log_bad_owner(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request from non-local host") +static unsigned long +strtoint(const char *str) +{ + unsigned long i, n = 0; + size_t len = strlen(str); -#define log_no_forward(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request not forwarded") + for (i = 0; i < len; i++) + n += (unsigned char)str[i] * i; -#define log_client(addr, proc, prog) \ - logit(allow_severity, addr, proc, prog, "") + return n; +} -int -good_client(daemon, addr) -char *daemon; -struct sockaddr_in *addr; +static unsigned int +hashint(const unsigned long num) { - struct hostent *hp; - char **sp; - char *tmpname; - - /* Check the IP address first. */ - if (hosts_ctl(daemon, "", inet_ntoa(addr->sin_addr), "")) - return 1; - - /* Check the hostname. */ - hp = gethostbyaddr ((const char *) &(addr->sin_addr), - sizeof (addr->sin_addr), AF_INET); - - if (!hp) - return 0; - - /* must make sure the hostent is authorative. */ - tmpname = alloca (strlen (hp->h_name) + 1); - strcpy (tmpname, hp->h_name); - hp = gethostbyname(tmpname); - if (hp) { - /* now make sure the "addr->sin_addr" is on the list */ - for (sp = hp->h_addr_list ; *sp ; sp++) { - if (memcmp(*sp, &(addr->sin_addr), hp->h_length)==0) - break; - } - if (!*sp) - /* it was a FAKE. */ - return 0; - } - else - /* never heard of it. misconfigured DNS? */ - return 0; - - /* Check the official name first. */ - if (hosts_ctl(daemon, "", hp->h_name, "")) - return 1; - - /* Check aliases. */ - for (sp = hp->h_aliases; *sp ; sp++) { - if (hosts_ctl(daemon, "", *sp, "")) - return 1; - } - - /* No match */ - return 0; + return (unsigned int)(num % HASH_TABLE_SIZE); } -/* check_startup - additional startup code */ +static unsigned int +HASH(const char *addr, const unsigned long program) +{ + return hashint(strtoint(addr) + program); +} -void check_startup() +void haccess_add(struct sockaddr_in *addr, u_long prog, int access) { + hash_head *head; + haccess_t *hptr; + unsigned int hash; + + hptr = (haccess_t *)malloc(sizeof(haccess_t)); + if (hptr == NULL) + return; + + hash = HASH(inet_ntoa(addr->sin_addr), prog); + head = &(haccess_tbl[hash]); - /* - * Give up root privileges so that we can never allocate a privileged - * port when forwarding an rpc request. - * - * Fix 8/3/00 Philipp Knirsch: First lookup our rpc user. If we find it, - * switch to that uid, otherwise simply resue the old bin user and print - * out a warning in syslog. - */ - - struct passwd *pwent; - - pwent = getpwnam("rpc"); - if (pwent == NULL) { - syslog(LOG_WARNING, "user rpc not found, reverting to user bin"); - if (setuid(1) == -1) { - syslog(LOG_ERR, "setuid(1) failed: %m"); - exit(1); - } - } - else { - if (setuid(pwent->pw_uid) == -1) { - syslog(LOG_WARNING, "setuid() to rpc user failed: %m"); - if (setuid(1) == -1) { - syslog(LOG_ERR, "setuid(1) failed: %m"); - exit(1); - } - } - } - - (void) signal(SIGINT, toggle_verboselog); + hptr->access = access; + hptr->addr.s_addr = addr->sin_addr.s_addr; + + if (TAILQ_EMPTY(&head->h_head)) + TAILQ_INSERT_HEAD(&head->h_head, hptr, list); + else + TAILQ_INSERT_TAIL(&head->h_head, hptr, list); } +haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long prog) +{ + hash_head *head; + haccess_t *hptr; + unsigned int hash; -/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */ + hash = HASH(inet_ntoa(addr->sin_addr), prog); + head = &(haccess_tbl[hash]); -int -check_default(daemon, addr, proc, prog) -char *daemon; -struct sockaddr_in *addr; -u_long proc; -u_long prog; + TAILQ_FOREACH(hptr, &head->h_head, list) { + if (hptr->addr.s_addr == addr->sin_addr.s_addr) + return hptr; + } + return NULL; +} + +static void +logit(const struct sockaddr_in *sin) { - if (!(from_local(addr) || good_client(daemon, addr))) { - log_bad_host(addr, proc, prog); - return (FALSE); - } - if (verboselog) - log_client(addr, proc, prog); - return (TRUE); + char buf[INET_ADDRSTRLEN]; + + xlog_warn("connect from %s denied: request from unauthorized host", + inet_ntop(AF_INET, &sin->sin_addr, buf, sizeof(buf))); + } -/* check_privileged_port - additional checks for privileged-port updates */ int -check_privileged_port(addr, proc, prog, port) +good_client(daemon, addr) +char *daemon; struct sockaddr_in *addr; -u_long proc; -u_long prog; -u_long port; { -#ifdef CHECK_PORT - if (!legal_port(addr, port)) { - log_bad_port(addr, proc, prog); - return (FALSE); - } -#endif - return (TRUE); -} + struct request_info req; -/* toggle_verboselog - toggle verbose logging flag */ + request_init(&req, RQ_DAEMON, daemon, RQ_CLIENT_SIN, addr, 0); + sock_methods(&req); -static void toggle_verboselog(sig) -int sig; -{ - (void) signal(sig, toggle_verboselog); - verboselog = !verboselog; + if (hosts_access(&req)) + return ALLOW; + + return DENY; } -/* logit - report events of interest via the syslog daemon */ +/* check_files - check to see if either access files have changed */ -static void logit(severity, addr, procnum, prognum, text) -int severity; -struct sockaddr_in *addr; -u_long procnum; -u_long prognum; -char *text; +static int check_files() { - char *procname; - char procbuf[4 * sizeof(u_long)]; - char *progname; - char progbuf[4 * sizeof(u_long)]; - struct rpcent *rpc; - - /* - * Fork off a process or the portmap daemon might hang while - * getrpcbynumber() or syslog() does its thing. - * - * Don't forget to wait for the children, too... - */ - - if (fork() == 0) { - - /* Try to map program number to name. */ - - if (prognum == 0) { - progname = ""; - } else if ((rpc = getrpcbynumber((int) prognum))) { - progname = rpc->r_name; - } else { - sprintf(progname = progbuf, "%lu", prognum); - } + static time_t allow_mtime, deny_mtime; + struct stat astat, dstat; + int changed = 0; + + if (stat("/etc/hosts.allow", &astat) < 0) + astat.st_mtime = 0; + if (stat("/etc/hosts.deny", &dstat) < 0) + dstat.st_mtime = 0; - /* Try to map procedure number to name. */ + if(!astat.st_mtime || !dstat.st_mtime) + return changed; - sprintf(procname = procbuf, "%lu", (u_long) procnum); + if (astat.st_mtime != allow_mtime) + changed = 1; + else if (dstat.st_mtime != deny_mtime) + changed = 1; - /* Write syslog record. */ + allow_mtime = astat.st_mtime; + deny_mtime = dstat.st_mtime; - syslog(severity, "connect from %s to %s(%s)%s", - inet_ntoa(addr->sin_addr), procname, progname, text); - exit(0); - } + return changed; } + +/** + * check_default - additional checks for NULL, DUMP, GETPORT and unknown + * @daemon: pointer to '\0'-terminated ASCII string containing name of the + * daemon requesting the access check + * @addr: pointer to socket address containing address of caller + * @prog: RPC program number caller is attempting to access + * + * Returns TRUE if the caller is allowed access; otherwise FALSE is returned. + */ +int +check_default(char *daemon, struct sockaddr_in *addr, u_long prog) +{ + haccess_t *acc = NULL; + int changed = check_files(); + + acc = haccess_lookup(addr, prog); + if (acc && changed == 0) + return (acc->access); + + if (!(from_local((struct sockaddr *)addr) || good_client(daemon, addr))) { + logit(addr); + if (acc) + acc->access = FALSE; + else + haccess_add(addr, prog, FALSE); + return (FALSE); + } + + if (acc) + acc->access = TRUE; + else + haccess_add(addr, prog, TRUE); + + return (TRUE); +} + +#endif /* HAVE_LIBWRAP */