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
51static char linbuf[MAXLEN];	/* Buffer to hold one line of config-file  */
52static char unibuf[MAXLEN];	/* Buffer to hold unified line             */
53static hacc_t *ha_arr;		/* Array with host access information      */
54
55static FILE *ptFp;		/* FILE * into host access config file     */
56static int iHaInd = 0;		/* Index in ha_arr                         */
57static int iHaSize;		/* Will hold actual #elems in ha_arr       */
58static 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
68int 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
148static 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
285static 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
310static 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
343static void fatalmsg(char *pcMsg)
344{
345    syslog(LOG_INFO, "host_access: %s", pcMsg);
346}
347
348static 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 */
360