Initial import from FreeBSD RELENG_4:
[dragonfly.git] / usr.sbin / portmap / pmap_check.c
1  /*
2   * pmap_check - additional portmap security.
3   *
4   * Always reject non-local requests to update the portmapper tables.
5   *
6   * Refuse to forward mount requests to the nfs mount daemon. Otherwise, the
7   * requests would appear to come from the local system, and nfs export
8   * restrictions could be bypassed.
9   *
10   * Refuse to forward requests to the nfsd process.
11   *
12   * Refuse to forward requests to NIS (YP) daemons; The only exception is the
13   * YPPROC_DOMAIN_NONACK broadcast rpc call that is used to establish initial
14   * contact with the NIS server.
15   *
16   * Always allocate an unprivileged port when forwarding a request.
17   *
18   * If compiled with -DCHECK_PORT, require that requests to register or
19   * unregister a privileged port come from a privileged port. This makes it
20   * more difficult to replace a critical service by a trojan.
21   *
22   * If compiled with -DHOSTS_ACCESS, reject requests from hosts that are not
23   * authorized by the /etc/hosts.{allow,deny} files. The local system is
24   * always treated as an authorized host. The access control tables are never
25   * consulted for requests from the local system, and are always consulted
26   * for requests from other hosts. Access control is based on IP addresses
27   * only; attempts to map an address to a host name might cause the
28   * portmapper to hang.
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 #ifndef lint
35 #if 0
36 static char sccsid[] = "@(#) pmap_check.c 1.6 93/11/21 20:58:59";
37 #endif
38 static const char rcsid[] =
39   "$FreeBSD: src/usr.sbin/portmap/pmap_check.c,v 1.6 2000/01/15 23:08:28 brian Exp $";
40 #endif
41
42 #include <stdio.h>
43 #include <unistd.h>
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48
49 #include <rpc/rpc.h>
50 #include <rpc/pmap_prot.h>
51 #include <syslog.h>
52 #include <netdb.h>
53 #include <sys/signal.h>
54
55 #include "pmap_check.h"
56
57 /* Explicit #defines in case the include files are not available. */
58
59 #define NFSPROG         ((u_long) 100003)
60 #define MOUNTPROG       ((u_long) 100005)
61 #define YPXPROG         ((u_long) 100069)
62 #define YPPROG          ((u_long) 100004)
63 #define YPPROC_DOMAIN_NONACK ((u_long) 2)
64 #define MOUNTPROC_MNT   ((u_long) 1)
65
66 static void logit __P((int, struct sockaddr_in *, u_long, u_long, const char *));
67 static void toggle_verboselog __P((int));
68
69 int     verboselog = 0;
70 int     allow_severity = LOG_INFO;
71 int     deny_severity = LOG_WARNING;
72
73 /* A handful of macros for "readability". */
74
75 #define good_client(a) hosts_ctl("portmap", "", inet_ntoa(a->sin_addr), "")
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 /* check_startup - additional startup code */
96
97 void
98 check_startup()
99 {
100
101     /*
102      * Give up root privileges so that we can never allocate a privileged
103      * port when forwarding an rpc request.
104      */
105     if (setuid(1) == -1) {
106         syslog(LOG_ERR, "setuid(1) failed: %m");
107         exit(1);
108     }
109     (void) signal(SIGINT, toggle_verboselog);
110 }
111
112 /* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
113
114 int
115 check_default(addr, proc, prog)
116     struct sockaddr_in *addr;
117     u_long proc, prog;
118 {
119 #ifdef HOSTS_ACCESS
120     if (!(from_local(addr) || good_client(addr))) {
121         log_bad_host(addr, proc, prog);
122         return (FALSE);
123     }
124 #endif
125     if (verboselog)
126         log_client(addr, proc, prog);
127     return (TRUE);
128 }
129
130 /* check_privileged_port - additional checks for privileged-port updates */
131
132 int
133 check_privileged_port(addr, proc, prog, port)
134     struct sockaddr_in *addr;
135     u_long proc, prog, port;
136 {
137 #ifdef CHECK_PORT
138     if (!legal_port(addr, port)) {
139         log_bad_port(addr, proc, prog);
140         return (FALSE);
141     }
142 #endif
143     return (TRUE);
144 }
145
146 /* check_setunset - additional checks for update requests */
147
148 int
149 check_setunset(addr, proc, prog, port)
150     struct sockaddr_in *addr;
151     u_long proc, prog, port;
152 {
153     if (!from_local(addr)) {
154 #ifdef HOSTS_ACCESS
155         (void) good_client(addr);               /* because of side effects */
156 #endif
157         log_bad_owner(addr, proc, prog);
158         return (FALSE);
159     }
160     if (port && !check_privileged_port(addr, proc, prog, port))
161         return (FALSE);
162     if (verboselog)
163         log_client(addr, proc, prog);
164     return (TRUE);
165 }
166
167 /* check_callit - additional checks for forwarded requests */
168
169 int
170 check_callit(addr, proc, prog, aproc)
171     struct sockaddr_in *addr;
172     u_long proc, prog, aproc;
173 {
174 #ifdef HOSTS_ACCESS
175     if (!(from_local(addr) || good_client(addr))) {
176         log_bad_host(addr, proc, prog);
177         return (FALSE);
178     }
179 #endif
180     if (prog == PMAPPROG || prog == NFSPROG || prog == YPXPROG ||
181         (prog == MOUNTPROG && aproc == MOUNTPROC_MNT) ||
182         (prog == YPPROG && aproc != YPPROC_DOMAIN_NONACK)) {
183         log_no_forward(addr, proc, prog);
184         return (FALSE);
185     }
186     if (verboselog)
187         log_client(addr, proc, prog);
188     return (TRUE);
189 }
190
191 /* toggle_verboselog - toggle verbose logging flag */
192
193 static void
194 toggle_verboselog(sig)
195     int sig;
196 {
197     (void) signal(sig, toggle_verboselog);
198     verboselog = !verboselog;
199 }
200
201 /* logit - report events of interest via the syslog daemon */
202
203 static void
204 logit(severity, addr, procnum, prognum, text)
205     int severity;
206     struct sockaddr_in *addr;
207     u_long procnum, prognum;
208     const char *text;
209 {
210     const char *procname;
211     char    procbuf[4 * sizeof(u_long)];
212     const char *progname;
213     char    progbuf[4 * sizeof(u_long)];
214     struct rpcent *rpc;
215     struct proc_map {
216         u_long  code;
217         const char *proc;
218     };
219     struct proc_map *procp;
220     static struct proc_map procmap[] = {
221         {PMAPPROC_CALLIT, "callit"},
222         {PMAPPROC_DUMP, "dump"},
223         {PMAPPROC_GETPORT, "getport"},
224         {PMAPPROC_NULL, "null"},
225         {PMAPPROC_SET, "set"},
226         {PMAPPROC_UNSET, "unset"},
227         {0, 0},
228     };
229
230     /*
231      * Fork off a process or the portmap daemon might hang while
232      * getrpcbynumber() or syslog() does its thing.
233      */
234
235     if (fork() == 0) {
236
237         /* Try to map program number to name. */
238
239         if (prognum == 0) {
240             progname = "";
241         } else if ((rpc = getrpcbynumber((int) prognum))) {
242             progname = rpc->r_name;
243         } else {
244             sprintf(progbuf, "%lu", prognum);
245             progname = progbuf;
246         }
247
248         /* Try to map procedure number to name. */
249
250         for (procp = procmap; procp->proc && procp->code != procnum; procp++)
251              /* void */ ;
252         if ((procname = procp->proc) == 0) {
253             sprintf(procbuf, "%lu", (u_long) procnum);
254             procname = procbuf;
255         }
256
257         /* Write syslog record. */
258
259         syslog(severity, "connect from %s to %s(%s)%s",
260                inet_ntoa(addr->sin_addr), procname, progname, text);
261         exit(0);
262     }
263 }