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