1 /* 2 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 3 * Use is subject to license terms. 4 */ 5 6 #pragma ident "%Z%%M% %I% %E% SMI" 7 8 /**************************************************************************** 9 10 Copyright (c) 1999,2000 WU-FTPD Development Group. 11 All rights reserved. 12 13 Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994 14 The Regents of the University of California. 15 Portions Copyright (c) 1993, 1994 Washington University in Saint Louis. 16 Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc. 17 Portions Copyright (c) 1989 Massachusetts Institute of Technology. 18 Portions Copyright (c) 1998 Sendmail, Inc. 19 Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P. Allman. 20 Portions Copyright (c) 1997 by Stan Barber. 21 Portions Copyright (c) 1997 by Kent Landfield. 22 Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997 23 Free Software Foundation, Inc. 24 25 Use and distribution of this software and its source code are governed 26 by the terms and conditions of the WU-FTPD Software License ("LICENSE"). 27 28 If you did not receive a copy of the license, it may be obtained online 29 at http://www.wu-ftpd.org/license.html. 30 31 $Id: domain.c,v 1.11 2000/07/01 18:17:38 wuftpd Exp $ 32 33 ****************************************************************************/ 34 /* 35 * domain.c - Name and address lookup and checking functions 36 * 37 * INITIAL AUTHOR - * Nikos Mouat <nikm@cyberflunk.com> 38 */ 39 40 #include "config.h" 41 #include <sys/types.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #ifdef HAVE_SYS_SYSLOG_H 46 #include <sys/syslog.h> 47 #endif 48 #if defined(HAVE_SYSLOG_H) || (!defined(AUTOCONF) && !defined(HAVE_SYS_SYSLOG_H)) 49 #include <syslog.h> 50 #endif 51 #include <netdb.h> 52 #include <sys/socket.h> 53 #include <netinet/in.h> 54 #include "extensions.h" 55 #include "proto.h" 56 57 /* these should go in a new ftpd.h perhaps? config.h doesn't seem appropriate */ 58 /* and there does not appear to be a global include file */ 59 #ifndef TRUE 60 #define TRUE 1 61 #endif 62 63 #ifndef FALSE 64 #define FALSE !TRUE 65 #endif 66 67 /**************************************************************************** 68 * check_name_for_ip() 69 * This routine checks if the IP address in remote_socket is a valid IP 70 * address for name. 71 ***************************************************************************/ 72 static int check_name_for_ip(char *name, struct SOCKSTORAGE *remote_socket) 73 { 74 #ifdef INET6 75 int family; 76 size_t sockaddrlen, addrlen; 77 char *raddr, *addr; 78 struct addrinfo hints, *result, *ai; 79 80 family = SOCK_FAMILY(*remote_socket); 81 raddr = SOCK_ADDR(*remote_socket); 82 if ((family == AF_INET6) && 83 IN6_IS_ADDR_V4MAPPED((struct in6_addr *)raddr)) { 84 family = AF_INET; 85 /* move to the IPv4 part of an IPv4-mapped IPv6 address */ 86 raddr += 12; 87 } 88 89 if (family == AF_INET6) { 90 sockaddrlen = sizeof(struct sockaddr_in6); 91 addrlen = sizeof(struct in6_addr); 92 } 93 else { 94 sockaddrlen = sizeof(struct sockaddr_in); 95 addrlen = sizeof(struct in_addr); 96 } 97 98 memset(&hints, 0, sizeof(hints)); 99 hints.ai_family = family; 100 101 if (getaddrinfo(name, NULL, &hints, &result) == 0) { 102 for (ai = result; ai != NULL; ai = ai->ai_next) { 103 if ((family == ai->ai_family) && (sockaddrlen == ai->ai_addrlen)) { 104 if (family == AF_INET6) 105 addr = (void *)&((struct sockaddr_in6 *)(ai->ai_addr))->sin6_addr; 106 else 107 addr = (void *)&((struct sockaddr_in *)(ai->ai_addr))->sin_addr; 108 if (memcmp(addr, raddr, addrlen) == 0) { 109 freeaddrinfo(result); 110 return TRUE; 111 } 112 } 113 } 114 freeaddrinfo(result); 115 } 116 #else 117 char **addrl; 118 struct hostent *hp; 119 120 if ((hp = gethostbyname(name)) != NULL) { 121 for (addrl = hp->h_addr_list; addrl != NULL; addrl++) { 122 if (memcmp(&remote_socket->sin_addr, *addrl, 123 sizeof(struct in_addr)) == 0) 124 return TRUE; 125 } 126 } 127 #endif /* INET6 */ 128 129 /* no matching IP's */ 130 return FALSE; 131 } 132 133 /**************************************************************************** 134 * lookup() 135 * This routine returns the result of the lookup specified by dnsarg, 136 * which is either "refuse_no_reverse" or "refuse_mismatch", using the 137 * remote host's IP address. 138 ***************************************************************************/ 139 static int lookup(char *dnsarg) 140 { 141 static int rhost_matches = FALSE; 142 static int rhost_matches_set = FALSE; 143 extern struct SOCKSTORAGE his_addr; 144 extern int rhlookup, nameserved; 145 extern char remotehost[]; 146 147 /* skip lookups when not looking up the remote host's name */ 148 if (!rhlookup) 149 return FALSE; 150 151 if (strcasecmp(dnsarg, "refuse_no_reverse") == 0) 152 return nameserved; 153 154 /* refuse_mismatch */ 155 if (!rhost_matches_set) { 156 if (nameserved) { 157 /* 158 * We have the hostname based on the real IP address. Lookup 159 * the hostname to make sure the real IP address is listed as 160 * a valid address for the hostname. 161 */ 162 rhost_matches = check_name_for_ip(remotehost, &his_addr); 163 } 164 else 165 rhost_matches = TRUE; /* no reverse, nothing to match */ 166 rhost_matches_set = TRUE; 167 } 168 return rhost_matches; 169 } 170 171 /**************************************************************************** 172 * dns_check() 173 * This routine returns FALSE if the operation specified by dnsarg is 174 * FALSE and "override" wasn't specified, otherwise it returns TRUE. 175 ***************************************************************************/ 176 static int dns_check(char *dnsarg) 177 { 178 struct aclmember *entry = NULL; 179 int rc = TRUE; 180 181 /* check the config to see if we care */ 182 /* dns refuse_mismatch|refuse_no_reverse <filename> [override] */ 183 while (getaclentry("dns", &entry)) { 184 if (!ARG0 || !ARG1) 185 continue; 186 if (!strcasecmp(ARG0, dnsarg)) { 187 FILE *msg_file; 188 char linebuf[MAXPATHLEN]; 189 char outbuf[MAXPATHLEN]; 190 int code = 530; 191 char *crptr; 192 193 /* lookups can be slow, so only call now result is needed */ 194 if (!lookup(dnsarg)) { 195 /* ok, so we need to kick out this user */ 196 197 /* check to see if admin wants to override */ 198 if (ARG2 && (!strcasecmp(ARG2, "override"))) { 199 /* Administrative override - but display warning anyway */ 200 code = 220; 201 } 202 203 msg_file = fopen(ARG1, "r"); 204 if (msg_file != NULL) { 205 while (fgets(linebuf, sizeof(linebuf), msg_file)) { 206 if ((crptr = strchr(linebuf, '\n')) != NULL) 207 *crptr = '\0'; 208 msg_massage(linebuf, outbuf, sizeof(outbuf)); 209 lreply(code, "%s", outbuf); 210 } 211 fclose(msg_file); 212 #ifndef NO_SUCKING_NEWLINES 213 lreply(code, ""); 214 #endif 215 if (code == 530) { 216 reply(code, ""); 217 rc = FALSE; 218 } 219 else { 220 lreply(code, "Administrative Override. Permission granted."); 221 lreply(code, ""); 222 } 223 } 224 } 225 } 226 } 227 return rc; 228 } 229 230 /**************************************************************************** 231 * check_rhost_reverse() 232 * This routine returns FALSE if the remote host's IP address has no 233 * associated name and access should be refused, otherwise it returns TRUE. 234 ***************************************************************************/ 235 int check_rhost_reverse(void) 236 { 237 return dns_check("refuse_no_reverse"); 238 } 239 240 /**************************************************************************** 241 * check_rhost_matches() 242 * This routine returns FALSE if the remote host's IP address isn't listed 243 * as a valid IP address for the remote hostname and access should be 244 * refused, otherwise it returns TRUE. 245 ***************************************************************************/ 246 int check_rhost_matches(void) 247 { 248 return dns_check("refuse_mismatch"); 249 } 250 251 /**************************************************************************** 252 * rhostlookup() 253 * This routine returns TRUE if the remote host's name of a connection 254 * from remoteaddr should be looked up, otherwise it returns FALSE. 255 ***************************************************************************/ 256 int rhostlookup(char *remoteaddr) 257 { 258 int found, lookup, set, which; 259 struct aclmember *entry = NULL; 260 261 /* default is to lookup the remote host's name */ 262 lookup = TRUE; 263 found = FALSE; 264 265 /* rhostlookup yes|no [<addrglob> ...] */ 266 while (!found && getaclentry("rhostlookup", &entry)) { 267 if (!ARG0) 268 continue; 269 if (strcasecmp(ARG0, "yes") == 0) 270 set = TRUE; 271 else if (strcasecmp(ARG0, "no") == 0) 272 set = FALSE; 273 else 274 continue; 275 276 if (!ARG1) 277 lookup = set; 278 else { 279 for (which = 1; (which < MAXARGS) && ARG[which]; which++) { 280 if (hostmatch(ARG[which], remoteaddr, NULL)) { 281 lookup = set; 282 found = TRUE; 283 break; 284 } 285 } 286 } 287 } 288 return lookup; 289 } 290 291 /**************************************************************************** 292 * set_res_options() 293 * set resolver options by setting the RES_OPTIONS environment variable. 294 * Note: name and address lookups are no longer done using DNS directly, 295 * so setting resolver options may have no effect. 296 ***************************************************************************/ 297 void set_res_options(void) 298 { 299 int which; 300 struct aclmember *entry = NULL; 301 static char envbuf[BUFSIZ]; 302 303 envbuf[0] = '\0'; 304 305 /* dns resolveroptions [options] */ 306 while (getaclentry("dns", &entry)) { 307 if (!ARG0 || !ARG1) 308 continue; 309 /* there are other DNS options, we only care about 'resolveroptions' */ 310 if (strcasecmp(ARG0, "resolveroptions") == 0) { 311 for (which = 1; (which < MAXARGS) && ARG[which]; which++) { 312 if (envbuf[0] == '\0') 313 (void) strlcpy(envbuf, "RES_OPTIONS=", sizeof(envbuf)); 314 else 315 (void) strlcat(envbuf, " ", sizeof(envbuf)); 316 (void) strlcat(envbuf, ARG[which], sizeof(envbuf)); 317 } 318 } 319 } 320 if (envbuf[0] != '\0') { 321 if (putenv(envbuf) != 0) 322 syslog(LOG_WARNING, "putenv(\"%s\") failed", envbuf); 323 } 324 }