1/*	$NetBSD: security.c,v 1.5 2000/06/08 09:01:05 fvdl Exp $	*/
2/*	$FreeBSD$ */
3
4#include <sys/types.h>
5#include <sys/time.h>
6#include <sys/socket.h>
7#include <netinet/in.h>
8#include <arpa/inet.h>
9#include <rpc/rpc.h>
10#include <rpc/rpcb_prot.h>
11#include <rpc/pmap_prot.h>
12#include <stdio.h>
13#include <string.h>
14#include <unistd.h>
15#include <syslog.h>
16#include <netdb.h>
17
18/*
19 * XXX for special case checks in check_callit.
20 */
21#include <rpcsvc/mount.h>
22#include <rpcsvc/rquota.h>
23#include <rpcsvc/nfs_prot.h>
24#include <rpcsvc/yp.h>
25#include <rpcsvc/ypclnt.h>
26#include <rpcsvc/yppasswd.h>
27
28#include "rpcbind.h"
29
30#ifdef LIBWRAP
31# include <tcpd.h>
32#ifndef LIBWRAP_ALLOW_FACILITY
33# define LIBWRAP_ALLOW_FACILITY LOG_AUTH
34#endif
35#ifndef LIBWRAP_ALLOW_SEVERITY
36# define LIBWRAP_ALLOW_SEVERITY LOG_INFO
37#endif
38#ifndef LIBWRAP_DENY_FACILITY
39# define LIBWRAP_DENY_FACILITY LOG_AUTH
40#endif
41#ifndef LIBWRAP_DENY_SEVERITY
42# define LIBWRAP_DENY_SEVERITY LOG_WARNING
43#endif
44int allow_severity = LIBWRAP_ALLOW_FACILITY|LIBWRAP_ALLOW_SEVERITY;
45int deny_severity = LIBWRAP_DENY_FACILITY|LIBWRAP_DENY_SEVERITY;
46#endif
47
48#ifndef PORTMAP_LOG_FACILITY
49# define PORTMAP_LOG_FACILITY LOG_AUTH
50#endif
51#ifndef PORTMAP_LOG_SEVERITY
52# define PORTMAP_LOG_SEVERITY LOG_INFO
53#endif
54int log_severity = PORTMAP_LOG_FACILITY|PORTMAP_LOG_SEVERITY;
55
56extern int verboselog;
57
58int
59check_access(SVCXPRT *xprt, rpcproc_t proc, void *args, unsigned int rpcbvers)
60{
61	struct netbuf *caller = svc_getrpccaller(xprt);
62	struct sockaddr *addr = (struct sockaddr *)caller->buf;
63#ifdef LIBWRAP
64	struct request_info req;
65#endif
66	rpcprog_t prog = 0;
67	rpcb *rpcbp;
68	struct pmap *pmap;
69
70	/*
71	 * The older PMAP_* equivalents have the same numbers, so
72	 * they are accounted for here as well.
73	 */
74	switch (proc) {
75	case RPCBPROC_GETADDR:
76	case RPCBPROC_SET:
77	case RPCBPROC_UNSET:
78		if (rpcbvers > PMAPVERS) {
79			rpcbp = (rpcb *)args;
80			prog = rpcbp->r_prog;
81		} else {
82			pmap = (struct pmap *)args;
83			prog = pmap->pm_prog;
84		}
85		if (proc == RPCBPROC_GETADDR)
86			break;
87		if (!insecure && !is_loopback(caller)) {
88			if (verboselog)
89				logit(log_severity, addr, proc, prog,
90				    " declined (non-loopback sender)");
91			return 0;
92		}
93		break;
94	case RPCBPROC_CALLIT:
95	case RPCBPROC_INDIRECT:
96	case RPCBPROC_DUMP:
97	case RPCBPROC_GETTIME:
98	case RPCBPROC_UADDR2TADDR:
99	case RPCBPROC_TADDR2UADDR:
100	case RPCBPROC_GETVERSADDR:
101	case RPCBPROC_GETADDRLIST:
102	case RPCBPROC_GETSTAT:
103	default:
104		break;
105	}
106
107#ifdef LIBWRAP
108	if (libwrap && addr->sa_family != AF_LOCAL) {
109		request_init(&req, RQ_DAEMON, "rpcbind", RQ_CLIENT_SIN, addr,
110		    0);
111		sock_methods(&req);
112		if(!hosts_access(&req)) {
113			logit(deny_severity, addr, proc, prog,
114			    ": request from unauthorized host");
115			return 0;
116		}
117	}
118#endif
119	if (verboselog)
120		logit(log_severity, addr, proc, prog, "");
121    	return 1;
122}
123
124int
125is_loopback(struct netbuf *nbuf)
126{
127	struct sockaddr *addr = (struct sockaddr *)nbuf->buf;
128	struct sockaddr_in *sin;
129#ifdef INET6
130	struct sockaddr_in6 *sin6;
131#endif
132
133	switch (addr->sa_family) {
134	case AF_INET:
135		if (!oldstyle_local)
136			return 0;
137		sin = (struct sockaddr_in *)addr;
138        	return ((sin->sin_addr.s_addr == htonl(INADDR_LOOPBACK)) &&
139		    (ntohs(sin->sin_port) < IPPORT_RESERVED));
140#ifdef INET6
141	case AF_INET6:
142		if (!oldstyle_local)
143			return 0;
144		sin6 = (struct sockaddr_in6 *)addr;
145		return (IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr) &&
146		    (ntohs(sin6->sin6_port) < IPV6PORT_RESERVED));
147#endif
148	case AF_LOCAL:
149		return 1;
150	default:
151		break;
152	}
153
154	return 0;
155}
156
157
158/* logit - report events of interest via the syslog daemon */
159void
160logit(int severity, struct sockaddr *addr, rpcproc_t procnum, rpcprog_t prognum,
161      const char *text)
162{
163	const char *procname;
164	char	procbuf[32];
165	char   *progname;
166	char	progbuf[32];
167	char fromname[NI_MAXHOST];
168	struct rpcent *rpc;
169	static const char *procmap[] = {
170	/* RPCBPROC_NULL */		"null",
171	/* RPCBPROC_SET */		"set",
172	/* RPCBPROC_UNSET */		"unset",
173	/* RPCBPROC_GETADDR */		"getport/addr",
174	/* RPCBPROC_DUMP */		"dump",
175	/* RPCBPROC_CALLIT */		"callit",
176	/* RPCBPROC_GETTIME */		"gettime",
177	/* RPCBPROC_UADDR2TADDR */	"uaddr2taddr",
178	/* RPCBPROC_TADDR2UADDR */	"taddr2uaddr",
179	/* RPCBPROC_GETVERSADDR */	"getversaddr",
180	/* RPCBPROC_INDIRECT */		"indirect",
181	/* RPCBPROC_GETADDRLIST */	"getaddrlist",
182	/* RPCBPROC_GETSTAT */		"getstat"
183	};
184
185	/*
186	 * Fork off a process or the portmap daemon might hang while
187	 * getrpcbynumber() or syslog() does its thing.
188	 */
189
190	if (fork() == 0) {
191		setproctitle("logit");
192
193		/* Try to map program number to name. */
194
195		if (prognum == 0) {
196			progname = "";
197		} else if ((rpc = getrpcbynumber((int) prognum))) {
198			progname = rpc->r_name;
199		} else {
200			snprintf(progname = progbuf, sizeof(progbuf), "%u",
201			    (unsigned)prognum);
202		}
203
204		/* Try to map procedure number to name. */
205
206		if (procnum >= (sizeof procmap / sizeof (char *))) {
207			snprintf(procbuf, sizeof procbuf, "%u",
208			    (unsigned)procnum);
209			procname = procbuf;
210		} else
211			procname = procmap[procnum];
212
213		/* Write syslog record. */
214
215		if (addr->sa_family == AF_LOCAL)
216			strcpy(fromname, "local");
217		else
218			getnameinfo(addr, addr->sa_len, fromname,
219			    sizeof fromname, NULL, 0, NI_NUMERICHOST);
220
221		syslog(severity, "connect from %s to %s(%s)%s",
222			fromname, procname, progname, text);
223		_exit(0);
224	}
225}
226
227int
228check_callit(SVCXPRT *xprt, struct r_rmtcall_args *args, int versnum __unused)
229{
230	struct sockaddr *sa = (struct sockaddr *)svc_getrpccaller(xprt)->buf;
231
232	/*
233	 * Always allow calling NULLPROC
234	 */
235	if (args->rmt_proc == 0)
236		return 1;
237
238	/*
239	 * XXX - this special casing sucks.
240	 */
241	switch (args->rmt_prog) {
242	case RPCBPROG:
243		/*
244		 * Allow indirect calls to ourselves in insecure mode.
245		 * The is_loopback checks aren't useful then anyway.
246		 */
247		if (!insecure)
248			goto deny;
249		break;
250	case MOUNTPROG:
251		if (args->rmt_proc != MOUNTPROC_MNT &&
252		    args->rmt_proc != MOUNTPROC_UMNT)
253			break;
254		goto deny;
255	case YPBINDPROG:
256		if (args->rmt_proc != YPBINDPROC_SETDOM)
257			break;
258		/* FALLTHROUGH */
259	case YPPASSWDPROG:
260	case NFS_PROGRAM:
261	case RQUOTAPROG:
262		goto deny;
263	case YPPROG:
264		switch (args->rmt_proc) {
265		case YPPROC_ALL:
266		case YPPROC_MATCH:
267		case YPPROC_FIRST:
268		case YPPROC_NEXT:
269			goto deny;
270		default:
271			break;
272		}
273	default:
274		break;
275	}
276
277	return 1;
278deny:
279#ifdef LIBWRAP
280	logit(deny_severity, sa, args->rmt_proc, args->rmt_prog,
281	    ": indirect call not allowed");
282#else
283	logit(0, sa, args->rmt_proc, args->rmt_prog,
284	    ": indirect call not allowed");
285#endif
286	return 0;
287}
288