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