yp_access.c revision 16044
1116742Ssam/*
2116904Ssam * Copyright (c) 1995
3186904Ssam *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
4116742Ssam *
5116742Ssam * Redistribution and use in source and binary forms, with or without
6116742Ssam * modification, are permitted provided that the following conditions
7116742Ssam * are met:
8116742Ssam * 1. Redistributions of source code must retain the above copyright
9116742Ssam *    notice, this list of conditions and the following disclaimer.
10116742Ssam * 2. Redistributions in binary form must reproduce the above copyright
11116742Ssam *    notice, this list of conditions and the following disclaimer in the
12116742Ssam *    documentation and/or other materials provided with the distribution.
13116742Ssam * 3. All advertising materials mentioning features or use of this software
14116742Ssam *    must display the following acknowledgement:
15116904Ssam *	This product includes software developed by Bill Paul.
16116904Ssam * 4. Neither the name of the author nor the names of any co-contributors
17116904Ssam *    may be used to endorse or promote products derived from this software
18116904Ssam *    without specific prior written permission.
19116904Ssam *
20116904Ssam * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
21116904Ssam * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22116904Ssam * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23116904Ssam * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
24116904Ssam * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25116904Ssam * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26116742Ssam * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27116742Ssam * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28116742Ssam * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29116742Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30116742Ssam * SUCH DAMAGE.
31116742Ssam *
32116742Ssam */
33116742Ssam
34138568Ssam#include <stdlib.h>
35138568Ssam#include <rpc/rpc.h>
36138568Ssam#include <rpcsvc/yp.h>
37116742Ssam#include <rpcsvc/yppasswd.h>
38138568Ssam#include <sys/types.h>
39161146Ssam#include <limits.h>
40138568Ssam#include <db.h>
41138568Ssam#include <sys/socket.h>
42170530Ssam#include <netinet/in.h>
43170530Ssam#include <arpa/inet.h>
44170530Ssam#include <sys/stat.h>
45170530Ssam#include <sys/fcntl.h>
46170530Ssam#include <paths.h>
47170530Ssam#include <errno.h>
48170530Ssam#include <sys/param.h>
49170530Ssam#include "yp_extern.h"
50138568Ssam#ifdef TCP_WRAPPER
51170530Ssam#include "tcpd.h"
52170530Ssam#endif
53170530Ssam
54170530Ssam#ifndef lint
55170530Ssamstatic const char rcsid[] = "$Id: yp_access.c,v 1.7 1996/04/28 04:38:47 wpaul Exp $";
56170530Ssam#endif
57170530Ssam
58170530Ssamextern int debug;
59170530Ssam
60170530Ssam			/* NIS v1 */
61170530Ssamchar *yp_procs[] = {	"ypoldproc_null",
62170530Ssam			"ypoldproc_domain",
63173273Ssam			"ypoldproc_domain_nonack",
64138568Ssam			"ypoldproc_match",
65170530Ssam			"ypoldproc_first",
66170530Ssam			"ypoldproc_next",
67193239Ssam			"ypoldproc_poll",
68170530Ssam			"ypoldproc_push",
69170530Ssam			"ypoldproc_get",
70170530Ssam			"badproc1", /* placeholder */
71170530Ssam			"badproc2", /* placeholder */
72138568Ssam			"badproc3", /* placeholder */
73170530Ssam
74170530Ssam			/* NIS v2 */
75138568Ssam			"ypproc_null" ,
76170530Ssam			"ypproc_domain",
77138568Ssam			"ypproc_domain_nonack",
78138568Ssam			"ypproc_match",
79170530Ssam			"ypproc_first",
80170530Ssam			"ypproc_next",
81170530Ssam			"ypproc_xfr",
82170530Ssam			"ypproc_clear",
83170530Ssam			"ypproc_all",
84170530Ssam			"ypproc_master",
85170530Ssam			"ypproc_order",
86170530Ssam			"ypproc_maplist"
87193239Ssam		   };
88138568Ssam
89138568Ssam
90138568Ssam#ifdef TCP_WRAPPER
91138568Ssamvoid load_securenets()
92138568Ssam{
93121180Ssam}
94170530Ssam#else
95170530Ssamstruct securenet {
96170530Ssam	struct in_addr net;
97170530Ssam	struct in_addr mask;
98170530Ssam	struct securenet *next;
99170530Ssam};
100170530Ssam
101170530Ssamstruct securenet *securenets;
102170530Ssam
103170530Ssam#define LINEBUFSZ 1024
104170530Ssam
105170530Ssam/*
106193239Ssam * Read /var/yp/securenets file and initialize the securenets
107170530Ssam * list. If the file doesn't exist, we set up a dummy entry that
108170530Ssam * allows all hosts to connect.
109170530Ssam */
110170530Ssamvoid load_securenets()
111170530Ssam{
112170530Ssam	FILE *fp;
113170530Ssam	char path[MAXPATHLEN + 2];
114170530Ssam	char linebuf[1024 + 2];
115170530Ssam	struct securenet *tmp;
116170530Ssam
117170530Ssam	/*
118170530Ssam	 * If securenets is not NULL, we are being called to reload
119170530Ssam	 * the list; free the existing list before re-reading the
120170530Ssam	 * securenets file.
121170530Ssam	 */
122170530Ssam	while(securenets) {
123170530Ssam		tmp = securenets->next;
124170530Ssam		free(securenets);
125170530Ssam		securenets = tmp;
126170530Ssam	}
127170530Ssam
128170530Ssam	snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir);
129170530Ssam
130170530Ssam	if ((fp = fopen(path, "r")) == NULL) {
131170530Ssam		if (errno == ENOENT) {
132170530Ssam			securenets = (struct securenet *)malloc(sizeof(struct securenet));
133170530Ssam			securenets->net.s_addr = INADDR_ANY;
134170530Ssam			securenets->mask.s_addr = INADDR_ANY;
135170530Ssam			securenets->next = NULL;
136170530Ssam			return;
137170530Ssam		} else {
138170530Ssam			yp_error("fopen(%s) failed: %s", path, strerror(errno));
139170530Ssam			exit(1);
140170530Ssam		}
141170530Ssam	}
142170530Ssam
143170530Ssam	securenets = NULL;
144170530Ssam
145170530Ssam	while(fgets(linebuf, LINEBUFSZ, fp)) {
146170530Ssam		char addr1[20], addr2[20];
147170530Ssam
148170530Ssam		if (linebuf[0] == '#')
149170530Ssam			continue;
150170530Ssam		if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) {
151170530Ssam			yp_error("badly formatted securenets entry: %s",
152170530Ssam							linebuf);
153170530Ssam			continue;
154170530Ssam		}
155170530Ssam
156170530Ssam		tmp = (struct securenet *)malloc(sizeof(struct securenet));
157170530Ssam
158170530Ssam		if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) {
159170530Ssam			yp_error("badly formatted securenets entry: %s", addr1);
160170530Ssam			free(tmp);
161170530Ssam			continue;
162170530Ssam		}
163170530Ssam
164170530Ssam		if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) {
165170530Ssam			yp_error("badly formatted securenets entry: %s", addr2);
166170530Ssam			free(tmp);
167170530Ssam			continue;
168170530Ssam		}
169170530Ssam
170170530Ssam		tmp->next = securenets;
171170530Ssam		securenets = tmp;
172170530Ssam	}
173170530Ssam
174170530Ssam	fclose(fp);
175170530Ssam
176170530Ssam}
177170530Ssam#endif
178170530Ssam
179170530Ssam/*
180170530Ssam * Access control functions.
181170530Ssam *
182170530Ssam * yp_access() checks the mapname and client host address and watches for
183170530Ssam * the following things:
184170530Ssam *
185170530Ssam * - If the client is referencing one of the master.passwd.* maps, it must
186170530Ssam *   be using a privileged port to make its RPC to us. If it is, then we can
187170530Ssam *   assume that the caller is root and allow the RPC to succeed. If it
188170530Ssam *   isn't access is denied.
189170530Ssam *
190170530Ssam * - The client's IP address is checked against the securenets rules.
191170530Ssam *   There are two kinds of securenets support: the built-in support,
192173273Ssam *   which is very simple and depends on the presence of a
193170530Ssam *   /var/yp/securenets file, and tcp-wrapper support, which requires
194170530Ssam *   Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper
195170530Ssam *   package does not ship with FreeBSD, we use the built-in support
196170530Ssam *   by default. Users can recompile the server with the tcp-wrapper library
197173273Ssam *   if they already have it installed and want to use hosts.allow and
198173273Ssam *   hosts.deny to control access instead of having a seperate securenets
199173273Ssam *   file.)
200173273Ssam *
201178354Ssam *   If no /var/yp/securenets file is present, the host access checks
202178354Ssam *   are bypassed and all hosts are allowed to connect.
203173273Ssam *
204173273Ssam * The yp_validdomain() function checks the domain specified by the caller
205173273Ssam * to make sure it's actually served by this server. This is more a sanity
206173273Ssam * check than an a security check, but this seems to be the best place for
207173273Ssam * it.
208173273Ssam */
209173273Ssam
210173273Ssamint yp_access(map, rqstp)
211173273Ssam	const char *map;
212182829Ssam	const struct svc_req *rqstp;
213173273Ssam{
214173273Ssam	struct sockaddr_in *rqhost;
215173273Ssam	int status = 0;
216178354Ssam	static unsigned long oldaddr = 0;
217178354Ssam#ifndef TCP_WRAPPER
218178354Ssam	struct securenet *tmp;
219178354Ssam#endif
220178354Ssam	char *yp_procedure = NULL;
221186904Ssam
222190579Ssam	yp_procedure = rqstp->rq_prog == YPPASSWDPROG ? "yppasswdprog_update" :
223193239Ssam			yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))];
224193239Ssam
225193239Ssam	rqhost = svc_getcaller(rqstp->rq_xprt);
226195618Srpaulo
227195618Srpaulo	if (debug) {
228195618Srpaulo		yp_error("Procedure %s called from %s:%d", yp_procedure,
229195618Srpaulo			inet_ntoa(rqhost->sin_addr),
230195618Srpaulo			ntohs(rqhost->sin_port));
231195618Srpaulo		if (map != NULL)
232195618Srpaulo			yp_error("Client is referencing map \"%s\".", map);
233195618Srpaulo	}
234195618Srpaulo
235195618Srpaulo	/* Check the map name if one was supplied. */
236195618Srpaulo	if (map != NULL) {
237195618Srpaulo		if ((strstr(map, "master.passwd.") ||
238195784Srpaulo		    rqstp->rq_proc == YPPROC_XFR) &&
239195784Srpaulo		    ntohs(rqhost->sin_port) > 1023) {
240195784Srpaulo			yp_error("Access to %s denied -- client not privileged", map);
241195784Srpaulo			return(1);
242195757Ssam		}
243195908Srpaulo	}
244232244Sadrian
245234018Sadrian#ifdef TCP_WRAPPER
246234018Sadrian	status = hosts_ctl(progname, STRING_UNKNOWN,
247234018Sadrian			   inet_ntoa(rqhost->sin_addr), "");
248234018Sadrian#else
249234018Sadrian	tmp = securenets;
250121180Ssam	while(tmp) {
251121180Ssam		if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr)
252138568Ssam		    | tmp->net.s_addr) == rqhost->sin_addr.s_addr) {
253138568Ssam			status = 1;
254138568Ssam			break;
255138568Ssam		}
256138568Ssam		tmp = tmp->next;
257138568Ssam	}
258138568Ssam#endif
259138568Ssam
260138568Ssam	if (!status) {
261138568Ssam		if (rqhost->sin_addr.s_addr != oldaddr) {
262138568Ssam			yp_error("connect from %s:%d to procedure %s refused",
263138568Ssam					inet_ntoa(rqhost->sin_addr),
264138568Ssam					ntohs(rqhost->sin_port),
265138568Ssam					yp_procedure);
266138568Ssam			oldaddr = rqhost->sin_addr.s_addr;
267173273Ssam		}
268138568Ssam		return(1);
269138568Ssam	}
270170530Ssam	return(0);
271170530Ssam
272170530Ssam}
273170530Ssam
274170530Ssamint yp_validdomain(domain)
275138568Ssam	const char *domain;
276138568Ssam{
277170530Ssam	struct stat statbuf;
278170530Ssam	char dompath[MAXPATHLEN + 2];
279170530Ssam
280170530Ssam	if (domain == NULL || strstr(domain, "binding") ||
281138568Ssam	    !strcmp(domain, ".") || !strcmp(domain, "..") ||
282138568Ssam	    strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN)
283138568Ssam		return(1);
284138568Ssam
285138568Ssam	snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain);
286138568Ssam
287138568Ssam	if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode))
288170530Ssam		return(1);
289170530Ssam
290138568Ssam
291138568Ssam	return(0);
292138568Ssam}
293138568Ssam