#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
-#include <tcpwrapper.h>
+
+#ifdef HAVE_LIBWRAP
#include <unistd.h>
#include <string.h>
#include <rpc/rpc.h>
#include <rpc/pmap_prot.h>
-#include <syslog.h>
#include <netdb.h>
#include <pwd.h>
#include <sys/types.h>
#include <sys/signal.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+#include <tcpd.h>
+
+#include "sockaddr.h"
+#include "tcpwrapper.h"
+#include "xlog.h"
+
#ifdef SYSV40
#include <netinet/in.h>
#include <rpc/rpcent.h>
-#endif
+#endif /* SYSV40 */
-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;
+#define ALLOW 1
+#define DENY 0
-/* A handful of macros for "readability". */
+#ifdef 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;
+ 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;
+ }
-#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)
+ 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)
{
- return 0;
+ const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
+ socklen_t len = (socklen_t)buflen;
+
+ if (sap->sa_family == AF_INET)
+ if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0)
+ return;
+
+ memset(buf, 0, buflen);
+ strncpy(buf, "unrecognized caller", (size_t)buflen);
}
-#endif
+#endif /* !IPV6_SUPPORTED */
-#define legal_port(a,p) \
- (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED)
+typedef struct _haccess_t {
+ TAILQ_ENTRY(_haccess_t) list;
+ int allowed;
+ union nfs_sockaddr address;
+} 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;
-#define log_bad_host(addr, proc, prog) \
- logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
+static hash_head haccess_tbl[HASH_TABLE_SIZE];
-#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)
+static void
+haccess_add(const struct sockaddr *sap, const char *address,
+ const unsigned long program, const int allowed)
{
+ hash_head *head;
+ haccess_t *hptr;
+ unsigned int 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 = (haccess_t *)malloc(sizeof(haccess_t));
+ if (hptr == NULL)
+ return;
-/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
+ hash = HASH(address, program);
+ head = &(haccess_tbl[hash]);
-int
-check_default(daemon, addr, proc, prog)
-char *daemon;
-struct sockaddr_in *addr;
-u_long proc;
-u_long prog;
-{
- 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);
+ 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);
}
-/* check_privileged_port - additional checks for privileged-port updates */
-int
-check_privileged_port(struct sockaddr_in *addr,
- u_long proc, u_long prog, u_long port)
+static haccess_t *
+haccess_lookup(const struct sockaddr *sap, const char *address,
+ const unsigned long program)
{
-#ifdef CHECK_PORT
- if (!legal_port(addr, port)) {
- log_bad_port(addr, proc, prog);
- return (FALSE);
- }
-#endif
- return (TRUE);
+ hash_head *head;
+ haccess_t *hptr;
+ unsigned int hash;
+
+ hash = HASH(address, program);
+ head = &(haccess_tbl[hash]);
+
+ TAILQ_FOREACH(hptr, &head->h_head, list) {
+ if (nfs_compare_sockaddr(&hptr->address.sa, sap))
+ return hptr;
+ }
+ return NULL;
}
-/* toggle_verboselog - toggle verbose logging flag */
+static void
+logit(const char *address)
+{
+ xlog_warn("connect from %s denied: request from unauthorized host",
+ address);
+}
-static void toggle_verboselog(int sig)
+static int
+good_client(char *name, struct sockaddr *sap)
{
- (void) signal(sig, toggle_verboselog);
- verboselog = !verboselog;
+ struct request_info req;
+
+ request_init(&req, RQ_DAEMON, name, RQ_CLIENT_SIN, sap, 0);
+ sock_methods(&req);
+
+ if (hosts_access(&req))
+ return ALLOW;
+
+ return DENY;
}
-/* logit - report events of interest via the syslog daemon */
+static int
+check_files(void)
+{
+ 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;
-static void logit(int severity, struct sockaddr_in *addr,
- u_long procnum, u_long prognum, char *text)
+ if(!astat.st_mtime || !dstat.st_mtime)
+ return changed;
+
+ if (astat.st_mtime != allow_mtime)
+ changed = 1;
+ else if (dstat.st_mtime != deny_mtime)
+ changed = 1;
+
+ allow_mtime = astat.st_mtime;
+ deny_mtime = dstat.st_mtime;
+
+ return changed;
+}
+
+/**
+ * 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_default(char *name, struct sockaddr *sap, const unsigned long program)
{
- 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);
- }
+ haccess_t *acc = NULL;
+ int changed = check_files();
+ char buf[INET6_ADDRSTRLEN];
- /* Try to map procedure number to name. */
+ present_address(sap, buf, sizeof(buf));
- snprintf(procname = procbuf, sizeof (procbuf),
- "proc (%lu)", (u_long) procnum);
+ 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;
+ }
- /* Write syslog record. */
+ 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);
+ }
- syslog(severity, "connect from %s to %s in %s%s",
- inet_ntoa(addr->sin_addr), procname, progname, text);
- exit(0);
- }
+ if (acc != NULL)
+ acc->allowed = TRUE;
+ else
+ haccess_add(sap, buf, program, TRUE);
+ xlog(D_GENERAL, "%s: access by %s ALLOWED", __func__, buf);
+
+ return (TRUE);
}
+
+#endif /* HAVE_LIBWRAP */