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