Home | History | Annotate | Download | only in in.ftpd
      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 (at) 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 }
    325