X-Git-Url: https://git.decadent.org.uk/gitweb/?p=nfs-utils.git;a=blobdiff_plain;f=support%2Fmisc%2Ftcpwrapper.c;h=06b0a463019ad08f7de34d3dad4bbaaf2ed8c93c;hp=f7fd3a9cd2d1bfe5dc13051e00bbd42c7771262c;hb=9bb85c5e8d2285f82367c75df5530a71a9a5a5f2;hpb=58e0a308fec476361dd21f7d3856faceb6e308ee diff --git a/support/misc/tcpwrapper.c b/support/misc/tcpwrapper.c index f7fd3a9..06b0a46 100644 --- a/support/misc/tcpwrapper.c +++ b/support/misc/tcpwrapper.c @@ -34,307 +34,236 @@ #ifdef HAVE_CONFIG_H #include #endif -#include + +#ifdef HAVE_LIBWRAP #include #include #include #include -#include #include #include #include #include #include +#include +#include + +#include "sockaddr.h" +#include "tcpwrapper.h" +#include "xlog.h" + #ifdef SYSV40 #include #include -#endif - -static void logit(int severity, struct sockaddr_in *addr, - u_long procnum, u_long prognum, char *text); -static void toggle_verboselog(int sig); -int verboselog = 0; -int allow_severity = LOG_INFO; -int deny_severity = LOG_WARNING; +#endif /* SYSV40 */ -/* A handful of macros for "readability". */ +#define ALLOW 1 +#define DENY 0 -#ifdef HAVE_LIBWRAP -/* coming from libwrap.a (tcp_wrappers) */ -extern int hosts_ctl(char *daemon, char *name, char *addr, char *user); -#else -int hosts_ctl(char *daemon, char *name, char *addr, char *user) +#ifdef IPV6_SUPPORTED +static void +present_address(const struct sockaddr *sap, char *buf, const size_t buflen) { - return 0; -} -#endif - -#define legal_port(a,p) \ - (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED) - -#define log_bad_port(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request from unprivileged port") - -#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") + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap; + socklen_t len = (socklen_t)buflen; + + switch (sap->sa_family) { + case AF_INET: + if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0) + return; + case AF_INET6: + if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len) != 0) + return; + } -#define log_no_forward(addr, proc, prog) \ - logit(deny_severity, addr, proc, prog, ": request not forwarded") + memset(buf, 0, buflen); + strncpy(buf, "unrecognized caller", buflen); +} +#else /* !IPV6_SUPPORTED */ +static void +present_address(const struct sockaddr *sap, char *buf, const size_t buflen) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sap; + socklen_t len = (socklen_t)buflen; -#define log_client(addr, proc, prog) \ - logit(allow_severity, addr, proc, prog, "") + if (sap->sa_family == AF_INET) + if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0) + return; -#define ALLOW 1 -#define DENY 0 + memset(buf, 0, buflen); + strncpy(buf, "unrecognized caller", (size_t)buflen); +} +#endif /* !IPV6_SUPPORTED */ typedef struct _haccess_t { - TAILQ_ENTRY(_haccess_t) list; - int access; - struct in_addr addr; + TAILQ_ENTRY(_haccess_t) list; + int allowed; + union nfs_sockaddr address; } haccess_t; #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, u_long); -static void haccess_add(struct sockaddr_in *addr, u_long, u_long, int); -inline unsigned int strtoint(char *str) +static hash_head haccess_tbl[HASH_TABLE_SIZE]; + +static unsigned long +strtoint(const char *str) { - unsigned int n = 0; - int len = strlen(str); - int i; + unsigned long i, n = 0; + size_t len = strlen(str); - for (i=0; i < len; i++) - n+=((int)str[i])*i; + for (i = 0; i < len; i++) + n += (unsigned char)str[i] * i; return n; } -inline int hashint(unsigned int num) + +static unsigned int +hashint(const unsigned long num) +{ + return (unsigned int)(num % HASH_TABLE_SIZE); +} + +static unsigned int +HASH(const char *addr, const unsigned long program) { - return num % HASH_TABLE_SIZE; + return hashint(strtoint(addr) + program); } -#define HASH(_addr, _proc, _prog) \ - hashint((strtoint((_addr))+(_proc)+(_prog))) -void haccess_add(struct sockaddr_in *addr, u_long proc, - u_long prog, int access) +static void +haccess_add(const struct sockaddr *sap, const char *address, + const unsigned long program, const int allowed) { hash_head *head; - haccess_t *hptr; - int hash; + haccess_t *hptr; + unsigned int hash; hptr = (haccess_t *)malloc(sizeof(haccess_t)); if (hptr == NULL) return; - hash = HASH(inet_ntoa(addr->sin_addr), proc, prog); + hash = HASH(address, program); head = &(haccess_tbl[hash]); - hptr->access = access; - hptr->addr.s_addr = addr->sin_addr.s_addr; + hptr->allowed = allowed; + memcpy(&hptr->address, sap, (size_t)nfs_sockaddr_length(sap)); 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 proc, u_long prog) + +static haccess_t * +haccess_lookup(const struct sockaddr *sap, const char *address, + const unsigned long program) { hash_head *head; - haccess_t *hptr; - int hash; + haccess_t *hptr; + unsigned int hash; - hash = HASH(inet_ntoa(addr->sin_addr), proc, prog); + hash = HASH(address, program); head = &(haccess_tbl[hash]); TAILQ_FOREACH(hptr, &head->h_head, list) { - if (hptr->addr.s_addr == addr->sin_addr.s_addr) + if (nfs_compare_sockaddr(&hptr->address.sa, sap)) return hptr; } return NULL; } -int -good_client(daemon, addr) -char *daemon; -struct sockaddr_in *addr; +static void +logit(const char *address) { - struct hostent *hp; - char **sp; - char *tmpname; - - /* First check the address. */ - if (hosts_ctl(daemon, "", inet_ntoa(addr->sin_addr), "") == DENY) - return DENY; - - /* Now do the hostname lookup */ - hp = gethostbyaddr ((const char *) &(addr->sin_addr), - sizeof (addr->sin_addr), AF_INET); - if (!hp) - return DENY; /* never heard of it. misconfigured DNS? */ - - /* Make sure the hostent is authorative. */ - tmpname = strdup(hp->h_name); - if (!tmpname) - return DENY; - hp = gethostbyname(tmpname); - free(tmpname); - if (!hp) - return DENY; /* never heard of it. misconfigured DNS? */ - - /* Now make sure the address 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) - return DENY; /* it was a FAKE. */ + xlog_warn("connect from %s denied: request from unauthorized host", + address); +} - /* Check the official name and address. */ - if (hosts_ctl(daemon, hp->h_name, inet_ntoa(addr->sin_addr), "") == DENY) - return DENY; +static int +good_client(char *name, struct sockaddr *sap) +{ + struct request_info req; - /* Now check aliases. */ - for (sp = hp->h_aliases; *sp ; sp++) { - if (hosts_ctl(daemon, *sp, inet_ntoa(addr->sin_addr), "") == DENY) - return DENY; - } + request_init(&req, RQ_DAEMON, name, RQ_CLIENT_SIN, sap, 0); + sock_methods(&req); - return ALLOW; -} + if (hosts_access(&req)) + return ALLOW; -/* check_startup - additional startup code */ + return DENY; +} -void check_startup(void) +static int +check_files(void) { + static time_t allow_mtime, deny_mtime; + struct stat astat, dstat; + int changed = 0; - /* - * 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); -} + if (stat("/etc/hosts.allow", &astat) < 0) + astat.st_mtime = 0; + if (stat("/etc/hosts.deny", &dstat) < 0) + dstat.st_mtime = 0; -/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */ + if(!astat.st_mtime || !dstat.st_mtime) + return changed; -int -check_default(daemon, addr, proc, prog) -char *daemon; -struct sockaddr_in *addr; -u_long proc; -u_long prog; -{ - haccess_t *acc = NULL; + if (astat.st_mtime != allow_mtime) + changed = 1; + else if (dstat.st_mtime != deny_mtime) + changed = 1; - acc = haccess_lookup(addr, proc, prog); - if (acc) - return (acc->access); - - if (!(from_local(addr) || good_client(daemon, addr))) { - log_bad_host(addr, proc, prog); - haccess_add(addr, proc, prog, FALSE); - return (FALSE); - } - if (verboselog) - log_client(addr, proc, prog); + allow_mtime = astat.st_mtime; + deny_mtime = dstat.st_mtime; - haccess_add(addr, proc, prog, TRUE); - return (TRUE); + return changed; } -/* check_privileged_port - additional checks for privileged-port updates */ +/** + * check_default - additional checks for NULL, DUMP, GETPORT and unknown + * @name: pointer to '\0'-terminated ASCII string containing name of the + * daemon requesting the access check + * @sap: pointer to sockaddr containing network address of caller + * @program: RPC program number caller is attempting to access + * + * Returns TRUE if the caller is allowed access; otherwise FALSE is returned. + */ int -check_privileged_port(struct sockaddr_in *addr, - u_long proc, u_long prog, u_long port) +check_default(char *name, struct sockaddr *sap, const unsigned long program) { -#ifdef CHECK_PORT - if (!legal_port(addr, port)) { - log_bad_port(addr, proc, prog); - return (FALSE); - } -#endif - return (TRUE); -} - -/* toggle_verboselog - toggle verbose logging flag */ - -static void toggle_verboselog(int sig) -{ - (void) signal(sig, toggle_verboselog); - verboselog = !verboselog; -} + haccess_t *acc = NULL; + int changed = check_files(); + char buf[INET6_ADDRSTRLEN]; -/* logit - report events of interest via the syslog daemon */ + present_address(sap, buf, sizeof(buf)); -static void logit(int severity, struct sockaddr_in *addr, - u_long procnum, u_long prognum, char *text) -{ - char *procname; - char procbuf[16 + 4 * sizeof(u_long)]; - char *progname; - char progbuf[16 + 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 { - snprintf(progname = progbuf, sizeof (progbuf), - "prog (%lu)", prognum); + acc = haccess_lookup(sap, buf, program); + if (acc != NULL && changed == 0) { + xlog(D_GENERAL, "%s: access by %s %s (cached)", __func__, + buf, acc->allowed ? "ALLOWED" : "DENIED"); + return acc->allowed; } - /* Try to map procedure number to name. */ - - snprintf(procname = procbuf, sizeof (procbuf), - "proc (%lu)", (u_long) procnum); + if (!(from_local(sap) || good_client(name, sap))) { + logit(buf); + if (acc != NULL) + acc->allowed = FALSE; + else + haccess_add(sap, buf, program, FALSE); + xlog(D_GENERAL, "%s: access by %s DENIED", __func__, buf); + return (FALSE); + } - /* Write syslog record. */ + if (acc != NULL) + acc->allowed = TRUE; + else + haccess_add(sap, buf, program, TRUE); + xlog(D_GENERAL, "%s: access by %s ALLOWED", __func__, buf); - syslog(severity, "connect from %s to %s in %s%s", - inet_ntoa(addr->sin_addr), procname, progname, text); - exit(0); - } + return (TRUE); } + +#endif /* HAVE_LIBWRAP */