1 #pragma ident   "%Z%%M% %I%     %E% SMI"
   2 
   3 /****************************************************************************    
   4   Copyright (c) 1999,2000 WU-FTPD Development Group.  
   5   All rights reserved.
   6   
   7   Portions Copyright (c) 1980, 1985, 1988, 1989, 1990, 1991, 1993, 1994 
   8     The Regents of the University of California.
   9   Portions Copyright (c) 1993, 1994 Washington University in Saint Louis. 
  10   Portions Copyright (c) 1996, 1998 Berkeley Software Design, Inc. 
  11   Portions Copyright (c) 1989 Massachusetts Institute of Technology. 
  12   Portions Copyright (c) 1998 Sendmail, Inc. 
  13   Portions Copyright (c) 1983, 1995, 1996, 1997 Eric P.  Allman. 
  14   Portions Copyright (c) 1997 by Stan Barber. 
  15   Portions Copyright (c) 1997 by Kent Landfield. 
  16   Portions Copyright (c) 1991, 1992, 1993, 1994, 1995, 1996, 1997 
  17     Free Software Foundation, Inc.   
  18   
  19   Use and distribution of this software and its source code are governed  
  20   by the terms and conditions of the WU-FTPD Software License ("LICENSE"). 
  21   
  22   If you did not receive a copy of the license, it may be obtained online 
  23   at http://www.wu-ftpd.org/license.html. 
  24   
  25   $Id: hostacc.c,v 1.8 2000/07/01 18:17:39 wuftpd Exp $ 
  26   
  27 ****************************************************************************/
  28 /*
  29  *      hostacc.c  -  Implementation of host access for the
  30  *                    experimental FTP daemon developed at
  31  *                    Washington University.
  32  *
  33  * INITIAL AUTHOR  - Bart Muijzer    <bartm@cv.ruu.nl>
  34  *
  35  * HISTORY
  36  *      930316  BM      Created
  37  *      930317  BM      Converted to local naming convention;
  38  *                      added rhost_ok(), cleanup code in enghacc()
  39  *      930318  BM      Ported to BSD; fixed memory leaks
  40  *      930322  BM      Changed algorithm: not in configfile =  allow
  41  *                                         in configfile and match = allow|deny
  42  *                                         in configfile and no match = deny
  43  */
  44 #include "config.h"
  45 
  46 #ifdef  HOST_ACCESS
  47 
  48 #include "proto.h"
  49 #include "hostacc.h"
  50 
  51 static char linbuf[MAXLEN];     /* Buffer to hold one line of config-file  */
  52 static char unibuf[MAXLEN];     /* Buffer to hold unified line             */
  53 static hacc_t *ha_arr;          /* Array with host access information      */
  54 
  55 static FILE *ptFp;              /* FILE * into host access config file     */
  56 static int iHaInd = 0;          /* Index in ha_arr                         */
  57 static int iHaSize;             /* Will hold actual #elems in ha_arr       */
  58 static int iFirstTim = 1;       /* Used by gethacc() to see if index in    */
  59                                  /* ha_arr needs to be reset                */
  60 
  61 /* ------------------------------------------------------------------------ *\
  62  * FUNCTION  : rhost_ok                                                     *
  63  * PURPOSE   : Check if a host is allowed to make a connection              *
  64  * ARGUMENTS : Remote user name, remote host name, remote host address      *
  65  * RETURNS   : 1 if host is granted access, 0 if not                        *
  66  \* ------------------------------------------------------------------------ */
  67 
  68 int rhost_ok(char *pcRuser, char *pcRhost, char *pcRaddr)
  69 {
  70     hacc_t *ptHtmp;
  71     char *pcHost;
  72     char *ha_login;
  73     int iInd, iLineMatch = 0, iUserSeen = 0;
  74 
  75     switch (sethacc()) {
  76     case 1:
  77         /* no hostaccess file; disable mechanism */
  78         return (1);
  79         /* break; */
  80     case -1:
  81         syslog(LOG_INFO, "rhost_ok: sethacc failed");
  82         endhacc();
  83         return (0);
  84         /* break; */
  85     default:
  86         break;
  87     }
  88 
  89     /* user names "ftp" and "anonymous" are equivalent */
  90     if (!strcasecmp(pcRuser, "anonymous"))
  91         pcRuser = "ftp";
  92 
  93     while (((ptHtmp = gethacc()) != (hacc_t *) NULL) && !iLineMatch) {
  94         if (strcasecmp(ptHtmp->ha_login, "anonymous"))
  95             ha_login = ptHtmp->ha_login;
  96         else
  97             ha_login = "ftp";
  98 
  99         if ((strcasecmp(pcRuser, ha_login)) && strcmp(ha_login, "*"))
 100             /* wrong user, check rest of file */
 101             continue;
 102 
 103         /*
 104          * We have seen a line regarding the current user.
 105          * Remember this.
 106          */
 107         iUserSeen = 1;
 108 
 109         for (iInd = 0, pcHost = ptHtmp->ha_hosts[0];
 110              ((iInd < MAXHST) && (pcHost != NULL) && !iLineMatch);
 111              pcHost = ptHtmp->ha_hosts[++iInd]) {
 112             iLineMatch = hostmatch(pcHost, pcRaddr, pcRhost);
 113             if (iLineMatch) {
 114                 iLineMatch = (ptHtmp->ha_type == ALLOW) ? 1 : 0;
 115                 goto match;
 116             }
 117         }
 118     }
 119 
 120   match:
 121     /*
 122      * At this point, iUserSeen == 1 if we've seen lines regarding
 123      * the current user, and 0 otherwise. If we reached the end of
 124      * the config file without a match we allow. Else, we allow or
 125      * deny according to the rule found.
 126      */
 127 
 128     if (endhacc()) {
 129         syslog(LOG_INFO, "rhost_ok: endhacc failed");
 130         return (0);
 131     }
 132 
 133     if (iUserSeen)
 134         return (ptHtmp == NULL) ? 0 : iLineMatch;
 135     else
 136         /* Nothing at all about user in configfile, allow */
 137         return (1);
 138 }
 139 
 140 /* ------------------------------------------------------------------------ *\
 141  * FUNCTION  : sethacc                                                      *
 142  * PURPOSE   : Initialize data structures for host access                   *
 143  * ARGUMENTS : None                                                         *
 144  * RETURNS   : -1 on failure, 1 if host access file doesn't exist,          *
 145  *             0 otherwise                                                  *
 146  \* ------------------------------------------------------------------------ */
 147 
 148 static int sethacc(void)
 149 {
 150     int iHaHind = 0;            /* Index in list of hosts   */
 151     char *pcBegin, *pcEnd, *pcColon;
 152     char *pcTmp1, *pcTmp2;
 153     int iHaMalloc = 0;          /* how many elem malloced */
 154 
 155     iHaInd = 0;
 156     iFirstTim = 1;
 157     /* Open config file */
 158     if ((ptFp = fopen(_path_ftphosts, "r")) == NULL) {
 159         if (errno == ENOENT)
 160             return (1);
 161         else {
 162             fatalmsg("Can't open host access file");
 163             iHaSize = iHaInd;
 164             return (-1);
 165         }
 166     }
 167     ha_arr = (hacc_t *) malloc((iHaMalloc = 10) * sizeof(hacc_t));
 168     if (ha_arr == NULL) {
 169         syslog(LOG_ERR, "malloc error in sethacc");
 170         exit(0);
 171     }
 172 
 173     while (fgets(linbuf, MAXLEN, ptFp) != NULL) {
 174         iHaHind = 0;
 175 
 176         /* Find first non-whitespace character */
 177         for (pcBegin = linbuf;
 178              ((*pcBegin == '\t') || (*pcBegin == ' '));
 179              pcBegin++);
 180 
 181         /* Get rid of comments */
 182         if ((pcEnd = strchr(linbuf, '#')) != NULL)
 183             *pcEnd = '\0';
 184 
 185 
 186         /* Skip empty lines */
 187         if ((pcBegin == pcEnd) || (*pcBegin == '\n'))
 188             continue;
 189 
 190         /* Substitute all whitespace by a single ":" so we can
 191          * easily break on words later on. The easiest way is 
 192          * to copy the result into a temporary buffer (called
 193          * the "unified buffer" because it will store a line in 
 194          * the same format, regardless of the format the original
 195          * line was in).
 196          * The result will look like: "allow:name:host:host:host"
 197          */
 198         for (pcTmp1 = pcBegin, pcTmp2 = unibuf; *pcTmp1; pcTmp1++) {
 199             if (*pcTmp1 != '\t' && *pcTmp1 != ' ' && *pcTmp1 != '\n')
 200                 *pcTmp2++ = *pcTmp1;
 201             else
 202                 /* whitespace */
 203             if (*(pcTmp2 - 1) == ':')
 204                 continue;
 205             else
 206                 *pcTmp2++ = ':';
 207         }
 208 
 209         /* Throw away trailing whitespace, now indicated by
 210          * the last character of the unified buffer being a 
 211          * colon. Remember where the news string ends.
 212          */
 213         pcEnd = (*(pcTmp2 - 1) == ':') ? (pcTmp2 - 1) : pcTmp2;
 214         *pcEnd = '\0';          /* Terminate new string */
 215 
 216         /*
 217          * Check if we need to expand the array with
 218          * host access information
 219          */
 220         if (iHaInd >= iHaMalloc) {
 221             ha_arr = (hacc_t *) realloc(ha_arr, (iHaMalloc += 10) * sizeof(hacc_t));
 222             if (!ha_arr) {
 223                 fatalmsg("Failed to realloc host access array");
 224                 iHaSize = iHaInd;
 225                 return (-1);
 226             }
 227         }
 228 
 229         /* Store what's left of the line into the
 230          * hacc_t structure. First the access type,
 231          * then the loginname, and finally a list of
 232          * hosts to which all this applies.
 233          */
 234         pcBegin = unibuf;
 235         if (!strncmp(pcBegin, "deny", 4)) {
 236             ha_arr[iHaInd].ha_type = DENY;
 237             pcBegin += 5;
 238         }
 239         else if (!strncmp(pcBegin, "allow", 5)) {
 240             ha_arr[iHaInd].ha_type = ALLOW;
 241             pcBegin += 6;
 242         }
 243         else {
 244             fatalmsg("Format error in host access file");
 245             iHaSize = iHaInd;
 246             return (-1);
 247         }
 248 
 249         if ((pcColon = strchr(pcBegin, ':')) != NULL)
 250             ha_arr[iHaInd].ha_login =
 251                 strnsav(pcBegin, (pcColon - pcBegin));
 252         else {
 253             fatalmsg("Format error in host access file");
 254             iHaSize = iHaInd;
 255             return (-1);
 256         }
 257 
 258         pcBegin = pcColon + 1;
 259         while ((pcColon = strchr(pcBegin, ':')) != NULL) {
 260             ha_arr[iHaInd].ha_hosts[iHaHind++] =
 261                 strnsav(pcBegin, (pcColon - pcBegin));
 262             pcBegin = pcColon + 1;
 263             if (iHaHind >= MAXHST) {
 264                 fatalmsg("Line too long");
 265                 iHaSize = iHaInd;
 266                 return (-1);
 267             }
 268         }
 269         ha_arr[iHaInd].ha_hosts[iHaHind++] =
 270             strnsav(pcBegin, (pcEnd - pcBegin));
 271         ha_arr[iHaInd].ha_hosts[iHaHind] = NULL;
 272         iHaInd++;
 273     }
 274     iHaSize = iHaInd;           /* Record current size of ha_arr */
 275     return ((feof(ptFp)) ? 0 : -1);
 276 }
 277 
 278 /* ------------------------------------------------------------------------ *\
 279  * FUNCTION  : gethacc                                                      *
 280  * PURPOSE   : return pointer to the next host_access structure             *
 281  * ARGUMENTS : None                                                         *
 282  * RETURNS   : NULL on failure, pointervalue otherwise                      *
 283  \* ------------------------------------------------------------------------ */
 284 
 285 static hacc_t *gethacc(void)
 286 {
 287     static int iHaInd;
 288     static hacc_t ptTmp;
 289 
 290     if (iFirstTim) {
 291         iFirstTim = 0;
 292         iHaInd = 0;
 293     }
 294     if (iHaInd >= iHaSize)
 295         return ((hacc_t *) NULL);
 296     else {
 297         memmove(&ptTmp, &(ha_arr[iHaInd]), sizeof(hacc_t));
 298         iHaInd++;
 299         return (&ptTmp);
 300     }
 301 }
 302 
 303 /* ------------------------------------------------------------------------ *\
 304  * FUNCTION  : endhacc                                                      *
 305  * PURPOSE   : Free allocated data structures for host access               *
 306  * ARGUMENTS : None                                                         *
 307  * RETURNS   : -1 on failure, 0 otherwise                                   *
 308  \* ------------------------------------------------------------------------ */
 309 
 310 static int endhacc(void)
 311 {
 312     int iInd;
 313     hacc_t *ptHtmp;
 314 
 315     if (ha_arr == (hacc_t *) NULL)
 316         return (0);
 317 
 318     for (ptHtmp = ha_arr;
 319          ptHtmp < ha_arr + iHaSize && ptHtmp->ha_type;
 320          ptHtmp++) {
 321         ptHtmp->ha_type = 0;
 322         if (ptHtmp->ha_login) {
 323             free(ptHtmp->ha_login);
 324             ptHtmp->ha_login = NULL;
 325         }
 326         for (iInd = 0;
 327              iInd < MAXHST && ptHtmp->ha_hosts[iInd];
 328              iInd++) {
 329             free(ptHtmp->ha_hosts[iInd]);
 330             ptHtmp->ha_hosts[iInd] = NULL;
 331         }
 332     }
 333     free(ha_arr);
 334     ha_arr = NULL;
 335 
 336     if (ptFp && fclose(ptFp))
 337         return (-1);
 338     return (0);
 339 }
 340 
 341 /* ------------------------------------------------------------------------ */
 342 
 343 static void fatalmsg(char *pcMsg)
 344 {
 345     syslog(LOG_INFO, "host_access: %s", pcMsg);
 346 }
 347 
 348 static char *strnsav(char *pcStr, int iLen)
 349 {
 350     char *pcBuf;
 351 
 352     if ((pcBuf = (char *) malloc(iLen + 1)) == NULL)
 353         return (NULL);
 354     strncpy(pcBuf, pcStr, iLen);
 355     pcBuf[iLen] = '\0';
 356     return (pcBuf);
 357 }
 358 
 359 #endif /* HOST_ACCESS */