]> git.decadent.org.uk Git - nfs-utils.git/blob - support/misc/tcpwrapper.c
To ensure the hash table of clients has valid
[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 #ifdef SYSV40
52 #include <netinet/in.h>
53 #include <rpc/rpcent.h>
54 #endif
55
56 static void logit(int severity, struct sockaddr_in *addr,
57                   u_long procnum, u_long prognum, char *text);
58 static void toggle_verboselog(int sig);
59 static int check_files(void);
60
61 int     verboselog = 0;
62 int     allow_severity = LOG_INFO;
63 int     deny_severity = LOG_WARNING;
64
65 /* A handful of macros for "readability". */
66
67 #ifdef HAVE_LIBWRAP
68 /* coming from libwrap.a (tcp_wrappers) */
69 extern int hosts_ctl(char *daemon, char *name, char *addr, char *user);
70 #else
71 int hosts_ctl(char *daemon, char *name, char *addr, char *user)
72 {
73         return 0;
74 }
75 #endif
76
77 #define legal_port(a,p) \
78   (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED)
79
80 #define log_bad_port(addr, proc, prog) \
81   logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
82
83 #define log_bad_host(addr, proc, prog) \
84   logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
85
86 #define log_bad_owner(addr, proc, prog) \
87   logit(deny_severity, addr, proc, prog, ": request from non-local host")
88
89 #define log_no_forward(addr, proc, prog) \
90   logit(deny_severity, addr, proc, prog, ": request not forwarded")
91
92 #define log_client(addr, proc, prog) \
93   logit(allow_severity, addr, proc, prog, "")
94
95 #define ALLOW 1
96 #define DENY 0
97
98 typedef struct _haccess_t {
99         TAILQ_ENTRY(_haccess_t) list;
100         int access;
101     struct in_addr addr;
102 } haccess_t;
103
104 #define HASH_TABLE_SIZE 1021
105 typedef struct _hash_head {
106         TAILQ_HEAD(host_list, _haccess_t) h_head;
107 } hash_head;
108 hash_head haccess_tbl[HASH_TABLE_SIZE];
109 static haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long, u_long);
110 static void haccess_add(struct sockaddr_in *addr, u_long, u_long, int);
111
112 inline unsigned int strtoint(char *str)
113 {
114         unsigned int n = 0;
115         int len = strlen(str);
116         int i;
117
118         for (i=0; i < len; i++)
119                 n+=((int)str[i])*i;
120
121         return n;
122 }
123 inline int hashint(unsigned int num)
124 {
125         return num % HASH_TABLE_SIZE;
126 }
127 #define HASH(_addr, _proc, _prog) \
128         hashint((strtoint((_addr))+(_proc)+(_prog)))
129
130 void haccess_add(struct sockaddr_in *addr, u_long proc, 
131         u_long prog, int access)
132 {
133         hash_head *head;
134         haccess_t *hptr;
135         int hash;
136
137         hptr = (haccess_t *)malloc(sizeof(haccess_t));
138         if (hptr == NULL)
139                 return;
140
141         hash = HASH(inet_ntoa(addr->sin_addr), proc, prog);
142         head = &(haccess_tbl[hash]);
143
144         hptr->access = access;
145         hptr->addr.s_addr = addr->sin_addr.s_addr;
146
147         if (TAILQ_EMPTY(&head->h_head))
148                 TAILQ_INSERT_HEAD(&head->h_head, hptr, list);
149         else
150                 TAILQ_INSERT_TAIL(&head->h_head, hptr, list);
151 }
152 haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long proc, u_long prog)
153 {
154         hash_head *head;
155         haccess_t *hptr;
156         int hash;
157
158         hash = HASH(inet_ntoa(addr->sin_addr), proc, prog);
159         head = &(haccess_tbl[hash]);
160
161         TAILQ_FOREACH(hptr, &head->h_head, list) {
162                 if (hptr->addr.s_addr == addr->sin_addr.s_addr)
163                         return hptr;
164         }
165         return NULL;
166 }
167
168 int
169 good_client(daemon, addr)
170 char *daemon;
171 struct sockaddr_in *addr;
172 {
173     struct hostent *hp;
174     char **sp;
175     char *tmpname;
176
177         /* First check the address. */
178         if (hosts_ctl(daemon, "", inet_ntoa(addr->sin_addr), "") == DENY)
179                 return DENY;
180
181         /* Now do the hostname lookup */
182         hp = gethostbyaddr ((const char *) &(addr->sin_addr),
183                 sizeof (addr->sin_addr), AF_INET);
184         if (!hp)
185                 return DENY; /* never heard of it. misconfigured DNS? */
186
187         /* Make sure the hostent is authorative. */
188         tmpname = strdup(hp->h_name);
189         if (!tmpname)
190                 return DENY;
191         hp = gethostbyname(tmpname);
192         free(tmpname);
193         if (!hp)
194                 return DENY; /* never heard of it. misconfigured DNS? */
195
196         /* Now make sure the address is on the list */
197         for (sp = hp->h_addr_list ; *sp ; sp++) {
198             if (memcmp(*sp, &(addr->sin_addr), hp->h_length) == 0)
199                         break;
200         }
201         if (!*sp)
202             return DENY; /* it was a FAKE. */
203
204         /* Check the official name and address. */
205         if (hosts_ctl(daemon, hp->h_name, inet_ntoa(addr->sin_addr), "") == DENY)
206                 return DENY;
207
208         /* Now check aliases. */
209         for (sp = hp->h_aliases; *sp ; sp++) {
210                 if (hosts_ctl(daemon, *sp, inet_ntoa(addr->sin_addr), "") == DENY)
211                 return DENY;
212         }
213
214    return ALLOW;
215 }
216
217 /* check_startup - additional startup code */
218
219 void    check_startup(void)
220 {
221
222     /*
223      * Give up root privileges so that we can never allocate a privileged
224      * port when forwarding an rpc request.
225      *
226      * Fix 8/3/00 Philipp Knirsch: First lookup our rpc user. If we find it,
227      * switch to that uid, otherwise simply resue the old bin user and print
228      * out a warning in syslog.
229      */
230
231     struct passwd *pwent;
232
233     pwent = getpwnam("rpc");
234     if (pwent == NULL) {
235         syslog(LOG_WARNING, "user rpc not found, reverting to user bin");
236         if (setuid(1) == -1) {
237             syslog(LOG_ERR, "setuid(1) failed: %m");
238             exit(1);
239         }
240     }
241     else {
242         if (setuid(pwent->pw_uid) == -1) {
243             syslog(LOG_WARNING, "setuid() to rpc user failed: %m");
244             if (setuid(1) == -1) {
245                 syslog(LOG_ERR, "setuid(1) failed: %m");
246                 exit(1);
247             }
248         }
249     }
250
251     (void) signal(SIGINT, toggle_verboselog);
252 }
253
254 /* check_files - check to see if either access files have changed */
255
256 static int check_files()
257 {
258         static time_t allow_mtime, deny_mtime;
259         struct stat astat, dstat;
260         int changed = 0;
261
262         if (stat("/etc/hosts.allow", &astat) < 0)
263                 astat.st_mtime = 0;
264         if (stat("/etc/hosts.deny", &dstat) < 0)
265                 dstat.st_mtime = 0;
266
267         if(!astat.st_mtime || !dstat.st_mtime)
268                 return changed;
269
270         if (astat.st_mtime != allow_mtime)
271                 changed = 1;
272         else if (dstat.st_mtime != deny_mtime)
273                 changed = 1;
274
275         allow_mtime = astat.st_mtime;
276         deny_mtime = dstat.st_mtime;
277
278         return changed;
279 }
280
281 /* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
282
283 int
284 check_default(daemon, addr, proc, prog)
285 char *daemon;
286 struct sockaddr_in *addr;
287 u_long  proc;
288 u_long  prog;
289 {
290         haccess_t *acc = NULL;
291         int changed = check_files();
292
293         acc = haccess_lookup(addr, proc, prog);
294         if (acc && changed == 0)
295                 return (acc->access);
296
297         if (!(from_local(addr) || good_client(daemon, addr))) {
298                 log_bad_host(addr, proc, prog);
299                 if (acc)
300                         acc->access = FALSE;
301                 else 
302                         haccess_add(addr, proc, prog, FALSE);
303                 return (FALSE);
304         }
305         if (verboselog)
306                 log_client(addr, proc, prog);
307
308         if (acc)
309                 acc->access = TRUE;
310         else 
311                 haccess_add(addr, proc, prog, TRUE);
312     return (TRUE);
313 }
314
315 /* check_privileged_port - additional checks for privileged-port updates */
316 int
317 check_privileged_port(struct sockaddr_in *addr, 
318                       u_long proc, u_long prog, u_long port)
319 {
320 #ifdef CHECK_PORT
321     if (!legal_port(addr, port)) {
322         log_bad_port(addr, proc, prog);
323         return (FALSE);
324     }
325 #endif
326     return (TRUE);
327 }
328
329 /* toggle_verboselog - toggle verbose logging flag */
330
331 static void toggle_verboselog(int sig)
332 {
333     (void) signal(sig, toggle_verboselog);
334     verboselog = !verboselog;
335 }
336
337 /* logit - report events of interest via the syslog daemon */
338
339 static void logit(int severity, struct sockaddr_in *addr,
340                   u_long procnum, u_long prognum, char *text)
341 {
342     char   *procname;
343     char    procbuf[16 + 4 * sizeof(u_long)];
344     char   *progname;
345     char    progbuf[16 + 4 * sizeof(u_long)];
346     struct rpcent *rpc;
347
348     /*
349      * Fork off a process or the portmap daemon might hang while
350      * getrpcbynumber() or syslog() does its thing.
351      *
352      * Don't forget to wait for the children, too...
353      */
354
355     if (fork() == 0) {
356
357         /* Try to map program number to name. */
358
359         if (prognum == 0) {
360             progname = "";
361         } else if ((rpc = getrpcbynumber((int) prognum))) {
362             progname = rpc->r_name;
363         } else {
364             snprintf(progname = progbuf, sizeof (progbuf),
365                      "prog (%lu)", prognum);
366         }
367
368         /* Try to map procedure number to name. */
369
370         snprintf(procname = procbuf, sizeof (procbuf),
371                  "proc (%lu)", (u_long) procnum);
372
373         /* Write syslog record. */
374
375         syslog(severity, "connect from %s to %s in %s%s",
376                inet_ntoa(addr->sin_addr), procname, progname, text);
377         exit(0);
378     }
379 }