yp_access.c revision 14302
112891Swpaul/*
212891Swpaul * Copyright (c) 1995
312891Swpaul *	Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
412891Swpaul *
512891Swpaul * Redistribution and use in source and binary forms, with or without
612891Swpaul * modification, are permitted provided that the following conditions
712891Swpaul * are met:
812891Swpaul * 1. Redistributions of source code must retain the above copyright
912891Swpaul *    notice, this list of conditions and the following disclaimer.
1012891Swpaul * 2. Redistributions in binary form must reproduce the above copyright
1112891Swpaul *    notice, this list of conditions and the following disclaimer in the
1212891Swpaul *    documentation and/or other materials provided with the distribution.
1312891Swpaul * 3. All advertising materials mentioning features or use of this software
1412891Swpaul *    must display the following acknowledgement:
1512891Swpaul *	This product includes software developed by Bill Paul.
1612891Swpaul * 4. Neither the name of the author nor the names of any co-contributors
1712891Swpaul *    may be used to endorse or promote products derived from this software
1812891Swpaul *    without specific prior written permission.
1912891Swpaul *
2012891Swpaul * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
2112891Swpaul * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2212891Swpaul * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2312891Swpaul * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
2412891Swpaul * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2512891Swpaul * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2612891Swpaul * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2712891Swpaul * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2812891Swpaul * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2912891Swpaul * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3012891Swpaul * SUCH DAMAGE.
3112891Swpaul *
3212891Swpaul */
3312891Swpaul
3414248Swpaul#include <stdlib.h>
3512891Swpaul#include <rpc/rpc.h>
3614240Swpaul#include <rpcsvc/yp.h>
3714240Swpaul#include <rpcsvc/yppasswd.h>
3812891Swpaul#include <sys/types.h>
3912891Swpaul#include <sys/socket.h>
4012891Swpaul#include <netinet/in.h>
4112891Swpaul#include <arpa/inet.h>
4212891Swpaul#include <sys/stat.h>
4312891Swpaul#include <paths.h>
4414240Swpaul#include <errno.h>
4512891Swpaul#include <sys/param.h>
4612891Swpaul#include "yp_extern.h"
4712891Swpaul#ifdef TCP_WRAPPER
4812891Swpaul#include "tcpd.h"
4912891Swpaul#endif
5012891Swpaul
5114240Swpaul#ifndef lint
5214302Sadamstatic const char rcsid[] = "$Id: yp_access.c,v 1.5 1996/02/26 02:34:23 wpaul Exp $";
5314240Swpaul#endif
5414240Swpaul
5512891Swpaulextern int debug;
5612891Swpaul
5714262Swpaul			/* NIS v1 */
5814262Swpaulchar *yp_procs[] = {	"ypoldproc_null",
5914262Swpaul			"ypoldproc_domain",
6014262Swpaul			"ypoldproc_domain_nonack",
6114262Swpaul			"ypoldproc_match",
6214262Swpaul			"ypoldproc_first",
6314262Swpaul			"ypoldproc_next",
6414262Swpaul			"ypoldproc_poll",
6514262Swpaul			"ypoldproc_push",
6614262Swpaul			"ypoldproc_get",
6714262Swpaul			"badproc1", /* placeholder */
6814262Swpaul			"badproc2", /* placeholder */
6914262Swpaul			"badproc3", /* placeholder */
7014262Swpaul
7114262Swpaul			/* NIS v2 */
7214262Swpaul			"ypproc_null" ,
7312891Swpaul			"ypproc_domain",
7412891Swpaul			"ypproc_domain_nonack",
7512891Swpaul			"ypproc_match",
7612891Swpaul			"ypproc_first",
7712891Swpaul			"ypproc_next",
7812891Swpaul			"ypproc_xfr",
7912891Swpaul			"ypproc_clear",
8012891Swpaul			"ypproc_all",
8112891Swpaul			"ypproc_master",
8212891Swpaul			"ypproc_order",
8312891Swpaul			"ypproc_maplist"
8412891Swpaul		   };
8512891Swpaul
8614262Swpaul
8714240Swpaul#ifdef TCP_WRAPPER
8814240Swpaulvoid load_securenets()
8914240Swpaul{
9014240Swpaul}
9114240Swpaul#else
9214240Swpaulstruct securenet {
9314240Swpaul	struct in_addr net;
9414240Swpaul	struct in_addr mask;
9514240Swpaul	struct securenet *next;
9614240Swpaul};
9714240Swpaul
9814240Swpaulstruct securenet *securenets;
9914240Swpaul
10014240Swpaul#define LINEBUFSZ 1024
10114240Swpaul
10212891Swpaul/*
10314240Swpaul * Read /var/yp/securenets file and initialize the securenets
10414240Swpaul * list. If the file doesn't exist, we set up a dummy entry that
10514240Swpaul * allows all hosts to connect.
10614240Swpaul */
10714240Swpaulvoid load_securenets()
10814240Swpaul{
10914240Swpaul	FILE *fp;
11014240Swpaul	char path[MAXPATHLEN + 2];
11114240Swpaul	char linebuf[1024 + 2];
11214240Swpaul	struct securenet *tmp;
11314240Swpaul
11414240Swpaul	/*
11514240Swpaul	 * If securenets is not NULL, we are being called to reload
11614240Swpaul	 * the list; free the existing list before re-reading the
11714240Swpaul	 * securenets file.
11814240Swpaul	 */
11914240Swpaul	if (securenets != NULL) {
12014240Swpaul		while(securenets) {
12114240Swpaul			tmp = securenets->next;
12214240Swpaul			free(securenets);
12314240Swpaul			securenets = tmp;
12414240Swpaul		}
12514240Swpaul	}
12614240Swpaul
12714240Swpaul	snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir);
12814240Swpaul
12914240Swpaul	if ((fp = fopen(path, "r")) == NULL) {
13014240Swpaul		if (errno == ENOENT) {
13114240Swpaul			securenets = (struct securenet *)malloc(sizeof(struct securenet));
13214240Swpaul			securenets->net.s_addr = INADDR_ANY;
13314302Sadam			securenets->mask.s_addr = INADDR_ANY;
13414240Swpaul			securenets->next = NULL;
13514240Swpaul			return;
13614240Swpaul		} else {
13714240Swpaul			yp_error("fopen(%s) failed: %s", path, strerror(errno));
13814240Swpaul			exit(1);
13914240Swpaul		}
14014240Swpaul	}
14114240Swpaul
14214240Swpaul	securenets = NULL;
14314240Swpaul
14414240Swpaul	while(fgets(linebuf, LINEBUFSZ, fp)) {
14514240Swpaul		char addr1[20], addr2[20];
14614240Swpaul
14714240Swpaul		if (linebuf[0] == '#')
14814240Swpaul			continue;
14914240Swpaul		if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) {
15014240Swpaul			yp_error("badly formatted securenets entry: %s",
15114240Swpaul							linebuf);
15214240Swpaul			continue;
15314240Swpaul		}
15414240Swpaul
15514240Swpaul		tmp = (struct securenet *)malloc(sizeof(struct securenet));
15614240Swpaul
15714240Swpaul		if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) {
15814240Swpaul			yp_error("badly formatted securenets entry: %s", addr1);
15914240Swpaul			free(tmp);
16014240Swpaul			continue;
16114240Swpaul		}
16214240Swpaul
16314240Swpaul		if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) {
16414240Swpaul			yp_error("badly formatted securenets entry: %s", addr2);
16514240Swpaul			free(tmp);
16614240Swpaul			continue;
16714240Swpaul		}
16814240Swpaul
16914240Swpaul		tmp->next = securenets;
17014240Swpaul		securenets = tmp;
17114240Swpaul	}
17214240Swpaul
17314240Swpaul	fclose(fp);
17414240Swpaul
17514240Swpaul}
17614240Swpaul#endif
17714240Swpaul
17814240Swpaul/*
17912891Swpaul * Access control functions.
18012891Swpaul *
18112891Swpaul * yp_access() checks the mapname and client host address and watches for
18212891Swpaul * the following things:
18312891Swpaul *
18412891Swpaul * - If the client is referencing one of the master.passwd.* maps, it must
18512891Swpaul *   be using a privileged port to make its RPC to us. If it is, then we can
18612891Swpaul *   assume that the caller is root and allow the RPC to succeed. If it
18712891Swpaul *   isn't access is denied.
18812891Swpaul *
18914240Swpaul * - The client's IP address is checked against the securenets rules.
19014240Swpaul *   There are two kinds of securenets support: the built-in support,
19114240Swpaul *   which is very simple and depends on the presense of a
19214240Swpaul *   /var/yp/securenets file, and tcp-wrapper support, which requires
19314240Swpaul *   Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper
19414240Swpaul *   package does not ship with FreeBSD, we use the built-in support
19514240Swpaul *   by default. Users can recompile the server the tcp-wrapper library
19614240Swpaul *   if they already have it installed and want to use hosts.allow and
19714240Swpaul *   hosts.deny to control access instead od having a seperate securenets
19814240Swpaul *   file.)
19912891Swpaul *
20014240Swpaul *   If no /var/yp/securenets file is present, the host access checks
20114240Swpaul *   are bypassed and all hosts are allowed to connect.
20214240Swpaul *
20312891Swpaul * The yp_validdomain() functions checks the domain specified by the caller
20412891Swpaul * to make sure it's actually served by this server. This is more a sanity
20512891Swpaul * check than an a security check, but this seems to be the best place for
20612891Swpaul * it.
20712891Swpaul */
20812891Swpaul
20912891Swpaulint yp_access(map, rqstp)
21012891Swpaul	const char *map;
21112891Swpaul	const struct svc_req *rqstp;
21212891Swpaul{
21312891Swpaul	struct sockaddr_in *rqhost;
21412891Swpaul	int status = 0;
21512891Swpaul	unsigned long oldaddr;
21614240Swpaul#ifndef TCP_WRAPPER
21714240Swpaul	struct securenet *tmp;
21812891Swpaul#endif
21914262Swpaul	char *yp_procedure = NULL;
22012891Swpaul
22114262Swpaul	yp_procedure = rqstp->rq_prog == YPPASSWDPROG ? "yppasswdprog_update" :
22214262Swpaul			yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))];
22314262Swpaul
22412891Swpaul	rqhost = svc_getcaller(rqstp->rq_xprt);
22512891Swpaul
22612891Swpaul	if (debug) {
22714262Swpaul		yp_error("Procedure %s called from %s:%d", yp_procedure,
22814262Swpaul			inet_ntoa(rqhost->sin_addr),
22912891Swpaul			ntohs(rqhost->sin_port));
23012891Swpaul		if (map != NULL)
23112891Swpaul			yp_error("Client is referencing map \"%s\".", map);
23212891Swpaul	}
23312891Swpaul
23412891Swpaul	/* Check the map name if one was supplied. */
23512891Swpaul	if (map != NULL) {
23614240Swpaul		if ((strstr(map, "master.passwd.") ||
23714240Swpaul		    rqstp->rq_proc == YPPROC_XFR) &&
23814240Swpaul		    ntohs(rqhost->sin_port) > 1023) {
23912891Swpaul			yp_error("Access to %s denied -- client not privileged", map);
24012891Swpaul			return(1);
24112891Swpaul		}
24212891Swpaul	}
24312891Swpaul
24412891Swpaul#ifdef TCP_WRAPPER
24512891Swpaul	status = hosts_ctl(progname, STRING_UNKNOWN,
24614240Swpaul			   inet_ntoa(rqhost->sin_addr), "");
24714240Swpaul#else
24814240Swpaul	tmp = securenets;
24914240Swpaul	while(tmp) {
25014240Swpaul		if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr)
25114240Swpaul		    | tmp->net.s_addr) == rqhost->sin_addr.s_addr) {
25214240Swpaul			status = 1;
25314240Swpaul			break;
25414240Swpaul		}
25514240Swpaul		tmp = tmp->next;
25614240Swpaul	}
25714240Swpaul#endif
25812891Swpaul
25914240Swpaul	if (!status) {
26014240Swpaul		if (rqhost->sin_addr.s_addr != oldaddr) {
26114240Swpaul			yp_error("connect from %s:%d to procedure %s refused",
26214240Swpaul					inet_ntoa(rqhost->sin_addr),
26314240Swpaul					ntohs(rqhost->sin_port),
26414262Swpaul					yp_procedure);
26514240Swpaul			oldaddr = rqhost->sin_addr.s_addr;
26614240Swpaul		}
26712891Swpaul		return(1);
26812891Swpaul	}
26912891Swpaul	return(0);
27012891Swpaul
27112891Swpaul}
27212891Swpaul
27312891Swpaulint yp_validdomain(domain)
27412891Swpaul	const char *domain;
27512891Swpaul{
27612891Swpaul	struct stat statbuf;
27712891Swpaul	char dompath[MAXPATHLEN + 2];
27812891Swpaul
27912891Swpaul	if (domain == NULL || strstr(domain, "binding") ||
28012891Swpaul	    !strcmp(domain, ".") || !strcmp(domain, "..") ||
28114240Swpaul	    strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN)
28212891Swpaul		return(1);
28312891Swpaul
28412891Swpaul	snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain);
28512891Swpaul
28612891Swpaul	if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode))
28712891Swpaul		return(1);
28812891Swpaul
28912891Swpaul	return(0);
29012891Swpaul}
291