/* 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); }