// SPDX-License-Identifier: AGPL-1.0-only // Copyright (C) 2018 Ludvig Strigeus . All Rights Reserved. #include "stdafx.h" #include "service_win32.h" #include #include "util.h" #include "network_win32_api.h" #include #include #include #include "util_win32.h" #include "service_win32_constants.h" static SERVICE_STATUS_HANDLE m_statusHandle; static TunsafeServiceManager *g_service; #define SERVICE_DEBUGGING 0 #define SERVICE_NAME L"TunSafeService" #define SERVICE_NAMEA "TunSafeService" #define SERVICE_START_TYPE SERVICE_AUTO_START #define SERVICE_DEPENDENCIES L"tap0901\0dhcp\0" #define SERVICE_ACCOUNT NULL #define SERVICE_PASSWORD NULL struct ServiceHandles { SC_HANDLE manager; SC_HANDLE service; ServiceHandles() : manager(NULL), service(NULL) {} ~ServiceHandles() { if (manager) CloseServiceHandle(manager); if (service) CloseServiceHandle(service); } bool Open(PWSTR pszServiceName, DWORD sc_rights, DWORD service_rights); bool StopService(); bool StartService(); }; static DWORD InstallService(PWSTR pszServiceName, PWSTR pszDisplayName, DWORD dwStartType, PWSTR pszDependencies, PWSTR pszAccount, PWSTR pszPassword) { wchar_t szPath[MAX_PATH + 32]; ServiceHandles handles; DWORD res; szPath[0] = '"'; if (GetModuleFileNameW(NULL, szPath + 1, MAX_PATH) == 0) { res = GetLastError(); goto Cleanup; } size_t len = wcslen(szPath); memcpy(szPath + len, L"\" --service", 12 * sizeof(wchar_t)); // Open the local default service control manager database handles.manager = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT | SC_MANAGER_CREATE_SERVICE); if (handles.manager == NULL) { res = GetLastError(); goto Cleanup; } // Install the service into SCM by calling CreateService handles.service = CreateServiceW( handles.manager, // SCManager database pszServiceName, // Name of service pszDisplayName, // Name to display SERVICE_QUERY_STATUS, // Desired access SERVICE_WIN32_OWN_PROCESS, // Service type dwStartType, // Service start type SERVICE_ERROR_NORMAL, // Error control type szPath, // Service's binary NULL, // No load ordering group NULL, // No tag identifier pszDependencies, // Dependencies pszAccount, // Service running account pszPassword // Password of the account ); if (handles.service == NULL) { res = GetLastError(); goto Cleanup; } { SERVICE_DESCRIPTIONA desc; desc.lpDescription = "TunSafe uses this service to connect to a VPN server in the background."; ChangeServiceConfig2A(handles.service, SERVICE_CONFIG_DESCRIPTION, &desc); } res = 0; Cleanup: if (res && res != ERROR_SERVICE_EXISTS) RERROR("TunSafe service installation failed: %d", res); return res; } bool ServiceHandles::Open(PWSTR pszServiceName, DWORD sc_rights, DWORD service_rights) { manager = OpenSCManagerW(NULL, NULL, sc_rights); if (manager == NULL) return false; service = OpenServiceW(manager, pszServiceName, service_rights); return (service != NULL); } bool ServiceHandles::StopService() { SERVICE_STATUS ssSvcStatus = {}; // Try to stop the service if (ControlService(service, SERVICE_CONTROL_STOP, &ssSvcStatus)) { Sleep(100); while (QueryServiceStatus(service, &ssSvcStatus)) { if (ssSvcStatus.dwCurrentState == SERVICE_STOP_PENDING) { Sleep(100); } else { break; } } } return (ssSvcStatus.dwCurrentState == SERVICE_STOPPED); } static wchar_t *GetUsernameOfCurrentUser(bool use_thread_token) { HANDLE thread_token = NULL; wchar_t *result = NULL; DWORD len; PTOKEN_USER token_user = NULL; DWORD domain_len; WCHAR username[256], domain[256]; SID_NAME_USE sid_type; if (use_thread_token) { if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &thread_token)) goto getout; } else { if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &thread_token)) goto getout; } len = 0; token_user = NULL; while (!GetTokenInformation(thread_token, TokenUser, token_user, len, &len)) { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) goto getout; token_user = (PTOKEN_USER)realloc(token_user, len); if (!token_user) goto getout; } if (!IsValidSid(token_user->User.Sid)) goto getout; domain_len = len = 256; if (!LookupAccountSidW(NULL, token_user->User.Sid, username, &len, domain, &domain_len, &sid_type)) goto getout; size_t alen = wcslen(username); size_t blen = wcslen(domain); result = (wchar_t*)malloc((alen + blen + 2) * sizeof(wchar_t)); if (result) { result[alen] = '@'; memcpy(result, username, alen * sizeof(wchar_t)); memcpy(result + alen + 1, domain, (blen + 1) * sizeof(wchar_t)); } getout: free(token_user); CloseHandle(thread_token); return result; } static wchar_t *RegReadStrW(HKEY hkey, const wchar_t *key, const wchar_t *def) { wchar_t buf[1024]; DWORD n = sizeof(buf) - 2; DWORD type = 0; if (RegQueryValueExW(hkey, key, NULL, &type, (BYTE*)buf, &n) != ERROR_SUCCESS || type != REG_SZ) return def ? _wcsdup(def) : NULL; n >>= 1; if (n && buf[n - 1] == 0) n--; buf[n] = 0; return _wcsdup(buf); } static DWORD GetNonTransientServiceStatus(SC_HANDLE service) { SERVICE_STATUS ssSvcStatus = {}; int delay = 100; for(;;) { if (!QueryServiceStatus(service, &ssSvcStatus)) return 0; if (--delay == 0 || ssSvcStatus.dwCurrentState != SERVICE_START_PENDING && ssSvcStatus.dwCurrentState != SERVICE_STOP_PENDING) return ssSvcStatus.dwCurrentState; Sleep(100); delay--; } } bool ServiceHandles::StartService() { DWORD state = GetNonTransientServiceStatus(service); if (state == 0 || state == SERVICE_RUNNING) return false; // service already running, no need to start if (!::StartService(service, 0, NULL)) { // if (GetLastError() == ERROR_SERVICE_ALREADY_RUNNING) // return false; return false; } return GetNonTransientServiceStatus(service) == SERVICE_RUNNING; } static bool StartTunsafeService() { ServiceHandles handles; if (!handles.Open(SERVICE_NAME, SC_MANAGER_CONNECT, SERVICE_START | SERVICE_QUERY_STATUS)) return false; return handles.StartService(); } bool IsTunsafeServiceRunning() { ServiceHandles handles; #if SERVICE_DEBUGGING return true; #endif if (!handles.Open(SERVICE_NAME, SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS)) return false; return GetNonTransientServiceStatus(handles.service) == SERVICE_RUNNING; } void StopTunsafeService() { ServiceHandles handles; if (!handles.Open(SERVICE_NAME, SC_MANAGER_CONNECT, SERVICE_STOP | SERVICE_QUERY_STATUS)) goto Cleanup; handles.StopService(); Cleanup: return; } static void SetTunsafeUserNameInRegistry() { wchar_t *user = GetUsernameOfCurrentUser(false); if (!user) { RERROR("Unable to get current username"); return; } HKEY hkey = NULL; RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\TunSafe", NULL, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey, NULL); if (!hkey) { RERROR("Unable to open registry key"); return; } if (RegSetValueExW(hkey, L"AllowedUsername", NULL, REG_SZ, (BYTE*)user, (DWORD)(wcslen(user) + 1) * 2) != ERROR_SUCCESS) { RERROR("Unable to set registry key"); } RegCloseKey(hkey); } void InstallTunSafeWindowsService() { InstallService(SERVICE_NAME, L"TunSafe Service", SERVICE_START_TYPE, SERVICE_DEPENDENCIES, SERVICE_ACCOUNT, SERVICE_PASSWORD); StartTunsafeService(); SetTunsafeUserNameInRegistry(); } bool UninstallTunSafeWindowsService() { ServiceHandles handles; if (!handles.Open(SERVICE_NAME, SC_MANAGER_CONNECT, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE)) goto Cleanup; handles.StopService(); if (!DeleteService(handles.service)) goto Cleanup; return true; Cleanup: return false; } bool IsTunSafeServiceInstalled() { ServiceHandles handles; return handles.Open(SERVICE_NAME, SC_MANAGER_CONNECT, SERVICE_QUERY_STATUS); } static void WriteServiceLog(const char *pszFunction, WORD dwError) { char szMessage[260]; snprintf(szMessage, ARRAYSIZE(szMessage), "%s failed w/err 0x%08lx", pszFunction, dwError); HANDLE hEventSource = NULL; LPCSTR lpszStrings[2] = {NULL, NULL}; hEventSource = RegisterEventSourceW(NULL, SERVICE_NAME); if (hEventSource) { lpszStrings[0] = SERVICE_NAMEA; lpszStrings[1] = szMessage; ReportEventA(hEventSource, // Event log handle dwError, // Event type 0, // Event category 0, // Event identifier NULL, // No security identifier 2, // Size of lpszStrings array 0, // No binary data lpszStrings, // Array of strings NULL // No binary data ); DeregisterEventSource(hEventSource); } } static void SetServiceStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode = 0, DWORD dwWaitHint = 0) { static DWORD dwCheckPoint = 1; SERVICE_STATUS m_status; m_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; m_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; m_status.dwServiceSpecificExitCode = 0; m_status.dwCurrentState = dwCurrentState; m_status.dwWin32ExitCode = dwWin32ExitCode; m_status.dwWaitHint = dwWaitHint; m_status.dwCheckPoint = ((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) ? 0 : dwCheckPoint++; // Report the status of the service to the SCM. ::SetServiceStatus(m_statusHandle, &m_status); } static void OnServiceStart(DWORD dwArgc, PWSTR *pszArgv) { WriteServiceLog("Service Starting", EVENTLOG_INFORMATION_TYPE); SetServiceStatus(SERVICE_START_PENDING); DWORD rv = g_service->OnStart(dwArgc, pszArgv); if (rv) { SetServiceStatus(SERVICE_STOPPED, rv); } else { SetServiceStatus(SERVICE_RUNNING); } } static void OnServiceStop() { WriteServiceLog("Service Stopping", EVENTLOG_INFORMATION_TYPE); SetServiceStatus(SERVICE_STOP_PENDING); g_service->OnStop(); SetServiceStatus(SERVICE_STOPPED); } static void OnServiceShutdown() { g_service->OnShutdown(); SetServiceStatus(SERVICE_STOPPED); } static void WINAPI ServiceCtrlHandler(DWORD dwCtrl) { switch (dwCtrl) { case SERVICE_CONTROL_STOP: OnServiceStop(); break; // case SERVICE_CONTROL_PAUSE: OnServicePause(); break; // case SERVICE_CONTROL_CONTINUE: OnServiceContinue(); break; case SERVICE_CONTROL_SHUTDOWN: OnServiceShutdown(); break; case SERVICE_CONTROL_INTERROGATE: break; default: break; } } static void WINAPI ServiceMain(DWORD dwArgc, PWSTR *pszArgv) { // Register the handler function for the service m_statusHandle = RegisterServiceCtrlHandlerW(SERVICE_NAME, ServiceCtrlHandler); if (m_statusHandle == NULL) throw GetLastError(); // Start the service. OnServiceStart(dwArgc, pszArgv); } static const SERVICE_TABLE_ENTRYW serviceTable[] = { {SERVICE_NAME, ServiceMain}, {NULL, NULL} }; /////////////////////////////////////////////////////////////////////////////////////// // TunsafeServiceManager /////////////////////////////////////////////////////////////////////////////////////// TunsafeServiceManager::TunsafeServiceManager() : pipe_manager_(TUNSAFE_PIPE_NAME, true, this) { server_unique_id_ = 0; hkey_ = NULL; RegCreateKeyEx(HKEY_LOCAL_MACHINE, "Software\\TunSafe", NULL, NULL, 0, KEY_ALL_ACCESS, NULL, &hkey_, NULL); main_backend_ = new TunsafeServiceBackend(this); backends_.push_back(main_backend_); } TunsafeServiceManager::~TunsafeServiceManager() { for (TunsafeServiceBackend *backend : backends_) delete backend; RegCloseKey(hkey_); } void TunsafeServiceManager::HandleNotify() { for (TunsafeServiceBackend *backend : backends_) backend->HandleNotify(); } PipeConnection::Delegate *TunsafeServiceManager::HandleNewConnection(PipeConnection *connection) { TunsafeServiceServer *server = new TunsafeServiceServer(connection, main_backend_, server_unique_id_++); main_backend_->AddPipeServer(server); pipe_manager_.TryStartNewListener(); return server; } unsigned TunsafeServiceManager::OnStart(int argc, wchar_t **argv) { uint32 service_flags = RegReadInt(hkey_, "ServiceStartupFlags", 0); if ((service_flags & kStartupFlag_BackgroundService) && (service_flags & kStartupFlag_ConnectWhenWindowsStarts)) { char *conf = RegReadStr(hkey_, "LastUsedConfigFile", ""); if (conf && *conf) main_backend_->Start(conf); free(conf); } pipe_manager_.StartThread(); return 0; } void TunsafeServiceManager::OnStop() { pipe_manager_.StopThread(); for (TunsafeServiceBackend *backend : backends_) backend->Stop(); } void TunsafeServiceManager::OnShutdown() { } TunsafeServiceBackend *TunsafeServiceManager::CreateBackend(const char *guid) { TunsafeServiceBackend *service_backend = new TunsafeServiceBackend(this); // If we're unable to assign the name, maybe it's already in use if (!service_backend->backend()->SetTunAdapterName(guid)) { delete service_backend; return NULL; } backends_.push_back(service_backend); return service_backend; } void TunsafeServiceManager::DestroyBackend(TunsafeServiceBackend *service_backend) { assert(service_backend != main_backend_); // Erase from the list auto it = std::find(backends_.begin(), backends_.end(), service_backend); if (it != backends_.end()) backends_.erase(it); delete service_backend; } bool TunsafeServiceManager::SwitchInterface(TunsafeServiceServer *server, const char *interfac, bool want_create) { // Find a backend by name TunsafeBackend *backend = TunsafeBackend::FindBackendByTunGuid(interfac); TunsafeServiceBackend *service_backend = NULL; if (backend) { for (TunsafeServiceBackend *sb : backends_) { if (sb->backend() == backend) { service_backend = sb; break; } } } if (!service_backend) { if (!want_create) return false; service_backend = CreateBackend(interfac); if (!service_backend) return false; } if (server->service_backend() != service_backend) { server->service_backend()->RemovePipeServer(server); service_backend->AddPipeServer(server); server->set_service_backend(service_backend); } return true; } /////////////////////////////////////////////////////////////////////////////////////// // TunsafeServiceBackend /////////////////////////////////////////////////////////////////////////////////////// TunsafeServiceBackend::TunsafeServiceBackend(TunsafeServiceManager *manager) { manager_ = manager; historical_log_lines_count_ = historical_log_lines_pos_ = 0; memset(historical_log_lines_, 0, sizeof(historical_log_lines_)); HANDLE event = manager_->pipe_manager_.notify_handle(); thread_delegate_ = CreateTunsafeBackendDelegateThreaded(this, [=] { SetEvent(event); }); backend_ = CreateNativeTunsafeBackend(thread_delegate_); } TunsafeServiceBackend::~TunsafeServiceBackend() { assert(pipe_servers_.empty()); delete backend_; delete thread_delegate_; } void TunsafeServiceBackend::OnGetStats(const WgProcessorStats &stats) { for (TunsafeServiceServer *pipe_server : pipe_servers_) if (pipe_server->want_stats()) pipe_server->WritePacket(TS_SERVICE_MSG_STATS, (uint8*)&stats, sizeof(stats)); } void TunsafeServiceBackend::OnClearLog() { historical_log_lines_pos_ = 0; historical_log_lines_count_ = 0; for (TunsafeServiceServer *pipe_server : pipe_servers_) if (pipe_server->want_state_updates()) pipe_server->WritePacket(TS_SERVICE_MSG_CLEARLOG, NULL, 0); } void TunsafeServiceBackend::OnLogLine(const char **s) { assert(manager_->pipe_manager_.VerifyThread()); char *ss = (char*)*s; *s = NULL; char *&x = historical_log_lines_[historical_log_lines_pos_++ & (LOGLINE_COUNT - 1)]; std::swap(x, ss); if (historical_log_lines_count_ < LOGLINE_COUNT) historical_log_lines_count_++; free(ss); for (TunsafeServiceServer *pipe_server : pipe_servers_) pipe_server->SendQueuedLogLines(); } void TunsafeServiceBackend::OnStateChanged() { SendStateUpdate(NULL); // Send to all } void TunsafeServiceBackend::OnStatusCode(TunsafeBackend::StatusCode status) { if (status == TunsafeBackend::kStatusConnected) OnStateChanged(); // ensure we know the ip first uint32 v32 = (uint32)status; for (TunsafeServiceServer *pipe_server : pipe_servers_) if (pipe_server->want_state_updates()) pipe_server->WritePacket(TS_SERVICE_MSG_STATUS_CODE, (uint8*)&v32, 4); } void TunsafeServiceBackend::OnGraphAvailable() { for (TunsafeServiceServer *pipe_server : pipe_servers_) pipe_server->OnGraphAvailable(); } void TunsafeServiceBackend::OnConfigurationProtocolReply(uint32 ident, const std::string &&reply) { for (TunsafeServiceServer *pipe_server : pipe_servers_) if (pipe_server->unique_id() == ident) pipe_server->WritePacket(TS_SERVICE_REQ_TEXT_PROTOCOL_REPLY, (uint8*)reply.data(), reply.size()); } void TunsafeServiceBackend::Start(const char *filename) { g_allow_pre_post = RegReadInt(manager_->hkey_, "AllowPrePost", 0) != 0; current_filename_ = filename; backend_->Start(filename); } void TunsafeServiceBackend::RememberLastUsedConfigFile(const char *filename) { if (manager_->main_backend() == this) RegWriteStr(manager_->hkey_, "LastUsedConfigFile", filename); } void TunsafeServiceBackend::Stop() { if (manager_->main_backend() == this) RegWriteStr(manager_->hkey_, "LastUsedConfigFile", ""); backend_->Stop(); OnStateChanged(); } void TunsafeServiceBackend::UpdateRequestStats() { bool want = false; for (auto it = pipe_servers_.begin(); it != pipe_servers_.end(); ++it) { if ((*it)->want_stats()) { want = true; break; } } backend_->RequestStats(want); } void TunsafeServiceBackend::HandleNotify() { thread_delegate_->DoWork(); } void TunsafeServiceBackend::SendStateUpdate(TunsafeServiceServer *filter) { if (pipe_servers_.empty()) return; uint8 *temp = new uint8[current_filename_.size() + 1 + sizeof(ServiceState)]; bool is_activated; memset(temp, 0, sizeof(ServiceState)); ServiceState *ss = (ServiceState *)temp; ss->is_started = backend_->is_started(); ss->internet_block_state = backend_->GetInternetBlockState(&is_activated); ss->internet_block_state_active = is_activated; ss->ipv4_ip = backend_->GetIP(); memcpy(ss->public_key, backend_->public_key(), 32); memcpy(temp + sizeof(ServiceState), current_filename_.c_str(), current_filename_.size() + 1); for (TunsafeServiceServer *pipe_server : pipe_servers_) { if (filter != NULL && pipe_server != filter) continue; if (pipe_server->want_state_updates()) pipe_server->WritePacket(TS_SERVICE_MSG_STATE, temp, current_filename_.size() + 1 + sizeof(ServiceState)); } delete[] temp; } void TunsafeServiceBackend::RemovePipeServer(TunsafeServiceServer *pipe_server) { auto it = std::find(pipe_servers_.begin(), pipe_servers_.end(), pipe_server); if (it != pipe_servers_.end()) pipe_servers_.erase(it); UpdateRequestStats(); // Stop the main backend, or destroy a disconnetced backend, when the last client disconnects. if (pipe_servers_.empty()) { if (this == manager_->main_backend_) { uint32 service_flags = RegReadInt(manager_->hkey_, "ServiceStartupFlags", 0); if (!(service_flags & kStartupFlag_BackgroundService)) backend_->Stop(); } else { if (!backend_->is_started()) manager_->DestroyBackend(this); } } } void TunsafeServiceBackend::AddPipeServer(TunsafeServiceServer *pipe_server) { pipe_servers_.push_back(pipe_server); } /////////////////////////////////////////////////////////////////////////////////////// // TunsafeServiceServer /////////////////////////////////////////////////////////////////////////////////////// TunsafeServiceServer::TunsafeServiceServer(PipeConnection *pipe, TunsafeServiceBackend *backend, uint32 unique_id) { unique_id_ = unique_id; connection_ = pipe; service_backend_ = backend; last_line_sent_ = 0; want_state_updates_ = false; did_authenticate_user_ = false; want_stats_ = false; want_graph_type_ = 0xffffffff; } TunsafeServiceServer::~TunsafeServiceServer() { } void TunsafeServiceServer::WritePacket(int type, const uint8 *data, size_t data_size) { connection_->WritePacket(type, data, data_size); } struct ServiceLoginMessage { uint64 version; char interfac[kTsMaxDevnameSize]; bool want_state_updates; bool want_create_interface; }; bool TunsafeServiceServer::HandleMessage(int type, uint8 *data, size_t size) { if (!did_authenticate_user_) { if (type != TS_SERVICE_REQ_LOGIN || size < sizeof(ServiceLoginMessage) || ((ServiceLoginMessage*)data)->version != TUNSAFE_SERVICE_PROTOCOL_VERSION) { const char *s = "Versioning Problem: The TunSafe service is a different version than the UI."; connection_->WritePacket(TS_SERVICE_MSG_ERROR_REPLY, (uint8*)s, strlen(s)); return false; } if (!AuthenticateUser()) { const char *s = "Permission Problem: Your Windows account is different from the account\r\nthat installed the TunSafe Service. Please reinstall it."; connection_->WritePacket(TS_SERVICE_MSG_ERROR_REPLY, (uint8*)s, strlen(s)); return false; } } switch (type) { case TS_SERVICE_REQ_START: { if (size == 0 || data[size - 1] != 0) return false; for (size_t i = 0; i < size; i++) { if (data[i] == '/') data[i] = '\\'; } char buf[MAX_PATH]; buf[0] = 0; if (data[0]) { if (!ExpandConfigPath((char*)data, buf, sizeof(buf)) || GetFileAttributesA(buf) == INVALID_FILE_ATTRIBUTES) { char *s = str_cat_alloc("File '", (char*)data, "' not found"); connection_->WritePacket(TS_SERVICE_MSG_ERROR_REPLY, (uint8*)s, strlen(s)); free(s); return false; } // Don't allow reading arbitrary files on disk if (!EnsureValidConfigPath(buf)) { GetConfigPath(buf, sizeof(buf)); char *s = str_cat_alloc("Permission Problem: The Config file is in an unsafe location.\r\n Must be in: ", buf, ""); connection_->WritePacket(TS_SERVICE_MSG_ERROR_REPLY, (uint8*)s, strlen(s)); free(s); return false; } } service_backend_->Start(buf); service_backend_->RememberLastUsedConfigFile(buf); // Ensure we reply with something if (!want_state_updates_) { uint32 v32 = (uint32)service_backend_->backend_->status(); connection_->WritePacket(TS_SERVICE_MSG_STATUS_CODE, (uint8*)&v32, 4); } break; } case TS_SERVICE_REQ_STOP: service_backend_->Stop(); if (!want_state_updates_) { uint32 v32 = (uint32)service_backend_->backend_->status(); connection_->WritePacket(TS_SERVICE_MSG_STATUS_CODE, (uint8*)&v32, 4); } break; case TS_SERVICE_REQ_LOGIN: { if (((ServiceLoginMessage*)data)->interfac[kTsMaxDevnameSize - 1]) return false; // sanity check if (((ServiceLoginMessage*)data)->interfac[0] != 0) { if (!service_backend_->manager_->SwitchInterface(this, ((ServiceLoginMessage*)data)->interfac, ((ServiceLoginMessage*)data)->want_create_interface)) { const char *s = ((ServiceLoginMessage*)data)->want_create_interface ? "Unable to add the interface" : "Interface is not started"; connection_->WritePacket(TS_SERVICE_MSG_ERROR_REPLY, (uint8*)s, strlen(s)); return false; } } want_state_updates_ = ((ServiceLoginMessage*)data)->want_state_updates; if (want_state_updates_) { SendQueuedLogLines(); service_backend_->SendStateUpdate(this); uint32 v32 = (uint32)service_backend_->backend_->status(); connection_->WritePacket(TS_SERVICE_MSG_STATUS_CODE, (uint8*)&v32, 4); } break; } // return a list of all running interfaces case TS_SERVICE_REQ_GETINTERFACES: { char *s = TunsafeBackend::GetAllGuid(); connection_->WritePacket(TS_SERVICE_REQ_GETINTERFACES_REPLY, (uint8*)s, s ? strlen(s) : 0); free(s); break; } case TS_SERVICE_REQ_GETSTATS: if (size < 1) return false; want_stats_ = (data[0] != 0); service_backend_->UpdateRequestStats(); break; case TS_SERVICE_REQ_SET_INTERNET_BLOCKSTATE: if (size < 1) return false; service_backend_->backend_->SetInternetBlockState((InternetBlockState)data[0]); service_backend_->OnStateChanged(); break; case TS_SERVICE_REQ_RESETSTATS: service_backend_->backend_->ResetStats(); break; case TS_SERVICE_REQ_GET_GRAPH: if (size < 4) return false; want_graph_type_ = *(int*)data; TunsafeServiceServer::OnGraphAvailable(); break; case TS_SERVICE_REQ_SET_STARTUP_FLAGS: if (size < 4) return false; RegSetValueEx(service_backend_->manager_->hkey_, "ServiceStartupFlags", NULL, REG_DWORD, (BYTE*)data, 4); break; case TS_SERVICE_REQ_TEXT_PROTOCOL: if (!service_backend_->backend_->is_started()) service_backend_->Start(""); service_backend_->backend_->SendConfigurationProtocolPacket(unique_id_, std::string((char*)data, size)); break; default: return false; } return true; } void TunsafeServiceServer::HandleDisconnect() { service_backend_->RemovePipeServer(this); delete this; } void TunsafeServiceServer::OnGraphAvailable() { if (want_graph_type_ != 0xffffffff) { LinearizedGraph *graph = service_backend_->backend_->GetGraph(want_graph_type_); if (graph) { connection_->WritePacket(TS_SERVICE_MSG_GRAPH, (uint8*)graph, graph->total_size); free(graph); } } } void TunsafeServiceServer::SendQueuedLogLines() { if (!want_state_updates_) return; assert(connection_->VerifyThread()); uint32 maxi = std::min(service_backend_->historical_log_lines_count_, service_backend_->historical_log_lines_pos_ - last_line_sent_); last_line_sent_ = service_backend_->historical_log_lines_pos_; for (uint32 i = 0; i < maxi; i++) { const char *s = service_backend_->historical_log_lines_[(service_backend_->historical_log_lines_pos_ - maxi + i) & (TunsafeServiceBackend::LOGLINE_COUNT - 1)]; if (s) connection_->WritePacket(TS_SERVICE_MSG_LOGLINE, (uint8*)s, strlen(s)); } } bool TunsafeServiceServer::AuthenticateUser() { if (!ImpersonateNamedPipeClient(connection_->pipe_handle())) return false; wchar_t *user = GetUsernameOfCurrentUser(true); RevertToSelf(); if (!user) return false; wchar_t *valid_user = RegReadStrW(service_backend_->manager_->hkey_, L"AllowedUsername", L""); bool rv = valid_user && wcscmp(user, valid_user) == 0; did_authenticate_user_ = rv; free(user); free(valid_user); return rv; } static void PushServiceLine(int type, const char *s) { if (g_service) { char buf[64]; SYSTEMTIME t; size_t l = strlen(s); GetLocalTime(&t); snprintf(buf, sizeof(buf), "[%.2d:%.2d:%.2d] ", t.wHour, t.wMinute, t.wSecond); size_t tl = strlen(buf); char *x = (char*) malloc(tl + l + 1); memcpy(x, buf, tl); memcpy(x + tl, s, l); x[l + tl] = '\0'; g_service->main_backend()->delegate()->OnLogLine((const char**)&x); free(x); } else { size_t l = strlen(s); char buf[1024]; SYSTEMTIME t; GetLocalTime(&t); snprintf(buf, sizeof(buf), "[%.2d:%.2d:%.2d] ", t.wHour, t.wMinute, t.wSecond); size_t tl = strlen(buf); if (l >= ARRAYSIZE(buf) - tl - 1) l = ARRAYSIZE(buf) - tl - 1; memcpy(buf + tl, s, l); buf[l + tl] = '\0'; WriteServiceLog(buf, EVENTLOG_INFORMATION_TYPE); } } BOOL RunProcessAsTunsafeServiceProcess() { g_service = new TunsafeServiceManager; g_logger = &PushServiceLine; #if SERVICE_DEBUGGING g_service->OnStart(NULL, 0); while (true) Sleep(1000); #endif // Connects the main thread of a service process to the service control // manager, which causes the thread to be the service control dispatcher // thread for the calling process. This call returns when the service has // stopped. The process should simply terminate when the call returns. return StartServiceCtrlDispatcherW(serviceTable); } /////////////////////////////////////////////////////////////////////////////////////// // TunsafeServiceClient /////////////////////////////////////////////////////////////////////////////////////// TunsafeServiceClient::TunsafeServiceClient(TunsafeBackend::Delegate *delegate) : pipe_manager_(TUNSAFE_PIPE_NAME, false, this) { is_remote_ = true; got_state_from_control_ = false; delegate_ = delegate; cached_graph_ = 0; last_graph_type_ = 0xffffffff; memset(&service_state_, 0, sizeof(service_state_)); connection_ = pipe_manager_.GetClientConnection(); } TunsafeServiceClient::~TunsafeServiceClient() { pipe_manager_.StopThread(); } bool TunsafeServiceClient::Configure() { return pipe_manager_.StartThread(); } void TunsafeServiceClient::Start(const char *config_file) { connection_->WritePacket(TS_SERVICE_REQ_START, (uint8*)config_file, strlen(config_file) + 1); } void TunsafeServiceClient::Stop() { connection_->WritePacket(TS_SERVICE_REQ_STOP, NULL, 0); } void TunsafeServiceClient::RequestStats(bool enable) { want_stats_ = enable; if (connection_->is_connected()) connection_->WritePacket(TS_SERVICE_REQ_GETSTATS, &want_stats_, 1); } void TunsafeServiceClient::ResetStats() { connection_->WritePacket(TS_SERVICE_REQ_RESETSTATS, NULL, 0); } InternetBlockState TunsafeServiceClient::GetInternetBlockState(bool *is_activated) { if (is_activated) *is_activated = service_state_.internet_block_state_active; return (InternetBlockState)service_state_.internet_block_state; } void TunsafeServiceClient::SetInternetBlockState(InternetBlockState s) { uint8 v = (uint8)s; connection_->WritePacket(TS_SERVICE_REQ_SET_INTERNET_BLOCKSTATE, &v, 1); } void TunsafeServiceClient::SetServiceStartupFlags(uint32 flags) { connection_->WritePacket(TS_SERVICE_REQ_SET_STARTUP_FLAGS, (uint8*)&flags, 4); } LinearizedGraph *TunsafeServiceClient::GetGraph(int type) { if (type != last_graph_type_) { last_graph_type_ = type; connection_->WritePacket(TS_SERVICE_REQ_GET_GRAPH, (uint8*)&type, 4); } mutex_.Acquire(); LinearizedGraph *graph = cached_graph_; LinearizedGraph *new_graph = (graph && graph->graph_type == type) ? (LinearizedGraph*)memdup(graph, graph->total_size) : NULL; mutex_.Release(); return new_graph; } void TunsafeServiceClient::SendConfigurationProtocolPacket(uint32 identifier, const std::string &&message) { } std::string TunsafeServiceClient::GetConfigFileName() { mutex_.Acquire(); std::string rv = config_file_; mutex_.Release(); return rv; } bool TunsafeServiceClient::HandleMessage(int type, uint8 *data, size_t data_size) { switch (type) { case TS_SERVICE_MSG_STATE: if (data_size <= sizeof(service_state_) || data[data_size - 1]) return false; got_state_from_control_ = true; mutex_.Acquire(); config_file_.assign((char*)data + sizeof(service_state_), data_size - 1 - sizeof(service_state_)); memcpy(&service_state_, data, sizeof(service_state_)); memcpy(public_key_, service_state_.public_key, 32); is_started_ = service_state_.is_started; ipv4_ip_ = service_state_.ipv4_ip; mutex_.Release(); delegate_->OnStateChanged(); return true; case TS_SERVICE_MSG_LOGLINE: case TS_SERVICE_MSG_ERROR_REPLY: { if (data_size == 0) return false; char *s = my_strndup((char*)data, data_size); if (s) { delegate_->OnLogLine((const char **)&s); free(s); } return true; } case TS_SERVICE_MSG_STATS: { WgProcessorStats stats; if (data_size != sizeof(WgProcessorStats)) return false; memcpy(&stats, data, sizeof(WgProcessorStats)); delegate_->OnGetStats(stats); return true; } case TS_SERVICE_MSG_CLEARLOG: delegate_->OnClearLog(); return true; case TS_SERVICE_MSG_STATUS_CODE: if (data_size < 4) return false; status_ = (StatusCode)*(uint32*)data; delegate_->OnStatusCode(status_); return true; case TS_SERVICE_MSG_GRAPH: if (data_size < sizeof(LinearizedGraph) || data_size != *(uint32*)data) return false; LinearizedGraph *graph = (LinearizedGraph*)memdup(data, data_size); mutex_.Acquire(); std::swap(graph, cached_graph_); mutex_.Release(); free(graph); delegate_->OnGraphAvailable(); return true; } return false; } void TunsafeServiceClient::HandleNotify() { } PipeConnection::Delegate *TunsafeServiceClient::HandleNewConnection(PipeConnection *connection) { assert(connection == connection_); ServiceLoginMessage msg = {0}; msg.want_state_updates = true; msg.version = TUNSAFE_SERVICE_PROTOCOL_VERSION; connection_->WritePacket(TS_SERVICE_REQ_LOGIN, (uint8*)&msg, sizeof(msg)); if (want_stats_) connection_->WritePacket(TS_SERVICE_REQ_GETSTATS, &want_stats_, 1); return this; } void TunsafeServiceClient::HandleDisconnect() { status_ = TunsafeBackend::kErrorServiceLost; delegate_->OnStatusCode(TunsafeBackend::kErrorServiceLost); } void TunsafeServiceClient::Teardown() { pipe_manager_.StopThread(); } bool TunsafeServiceClient::SetTunAdapterName(const char *name) { // override which tun adapter we want to start return false; } TunsafeBackend *CreateTunsafeServiceClient(TunsafeBackend::Delegate *delegate) { TunsafeServiceClient *client = new TunsafeServiceClient(delegate); if (client && !client->Configure()) { delete client; client = NULL; } return client; }