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