]> git.decadent.org.uk Git - nfs-utils.git/blob - support/misc/tcpwrapper.c
configure: use "--disable-uuid" instead of "--without-uuid"
[nfs-utils.git] / support / misc / tcpwrapper.c
1 /* This is copied from portmap 4.0-29 in RedHat. */
2
3  /*
4   * pmap_check - additional portmap security.
5   * 
6   * Always reject non-local requests to update the portmapper tables.
7   * 
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.
11   * 
12   * Refuse to forward requests to the nfsd process.
13   * 
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.
17   * 
18   * Always allocate an unprivileged port when forwarding a request.
19   * 
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.
23   * 
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.
29   * 
30   * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
31   * Computing Science, Eindhoven University of Technology, The Netherlands.
32   */
33
34 #ifdef HAVE_CONFIG_H
35 #include <config.h>
36 #endif
37 #include <tcpwrapper.h>
38 #include <unistd.h>
39 #include <string.h>
40 #include <rpc/rpc.h>
41 #include <rpc/pmap_prot.h>
42 #include <syslog.h>
43 #include <netdb.h>
44 #include <pwd.h>
45 #include <sys/types.h>
46 #include <sys/signal.h>
47 #include <sys/queue.h>
48 #include <sys/stat.h>
49 #include <unistd.h>
50
51 #include "xlog.h"
52
53 #ifdef SYSV40
54 #include <netinet/in.h>
55 #include <rpc/rpcent.h>
56 #endif
57
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);
62
63 int     verboselog = 0;
64 int     allow_severity = LOG_INFO;
65 int     deny_severity = LOG_WARNING;
66
67 /* A handful of macros for "readability". */
68
69 #ifdef HAVE_LIBWRAP
70 /* coming from libwrap.a (tcp_wrappers) */
71 extern int hosts_ctl(char *daemon, char *name, char *addr, char *user);
72 #else
73 int hosts_ctl(char *daemon, char *name, char *addr, char *user)
74 {
75         return 0;
76 }
77 #endif
78
79 #define legal_port(a,p) \
80   (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED)
81
82 #define log_bad_port(addr, proc, prog) \
83   logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
84
85 #define log_bad_host(addr, proc, prog) \
86   logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
87
88 #define log_bad_owner(addr, proc, prog) \
89   logit(deny_severity, addr, proc, prog, ": request from non-local host")
90
91 #define log_no_forward(addr, proc, prog) \
92   logit(deny_severity, addr, proc, prog, ": request not forwarded")
93
94 #define log_client(addr, proc, prog) \
95   logit(allow_severity, addr, proc, prog, "")
96
97 #define ALLOW 1
98 #define DENY 0
99
100 typedef struct _haccess_t {
101         TAILQ_ENTRY(_haccess_t) list;
102         int access;
103     struct in_addr addr;
104 } haccess_t;
105
106 #define HASH_TABLE_SIZE 1021
107 typedef struct _hash_head {
108         TAILQ_HEAD(host_list, _haccess_t) h_head;
109 } hash_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);
113
114 inline unsigned int strtoint(char *str)
115 {
116         unsigned int n = 0;
117         int len = strlen(str);
118         int i;
119
120         for (i=0; i < len; i++)
121                 n+=((int)str[i])*i;
122
123         return n;
124 }
125 inline int hashint(unsigned int num)
126 {
127         return num % HASH_TABLE_SIZE;
128 }
129 #define HASH(_addr, _proc, _prog) \
130         hashint((strtoint((_addr))+(_proc)+(_prog)))
131
132 void haccess_add(struct sockaddr_in *addr, u_long proc, 
133         u_long prog, int access)
134 {
135         hash_head *head;
136         haccess_t *hptr;
137         int hash;
138
139         hptr = (haccess_t *)malloc(sizeof(haccess_t));
140         if (hptr == NULL)
141                 return;
142
143         hash = HASH(inet_ntoa(addr->sin_addr), proc, prog);
144         head = &(haccess_tbl[hash]);
145
146         hptr->access = access;
147         hptr->addr.s_addr = addr->sin_addr.s_addr;
148
149         if (TAILQ_EMPTY(&head->h_head))
150                 TAILQ_INSERT_HEAD(&head->h_head, hptr, list);
151         else
152                 TAILQ_INSERT_TAIL(&head->h_head, hptr, list);
153 }
154 haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long proc, u_long prog)
155 {
156         hash_head *head;
157         haccess_t *hptr;
158         int hash;
159
160         hash = HASH(inet_ntoa(addr->sin_addr), proc, prog);
161         head = &(haccess_tbl[hash]);
162
163         TAILQ_FOREACH(hptr, &head->h_head, list) {
164                 if (hptr->addr.s_addr == addr->sin_addr.s_addr)
165                         return hptr;
166         }
167         return NULL;
168 }
169
170 int
171 good_client(daemon, addr)
172 char *daemon;
173 struct sockaddr_in *addr;
174 {
175     struct hostent *hp;
176     char **sp;
177     char *tmpname;
178
179         /* First check the address. */
180         if (hosts_ctl(daemon, "", inet_ntoa(addr->sin_addr), "") == DENY)
181                 return DENY;
182
183         /* Now do the hostname lookup */
184         hp = gethostbyaddr ((const char *) &(addr->sin_addr),
185                 sizeof (addr->sin_addr), AF_INET);
186         if (!hp) {
187                 xlog(L_WARNING, 
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? */
191         }
192
193         /* Make sure the hostent is authorative. */
194         tmpname = strdup(hp->h_name);
195         if (!tmpname) {
196                 xlog(L_WARNING, "Warning: No memory for Host access check");
197                 return DENY;
198         }
199         hp = gethostbyname(tmpname);
200         if (!hp) {
201                 xlog(L_WARNING, 
202                         "Warning: Client hostname '%s' not found in host lookup", tmpname);
203                 free(tmpname);
204                 return DENY; /* never heard of it. misconfigured DNS? */
205         }
206         free(tmpname);
207
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)
211                         break;
212         }
213         if (!*sp)
214             return DENY; /* it was a FAKE. */
215
216         /* Check the official name and address. */
217         if (hosts_ctl(daemon, hp->h_name, inet_ntoa(addr->sin_addr), "") == DENY)
218                 return DENY;
219
220         /* Now check aliases. */
221         for (sp = hp->h_aliases; *sp ; sp++) {
222                 if (hosts_ctl(daemon, *sp, inet_ntoa(addr->sin_addr), "") == DENY)
223                 return DENY;
224         }
225
226    return ALLOW;
227 }
228
229 /* check_startup - additional startup code */
230
231 void    check_startup(void)
232 {
233
234     /*
235      * Give up root privileges so that we can never allocate a privileged
236      * port when forwarding an rpc request.
237      *
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.
241      */
242
243     struct passwd *pwent;
244
245     pwent = getpwnam("rpc");
246     if (pwent == NULL) {
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");
250             exit(1);
251         }
252     }
253     else {
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");
258                 exit(1);
259             }
260         }
261     }
262
263     (void) signal(SIGINT, toggle_verboselog);
264 }
265
266 /* check_files - check to see if either access files have changed */
267
268 static int check_files()
269 {
270         static time_t allow_mtime, deny_mtime;
271         struct stat astat, dstat;
272         int changed = 0;
273
274         if (stat("/etc/hosts.allow", &astat) < 0)
275                 astat.st_mtime = 0;
276         if (stat("/etc/hosts.deny", &dstat) < 0)
277                 dstat.st_mtime = 0;
278
279         if(!astat.st_mtime || !dstat.st_mtime)
280                 return changed;
281
282         if (astat.st_mtime != allow_mtime)
283                 changed = 1;
284         else if (dstat.st_mtime != deny_mtime)
285                 changed = 1;
286
287         allow_mtime = astat.st_mtime;
288         deny_mtime = dstat.st_mtime;
289
290         return changed;
291 }
292
293 /* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
294
295 int
296 check_default(daemon, addr, proc, prog)
297 char *daemon;
298 struct sockaddr_in *addr;
299 u_long  proc;
300 u_long  prog;
301 {
302         haccess_t *acc = NULL;
303         int changed = check_files();
304
305         acc = haccess_lookup(addr, proc, prog);
306         if (acc && changed == 0)
307                 return (acc->access);
308
309         if (!(from_local(addr) || good_client(daemon, addr))) {
310                 log_bad_host(addr, proc, prog);
311                 if (acc)
312                         acc->access = FALSE;
313                 else 
314                         haccess_add(addr, proc, prog, FALSE);
315                 return (FALSE);
316         }
317         if (verboselog)
318                 log_client(addr, proc, prog);
319
320         if (acc)
321                 acc->access = TRUE;
322         else 
323                 haccess_add(addr, proc, prog, TRUE);
324     return (TRUE);
325 }
326
327 /* check_privileged_port - additional checks for privileged-port updates */
328 int
329 check_privileged_port(struct sockaddr_in *addr, 
330                       u_long proc, u_long prog, u_long port)
331 {
332 #ifdef CHECK_PORT
333     if (!legal_port(addr, port)) {
334         log_bad_port(addr, proc, prog);
335         return (FALSE);
336     }
337 #endif
338     return (TRUE);
339 }
340
341 /* toggle_verboselog - toggle verbose logging flag */
342
343 static void toggle_verboselog(int sig)
344 {
345     (void) signal(sig, toggle_verboselog);
346     verboselog = !verboselog;
347 }
348
349 /* logit - report events of interest via the syslog daemon */
350
351 static void logit(int severity, struct sockaddr_in *addr,
352                   u_long procnum, u_long prognum, char *text)
353 {
354     char   *procname;
355     char    procbuf[16 + 4 * sizeof(u_long)];
356     char   *progname;
357     char    progbuf[16 + 4 * sizeof(u_long)];
358     struct rpcent *rpc;
359
360     /*
361      * Fork off a process or the portmap daemon might hang while
362      * getrpcbynumber() or syslog() does its thing.
363      *
364      * Don't forget to wait for the children, too...
365      */
366
367     if (fork() == 0) {
368
369         /* Try to map program number to name. */
370
371         if (prognum == 0) {
372             progname = "";
373         } else if ((rpc = getrpcbynumber((int) prognum))) {
374             progname = rpc->r_name;
375         } else {
376             snprintf(progname = progbuf, sizeof (progbuf),
377                      "prog (%lu)", prognum);
378         }
379
380         /* Try to map procedure number to name. */
381
382         snprintf(procname = procbuf, sizeof (procbuf),
383                  "proc (%lu)", (u_long) procnum);
384
385         /* Write syslog record. */
386
387         syslog(severity, "connect from %s to %s in %s%s",
388                inet_ntoa(addr->sin_addr), procname, progname, text);
389         exit(0);
390     }
391 }