yp_access.c revision 19161
1/*
2 * Copyright (c) 1995
3 *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 *    must display the following acknowledgement:
15 *	This product includes software developed by Bill Paul.
16 * 4. Neither the name of the author nor the names of any co-contributors
17 *    may be used to endorse or promote products derived from this software
18 *    without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 *
32 */
33
34#include <stdlib.h>
35#include <rpc/rpc.h>
36#include <rpcsvc/yp.h>
37#include <rpcsvc/yppasswd.h>
38#include <rpcsvc/ypxfrd.h>
39#include <sys/types.h>
40#include <limits.h>
41#include <db.h>
42#include <sys/socket.h>
43#include <netinet/in.h>
44#include <arpa/inet.h>
45#include <sys/stat.h>
46#include <sys/fcntl.h>
47#include <paths.h>
48#include <errno.h>
49#include <sys/param.h>
50#include "yp_extern.h"
51#ifdef TCP_WRAPPER
52#include "tcpd.h"
53#endif
54
55#ifndef lint
56static const char rcsid[] = "$Id: yp_access.c,v 1.9 1996/06/05 02:01:29 wpaul Exp $";
57#endif
58
59extern int debug;
60
61			/* NIS v1 */
62char *yp_procs[] = {	"ypoldproc_null",
63			"ypoldproc_domain",
64			"ypoldproc_domain_nonack",
65			"ypoldproc_match",
66			"ypoldproc_first",
67			"ypoldproc_next",
68			"ypoldproc_poll",
69			"ypoldproc_push",
70			"ypoldproc_get",
71			"badproc1", /* placeholder */
72			"badproc2", /* placeholder */
73			"badproc3", /* placeholder */
74
75			/* NIS v2 */
76			"ypproc_null" ,
77			"ypproc_domain",
78			"ypproc_domain_nonack",
79			"ypproc_match",
80			"ypproc_first",
81			"ypproc_next",
82			"ypproc_xfr",
83			"ypproc_clear",
84			"ypproc_all",
85			"ypproc_master",
86			"ypproc_order",
87			"ypproc_maplist"
88		   };
89
90
91#ifdef TCP_WRAPPER
92void load_securenets()
93{
94}
95#else
96struct securenet {
97	struct in_addr net;
98	struct in_addr mask;
99	struct securenet *next;
100};
101
102struct securenet *securenets;
103
104#define LINEBUFSZ 1024
105
106/*
107 * Read /var/yp/securenets file and initialize the securenets
108 * list. If the file doesn't exist, we set up a dummy entry that
109 * allows all hosts to connect.
110 */
111void load_securenets()
112{
113	FILE *fp;
114	char path[MAXPATHLEN + 2];
115	char linebuf[1024 + 2];
116	struct securenet *tmp;
117
118	/*
119	 * If securenets is not NULL, we are being called to reload
120	 * the list; free the existing list before re-reading the
121	 * securenets file.
122	 */
123	while(securenets) {
124		tmp = securenets->next;
125		free(securenets);
126		securenets = tmp;
127	}
128
129	snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir);
130
131	if ((fp = fopen(path, "r")) == NULL) {
132		if (errno == ENOENT) {
133			securenets = (struct securenet *)malloc(sizeof(struct securenet));
134			securenets->net.s_addr = INADDR_ANY;
135			securenets->mask.s_addr = INADDR_ANY;
136			securenets->next = NULL;
137			return;
138		} else {
139			yp_error("fopen(%s) failed: %s", path, strerror(errno));
140			exit(1);
141		}
142	}
143
144	securenets = NULL;
145
146	while(fgets(linebuf, LINEBUFSZ, fp)) {
147		char addr1[20], addr2[20];
148
149		if (linebuf[0] == '#')
150			continue;
151		if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) {
152			yp_error("badly formatted securenets entry: %s",
153							linebuf);
154			continue;
155		}
156
157		tmp = (struct securenet *)malloc(sizeof(struct securenet));
158
159		if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) {
160			yp_error("badly formatted securenets entry: %s", addr1);
161			free(tmp);
162			continue;
163		}
164
165		if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) {
166			yp_error("badly formatted securenets entry: %s", addr2);
167			free(tmp);
168			continue;
169		}
170
171		tmp->next = securenets;
172		securenets = tmp;
173	}
174
175	fclose(fp);
176
177}
178#endif
179
180/*
181 * Access control functions.
182 *
183 * yp_access() checks the mapname and client host address and watches for
184 * the following things:
185 *
186 * - If the client is referencing one of the master.passwd.* maps, it must
187 *   be using a privileged port to make its RPC to us. If it is, then we can
188 *   assume that the caller is root and allow the RPC to succeed. If it
189 *   isn't access is denied.
190 *
191 * - The client's IP address is checked against the securenets rules.
192 *   There are two kinds of securenets support: the built-in support,
193 *   which is very simple and depends on the presence of a
194 *   /var/yp/securenets file, and tcp-wrapper support, which requires
195 *   Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper
196 *   package does not ship with FreeBSD, we use the built-in support
197 *   by default. Users can recompile the server with the tcp-wrapper library
198 *   if they already have it installed and want to use hosts.allow and
199 *   hosts.deny to control access instead of having a seperate securenets
200 *   file.)
201 *
202 *   If no /var/yp/securenets file is present, the host access checks
203 *   are bypassed and all hosts are allowed to connect.
204 *
205 * The yp_validdomain() function checks the domain specified by the caller
206 * to make sure it's actually served by this server. This is more a sanity
207 * check than an a security check, but this seems to be the best place for
208 * it.
209 */
210
211#ifdef DB_CACHE
212int yp_access(map, domain, rqstp)
213#else
214int yp_access(map, rqstp)
215#endif
216	const char *map;
217#ifdef DB_CACHE
218	const char *domain;
219#endif
220	const struct svc_req *rqstp;
221{
222	struct sockaddr_in *rqhost;
223	int status = 0;
224	static unsigned long oldaddr = 0;
225#ifndef TCP_WRAPPER
226	struct securenet *tmp;
227#endif
228	char *yp_procedure = NULL;
229	char procbuf[50];
230
231	if (rqstp->rq_prog != YPPASSWDPROG && rqstp->rq_prog != YPPROG) {
232		snprintf(procbuf, sizeof(procbuf), "#%lu/#%lu", rqstp->rq_prog,
233								rqstp->rq_proc);
234		yp_procedure = (char *)&procbuf;
235	} else {
236		yp_procedure = rqstp->rq_prog == YPPASSWDPROG ?
237		"yppasswdprog_update" :
238		yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))];
239	}
240
241	rqhost = svc_getcaller(rqstp->rq_xprt);
242
243	if (debug) {
244		yp_error("Procedure %s called from %s:%d", yp_procedure,
245			inet_ntoa(rqhost->sin_addr),
246			ntohs(rqhost->sin_port));
247		if (map != NULL)
248			yp_error("Client is referencing map \"%s\".", map);
249	}
250
251	/* Check the map name if one was supplied. */
252	if (map != NULL) {
253		if (strchr(map, '/')) {
254			yp_error("embedded slash in map name \"%s\" -- \
255possible spoof attempt from %s:%d",
256				map, inet_ntoa(rqhost->sin_addr),
257				ntohs(rqhost->sin_port));
258		}
259#ifdef DB_CACHE
260		if ((yp_testflag((char *)map, (char *)domain, YP_SECURE) ||
261#else
262		if ((strstr(map, "master.passwd.") ||
263#endif
264		    (rqstp->rq_prog == YPPROG &&
265		     rqstp->rq_proc == YPPROC_XFR) ||
266		    (rqstp->rq_prog == YPXFRD_FREEBSD_PROG &&
267		     rqstp->rq_proc == YPXFRD_GETMAP)) &&
268		     ntohs(rqhost->sin_port) >= IPPORT_RESERVED) {
269			yp_error("Access to %s denied -- client %s:%d \
270not privileged", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port));
271			return(1);
272		}
273	}
274
275#ifdef TCP_WRAPPER
276	status = hosts_ctl(progname, STRING_UNKNOWN,
277			   inet_ntoa(rqhost->sin_addr), "");
278#else
279	tmp = securenets;
280	while(tmp) {
281		if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr)
282		    | tmp->net.s_addr) == rqhost->sin_addr.s_addr) {
283			status = 1;
284			break;
285		}
286		tmp = tmp->next;
287	}
288#endif
289
290	if (!status) {
291		if (rqhost->sin_addr.s_addr != oldaddr) {
292			yp_error("connect from %s:%d to procedure %s refused",
293					inet_ntoa(rqhost->sin_addr),
294					ntohs(rqhost->sin_port),
295					yp_procedure);
296			oldaddr = rqhost->sin_addr.s_addr;
297		}
298		return(1);
299	}
300	return(0);
301
302}
303
304int yp_validdomain(domain)
305	const char *domain;
306{
307	struct stat statbuf;
308	char dompath[MAXPATHLEN + 2];
309
310	if (domain == NULL || strstr(domain, "binding") ||
311	    !strcmp(domain, ".") || !strcmp(domain, "..") ||
312	    strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN)
313		return(1);
314
315	snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain);
316
317	if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode))
318		return(1);
319
320
321	return(0);
322}
323