/* cfloader.c Configuration loader
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "ftpd.h"
#include "reply.h"
VSERVER serverdefaults =
{
NULL,
MAXUSERS,
LOGSTRENGTH,
TIMEOUT,
LOGFILE,
EMAIL,
LOGINTRIES,
BADAUTHWAIT,
0,
0,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL
};
char *gethostname2(void)
{
char *hostname;
int size = 32;
struct hostent *h;
hostname = mallocwrapper(size);
while(gethostname(hostname, size-1) == -1)
{
size *= 2;
reallocwrapper(size, (void **)&hostname);
}
h = gethostbyname(hostname);
freewrapper(hostname);
if (!h)
return(NULL);
hostname = strdupwrapper(h->h_name);
return(hostname);
}
/* generates a busy string from config file data */
char *makebusystring(char *input, int alt)
{
char *r, *n;
r = mallocwrapper(strlen(input) + ((strchrcount(input, '/') + 1) * 6)
+ strlen(REPLY_SERVERBUSY) + 2);
strcpy(r, input);
/* do / translation */
converttorealstr(r);
/* insert inital 421 */
memmove(r + 4, r, strlen(r) + 1);
memcpy(r, "421-", 4);
/* insert further 421 for each line */
n = r;
while((n = strchr(n, '\n')) != NULL)
{
int len = strlen(n);
memmove(n + 5, n, len + 1);
if(alt)
memcpy(n, "\r\n421-", 6);
else
memcpy(n, "\r\n ", 6);
n = n + 2;
}
/* insert server message */
strcpy(r + strlen(r), "\r\n");
strcpy(r + strlen(r), REPLY_SERVERBUSY);
return(r);
}
void configerror(char *str)
{
switch(logerrors)
{
case SYSLOG:
syslog(LOG_ERR, PROGNAME":%s", str);
break;
case TERMINAL:
fprintf(stderr, "CONFIG: %s\n", str);
break;
case MUDLOG:
log_addentry(MYLOG_INFO, NULL, str);
}
freewrapper(str);
}
void vserver_kill(VSERVER *c)
{
if (c->ipaccess)
ipacllist_destroy(c->ipaccess);
freeifnotnull(c->prelogindumpdata);
freeifnotnull(c->grouplist);
freeifnotnull(c->toobusy);
freewrapper(c);
}
VSERVER *vserver_load(CONFIGDATA *cf, char *sectionname, VSERVER *def, VSERVERCONN ***vp)
{
int section, occur;
char *setting;
VSERVER *newvs = mallocwrapper(sizeof(VSERVER));
VSERVER *dvs = def;
if (!dvs)
dvs = &serverdefaults;
newvs->grouplist = NULL;
newvs->ipaccess = NULL;
newvs->sectionname = sectionname;
section = getsectionid(cf->configfile, sectionname);
if (section == -1)
{
configerror(safe_snprintf("Cannot get %s section in config file!", sectionname));
vserver_kill(newvs);
return(NULL);
}
loadintfromconfig(cf->configfile, section, "maxusers",
&(newvs->maxusers), dvs->maxusers);
loadintfromconfig(cf->configfile, section, "logstrength",
&(newvs->loglevel), dvs->loglevel);
loadintfromconfig(cf->configfile, section, "logintries",
&(newvs->logincount), dvs->logincount);
loadintfromconfig(cf->configfile, section, "timeout",
&(newvs->timeout), dvs->timeout);
loadintfromconfig(cf->configfile, section, "umask",
&(newvs->umask), dvs->umask);
loadintfromconfig(cf->configfile, section, "maxconnectperip",
&(newvs->maxperip), dvs->maxperip);
if (newvs->logincount == 0)
newvs->logincount = -1;
loadintfromconfig(cf->configfile, section, "badauthwait",
&(newvs->authwait), dvs->authwait / 1000);
newvs->authwait *= 1000;
loadstrfromconfig(cf->configfile, section, "logfile",
&(newvs->logfile), dvs->logfile);
loadstrfromconfig(cf->configfile, section, "busydumpdata",
&setting, NULL);
if (setting)
newvs->toobusy = makebusystring(setting, cf->altlongreplies);
else if (dvs->toobusy)
newvs->toobusy = strdupwrapper(dvs->toobusy);
else
newvs->toobusy = NULL;
loadstrfromconfig(cf->configfile, section, "email",
&(newvs->email), dvs->email);
loadstrfromconfig(cf->configfile, section, "greeting",
&(newvs->greetline), dvs->greetline);
loadstrfromconfig(cf->configfile, section, "hostname",
&(newvs->vhostname), dvs->vhostname);
if (newvs->vhostname == NULL)
newvs->vhostname = cf->hostname;
loadstrfromconfig(cf->configfile, section, "logindump",
&(newvs->prelogindump), dvs->prelogindump);
loadstrfromconfig(cf->configfile, section, "logindumpdata",
&(newvs->prelogindumpdata), dvs->prelogindumpdata);
/* if prelogindumpdata is there, allocate it by itself and then
convert it to a displayable string */
if (newvs->prelogindumpdata)
{
newvs->prelogindumpdata = strdupwrapper(newvs->prelogindumpdata);
converttorealstr(newvs->prelogindumpdata);
}
newvs->grouplist = makeconfiglist(cf->configfile, sectionname, "group");
newvs->ipaccess = ipacllist_new(cf->configfile, section, "ipacl");
if (vp == NULL)
return(newvs);
occur = 1;
while((setting = getconfigdata(cf->configfile, section, "ftpport", occur)) != NULL)
{
char *bindip = strchr(setting, '/');
**vp = mallocwrapper(sizeof(VSERVERCONN));
if (bindip == NULL)
getnetworkint("0.0.0.0", &((**vp)->ip));
else
{
*bindip = 0;
getnetworkint(++bindip, &((**vp)->ip));
}
sscanf(setting, "%d", &((**vp)->port));
(**vp)->fd = 0;
(**vp)->vptr = newvs;
*vp = &((**vp)->next);
occur++;
}
return(newvs);
}
void ftpd_killconfig(CONFIGDATA *dc)
{
if (dc->configfile)
freeconfigcache(dc->configfile);
freeifnotnull(dc->vserverlist);
freeifnotnull(dc->hostname);
if (dc->vservers)
{
VSERVER *c = dc->vservers;
while(c != NULL)
{
VSERVER *d = c->next;
vserver_kill(c);
c = d;
}
}
if (dc->inports)
{
VSERVERCONN *c = dc->inports;
while (c != NULL)
{
VSERVERCONN *d = c->next;
freewrapper(c);
c = d;
}
}
if (dc->defaults)
vserver_kill(dc->defaults);
freewrapper(dc);
}
CONFIGDATA *ftpd_loadconfig(char *inidata, int as_inetd, int use_umask)
{
CONFIGDATA *newconfig = mallocwrapper(sizeof(CONFIGDATA));
int section, occur, line, error;
struct passwd *userinfo;
char *setting;
VSERVERCONN **vscpos;
VSERVER **vspos, *vpos;
newconfig->configfile = NULL;
newconfig->vserverlist = NULL;
newconfig->vservers = NULL;
newconfig->inports = NULL;
newconfig->inetd = as_inetd;
newconfig->parentpid = getpid();
newconfig->defaults = NULL;
newconfig->defaulthost = 0;
userinfo = getpwnam("nobody");
if (userinfo == NULL)
{
ftpd_killconfig(newconfig);
configerror(strdupwrapper("Cannot find uid/gid for user nobody!"));
return(NULL);
}
newconfig->hostname = gethostname2();
if (!(newconfig->hostname))
{
ftpd_killconfig(newconfig);
configerror(strdupwrapper("Could not resolve hostname for local machine."));
return(NULL);
}
newconfig->gidt_nobodygid = userinfo->pw_gid;
newconfig->uidt_nobodyuid = userinfo->pw_uid;
newconfig->configfile = loadconfigcache(inidata, &line, &error);
if (!(newconfig->configfile))
{
configerror(safe_snprintf("Error on line %d, %s", line, config_errorstr(error)));
configerror(safe_snprintf("Cannot open config file %s", inidata));
ftpd_killconfig(newconfig);
return(NULL);
}
section = getsectionid(newconfig->configfile, "main");
if (section == -1)
{
ftpd_killconfig(newconfig);
configerror(strdupwrapper("Cannot get main section in config file!"));
return(NULL);
}
loadintfromconfig(newconfig->configfile, section, "altlongreplies",
&(newconfig->altlongreplies), ALTLONGREPLIES);
loadintfromconfig(newconfig->configfile, section, "smartbind",
&(newconfig->smartbind), SMARTBIND);
loadintfromconfig(newconfig->configfile, section, "zerobind",
&(newconfig->zerobind), ZEROBIND);
loadintfromconfig(newconfig->configfile, section, "vserverhost",
&(newconfig->hostvservers), 0);
loadintfromconfig(newconfig->configfile, section, "rdnstimeout",
&(newconfig->dnstimeout), RDNSTIMEOUT);
if (newconfig->dnstimeout == 0)
newconfig->dnstimeout = -1;
if (!(setting = getconfigdata(newconfig->configfile, section, "runasuser", 1)))
{
newconfig->gidt_asgid = getgid();
newconfig->uidt_asuid = getuid();
newconfig->username = NULL;
}
else
{
userinfo = getpwnam(setting);
if (userinfo == NULL)
{
configerror(safe_snprintf("runasuser: username '%s' doesn't exist!", setting));
ftpd_killconfig(newconfig);
return(NULL);
}
newconfig->gidt_asgid = userinfo->pw_gid;
newconfig->uidt_asuid = userinfo->pw_uid;
newconfig->username = setting;
}
vscpos = &(newconfig->inports);
newconfig->vserverlist = makeconfiglist(newconfig->configfile, "main", "vserver");
if (newconfig->vserverlist[0] == NULL)
{
/* we don't have vservers, so we create one vserver
using the main section. This has the major advantage
of not needing lots of code for non-vserver setups */
newconfig->defaults = vserver_load(newconfig, "main", NULL, &(vscpos));
newconfig->defaults->umask = use_umask;
newconfig->vservers = NULL;
}
else
{
if (newconfig->hostvservers)
newconfig->defaults = vserver_load(newconfig, "main", NULL, &(vscpos));
else
newconfig->defaults = vserver_load(newconfig, "main", NULL, NULL);
newconfig->defaults->umask = use_umask;
occur = 0;
vspos = &(newconfig->vservers);
while(newconfig->vserverlist[occur] != NULL)
{
if (strlen(newconfig->vserverlist[occur]) >= MAXSECTIONLEN)
{
configerror(safe_snprintf("vserver '%s', name too long. Must be less than %d characters long.", newconfig->vserverlist[occur], MAXSECTIONLEN));
ftpd_killconfig(newconfig);
return(NULL);
}
*vspos = vserver_load(newconfig, newconfig->vserverlist[occur], newconfig->defaults, &(vscpos));
if (*vspos == NULL)
{
ftpd_killconfig(newconfig);
return(NULL);
}
vspos = &((*vspos)->next);
occur++;
}
*vspos = NULL;
}
*vscpos = NULL;
newconfig->rootmode = ((int)newconfig->uidt_asuid == 0);
loadstrfromconfig(newconfig->configfile, section, "vserverdefault",
&setting, NULL);
if ((setting) && (newconfig->vservers) && (newconfig->hostvservers))
{
vpos = newconfig->vservers;
while((vpos != NULL) && (strcmp(vpos->sectionname, setting) != 0))
vpos = vpos->next;
if (vpos == NULL)
{
configerror(safe_snprintf("vserverdefault section '%s' is not defined."));
ftpd_killconfig(newconfig);
return(NULL);
}
newconfig->defaulthost = vpos;
}
return(newconfig);
}
int ftpd_checkvserver(CONFIGDATA *cdat, VSERVER *vs)
{
int result = TRUE;
int count = 0;
if (vs->maxusers <= 0)
{
configerror(safe_snprintf("section '%s': must has maxusers more than zero.", vs->sectionname));
result = FALSE;
}
if ((vs->loglevel > 127) || (vs->loglevel < 0))
{
configerror(safe_snprintf("section '%s': Logstrength must be between 0 and 127", vs->sectionname));
result = FALSE;
}
if (vs->logfile)
if (vs->logfile[0] != '/')
{
configerror(safe_snprintf("section '%s': logfile is not an absolute filename", vs->sectionname));
result = FALSE;
}
if (vs->prelogindump)
if (vs->prelogindump[0] != '/')
{
configerror(safe_snprintf("section '%s': logindump is not an absolute filename.", vs->sectionname));
result = FALSE;
}
if ((vs->logincount == 0) || (vs->logincount < -1))
{
configerror(safe_snprintf("section '%s': logintries must be more than 0 or set to -1.", vs->sectionname));
result = FALSE;
}
if (vs->authwait < 0)
{
configerror(safe_snprintf("section '%s': badauthwait must be 0 or more milliseconds.", vs->sectionname));
result = FALSE;
}
while(vs->grouplist[count] != NULL)
{
if (strlen(vs->grouplist[count]) >= MAXSECTIONLEN)
{
configerror(safe_snprintf("section '%s': group '%s', name too long. Must be less than %d characters long", vs->sectionname, vs->grouplist[count], MAXSECTIONLEN));
result = FALSE;
}
else if (getsectionid(cdat->configfile, vs->grouplist[count]) == -1)
{
configerror(safe_snprintf("section '%s': group '%s' does not have a section in the config file.", vs->sectionname, vs->grouplist[count]));
result = FALSE;
}
count++;
}
return(result);
}
int ftpd_checkconfig(CONFIGDATA *cdat)
{
int result = TRUE;
VSERVERCONN *vsc, *vsc2;
VSERVER *vs;
if (cdat->dnstimeout < -1)
{
result = FALSE;
configerror(strdupwrapper("dnstimeout must be zero or more seconds"));
}
/* search for duplicate binds! */
vsc = cdat->inports;
if (vsc == NULL)
{
result = FALSE;
configerror(strdupwrapper("there are no ports to bind to."));
}
while ((vsc != NULL) && result)
{
vsc2 = vsc->next;
if ((vsc->port <= 0) || (vsc->port > 32768))
{
result = FALSE;
configerror(strdupwrapper("port to bind to not in valid range"));
}
while ((vsc2 != NULL) && result)
{
if ((vsc2->port) == (vsc->port))
if ((vsc2->ip == 0) ||
(vsc2->ip == vsc->ip))
{
result = FALSE;
configerror(strdupwrapper("Overlapping port binds found!"));
}
vsc2 = vsc2->next;
}
vsc = vsc->next;
}
/* now check all the vservers */
vs = cdat->vservers;
while ((vs != NULL) && result)
{
result = ftpd_checkvserver(cdat, vs);
vs = vs->next;
}
if (result)
result = ftpd_checkvserver(cdat, cdat->defaults);
/* now check the config file, and open attempt to open it */
if (result) /* if everything is ok still */
{
cdat->logout = log_initcontext(cdat->defaults->logfile);
if (cdat->logout == -1)
{
result = FALSE;
configerror(safe_snprintf("Couldn't open logfile '%s'", cdat->defaults->logfile));
}
}
return(result);
}
syntax highlighted by Code2HTML, v. 0.9.1