OpenVPN 3 Core Library
Loading...
Searching...
No Matches
ovpnagent.cpp
Go to the documentation of this file.
1// OpenVPN -- An application to securely tunnel IP networks
2// over a single port, with support for SSL/TLS-based
3// session authentication and key exchange,
4// packet encryption, packet authentication, and
5// packet compression.
6//
7// Copyright (C) 2012- OpenVPN Inc.
8//
9// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
10//
11
12// OpenVPN agent for Windows
13
14#include <iostream>
15#include <string>
16#include <vector>
17#include <memory>
18#include <utility>
19
20#include <openvpn/io/io.hpp>
21
23// VERSION version can be passed on build command line
24#ifdef VERSION
25#define HTTP_SERVER_VERSION OPENVPN_STRINGIZE(VERSION)
26#else
27#define HTTP_SERVER_VERSION "0.1.0"
28#endif
29// OVPNAGENT_NAME can be passed on build command line.
30// Customized agent name is needed with purpose to install
31// few app with agents on one OS (e.g OC 3.0 and PT)
32#ifdef OVPNAGENT_NAME
33#define OVPNAGENT_NAME_STRING OPENVPN_STRINGIZE(OVPNAGENT_NAME)
34#else
35#define OVPNAGENT_NAME_STRING "ovpnagent"
36#endif
37
39
43#include <openvpn/common/rc.hpp>
63
65{
66 OPENVPN_LOG("OpenVPN Agent " HTTP_SERVER_VERSION " [" SSL_LIB_NAME "]");
67}
68
69using namespace openvpn;
70
72{
74 {
77#ifdef OPENVPN_AGENT_START_PROCESS
79#endif
81 }
82
83 std::string pipe_name;
84 std::string server_exe;
85 std::string omiclient_exe;
86 unsigned int n_pipe_instances;
87};
88
89class MySessionStats : public SessionStats
90{
91 public:
93
94 void error(const size_t err_type, const std::string *text = nullptr) override
95 {
97 }
98
99 std::string dump() const
100 {
101 std::ostringstream os;
102 os << "OpenVPN Agent Stats" << std::endl;
103 return os.str();
104 }
105};
106
107class MyListener : public WS::Server::Listener
108{
109 public:
111
112 MyListener(const MyConfig &config_arg,
113 openvpn_io::io_context &io_context,
114 const WS::Server::Config::Ptr &hconf,
117 : WS::Server::Listener(io_context, hconf, listen_list, client_factory),
118 config(config_arg),
123 {
124 }
125
127 const TunWin::Type tun_type,
128 bool allow_local_dns_resolvers)
129 {
130 if (!tun)
131 tun.reset(new TunWin::Setup(io_context_, tun_type, allow_local_dns_resolvers));
132 return Win::ScopedHANDLE(tun->get_handle(os));
133 }
134
136 {
137 return tun->get_adapter_state();
138 }
139
141 const std::wstring &openvpn_app_path,
142 DWORD client_process_id,
143 Stop *stop,
144 std::ostream &os,
145 TunWin::Type tun_type,
146 bool allow_local_dns_resolvers,
148 {
149 if (!tun)
150 tun.reset(new TunWin::Setup(io_context_, tun_type, allow_local_dns_resolvers));
151
152 if ((tun_type == TunWin::OvpnDco) && (tap.index != DWORD(-1)))
153 tun->set_adapter_state(tap);
154
155 tun->set_process_id(client_process_id);
156 auto th = tun->establish(tbc, openvpn_app_path, stop, os, ring_buffer);
157 // store VPN interface index to be able to exclude it
158 // when next time adding bypass route
159 vpn_interface_index = tun->vpn_interface_index();
160 return Win::ScopedHANDLE(th);
161 }
162
163 // return true if we did any work
164 bool destroy_tun(std::ostream &os)
165 {
166 bool ret = false;
167 try
168 {
169 // close the remote tap handle in the client process
170 if (client_process.is_open() && !remote_tap_handle_hex.empty())
171 {
172 ret = true;
173 const HANDLE remote_tap_handle = BufHex::parse<HANDLE>(remote_tap_handle_hex, "remote TAP handle");
174 Win::ScopedHANDLE local_tap_handle; // dummy handle, immediately closed after duplication
175 if (::DuplicateHandle(client_process.native_handle(),
176 remote_tap_handle,
177 GetCurrentProcess(),
178 local_tap_handle.ref(),
179 0,
180 FALSE,
181 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
182 {
183 os << "destroy_tun: no client confirm, DuplicateHandle (close) succeeded" << std::endl;
184 }
185 else
186 {
187 const Win::LastError err;
188 os << "destroy_tun: no client confirm, DuplicateHandle (close) failed: " << err.message() << std::endl;
189 }
190 }
191 }
192 catch (const std::exception &e)
193 {
194 os << "destroy_tun: exception in remote tap handle close: " << e.what() << std::endl;
195 }
196
197 try
198 {
200
201 // undo the effects of establish_tun
202 if (tun)
203 {
204 ret = true;
205 tun->destroy(os);
206 }
207 }
208 catch (const std::exception &e)
209 {
210 os << "destroy_tun: exception in tun teardown: " << e.what() << std::endl;
211 }
212
213 try
214 {
215 tun.reset();
216 remote_tap_handle_hex.clear();
217 client_process.close();
218 client_confirm_event.close();
220 }
221 catch (const std::exception &e)
222 {
223 os << "destroy_tun: exception in cleanup: " << e.what() << std::endl;
224 }
225 vpn_interface_index = DWORD(-1);
226 return ret;
227 }
228
230 {
231 std::ostringstream os;
233 OPENVPN_LOG_NTNL("TUN CLOSE (exit)\n"
234 << os.str());
235 }
236
238 {
239 client_process.close();
240 client_process.assign(proc.release());
241
242 // special failsafe to destroy tun in case client crashes without closing it
243 client_process.async_wait([self = Ptr(this)](const openvpn_io::error_code &error)
244 {
245 if (!error)
246 {
247 {
248 std::ostringstream os;
249 self->remove_cmds_bypass_hosts.execute(os);
250 self->remove_cmds_bypass_hosts.clear();
251 OPENVPN_LOG_NTNL("remove bypass route (failsafe)\n" << os.str());
252 }
253
254 if (self->tun)
255 {
256 std::ostringstream os;
257 self->destroy_tun(os);
258 OPENVPN_LOG_NTNL("TUN CLOSE (failsafe)\n" << os.str());
259 }
260 } });
261 }
262
263 void set_client_confirm_event(const std::string &confirm_handle_hex)
264 {
265 client_confirm_event.close();
266
267 const HANDLE remote_event = BufHex::parse<HANDLE>(confirm_handle_hex, "confirm event handle");
268 HANDLE event_handle;
269 if (!::DuplicateHandle(get_client_process(),
270 remote_event,
271 GetCurrentProcess(),
272 &event_handle,
273 0,
274 FALSE,
275 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
276 {
277 const Win::LastError err;
278 OPENVPN_THROW_EXCEPTION("set_client_confirm_event: DuplicateHandle failed: " << err.message());
279 }
280 client_confirm_event.assign(event_handle);
281
282 // Check if the event is okay
283 {
284 const DWORD status = ::WaitForSingleObject(client_confirm_event.native_handle(), 0);
285 const Win::LastError err;
286 switch (status)
287 {
288 case WAIT_OBJECT_0: // acceptable status
289 case WAIT_TIMEOUT: // acceptable status
290 break;
291 case WAIT_ABANDONED:
292 throw Exception("set_client_confirm_event: confirm event is abandoned");
293 default:
294 OPENVPN_THROW_EXCEPTION("set_client_confirm_event: WaitForSingleObject failed: " << err.message());
295 }
296 }
297
298 // When the client signals the client_confirm event, it means
299 // that the client has taken ownership of the TAP device HANDLE,
300 // so we locally release ownership by clearing remote_tap_handle_hex,
301 // effectively preventing the cross-process release of TAP device
302 // HANDLE in destroy_tun() above.
303 client_confirm_event.async_wait([self = Ptr(this)](const openvpn_io::error_code &error)
304 {
305 if (!error)
306 {
307 self->remote_tap_handle_hex.clear();
308 OPENVPN_LOG_STRING("TUN CONFIRM\n");
309 } });
310 }
311
312 void set_client_destroy_event(const std::string &event_handle_hex)
313 {
315
316 // Move the remote event HANDLE (already duplicated in remote process)
317 // to local process.
318 const HANDLE remote_event = BufHex::parse<HANDLE>(event_handle_hex, "destroy event handle");
319 HANDLE event_handle;
320 if (!::DuplicateHandle(get_client_process(),
321 remote_event,
322 GetCurrentProcess(),
323 &event_handle,
324 0,
325 FALSE,
326 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
327 {
328 const Win::LastError err;
329 OPENVPN_THROW_EXCEPTION("set_client_destroy_event: DuplicateHandle failed: " << err.message());
330 }
331 client_destroy_event.assign(event_handle);
332
333 // Check if the event is already signaled, or has some other error
335
336 // normal event-based tun close processing
337 client_destroy_event.async_wait([self = Ptr(this)](const openvpn_io::error_code &error)
338 {
339 if (!error)
340 {
341 {
342 std::ostringstream os;
343 self->remove_cmds_bypass_hosts.execute(os);
344 self->remove_cmds_bypass_hosts.clear();
345 OPENVPN_LOG_NTNL("remove bypass route (event)\n" << os.str());
346 }
347
348 if (self->tun)
349 {
350 std::ostringstream os;
351 self->destroy_tun(os);
352 OPENVPN_LOG_NTNL("TUN CLOSE (event)\n" << os.str());
353 }
354 } });
355 }
356
358 {
359 if (!client_process.is_open())
360 throw Exception("no client process");
361 return client_process.native_handle();
362 }
363
364 void set_remote_tap_handle_hex(const HANDLE tap_handle)
365 {
367 }
368
369 const std::string &get_remote_tap_handle_hex()
370 {
372 }
373
375 {
376 ring_buffer.reset(ring_buffer_arg);
377 }
378
379 void add_bypass_route(const std::string &host, bool ipv6)
380 {
381 std::ostringstream os;
384
385 ActionList add_cmds;
386 // we might have broken VPN connection up, so we must
387 // exclude VPN interface whe searching for the best gateway
388 ADDRESS_FAMILY af = ipv6 ? AF_INET6 : AF_INET;
391 add_cmds.execute(os);
392
393 OPENVPN_LOG(os.str());
394 }
395
396#ifdef OPENVPN_AGENT_START_PROCESS
397 void start_openvpn_process(HANDLE client_pipe,
398 const std::string &config_file,
399 const std::string &config_dir,
400 const std::string &exit_event_name,
401 const std::string &management_host,
402 const std::string &management_password,
403 const int management_port,
404 const std::string &log,
405 const bool log_append)
406 {
407 // impersonate pipe client
408 Win::NamedPipeImpersonate impersonate{client_pipe};
409
410 {
411 // create primary token from impersonation token
412 HANDLE imp_token = NULL, pri_token = NULL;
413 BOOL res = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, FALSE, &imp_token);
414 if (res == 0)
415 {
416 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
417 OPENVPN_THROW_EXCEPTION("failed to open thread token: " << err.message());
418 }
419 res = DuplicateTokenEx(imp_token, 0, NULL, SECURITY_IMPERSONATION_LEVEL::SecurityAnonymous, TokenPrimary, &pri_token);
420 if (res == 0)
421 {
422 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
423 OPENVPN_THROW_EXCEPTION("failed to duplicate token: " << err.message());
424 }
425
426 // create pipe which is used to write password to openvpn process's stdin
427 HANDLE stdin_read = NULL, stdin_write = NULL;
428 SECURITY_ATTRIBUTES inheritable = {sizeof(inheritable), NULL, TRUE};
429 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
430 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
431 {
432 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
433 OPENVPN_THROW_EXCEPTION("failed to set up pipe: " << err.message());
434 }
435
436 // create command line for openvpn process
437 std::ostringstream ss;
438 ss << "client --config \"" << config_dir << "\\" << config_file << "\" --exit-event-name "
439 << exit_event_name << " --auth-retry interact --management " << management_host << " "
440 << management_port << " stdin --management-query-passwords --management-hold "
441 << "--log"
442 << (log_append ? "-append \"" : " \"") << log << "\"";
443 std::string cmd = ss.str();
444 std::unique_ptr<char[]> buf(new char[cmd.length() + 1]);
445 strcpy(buf.get(), cmd.c_str());
446
447 // OPENVPN_LOG("Launching omiclient: " << config.omiclient_exe.c_str() << " " << buf.get());
448
449 STARTUPINFO startup_info = {0};
450 startup_info.cb = sizeof(startup_info);
451 startup_info.dwFlags = STARTF_USESTDHANDLES;
452 startup_info.hStdInput = stdin_read;
453
454 // create openvpn process
455 PROCESS_INFORMATION proc_info;
456 ZeroMemory(&proc_info, sizeof(proc_info));
457 res = CreateProcessAsUser(pri_token,
458 config.omiclient_exe.c_str(),
459 buf.get(),
460 NULL,
461 NULL,
462 TRUE,
463 CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
464 0,
465 0,
466 &startup_info,
467 &proc_info);
468 if (res == 0)
469 {
470 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
471 OPENVPN_THROW_EXCEPTION("failed to create openvpn process: " << err.message());
472 }
473 CloseHandle(proc_info.hProcess);
474 CloseHandle(proc_info.hThread);
475
476 // write management password to process's stdin
477 DWORD written;
478 if (!is_safe_conversion<DWORD>(management_password.length()))
479 return;
480 WriteFile(stdin_write, management_password.c_str(), static_cast<DWORD>(management_password.length()), &written, NULL);
481 }
482 }
483#endif
484
487
489
490 private:
491 bool allow_client(AsioPolySock::Base &sock) override
492 {
493 AsioPolySock::NamedPipe *np = dynamic_cast<AsioPolySock::NamedPipe *>(&sock);
494 if (np)
495 {
496 Win::NamedPipePeerInfoClient npinfo(np->handle.native_handle());
497 const std::string client_exe = wstring::to_utf8(npinfo.exe_path);
498 OPENVPN_LOG("connection from " << client_exe);
499 if (Agent::valid_pipe(client_exe, config.server_exe))
500 return true;
501 OPENVPN_LOG(client_exe << " not recognized as a valid client");
502 }
503 else
504 OPENVPN_LOG("only named pipe clients are allowed");
505 return false;
506 }
507
509 openvpn_io::windows::object_handle client_process;
510 openvpn_io::windows::object_handle client_confirm_event;
513 openvpn_io::io_context &io_context_;
514
515 // with persist tunnel and redirect-gw we must exclude
516 // VPN interface when searching for best gateway when
517 // adding bypass route for the next remote
518 DWORD vpn_interface_index = DWORD(-1);
519};
520
522{
523 public:
525
530
531 virtual ~MyClientInstance() = default;
532
533 private:
534 void generate_reply(const Json::Value &jout)
535 {
536 out = buf_from_string(jout.toStyledString());
537
540 ci.type = "application/json";
541 ci.length = out->size();
544 }
545
546 void http_request_received() override
547 {
548 // alloc output buffer
549 std::ostringstream os;
550
551 try
552 {
553 const HANDLE client_pipe = get_client_pipe();
554 const DWORD client_pid = get_client_pid(client_pipe);
555 const std::wstring client_exe = get_client_exe(client_pipe);
556
557 const HTTP::Request &req = request();
558 OPENVPN_LOG("HTTP request received from " << sock->remote_endpoint_str() << '\n'
559 << req.to_string());
560
561 // get content-type
562 const std::string content_type = req.headers.get_value_trim("content-type");
563
564 if (req.method == "POST")
565 {
566 // verify correct content-type
567 if (string::strcasecmp(content_type, "application/json"))
568 throw Exception("bad content-type");
569
570 // parse the json dict
571 const Json::Value root = json::parse(in.to_string(), "JSON request");
572 if (!root.isObject())
573 throw Exception("json parse error: top level json object is not a dictionary");
574
575 if (req.uri == "/tun-open")
576 {
577 // destroy previous instance
578 if (parent()->destroy_tun(os))
579 {
580 os << "Destroyed previous TAP instance" << std::endl;
581 ::Sleep(1000);
582 }
583
584 {
585 // remember the client process that sent the request
586 ULONG pid = json::get_uint_optional(root, "pid", 0);
587 const std::string confirm_event_hex = json::get_string(root, "confirm_event");
588 const std::string destroy_event_hex = json::get_string(root, "destroy_event");
589
590 Win::NamedPipeImpersonate impersonate(client_pipe);
591
592 parent()->set_client_process(get_client_process(client_pipe, pid));
593 parent()->set_client_confirm_event(confirm_event_hex);
594 parent()->set_client_destroy_event(destroy_event_hex);
595 }
596
597 bool allow_local_dns_resolvers = json::get_bool_optional(root, "allow_local_dns_resolvers");
598 Win::ScopedHANDLE th(parent()->tun_get_handle(os, TunWin::OvpnDco, allow_local_dns_resolvers));
599 {
600 // duplicate the TAP handle into the client process
601 Win::NamedPipeImpersonate impersonate(client_pipe);
603 }
604
605 // build JSON return dictionary
606 const std::string log_txt = string::remove_blanks(os.str());
607 Json::Value jout(Json::objectValue);
608 jout["log_txt"] = log_txt;
609 jout["tap_handle_hex"] = parent()->get_remote_tap_handle_hex();
610
611 auto tap = parent()->get_adapter_state();
612 jout["adapter_guid"] = tap.guid;
613 jout["adapter_index"] = Json::Int(tap.index);
614 jout["adapter_name"] = tap.name;
615
616 OPENVPN_LOG_NTNL("TUN SETUP\n"
617 << log_txt);
618
619 generate_reply(jout);
620 }
621 else if (req.uri == "/tun-setup")
622 {
623 // get PID
624 ULONG pid = json::get_uint_optional(root, "pid", 0);
625
626 TunWin::Type tun_type;
627 switch (json::get_int_optional(root, "tun_type", TunWin::TapWindows6))
628 {
629 case TunWin::Wintun:
630 tun_type = TunWin::Wintun;
631 break;
632 case TunWin::OvpnDco:
633 tun_type = TunWin::OvpnDco;
634 break;
636 default:
637 tun_type = TunWin::TapWindows6;
638 break;
639 }
640
641 bool allow_local_dns_resolvers = json::get_bool_optional(root, "allow_local_dns_resolvers");
642 // parse JSON data into a TunBuilderCapture object
644 tbc->validate();
645
646 // destroy previous instance
647 if (tun_type != TunWin::OvpnDco && parent()->destroy_tun(os))
648 {
649 os << "Destroyed previous TAP instance" << std::endl;
650 ::Sleep(1000);
651 }
652
653 // pre-establish impersonation
654 {
655 Win::NamedPipeImpersonate impersonate(client_pipe);
656
657 // remember the client process that sent the request
658 parent()->set_client_process(get_client_process(client_pipe, pid));
659
660 // save destroy event
661 const std::string destroy_event_hex = json::get_string(root, "destroy_event");
662 parent()->set_client_destroy_event(destroy_event_hex);
663
664 if (tun_type != TunWin::OvpnDco)
665 {
666 // get remote event handle for tun object confirmation
667 const std::string confirm_event_hex = json::get_string(root, "confirm_event");
668
669 // save the confirm event
670 parent()->set_client_confirm_event(confirm_event_hex);
671 }
672 }
673
674 if (tun_type == TunWin::Wintun)
675 {
678 json::get_string(root, "send_ring_hmem"),
679 json::get_string(root, "receive_ring_hmem"),
680 json::get_string(root, "send_ring_tail_moved"),
681 json::get_string(root, "receive_ring_tail_moved")));
682 }
683
685 if (tun_type == TunWin::OvpnDco)
686 {
687 tap.guid = json::get_string(root, "adapter_guid");
688 tap.index = (DWORD)json::get_int(root, "adapter_index");
689 tap.name = json::get_string(root, "adapter_name");
690 }
691
692 // establish the tun setup object
693 Win::ScopedHANDLE tap_handle(parent()->establish_tun(*tbc, client_exe, client_pid, nullptr, os, tun_type, allow_local_dns_resolvers, tap));
694
695 // post-establish impersonation
696 {
697 Win::NamedPipeImpersonate impersonate(client_pipe);
698
699 // duplicate the TAP handle into the client process
700 parent()->set_remote_tap_handle_hex(tap_handle());
701 }
702
703 // build JSON return dictionary
704 const std::string log_txt = string::remove_blanks(os.str());
705 Json::Value jout(Json::objectValue);
706 jout["log_txt"] = log_txt;
707 jout["tap_handle_hex"] = parent()->get_remote_tap_handle_hex();
708 OPENVPN_LOG_NTNL("TUN SETUP\n"
709 << log_txt);
710
711 generate_reply(jout);
712 }
713 else if (req.uri == "/add-bypass-route")
714 {
715 ULONG pid = json::get_uint_optional(root, "pid", 0);
716 bool ipv6 = json::get_bool(root, "ipv6");
717 const std::string host = json::get_string(root, "host");
718
719 // pre-establish impersonation
720 {
721 Win::NamedPipeImpersonate impersonate(client_pipe);
722
723 // remember the client process that sent the request
724 parent()->set_client_process(get_client_process(client_pipe, pid));
725 }
726
728
729 Json::Value jout(Json::objectValue);
730
731 generate_reply(jout);
732 }
733#ifdef OPENVPN_AGENT_START_PROCESS
734 else if (req.uri == "/start")
735 {
736 const std::string config_file = json::get_string(root, "config_file");
737 const std::string config_dir = json::get_string(root, "config_dir");
738 const std::string exit_event_name = json::get_string(root, "exit_event_name");
739 const std::string management_host = json::get_string(root, "management_host");
740 const std::string management_password = json::get_string(root, "management_password") + "\n";
741 const int management_port = json::get_int(root, "management_port");
742 const std::string log = json::get_string(root, "log");
743 const bool log_append = json::get_int(root, "log-append") == 1;
744
745 parent()->start_openvpn_process(client_pipe,
746 config_file,
747 config_dir,
748 exit_event_name,
749 management_host,
750 management_password,
751 management_port,
752 log,
753 log_append);
754
755 Json::Value jout(Json::objectValue);
756 generate_reply(jout);
757 }
758#endif
759 else
760 {
761 OPENVPN_LOG("PAGE NOT FOUND");
762 out = buf_from_string("page not found\n");
765 ci.type = "text/plain";
766 ci.length = out->size();
768 }
769 }
770 }
771 catch (const std::exception &e)
772 {
773 if (parent()->destroy_tun(os))
774 os << "Destroyed previous TAP instance due to exception" << std::endl;
775
776 const std::string error_msg = string::remove_blanks(os.str() + e.what() + '\n');
777 OPENVPN_LOG_NTNL("EXCEPTION\n"
778 << error_msg);
779
780 out = buf_from_string(error_msg);
783 ci.type = "text/plain";
784 ci.length = out->size();
786 }
787 }
788
790 {
791 if (buf.defined())
792 in.emplace_back(BufferAllocatedRc::Create(std::move(buf)));
793 }
794
796 {
798 ret.swap(out);
799 return ret;
800 }
801
802 bool http_out_eof() override
803 {
804 // OPENVPN_LOG("HTTP output EOF");
805 return true;
806 }
807
808 bool http_stop(const int status, const std::string &description) override
809 {
810 if (status != WS::Server::Status::E_SUCCESS)
811 {
812 OPENVPN_LOG("INSTANCE STOP : " << WS::Server::Status::error_str(status) << " : " << description);
813 return false;
814 }
815 else
816 return true;
817 }
818
819 HANDLE get_client_pipe() const
820 {
821 AsioPolySock::NamedPipe *np = dynamic_cast<AsioPolySock::NamedPipe *>(sock.get());
822 if (!np)
823 throw Exception("only named pipe clients are allowed");
824 return np->handle.native_handle();
825 }
826
833 DWORD get_client_pid(const HANDLE client_pipe)
834 {
835 return NamedPipePeerInfo::get_pid(client_pipe, true);
836 }
837
838 std::wstring get_client_exe(const HANDLE client_pipe)
839 {
840 Win::NamedPipePeerInfoClient npinfo(client_pipe);
841 return npinfo.exe_path;
842 }
843
844 Win::ScopedHANDLE get_client_process(const HANDLE pipe, ULONG pid_hint) const
845 {
846 pid_hint = Win::NamedPipePeerInfo::get_pid(pipe, true);
847 if (!pid_hint)
848 throw Exception("cannot determine client PID");
849 return Win::NamedPipePeerInfo::get_process(pid_hint, false);
850 }
851
853 {
854 return static_cast<MyListener *>(get_parent());
855 }
856
859};
860
862{
863 public:
865
870};
871
873{
874 public:
876 : Win::Service(config())
877 {
878 }
879
880 void service_work(DWORD argc, LPWSTR *argv) override
881 {
882 if (is_service())
883 {
884 try
885 {
886 log.reset(new Win::LogFile(log_fn(), "", false));
887 }
888 catch (const std::exception &e)
889 {
890 std::cerr << e.what() << std::endl;
891 }
892 }
893 if (!log)
894 log.reset(new LogBaseSimple());
895
896 io_context.reset(new openvpn_io::io_context(1)); // concurrency hint=1
897
898 log_version();
899
900 MyConfig conf;
901
903 TunWin::NRPT::delete_rules(0); // remove stale NRPT rules
904
906 hconf->http_server_id = OVPNAGENT_NAME_STRING "/" HTTP_SERVER_VERSION;
907 hconf->frame = frame_init_simple(2048);
908 hconf->stats.reset(new MySessionStats);
909
910 // DACL string for creating named pipe
911 hconf->sddl_string = "D:" // discretionary ACL
912 "(D;OICI;GA;;;S-1-5-2)" // deny all access for network users
913 "(A;OICI;GA;;;S-1-5-32-544)" // allow full access to Admin group
914 "(A;OICI;GA;;;S-1-5-18)" // allow full access to Local System account
915 "(D;OICI;0x4;;;S-1-1-0)" // deny FILE_CREATE_PIPE_INSTANCE for Everyone
916 "(A;OICI;GRGW;;;S-1-5-11)" // allow read/write access for authenticated users
917 "(A;OICI;GRGW;;;S-1-5-32-546)" // allow read/write access for built-in guest account
918 ;
919
920 Listen::List ll;
921 const unsigned int n_pipe_instances = 4;
922 for (unsigned int i = 0; i < n_pipe_instances; ++i)
923 {
924 Listen::Item li;
925 li.directive = "http-listen";
926 li.addr = conf.pipe_name;
929 li.n_threads = n_pipe_instances;
930 ll.push_back(std::move(li));
931 }
932
934 listener.reset(new MyListener(conf, *io_context, hconf, ll, factory));
935 listener->start();
936
938
939 io_context->run();
940 }
941
942 // Called by service control manager in another thread
943 // to signal the service_work() method to exit.
944 void service_stop() override
945 {
946 openvpn_io::post(*io_context, [this]()
947 {
948 if (listener)
949 {
951 listener->stop();
952 } });
953 }
954
955 private:
956 static Config config()
957 {
958 Config c;
960 c.display_name = "OpenVPN Agent " OVPNAGENT_NAME_STRING;
961 c.autostart = true;
962 c.restart_on_fail = true;
963 return c;
964 }
965
966 static std::string log_fn()
967 {
968 const std::string modname = Win::module_name_utf8();
969 const std::string moddir = path::dirname(modname);
970 const std::string fn = path::join(moddir, "agent.log");
971 return fn;
972 }
973
974 std::unique_ptr<openvpn_io::io_context> io_context;
977};
978
980
981int main(int argc, char *argv[])
982{
983 int ret = 0;
984
985 // process-wide initialization
987
988 try
989 {
990 MyService serv;
991 if (argc >= 2)
992 {
993 const std::string arg = argv[1];
994 if (arg == "run")
995 serv.service_work(0, nullptr);
996 else if (arg == "install")
997 serv.install();
998 else if (arg == "remove")
999 serv.remove();
1000 else if (arg == "modname")
1001 std::wcout << Win::module_name() << std::endl;
1002 else if (arg == "help")
1003 {
1004 std::cout << "usage: ovpnagent [options]" << std::endl;
1005 std::cout << " run -- run in foreground (for debugging)" << std::endl;
1006 std::cout << " install -- install as service" << std::endl;
1007 std::cout << " remove -- uninstall" << std::endl;
1008 std::cout << " modname -- show module name" << std::endl;
1009 std::cout << " help -- show help message" << std::endl;
1010 std::cout << " [default] -- start as service" << std::endl;
1011 }
1012 else
1013 {
1014 std::cout << "unrecognized option, use 'help' for more info" << std::endl;
1015 ret = 2;
1016 }
1017 }
1018 else
1019 serv.start();
1020 }
1021 catch (const std::exception &e)
1022 {
1023 std::cout << "ovpnagent: " << e.what() << std::endl;
1024 ret = 1;
1025 }
1026
1027 return ret;
1028}
WS::Server::Listener::Client::Ptr new_client(WS::Server::Listener::Client::Initializer &ci) override
RCPtr< MyClientFactory > Ptr
DWORD get_client_pid(const HANDLE client_pipe)
Get the named pipe client process id.
MyListener * parent()
void http_content_in(BufferAllocated &buf) override
bool http_stop(const int status, const std::string &description) override
void generate_reply(const Json::Value &jout)
MyClientInstance(WS::Server::Listener::Client::Initializer &ci)
bool http_out_eof() override
void http_request_received() override
BufferPtr http_content_out() override
std::wstring get_client_exe(const HANDLE client_pipe)
HANDLE get_client_pipe() const
virtual ~MyClientInstance()=default
RCPtr< MyClientInstance > Ptr
Win::ScopedHANDLE get_client_process(const HANDLE pipe, ULONG pid_hint) const
MyListener(const MyConfig &config_arg, openvpn_io::io_context &io_context, const WS::Server::Config::Ptr &hconf, const Listen::List &listen_list, const WS::Server::Listener::Client::Factory::Ptr &client_factory)
void set_client_destroy_event(const std::string &event_handle_hex)
bool allow_client(AsioPolySock::Base &sock) override
TunWin::Util::TapNameGuidPair get_adapter_state()
Win::ScopedHANDLE tun_get_handle(std::ostream &os, const TunWin::Type tun_type, bool allow_local_dns_resolvers)
Win::ScopedHANDLE establish_tun(const TunBuilderCapture &tbc, const std::wstring &openvpn_app_path, DWORD client_process_id, Stop *stop, std::ostream &os, TunWin::Type tun_type, bool allow_local_dns_resolvers, TunWin::Util::TapNameGuidPair tap)
const std::string & get_remote_tap_handle_hex()
const MyConfig & config
void set_client_confirm_event(const std::string &confirm_handle_hex)
void destroy_tun_exit()
void set_remote_tap_handle_hex(const HANDLE tap_handle)
ActionList remove_cmds_bypass_hosts
DWORD vpn_interface_index
openvpn_io::io_context & io_context_
openvpn_io::windows::object_handle client_confirm_event
void set_client_process(Win::ScopedHANDLE &&proc)
bool destroy_tun(std::ostream &os)
void destroy_tun(std::ostream &os)
void add_bypass_route(const std::string &host, bool ipv6)
TunWin::Setup::Ptr tun
HANDLE get_client_process()
TunMac::Setup::Ptr tun
std::string remote_tap_handle_hex
TunWin::RingBuffer::Ptr ring_buffer
RCPtr< MyListener > Ptr
openvpn_io::windows::object_handle client_process
void assign_ring_buffer(TunWin::RingBuffer *ring_buffer_arg)
CancelableHandle client_destroy_event
void service_stop() override
void service_work(DWORD argc, LPWSTR *argv) override
LogBase::Ptr log
MyListener::Ptr listener
std::unique_ptr< openvpn_io::io_context > io_context
static Config config()
static std::string log_fn()
void error(const size_t err_type, const std::string *text=nullptr) override
Definition ovpnagent.cpp:94
RCPtr< MySessionStats > Ptr
Definition ovpnagent.cpp:92
std::string dump() const
Definition ovpnagent.cpp:99
virtual std::unordered_set< std::string > execute(std::ostream &os)
Executes a sequence of actions and returns marks of failed actions.
Definition action.hpp:98
static std::string named_pipe_path()
static bool valid_pipe(const std::string &client_exe, const std::string &server_exe)
Wrapper for an asynchronous handle supporting cancellation and closure.
void check_is_already_signalled()
Checks if the handle's event is already signaled.
void async_wait(Handler &&handler)
Initiates an asynchronous wait on the handle.
void cancel_and_close()
Cancels and closes the handle if not already closed.
void assign(HANDLE handle)
Assigns a native Windows handle.
bool defined() const
Returns true if the buffer is not empty.
Definition buffer.hpp:1224
The smart pointer class.
Definition rc.hpp:119
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
void swap(RCPtr &rhs) noexcept
swaps the contents of two RCPtr<T>
Definition rc.hpp:311
T * get() const noexcept
Returns the raw pointer to the object T, or nullptr.
Definition rc.hpp:321
static Ptr Create(ArgsT &&...args)
Creates a new instance of RcEnable with the given arguments.
Definition make_rc.hpp:43
void validate() const
Validates the configuration of the tunnel.
Definition capture.hpp:946
static TunBuilderCapture::Ptr from_json(const Json::Value &root)
Creates a TunBuilderCapture instance from a JSON representation.
Definition capture.hpp:1049
static void delete_rules(DWORD process_id)
Remove our NRPT rules from the registry.
Definition nrpt.hpp:157
static void add_bypass_route(const Util::BestGateway &gw, const std::string &route, bool ipv6, ActionList &add_cmds, ActionList &remove_cmds_bypass_gw)
Definition tunsetup.hpp:258
void generate_reply_headers(ContentInfo ci)
Definition httpserv.hpp:293
const HTTP::Request & request() const
Definition httpserv.hpp:331
openvpn_io::io_context & io_context
Definition httpserv.hpp:386
openvpn_io::io_context & io_context
Listener(openvpn_io::io_context &io_context_arg, const Config::Ptr &config_arg, const L &listen_item_or_list, const Client::Factory::Ptr &client_factory_arg)
Definition httpserv.hpp:777
Client::Factory::Ptr client_factory
bool is_service() const
Definition winsvc.hpp:54
void report_service_running()
Definition winsvc.hpp:182
#define OPENVPN_SIMPLE_EXCEPTION(C)
Definition exception.hpp:75
#define OPENVPN_THROW_EXCEPTION(stuff)
#define OPENVPN_LOG_NTNL(args)
#define OPENVPN_LOG(args)
#define OPENVPN_LOG_STRING(str)
const char * name(const size_t type)
Definition error.hpp:117
std::string send_handle(const HANDLE handle, const HANDLE remote_process)
std::string module_name_utf8()
Definition modname.hpp:41
std::string omiclient_path()
Definition modname.hpp:46
std::wstring module_name()
Definition modname.hpp:29
int get_int(const Json::Value &root, const NAME &name, const TITLE &title)
bool get_bool_optional(const Json::Value &root, const NAME &name, const bool default_value=false)
bool get_bool(const Json::Value &root, const NAME &name, const TITLE &title)
unsigned int get_uint_optional(const Json::Value &root, const NAME &name, const unsigned int default_value, const TITLE &title)
const Json::Value & get_dict(const Json::Value &root, const NAME &name, const bool optional, const TITLE &title)
int get_int_optional(const Json::Value &root, const NAME &name, const int default_value, const TITLE &title)
Json::Value parse(const std::string &str, const TITLE &title)
std::string get_string(const Json::Value &root, const NAME &name, const TITLE &title)
std::string dirname(const std::string &path)
Definition path.hpp:91
std::string join(const std::string &p1, const std::string &p2)
Definition path.hpp:180
std::string remove_blanks(const std::string &str)
Definition string.hpp:620
int strcasecmp(const char *s1, const char *s2)
Definition string.hpp:29
Frame::Ptr frame_init_simple(const size_t payload)
BufferPtr buf_from_string(const std::string &str)
Definition bufstr.hpp:46
std::string server_exe
Definition ovpnagent.cpp:84
unsigned int n_pipe_instances
Definition ovpnagent.cpp:86
std::string omiclient_exe
Definition ovpnagent.cpp:85
std::string pipe_name
Definition ovpnagent.cpp:83
std::string to_string() const
Definition buflist.hpp:72
std::string get_value_trim(const std::string &key) const
Definition header.hpp:84
std::string to_string() const
Definition request.hpp:44
unsigned int n_threads
static std::string error_str(const size_t status)
Definition httpserv.hpp:102
static Win::ScopedHANDLE get_process(const ULONG pid, const bool limited)
Definition npinfo.hpp:57
static ULONG get_pid(const HANDLE np_handle, const bool client)
Definition npinfo.hpp:113
static void allow_client_query()
Definition npinfo.hpp:76
const TunBuilderCapture::Ptr tbc(new TunBuilderCapture)
remote_address ipv6
proxy_host_port host
std::string ret
std::ostringstream os
int main(int argc, char *argv[])
void log_version()
Definition ovpnagent.cpp:64
#define HTTP_SERVER_VERSION
Definition ovpnagent.cpp:27
#define OVPNAGENT_NAME_STRING
Definition ovpnagent.cpp:35