1 /* This is copied from portmap 4.0-29 in RedHat. */
4 * pmap_check - additional portmap security.
6 * Always reject non-local requests to update the portmapper tables.
8 * Refuse to forward mount requests to the nfs mount daemon. Otherwise, the
9 * requests would appear to come from the local system, and nfs export
10 * restrictions could be bypassed.
12 * Refuse to forward requests to the nfsd process.
14 * Refuse to forward requests to NIS (YP) daemons; The only exception is the
15 * YPPROC_DOMAIN_NONACK broadcast rpc call that is used to establish initial
16 * contact with the NIS server.
18 * Always allocate an unprivileged port when forwarding a request.
20 * If compiled with -DCHECK_PORT, require that requests to register or
21 * unregister a privileged port come from a privileged port. This makes it
22 * more difficult to replace a critical service by a trojan.
24 * If compiled with -DHOSTS_ACCESS, reject requests from hosts that are not
25 * authorized by the /etc/hosts.{allow,deny} files. The local system is
26 * always treated as an authorized host. The access control tables are never
27 * consulted for requests from the local system, and are always consulted
28 * for requests from other hosts.
30 * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
31 * Computing Science, Eindhoven University of Technology, The Netherlands.
37 #include <tcpwrapper.h>
41 #include <rpc/pmap_prot.h>
45 #include <sys/types.h>
46 #include <sys/signal.h>
47 #include <sys/queue.h>
54 #include <netinet/in.h>
55 #include <rpc/rpcent.h>
58 static void logit(int severity, struct sockaddr_in *addr,
59 u_long procnum, u_long prognum, char *text);
60 static void toggle_verboselog(int sig);
61 static int check_files(void);
64 int allow_severity = LOG_INFO;
65 int deny_severity = LOG_WARNING;
67 /* A handful of macros for "readability". */
70 /* coming from libwrap.a (tcp_wrappers) */
71 extern int hosts_ctl(char *daemon, char *name, char *addr, char *user);
73 int hosts_ctl(char *daemon, char *name, char *addr, char *user)
79 #define legal_port(a,p) \
80 (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED)
82 #define log_bad_port(addr, proc, prog) \
83 logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
85 #define log_bad_host(addr, proc, prog) \
86 logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
88 #define log_bad_owner(addr, proc, prog) \
89 logit(deny_severity, addr, proc, prog, ": request from non-local host")
91 #define log_no_forward(addr, proc, prog) \
92 logit(deny_severity, addr, proc, prog, ": request not forwarded")
94 #define log_client(addr, proc, prog) \
95 logit(allow_severity, addr, proc, prog, "")
100 typedef struct _haccess_t {
101 TAILQ_ENTRY(_haccess_t) list;
106 #define HASH_TABLE_SIZE 1021
107 typedef struct _hash_head {
108 TAILQ_HEAD(host_list, _haccess_t) h_head;
110 hash_head haccess_tbl[HASH_TABLE_SIZE];
111 static haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long, u_long);
112 static void haccess_add(struct sockaddr_in *addr, u_long, u_long, int);
114 inline unsigned int strtoint(char *str)
117 int len = strlen(str);
120 for (i=0; i < len; i++)
125 inline int hashint(unsigned int num)
127 return num % HASH_TABLE_SIZE;
129 #define HASH(_addr, _proc, _prog) \
130 hashint((strtoint((_addr))+(_proc)+(_prog)))
132 void haccess_add(struct sockaddr_in *addr, u_long proc,
133 u_long prog, int access)
139 hptr = (haccess_t *)malloc(sizeof(haccess_t));
143 hash = HASH(inet_ntoa(addr->sin_addr), proc, prog);
144 head = &(haccess_tbl[hash]);
146 hptr->access = access;
147 hptr->addr.s_addr = addr->sin_addr.s_addr;
149 if (TAILQ_EMPTY(&head->h_head))
150 TAILQ_INSERT_HEAD(&head->h_head, hptr, list);
152 TAILQ_INSERT_TAIL(&head->h_head, hptr, list);
154 haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long proc, u_long prog)
160 hash = HASH(inet_ntoa(addr->sin_addr), proc, prog);
161 head = &(haccess_tbl[hash]);
163 TAILQ_FOREACH(hptr, &head->h_head, list) {
164 if (hptr->addr.s_addr == addr->sin_addr.s_addr)
171 good_client(daemon, addr)
173 struct sockaddr_in *addr;
179 /* First check the address. */
180 if (hosts_ctl(daemon, "", inet_ntoa(addr->sin_addr), "") == DENY)
183 /* Now do the hostname lookup */
184 hp = gethostbyaddr ((const char *) &(addr->sin_addr),
185 sizeof (addr->sin_addr), AF_INET);
188 "Warning: Client IP address '%s' not found in host lookup",
189 inet_ntoa(addr->sin_addr));
190 return DENY; /* never heard of it. misconfigured DNS? */
193 /* Make sure the hostent is authorative. */
194 tmpname = strdup(hp->h_name);
196 xlog(L_WARNING, "Warning: No memory for Host access check");
199 hp = gethostbyname(tmpname);
202 "Warning: Client hostname '%s' not found in host lookup", tmpname);
204 return DENY; /* never heard of it. misconfigured DNS? */
208 /* Now make sure the address is on the list */
209 for (sp = hp->h_addr_list ; *sp ; sp++) {
210 if (memcmp(*sp, &(addr->sin_addr), hp->h_length) == 0)
214 return DENY; /* it was a FAKE. */
216 /* Check the official name and address. */
217 if (hosts_ctl(daemon, hp->h_name, inet_ntoa(addr->sin_addr), "") == DENY)
220 /* Now check aliases. */
221 for (sp = hp->h_aliases; *sp ; sp++) {
222 if (hosts_ctl(daemon, *sp, inet_ntoa(addr->sin_addr), "") == DENY)
229 /* check_startup - additional startup code */
231 void check_startup(void)
235 * Give up root privileges so that we can never allocate a privileged
236 * port when forwarding an rpc request.
238 * Fix 8/3/00 Philipp Knirsch: First lookup our rpc user. If we find it,
239 * switch to that uid, otherwise simply resue the old bin user and print
240 * out a warning in syslog.
243 struct passwd *pwent;
245 pwent = getpwnam("rpc");
247 syslog(LOG_WARNING, "user rpc not found, reverting to user bin");
248 if (setuid(1) == -1) {
249 syslog(LOG_ERR, "setuid(1) failed: %m");
254 if (setuid(pwent->pw_uid) == -1) {
255 syslog(LOG_WARNING, "setuid() to rpc user failed: %m");
256 if (setuid(1) == -1) {
257 syslog(LOG_ERR, "setuid(1) failed: %m");
263 (void) signal(SIGINT, toggle_verboselog);
266 /* check_files - check to see if either access files have changed */
268 static int check_files()
270 static time_t allow_mtime, deny_mtime;
271 struct stat astat, dstat;
274 if (stat("/etc/hosts.allow", &astat) < 0)
276 if (stat("/etc/hosts.deny", &dstat) < 0)
279 if(!astat.st_mtime || !dstat.st_mtime)
282 if (astat.st_mtime != allow_mtime)
284 else if (dstat.st_mtime != deny_mtime)
287 allow_mtime = astat.st_mtime;
288 deny_mtime = dstat.st_mtime;
293 /* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
296 check_default(daemon, addr, proc, prog)
298 struct sockaddr_in *addr;
302 haccess_t *acc = NULL;
303 int changed = check_files();
305 acc = haccess_lookup(addr, proc, prog);
306 if (acc && changed == 0)
307 return (acc->access);
309 if (!(from_local(addr) || good_client(daemon, addr))) {
310 log_bad_host(addr, proc, prog);
314 haccess_add(addr, proc, prog, FALSE);
318 log_client(addr, proc, prog);
323 haccess_add(addr, proc, prog, TRUE);
327 /* check_privileged_port - additional checks for privileged-port updates */
329 check_privileged_port(struct sockaddr_in *addr,
330 u_long proc, u_long prog, u_long port)
333 if (!legal_port(addr, port)) {
334 log_bad_port(addr, proc, prog);
341 /* toggle_verboselog - toggle verbose logging flag */
343 static void toggle_verboselog(int sig)
345 (void) signal(sig, toggle_verboselog);
346 verboselog = !verboselog;
349 /* logit - report events of interest via the syslog daemon */
351 static void logit(int severity, struct sockaddr_in *addr,
352 u_long procnum, u_long prognum, char *text)
355 char procbuf[16 + 4 * sizeof(u_long)];
357 char progbuf[16 + 4 * sizeof(u_long)];
361 * Fork off a process or the portmap daemon might hang while
362 * getrpcbynumber() or syslog() does its thing.
364 * Don't forget to wait for the children, too...
369 /* Try to map program number to name. */
373 } else if ((rpc = getrpcbynumber((int) prognum))) {
374 progname = rpc->r_name;
376 snprintf(progname = progbuf, sizeof (progbuf),
377 "prog (%lu)", prognum);
380 /* Try to map procedure number to name. */
382 snprintf(procname = procbuf, sizeof (procbuf),
383 "proc (%lu)", (u_long) procnum);
385 /* Write syslog record. */
387 syslog(severity, "connect from %s to %s in %s%s",
388 inet_ntoa(addr->sin_addr), procname, progname, text);