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>
62
64{
65 OPENVPN_LOG("OpenVPN Agent " HTTP_SERVER_VERSION " [" SSL_LIB_NAME "]");
66}
67
68using namespace openvpn;
69
71{
73 {
76#ifdef OPENVPN_AGENT_START_PROCESS
78#endif
80 }
81
82 std::string pipe_name;
83 std::string server_exe;
84 std::string omiclient_exe;
85 unsigned int n_pipe_instances;
86};
87
88class MySessionStats : public SessionStats
89{
90 public:
92
93 void error(const size_t err_type, const std::string *text = nullptr) override
94 {
96 }
97
98 std::string dump() const
99 {
100 std::ostringstream os;
101 os << "OpenVPN Agent Stats" << std::endl;
102 return os.str();
103 }
104};
105
106class MyListener : public WS::Server::Listener
107{
108 public:
110
111 MyListener(const MyConfig &config_arg,
112 openvpn_io::io_context &io_context,
113 const WS::Server::Config::Ptr &hconf,
116 : WS::Server::Listener(io_context, hconf, listen_list, client_factory),
117 config(config_arg),
122 {
123 }
124
126 const TunWin::Type tun_type,
127 bool allow_local_dns_resolvers)
128 {
129 if (!tun)
130 tun.reset(new TunWin::Setup(io_context_, tun_type, allow_local_dns_resolvers));
131 return Win::ScopedHANDLE(tun->get_handle(os));
132 }
133
135 {
136 return tun->get_adapter_state();
137 }
138
140 const std::wstring &openvpn_app_path,
141 DWORD client_process_id,
142 Stop *stop,
143 std::ostream &os,
144 TunWin::Type tun_type,
145 bool allow_local_dns_resolvers,
147 {
148 if (!tun)
149 tun.reset(new TunWin::Setup(io_context_, tun_type, allow_local_dns_resolvers));
150
151 if ((tun_type == TunWin::OvpnDco) && (tap.index != DWORD(-1)))
152 tun->set_adapter_state(tap);
153
154 tun->set_process_id(client_process_id);
155 auto th = tun->establish(tbc, openvpn_app_path, stop, os, ring_buffer);
156 // store VPN interface index to be able to exclude it
157 // when next time adding bypass route
158 vpn_interface_index = tun->vpn_interface_index();
159 return Win::ScopedHANDLE(th);
160 }
161
162 // return true if we did any work
163 bool destroy_tun(std::ostream &os)
164 {
165 bool ret = false;
166 try
167 {
168 // close the remote tap handle in the client process
169 if (client_process.is_open() && !remote_tap_handle_hex.empty())
170 {
171 ret = true;
172 const HANDLE remote_tap_handle = BufHex::parse<HANDLE>(remote_tap_handle_hex, "remote TAP handle");
173 Win::ScopedHANDLE local_tap_handle; // dummy handle, immediately closed after duplication
174 if (::DuplicateHandle(client_process.native_handle(),
175 remote_tap_handle,
176 GetCurrentProcess(),
177 local_tap_handle.ref(),
178 0,
179 FALSE,
180 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
181 {
182 os << "destroy_tun: no client confirm, DuplicateHandle (close) succeeded" << std::endl;
183 }
184 else
185 {
186 const Win::LastError err;
187 os << "destroy_tun: no client confirm, DuplicateHandle (close) failed: " << err.message() << std::endl;
188 }
189 }
190 }
191 catch (const std::exception &e)
192 {
193 os << "destroy_tun: exception in remote tap handle close: " << e.what() << std::endl;
194 }
195
196 try
197 {
199
200 // undo the effects of establish_tun
201 if (tun)
202 {
203 ret = true;
204 tun->destroy(os);
205 }
206 }
207 catch (const std::exception &e)
208 {
209 os << "destroy_tun: exception in tun teardown: " << e.what() << std::endl;
210 }
211
212 try
213 {
214 tun.reset();
215 remote_tap_handle_hex.clear();
216 client_process.close();
217 client_confirm_event.close();
218 client_destroy_event.close();
219 }
220 catch (const std::exception &e)
221 {
222 os << "destroy_tun: exception in cleanup: " << e.what() << std::endl;
223 }
224 vpn_interface_index = DWORD(-1);
225 return ret;
226 }
227
229 {
230 std::ostringstream os;
231 destroy_tun(os);
232 OPENVPN_LOG_NTNL("TUN CLOSE (exit)\n"
233 << os.str());
234 }
235
237 {
238 client_process.close();
239 client_process.assign(proc.release());
240
241 // special failsafe to destroy tun in case client crashes without closing it
242 client_process.async_wait([self = Ptr(this)](const openvpn_io::error_code &error)
243 {
244 if (!error)
245 {
246 {
247 std::ostringstream os;
248 self->remove_cmds_bypass_hosts.execute(os);
249 self->remove_cmds_bypass_hosts.clear();
250 OPENVPN_LOG_NTNL("remove bypass route (failsafe)\n" << os.str());
251 }
252
253 if (self->tun)
254 {
255 std::ostringstream os;
256 self->destroy_tun(os);
257 OPENVPN_LOG_NTNL("TUN CLOSE (failsafe)\n" << os.str());
258 }
259 } });
260 }
261
262 void set_client_confirm_event(const std::string &confirm_handle_hex)
263 {
264 client_confirm_event.close();
265
266 const HANDLE remote_event = BufHex::parse<HANDLE>(confirm_handle_hex, "confirm event handle");
267 HANDLE event_handle;
268 if (!::DuplicateHandle(get_client_process(),
269 remote_event,
270 GetCurrentProcess(),
271 &event_handle,
272 0,
273 FALSE,
274 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
275 {
276 const Win::LastError err;
277 OPENVPN_THROW_EXCEPTION("set_client_confirm_event: DuplicateHandle failed: " << err.message());
278 }
279 client_confirm_event.assign(event_handle);
280
281 // Check if the event is okay
282 {
283 const DWORD status = ::WaitForSingleObject(client_confirm_event.native_handle(), 0);
284 const Win::LastError err;
285 switch (status)
286 {
287 case WAIT_OBJECT_0: // acceptable status
288 case WAIT_TIMEOUT: // acceptable status
289 break;
290 case WAIT_ABANDONED:
291 throw Exception("set_client_confirm_event: confirm event is abandoned");
292 default:
293 OPENVPN_THROW_EXCEPTION("set_client_confirm_event: WaitForSingleObject failed: " << err.message());
294 }
295 }
296
297 // When the client signals the client_confirm event, it means
298 // that the client has taken ownership of the TAP device HANDLE,
299 // so we locally release ownership by clearing remote_tap_handle_hex,
300 // effectively preventing the cross-process release of TAP device
301 // HANDLE in destroy_tun() above.
302 client_confirm_event.async_wait([self = Ptr(this)](const openvpn_io::error_code &error)
303 {
304 if (!error)
305 {
306 self->remote_tap_handle_hex.clear();
307 OPENVPN_LOG_STRING("TUN CONFIRM\n");
308 } });
309 }
310
311 void set_client_destroy_event(const std::string &event_handle_hex)
312 {
313 client_destroy_event.close();
314
315 // Move the remote event HANDLE (already duplicated in remote process)
316 // to local process.
317 const HANDLE remote_event = BufHex::parse<HANDLE>(event_handle_hex, "destroy event handle");
318 HANDLE event_handle;
319 if (!::DuplicateHandle(get_client_process(),
320 remote_event,
321 GetCurrentProcess(),
322 &event_handle,
323 0,
324 FALSE,
325 DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
326 {
327 const Win::LastError err;
328 OPENVPN_THROW_EXCEPTION("set_client_destroy_event: DuplicateHandle failed: " << err.message());
329 }
330 client_destroy_event.assign(event_handle);
331
332 // Check if the event is already signaled, or has some other error
333 {
334 const DWORD status = ::WaitForSingleObject(client_destroy_event.native_handle(), 0);
335 const Win::LastError err;
336 switch (status)
337 {
338 case WAIT_TIMEOUT: // expected status
339 break;
340 case WAIT_OBJECT_0:
341 throw Exception("set_client_destroy_event: destroy event is already signaled");
342 case WAIT_ABANDONED:
343 throw Exception("set_client_destroy_event: destroy event is abandoned");
344 default:
345 OPENVPN_THROW_EXCEPTION("set_client_destroy_event: WaitForSingleObject failed: " << err.message());
346 }
347 }
348
349 // normal event-based tun close processing
350 client_destroy_event.async_wait([self = Ptr(this)](const openvpn_io::error_code &error)
351 {
352 if (!error)
353 {
354 {
355 std::ostringstream os;
356 self->remove_cmds_bypass_hosts.execute(os);
357 self->remove_cmds_bypass_hosts.clear();
358 OPENVPN_LOG_NTNL("remove bypass route (event)\n" << os.str());
359 }
360
361 if (self->tun)
362 {
363 std::ostringstream os;
364 self->destroy_tun(os);
365 OPENVPN_LOG_NTNL("TUN CLOSE (event)\n" << os.str());
366 }
367 } });
368 }
369
371 {
372 if (!client_process.is_open())
373 throw Exception("no client process");
374 return client_process.native_handle();
375 }
376
377 void set_remote_tap_handle_hex(const HANDLE tap_handle)
378 {
380 }
381
382 const std::string &get_remote_tap_handle_hex()
383 {
385 }
386
388 {
389 ring_buffer.reset(ring_buffer_arg);
390 }
391
392 void add_bypass_route(const std::string &host, bool ipv6)
393 {
394 std::ostringstream os;
397
398 ActionList add_cmds;
399 // we might have broken VPN connection up, so we must
400 // exclude VPN interface whe searching for the best gateway
401 ADDRESS_FAMILY af = ipv6 ? AF_INET6 : AF_INET;
404 add_cmds.execute(os);
405
406 OPENVPN_LOG(os.str());
407 }
408
409#ifdef OPENVPN_AGENT_START_PROCESS
410 void start_openvpn_process(HANDLE client_pipe,
411 const std::string &config_file,
412 const std::string &config_dir,
413 const std::string &exit_event_name,
414 const std::string &management_host,
415 const std::string &management_password,
416 const int management_port,
417 const std::string &log,
418 const bool log_append)
419 {
420 // impersonate pipe client
421 Win::NamedPipeImpersonate impersonate{client_pipe};
422
423 {
424 // create primary token from impersonation token
425 HANDLE imp_token = NULL, pri_token = NULL;
426 BOOL res = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY, FALSE, &imp_token);
427 if (res == 0)
428 {
429 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
430 OPENVPN_THROW_EXCEPTION("failed to open thread token: " << err.message());
431 }
432 res = DuplicateTokenEx(imp_token, 0, NULL, SECURITY_IMPERSONATION_LEVEL::SecurityAnonymous, TokenPrimary, &pri_token);
433 if (res == 0)
434 {
435 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
436 OPENVPN_THROW_EXCEPTION("failed to duplicate token: " << err.message());
437 }
438
439 // create pipe which is used to write password to openvpn process's stdin
440 HANDLE stdin_read = NULL, stdin_write = NULL;
441 SECURITY_ATTRIBUTES inheritable = {sizeof(inheritable), NULL, TRUE};
442 if (!CreatePipe(&stdin_read, &stdin_write, &inheritable, 0)
443 || !SetHandleInformation(stdin_write, HANDLE_FLAG_INHERIT, 0))
444 {
445 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
446 OPENVPN_THROW_EXCEPTION("failed to set up pipe: " << err.message());
447 }
448
449 // create command line for openvpn process
450 std::ostringstream ss;
451 ss << "client --config \"" << config_dir << "\\" << config_file << "\" --exit-event-name "
452 << exit_event_name << " --auth-retry interact --management " << management_host << " "
453 << management_port << " stdin --management-query-passwords --management-hold "
454 << "--log"
455 << (log_append ? "-append \"" : " \"") << log << "\"";
456 std::string cmd = ss.str();
457 std::unique_ptr<char[]> buf(new char[cmd.length() + 1]);
458 strcpy(buf.get(), cmd.c_str());
459
460 // OPENVPN_LOG("Launching omiclient: " << config.omiclient_exe.c_str() << " " << buf.get());
461
462 STARTUPINFO startup_info = {0};
463 startup_info.cb = sizeof(startup_info);
464 startup_info.dwFlags = STARTF_USESTDHANDLES;
465 startup_info.hStdInput = stdin_read;
466
467 // create openvpn process
468 PROCESS_INFORMATION proc_info;
469 ZeroMemory(&proc_info, sizeof(proc_info));
470 res = CreateProcessAsUser(pri_token,
471 config.omiclient_exe.c_str(),
472 buf.get(),
473 NULL,
474 NULL,
475 TRUE,
476 CREATE_NO_WINDOW | CREATE_UNICODE_ENVIRONMENT,
477 0,
478 0,
479 &startup_info,
480 &proc_info);
481 if (res == 0)
482 {
483 const openvpn_io::error_code err(::GetLastError(), openvpn_io::error::get_system_category());
484 OPENVPN_THROW_EXCEPTION("failed to create openvpn process: " << err.message());
485 }
486 CloseHandle(proc_info.hProcess);
487 CloseHandle(proc_info.hThread);
488
489 // write management password to process's stdin
490 DWORD written;
491 if (!is_safe_conversion<DWORD>(management_password.length()))
492 return;
493 WriteFile(stdin_write, management_password.c_str(), static_cast<DWORD>(management_password.length()), &written, NULL);
494 }
495 }
496#endif
497
500
502
503 private:
504 bool allow_client(AsioPolySock::Base &sock) override
505 {
506 AsioPolySock::NamedPipe *np = dynamic_cast<AsioPolySock::NamedPipe *>(&sock);
507 if (np)
508 {
509 Win::NamedPipePeerInfoClient npinfo(np->handle.native_handle());
510 const std::string client_exe = wstring::to_utf8(npinfo.exe_path);
511 OPENVPN_LOG("connection from " << client_exe);
512 if (Agent::valid_pipe(client_exe, config.server_exe))
513 return true;
514 OPENVPN_LOG(client_exe << " not recognized as a valid client");
515 }
516 else
517 OPENVPN_LOG("only named pipe clients are allowed");
518 return false;
519 }
520
522 openvpn_io::windows::object_handle client_process;
523 openvpn_io::windows::object_handle client_confirm_event;
524 openvpn_io::windows::object_handle client_destroy_event;
526 openvpn_io::io_context &io_context_;
527
528 // with persist tunnel and redirect-gw we must exclude
529 // VPN interface when searching for best gateway when
530 // adding bypass route for the next remote
531 DWORD vpn_interface_index = DWORD(-1);
532};
533
535{
536 public:
538
543
544 virtual ~MyClientInstance() = default;
545
546 private:
547 void generate_reply(const Json::Value &jout)
548 {
549 out = buf_from_string(jout.toStyledString());
550
553 ci.type = "application/json";
554 ci.length = out->size();
557 }
558
559 void http_request_received() override
560 {
561 // alloc output buffer
562 std::ostringstream os;
563
564 try
565 {
566 const HANDLE client_pipe = get_client_pipe();
567 const DWORD client_pid = get_client_pid(client_pipe);
568 const std::wstring client_exe = get_client_exe(client_pipe);
569
570 const HTTP::Request &req = request();
571 OPENVPN_LOG("HTTP request received from " << sock->remote_endpoint_str() << '\n'
572 << req.to_string());
573
574 // get content-type
575 const std::string content_type = req.headers.get_value_trim("content-type");
576
577 if (req.method == "POST")
578 {
579 // verify correct content-type
580 if (string::strcasecmp(content_type, "application/json"))
581 throw Exception("bad content-type");
582
583 // parse the json dict
584 const Json::Value root = json::parse(in.to_string(), "JSON request");
585 if (!root.isObject())
586 throw Exception("json parse error: top level json object is not a dictionary");
587
588 if (req.uri == "/tun-open")
589 {
590 // destroy previous instance
591 if (parent()->destroy_tun(os))
592 {
593 os << "Destroyed previous TAP instance" << std::endl;
594 ::Sleep(1000);
595 }
596
597 {
598 // remember the client process that sent the request
599 ULONG pid = json::get_uint_optional(root, "pid", 0);
600 const std::string confirm_event_hex = json::get_string(root, "confirm_event");
601 const std::string destroy_event_hex = json::get_string(root, "destroy_event");
602
603 Win::NamedPipeImpersonate impersonate(client_pipe);
604
605 parent()->set_client_process(get_client_process(client_pipe, pid));
606 parent()->set_client_confirm_event(confirm_event_hex);
607 parent()->set_client_destroy_event(destroy_event_hex);
608 }
609
610 bool allow_local_dns_resolvers = json::get_bool_optional(root, "allow_local_dns_resolvers");
611 Win::ScopedHANDLE th(parent()->tun_get_handle(os, TunWin::OvpnDco, allow_local_dns_resolvers));
612 {
613 // duplicate the TAP handle into the client process
614 Win::NamedPipeImpersonate impersonate(client_pipe);
616 }
617
618 // build JSON return dictionary
619 const std::string log_txt = string::remove_blanks(os.str());
620 Json::Value jout(Json::objectValue);
621 jout["log_txt"] = log_txt;
622 jout["tap_handle_hex"] = parent()->get_remote_tap_handle_hex();
623
624 auto tap = parent()->get_adapter_state();
625 jout["adapter_guid"] = tap.guid;
626 jout["adapter_index"] = Json::Int(tap.index);
627 jout["adapter_name"] = tap.name;
628
629 OPENVPN_LOG_NTNL("TUN SETUP\n"
630 << log_txt);
631
632 generate_reply(jout);
633 }
634 else if (req.uri == "/tun-setup")
635 {
636 // get PID
637 ULONG pid = json::get_uint_optional(root, "pid", 0);
638
639 TunWin::Type tun_type;
640 switch (json::get_int_optional(root, "tun_type", TunWin::TapWindows6))
641 {
642 case TunWin::Wintun:
643 tun_type = TunWin::Wintun;
644 break;
645 case TunWin::OvpnDco:
646 tun_type = TunWin::OvpnDco;
647 break;
649 default:
650 tun_type = TunWin::TapWindows6;
651 break;
652 }
653
654 bool allow_local_dns_resolvers = json::get_bool_optional(root, "allow_local_dns_resolvers");
655 // parse JSON data into a TunBuilderCapture object
656 TunBuilderCapture::Ptr tbc = TunBuilderCapture::from_json(json::get_dict(root, "tun", false));
657 tbc->validate();
658
659 // destroy previous instance
660 if (tun_type != TunWin::OvpnDco && parent()->destroy_tun(os))
661 {
662 os << "Destroyed previous TAP instance" << std::endl;
663 ::Sleep(1000);
664 }
665
666 // pre-establish impersonation
667 {
668 Win::NamedPipeImpersonate impersonate(client_pipe);
669
670 // remember the client process that sent the request
671 parent()->set_client_process(get_client_process(client_pipe, pid));
672
673 // save destroy event
674 const std::string destroy_event_hex = json::get_string(root, "destroy_event");
675 parent()->set_client_destroy_event(destroy_event_hex);
676
677 if (tun_type != TunWin::OvpnDco)
678 {
679 // get remote event handle for tun object confirmation
680 const std::string confirm_event_hex = json::get_string(root, "confirm_event");
681
682 // save the confirm event
683 parent()->set_client_confirm_event(confirm_event_hex);
684 }
685 }
686
687 if (tun_type == TunWin::Wintun)
688 {
691 json::get_string(root, "send_ring_hmem"),
692 json::get_string(root, "receive_ring_hmem"),
693 json::get_string(root, "send_ring_tail_moved"),
694 json::get_string(root, "receive_ring_tail_moved")));
695 }
696
698 if (tun_type == TunWin::OvpnDco)
699 {
700 tap.guid = json::get_string(root, "adapter_guid");
701 tap.index = (DWORD)json::get_int(root, "adapter_index");
702 tap.name = json::get_string(root, "adapter_name");
703 }
704
705 // establish the tun setup object
706 Win::ScopedHANDLE tap_handle(parent()->establish_tun(*tbc, client_exe, client_pid, nullptr, os, tun_type, allow_local_dns_resolvers, tap));
707
708 // post-establish impersonation
709 {
710 Win::NamedPipeImpersonate impersonate(client_pipe);
711
712 // duplicate the TAP handle into the client process
713 parent()->set_remote_tap_handle_hex(tap_handle());
714 }
715
716 // build JSON return dictionary
717 const std::string log_txt = string::remove_blanks(os.str());
718 Json::Value jout(Json::objectValue);
719 jout["log_txt"] = log_txt;
720 jout["tap_handle_hex"] = parent()->get_remote_tap_handle_hex();
721 OPENVPN_LOG_NTNL("TUN SETUP\n"
722 << log_txt);
723
724 generate_reply(jout);
725 }
726 else if (req.uri == "/add-bypass-route")
727 {
728 ULONG pid = json::get_uint_optional(root, "pid", 0);
729 bool ipv6 = json::get_bool(root, "ipv6");
730 const std::string host = json::get_string(root, "host");
731
732 // pre-establish impersonation
733 {
734 Win::NamedPipeImpersonate impersonate(client_pipe);
735
736 // remember the client process that sent the request
737 parent()->set_client_process(get_client_process(client_pipe, pid));
738 }
739
741
742 Json::Value jout(Json::objectValue);
743
744 generate_reply(jout);
745 }
746#ifdef OPENVPN_AGENT_START_PROCESS
747 else if (req.uri == "/start")
748 {
749 const std::string config_file = json::get_string(root, "config_file");
750 const std::string config_dir = json::get_string(root, "config_dir");
751 const std::string exit_event_name = json::get_string(root, "exit_event_name");
752 const std::string management_host = json::get_string(root, "management_host");
753 const std::string management_password = json::get_string(root, "management_password") + "\n";
754 const int management_port = json::get_int(root, "management_port");
755 const std::string log = json::get_string(root, "log");
756 const bool log_append = json::get_int(root, "log-append") == 1;
757
758 parent()->start_openvpn_process(client_pipe,
759 config_file,
760 config_dir,
761 exit_event_name,
762 management_host,
763 management_password,
764 management_port,
765 log,
766 log_append);
767
768 Json::Value jout(Json::objectValue);
769 generate_reply(jout);
770 }
771#endif
772 else
773 {
774 OPENVPN_LOG("PAGE NOT FOUND");
775 out = buf_from_string("page not found\n");
778 ci.type = "text/plain";
779 ci.length = out->size();
781 }
782 }
783 }
784 catch (const std::exception &e)
785 {
786 if (parent()->destroy_tun(os))
787 os << "Destroyed previous TAP instance due to exception" << std::endl;
788
789 const std::string error_msg = string::remove_blanks(os.str() + e.what() + '\n');
790 OPENVPN_LOG_NTNL("EXCEPTION\n"
791 << error_msg);
792
793 out = buf_from_string(error_msg);
796 ci.type = "text/plain";
797 ci.length = out->size();
799 }
800 }
801
803 {
804 if (buf.defined())
805 in.emplace_back(BufferAllocatedRc::Create(std::move(buf)));
806 }
807
809 {
811 ret.swap(out);
812 return ret;
813 }
814
815 bool http_out_eof() override
816 {
817 // OPENVPN_LOG("HTTP output EOF");
818 return true;
819 }
820
821 bool http_stop(const int status, const std::string &description) override
822 {
823 if (status != WS::Server::Status::E_SUCCESS)
824 {
825 OPENVPN_LOG("INSTANCE STOP : " << WS::Server::Status::error_str(status) << " : " << description);
826 return false;
827 }
828 else
829 return true;
830 }
831
832 HANDLE get_client_pipe() const
833 {
834 AsioPolySock::NamedPipe *np = dynamic_cast<AsioPolySock::NamedPipe *>(sock.get());
835 if (!np)
836 throw Exception("only named pipe clients are allowed");
837 return np->handle.native_handle();
838 }
839
846 DWORD get_client_pid(const HANDLE client_pipe)
847 {
848 return NamedPipePeerInfo::get_pid(client_pipe, true);
849 }
850
851 std::wstring get_client_exe(const HANDLE client_pipe)
852 {
853 Win::NamedPipePeerInfoClient npinfo(client_pipe);
854 return npinfo.exe_path;
855 }
856
857 Win::ScopedHANDLE get_client_process(const HANDLE pipe, ULONG pid_hint) const
858 {
859 pid_hint = Win::NamedPipePeerInfo::get_pid(pipe, true);
860 if (!pid_hint)
861 throw Exception("cannot determine client PID");
862 return Win::NamedPipePeerInfo::get_process(pid_hint, false);
863 }
864
866 {
867 return static_cast<MyListener *>(get_parent());
868 }
869
872};
873
875{
876 public:
878
883};
884
886{
887 public:
889 : Win::Service(config())
890 {
891 }
892
893 void service_work(DWORD argc, LPWSTR *argv) override
894 {
895 if (is_service())
896 {
897 try
898 {
899 log.reset(new Win::LogFile(log_fn(), "", false));
900 }
901 catch (const std::exception &e)
902 {
903 std::cerr << e.what() << std::endl;
904 }
905 }
906 if (!log)
907 log.reset(new LogBaseSimple());
908
909 io_context.reset(new openvpn_io::io_context(1)); // concurrency hint=1
910
911 log_version();
912
913 MyConfig conf;
914
916 TunWin::NRPT::delete_rules(0); // remove stale NRPT rules
917
919 hconf->http_server_id = OVPNAGENT_NAME_STRING "/" HTTP_SERVER_VERSION;
920 hconf->frame = frame_init_simple(2048);
921 hconf->stats.reset(new MySessionStats);
922
923 // DACL string for creating named pipe
924 hconf->sddl_string = "D:" // discretionary ACL
925 "(D;OICI;GA;;;S-1-5-2)" // deny all access for network users
926 "(A;OICI;GA;;;S-1-5-32-544)" // allow full access to Admin group
927 "(A;OICI;GA;;;S-1-5-18)" // allow full access to Local System account
928 "(D;OICI;0x4;;;S-1-1-0)" // deny FILE_CREATE_PIPE_INSTANCE for Everyone
929 "(A;OICI;GRGW;;;S-1-5-11)" // allow read/write access for authenticated users
930 "(A;OICI;GRGW;;;S-1-5-32-546)" // allow read/write access for built-in guest account
931 ;
932
933 Listen::List ll;
934 const unsigned int n_pipe_instances = 4;
935 for (unsigned int i = 0; i < n_pipe_instances; ++i)
936 {
937 Listen::Item li;
938 li.directive = "http-listen";
939 li.addr = conf.pipe_name;
942 li.n_threads = n_pipe_instances;
943 ll.push_back(std::move(li));
944 }
945
947 listener.reset(new MyListener(conf, *io_context, hconf, ll, factory));
948 listener->start();
949
951
952 io_context->run();
953 }
954
955 // Called by service control manager in another thread
956 // to signal the service_work() method to exit.
957 void service_stop() override
958 {
959 openvpn_io::post(*io_context, [this]()
960 {
961 if (listener)
962 {
964 listener->stop();
965 } });
966 }
967
968 private:
969 static Config config()
970 {
971 Config c;
973 c.display_name = "OpenVPN Agent " OVPNAGENT_NAME_STRING;
974 c.autostart = true;
975 c.restart_on_fail = true;
976 return c;
977 }
978
979 static std::string log_fn()
980 {
981 const std::string modname = Win::module_name_utf8();
982 const std::string moddir = path::dirname(modname);
983 const std::string fn = path::join(moddir, "agent.log");
984 return fn;
985 }
986
987 std::unique_ptr<openvpn_io::io_context> io_context;
990};
991
993
994int main(int argc, char *argv[])
995{
996 int ret = 0;
997
998 // process-wide initialization
1000
1001 try
1002 {
1003 MyService serv;
1004 if (argc >= 2)
1005 {
1006 const std::string arg = argv[1];
1007 if (arg == "run")
1008 serv.service_work(0, nullptr);
1009 else if (arg == "install")
1010 serv.install();
1011 else if (arg == "remove")
1012 serv.remove();
1013 else if (arg == "modname")
1014 std::wcout << Win::module_name() << std::endl;
1015 else if (arg == "help")
1016 {
1017 std::cout << "usage: ovpnagent [options]" << std::endl;
1018 std::cout << " run -- run in foreground (for debugging)" << std::endl;
1019 std::cout << " install -- install as service" << std::endl;
1020 std::cout << " remove -- uninstall" << std::endl;
1021 std::cout << " modname -- show module name" << std::endl;
1022 std::cout << " help -- show help message" << std::endl;
1023 std::cout << " [default] -- start as service" << std::endl;
1024 }
1025 else
1026 {
1027 std::cout << "unrecognized option, use 'help' for more info" << std::endl;
1028 ret = 2;
1029 }
1030 }
1031 else
1032 serv.start();
1033 }
1034 catch (const std::exception &e)
1035 {
1036 std::cout << "ovpnagent: " << e.what() << std::endl;
1037 ret = 1;
1038 }
1039
1040 return ret;
1041}
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)
openvpn_io::windows::object_handle 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:93
RCPtr< MySessionStats > Ptr
Definition ovpnagent.cpp:91
std::string dump() const
Definition ovpnagent.cpp:98
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)
bool defined() const
Returns true if the buffer is not empty.
Definition buffer.hpp:1207
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
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:185
#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
Support deferred server-side state creation when client connects.
Definition ovpncli.cpp:95
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:83
unsigned int n_pipe_instances
Definition ovpnagent.cpp:85
std::string omiclient_exe
Definition ovpnagent.cpp:84
std::string pipe_name
Definition ovpnagent.cpp:82
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
remote_address ipv6
proxy_host_port host
std::string ret
int main(int argc, char *argv[])
void log_version()
Definition ovpnagent.cpp:63
#define HTTP_SERVER_VERSION
Definition ovpnagent.cpp:27
#define OVPNAGENT_NAME_STRING
Definition ovpnagent.cpp:35