This should allow us to stop exporting __ivaliduser and __ivaliduser_sa from libc and to eventually remove ruserok from libc entirely.
Needs testing by someone who actually runs lpd... - todd Index: usr.sbin/lpr/lpd/Makefile =================================================================== RCS file: /cvs/src/usr.sbin/lpr/lpd/Makefile,v retrieving revision 1.5 diff -u -p -u -r1.5 Makefile --- usr.sbin/lpr/lpd/Makefile 10 Dec 2013 16:38:04 -0000 1.5 +++ usr.sbin/lpr/lpd/Makefile 13 Sep 2015 15:21:13 -0000 @@ -5,7 +5,7 @@ PROG= lpd CFLAGS+=-I${.CURDIR}/../common_source MAN= lpd.8 SRCS= lpd.c printjob.c recvjob.c displayq.c rmjob.c startdaemon.c \ - lpdchar.c common.c key.c modes.c common_vars.c + lpdchar.c common.c key.c modes.c common_vars.c allowedhost.c BINGRP= daemon BINMODE=2550 .PATH: ${.CURDIR}/../common_source Index: usr.sbin/lpr/lpd/allowedhost.c =================================================================== RCS file: usr.sbin/lpr/lpd/allowedhost.c diff -N usr.sbin/lpr/lpd/allowedhost.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ usr.sbin/lpr/lpd/allowedhost.c 13 Sep 2015 15:21:32 -0000 @@ -0,0 +1,249 @@ +/* + * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved. + * Copyright (c) 1983, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/socket.h> +#include <sys/stat.h> + +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <ctype.h> +#include <limits.h> +#include <netdb.h> +#include <netgroup.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> + +int allowedhost(FILE *, struct sockaddr *, socklen_t); +static int checkhost(struct sockaddr *, socklen_t, const char *); +static char *gethostloop(struct sockaddr *, socklen_t); + +/* + * Check whether the specified addr is listed in hostf. + * Returns 0 if ok, -1 if not ok. + */ +int +allowedhost(FILE *hostf, struct sockaddr *raddr, socklen_t salen) +{ + char *buf, *cp, *ep; + const char *ahost; + int hostok; + char *rhost = (char *)-1; + char domain[HOST_NAME_MAX+1]; + size_t buflen; + + getdomainname(domain, sizeof(domain)); + + while ((buf = fgetln(hostf, &buflen))) { + cp = buf; + ep = buf + buflen; + if (*cp == '#') + continue; + while (cp < ep && *cp != '\n' && *cp != ' ' && *cp != '\t') { + if (!isprint((unsigned char)*cp)) + goto bail; + *cp = isupper((unsigned char)*cp) ? + tolower((unsigned char)*cp) : *cp; + cp++; + } + while (cp < ep && (*cp == '\n' || *cp == ' ' || *cp == '\t')) + *cp++ = '\0'; + if (cp > ep) + continue; + if (cp == buf || cp[-1] != '\0') + continue; + + ahost = buf; + if (strlen(ahost) > HOST_NAME_MAX) + continue; + + /* + * innetgr() must lookup a hostname (we do not attempt + * to change the semantics so that netgroups may have + * #.#.#.# addresses in the list.) + */ + if (ahost[0] == '+') + switch (ahost[1]) { + case '\0': + hostok = 1; + break; + case '@': + if (rhost == (char *)-1) + rhost = gethostloop(raddr, salen); + hostok = 0; + if (rhost) + hostok = innetgr(&ahost[2], rhost, + NULL, domain); + break; + default: + hostok = checkhost(raddr, salen, &ahost[1]); + break; + } + else if (ahost[0] == '-') + switch (ahost[1]) { + case '\0': + hostok = -1; + break; + case '@': + if (rhost == (char *)-1) + rhost = gethostloop(raddr, salen); + hostok = 0; + if (rhost) + hostok = -innetgr(&ahost[2], rhost, + NULL, domain); + break; + default: + hostok = -checkhost(raddr, salen, &ahost[1]); + break; + } + else + hostok = checkhost(raddr, salen, ahost); + + + /* Check if one component did not match */ + if (hostok == 0) + continue; + + /* Check if we got a forbidden pair */ + if (hostok <= -1) + return (-1); + + /* Check if we got a valid pair */ + if (hostok >= 1) + return (0); + } +bail: + return (-1); +} + +/* + * Returns "true" if match, 0 if no match. If we do not find any + * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work. + */ +static int +checkhost(struct sockaddr *raddr, socklen_t salen, const char *lhost) +{ + struct addrinfo hints, *res, *r; + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + int error; + const int niflags = NI_NUMERICHOST; + + h1[0] = '\0'; + if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, + niflags) != 0) + return (0); + + /* Resolve laddr into sockaddr */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = raddr->sa_family; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + res = NULL; + error = getaddrinfo(lhost, "0", &hints, &res); + if (error) + return (0); + + /* + * Try string comparisons between raddr and laddr. + */ + for (r = res; r; r = r->ai_next) { + h2[0] = '\0'; + if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), + NULL, 0, niflags) != 0) + continue; + if (strcmp(h1, h2) == 0) { + freeaddrinfo(res); + return (1); + } + } + + /* No match. */ + freeaddrinfo(res); + return (0); +} + +/* + * Return the hostname associated with the supplied address. + * Do a reverse lookup as well for security. If a loop cannot + * be found, pack the result of inet_ntoa() into the string. + */ +static char * +gethostloop(struct sockaddr *raddr, socklen_t salen) +{ + static char remotehost[NI_MAXHOST]; + char h1[NI_MAXHOST], h2[NI_MAXHOST]; + struct addrinfo hints, *res, *r; + int error; + const int niflags = NI_NUMERICHOST; + + h1[0] = remotehost[0] = '\0'; + if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost), + NULL, 0, NI_NAMEREQD) != 0) + return (NULL); + if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, + niflags) != 0) + return (NULL); + + /* + * Look up the name and check that the supplied + * address is in the list + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_family = raddr->sa_family; + hints.ai_socktype = SOCK_DGRAM; /*dummy*/ + hints.ai_flags = AI_CANONNAME; + res = NULL; + error = getaddrinfo(remotehost, "0", &hints, &res); + if (error) + return (NULL); + + for (r = res; r; r = r->ai_next) { + h2[0] = '\0'; + if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), + NULL, 0, niflags) != 0) + continue; + if (strcmp(h1, h2) == 0) { + freeaddrinfo(res); + return (remotehost); + } + } + + /* + * either the DNS adminstrator has made a configuration + * mistake, or someone has attempted to spoof us + */ + syslog(LOG_NOTICE, "lpd: address %s not listed for host %s", + h1, res->ai_canonname ? res->ai_canonname : remotehost); + freeaddrinfo(res); + return (NULL); +} Index: usr.sbin/lpr/lpd/lpd.c =================================================================== RCS file: /cvs/src/usr.sbin/lpr/lpd/lpd.c,v retrieving revision 1.58 diff -u -p -u -r1.58 lpd.c --- usr.sbin/lpr/lpd/lpd.c 9 Feb 2015 23:00:14 -0000 1.58 +++ usr.sbin/lpr/lpd/lpd.c 13 Sep 2015 15:21:03 -0000 @@ -111,8 +111,7 @@ static int ckqueue(char *); static __dead void usage(void); static int *socksetup(int, int, const char *); -extern int __ivaliduser_sa(FILE *, struct sockaddr *, socklen_t, - const char *, const char *); +extern int allowedhost(FILE *, struct sockaddr *, socklen_t); /* unused, needed for lpc */ volatile sig_atomic_t gotintr; @@ -650,8 +649,6 @@ ckqueue(char *cap) return (0); } -#define DUMMY ":nobody::" - /* * Check to see if the from host has access to the line printer. */ @@ -715,7 +712,7 @@ chkhost(struct sockaddr *f) hostf = fopen(_PATH_HOSTSLPD, "r"); PRIV_END; if (hostf) { - if (__ivaliduser_sa(hostf, f, f->sa_len, DUMMY, DUMMY) == 0) { + if (allowedhost(hostf, f, f->sa_len) == 0) { (void)fclose(hostf); return; }