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 ***************************************************************************/
check_name_for_ip(char * name,struct SOCKSTORAGE * remote_socket)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 ***************************************************************************/
lookup(char * dnsarg)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 ***************************************************************************/
dns_check(char * dnsarg)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 ***************************************************************************/
check_rhost_reverse(void)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 ***************************************************************************/
check_rhost_matches(void)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 ***************************************************************************/
rhostlookup(char * remoteaddr)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 ***************************************************************************/
set_res_options(void)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 }
325