]> git.neil.brown.name Git - portmap.git/blob - pmap_check.c
Only fail an 'unregister' attempt if nothing can be unregistered.
[portmap.git] / 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. Also, require
21   * that requests to set/unset the NFSD port come form a privileged port.
22   * 
23   * If compiled with -DHOSTS_ACCESS, reject requests from hosts that are not
24   * authorized by the /etc/hosts.{allow,deny} files. The local system is
25   * always treated as an authorized host. The access control tables are never
26   * consulted for requests from the local system, and are always consulted
27   * for requests from other hosts. Access control is based on IP addresses
28   * only; attempts to map an address to a host name might cause the
29   * portmapper to hang.
30   * 
31   * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
32   * Computing Science, Eindhoven University of Technology, The Netherlands.
33   */
34
35
36 #include <sys/types.h>
37 #include <unistd.h>
38 #include <rpc/rpc.h>
39 #include <rpc/pmap_prot.h>
40 #include <syslog.h>
41 #include <netdb.h>
42 #include <sys/signal.h>
43 #ifdef SYSV40
44 #include <netinet/in.h>
45 #include <rpc/rpcent.h>
46 #endif
47 #ifdef HOSTS_ACCESS
48 #include <tcpd.h>
49 #endif
50 #include <arpa/inet.h>
51 #include <grp.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 #define NFS_PORT        2049
64
65 static void logit(int severity, struct sockaddr_in *addr,
66                   u_long procnum, u_long prognum, const char *text);
67 static void toggle_verboselog(int sig);
68 int     verboselog __attribute ((visibility ("hidden"))) = 0;
69 int     allow_severity __attribute ((visibility ("hidden"))) = LOG_INFO;
70 int     deny_severity __attribute ((visibility ("hidden"))) = LOG_WARNING;
71
72 /* A handful of macros for "readability". */
73
74 #define reserved_port(p) ((p) < IPPORT_RESERVED)
75
76 #define unreserved_port(p) (IPPORT_RESERVED <= (p) && (p) != NFS_PORT)
77
78 #define legal_port(a,p) \
79   (reserved_port(ntohs((a)->sin_port)) || unreserved_port(p))
80
81 #define log_bad_port(addr, proc, prog) \
82   logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
83
84 #define log_bad_host(addr, proc, prog) \
85   logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
86
87 #define log_bad_owner(addr, proc, prog) \
88   logit(deny_severity, addr, proc, prog, ": request from non-local host")
89
90 #define log_no_forward(addr, proc, prog) \
91   logit(deny_severity, addr, proc, prog, ": request not forwarded")
92
93 #define log_client(addr, proc, prog) \
94   logit(allow_severity, addr, proc, prog, "")
95
96 /* check_startup - additional startup code */
97
98 void check_startup(void)
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     setgid(daemon_gid);
106     setgroups(0, NULL);
107     if (setuid(daemon_uid) == -1) {
108         syslog(LOG_ERR, "setuid(1) failed: %m");
109         exit(1);
110     }
111     (void) signal(SIGINT, toggle_verboselog);
112 }
113
114
115 #ifdef HOSTS_ACCESS
116 static int
117 good_client(struct sockaddr_in *addr)
118 {
119         if (hosts_ctl((char*)("portmap"), (char*)(""), inet_ntoa(addr->sin_addr), (char*)("")))
120                 return 1;
121 #ifdef ENABLE_DNS
122 {
123         struct hostent *hp;
124         char **sp;
125         char *tmpname;
126
127         /* Check the hostname. */
128         hp = gethostbyaddr ((const char *) &(addr->sin_addr),
129                             sizeof (addr->sin_addr), AF_INET);
130
131         if (!hp)
132                 return 0;
133
134         /* must make sure the hostent is authoritative. */
135         tmpname = alloca (strlen (hp->h_name) + 1);
136         strcpy (tmpname, hp->h_name);
137         hp = gethostbyname(tmpname);
138         if (hp) {
139                 /* now make sure the "addr->sin_addr" is on the list */
140                 for (sp = hp->h_addr_list ; *sp ; sp++) {
141                         if (memcmp(*sp, &(addr->sin_addr), hp->h_length)==0)
142                                 break;
143                 }
144                 if (!*sp)
145                         /* it was a FAKE. */
146                         return 0;
147         } else
148                 /* never heard of it. misconfigured DNS? */
149                 return 0;
150
151         /* Check the official name first. */
152         if (hosts_ctl((char*)("portmap"), (char*)(""), hp->h_name, (char*)("")))
153                 return 1;
154
155         /* Check aliases. */
156         for (sp = hp->h_aliases; *sp ; sp++) {
157                 if (hosts_ctl((char*)("portmap"), (char*)(""), *sp, (char*)("")))
158                         return 1;
159         }
160 }
161 #endif /* ENABLE_DNS */
162         return 0;
163 }
164 #endif /* HOSTS_ACCESS */
165
166 /* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
167
168 int
169 check_default(struct sockaddr_in *addr, u_long  proc,
170               u_long  prog)
171 {
172 #ifdef HOSTS_ACCESS
173     if (!(from_local(addr) || good_client(addr))) {
174         log_bad_host(addr, proc, prog);
175         return (FALSE);
176     }
177 #endif
178     if (verboselog)
179         log_client(addr, proc, prog);
180     return (TRUE);
181 }
182
183 /* check_privileged_port - additional checks for privileged-port updates */
184
185 int
186 check_privileged_port(struct sockaddr_in *addr, u_long  proc,
187                       u_long  prog, u_long  port)
188 {
189 #ifdef CHECK_PORT
190     if (!legal_port(addr, port)) {
191         log_bad_port(addr, proc, prog);
192         return (FALSE);
193     }
194 #endif
195     return (TRUE);
196 }
197
198 /* check_setunset - additional checks for update requests */
199
200 #ifdef LOOPBACK_SETUNSET
201
202 int
203 check_setunset(SVCXPRT *xprt, SVCXPRT *ludp_xprt, SVCXPRT *ltcp_xprt,
204                u_long  proc, u_long  prog, u_long  port)
205 {
206     struct sockaddr_in *addr = svc_getcaller(xprt);
207
208     if (xprt != ludp_xprt && xprt != ltcp_xprt) {
209 #ifdef HOSTS_ACCESS
210         (void) good_client(addr);               /* because of side effects */
211 #endif
212         log_bad_owner(addr, proc, prog);
213         return (FALSE);
214     }
215     if (port && !check_privileged_port(addr, proc, prog, port))
216         return (FALSE);
217     if (verboselog)
218         log_client(addr, proc, prog);
219     return (TRUE);
220 }
221
222 #else
223
224 int
225 check_setunset(struct sockaddr_in *addr, u_long  proc,
226                u_long  prog, u_long  port)
227 {
228     if (!from_local(addr)) {
229 #ifdef HOSTS_ACCESS
230         (void) good_client(addr);               /* because of side effects */
231 #endif
232         log_bad_owner(addr, proc, prog);
233         return (FALSE);
234     }
235     if (port && !check_privileged_port(addr, proc, prog, port))
236         return (FALSE);
237     if (verboselog)
238         log_client(addr, proc, prog);
239     return (TRUE);
240 }
241
242 #endif
243
244 /* check_callit - additional checks for forwarded requests */
245
246 int
247 check_callit(struct sockaddr_in *addr, u_long  proc,
248              u_long  prog, u_long  aproc)
249 {
250 #ifdef HOSTS_ACCESS
251     if (!(from_local(addr) || good_client(addr))) {
252         log_bad_host(addr, proc, prog);
253         return (FALSE);
254     }
255 #endif
256     if (prog == PMAPPROG || prog == NFSPROG || prog == YPXPROG ||
257         (prog == MOUNTPROG && aproc == MOUNTPROC_MNT) ||
258         (prog == YPPROG && aproc != YPPROC_DOMAIN_NONACK)) {
259         log_no_forward(addr, proc, prog);
260         return (FALSE);
261     }
262     if (verboselog)
263         log_client(addr, proc, prog);
264     return (TRUE);
265 }
266
267 /* toggle_verboselog - toggle verbose logging flag */
268
269 static void toggle_verboselog(int sig)
270 {
271     (void) signal(sig, toggle_verboselog);
272     verboselog = !verboselog;
273 }
274
275 /* logit - report events of interest via the syslog daemon */
276
277 static void logit(int severity, struct sockaddr_in *addr,
278                   u_long procnum, u_long prognum, const char *text)
279 {
280     const char *procname;
281     char    procbuf[4 * sizeof(u_long)];
282     const char *progname;
283     char    progbuf[4 * sizeof(u_long)];
284     struct rpcent *rpc;
285     struct proc_map {
286         u_long  code;
287         const char *proc;
288     };
289     struct proc_map *procp;
290     static struct proc_map procmap[] = {
291         { PMAPPROC_CALLIT, "callit" },
292         { PMAPPROC_DUMP, "dump"} ,
293         { PMAPPROC_GETPORT, "getport"} ,
294         { PMAPPROC_NULL, "null"} ,
295         { PMAPPROC_SET, "set"} ,
296         { PMAPPROC_UNSET, "unset"} ,
297         { 0, 0} ,
298     };
299
300     /*
301      * Fork off a process or the portmap daemon might hang while
302      * getrpcbynumber() or syslog() does its thing.
303      */
304
305 #ifndef NO_FORK
306     if (fork() == 0)
307 #endif
308     {
309         /* Try to map program number to name. */
310
311         if (prognum == 0) {
312             progname = "";
313         } else if ((rpc = getrpcbynumber((int) prognum))) {
314             progname = rpc->r_name;
315         } else {
316             sprintf(progbuf, "%lu", prognum);
317             progname = progbuf;
318         }
319
320         /* Try to map procedure number to name. */
321
322         for (procp = procmap; procp->proc && procp->code != procnum; procp++)
323              /* void */ ;
324         if ((procname = procp->proc) == 0)
325         {
326             sprintf(procbuf, "%lu", (u_long) procnum);
327             procname = procbuf;
328         }
329
330         /* Write syslog record. */
331
332         syslog(severity, "connect from %s to %s(%s)%s",
333                inet_ntoa(addr->sin_addr), procname, progname, text);
334         exit(0);
335     }
336 }