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