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  ***************************************************************************/
  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 }