static bool run(int argc, char *const argv[]) { struct passwd* userInfo = getpwuid(getuid()); if (!userInfo) { fprintf(stderr, "Couldn't get the current user: %s.\n", strerror(errno)); return false; } appendDirectoryComponent(sandboxDirectory, userInfo->pw_dir, "/.wk2-sandbox"); // Currently we use 'nobody' user as the sandbox user and fall back to the real user // if we failed to get it (we could extend this in the future with a specific restricted user). if (struct passwd* nobodyUser = getpwnam("nobody")) { sandboxUserUID = nobodyUser->pw_uid; sandboxUserGID = nobodyUser->pw_gid; } else { sandboxUserUID = getuid(); sandboxUserGID = getgid(); } // We should have three parameters: // path_of_this_binary path_of_the_webprocess socket_to_communicate_with_uiprocess if (argc != 3) { fprintf(stderr, "Starting SandboxProcess requires 3 parameters!\n"); return false; } // SandboxProcess should be run with suid flag ... if (geteuid()) { fprintf(stderr, "The sandbox is not seteuid root.\n"); return false; } // ... but not as root (not with sudo). if (!getuid()) { fprintf(stderr, "The sandbox is not designed to be run by root.\n"); return false; } if (!initializeSandbox()) return false; if (!restrictCapabilities()) return false; // We move ourself and our children into a new PID namespace, // where process IDs start from 0 again. if (!moveToNewPIDNamespace()) return false; // Starting a helper what will waiting for the "chrootme" message from WebProcess. if (!prepareAndStartChangeRootHelper()) return false; // We don't need any special privileges anymore. if (!dropPrivileges()) return false; // Sanity check: if our effective or real uid/gid is still 0 (root) or // we can set any of them to 0, then the dropping of privileges is failed. // We ensure here that we cannot set root id after here. if (!geteuid() || !getegid() || !setuid(0) || !setgid(0)) { fprintf(stderr, "Dropping privileges failed!\n"); return false; } // Start the WebProcess. if (execl(argv[1], argv[1], argv[2], reinterpret_cast<char*>(0)) == -1) { fprintf(stderr, "Couldn't start WebProcess: %s\n", strerror(errno)); return false; } return true; }
int main(int argc, char * const argv[]) { struct option long_options[] = { { "daemon", no_argument, nullptr, 'd' }, { "user", required_argument, nullptr, 'u' }, { "varnish", required_argument, nullptr, 'v' }, { "redis", required_argument, nullptr, 'r' }, { 0, 0, 0, 0 } }; bool daemonize = false; std::string userName; std::string varnishAddr; std::string redisAddr; for (bool done = false; !done; ) { int long_index = 0; switch (getopt_long(argc, argv, "hdu:v:r:", long_options, &long_index)) { case 'h': printHelp(); return 0; case 'd': daemonize = true; break; case 'u': userName = optarg; break; case 'v': varnishAddr = optarg; break; case 'r': redisAddr = optarg; break; case 0: // long opt with value != NULL done = true; break; case '?': // ambiguous match / unknown arg printHelp(); return 1; default: done = true; break; } } if (varnishAddr.empty() || redisAddr.empty()) { printf("varnish: %s\n", varnishAddr.c_str()); printf("redis: %s\n", redisAddr.c_str()); printHelp(); return 2; } if (!dropPrivileges(userName)) return 3; ev::default_loop ev; auto varnish_config = std::make_shared<varnish_cfg>(); if (!parseAddress(varnishAddr, varnish_config->host, varnish_config->port)) return 4; auto redis_config = std::make_shared<redis_cfg>(); redis_config->skey = REDIS_QUEUE_KEY; if (!parseAddress(redisAddr, redis_config->host, redis_config->port)) return 5; std::unique_ptr<PurgeWorker> worker(new PurgeWorker(ev, redis_config.get(), varnish_config.get())); if (daemonize && ::daemon(false /*no chdir?*/, false /*no close?*/) < 0) { fprintf(stderr, "Daemonizing failed: %s\n", strerror(errno)); return 6; } worker->run(); return 0; }