]> git.decadent.org.uk Git - nfs-utils.git/blob - support/misc/tcpwrapper.c
Merge branch 'sid'
[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
38 #ifdef HAVE_LIBWRAP
39 #include <unistd.h>
40 #include <string.h>
41 #include <rpc/rpc.h>
42 #include <rpc/pmap_prot.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 <tcpd.h>
50
51 #include "sockaddr.h"
52 #include "tcpwrapper.h"
53 #include "xlog.h"
54
55 #ifdef SYSV40
56 #include <netinet/in.h>
57 #include <rpc/rpcent.h>
58 #endif  /* SYSV40 */
59
60 #define ALLOW 1
61 #define DENY 0
62
63 #ifdef IPV6_SUPPORTED
64 static void
65 present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
66 {
67         const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
68         const struct sockaddr_in6 *sin6 = (const struct sockaddr_in6 *)sap;
69         socklen_t len = (socklen_t)buflen;
70
71         switch (sap->sa_family) {
72         case AF_INET:
73                 if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0)
74                         return;
75         case AF_INET6:
76                 if (inet_ntop(AF_INET6, &sin6->sin6_addr, buf, len) != 0)
77                         return;
78         }
79
80         memset(buf, 0, buflen);
81         strncpy(buf, "unrecognized caller", buflen);
82 }
83 #else   /* !IPV6_SUPPORTED */
84 static void
85 present_address(const struct sockaddr *sap, char *buf, const size_t buflen)
86 {
87         const struct sockaddr_in *sin = (const struct sockaddr_in *)sap;
88         socklen_t len = (socklen_t)buflen;
89
90         if (sap->sa_family == AF_INET)
91                 if (inet_ntop(AF_INET, &sin->sin_addr, buf, len) != 0)
92                         return;
93
94         memset(buf, 0, buflen);
95         strncpy(buf, "unrecognized caller", (size_t)buflen);
96 }
97 #endif  /* !IPV6_SUPPORTED */
98
99 typedef struct _haccess_t {
100         TAILQ_ENTRY(_haccess_t) list;
101         int                     allowed;
102         union nfs_sockaddr      address;
103 } haccess_t;
104
105 #define HASH_TABLE_SIZE 1021
106 typedef struct _hash_head {
107         TAILQ_HEAD(host_list, _haccess_t) h_head;
108 } hash_head;
109
110 static hash_head haccess_tbl[HASH_TABLE_SIZE];
111
112 static unsigned long
113 strtoint(const char *str)
114 {
115         unsigned long i, n = 0;
116         size_t len = strlen(str);
117
118         for (i = 0; i < len; i++)
119                 n += (unsigned char)str[i] * i;
120
121         return n;
122 }
123
124 static unsigned int
125 hashint(const unsigned long num)
126 {
127         return (unsigned int)(num % HASH_TABLE_SIZE);
128 }
129
130 static unsigned int
131 HASH(const char *addr, const unsigned long program)
132 {
133         return hashint(strtoint(addr) + program);
134 }
135
136 static void
137 haccess_add(const struct sockaddr *sap, const char *address,
138                 const unsigned long program, const int allowed)
139 {
140         hash_head *head;
141         haccess_t *hptr;
142         unsigned int hash;
143
144         hptr = (haccess_t *)malloc(sizeof(haccess_t));
145         if (hptr == NULL)
146                 return;
147
148         hash = HASH(address, program);
149         head = &(haccess_tbl[hash]);
150
151         hptr->allowed = allowed;
152         memcpy(&hptr->address, sap, (size_t)nfs_sockaddr_length(sap));
153
154         if (TAILQ_EMPTY(&head->h_head))
155                 TAILQ_INSERT_HEAD(&head->h_head, hptr, list);
156         else
157                 TAILQ_INSERT_TAIL(&head->h_head, hptr, list);
158 }
159
160 static haccess_t *
161 haccess_lookup(const struct sockaddr *sap, const char *address,
162                 const unsigned long program)
163 {
164         hash_head *head;
165         haccess_t *hptr;
166         unsigned int hash;
167
168         hash = HASH(address, program);
169         head = &(haccess_tbl[hash]);
170
171         TAILQ_FOREACH(hptr, &head->h_head, list) {
172                 if (nfs_compare_sockaddr(&hptr->address.sa, sap))
173                         return hptr;
174         }
175         return NULL;
176 }
177
178 static void
179 logit(const char *address)
180 {
181         xlog_warn("connect from %s denied: request from unauthorized host",
182                         address);
183 }
184
185 static int
186 good_client(char *name, struct sockaddr *sap)
187 {
188         struct request_info req;
189
190         request_init(&req, RQ_DAEMON, name, RQ_CLIENT_SIN, sap, 0);
191         sock_methods(&req);
192
193         if (hosts_access(&req)) 
194                 return ALLOW;
195
196         return DENY;
197 }
198
199 static int
200 check_files(void)
201 {
202         static time_t allow_mtime, deny_mtime;
203         struct stat astat, dstat;
204         int changed = 0;
205
206         if (stat("/etc/hosts.allow", &astat) < 0)
207                 astat.st_mtime = 0;
208         if (stat("/etc/hosts.deny", &dstat) < 0)
209                 dstat.st_mtime = 0;
210
211         if(!astat.st_mtime || !dstat.st_mtime)
212                 return changed;
213
214         if (astat.st_mtime != allow_mtime)
215                 changed = 1;
216         else if (dstat.st_mtime != deny_mtime)
217                 changed = 1;
218
219         allow_mtime = astat.st_mtime;
220         deny_mtime = dstat.st_mtime;
221
222         return changed;
223 }
224
225 /**
226  * check_default - additional checks for NULL, DUMP, GETPORT and unknown
227  * @name: pointer to '\0'-terminated ASCII string containing name of the
228  *              daemon requesting the access check
229  * @sap: pointer to sockaddr containing network address of caller
230  * @program: RPC program number caller is attempting to access
231  *
232  * Returns TRUE if the caller is allowed access; otherwise FALSE is returned.
233  */
234 int
235 check_default(char *name, struct sockaddr *sap, const unsigned long program)
236 {
237         haccess_t *acc = NULL;
238         int changed = check_files();
239         char buf[INET6_ADDRSTRLEN];
240
241         present_address(sap, buf, sizeof(buf));
242
243         acc = haccess_lookup(sap, buf, program);
244         if (acc != NULL && changed == 0) {
245                 xlog(D_GENERAL, "%s: access by %s %s (cached)", __func__,
246                         buf, acc->allowed ? "ALLOWED" : "DENIED");
247                 return acc->allowed;
248         }
249
250         if (!(from_local(sap) || good_client(name, sap))) {
251                 logit(buf);
252                 if (acc != NULL)
253                         acc->allowed = FALSE;
254                 else
255                         haccess_add(sap, buf, program, FALSE);
256                 xlog(D_GENERAL, "%s: access by %s DENIED", __func__, buf);
257                 return (FALSE);
258         }
259
260         if (acc != NULL)
261                 acc->allowed = TRUE;
262         else
263                 haccess_add(sap, buf, program, TRUE);
264         xlog(D_GENERAL, "%s: access by %s ALLOWED", __func__, buf);
265
266         return (TRUE);
267 }
268
269 #endif  /* HAVE_LIBWRAP */