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