1da60204c541a3ee178d9a8ec869bc277387de93
[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 #ifdef HAVE_LIBWRAP
38 #include <tcpwrapper.h>
39 #include <unistd.h>
40 #include <string.h>
41 #include <rpc/rpc.h>
42 #include <rpc/pmap_prot.h>
43 #include <syslog.h>
44 #include <netdb.h>
45 #include <pwd.h>
46 #include <sys/types.h>
47 #include <sys/signal.h>
48 #include <sys/queue.h>
49 #include <sys/stat.h>
50 #include <tcpd.h>
51
52 #include "xlog.h"
53
54 #ifdef SYSV40
55 #include <netinet/in.h>
56 #include <rpc/rpcent.h>
57 #endif
58
59 static void logit(int severity, struct sockaddr_in *addr,
60                   u_long procnum, u_long prognum, char *text);
61 static int check_files(void);
62
63 /*
64  * These need to exist since they are externed 
65  * public header files.
66  */
67 int     verboselog = 0;
68 int     allow_severity = LOG_INFO;
69 int     deny_severity = LOG_WARNING;
70
71 #define log_bad_host(addr, proc, prog) \
72   logit(deny_severity, addr, proc, prog, "request from unauthorized host")
73
74 #define ALLOW 1
75 #define DENY 0
76
77 typedef struct _haccess_t {
78         TAILQ_ENTRY(_haccess_t) list;
79         int access;
80     struct in_addr addr;
81 } haccess_t;
82
83 #define HASH_TABLE_SIZE 1021
84 typedef struct _hash_head {
85         TAILQ_HEAD(host_list, _haccess_t) h_head;
86 } hash_head;
87 hash_head haccess_tbl[HASH_TABLE_SIZE];
88 static haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long);
89 static void haccess_add(struct sockaddr_in *addr, u_long, int);
90
91 inline unsigned int strtoint(char *str)
92 {
93         unsigned int n = 0;
94         int len = strlen(str);
95         int i;
96
97         for (i=0; i < len; i++)
98                 n+=((int)str[i])*i;
99
100         return n;
101 }
102 static inline int hashint(unsigned int num)
103 {
104         return num % HASH_TABLE_SIZE;
105 }
106 #define HASH(_addr, _prog) \
107         hashint((strtoint((_addr))+(_prog)))
108
109 void haccess_add(struct sockaddr_in *addr, u_long prog, int access)
110 {
111         hash_head *head;
112         haccess_t *hptr;
113         int hash;
114
115         hptr = (haccess_t *)malloc(sizeof(haccess_t));
116         if (hptr == NULL)
117                 return;
118
119         hash = HASH(inet_ntoa(addr->sin_addr), prog);
120         head = &(haccess_tbl[hash]);
121
122         hptr->access = access;
123         hptr->addr.s_addr = addr->sin_addr.s_addr;
124
125         if (TAILQ_EMPTY(&head->h_head))
126                 TAILQ_INSERT_HEAD(&head->h_head, hptr, list);
127         else
128                 TAILQ_INSERT_TAIL(&head->h_head, hptr, list);
129 }
130 haccess_t *haccess_lookup(struct sockaddr_in *addr, u_long prog)
131 {
132         hash_head *head;
133         haccess_t *hptr;
134         int hash;
135
136         hash = HASH(inet_ntoa(addr->sin_addr), prog);
137         head = &(haccess_tbl[hash]);
138
139         TAILQ_FOREACH(hptr, &head->h_head, list) {
140                 if (hptr->addr.s_addr == addr->sin_addr.s_addr)
141                         return hptr;
142         }
143         return NULL;
144 }
145
146 int
147 good_client(daemon, addr)
148 char *daemon;
149 struct sockaddr_in *addr;
150 {
151         struct request_info req;
152
153         request_init(&req, RQ_DAEMON, daemon, RQ_CLIENT_SIN, addr, 0);
154         sock_methods(&req);
155
156         if (hosts_access(&req)) 
157                 return ALLOW;
158
159         return DENY;
160 }
161
162 /* check_files - check to see if either access files have changed */
163
164 static int check_files()
165 {
166         static time_t allow_mtime, deny_mtime;
167         struct stat astat, dstat;
168         int changed = 0;
169
170         if (stat("/etc/hosts.allow", &astat) < 0)
171                 astat.st_mtime = 0;
172         if (stat("/etc/hosts.deny", &dstat) < 0)
173                 dstat.st_mtime = 0;
174
175         if(!astat.st_mtime || !dstat.st_mtime)
176                 return changed;
177
178         if (astat.st_mtime != allow_mtime)
179                 changed = 1;
180         else if (dstat.st_mtime != deny_mtime)
181                 changed = 1;
182
183         allow_mtime = astat.st_mtime;
184         deny_mtime = dstat.st_mtime;
185
186         return changed;
187 }
188
189 /* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
190
191 int
192 check_default(daemon, addr, proc, prog)
193 char *daemon;
194 struct sockaddr_in *addr;
195 u_long  proc;
196 u_long  prog;
197 {
198         haccess_t *acc = NULL;
199         int changed = check_files();
200
201         acc = haccess_lookup(addr, prog);
202         if (acc && changed == 0)
203                 return (acc->access);
204
205         if (!(from_local(addr) || good_client(daemon, addr))) {
206                 log_bad_host(addr, proc, prog);
207                 if (acc)
208                         acc->access = FALSE;
209                 else 
210                         haccess_add(addr, prog, FALSE);
211                 return (FALSE);
212         }
213
214         if (acc)
215                 acc->access = TRUE;
216         else 
217                 haccess_add(addr, prog, TRUE);
218
219     return (TRUE);
220 }
221
222 /* logit - report events of interest via the syslog daemon */
223
224 static void logit(int severity, struct sockaddr_in *addr,
225                   u_long procnum, u_long prognum, char *text)
226 {
227         syslog(severity, "connect from %s denied: %s",
228                inet_ntoa(addr->sin_addr), text);
229 }
230 #endif