xref: /openbsd-src/usr.sbin/lpr/lpd/allowedhost.c (revision 3a50f0a93a2072911d0ba6ababa815fb04bf9a71)
168500a17Sderaadt /*
268500a17Sderaadt  * Copyright (c) 1995, 1996, 1998 Theo de Raadt.  All rights reserved.
368500a17Sderaadt  * Copyright (c) 1983, 1993, 1994
468500a17Sderaadt  *	The Regents of the University of California.  All rights reserved.
568500a17Sderaadt  *
668500a17Sderaadt  * Redistribution and use in source and binary forms, with or without
768500a17Sderaadt  * modification, are permitted provided that the following conditions
868500a17Sderaadt  * are met:
968500a17Sderaadt  * 1. Redistributions of source code must retain the above copyright
1068500a17Sderaadt  *    notice, this list of conditions and the following disclaimer.
1168500a17Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
1268500a17Sderaadt  *    notice, this list of conditions and the following disclaimer in the
1368500a17Sderaadt  *    documentation and/or other materials provided with the distribution.
1468500a17Sderaadt  * 3. Neither the name of the University nor the names of its contributors
1568500a17Sderaadt  *    may be used to endorse or promote products derived from this software
1668500a17Sderaadt  *    without specific prior written permission.
1768500a17Sderaadt  *
1868500a17Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
1968500a17Sderaadt  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2068500a17Sderaadt  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2168500a17Sderaadt  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2268500a17Sderaadt  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2368500a17Sderaadt  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2468500a17Sderaadt  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2568500a17Sderaadt  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2668500a17Sderaadt  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2768500a17Sderaadt  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2868500a17Sderaadt  * SUCH DAMAGE.
2968500a17Sderaadt  */
3068500a17Sderaadt 
3168500a17Sderaadt #include <sys/socket.h>
3268500a17Sderaadt #include <sys/types.h>
3368500a17Sderaadt 
3468500a17Sderaadt #include <netinet/in.h>
3568500a17Sderaadt #include <arpa/inet.h>
3668500a17Sderaadt 
3768500a17Sderaadt #include <ctype.h>
3868500a17Sderaadt #include <limits.h>
3968500a17Sderaadt #include <netdb.h>
4068500a17Sderaadt #include <netgroup.h>
4168500a17Sderaadt #include <signal.h>
4268500a17Sderaadt #include <stdio.h>
4368500a17Sderaadt #include <stdlib.h>
4468500a17Sderaadt #include <string.h>
4568500a17Sderaadt #include <syslog.h>
4668500a17Sderaadt #include <unistd.h>
4768500a17Sderaadt 
4868500a17Sderaadt static int checkhost(struct sockaddr *, socklen_t, const char *);
4968500a17Sderaadt static char *gethostloop(struct sockaddr *, socklen_t);
5068500a17Sderaadt 
5168500a17Sderaadt /*
5268500a17Sderaadt  * Check whether the specified addr is listed in hostf.
5368500a17Sderaadt  * Returns 0 if allowed, else -1.
5468500a17Sderaadt  */
5568500a17Sderaadt int
allowedhost(FILE * hostf,struct sockaddr * raddr,socklen_t salen)5668500a17Sderaadt allowedhost(FILE *hostf, struct sockaddr *raddr, socklen_t salen)
5768500a17Sderaadt {
5868500a17Sderaadt 	char *cp, *ep, *line = NULL;
5968500a17Sderaadt 	char *ahost, *rhost = (char *)-1;
6068500a17Sderaadt 	char domain[HOST_NAME_MAX+1];
6168500a17Sderaadt 	size_t linesize = 0;
6268500a17Sderaadt 	ssize_t linelen;
6368500a17Sderaadt 	int hostok = 0;
6468500a17Sderaadt 
6568500a17Sderaadt 	getdomainname(domain, sizeof(domain));
6668500a17Sderaadt 
6768500a17Sderaadt 	while ((linelen = getline(&line, &linesize, hostf)) != -1) {
6868500a17Sderaadt 		cp = line;
6968500a17Sderaadt 		ep = line + linelen;
7068500a17Sderaadt 		if (*cp == '#')
7168500a17Sderaadt 			continue;
7268500a17Sderaadt 		while (cp < ep && !isspace((unsigned char)*cp)) {
7368500a17Sderaadt 			if (!isprint((unsigned char)*cp))
7468500a17Sderaadt 				goto bail;
7568500a17Sderaadt 			*cp = isupper((unsigned char)*cp) ?
7668500a17Sderaadt 			    tolower((unsigned char)*cp) : *cp;
7768500a17Sderaadt 			cp++;
7868500a17Sderaadt 		}
7968500a17Sderaadt 		if (cp == line)
8068500a17Sderaadt 			continue;
8168500a17Sderaadt 		*cp = '\0';
8268500a17Sderaadt 
8368500a17Sderaadt 		ahost = line;
8468500a17Sderaadt 		if (strlen(ahost) > HOST_NAME_MAX)
8568500a17Sderaadt 			continue;
8668500a17Sderaadt 
8768500a17Sderaadt 		/*
8868500a17Sderaadt 		 * innetgr() must lookup a hostname (we do not attempt
8968500a17Sderaadt 		 * to change the semantics so that netgroups may have
9068500a17Sderaadt 		 * #.#.#.# addresses in the list.)
9168500a17Sderaadt 		 */
9268500a17Sderaadt 		switch (ahost[0]) {
9368500a17Sderaadt 		case '+':
9468500a17Sderaadt 		case '-':
9568500a17Sderaadt 			switch (ahost[1]) {
9668500a17Sderaadt 			case '\0':
9768500a17Sderaadt 				hostok = 1;
9868500a17Sderaadt 				break;
9968500a17Sderaadt 			case '@':
10068500a17Sderaadt 				if (rhost == (char *)-1)
10168500a17Sderaadt 					rhost = gethostloop(raddr, salen);
10268500a17Sderaadt 				hostok = 0;
10368500a17Sderaadt 				if (rhost)
10468500a17Sderaadt 					hostok = innetgr(&ahost[2], rhost,
10568500a17Sderaadt 					    NULL, domain);
10668500a17Sderaadt 				break;
10768500a17Sderaadt 			default:
10868500a17Sderaadt 				hostok = checkhost(raddr, salen, &ahost[1]);
10968500a17Sderaadt 				break;
11068500a17Sderaadt 			}
11168500a17Sderaadt 			if (ahost[0] == '-')
11268500a17Sderaadt 				hostok = -hostok;
11368500a17Sderaadt 			break;
11468500a17Sderaadt 		default:
11568500a17Sderaadt 			hostok = checkhost(raddr, salen, ahost);
11668500a17Sderaadt 			break;
11768500a17Sderaadt 		}
11868500a17Sderaadt 
11968500a17Sderaadt 		/* Check if we got a match (positive or negative). */
12068500a17Sderaadt 		if (hostok != 0)
12168500a17Sderaadt 			break;
12268500a17Sderaadt 	}
12368500a17Sderaadt bail:
12468500a17Sderaadt 	free(line);
12568500a17Sderaadt 	return (hostok > 0 ? 0 : -1);
12668500a17Sderaadt }
12768500a17Sderaadt 
12868500a17Sderaadt /*
12968500a17Sderaadt  * Returns 1 if match, 0 if no match.  If we do not find any
13068500a17Sderaadt  * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work.
13168500a17Sderaadt  */
13268500a17Sderaadt static int
checkhost(struct sockaddr * raddr,socklen_t salen,const char * lhost)13368500a17Sderaadt checkhost(struct sockaddr *raddr, socklen_t salen, const char *lhost)
13468500a17Sderaadt {
13568500a17Sderaadt 	struct addrinfo hints, *res, *r;
13668500a17Sderaadt 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
13768500a17Sderaadt 	int error;
13868500a17Sderaadt 	const int niflags = NI_NUMERICHOST;
13968500a17Sderaadt 
14068500a17Sderaadt 	h1[0] = '\0';
14168500a17Sderaadt 	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, niflags) != 0)
14268500a17Sderaadt 		return (0);
14368500a17Sderaadt 
14468500a17Sderaadt 	/* Resolve laddr into sockaddr */
14568500a17Sderaadt 	memset(&hints, 0, sizeof(hints));
14668500a17Sderaadt 	hints.ai_family = raddr->sa_family;
14768500a17Sderaadt 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
14868500a17Sderaadt 	res = NULL;
14968500a17Sderaadt 	error = getaddrinfo(lhost, "0", &hints, &res);
15068500a17Sderaadt 	if (error)
15168500a17Sderaadt 		return (0);
15268500a17Sderaadt 
15368500a17Sderaadt 	/*
15468500a17Sderaadt 	 * Try string comparisons between raddr and laddr.
15568500a17Sderaadt 	 */
15668500a17Sderaadt 	for (r = res; r; r = r->ai_next) {
15768500a17Sderaadt 		h2[0] = '\0';
15868500a17Sderaadt 		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
15968500a17Sderaadt 		    NULL, 0, niflags) != 0)
16068500a17Sderaadt 			continue;
16168500a17Sderaadt 		if (strcasecmp(h1, h2) == 0) {
16268500a17Sderaadt 			freeaddrinfo(res);
16368500a17Sderaadt 			return (1);
16468500a17Sderaadt 		}
16568500a17Sderaadt 	}
16668500a17Sderaadt 
16768500a17Sderaadt 	/* No match. */
16868500a17Sderaadt 	freeaddrinfo(res);
16968500a17Sderaadt 	return (0);
17068500a17Sderaadt }
17168500a17Sderaadt 
17268500a17Sderaadt /*
17368500a17Sderaadt  * Return the hostname associated with the supplied address.
17468500a17Sderaadt  * Do a reverse lookup as well for security. If a loop cannot
17568500a17Sderaadt  * be found, pack the result of inet_ntoa() into the string.
17668500a17Sderaadt  */
17768500a17Sderaadt static char *
gethostloop(struct sockaddr * raddr,socklen_t salen)17868500a17Sderaadt gethostloop(struct sockaddr *raddr, socklen_t salen)
17968500a17Sderaadt {
18068500a17Sderaadt 	static char remotehost[NI_MAXHOST];
18168500a17Sderaadt 	char h1[NI_MAXHOST], h2[NI_MAXHOST];
18268500a17Sderaadt 	struct addrinfo hints, *res, *r;
18368500a17Sderaadt 	int error;
18468500a17Sderaadt 	const int niflags = NI_NUMERICHOST;
18568500a17Sderaadt 
18668500a17Sderaadt 	h1[0] = remotehost[0] = '\0';
18768500a17Sderaadt 	if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost),
18868500a17Sderaadt 	    NULL, 0, NI_NAMEREQD) != 0)
18968500a17Sderaadt 		return (NULL);
19068500a17Sderaadt 	if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, niflags) != 0)
19168500a17Sderaadt 		return (NULL);
19268500a17Sderaadt 
19368500a17Sderaadt 	/*
19468500a17Sderaadt 	 * Look up the name and check that the supplied
19568500a17Sderaadt 	 * address is in the list
19668500a17Sderaadt 	 */
19768500a17Sderaadt 	memset(&hints, 0, sizeof(hints));
19868500a17Sderaadt 	hints.ai_family = raddr->sa_family;
19968500a17Sderaadt 	hints.ai_socktype = SOCK_DGRAM;	/*dummy*/
20068500a17Sderaadt 	hints.ai_flags = AI_CANONNAME;
20168500a17Sderaadt 	res = NULL;
20268500a17Sderaadt 	error = getaddrinfo(remotehost, "0", &hints, &res);
20368500a17Sderaadt 	if (error)
20468500a17Sderaadt 		return (NULL);
20568500a17Sderaadt 
20668500a17Sderaadt 	for (r = res; r; r = r->ai_next) {
20768500a17Sderaadt 		h2[0] = '\0';
20868500a17Sderaadt 		if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2),
20968500a17Sderaadt 		    NULL, 0, niflags) != 0)
21068500a17Sderaadt 			continue;
21168500a17Sderaadt 		if (strcasecmp(h1, h2) == 0) {
21268500a17Sderaadt 			freeaddrinfo(res);
21368500a17Sderaadt 			return (remotehost);
21468500a17Sderaadt 		}
21568500a17Sderaadt 	}
21668500a17Sderaadt 
21768500a17Sderaadt 	/*
218*3a50f0a9Sjmc 	 * Either the DNS administrator has made a configuration
21968500a17Sderaadt 	 * mistake, or someone has attempted to spoof us.
22068500a17Sderaadt 	 */
22168500a17Sderaadt 	syslog(LOG_NOTICE, "lpd: address %s not listed for host %s",
22268500a17Sderaadt 	    h1, res->ai_canonname ? res->ai_canonname : remotehost);
22368500a17Sderaadt 	freeaddrinfo(res);
22468500a17Sderaadt 	return (NULL);
22568500a17Sderaadt }
22668500a17Sderaadt 
22768500a17Sderaadt #ifdef DEBUG
22868500a17Sderaadt int
main(int argc,char * argv[])22968500a17Sderaadt main(int argc, char *argv[])
23068500a17Sderaadt {
23168500a17Sderaadt 	struct addrinfo hints;
23268500a17Sderaadt 	int i;
23368500a17Sderaadt 
23468500a17Sderaadt 	memset(&hints, 0, sizeof(hints));
23568500a17Sderaadt 	hints.ai_family = PF_UNSPEC;
23668500a17Sderaadt 	hints.ai_socktype = SOCK_STREAM;
23768500a17Sderaadt 
23868500a17Sderaadt 	for (i = 1; i < argc; i++) {
23968500a17Sderaadt 		struct addrinfo *res0;
24068500a17Sderaadt 		int error = getaddrinfo(argv[i], NULL, &hints, &res0);
24168500a17Sderaadt 		if (error) {
24268500a17Sderaadt 			printf("%s: %s\n", argv[i], gai_strerror(error));
24368500a17Sderaadt 			continue;
24468500a17Sderaadt 		}
24568500a17Sderaadt 		error = allowedhost(stdin, res0->ai_addr, res0->ai_addrlen);
24668500a17Sderaadt 		printf("%s: %s\n", argv[i], error ? "denied" : "allowed");
24768500a17Sderaadt 	}
24868500a17Sderaadt 	exit(0);
24968500a17Sderaadt }
25068500a17Sderaadt #endif /* DEBUG */
251