/* $Id: sfsconst.C,v 1.36 2001/11/11 03:04:57 kaminsky Exp $ */ /* * * Copyright (C) 1999 David Mazieres (dm@uun.org) * * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "sfsmisc.h" #include "amisc.h" #include "parseopt.h" #include #include #ifndef SFSUSER # define SFSUSER "sfs" #endif /* !SFSUSER */ u_int32_t sfs_release = SFS_RELEASE; u_int16_t sfs_port = SFS_PORT; uid_t sfs_uid; gid_t sfs_gid; uid_t nobody_uid = (uid_t) -1; gid_t nobody_gid = (gid_t) -1; gid_t sfs_resvgid_start; // First reserved gid u_int sfs_resvgid_count; // Number of reserved gid's #ifdef MAINTAINER bool runinplace; #endif /* MAINTAINER */ str sfsdir = SFSDIR; str sfssockdir = SFSDIR "/sockets"; str sfsdevdb; const char *etc1dir = ETCDIR; const char *etc2dir = DATADIR; const char *etc3dir = NULL; const char *sfsroot = "/sfs"; u_int sfs_pubkeysize = 1280; u_int sfs_minpubkeysize = 768; u_int sfs_maxpubkeysize = 9000; u_int sfs_pwdcost = 7; u_int sfs_hashcost = 0; u_int sfs_maxhashcost = 22; static bool const_set; #ifdef MAINTAINER # define fwarn warn #else /* !MAINTAINER */ # define fwarn fatal #endif /* !MAINTAINER */ static void idlookup (str uid, str gid) { if (!uid) uid = "sfs"; if (!gid) gid = uid; bool uidok = convertint (uid, &sfs_uid); struct passwd *pw = uidok ? getpwuid (sfs_uid) : getpwnam (uid.cstr ()); bool gidok = convertint (gid, &sfs_gid); struct group *gr = gidok ? getgrgid (sfs_gid) : getgrnam (gid.cstr ()); if (!uidok) { if (!pw) fatal << "Could not find user " << uid << "\n"; sfs_uid = pw->pw_uid; } if (!gidok) { if (!gr) fatal << "Could not find group " << gid << "\n"; sfs_gid = gr->gr_gid; } if (gr && gr->gr_mem[0]) fwarn << "Group " << gid << " must not have any members\n"; if (pw && gr && (gid_t) pw->pw_gid != (gid_t) gr->gr_gid) fwarn << "User " << uid << " must have login group " << gid << ".\n"; endpwent (); endgrent (); } static void resvgidset (str lowstr, str highstr) { if (!lowstr || !highstr) return; gid_t end; if (!convertint (lowstr, &sfs_resvgid_start)) fatal << "Could not interpret resvgid value " << lowstr << " as a number.\n"; if (!convertint (highstr, &end)) fatal << "Could not interpret resvgid value " << highstr << " as a number.\n"; if (sfs_resvgid_start > end) fatal << "Starting value of resvgid range is greater than end value.\n"; sfs_resvgid_count = end - sfs_resvgid_start + 1; } static void parseconfig (const char *dir, const char *file) { if (!dir) return; str cf = strbuf ("%s/%s", dir, file); if (access (cf, F_OK) < 0) { if (errno != ENOENT) warn << cf << ": " << strerror (errno) << "\n"; return; } str uid, gid, nuid, ngid, resvgidlow, resvgidhigh; bool dirset = false; bool errors = false; parseargs pa (cf); int line; vec av; while (pa.getline (&av, &line)) { if (!strcasecmp (av[0], "sfsuser")) { if (uid) { errors = true; warn << cf << ":" << line << ": Duplicate sfsuser directive\n"; } else if (av.size () == 2) uid = gid = av[1]; else if (av.size () == 3) { uid = av[1]; gid = av[2]; } else { errors = true; warn << cf << ":" << line << ": usage: sfsuser user [group]\n"; } } else if (!strcasecmp (av[0], "anonuser")) { if (nuid) { errors = true; warn << cf << ":" << line << ": Duplicate anonuser directive\n"; } else if (av.size () == 2) nuid = av[1]; else if (av.size () == 3) { nuid = av[1]; ngid = av[2]; } else { errors = true; warn << cf << ":" << line << ": usage: anonuser user [group]\n"; continue; } gid_t g; if (ngid) { if (!convertint (ngid, &g)) { if (struct group *gr = getgrnam (ngid)) g = gr->gr_gid; else { errors = true; warn << cf << ":" << line << ": no group " << ngid << "\n"; ngid = NULL; } } } uid_t u; if (!convertint (nuid, &u)) { struct passwd *pw = getpwnam (nuid); if (!pw) { errors = true; warn << cf << ":" << line << ": no user " << nuid << "\n"; continue; } nobody_uid = pw->pw_uid; if (ngid) nobody_gid = g; else nobody_gid = pw->pw_gid; } else if (ngid) { nobody_uid = u; nobody_gid = g; } else { errors = true; warn << cf << ":" << line << ": Must specify gid with numeric uid"; continue; } } else if (!strcasecmp (av[0], "sfsdir")) { if (dirset) { errors = true; warn << cf << ":" << line << ": Duplicate sfsdir directive\n"; } else if (av.size () != 2) { errors = true; warn << cf << ":" << line << ": usage: sfsdir directory\n"; } else if (!runinplace) { sfsdir = av[1]; sfssockdir = sfsdir << "/sockets"; } dirset = true; } else if (!strcasecmp (av[0], "resvgids")) { if (resvgidhigh) { errors = true; warn << cf << ":" << line << ": Duplicate resvgids directive\n"; } else if (av.size () != 3) { errors = true; warn << cf << ":" << line << ": usage: resvgids lower upper\n"; } else { resvgidlow = av[1]; resvgidhigh = av[2]; } } else if (!strcasecmp (av[0], "pubkeysize")) { if (av.size () != 2 || !convertint (av[1], &sfs_pubkeysize)) { errors = true; warn << cf << ":" << line << ": usage: pubkeysize nbits\n"; } } else if (!strcasecmp (av[0], "pwdcost")) { if (av.size () != 2 || !convertint (av[1], &sfs_pwdcost)) { errors = true; warn << cf << ":" << line << ": usage: pwdcost cost-param\n"; } else if (sfs_pwdcost > 32) { errors = true; warn << cf << ":" << line << ": pwdcost must be well under 32\n"; } } else if (!strcasecmp (av[0], "logpriority")) { if (av.size () != 2) { errors = true; warn << cf << ":" << line << ": usage: LogPriority facility.level\n"; } else syslog_priority = av[1]; } else warn << cf << ":" << line << ": Unknown directive '" << av[0] << "'\n"; } if (errors) warn << "errors in " << cf << "\n"; if (uid) idlookup (uid, gid); resvgidset (resvgidlow, resvgidhigh); } /* If the directory specified by path does not exist, create it with * the given mode. If we fail for any reason, terminate with error. */ void mksfsdir (str path, mode_t mode, struct stat *sbp, uid_t uid) { assert (path[0] == '/'); mode_t m = umask (0); struct stat sb; if (stat (path, &sb) < 0) { if (errno != ENOENT || (mkdir (path, mode) < 0 && errno != EEXIST)) fatal ("%s: %m\n", path.cstr ()); if (chown (path, uid, sfs_gid) < 0) { int saved_errno = errno; rmdir (path); fatal ("chown (%s): %s\n", path.cstr (), strerror (saved_errno)); } if (stat (path, &sb) < 0) fatal ("stat (%s): %m\n", path.cstr ()); } umask (m); if (!S_ISDIR (sb.st_mode)) fatal ("%s: not a directory\n", path.cstr ()); if (sb.st_uid != uid) fwarn << path << ": owned by uid " << sb.st_uid << ", should be uid " << uid << "\n"; if (sb.st_gid != sfs_gid) fwarn << path << ": has gid " << sb.st_gid << ", should be gid " << sfs_gid << "\n"; if (sb.st_mode & 07777 & ~mode) fwarn ("%s: mode 0%o, should be 0%o\n", path.cstr (), sb.st_mode & 07777, mode); if (sbp) *sbp = sb; } void sfsconst_init () { if (const_set) return; const_set = true; { char *p = safegetenv ("SFS_RELEASE"); if (!p || !convertint (p, &sfs_release)) { str rel (strbuf () << "SFS_RELEASE=" << sfs_release); putenv (xstrdup (rel)); } } #ifdef MAINTAINER if (char *p = safegetenv ("SFS_RUNINPLACE")) { runinplace = true; builddir = p; buildtmpdir = builddir << "/runinplace"; } if (char *p = safegetenv ("SFS_ROOT")) if (*p == '/') sfsroot = p; #endif /* MAINTAINER */ sfsdevdb = strbuf ("%s/.devdb", sfsroot); #ifdef MAINTAINER if (runinplace) { sfsdir = buildtmpdir; sfssockdir = sfsdir; etc3dir = etc1dir; etc1dir = sfsdir; etc2dir = xstrdup (str (builddir << "/etc")); } if (char *ps = safegetenv ("SFS_PORT")) if (int pv = atoi (ps)) sfs_port = pv; #endif /* MAINTAINER */ parseconfig (etc3dir, "sfs_config"); parseconfig (etc2dir, "sfs_config"); parseconfig (etc1dir, "sfs_config"); if (!sfs_uid) idlookup (NULL, NULL); if (char *p = getenv ("SFS_HASHCOST")) { sfs_hashcost = strtoi64 (p); if (sfs_hashcost > sfs_maxhashcost) sfs_hashcost = sfs_maxhashcost; } if (!getuid () && !runinplace) { mksfsdir (sfsdir, 0755); mksfsdir (sfssockdir, 0750); } else if (runinplace && access (sfsdir, 0) < 0) { struct stat sb; if (!stat (builddir, &sb)) { mode_t m = umask (0); if (!getuid ()) { if (pid_t pid = fork ()) waitpid (pid, NULL, 0); else { umask (0); setgid (sfs_gid); setuid (sb.st_uid); mkdir (sfsdir, 02770); _exit (0); } } else mkdir (sfsdir, 0777); umask (m); } } } str sfsconst_etcfile (const char *name) { str file; if (name[0] == '/') { file = name; if (!access (file, F_OK)) return file; return NULL; } file = strbuf ("%s/%s", etc1dir, name); if (!access (file, F_OK)) return file; if (errno != ENOENT) fatal << file << ": " << strerror (errno) << "\n"; file = strbuf ("%s/%s", etc2dir, name); if (!access (file, F_OK)) return file; if (errno != ENOENT) fatal << file << ": " << strerror (errno) << "\n"; if (etc3dir) { file = strbuf ("%s/%s", etc3dir, name); if (!access (file, F_OK)) return file; if (errno != ENOENT) fatal << file << ": " << strerror (errno) << "\n"; } return NULL; } str sfsconst_etcfile_required (const char *name) { str file = sfsconst_etcfile (name); str e3 = etc3dir ? str (strbuf (" %s/%s\n", etc3dir, name)) : str (""); if (!file) fatal ("Could not find '%s'. Searched:\n %s/%s\n %s/%s\n", name, etc1dir, name, etc2dir, name) << e3; return file; }