| // Copyright (c) 2012 The Chromium OS Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <poll.h> |
| #include <signal.h> |
| #include <stddef.h> |
| #include <sys/wait.h> |
| |
| #include <string> |
| |
| #include <base/command_line.h> |
| #include <base/files/scoped_temp_dir.h> |
| #include <base/logging.h> |
| #include <brillo/flag_helper.h> |
| #include <brillo/process/process.h> |
| #include <brillo/syslog_logging.h> |
| |
| #include "vpn-manager/daemon.h" |
| #include "vpn-manager/ipsec_manager.h" |
| #include "vpn-manager/l2tp_manager.h" |
| |
| using vpn_manager::IpsecManager; |
| using vpn_manager::L2tpManager; |
| using vpn_manager::ServiceManager; |
| |
| // True if a signal has requested termination. |
| static bool s_terminate_request = false; |
| |
| void HandleSignal(int sig_num) { |
| LOG(INFO) << "Caught signal " << sig_num; |
| switch (sig_num) { |
| case SIGTERM: |
| case SIGINT: |
| s_terminate_request = true; |
| break; |
| case SIGALRM: |
| break; |
| } |
| } |
| |
| static void InstallSignalHandlers() { |
| struct sigaction sa; |
| memset(&sa, 0, sizeof(sa)); |
| sa.sa_handler = HandleSignal; |
| sigaction(SIGTERM, &sa, nullptr); |
| sigaction(SIGINT, &sa, nullptr); |
| sigaction(SIGALRM, &sa, nullptr); |
| } |
| |
| static void LockDownUmask() { |
| // Only user and group may access configuration files we create. |
| umask(S_IWGRP | S_IROTH | S_IWOTH); |
| } |
| |
| // Run the main event loop. The events to handle are: |
| // 1) timeout from poll |
| // 2) caught signal |
| // 3) stdout/err of child process ready |
| // 4) child process dies |
| static void RunEventLoop(IpsecManager* ipsec, L2tpManager* l2tp) { |
| do { |
| int status; |
| int ipsec_poll_timeout = ipsec->Poll(); |
| int l2tp_poll_timeout = l2tp->Poll(); |
| int poll_timeout = (ipsec_poll_timeout > l2tp_poll_timeout) |
| ? ipsec_poll_timeout |
| : l2tp_poll_timeout; |
| |
| const int poll_input_count = 3; |
| struct pollfd poll_inputs[poll_input_count] = { |
| {ipsec->output_fd(), POLLIN}, // ipsec output |
| {l2tp->output_fd(), POLLIN}, // l2tp output |
| {l2tp->ppp_output_fd(), POLLIN} // ppp output |
| }; |
| int poll_result = poll(poll_inputs, poll_input_count, poll_timeout); |
| if (poll_result < 0 && errno != EINTR) { |
| int saved_errno = errno; |
| LOG(ERROR) << "Unexpected poll error: " << saved_errno; |
| return; |
| } |
| |
| // Check if there are any child processes to be reaped without |
| // blocking. |
| pid_t pid = waitpid(-1, &status, WNOHANG); |
| if (pid > 0 && (ipsec->IsChild(pid) || l2tp->IsChild(pid))) { |
| LOG(WARNING) << "Child process " << pid << " stopped early"; |
| s_terminate_request = true; |
| } |
| |
| if (poll_inputs[0].revents & POLLIN) |
| ipsec->ProcessOutput(); |
| if (poll_inputs[1].revents & POLLIN) |
| l2tp->ProcessOutput(); |
| if (poll_inputs[2].revents & POLLIN) |
| l2tp->ProcessPppOutput(); |
| } while (!ipsec->was_stopped() && !s_terminate_request); |
| } |
| |
| int main(int argc, char* argv[]) { |
| DEFINE_string(client_cert_id, "", "PKCS#11 key ID with client certificate"); |
| DEFINE_string(client_cert_slot, "", "PKCS#11 slot for client certificate"); |
| DEFINE_int32(log_level, 0, "Log verbosity; negative values enable debugging"); |
| DEFINE_string(psk_file, "", "File with IPsec pre-shared key"); |
| DEFINE_string(remote_host, "", "VPN server hostname"); |
| DEFINE_string(server_ca_file, "", "File with IPsec server CA in DER format"); |
| DEFINE_string(server_id, "", "ID expected from server"); |
| DEFINE_string(user_pin, "", "PKCS#11 User PIN"); |
| DEFINE_string(xauth_credentials_file, "", "File with Xauth user credentials"); |
| DEFINE_string(shill_task_service, "", "RPC Connection Identifier"); |
| DEFINE_string(shill_task_path, "", "RPC Object Identifier"); |
| |
| // IpsecManager related flags. |
| |
| // Phase 1 ciphersuites: |
| // aes128-sha256-modp3072: new strongSwan default |
| // aes128-sha1-modp2048: old strongSwan default |
| // 3des-sha1-modp1536: strongSwan fallback |
| // 3des-sha1-modp1024: for compatibility with Windows RRAS, which requires |
| // using the modp1024 dh-group |
| DEFINE_string(ike, |
| "aes128-sha256-modp3072," |
| "aes128-sha1-modp2048," |
| "3des-sha1-modp1536," |
| "3des-sha1-modp1024", |
| "ike proposals"); |
| |
| // Phase 2 ciphersuites: |
| // Cisco ASA L2TP/IPsec setup instructions indicate using md5 for |
| // authentication for the IPsec SA. Default StrongS/WAN setup is |
| // to only propose SHA1. |
| DEFINE_string(esp, |
| "aes128gcm16," |
| "aes128-sha256," |
| "aes128-sha1," |
| "3des-sha1," |
| "aes128-md5," |
| "3des-md5", |
| "esp proposals"); |
| |
| DEFINE_int32(ipsec_timeout, 30, "timeout for ipsec to be established"); |
| DEFINE_string(leftprotoport, "17/1701", "client protocol/port"); |
| DEFINE_bool(nat_traversal, true, "Enable NAT-T nat traversal"); |
| DEFINE_bool(pfs, false, "pfs"); |
| DEFINE_bool(rekey, true, "rekey"); |
| DEFINE_string(rightprotoport, "17/1701", "server protocol/port"); |
| DEFINE_string(tunnel_group, "", "Cisco Tunnel Group Name"); |
| DEFINE_string(type, "transport", "IPsec type (transport or tunnel)"); |
| |
| // L2tpManager related flags. |
| DEFINE_bool(defaultroute, true, "defaultroute"); |
| DEFINE_bool(length_bit, true, "length bit"); |
| DEFINE_bool(require_chap, true, "require chap"); |
| DEFINE_bool(refuse_pap, false, "refuse chap"); |
| DEFINE_bool(require_authentication, true, "require authentication"); |
| DEFINE_string(password, "", "password (insecure - use pppd plugin instead)"); |
| DEFINE_bool(ppp_lcp_echo, true, "ppp lcp echo connection monitoring"); |
| DEFINE_int32(ppp_setup_timeout, 60, "timeout to setup ppp (seconds)"); |
| DEFINE_string(pppd_plugin, "", "pppd plugin"); |
| DEFINE_bool(usepeerdns, true, "usepeerdns - ask peer for DNS"); |
| DEFINE_string(user, "", "user name"); |
| DEFINE_bool(systemconfig, true, "enable ppp to configure IPs/routes/DNS"); |
| |
| base::FilePath run_path("/run/l2tpipsec_vpn"); |
| base::FilePath persistent_path = run_path.Append("current"); |
| |
| // Make sure the run path exists. Since shill no longer runs as root user, we |
| // can't create the run path dir here. It should have been created in shill |
| // init scripts. |
| if (!base::DirectoryExists(run_path)) { |
| PLOG(ERROR) << "Directory does not exist " << run_path.value(); |
| return vpn_manager::kServiceErrorInternal; |
| } |
| |
| // Make sure the persistent path exists and is accessible for read by |
| // non-root users. This will allow items like CA certificates to be |
| // visible by the l2tpipsec process even after it has dropped privileges. |
| if (!base::DirectoryExists(persistent_path) && |
| !base::CreateDirectory(persistent_path)) { |
| PLOG(ERROR) << "Unable to create directory " << persistent_path.value(); |
| return vpn_manager::kServiceErrorInternal; |
| } |
| if (chmod(persistent_path.value().c_str(), |
| S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) { |
| PLOG(ERROR) << "Unable to change permissions of directory " |
| << persistent_path.value(); |
| return vpn_manager::kServiceErrorInternal; |
| } |
| |
| // Create temp_path directory under run_path, to be deleted at exit. |
| base::ScopedTempDir scoped_temp_dir; |
| if (!scoped_temp_dir.CreateUniqueTempDirUnderPath(run_path)) { |
| PLOG(ERROR) << "Unable create temp directory under " << run_path.value(); |
| return vpn_manager::kServiceErrorInternal; |
| } |
| const base::FilePath& temp_path = scoped_temp_dir.GetPath(); |
| |
| brillo::FlagHelper::Init(argc, argv, "Chromium OS l2tpipsec VPN"); |
| int log_flags = brillo::kLogToSyslog; |
| if (isatty(STDOUT_FILENO)) |
| log_flags |= brillo::kLogToStderr; |
| brillo::InitLog(log_flags); |
| logging::SetMinLogLevel(FLAGS_log_level); |
| brillo::OpenLog("l2tpipsec_vpn", true); |
| |
| // TODO(mortonm): When shill sandboxing lands and sticks, simplify this code |
| // by removing this if statement, since we will always be spawned in a |
| // minijail after that. |
| if (getenv("SHILL_TASK_SERVICE") == nullptr || |
| getenv("SHILL_TASK_SERVICE") == nullptr) { |
| // The necessary env vars were not passed in, implying we were spawned in a |
| // minijail. Clear the env vars left over from the parent and then set |
| // RPC-related vars to reflect the values that were passed in on the command |
| // line. |
| if (clearenv() != 0) { |
| PLOG(ERROR) << "Unable to clear environment vars"; |
| return vpn_manager::kServiceErrorInternal; |
| } |
| if (setenv("SHILL_TASK_SERVICE", FLAGS_shill_task_service.c_str(), 0) != |
| 0) { |
| PLOG(ERROR) << "Unable to set SHILL_TASK_SERVICE to: " |
| << FLAGS_shill_task_service.c_str(); |
| return vpn_manager::kServiceErrorInternal; |
| } |
| if (setenv("SHILL_TASK_PATH", FLAGS_shill_task_path.c_str(), 0) != 0) { |
| PLOG(ERROR) << "Unable to set SHILL_TASK_PATH to: " |
| << FLAGS_shill_task_path.c_str(); |
| return vpn_manager::kServiceErrorInternal; |
| } |
| } |
| |
| IpsecManager ipsec(FLAGS_esp, FLAGS_ike, FLAGS_ipsec_timeout, |
| FLAGS_leftprotoport, FLAGS_rekey, FLAGS_rightprotoport, |
| FLAGS_tunnel_group, FLAGS_type, temp_path, |
| persistent_path); |
| L2tpManager l2tp(FLAGS_defaultroute, FLAGS_length_bit, FLAGS_require_chap, |
| FLAGS_refuse_pap, FLAGS_require_authentication, |
| FLAGS_password, FLAGS_ppp_lcp_echo, FLAGS_ppp_setup_timeout, |
| FLAGS_pppd_plugin, FLAGS_usepeerdns, FLAGS_user, |
| FLAGS_systemconfig, temp_path); |
| |
| LockDownUmask(); |
| |
| sockaddr_storage remote_address; |
| if (!ServiceManager::ResolveNameToSockAddr(FLAGS_remote_host, |
| &remote_address)) { |
| LOG(ERROR) << "Unable to resolve hostname " << FLAGS_remote_host; |
| return vpn_manager::kServiceErrorResolveHostnameFailed; |
| } |
| |
| if (FLAGS_psk_file.empty() && !FLAGS_xauth_credentials_file.empty()) { |
| LOG(ERROR) << "Providing XAUTH credentials without a PSK is invalid"; |
| return vpn_manager::kServiceErrorInvalidArgument; |
| } |
| |
| if (!ipsec.Initialize(1, remote_address, FLAGS_psk_file, |
| FLAGS_xauth_credentials_file, FLAGS_server_ca_file, |
| FLAGS_server_id, FLAGS_client_cert_slot, |
| FLAGS_client_cert_id, FLAGS_user_pin)) { |
| return ipsec.GetError(); |
| } |
| if (!l2tp.Initialize(remote_address)) { |
| return l2tp.GetError(); |
| } |
| ServiceManager::SetLayerOrder(&ipsec, &l2tp); |
| |
| InstallSignalHandlers(); |
| if (!ipsec.Start()) { |
| LOG(ERROR) << "Unable to start IPsec layer"; |
| return ipsec.GetError(); |
| } |
| |
| RunEventLoop(&ipsec, &l2tp); |
| |
| LOG(INFO) << "Shutting down..."; |
| l2tp.Stop(); |
| return ipsec.GetError(); |
| } |