1 /* 2 * Copyright (c) 1995, 1996, 1998 Theo de Raadt. All rights reserved. 3 * Copyright (c) 1983, 1993, 1994 4 * The Regents of the University of California. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the University nor the names of its contributors 15 * may be used to endorse or promote products derived from this software 16 * without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31 #include <sys/socket.h> 32 #include <sys/types.h> 33 34 #include <netinet/in.h> 35 #include <arpa/inet.h> 36 37 #include <ctype.h> 38 #include <limits.h> 39 #include <netdb.h> 40 #include <netgroup.h> 41 #include <signal.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <syslog.h> 46 #include <unistd.h> 47 48 static int checkhost(struct sockaddr *, socklen_t, const char *); 49 static char *gethostloop(struct sockaddr *, socklen_t); 50 51 /* 52 * Check whether the specified addr is listed in hostf. 53 * Returns 0 if allowed, else -1. 54 */ 55 int 56 allowedhost(FILE *hostf, struct sockaddr *raddr, socklen_t salen) 57 { 58 char *cp, *ep, *line = NULL; 59 char *ahost, *rhost = (char *)-1; 60 char domain[HOST_NAME_MAX+1]; 61 size_t linesize = 0; 62 ssize_t linelen; 63 int hostok = 0; 64 65 getdomainname(domain, sizeof(domain)); 66 67 while ((linelen = getline(&line, &linesize, hostf)) != -1) { 68 cp = line; 69 ep = line + linelen; 70 if (*cp == '#') 71 continue; 72 while (cp < ep && !isspace((unsigned char)*cp)) { 73 if (!isprint((unsigned char)*cp)) 74 goto bail; 75 *cp = isupper((unsigned char)*cp) ? 76 tolower((unsigned char)*cp) : *cp; 77 cp++; 78 } 79 if (cp == line) 80 continue; 81 *cp = '\0'; 82 83 ahost = line; 84 if (strlen(ahost) > HOST_NAME_MAX) 85 continue; 86 87 /* 88 * innetgr() must lookup a hostname (we do not attempt 89 * to change the semantics so that netgroups may have 90 * #.#.#.# addresses in the list.) 91 */ 92 switch (ahost[0]) { 93 case '+': 94 case '-': 95 switch (ahost[1]) { 96 case '\0': 97 hostok = 1; 98 break; 99 case '@': 100 if (rhost == (char *)-1) 101 rhost = gethostloop(raddr, salen); 102 hostok = 0; 103 if (rhost) 104 hostok = innetgr(&ahost[2], rhost, 105 NULL, domain); 106 break; 107 default: 108 hostok = checkhost(raddr, salen, &ahost[1]); 109 break; 110 } 111 if (ahost[0] == '-') 112 hostok = -hostok; 113 break; 114 default: 115 hostok = checkhost(raddr, salen, ahost); 116 break; 117 } 118 119 /* Check if we got a match (positive or negative). */ 120 if (hostok != 0) 121 break; 122 } 123 bail: 124 free(line); 125 return (hostok > 0 ? 0 : -1); 126 } 127 128 /* 129 * Returns 1 if match, 0 if no match. If we do not find any 130 * semblance of an A->PTR->A loop, allow a simple #.#.#.# match to work. 131 */ 132 static int 133 checkhost(struct sockaddr *raddr, socklen_t salen, const char *lhost) 134 { 135 struct addrinfo hints, *res, *r; 136 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 137 int error; 138 const int niflags = NI_NUMERICHOST; 139 140 h1[0] = '\0'; 141 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, niflags) != 0) 142 return (0); 143 144 /* Resolve laddr into sockaddr */ 145 memset(&hints, 0, sizeof(hints)); 146 hints.ai_family = raddr->sa_family; 147 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 148 res = NULL; 149 error = getaddrinfo(lhost, "0", &hints, &res); 150 if (error) 151 return (0); 152 153 /* 154 * Try string comparisons between raddr and laddr. 155 */ 156 for (r = res; r; r = r->ai_next) { 157 h2[0] = '\0'; 158 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 159 NULL, 0, niflags) != 0) 160 continue; 161 if (strcasecmp(h1, h2) == 0) { 162 freeaddrinfo(res); 163 return (1); 164 } 165 } 166 167 /* No match. */ 168 freeaddrinfo(res); 169 return (0); 170 } 171 172 /* 173 * Return the hostname associated with the supplied address. 174 * Do a reverse lookup as well for security. If a loop cannot 175 * be found, pack the result of inet_ntoa() into the string. 176 */ 177 static char * 178 gethostloop(struct sockaddr *raddr, socklen_t salen) 179 { 180 static char remotehost[NI_MAXHOST]; 181 char h1[NI_MAXHOST], h2[NI_MAXHOST]; 182 struct addrinfo hints, *res, *r; 183 int error; 184 const int niflags = NI_NUMERICHOST; 185 186 h1[0] = remotehost[0] = '\0'; 187 if (getnameinfo(raddr, salen, remotehost, sizeof(remotehost), 188 NULL, 0, NI_NAMEREQD) != 0) 189 return (NULL); 190 if (getnameinfo(raddr, salen, h1, sizeof(h1), NULL, 0, niflags) != 0) 191 return (NULL); 192 193 /* 194 * Look up the name and check that the supplied 195 * address is in the list 196 */ 197 memset(&hints, 0, sizeof(hints)); 198 hints.ai_family = raddr->sa_family; 199 hints.ai_socktype = SOCK_DGRAM; /*dummy*/ 200 hints.ai_flags = AI_CANONNAME; 201 res = NULL; 202 error = getaddrinfo(remotehost, "0", &hints, &res); 203 if (error) 204 return (NULL); 205 206 for (r = res; r; r = r->ai_next) { 207 h2[0] = '\0'; 208 if (getnameinfo(r->ai_addr, r->ai_addrlen, h2, sizeof(h2), 209 NULL, 0, niflags) != 0) 210 continue; 211 if (strcasecmp(h1, h2) == 0) { 212 freeaddrinfo(res); 213 return (remotehost); 214 } 215 } 216 217 /* 218 * Either the DNS adminstrator has made a configuration 219 * mistake, or someone has attempted to spoof us. 220 */ 221 syslog(LOG_NOTICE, "lpd: address %s not listed for host %s", 222 h1, res->ai_canonname ? res->ai_canonname : remotehost); 223 freeaddrinfo(res); 224 return (NULL); 225 } 226 227 #ifdef DEBUG 228 int 229 main(int argc, char *argv[]) 230 { 231 struct addrinfo hints; 232 int i; 233 234 memset(&hints, 0, sizeof(hints)); 235 hints.ai_family = PF_UNSPEC; 236 hints.ai_socktype = SOCK_STREAM; 237 238 for (i = 1; i < argc; i++) { 239 struct addrinfo *res0; 240 int error = getaddrinfo(argv[i], NULL, &hints, &res0); 241 if (error) { 242 printf("%s: %s\n", argv[i], gai_strerror(error)); 243 continue; 244 } 245 error = allowedhost(stdin, res0->ai_addr, res0->ai_addrlen); 246 printf("%s: %s\n", argv[i], error ? "denied" : "allowed"); 247 } 248 exit(0); 249 } 250 #endif /* DEBUG */ 251