int do_main(int argc, char ** argv, get_hmac_key_prototype * hmac_fn, get_trace_key_prototype * trace_fn) { Inifile ini; { ConfigurationLoader cfg_loader_full(ini.configuration_holder(), CFG_PATH "/" RDPPROXY_INI); } UdevRandom rnd; CryptoContext cctx(rnd, ini); cctx.set_get_hmac_key_cb(hmac_fn); cctx.set_get_trace_key_cb(trace_fn); int res = -1; try { res = app_decrypter( argc, argv , "ReDemPtion DECrypter " VERSION ".\n" "Copyright (C) Wallix 2010-2015.\n" "Christophe Grosjean, Raphael Zhou." , cctx); if (res == 0){ printf("decrypt ok"); } else { printf("decrypt failed\n"); } } catch (const Error & e) { printf("decrypt failed: with id=%d\n", e.id); } return res; }
void redemption_new_session(CryptoContext & cctx, char const * config_filename) { char text[256]; char source_ip[256]; int target_port = 0; char real_target_ip[256]; union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } u; int sock_len = sizeof(u); Inifile ini; { ConfigurationLoader cfg_loader(ini.configuration_holder(), config_filename); } init_signals(); snprintf(text, 255, "redemption_%8.8x_main_term", unsigned(getpid())); getpeername(0, &u.s, reinterpret_cast<socklen_t *>(&sock_len)); strcpy(source_ip, inet_ntoa(u.s4.sin_addr)); union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } localAddress; socklen_t addressLength = sizeof(localAddress); int sck = 0; if (-1 == getsockname(sck, &localAddress.s, &addressLength)){ LOG(LOG_INFO, "getsockname failed error=%s", strerror(errno)); _exit(1); } target_port = localAddress.s4.sin_port; strcpy(real_target_ip, inet_ntoa(localAddress.s4.sin_addr)); if (ini.get<cfg::globals::enable_ip_transparent>()) { const int source_port = 0; char target_ip[256]; strcpy(target_ip, inet_ntoa(localAddress.s4.sin_addr)); int fd = open("/proc/net/ip_conntrack", O_RDONLY); // source and dest are inverted because we get the information we want from reply path rule int res = parse_ip_conntrack(fd, target_ip, source_ip, target_port, source_port, real_target_ip, sizeof(real_target_ip)); if (res){ LOG(LOG_WARNING, "Failed to get transparent proxy target from ip_conntrack"); } close(fd); } ini.set_acl<cfg::globals::host>(source_ip); ini.set_acl<cfg::globals::target>(real_target_ip); if (ini.get<cfg::debug::session>()){ LOG(LOG_INFO, "Setting new session socket to %d\n", sck); } int nodelay = 1; if (0 == setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&nodelay), sizeof(nodelay))){ Session session(sck, ini, cctx); if (ini.get<cfg::debug::session>()){ LOG(LOG_INFO, "Session::end of Session(%u)", sck); } shutdown(sck, 2); close(sck); } else { LOG(LOG_INFO, "Failed to set socket TCP_NODELAY option on client socket"); } }
Server_status start(int incoming_sck) override { union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } u; unsigned int sin_size = sizeof(u); memset(&u, 0, sin_size); int sck = accept(incoming_sck, &u.s, &sin_size); if (-1 == sck) { LOG(LOG_INFO, "Accept failed on socket %d (%s)", incoming_sck, strerror(errno)); _exit(1); } char source_ip[256]; strcpy(source_ip, inet_ntoa(u.s4.sin_addr)); REDEMPTION_DIAGNOSTIC_PUSH REDEMPTION_DIAGNOSTIC_GCC_IGNORE("-Wold-style-cast") // only to release const int source_port = ntohs(u.s4.sin_port); REDEMPTION_DIAGNOSTIC_POP /* start new process */ const pid_t pid = fork(); switch (pid) { case 0: /* child */ // TODO: see exit status of child, we could use it to diagnose session behaviours // TODO: we could probably use some session launcher object here. Something like // an abstraction layer that would manage either forking of threading behavior // this would also likely have some effect on network ressources management // (that means the select() on ressources could be managed by that layer) { close(incoming_sck); Inifile ini; ini.set<cfg::font>(Font(app_path(AppPath::DefaultFontFile))); ini.set<cfg::debug::config>(this->debug_config); configuration_load(ini.configuration_holder(), this->config_filename); if (ini.get<cfg::debug::session>()){ LOG(LOG_INFO, "Setting new session socket to %d\n", sck); } union { struct sockaddr s; struct sockaddr_storage ss; struct sockaddr_in s4; struct sockaddr_in6 s6; } localAddress; socklen_t addressLength = sizeof(localAddress); if (-1 == getsockname(sck, &localAddress.s, &addressLength)){ LOG(LOG_INFO, "getsockname failed error=%s", strerror(errno)); _exit(1); } char target_ip[256]; REDEMPTION_DIAGNOSTIC_PUSH REDEMPTION_DIAGNOSTIC_GCC_IGNORE("-Wold-style-cast") // only to release const int target_port = ntohs(localAddress.s4.sin_port); REDEMPTION_DIAGNOSTIC_POP // strcpy(real_target_ip, inet_ntoa(localAddress.s4.sin_addr)); strcpy(target_ip, inet_ntoa(localAddress.s4.sin_addr)); if (0 != strcmp(source_ip, "127.0.0.1")){ // do not log early messages for localhost (to avoid tracing in watchdog) LOG(LOG_INFO, "Redemption " VERSION); LOG(LOG_INFO, "src=%s sport=%d dst=%s dport=%d", source_ip, source_port, target_ip, target_port); } char real_target_ip[256]; if (ini.get<cfg::globals::enable_transparent_mode>() && (0 != strcmp(source_ip, "127.0.0.1"))) { int fd = open("/proc/net/ip_conntrack", O_RDONLY); // source and dest are inverted because we get the information we want from reply path rule int res = parse_ip_conntrack(fd, target_ip, source_ip, target_port, source_port, real_target_ip, sizeof(real_target_ip), 1); if (res){ LOG(LOG_WARNING, "Failed to get transparent proxy target from ip_conntrack: %d", fd); } close(fd); if (setgid(this->gid) != 0){ LOG(LOG_ERR, "Changing process group to %u failed with error: %s\n", this->gid, strerror(errno)); _exit(1); } if (setuid(this->uid) != 0){ LOG(LOG_ERR, "Changing process user to %u failed with error: %s\n", this->uid, strerror(errno)); _exit(1); } LOG(LOG_INFO, "src=%s sport=%d dst=%s dport=%d", source_ip, source_port, real_target_ip, target_port); } else { ::memset(real_target_ip, 0, sizeof(real_target_ip)); } int nodelay = 1; if (0 == setsockopt(sck, IPPROTO_TCP, TCP_NODELAY, &nodelay, sizeof(nodelay))){ // Create session file int child_pid = getpid(); char session_file[256]; sprintf(session_file, "%s/redemption/session_%d.pid", app_path(AppPath::Pid), child_pid); int fd = open(session_file, O_WRONLY | O_CREAT, S_IRWXU); if (fd == -1) { LOG(LOG_ERR, "Writing process id to SESSION ID FILE failed. Maybe no rights ?:%d:%s\n", errno, strerror(errno)); _exit(1); } char text[256]; const size_t lg = snprintf(text, 255, "%d", child_pid); if (write(fd, text, lg) == -1) { LOG(LOG_ERR, "Couldn't write pid to %s/redemption/session_<pid>.pid: %s", app_path(AppPath::Pid), strerror(errno)); _exit(1); } close(fd); // Launch session if (0 != strcmp(source_ip, "127.0.0.1")){ // do not log early messages for localhost (to avoid tracing in watchdog) LOG(LOG_INFO, "New session on %d (pid=%d) from %s to %s", sck, child_pid, source_ip, (real_target_ip[0] ? real_target_ip : target_ip)); } ini.set_acl<cfg::globals::host>(source_ip); // ini.context_set_value(AUTHID_TARGET, real_target_ip); ini.set_acl<cfg::globals::target>(target_ip); if (ini.get<cfg::globals::enable_transparent_mode>() && strncmp(target_ip, real_target_ip, strlen(real_target_ip))) { ini.set_acl<cfg::context::real_target_device>(real_target_ip); } Session session(sck, ini, this->cctx, this->rnd, this->fstat); // Suppress session file unlink(session_file); if (ini.get<cfg::debug::session>()){ LOG(LOG_INFO, "Session::end of Session(%d)", sck); } shutdown(sck, 2); close(sck); } else { LOG(LOG_ERR, "Failed to set socket TCP_NODELAY option on client socket"); } _exit(0); } break; default: /* father */ { close(sck); } break; case -1: // error forking LOG(LOG_ERR, "Error creating process for new session : %s\n", strerror(errno)); break; } return START_FAILED; }
int app_proxy( int argc, char const * const * argv, const char * copyright_notice , CryptoContext & cctx , ExtraOptions const & extrax_options, ExtracOptionChecker extrac_options_checker , PreLoopFn pre_loop_fn = PreLoopFn() ) { setlocale(LC_CTYPE, "C"); const unsigned uid = getuid(); const unsigned gid = getgid(); unsigned euid = uid; unsigned egid = gid; std::string config_filename = CFG_PATH "/" RDPPROXY_INI; program_options::options_description desc({ {'h', "help", "produce help message"}, {'v', "version", "show software version"}, {'k', "kill", "shut down rdpproxy"}, {'n', "nodaemon", "don't fork into background"}, {'u', "uid", &euid, "run with given uid"}, {'g', "gid", &egid, "run with given gid"}, //{'t', "trace", "trace behaviour"}, {'c', "check", "check installation files"}, {'f', "force", "remove application lock file"}, {'i', "inetd", "launch redemption with inetd like launcher"}, {"config-file", &config_filename, "used an another ini file"}, //{"test", "check Inifile syntax"} }); for (extra_option const & extra : extrax_options) { desc.add({extra.option_long, extra.description}); } auto options = program_options::parse_command_line(argc, const_cast<char**>(argv), desc); if (options.count("kill")) { int status = shutdown(PID_PATH "/redemption/" LOCKFILE); if (status){ // TODO check the real error that occured std::clog << "problem opening " << PID_PATH "/redemption/" LOCKFILE << "." " Maybe rdpproxy is not running\n"; } return status; } if (options.count("help")) { std::cout << copyright_notice << "\n\n"; std::cout << "Usage: rdpproxy [options]\n\n"; std::cout << desc << std::endl; return 0; } if (options.count("version")) { std::cout << copyright_notice << std::endl; return 0; } { bool quit = false; if (int status = extrac_options_checker(options, &quit)) { return status; } else if (quit) { return 0; } } openlog("rdpproxy", LOG_CONS | LOG_PERROR, LOG_USER); if (options.count("check")) { bool user_check_file_result = ((uid != euid) || (gid != egid)) ? CheckFile::check(user_check_file_list) : true; /* setgid(egid); setuid(euid); */ bool euser_check_file_result = CheckFile::check(euser_check_file_list); /* setgid(gid); setuid(uid); */ if ((uid != euid) || (gid != egid)) { CheckFile::ShowAll(user_check_file_list, uid, gid); } CheckFile::ShowAll(euser_check_file_list, euid, egid); if (!user_check_file_result || !euser_check_file_result) { if ((uid != euid) || (gid != egid)) { CheckFile::ShowErrors(user_check_file_list, uid, gid); } CheckFile::ShowErrors(euser_check_file_list, euid, egid); LOG(LOG_INFO, "%s", "Please verify that all tests passed. If not, " "you may need to remove " PID_PATH "/redemption/" LOCKFILE " or reinstall rdpproxy if some configuration " "files are missing."); } return 0; } if (options.count("inetd")) { redemption_new_session(cctx, config_filename.c_str()); return 0; } // if -f (force option) is set // force kill running rdpproxy // force remove pid file // don't check if it fails (proxy may be allready stopped) // and try to continue normal start process afterward if (mkdir(PID_PATH "/redemption", 0700) < 0){ // TODO check only for relevant errors (exists with expected permissions is OK) } if (chown(PID_PATH "/redemption", euid, egid) < 0){ LOG(LOG_INFO, "Failed to set owner %u.%u to " PID_PATH "/redemption", euid, egid); return 1; } if (options.count("force")){ shutdown(PID_PATH "/redemption/" LOCKFILE); } if (0 == access(PID_PATH "/redemption/" LOCKFILE, F_OK)) { std::clog << "File " << PID_PATH "/redemption/" LOCKFILE << " already exists. " "It looks like rdpproxy is already running, " "if not, try again with -f (force) option or delete the " PID_PATH "/redemption/" LOCKFILE " file and try again\n"; return 1; } /* write the pid to file */ int fd = open(PID_PATH "/redemption/" LOCKFILE, O_WRONLY | O_CREAT, S_IRWXU); if (fd == -1) { std::clog << "Writing process id to " PID_PATH "/redemption/" LOCKFILE " failed. Maybe no rights ?" << " : " << errno << ":'" << strerror(errno) << "'\n"; return 1; } { io::posix::fdbuf file(fd); const int pid = getpid(); char text[256]; size_t lg = snprintf(text, 255, "%d", pid); if (file.write(text, lg) == -1) { LOG(LOG_ERR, "Couldn't write pid to %s: %s", PID_PATH "/redemption/" LOCKFILE, strerror(errno)); return 1; } } if (!options.count("nodaemon")) { daemonize(PID_PATH "/redemption/" LOCKFILE); } Inifile ini; { ConfigurationLoader cfg_loader(ini.configuration_holder(), config_filename.c_str()); } OpenSSL_add_all_digests(); if (!ini.get<cfg::globals::enable_ip_transparent>()) { if (setgid(egid) != 0){ LOG(LOG_ERR, "Changing process group to %u failed with error: %s\n", egid, strerror(errno)); return 1; } if (setuid(euid) != 0){ LOG(LOG_ERR, "Changing process user to %u failed with error: %s\n", euid, strerror(errno)); return 1; } } pre_loop_fn(ini); LOG(LOG_INFO, "ReDemPtion " VERSION " starting"); redemption_main_loop(ini, cctx, euid, egid, std::move(config_filename)); /* delete the .pid file if it exists */ /* don't care about errors. */ /* If we are not in daemon mode this file will not exists, */ /* hence some errors are expected */ unlink(PID_PATH "/redemption/" LOCKFILE); return 0; }