Add PCICAP_{ID,NEXTPTR} to avoid using magic number
[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 /*
35  * @(#) pmap_check.c 1.6 93/11/21 20:58:59
36  * $FreeBSD: src/usr.sbin/portmap/pmap_check.c,v 1.6 2000/01/15 23:08:28 brian Exp $
37  * $DragonFly: src/usr.sbin/portmap/pmap_check.c,v 1.5 2007/11/25 01:28:24 swildner Exp $
38  */
39
40 #include <sys/types.h>
41 #include <sys/socket.h>
42 #include <sys/signal.h>
43 #include <netinet/in.h>
44 #include <arpa/inet.h>
45 #include <rpc/rpc.h>
46 #include <rpc/pmap_prot.h>
47
48 #include <netdb.h>
49 #include <stdio.h>
50 #include <stdlib.h>
51 #include <syslog.h>
52 #include <unistd.h>
53
54 #include "pmap_check.h"
55
56 /* Explicit #defines in case the include files are not available. */
57
58 #define NFSPROG         ((u_long) 100003)
59 #define MOUNTPROG       ((u_long) 100005)
60 #define YPXPROG         ((u_long) 100069)
61 #define YPPROG          ((u_long) 100004)
62 #define YPPROC_DOMAIN_NONACK ((u_long) 2)
63 #define MOUNTPROC_MNT   ((u_long) 1)
64
65 static void logit(int, struct sockaddr_in *, u_long, u_long, const char *);
66 static void toggle_verboselog(int);
67
68 int     verboselog = 0;
69 int     allow_severity = LOG_INFO;
70 int     deny_severity = LOG_WARNING;
71
72 /* A handful of macros for "readability". */
73
74 #define good_client(a) hosts_ctl("portmap", "", inet_ntoa(a->sin_addr), "")
75
76 #define legal_port(a,p) \
77   (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED)
78
79 #define log_bad_port(addr, proc, prog) \
80   logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
81
82 #define log_bad_host(addr, proc, prog) \
83   logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
84
85 #define log_bad_owner(addr, proc, prog) \
86   logit(deny_severity, addr, proc, prog, ": request from non-local host")
87
88 #define log_no_forward(addr, proc, prog) \
89   logit(deny_severity, addr, proc, prog, ": request not forwarded")
90
91 #define log_client(addr, proc, prog) \
92   logit(allow_severity, addr, proc, prog, "")
93
94 /* check_startup - additional startup code */
95
96 void
97 check_startup(void)
98 {
99     /*
100      * Give up root privileges so that we can never allocate a privileged
101      * port when forwarding an rpc request.
102      */
103     if (setuid(1) == -1) {
104         syslog(LOG_ERR, "setuid(1) failed: %m");
105         exit(1);
106     }
107     signal(SIGINT, toggle_verboselog);
108 }
109
110 /* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
111
112 int
113 check_default(struct sockaddr_in *addr, u_long proc, u_long prog)
114 {
115 #ifdef HOSTS_ACCESS
116     if (!(from_local(addr) || good_client(addr))) {
117         log_bad_host(addr, proc, prog);
118         return (FALSE);
119     }
120 #endif
121     if (verboselog)
122         log_client(addr, proc, prog);
123     return (TRUE);
124 }
125
126 /* check_privileged_port - additional checks for privileged-port updates */
127
128 int
129 check_privileged_port(struct sockaddr_in *addr,
130                       u_long proc, u_long prog, u_long port)
131 {
132 #ifdef CHECK_PORT
133     if (!legal_port(addr, port)) {
134         log_bad_port(addr, proc, prog);
135         return (FALSE);
136     }
137 #endif
138     return (TRUE);
139 }
140
141 /* check_setunset - additional checks for update requests */
142
143 int
144 check_setunset(struct sockaddr_in *addr,
145                u_long proc, u_long prog, u_long port)
146 {
147     if (!from_local(addr)) {
148 #ifdef HOSTS_ACCESS
149         good_client(addr);                      /* because of side effects */
150 #endif
151         log_bad_owner(addr, proc, prog);
152         return (FALSE);
153     }
154     if (port && !check_privileged_port(addr, proc, prog, port))
155         return (FALSE);
156     if (verboselog)
157         log_client(addr, proc, prog);
158     return (TRUE);
159 }
160
161 /* check_callit - additional checks for forwarded requests */
162
163 int
164 check_callit(struct sockaddr_in *addr, u_long proc, u_long prog, u_long aproc)
165 {
166 #ifdef HOSTS_ACCESS
167     if (!(from_local(addr) || good_client(addr))) {
168         log_bad_host(addr, proc, prog);
169         return (FALSE);
170     }
171 #endif
172     if (prog == PMAPPROG || prog == NFSPROG || prog == YPXPROG ||
173         (prog == MOUNTPROG && aproc == MOUNTPROC_MNT) ||
174         (prog == YPPROG && aproc != YPPROC_DOMAIN_NONACK)) {
175         log_no_forward(addr, proc, prog);
176         return (FALSE);
177     }
178     if (verboselog)
179         log_client(addr, proc, prog);
180     return (TRUE);
181 }
182
183 /* toggle_verboselog - toggle verbose logging flag */
184
185 static void
186 toggle_verboselog(int sig)
187 {
188     signal(sig, toggle_verboselog);
189     verboselog = !verboselog;
190 }
191
192 /* logit - report events of interest via the syslog daemon */
193
194 static void
195 logit(int severity, struct sockaddr_in *addr, u_long procnum, u_long prognum,
196       const char *text)
197 {
198     const char *procname;
199     char procbuf[4 * sizeof(u_long)];
200     const char *progname;
201     char progbuf[4 * sizeof(u_long)];
202     struct rpcent *rpc;
203     struct proc_map {
204         u_long  code;
205         const char *proc;
206     };
207     struct proc_map *procp;
208     static struct proc_map procmap[] = {
209         {PMAPPROC_CALLIT, "callit"},
210         {PMAPPROC_DUMP, "dump"},
211         {PMAPPROC_GETPORT, "getport"},
212         {PMAPPROC_NULL, "null"},
213         {PMAPPROC_SET, "set"},
214         {PMAPPROC_UNSET, "unset"},
215         {0, 0},
216     };
217
218     /*
219      * Fork off a process or the portmap daemon might hang while
220      * getrpcbynumber() or syslog() does its thing.
221      */
222
223     if (fork() == 0) {
224
225         /* Try to map program number to name. */
226
227         if (prognum == 0) {
228             progname = "";
229         } else if ((rpc = getrpcbynumber((int) prognum))) {
230             progname = rpc->r_name;
231         } else {
232             sprintf(progbuf, "%lu", prognum);
233             progname = progbuf;
234         }
235
236         /* Try to map procedure number to name. */
237
238         for (procp = procmap; procp->proc && procp->code != procnum; procp++)
239              /* void */ ;
240         if ((procname = procp->proc) == 0) {
241             sprintf(procbuf, "%lu", (u_long) procnum);
242             procname = procbuf;
243         }
244
245         /* Write syslog record. */
246
247         syslog(severity, "connect from %s to %s(%s)%s",
248                inet_ntoa(addr->sin_addr), procname, progname, text);
249         exit(0);
250     }
251 }