int main(int argc, char *argv[]) { /* We need to keep these env variables since systemd uses them for socket * activation */ static const char *keep_env[] = { "LISTEN_FDS", "LISTEN_PID", NULL }; const char *root, *cwd, *env, *uid_str, *gid_str, *exe; char **args; uid_t uid; gid_t *gids; size_t n_gids; exit_if(argc < 7, "Usage: %s /path/to/root /work/directory /env/file uid gid[,gid...] /to/exec [args ...]", argv[0]); root = argv[1]; cwd = argv[2]; env = argv[3]; uid_str = argv[4]; uid = atoi(uid_str); gid_str = argv[5]; args = &argv[6]; exe = args[0]; parse_gids(gid_str, &n_gids, &gids); load_env(env, keep_env); pexit_if(chroot(root) == -1, "Chroot \"%s\" failed", root); pexit_if(chdir(cwd) == -1, "Chdir \"%s\" failed", cwd); pexit_if(gids[0] > 0 && setresgid(gids[0], gids[0], gids[0]) == -1, "Setresgid \"%s\" failed", gid_str); pexit_if(n_gids > 1 && setgroups(n_gids - 1, &gids[1]) == -1, "Setgroups \"%s\" failed", gid_str); pexit_if(uid > 0 && setresuid(uid, uid, uid) == -1, "Setresuid \"%s\" failed", uid_str); /* XXX(vc): note that since execvp() is happening post-chroot, the * app's environment settings correctly affect the PATH search. * This is why execvpe() isn't being used, we manipulate the environment * manually then let it potentially affect execvp(). execvpe() simply * passes the environment to execve() _after_ performing the search, not * what we want here. */ pexit_if(execvp(exe, args) == -1 && errno != ENOENT && errno != EACCES, "Exec of \"%s\" failed", exe); diag(exe); return EXIT_FAILURE; }
int startup(int argc, char* argv[]) { const char* tmp; const char* end; const char* cwdstr; char* ptr; unsigned long session_timeout; unsigned startup_code; if ((tmp = getenv("TCPLOCALIP")) == 0) FAIL("Missing $TCPLOCALIP."); if (!parse_localip(tmp)) FAIL("Could not parse $TCPLOCALIP."); if ((tmp = getenv("TCPREMOTEIP")) == 0) FAIL("Missing $TCPREMOTEIP."); if (!parse_remoteip(tmp)) FAIL("Could not parse $TCPREMOTEIP."); if ((tmp = getenv("UID")) == 0) FAIL("Missing $UID."); if (!(uid = strtou(tmp, &end)) || *end) FAIL("Invalid $UID."); if ((tmp = getenv("GID")) == 0) FAIL("Missing $GID."); if (!(gid = strtou(tmp, &end)) || *end) FAIL("Invalid $GID."); if ((home = getenv("HOME")) == 0) FAIL("Missing $HOME."); if ((tmp = getenv("GIDS")) != 0 && !parse_gids(tmp)) FAIL("Could not parse or set supplementary group IDs."); /* Strip off trailing slashes in $HOME */ ptr = (char*)home + strlen(home)-1; while (ptr > home && *ptr == '/') *ptr-- = 0; if ((user = getenv("USER")) == 0) FAIL("Missing $USER."); if ((group = getenv("GROUP")) == 0) group = "mygroup"; if (chdir(home)) FAIL("Could not chdir to $HOME."); if (!load_tables()) FAIL("Loading startup tables failed."); if (getenv("CHROOT") != 0) { cwdstr = "/"; if (chroot(".")) FAIL("Could not chroot."); } else if (getenv("SOFTCHROOT") != 0) { cwdstr = "/"; } else { cwdstr = home; if (chdir("/")) FAIL("Could not chdir to '/'."); } if (!str_copys(&cwd, cwdstr)) FAIL("Could not set CWD string"); if (setgid(gid)) FAIL("Could not set GID."); if (setuid(uid)) FAIL("Could not set UID."); if ((user_len = strlen(user)) > MAX_NAME_LEN) { user_len = MAX_NAME_LEN; ((char*)user)[MAX_NAME_LEN] = 0; } if ((group_len = strlen(group)) > MAX_NAME_LEN) { group_len = MAX_NAME_LEN; ((char*)group)[MAX_NAME_LEN] = 0; } lockhome = (getenv("LOCKHOME") != 0); nodotfiles = (getenv("NODOTFILES") != 0); list_options = (nodotfiles ? 0 : PATH_MATCH_DOTFILES); session_timeout = 0; if ((tmp = getenv("SESSION_TIMEOUT")) != 0) session_timeout = strtou(tmp, &tmp); alarm(session_timeout); connect_timeout = timeout; if ((tmp = getenv("CONNECT_TIMEOUT")) != 0) connect_timeout = strtou(tmp, &tmp); if ((tmp = getenv("TWOFTPD_BIND_PORT_FD")) != 0) { if ((bind_port_fd = strtou(tmp, &end)) == 0 || *end != 0) FAIL("Invalid $TWOFTPD_BIND_PORT_FD"); } else bind_port_fd = -1; startup_code = (getenv("AUTHENTICATED") != 0) ? 230 : 220; if ((tmp = getenv("BANNER")) != 0) show_banner(startup_code, tmp); message_file = getenv("MESSAGEFILE"); show_message_file(startup_code); return respond(startup_code, 1, "Ready to transfer files."); (void)argc; (void)argv; }