2005-08-26 Kevin Coffman <kwc@citi.umich.edu>
[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 #include "tcpwrapper.h"
35
36 #include <unistd.h>
37 #include <string.h>
38 #include <rpc/rpc.h>
39 #include <rpc/pmap_prot.h>
40 #include <syslog.h>
41 #include <netdb.h>
42 #include <pwd.h>
43 #include <sys/types.h>
44 #include <sys/signal.h>
45 #ifdef SYSV40
46 #include <netinet/in.h>
47 #include <rpc/rpcent.h>
48 #endif
49
50 static void logit();
51 static void toggle_verboselog();
52 int     verboselog = 0;
53 int     allow_severity = LOG_INFO;
54 int     deny_severity = LOG_WARNING;
55
56 /* A handful of macros for "readability". */
57
58 /* coming from libwrap.a (tcp_wrappers) */
59 extern int hosts_ctl(char *daemon, char *name, char *addr, char *user);
60
61 #define legal_port(a,p) \
62   (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED)
63
64 #define log_bad_port(addr, proc, prog) \
65   logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
66
67 #define log_bad_host(addr, proc, prog) \
68   logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
69
70 #define log_bad_owner(addr, proc, prog) \
71   logit(deny_severity, addr, proc, prog, ": request from non-local host")
72
73 #define log_no_forward(addr, proc, prog) \
74   logit(deny_severity, addr, proc, prog, ": request not forwarded")
75
76 #define log_client(addr, proc, prog) \
77   logit(allow_severity, addr, proc, prog, "")
78
79 int
80 good_client(daemon, addr)
81 char *daemon;
82 struct sockaddr_in *addr;
83 {
84     struct hostent *hp;
85     char **sp;
86     char *tmpname;
87
88     /* Check the IP address first. */
89     if (hosts_ctl(daemon, "", inet_ntoa(addr->sin_addr), ""))
90         return 1;
91
92     /* Check the hostname. */
93     hp = gethostbyaddr ((const char *) &(addr->sin_addr),
94                         sizeof (addr->sin_addr), AF_INET);
95
96     if (!hp)
97         return 0;
98
99     /* must make sure the hostent is authorative. */
100     tmpname = alloca (strlen (hp->h_name) + 1);
101     strcpy (tmpname, hp->h_name);
102     hp = gethostbyname(tmpname);
103     if (hp) {
104         /* now make sure the "addr->sin_addr" is on the list */
105         for (sp = hp->h_addr_list ; *sp ; sp++) {
106             if (memcmp(*sp, &(addr->sin_addr), hp->h_length)==0)
107                 break;
108         }
109         if (!*sp)
110             /* it was a FAKE. */
111             return 0;
112     }
113     else
114            /* never heard of it. misconfigured DNS? */
115            return 0;
116
117    /* Check the official name first. */
118    if (hosts_ctl(daemon, "", hp->h_name, ""))
119         return 1;
120
121    /* Check aliases. */
122    for (sp = hp->h_aliases; *sp ; sp++) {
123         if (hosts_ctl(daemon, "", *sp, ""))
124             return 1;
125    }
126
127    /* No match */
128    return 0;
129 }
130
131 /* check_startup - additional startup code */
132
133 void    check_startup()
134 {
135
136     /*
137      * Give up root privileges so that we can never allocate a privileged
138      * port when forwarding an rpc request.
139      *
140      * Fix 8/3/00 Philipp Knirsch: First lookup our rpc user. If we find it,
141      * switch to that uid, otherwise simply resue the old bin user and print
142      * out a warning in syslog.
143      */
144
145     struct passwd *pwent;
146
147     pwent = getpwnam("rpc");
148     if (pwent == NULL) {
149         syslog(LOG_WARNING, "user rpc not found, reverting to user bin");
150         if (setuid(1) == -1) {
151             syslog(LOG_ERR, "setuid(1) failed: %m");
152             exit(1);
153         }
154     }
155     else {
156         if (setuid(pwent->pw_uid) == -1) {
157             syslog(LOG_WARNING, "setuid() to rpc user failed: %m");
158             if (setuid(1) == -1) {
159                 syslog(LOG_ERR, "setuid(1) failed: %m");
160                 exit(1);
161             }
162         }
163     }
164
165     (void) signal(SIGINT, toggle_verboselog);
166 }
167
168 /* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
169
170 int
171 check_default(daemon, addr, proc, prog)
172 char *daemon;
173 struct sockaddr_in *addr;
174 u_long  proc;
175 u_long  prog;
176 {
177     if (!(from_local(addr) || good_client(daemon, addr))) {
178         log_bad_host(addr, proc, prog);
179         return (FALSE);
180     }
181     if (verboselog)
182         log_client(addr, proc, prog);
183     return (TRUE);
184 }
185
186 /* check_privileged_port - additional checks for privileged-port updates */
187 int
188 check_privileged_port(addr, proc, prog, port)
189 struct sockaddr_in *addr;
190 u_long  proc;
191 u_long  prog;
192 u_long  port;
193 {
194 #ifdef CHECK_PORT
195     if (!legal_port(addr, port)) {
196         log_bad_port(addr, proc, prog);
197         return (FALSE);
198     }
199 #endif
200     return (TRUE);
201 }
202
203 /* toggle_verboselog - toggle verbose logging flag */
204
205 static void toggle_verboselog(sig)
206 int     sig;
207 {
208     (void) signal(sig, toggle_verboselog);
209     verboselog = !verboselog;
210 }
211
212 /* logit - report events of interest via the syslog daemon */
213
214 static void logit(severity, addr, procnum, prognum, text)
215 int     severity;
216 struct sockaddr_in *addr;
217 u_long  procnum;
218 u_long  prognum;
219 char   *text;
220 {
221     char   *procname;
222     char    procbuf[16 + 4 * sizeof(u_long)];
223     char   *progname;
224     char    progbuf[16 + 4 * sizeof(u_long)];
225     struct rpcent *rpc;
226
227     /*
228      * Fork off a process or the portmap daemon might hang while
229      * getrpcbynumber() or syslog() does its thing.
230      *
231      * Don't forget to wait for the children, too...
232      */
233
234     if (fork() == 0) {
235
236         /* Try to map program number to name. */
237
238         if (prognum == 0) {
239             progname = "";
240         } else if ((rpc = getrpcbynumber((int) prognum))) {
241             progname = rpc->r_name;
242         } else {
243             snprintf(progname = progbuf, sizeof (progbuf),
244                      "prog (%lu)", prognum);
245         }
246
247         /* Try to map procedure number to name. */
248
249         snprintf(procname = procbuf, sizeof (procbuf),
250                  "proc (%lu)", (u_long) procnum);
251
252         /* Write syslog record. */
253
254         syslog(severity, "connect from %s to %s in %s%s",
255                inet_ntoa(addr->sin_addr), procname, progname, text);
256         exit(0);
257     }
258 }