/* * @(#) hostacc.c - Implementation of host access for the * experimental FTP daemon developed at * Washington University. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * AUTHOR * Bart Muijzer * * HISTORY * 930316 BM Created * 930317 BM Converted to local naming convention; * added rhost_ok(), cleanup code in enghacc() * 930318 BM Ported to BSD; fixed memory leaks * 930322 BM Changed algorithm: not in configfile = allow * in configfile and match = allow|deny * in configfile and no match = deny */ #include "config.h" #ifdef HOST_ACCESS #include "hostacc.h" static char linbuf[MAXLEN]; /* Buffer to hold one line of config-file */ static char unibuf[MAXLEN]; /* Buffer to hold unified line */ static hacc_t ha_arr[MAXLIN]; /* Array with host access information */ static FILE *ptFp; /* FILE * into host access config file */ static int iHaInd = 0; /* Index in ha_arr */ static int iHaSize; /* Will hold actual #elems in ha_arr */ static int iFirstTim = 1; /* Used by gethacc() to see if index in */ /* ha_arr needs to be reset */ /* ------------------------------------------------------------------------ *\ * FUNCTION : rhost_ok * * PURPOSE : Check if a host is allowed to make a connection * * ARGUMENTS : Remote user name, remote host name, remote host address * * RETURNS : 1 if host is granted access, 0 if not * \* ------------------------------------------------------------------------ */ int rhost_ok(pcRuser, pcRhost, pcRaddr) char *pcRuser, *pcRhost, *pcRaddr; { hacc_t *ptHtmp; char *pcHost; int iInd, iLineMatch = 0, iUserSeen = 0; int m; switch(sethacc()){ case 1: /* no hostaccess file; disable mechanism */ return(1); break; case -1: syslog(LOG_INFO, "rhost_ok: sethacc failed"); return(0); break; default: break; } while (((ptHtmp = gethacc()) != (hacc_t *)NULL) && !iLineMatch) { if ( (strcasecmp(pcRuser, ptHtmp->ha_login)) && strcmp(ptHtmp->ha_login, "*") != NULL) /* wrong user, check rest of file */ continue; /* * We have seen a line regarding the current user. * Remember this. */ iUserSeen = 1; for(iInd=0, pcHost=ptHtmp->ha_hosts[0]; ((pcHost != NULL) && !iLineMatch); pcHost=ptHtmp->ha_hosts[++iInd]) { if (isdigit(*pcHost)) { iLineMatch = fnmatch(pcHost, pcRaddr, NULL); } else { iLineMatch = fnmatch(pcHost, pcRhost, NULL); } if (iLineMatch) { iLineMatch = (ptHtmp->ha_type == ALLOW) ? 1 : 0; goto match; } } } match: /* * At this point, iUserSeen == 1 if we've seen lines regarding * the current user, and 0 otherwise. If we reached the end of * the config file without a match we allow. Else, we allow or * deny according to the rule found. */ if (endhacc()) { syslog(LOG_INFO, "rhost_ok: endhacc failed"); return(0); } if (iUserSeen) return(ptHtmp == NULL) ? 0 : iLineMatch; else /* Nothing at all about user in configfile, allow */ return(1); } /* ------------------------------------------------------------------------ *\ * FUNCTION : sethacc * * PURPOSE : Initialize data structures for host access * * ARGUMENTS : None * * RETURNS : -1 on failure, 1 if host access file doesn't exist, * * 0 otherwise * \* ------------------------------------------------------------------------ */ static int sethacc() { int iHaHind = 0; /* Index in list of hosts */ char *pcBegin, *pcEnd, *pcColon; char *pcTmp1, *pcTmp2; /* Open config file */ if ((ptFp = fopen(_PATH_FTPHOSTS, "r")) == NULL) { if (errno == ENOENT) return(1); else { fatal("Can't open host access file"); return (-1); } } while (fgets(linbuf, MAXLEN, ptFp) != NULL) { iHaHind = 0; /* Find first non-whitespace character */ for (pcBegin=linbuf; ((*pcBegin == '\t') || (*pcBegin == ' ')); pcBegin++) ; /* Get rid of comments */ if ((pcEnd = strchr(linbuf, '#')) != NULL) *pcEnd = '\0'; /* Skip empty lines */ if ((pcBegin == pcEnd) || (*pcBegin == '\n')) continue; /* Substitute all whitespace by a single ":" so we can * easily break on words later on. The easiest way is * to copy the result into a temporary buffer (called * the "unified buffer" because it will store a line in * the same format, regardless of the format the original * line was in). * The result will look like: "allow:name:host:host:host" */ for (pcTmp1=pcBegin, pcTmp2=unibuf; *pcTmp1; pcTmp1++) { if (*pcTmp1 != '\t' && *pcTmp1 != ' ' && *pcTmp1 != '\n') *pcTmp2++ = *pcTmp1; else /* whitespace */ if (*(pcTmp2-1) == ':') continue; else *pcTmp2++ = ':'; } /* Throw away trailing whitespace, now indicated by * the last character of the unified buffer being a * colon. Remember where the news string ends. */ pcEnd = (*(pcTmp2 - 1) == ':') ? (pcTmp2 - 1) : pcTmp2; *pcEnd = '\0'; /* Terminate new string */ /* Store what's left of the line into the * hacc_t structure. First the access type, * then the loginname, and finally a list of * hosts to which all this applies. */ pcBegin = unibuf; if (!strncmp(pcBegin, "deny", 4)) { ha_arr[iHaInd].ha_type = DENY; pcBegin += 5; } else if (!strncmp(pcBegin, "allow", 5)) { ha_arr[iHaInd].ha_type = ALLOW; pcBegin += 6; } else { fatal("Format error in host access file"); return(-1); } if((pcColon = strchr(pcBegin, ':')) != NULL) ha_arr[iHaInd].ha_login = strnsav(pcBegin, (pcColon-pcBegin)); else { fatal("Format error in host access file"); return(-1); } pcBegin = pcColon+1; while ((pcColon = strchr(pcBegin, ':')) != NULL) { ha_arr[iHaInd].ha_hosts[iHaHind++] = strnsav(pcBegin, (pcColon-pcBegin)); pcBegin = pcColon+1; if (iHaHind >= MAXHST) { fatal("Line too long"); return(-1); } } ha_arr[iHaInd].ha_hosts[iHaHind++] = strnsav(pcBegin, (pcEnd-pcBegin)); ha_arr[iHaInd].ha_hosts[iHaHind] = NULL; /* * Check if we need to expand the array with * host access information */ if (++iHaInd > MAXLIN) { fatal("Config file too big!!"); return(-1); } } iHaSize = iHaInd; /* Record current size of ha_arr */ return ((feof(ptFp)) ? 0 : -1); } /* ------------------------------------------------------------------------ *\ * FUNCTION : gethacc * * PURPOSE : return pointer to the next host_access structure * * ARGUMENTS : None * * RETURNS : NULL on failure, pointervalue otherwise * \* ------------------------------------------------------------------------ */ static hacc_t *gethacc() { static int iHaInd; hacc_t *ptTmp; if ((ptTmp = (hacc_t *)malloc(sizeof(hacc_t))) == NULL) return(NULL); if (iFirstTim) { iFirstTim = 0; iHaInd = 0; } if (iHaInd >= iHaSize) return ((hacc_t *)NULL); else { #ifdef USG memmove(ptTmp, &(ha_arr[iHaInd]), sizeof(hacc_t)); #else bcopy(&(ha_arr[iHaInd]), ptTmp, sizeof(hacc_t)); #endif iHaInd++; return(ptTmp); } } /* ------------------------------------------------------------------------ *\ * FUNCTION : endhacc * * PURPOSE : Free allocated data structures for host access * * ARGUMENTS : None * * RETURNS : -1 on failure, 0 otherwise * \* ------------------------------------------------------------------------ */ static int endhacc() { int iInd; hacc_t *ptHtmp; for(iInd=0, ptHtmp = ha_arr; (ptHtmp->ha_hosts[iInd]); iInd++) { free(ptHtmp->ha_login); free(ptHtmp->ha_hosts[iInd]); } if (fclose(ptFp)) return (-1); return (0); } /* ------------------------------------------------------------------------ */ static void fatal(pcMsg) char *pcMsg; { syslog(LOG_INFO, "host_access: %s", pcMsg); } static char *strnsav(pcStr,iLen) char *pcStr; int iLen; { char *pcBuf; if ((pcBuf = (char *)malloc(iLen+1)) == NULL) return(NULL); strncpy(pcBuf,pcStr,iLen); pcBuf[iLen] = '\0'; return(pcBuf); } #endif /* HOST_ACCESS */