xref: /onnv-gate/usr/src/cmd/cmd-inet/usr.sbin/in.ftpd/domain.c (revision 0:68f95e015346)
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