OpenVPN 3 Core Library
Loading...
Searching...
No Matches
cli.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 3 test client
13
14#include <stdlib.h>
15
16#include <string>
17#include <iostream>
18#include <thread>
19#include <memory>
20#include <mutex>
21
23
24#ifdef OPENVPN_PLATFORM_MAC
25#include <CoreFoundation/CFBundle.h>
26#include <ApplicationServices/ApplicationServices.h>
27#endif
28
29// If enabled, don't direct ovpn3 core logging to
30// ClientAPI::OpenVPNClient::log() virtual method.
31// Instead, logging will go to LogBaseSimple::log().
32// In this case, make sure to define:
33// LogBaseSimple log;
34// at the top of your main() function to receive
35// log messages from all threads.
36// Also, note that the OPENVPN_LOG_GLOBAL setting
37// MUST be consistent across all compilation units.
38#ifdef OPENVPN_USE_LOG_BASE_SIMPLE
39#define OPENVPN_LOG_GLOBAL // use global rather than thread-local log object pointer
41#endif
42
43// don't export core symbols
44#define OPENVPN_CORE_API_VISIBILITY_HIDDEN
45
46// use SITNL on Linux by default
47#if defined(OPENVPN_PLATFORM_LINUX) && !defined(OPENVPN_USE_IPROUTE2) && !defined(OPENVPN_USE_SITNL)
48#define OPENVPN_USE_SITNL
49#endif
50
51// should be included before other openvpn includes,
52// with the exception of openvpn/log includes
53#include <client/ovpncli.cpp>
54
67
68#ifdef OPENVPN_REMOTE_OVERRIDE
70#endif
71
72#if defined(USE_MBEDTLS)
74#elif defined(USE_OPENSSL)
75#include <openssl/evp.h>
76#if (OPENSSL_VERSION_NUMBER >= 0x30000000L)
77#include <openssl/core_names.h>
78#endif
79#endif
80
81#if defined(OPENVPN_PLATFORM_WIN)
83#include <shellapi.h>
84#endif
85
86#ifdef USE_NETCFG
87#include "client/core-client-netcfg.hpp"
88#endif
89
90#if defined(OPENVPN_PLATFORM_LINUX)
91
93
94// we use a static polymorphism and define a
95// platform-specific TunSetup class, responsible
96// for setting up tun device
97#define TUN_CLASS_SETUP TunLinuxSetup::Setup<TUN_LINUX>
99#elif defined(OPENVPN_PLATFORM_MAC)
101#define TUN_CLASS_SETUP TunMac::Setup
102#endif
103
104using namespace openvpn;
105
106namespace {
108}
109
110#ifdef USE_TUN_BUILDER
112{
113 public:
114 bool tun_builder_new() override
115 {
116 return true;
117 }
118
119 int tun_builder_establish() override
120 {
121 tun.reset(new TUN_CLASS_SETUP());
122
123 TUN_CLASS_SETUP::Config config;
124 config.layer = Layer(Layer::Type::OSI_LAYER_3);
125 // no need to add bypass routes on establish since we do it on socket_protect
126 config.add_bypass_routes_on_establish = false;
127 return tun->establish(tbc, &config, nullptr, std::cout);
128 }
129
130 bool tun_builder_add_address(const std::string &address,
131 int prefix_length,
132 const std::string &gateway, // optional
133 bool ipv6,
134 bool net30) override
135 {
136 return tbc.tun_builder_add_address(address, prefix_length, gateway, ipv6, net30);
137 }
138
139 bool tun_builder_add_route(const std::string &address,
140 int prefix_length,
141 int metric,
142 bool ipv6) override
143 {
144 return tbc.tun_builder_add_route(address, prefix_length, metric, ipv6);
145 }
146
148 bool ipv6,
149 unsigned int flags) override
150 {
151 return tbc.tun_builder_reroute_gw(ipv4, ipv6, flags);
152 }
153
154 bool tun_builder_set_remote_address(const std::string &address,
155 bool ipv6) override
156 {
157 return tbc.tun_builder_set_remote_address(address, ipv6);
158 }
159
160 bool tun_builder_set_session_name(const std::string &name) override
161 {
162 return tbc.tun_builder_set_session_name(name);
163 }
164
165 void tun_builder_teardown(bool disconnect) override
166 {
167 std::ostringstream os;
168 auto os_print = Cleanup([&os]()
169 { OPENVPN_LOG_STRING(os.str()); });
170 tun->destroy(os);
171 }
172
173 bool socket_protect([[maybe_unused]] openvpn_io::detail::socket_type socket, std::string remote, bool ipv6) override
174 {
175 std::ostringstream os;
176 auto os_print = Cleanup([&os]()
177 { OPENVPN_LOG_STRING(os.str()); });
178 return tun->add_bypass_route(remote, ipv6, os);
179 }
180
181 bool tun_builder_set_dns_options(const DnsOptions &dns) override
182 {
183 return tbc.tun_builder_set_dns_options(dns);
184 }
185
186 bool tun_builder_set_mtu(int mtu) override
187 {
188 return tbc.tun_builder_set_mtu(mtu);
189 }
190
191 private:
192 TUN_CLASS_SETUP::Ptr tun = new TUN_CLASS_SETUP();
194};
195#else // USE_TUN_BUILDER
197{
198 public:
199 bool socket_protect(openvpn_io::detail::socket_type socket, std::string remote, bool ipv6) override
200 {
201 std::cout << "NOT IMPLEMENTED: *** socket_protect " << socket << " " << remote << std::endl;
202 return true;
203 }
204};
205#endif
206
207class Client : public ClientBase
208{
209 // This is the protocol tag that appcontrol expects to trigger the certcheck start.
210 static constexpr char certcheck_init_verb[] = "dpc1"; // TODO: std::string in C++ '20
211
212 public:
222
224 {
225 return !dc_cookie.empty();
226 }
227
229 {
230 return dc_cookie;
231 }
232
233 std::string epki_ca;
234 std::string epki_cert;
235#if defined(USE_MBEDTLS)
236 MbedTLSPKI::PKContext epki_ctx; // external PKI context
237 MbedTLSPKI::PKContext certcheck_pkey; // external PKI context
238#elif defined(USE_OPENSSL)
240 openvpn::OpenSSLPKI::PKey certcheck_pkey;
241#endif
242
243 std::string certcheck_cert;
245
247 {
248 clock_tick_action = action;
249 }
250
252 {
253 const int n = stats_n();
254 std::vector<long long> stats = stats_bundle();
255
256 std::cout << "STATS:" << std::endl;
257 for (int i = 0; i < n; ++i)
258 {
259 const long long value = stats[i];
260 if (value)
261 std::cout << " " << stats_name(i) << " : " << value << std::endl;
262 }
263 }
264
265#ifdef OPENVPN_REMOTE_OVERRIDE
266 void set_remote_override_cmd(const std::string &cmd)
267 {
268 remote_override_cmd = cmd;
269 }
270#endif
271
272 void set_write_url_fn(const std::string &fn)
273 {
274 write_url_fn = fn;
275 }
276
277
278 private:
279 void event(const ClientAPI::Event &ev) override
280 {
281 std::cout << date_time() << " EVENT: " << ev.name;
282 if (!ev.info.empty())
283 std::cout << ' ' << ev.info;
284 if (ev.fatal)
285 std::cout << " [FATAL-ERR]";
286 else if (ev.error)
287 std::cout << " [ERR]";
288 std::cout << std::endl;
289 if (ev.name == "DYNAMIC_CHALLENGE")
290 {
291 dc_cookie = ev.info;
292
295 {
296 std::cout << "DYNAMIC CHALLENGE" << std::endl;
297 std::cout << "challenge: " << dc.challenge << std::endl;
298 std::cout << "echo: " << dc.echo << std::endl;
299 std::cout << "responseRequired: " << dc.responseRequired << std::endl;
300 std::cout << "stateID: " << dc.stateID << std::endl;
301 }
302 }
303 else if (ev.name == "PROXY_NEED_CREDS")
304 {
305 std::cout << "PROXY_NEED_CREDS " << ev.info << std::endl;
306 }
307 else if (ev.name == "INFO")
308 {
309 if (string::starts_with(ev.info, "OPEN_URL:"))
310 {
311 open_url(ev.info.substr(9));
312 }
313 else if (string::starts_with(ev.info, "WEB_AUTH:"))
314 {
315 auto extra = ev.info.substr(9);
316 size_t flagsend = extra.find(':');
317 if (flagsend != std::string::npos)
318 {
319 open_url(extra.substr(flagsend + 1));
320 }
321 }
322 else if (string::starts_with(ev.info, "CR_TEXT:"))
323 {
324 std::string cr_response;
325 std::cout << "\n\n"
326 << ev.info.substr(8) << ": ";
327 std::getline(std::cin, cr_response);
328 post_cc_msg("CR_RESPONSE," + base64->encode(cr_response));
329 }
330 else
331 {
332 std::cout << "Unrecognized INFO/INFO_PRE message: " << ev.info << std::endl;
333 }
334 }
335 else
336 {
337 std::cout << "Received event " << ev.name << " " << ev.info << std::endl;
338 }
339 }
340
342 {
343 if (string::starts_with(acev.payload, "{\"dpc_request\"") && acev.payload.find("certificate") != std::string::npos)
344 {
345 std::cout << "ACC CERTCHECK challenge initiated" << std::endl;
347 }
348 else if (string::starts_with(acev.payload, "{\"dpc_request\"") && acev.payload.find("client_info") != std::string::npos)
349 {
350
351 std::string fakeResponse{R"({"dpc_response": {
352 "client_info" : {
353 "os" : {"type" : "Windows", "version" : "10.0.19045" }
354 }
355 }})"};
356
357 std::cout << "ACC DPC1: sending fake client info:" << fakeResponse << std::endl;
358 send_app_control_channel_msg("dpc1", fakeResponse);
359 }
360 else if (string::starts_with(acev.payload, "{\"dpc_request\""))
361 {
362 std::cout << "Cannot parse dpc request message:" << acev.payload << std::endl;
363 }
364 else
365 {
366 std::cout << "Cannot parse device posture message:" << acev.payload << std::endl;
367 }
368 }
369
374 void acc_event(const ClientAPI::AppCustomControlMessageEvent &acev) override
375 {
376 if (acev.protocol == "internal:supported_protocols")
377 {
378 std::cout << "Client/server common app custom control protocols: " << acev.payload << std::endl;
379 }
380 else if (acev.protocol == certcheck_init_verb)
381 {
383 }
384 else
385 {
386 std::cout << "received unhandled app custom control message for protocol " << acev.protocol
387 << " msg payload: " << acev.payload << std::endl;
388 }
389 }
395 {
396 if (certcheck_cert.empty() || !certcheck_pkey.defined())
397 {
398 std::cout << "ACC CERTCHECK FAILED: MISSING PARAMETERS" << std::endl;
399 return;
400 }
401
402 std::string clientca_pem = "";
403
404 if (!certcheck_clientca_fn.empty())
406
407 if constexpr (false)
408 {
409 /* cli.cpp typically goes the long complicated way of using the epki API to do its checks */
410
411 if (!clientca_pem.empty())
412 {
413 start_cert_check(certcheck_cert, certcheck_pkey.render_pem(), clientca_pem);
414 }
415 else
416 {
417 start_cert_check(certcheck_cert, certcheck_pkey.render_pem());
418 }
419 }
420 else
421 {
422 start_cert_check_epki("certcheck", clientca_pem);
423 }
424 }
425
426 void open_url(std::string url_str)
427 {
428 if (string::starts_with(url_str, "http://")
429 || string::starts_with(url_str, "https://"))
430 {
431 if (!write_url_fn.empty())
432 {
433 write_string(write_url_fn, url_str + '\n');
434 return;
435 }
436#ifdef OPENVPN_PLATFORM_MAC
437 std::cout << "Trying to launch " << url_str << std::endl;
438 std::thread thr([url_str]()
439 {
440 CFURLRef url = CFURLCreateWithBytes(
441 NULL, // allocator
442 (UInt8*) url_str.c_str(), // URLBytes
443 url_str.length(), // length
444 kCFStringEncodingUTF8, // encoding
445 NULL // baseURL
446 );
447 LSOpenCFURLRef(url, 0);
448 CFRelease(url); });
449 thr.detach();
450#elif defined(OPENVPN_PLATFORM_TYPE_UNIX)
451 Argv argv;
452 if (::getuid() == 0 && ::getenv("SUDO_USER"))
453 {
454 argv.emplace_back("/usr/sbin/runuser");
455 argv.emplace_back("-u");
456 argv.emplace_back(::getenv("SUDO_USER"));
457 }
458 argv.emplace_back("/usr/bin/xdg-open");
459 argv.emplace_back(url_str);
460 system_cmd(argv);
461#else
462 std::cout << "No implementation to launch " << url_str << std::endl;
463#endif
464 }
465 }
466
467 void log(const ClientAPI::LogInfo &log) override
468 {
469 std::lock_guard<std::mutex> lock(log_mutex);
470 std::cout << date_time() << ' ' << log.text << std::flush;
471 }
472
473 void clock_tick() override
474 {
475 const ClockTickAction action = clock_tick_action;
477
478 switch (action)
479 {
480 case CT_STOP:
481 std::cout << "signal: CT_STOP" << std::endl;
482 stop();
483 break;
484 case CT_RECONNECT:
485 std::cout << "signal: CT_RECONNECT" << std::endl;
486 reconnect(0);
487 break;
488 case CT_PAUSE:
489 std::cout << "signal: CT_PAUSE" << std::endl;
490 pause("clock-tick pause");
491 break;
492 case CT_RESUME:
493 std::cout << "signal: CT_RESUME" << std::endl;
494 resume();
495 break;
496 case CT_STATS:
497 std::cout << "signal: CT_STATS" << std::endl;
498 print_stats();
499 break;
500 default:
501 break;
502 }
503 }
504
506 {
507 if (certreq.alias == "epki" && !epki_cert.empty())
508 {
509 certreq.cert = epki_cert;
510 certreq.supportingChain = epki_ca;
511 }
512 else if (certreq.alias == "certcheck" && !certcheck_cert.empty())
513 {
514 certreq.cert = certcheck_cert;
516 }
517 else
518 {
519 certreq.error = true;
520 certreq.errorText = "external_pki_cert_request not implemented";
521 }
522 }
523
524#ifdef USE_OPENSSL
525 void doOpenSSLSignature(ClientAPI::ExternalPKISignRequest &signreq) const
526 {
527 using PKEY_CTX_unique_ptr = std::unique_ptr<::EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>;
528
530 base64->decode(signdata, signreq.data);
531
532 EVP_PKEY *pkey = epki_pkey.obj();
533 if (signreq.alias == "certcheck")
534 pkey = certcheck_pkey.obj();
535
536 PKEY_CTX_unique_ptr pkey_ctx(EVP_PKEY_CTX_new(pkey, nullptr), EVP_PKEY_CTX_free);
537
538 if (!(pkey_ctx))
539 throw Exception("epki_sign failed, error creating PKEY ctx");
540
541
542 if ((EVP_PKEY_sign_init(pkey_ctx.get()) < 0))
543 {
544 throw Exception("epki_sign failed, error in EVP_PKEY_sign_init: " + openssl_error());
545 }
546
547 if (signreq.algorithm == "RSA_PKCS1_PSS_PADDING")
548 {
549 EVP_PKEY_CTX_set_rsa_padding(pkey_ctx.get(), RSA_PKCS1_PSS_PADDING);
550 }
551 else if (signreq.algorithm == "RSA_PKCS1_PADDING")
552 {
553 EVP_PKEY_CTX_set_rsa_padding(pkey_ctx.get(), RSA_PKCS1_PADDING);
554 }
555 else if (signreq.algorithm == "RSA_NO_PADDING")
556 {
557 EVP_PKEY_CTX_set_rsa_padding(pkey_ctx.get(), RSA_NO_PADDING);
558 }
559
560 /* determine the output length */
561 size_t outlen;
562
563 if ((EVP_PKEY_sign(pkey_ctx.get(), nullptr, &outlen, signdata.c_data(), signdata.size())) < 0)
564 {
565 throw Exception("epki_sign failed, error signing data: " + openssl_error());
566 }
567
569
570 if ((EVP_PKEY_sign(pkey_ctx.get(), sig.data(), &outlen, signdata.c_data(), signdata.size())) < 0)
571 {
572 throw Exception("epki_sign failed, error signing data: " + openssl_error());
573 }
574
575 sig.set_size(outlen);
576 signreq.sig = base64->encode(sig);
577 OPENVPN_LOG("SIGNATURE[" << outlen << "]: " << signreq.sig);
578 }
579
580#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
581 void doOpenSSLDigestSignature(ClientAPI::ExternalPKISignRequest &signreq)
582 {
583 /* technically implementing this without OpenSSL 3.0 is possible but
584 * only the xkey_provider implementation for OpenSSL 3.0 requires this,
585 * so in the cli.cpp, which is only a test cient, we skip this extra
586 * effort and just use only the modern APIs in doOpenSSLDigestSignature
587 */
588 throw Exception("epki_sign failed, digest sign only implemented in OpenSSL 3.0");
589 }
590#else
591 void doOpenSSLDigestSignature(ClientAPI::ExternalPKISignRequest &signreq)
592 {
593 EVP_PKEY_CTX *pkey_ctx = nullptr;
595 base64->decode(signdata, signreq.data);
596
597 using MD_unique_ptr = std::unique_ptr<::EVP_MD_CTX, decltype(&::EVP_MD_CTX_free)>;
598
599 MD_unique_ptr md(EVP_MD_CTX_new(), EVP_MD_CTX_free);
600
601 if (!md)
602 throw Exception("epki_sign failed, error creating MD ctx");
603
604 if (!signreq.saltlen.empty() && signreq.saltlen != "digest")
605 {
606 throw Exception("epki_sign failed, only padding=digest supported" + openssl_error());
607 }
608
609 const char *padding = "none";
610
611 if (signreq.algorithm == "RSA_PKCS1_PSS_PADDING")
612 {
613 padding = "pss";
614 }
615 else if (signreq.algorithm == "RSA_PKCS1_PADDING")
616 {
617 padding = "pkcs1";
618 }
619 else if (signreq.algorithm == "RSA_NO_PADDING")
620 {
621 padding = "none";
622 }
623
624 EVP_PKEY *pkey = epki_pkey.obj();
625 if (signreq.alias == "certcheck")
626 pkey = certcheck_pkey.obj();
627
628 OSSL_PARAM params[6] = {OSSL_PARAM_END};
629
630 char *hashalg = const_cast<char *>(signreq.hashalg.c_str());
631 if (signreq.hashalg == "none")
632 hashalg = nullptr;
633
634 params[0] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_DIGEST, hashalg, 0);
635 params[1] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE, const_cast<char *>(padding), 0);
636
637 if (EVP_PKEY_get_id(pkey) == EVP_PKEY_RSA && !signreq.saltlen.empty())
638 {
639 /* The strings are used const in OpenSSL but the API definition has char * */
640 char *saltlen = const_cast<char *>(signreq.saltlen.c_str());
641 params[2] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PSS_SALTLEN, saltlen, 0);
642 params[3] = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_MGF1_DIGEST, hashalg, 0);
643 params[4] = OSSL_PARAM_construct_end();
644 }
645
646 EVP_DigestSignInit_ex(md.get(), &pkey_ctx, hashalg, nullptr, nullptr, pkey, params);
647
648 /* determine the output length */
649 size_t outlen;
650
651 if (EVP_DigestSign(md.get(), nullptr, &outlen, signdata.data(), signdata.size()) < 0)
652 {
653 throw Exception("epki_sign failed, error signing data: " + openssl_error());
654 }
655
657
658 if (EVP_DigestSign(md.get(), sig.data(), &outlen, signdata.data(), signdata.size()) < 0)
659 {
660 throw Exception("epki_sign failed, error signing data: " + openssl_error());
661 }
662
663 sig.set_size(outlen);
664 signreq.sig = base64->encode(sig);
665 OPENVPN_LOG("SIGNATURE[" << outlen << "]: " << signreq.sig);
666 }
667#endif
668
669 void doOpenSSLSignRequest(ClientAPI::ExternalPKISignRequest &signreq)
670 {
671 if (signreq.hashalg.empty())
672 {
673 doOpenSSLSignature(signreq);
674 }
675 else
676 {
677 doOpenSSLDigestSignature(signreq);
679 }
680#endif
681
683 {
684#if defined(USE_MBEDTLS)
685 if (epki_ctx.defined())
686 {
687 try
688 {
689 // decode base64 sign request
691 base64->decode(signdata, signreq.data);
692
693 // get MD alg
694 const mbedtls_md_type_t md_alg = PKCS1::DigestPrefix::MbedTLSParse().alg_from_prefix(signdata);
695
696 // log info
697 OPENVPN_LOG("SIGN[" << PKCS1::DigestPrefix::MbedTLSParse::to_string(md_alg) << ',' << signdata.size() << "]: " << render_hex_generic(signdata));
698
699 // allocate buffer for signature
700 BufferAllocated sig(mbedtls_pk_get_len(epki_ctx.get()), BufAllocFlags::ARRAY);
701
702 // sign it
703 size_t sig_size = 0;
704 const int status = mbedtls_pk_sign(epki_ctx.get(),
705 md_alg,
706 signdata.c_data(),
707 signdata.size(),
708 sig.data(),
709#if MBEDTLS_VERSION_NUMBER >= 0x03000000
710 sig.length(),
711#endif
712 &sig_size,
714 this);
715 if (status != 0)
716 throw Exception("mbedtls_pk_sign failed, err=" + openvpn::to_string(status));
717 if (sig.size() != sig_size)
718 throw Exception("unexpected signature size");
719
720 // encode base64 signature
721 signreq.sig = base64->encode(sig);
722 OPENVPN_LOG("SIGNATURE[" << sig_size << "]: " << signreq.sig);
723 }
724 catch (const std::exception &e)
725 {
726 signreq.error = true;
727 signreq.errorText = std::string("external_pki_sign_request: ") + e.what();
728 }
729 }
730 else
731#elif defined(USE_OPENSSL)
732
733 if ((epki_pkey.defined() && signreq.alias == "epki")
734 || (certcheck_pkey.defined() && signreq.alias == "certcheck"))
735 {
736 try
737 {
738 doOpenSSLSignRequest(signreq);
739 }
740 catch (const std::exception &e)
741 {
742 signreq.error = true;
743 signreq.errorText = std::string("external_pki_sign_request: ") + e.what();
744 }
745 }
746 else
747#endif
748 {
749 signreq.error = true;
750 signreq.errorText = "external_pki_sign_request not implemented";
752 }
753
754 // RNG callback
755 static int rng_callback(void *arg, unsigned char *data, size_t len)
756 {
757 Client *self = (Client *)arg;
758 if (!self->rng)
759 {
760 self->rng.reset(new SSLLib::RandomAPI());
762 return self->rng->rand_bytes_noexcept(data, len) ? 0 : -1; // using -1 as a general-purpose mbed TLS error code
763 }
764
765 bool pause_on_connection_timeout() override
766 {
767 return false;
768 }
769
770#ifdef OPENVPN_REMOTE_OVERRIDE
771 bool remote_override_enabled() override
772 {
773 return !remote_override_cmd.empty();
774 }
775
777 {
779 Argv argv;
780 argv.emplace_back(remote_override_cmd);
781 OPENVPN_LOG(argv.to_string());
782 const int status = system_cmd(remote_override_cmd,
783 argv,
784 nullptr,
785 pio,
787 nullptr);
788 if (!status)
789 {
790 const std::string out = string::first_line(pio.out);
791 OPENVPN_LOG("REMOTE OVERRIDE: " << out);
792 auto svec = string::split(out, ',');
793 if (svec.size() == 4)
794 {
795 ro.host = svec[0];
796 ro.ip = svec[1];
797 ro.port = svec[2];
798 ro.proto = svec[3];
799 }
800 else
801 ro.error = "cannot parse remote-override, expecting host,ip,port,proto (at least one or both of host and ip must be defined)";
802 }
803 else
804 ro.error = "status=" + std::to_string(status);
806#endif
808 std::mutex log_mutex;
809 std::string dc_cookie;
810 StrongRandomAPI::Ptr rng; // random data source for epki
812
813#ifdef OPENVPN_REMOTE_OVERRIDE
814 std::string remote_override_cmd;
815#endif
816
817 std::string write_url_fn;
818};
819
820static Client *the_client = nullptr; // GLOBAL
821
822static void worker_thread()
823{
824#if !defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
825 openvpn_io::detail::signal_blocker signal_blocker; // signals should be handled by parent thread
826#endif
827 try
828 {
829 std::cout << "Thread starting..." << std::endl;
830 ClientAPI::Status connect_status = the_client->connect();
831 if (connect_status.error)
832 {
833 std::cout << "connect error: ";
834 if (!connect_status.status.empty())
835 std::cout << connect_status.status << ": " << std::endl;
836 std::cout << connect_status.message << std::endl;
837 }
838 }
839 catch (const std::exception &e)
840 {
841 std::cout << "Connect thread exception: " << e.what() << std::endl;
842 }
843 std::cout << "Thread finished" << std::endl;
844}
845
846static std::string read_profile(const char *fn, const std::string *profile_content)
847{
848 if (!string::strcasecmp(fn, "http") && profile_content && !profile_content->empty())
849 return *profile_content;
850 else
851 {
852 ProfileMerge pm(fn,
853 "ovpn",
854 "",
859 OPENVPN_THROW_EXCEPTION("merge config error: " << pm.status_string() << " : " << pm.error());
860 return pm.profile_content();
861 }
862}
863
864#if defined(OPENVPN_PLATFORM_WIN)
865
866static void start_thread(Client &client)
867{
868 // Set Windows title bar
869 const std::string title_text = "F2:Stats F3:Reconnect F4:Stop F5:Pause";
871 Win::Console::Input console{false};
872
873 // start connect thread
874 std::unique_ptr<std::thread> thread;
875 volatile bool thread_exit = false;
876 the_client = &client;
877 thread.reset(new std::thread([&thread_exit]()
878 {
880 thread_exit = true; }));
881
882 // wait for connect thread to exit, also check for keypresses
883 while (!thread_exit)
884 {
885 while (true)
886 {
887 const unsigned int c = console.get();
888 if (!c)
889 break;
890 else if (c == 0x3C) // F2
892 else if (c == 0x3D) // F3
894 else if (c == 0x3E) // F4
895 the_client->stop();
896 else if (c == 0x3F) // F5
897 the_client->pause("user-pause");
898 }
899 Sleep(1000);
900 }
901
902 // wait for connect thread to exit
903 thread->join();
904
905 the_client = nullptr;
906}
907
908#elif defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
909
910static void handler(int signum)
911{
912 switch (signum)
913 {
914 case SIGTERM:
915 case SIGINT:
916 if (the_client)
918 break;
919 case SIGHUP:
920 if (the_client)
922 break;
923 case SIGUSR1:
924 if (the_client)
926 break;
927 case SIGUSR2:
928 {
929 // toggle pause/resume
930 static bool hup = false;
931 if (the_client)
932 {
933 if (hup)
935 else
937 hup = !hup;
938 }
939 }
940 break;
941 default:
942 break;
943 }
944}
945
946static void start_thread(Client &client)
947{
948 the_client = &client;
949
950 // capture signals that might occur while we're in worker_thread
952
953 // run the client
955
956 the_client = nullptr;
957}
958
959#else
960
961static void handler(int signum)
962{
963 switch (signum)
964 {
965 case SIGTERM:
966 case SIGINT:
967 std::cout << "received stop signal " << signum << std::endl;
968 if (the_client)
969 the_client->stop();
970 break;
971 case SIGHUP:
972 std::cout << "received reconnect signal " << signum << std::endl;
973 if (the_client)
975 break;
976 case SIGUSR1:
977 if (the_client)
979 break;
980 case SIGUSR2:
981 {
982 // toggle pause/resume
983 static bool hup = false;
984 std::cout << "received pause/resume toggle signal " << signum << std::endl;
985 if (the_client)
986 {
987 if (hup)
989 else
990 the_client->pause("pause-resume-signal");
991 hup = !hup;
992 }
993 }
994 break;
995 default:
996 std::cout << "received unknown signal " << signum << std::endl;
997 break;
998 }
999}
1000
1001static void start_thread(Client &client)
1002{
1003 std::unique_ptr<std::thread> thread;
1004
1005 // start connect thread
1006 the_client = &client;
1007 thread.reset(new std::thread([]()
1008 { worker_thread(); }));
1009
1010 {
1011 // catch signals that might occur while we're in join()
1013
1014 // wait for connect thread to exit
1015 thread->join();
1016 }
1017 the_client = nullptr;
1018}
1019
1020#endif
1021
1022int openvpn_client(int argc, char *argv[], const std::string *profile_content)
1023{
1024 static const struct option longopts[] = {
1025 // clang-format off
1026 { "username", required_argument, nullptr, 'u' },
1027 { "password", required_argument, nullptr, 'p' },
1028 { "response", required_argument, nullptr, 'r' },
1029 { "dc", required_argument, nullptr, 'D' },
1030 { "proto", required_argument, nullptr, 'P' },
1031 { "ipv6", required_argument, nullptr, '6' },
1032 { "server", required_argument, nullptr, 's' },
1033 { "port", required_argument, nullptr, 'R' },
1034 { "timeout", required_argument, nullptr, 't' },
1035 { "compress", required_argument, nullptr, 'c' },
1036 { "pk-password", required_argument, nullptr, 'z' },
1037 { "tvm-override", required_argument, nullptr, 'M' },
1038 { "proxy-host", required_argument, nullptr, 'h' },
1039 { "proxy-port", required_argument, nullptr, 'q' },
1040 { "proxy-username", required_argument, nullptr, 'U' },
1041 { "proxy-password", required_argument, nullptr, 'W' },
1042 { "peer-info", required_argument, nullptr, 'I' },
1043 { "acc-protos", required_argument, nullptr, 'K' },
1044 { "gremlin", required_argument, nullptr, 'G' },
1045 { "proxy-basic", no_argument, nullptr, 'B' },
1046 { "alt-proxy", no_argument, nullptr, 'A' },
1047#if defined(ENABLE_KOVPN) || defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)
1048 { "no-dco", no_argument, nullptr, 'd' },
1049#endif
1050 { "eval", no_argument, nullptr, 'e' },
1051 { "self-test", no_argument, nullptr, 'T' },
1052 { "cache-password", no_argument, nullptr, 'C' },
1053 { "no-cert", no_argument, nullptr, 'x' },
1054 { "force-aes-cbc", no_argument, nullptr, 'f' },
1055 { "google-dns", no_argument, nullptr, 'g' },
1056 { "persist-tun", no_argument, nullptr, 'j' },
1057 { "wintun", no_argument, nullptr, 'w' },
1058 { "allow-local-dns-resolvers", no_argument, nullptr, 'l' },
1059 { "def-keydir", required_argument, nullptr, 'k' },
1060 { "merge", no_argument, nullptr, 'm' },
1061 { "version", no_argument, nullptr, 'v' },
1062 { "auto-sess", no_argument, nullptr, 'a' },
1063 { "auth-retry", no_argument, nullptr, 'Y' },
1064 { "tcprof-override", required_argument, nullptr, 'X' },
1065 { "write-url", required_argument, nullptr, 'Z' },
1066 { "sso-methods", required_argument, nullptr, 'S' },
1067 { "ssl-debug", required_argument, nullptr, 1 },
1068 { "epki-cert", required_argument, nullptr, 2 },
1069 { "epki-ca", required_argument, nullptr, 3 },
1070 { "epki-key", required_argument, nullptr, 4 },
1071 { "legacy-algorithms", no_argument, nullptr, 'L' },
1072 { "non-preferred-algorithms", no_argument, nullptr, 'Q' },
1073#ifdef OPENVPN_REMOTE_OVERRIDE
1074 { "remote-override",required_argument, nullptr, 5 },
1075#endif
1076 { "tbc", no_argument, nullptr, 6 },
1077 { "app-custom-protocols", required_argument, nullptr, 'K' },
1078 { "certcheck-cert", required_argument, nullptr, 'o' },
1079 { "certcheck-pkey", required_argument, nullptr, 'O' },
1080 { "certcheck-clientca", required_argument, nullptr, 'b' },
1081 { nullptr, 0, nullptr, 0 }
1082 // clang-format on
1083 };
1084
1085 int ret = 0;
1086 auto cleanup = Cleanup([]()
1087 { the_client = nullptr; });
1088
1089 try
1090 {
1091 if (argc >= 2)
1092 {
1093 std::string username;
1094 std::string password;
1095 std::string response;
1096 std::string dynamicChallengeCookie;
1097 std::string proto;
1098 std::string allowUnusedAddrFamilies;
1099 std::string server;
1100 std::string port;
1101 int timeout = 0;
1102 std::string compress;
1103 std::string privateKeyPassword;
1104 std::string tlsVersionMinOverride;
1105 std::string tlsCertProfileOverride;
1106 std::string proxyHost;
1107 std::string proxyPort;
1108 std::string proxyUsername;
1109 std::string proxyPassword;
1110 std::string peer_info;
1111 std::string gremlin;
1112 std::string ssoMethods;
1113 std::string appCustomProtocols;
1114 std::string certcheck_cert_fn;
1115 std::string certcheck_pkey_fn;
1116 std::string certcheck_clientca;
1117 bool eval = false;
1118 bool self_test = false;
1119 bool disableClientCert = false;
1120 bool proxyAllowCleartextAuth = false;
1121 int defaultKeyDirection = -1;
1122 int sslDebugLevel = 0;
1123 bool googleDnsFallback = false;
1124 bool autologinSessions = false;
1125 bool retryOnAuthFailed = false;
1126 bool tunPersist = false;
1127 bool wintun = false;
1128 bool allowLocalDnsResolvers = false;
1129 bool enableLegacyAlgorithms = false;
1130 bool enableNonPreferredDCO = false;
1131 bool merge = false;
1132 bool version = false;
1133 bool altProxy = false;
1134#if defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)
1135 bool dco = true;
1136#else
1137 bool dco = false;
1138#endif
1139 bool generateTunBuilderCaptureEvent = false;
1140 std::string epki_cert_fn;
1141 std::string epki_ca_fn;
1142 std::string epki_key_fn;
1143
1144#ifdef OPENVPN_REMOTE_OVERRIDE
1145 std::string remote_override_cmd;
1146#endif
1147 std::string write_url_fn;
1148
1149 int ch;
1150 optind = 1;
1151
1152 while ((ch = getopt_long(argc, argv, "6:ABCD:G:I:K:LM:P:QR:S:TU:W:X:YZ:ac:degh:o:O:b:jk:lmp:q:r:s:t:u:vwxz:", longopts, nullptr)) != -1)
1153 {
1154 switch (ch)
1155 {
1156 case 1: // ssl-debug
1157 sslDebugLevel = ::atoi(optarg);
1158 break;
1159 case 2: // --epki-cert
1160 epki_cert_fn = optarg;
1161 break;
1162 case 3: // --epki-ca
1163 epki_ca_fn = optarg;
1164 break;
1165 case 4: // --epki-key
1166 epki_key_fn = optarg;
1167 break;
1168#ifdef OPENVPN_REMOTE_OVERRIDE
1169 case 5: // --remote-override
1170 remote_override_cmd = optarg;
1171 break;
1172#endif
1173 case 6: // --tbc
1174 generateTunBuilderCaptureEvent = true;
1175 break;
1176 case 'e':
1177 eval = true;
1178 break;
1179 case 'T':
1180 self_test = true;
1181 break;
1182 case 'x':
1183 disableClientCert = true;
1184 break;
1185 case 'u':
1186 username = optarg;
1187 break;
1188 case 'p':
1189 password = optarg;
1190 break;
1191 case 'r':
1192 response = optarg;
1193 break;
1194 case 'P':
1195 proto = optarg;
1196 break;
1197 case '6':
1198 allowUnusedAddrFamilies = optarg;
1199 break;
1200 case 's':
1201 server = optarg;
1202 break;
1203 case 'R':
1204 port = optarg;
1205 break;
1206 case 'S':
1207 ssoMethods = optarg;
1208 break;
1209 case 't':
1210 timeout = ::atoi(optarg);
1211 break;
1212 case 'c':
1213 compress = optarg;
1214 break;
1215 case 'z':
1216 privateKeyPassword = optarg;
1217 break;
1218 case 'M':
1219 tlsVersionMinOverride = optarg;
1220 break;
1221 case 'X':
1222 tlsCertProfileOverride = optarg;
1223 break;
1224 case 'h':
1225 proxyHost = optarg;
1226 break;
1227 case 'q':
1228 proxyPort = optarg;
1229 break;
1230 case 'Q':
1231 enableNonPreferredDCO = true;
1232 break;
1233 case 'U':
1234 proxyUsername = optarg;
1235 break;
1236 case 'W':
1237 proxyPassword = optarg;
1238 break;
1239 case 'B':
1240 proxyAllowCleartextAuth = true;
1241 break;
1242 case 'A':
1243 altProxy = true;
1244 break;
1245 case 'd':
1246 dco = false;
1247 break;
1248 case 'g':
1249 googleDnsFallback = true;
1250 break;
1251 case 'a':
1252 autologinSessions = true;
1253 break;
1254 case 'Y':
1255 retryOnAuthFailed = true;
1256 break;
1257 case 'j':
1258 tunPersist = true;
1259 break;
1260 case 'w':
1261 wintun = true;
1262 break;
1263 case 'l':
1264 allowLocalDnsResolvers = true;
1265 break;
1266 case 'm':
1267 merge = true;
1268 break;
1269 case 'v':
1270 version = true;
1271 break;
1272 case 'k':
1273 {
1274 const std::string arg = optarg;
1275 if (arg == "bi" || arg == "bidirectional")
1276 defaultKeyDirection = -1;
1277 else if (arg == "0")
1278 defaultKeyDirection = 0;
1279 else if (arg == "1")
1280 defaultKeyDirection = 1;
1281 else
1282 OPENVPN_THROW_EXCEPTION("bad default key-direction: " << arg);
1283 }
1284 break;
1285 case 'D':
1286 dynamicChallengeCookie = optarg;
1287 break;
1288 case 'I':
1289 peer_info = optarg;
1290 break;
1291 case 'G':
1292 gremlin = optarg;
1293 break;
1294 case 'K':
1295 appCustomProtocols = optarg;
1296 break;
1297 case 'o':
1298 certcheck_cert_fn = optarg;
1299 break;
1300 case 'O':
1301 certcheck_pkey_fn = optarg;
1302 break;
1303 case 'b':
1304 certcheck_clientca = optarg;
1305 break;
1306 case 'L':
1307 enableLegacyAlgorithms = true;
1308 break;
1309 case 'Z':
1310 write_url_fn = optarg;
1311 break;
1312 default:
1313 throw usage();
1314 }
1315 }
1316 argc -= optind;
1317 argv += optind;
1318
1319 if (version)
1320 {
1321 std::cout << "OpenVPN cli 1.0" << std::endl;
1322 std::cout << ClientAPI::OpenVPNClientHelper::platform() << std::endl;
1323 std::cout << ClientAPI::OpenVPNClientHelper::copyright() << std::endl;
1324 }
1325 else if (self_test)
1326 {
1328 std::cout << clihelper.crypto_self_test();
1329 }
1330 else if (merge)
1331 {
1332 if (argc != 1)
1333 throw usage();
1334 std::cout << read_profile(argv[0], profile_content);
1335 }
1336 else
1337 {
1338 if (argc < 1)
1339 throw usage();
1340
1341 bool retry;
1342 do
1343 {
1344 retry = false;
1345
1347 config.guiVersion = "cli 1.0";
1348#if defined(OPENVPN_PLATFORM_WIN)
1349 int nargs = 0;
1350 auto argvw = CommandLineToArgvW(GetCommandLineW(), &nargs);
1351 UTF8 utf8(Win::utf8(argvw[nargs - 1]));
1352 config.content = read_profile(utf8.get(), profile_content);
1353#else
1354 config.content = read_profile(argv[0], profile_content);
1355#endif
1356 for (int i = 1; i < argc; ++i)
1357 {
1358 config.content += argv[i];
1359 config.content += '\n';
1360 }
1361 config.serverOverride = server;
1362 config.portOverride = port;
1363 config.protoOverride = proto;
1364 config.connTimeout = timeout;
1365 config.compressionMode = compress;
1366 config.allowUnusedAddrFamilies = allowUnusedAddrFamilies;
1367 config.privateKeyPassword = privateKeyPassword;
1368 config.tlsVersionMinOverride = tlsVersionMinOverride;
1369 config.tlsCertProfileOverride = tlsCertProfileOverride;
1370 config.disableClientCert = disableClientCert;
1371 config.proxyHost = proxyHost;
1372 config.proxyPort = proxyPort;
1373 config.proxyUsername = proxyUsername;
1374 config.proxyPassword = proxyPassword;
1375 config.proxyAllowCleartextAuth = proxyAllowCleartextAuth;
1376 config.altProxy = altProxy;
1377 config.dco = dco;
1378 config.generateTunBuilderCaptureEvent = generateTunBuilderCaptureEvent;
1379 config.defaultKeyDirection = defaultKeyDirection;
1380 config.sslDebugLevel = sslDebugLevel;
1381 config.googleDnsFallback = googleDnsFallback;
1382 config.autologinSessions = autologinSessions;
1383 config.retryOnAuthFailed = retryOnAuthFailed;
1384 config.tunPersist = tunPersist;
1385 config.gremlinConfig = gremlin;
1386 config.info = true;
1387 config.wintun = wintun;
1388 config.allowLocalDnsResolvers = allowLocalDnsResolvers;
1389 config.enableLegacyAlgorithms = enableLegacyAlgorithms;
1390 config.enableNonPreferredDCAlgorithms = enableNonPreferredDCO;
1391 config.ssoMethods = ssoMethods;
1392 config.appCustomProtocols = appCustomProtocols;
1393#if defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
1394 config.clockTickMS = 250;
1395#endif
1396
1397 if (!epki_cert_fn.empty())
1398 config.externalPkiAlias = "epki"; // dummy string
1399
1400 PeerInfo::Set::parse_flexible(peer_info, config.peerInfo);
1401
1402 // allow -s server override to reference a friendly name
1403 // in the config.
1404 // setenv SERVER <HOST>/<FRIENDLY_NAME>
1405 if (!config.serverOverride.empty())
1406 {
1408 const ClientAPI::EvalConfig cfg_eval = clihelper.eval_config(config);
1409 for (auto &se : cfg_eval.serverList)
1410 {
1411 if (config.serverOverride == se.friendlyName)
1412 {
1413 config.serverOverride = se.server;
1414 break;
1415 }
1416 }
1417 }
1418
1419 if (eval)
1420 {
1422 const ClientAPI::EvalConfig cfg_eval = clihelper.eval_config(config);
1423 std::cout << "EVAL PROFILE" << std::endl;
1424 std::cout << "error=" << cfg_eval.error << std::endl;
1425 std::cout << "message=" << cfg_eval.message << std::endl;
1426 std::cout << "userlockedUsername=" << cfg_eval.userlockedUsername << std::endl;
1427 std::cout << "profileName=" << cfg_eval.profileName << std::endl;
1428 std::cout << "friendlyName=" << cfg_eval.friendlyName << std::endl;
1429 std::cout << "autologin=" << cfg_eval.autologin << std::endl;
1430 std::cout << "externalPki=" << cfg_eval.externalPki << std::endl;
1431 std::cout << "staticChallenge=" << cfg_eval.staticChallenge << std::endl;
1432 std::cout << "staticChallengeEcho=" << cfg_eval.staticChallengeEcho << std::endl;
1433 std::cout << "privateKeyPasswordRequired=" << cfg_eval.privateKeyPasswordRequired << std::endl;
1434 std::cout << "allowPasswordSave=" << cfg_eval.allowPasswordSave << std::endl;
1435
1436 if (!config.serverOverride.empty())
1437 std::cout << "server=" << config.serverOverride << std::endl;
1438
1439 for (size_t i = 0; i < cfg_eval.serverList.size(); ++i)
1440 {
1441 const ClientAPI::ServerEntry &se = cfg_eval.serverList[i];
1442 std::cout << '[' << i << "] " << se.server << '/' << se.friendlyName << std::endl;
1443 }
1444 }
1445 else
1446 {
1447#if defined(USE_NETCFG)
1448 auto dbus_system = DBus::Connection::Create(DBus::BusType::SYSTEM);
1449 NetCfgTunBuilder<Client> client(dbus_system);
1450#else
1451 Client client;
1452#endif
1453 const ClientAPI::EvalConfig eval = client.eval_config(config);
1454 if (eval.error)
1455 OPENVPN_THROW_EXCEPTION("eval config error: " << eval.message);
1456 if (eval.autologin)
1457 {
1458 if (!username.empty() || !password.empty())
1459 std::cout << "NOTE: creds were not needed" << std::endl;
1460
1461 // still provide proxy credentials if given
1462 if (!proxyUsername.empty())
1463 {
1465 creds.http_proxy_user = proxyUsername;
1466 creds.http_proxy_pass = proxyPassword;
1467 ClientAPI::Status creds_status = client.provide_creds(creds);
1468 if (creds_status.error)
1469 OPENVPN_THROW_EXCEPTION("creds error: " << creds_status.message);
1470 }
1471 }
1472 else
1473 {
1474 if (username.empty())
1475 OPENVPN_THROW_EXCEPTION("need creds");
1477 if (password.empty() && dynamicChallengeCookie.empty())
1478 password = get_password("Password:");
1479 creds.username = username;
1480 creds.password = password;
1481 creds.http_proxy_user = proxyUsername;
1482 creds.http_proxy_pass = proxyPassword;
1483 creds.response = response;
1484 creds.dynamicChallengeCookie = dynamicChallengeCookie;
1485 ClientAPI::Status creds_status = client.provide_creds(creds);
1486 if (creds_status.error)
1487 OPENVPN_THROW_EXCEPTION("creds error: " << creds_status.message);
1488 }
1489
1490 client.certcheck_clientca_fn = certcheck_clientca;
1491
1492 // external PKI
1493 if (!epki_cert_fn.empty())
1494 {
1495 client.epki_cert = read_text_utf8(epki_cert_fn);
1496 if (!epki_ca_fn.empty())
1497 client.epki_ca = read_text_utf8(epki_ca_fn);
1498#if defined(USE_MBEDTLS) || defined(USE_OPENSSL)
1499 if (!epki_key_fn.empty())
1500 {
1501 const std::string epki_key_txt = read_text_utf8(epki_key_fn);
1502#if defined(USE_MBEDTLS)
1503
1504 auto mbedrng = std::make_unique<MbedTLSRandom>();
1505 client.epki_ctx.parse(epki_key_txt, "EPKI", privateKeyPassword, *mbedrng);
1506#else
1507 client.epki_pkey.parse_pem(epki_key_txt, "epki private key", nullptr);
1508#endif
1509 }
1510 else
1511 OPENVPN_THROW_EXCEPTION("--epki-key must be specified");
1512#endif
1513 }
1514
1515 // Certcheck
1516 if (!certcheck_cert_fn.empty())
1517 {
1518 client.certcheck_cert = read_text_utf8(certcheck_cert_fn);
1519 const std::string epki_key_txt = read_text_utf8(certcheck_pkey_fn);
1520#ifdef USE_OPENSSL
1521 client.certcheck_pkey.parse_pem(epki_key_txt, "certcheck private key", nullptr);
1522#else
1523 auto mbedrng = std::make_unique<MbedTLSRandom>();
1524 client.certcheck_pkey.parse(epki_key_txt, "Certcheck", privateKeyPassword, *mbedrng);
1525#endif
1526 }
1527
1528#ifdef OPENVPN_REMOTE_OVERRIDE
1529 client.set_remote_override_cmd(remote_override_cmd);
1530#endif
1531
1532 client.set_write_url_fn(write_url_fn);
1533
1534 std::cout << "CONNECTING..." << std::endl;
1535
1536 // start the client thread
1537 start_thread(client);
1538
1539 // Get dynamic challenge response
1540 if (client.is_dynamic_challenge())
1541 {
1542 std::cout << "ENTER RESPONSE" << std::endl;
1543 std::getline(std::cin, response);
1544 if (!response.empty())
1545 {
1546 dynamicChallengeCookie = client.dynamic_challenge_cookie();
1547 retry = true;
1548 }
1549 }
1550 else
1551 {
1552 // print closing stats
1553 client.print_stats();
1554 }
1555 }
1556#ifdef USE_OPENSSL
1557 /* Since this is a debug/test program we check if the
1558 * internal OpenSSL error stack is empty on exit and
1559 * print the warnings otherwise */
1560 if (ERR_peek_error())
1561 {
1562 throw OpenSSLException{};
1563 }
1564#endif
1565 } while (retry);
1566 }
1567 }
1568 else
1569 throw usage();
1570 }
1571 catch (const usage &)
1572 {
1573 std::cout << "OpenVPN Client (ovpncli)" << std::endl;
1574 std::cout << "usage: cli [options] <config-file> [extra-config-directives...]" << std::endl;
1575 std::cout << "--version, -v : show version info" << std::endl;
1576 std::cout << "--eval, -e : evaluate profile only (standalone)" << std::endl;
1577 std::cout << "--merge, -m : merge profile into unified format (standalone)" << std::endl;
1578 std::cout << "--username, -u : username" << std::endl;
1579 std::cout << "--password, -p : password" << std::endl;
1580 std::cout << "--response, -r : static response" << std::endl;
1581 std::cout << "--dc, -D : dynamic challenge/response cookie" << std::endl;
1582 std::cout << "--proto, -P : protocol override (udp|tcp)" << std::endl;
1583 std::cout << "--server, -s : server override" << std::endl;
1584 std::cout << "--port, -R : port override" << std::endl;
1585#ifdef OPENVPN_REMOTE_OVERRIDE
1586 std::cout << "--remote-override : command to run to generate next remote (returning host,ip,port,proto)" << std::endl;
1587#endif
1588 std::cout << "--allowAF, -6 : Allow unused address families (yes|no|default)" << std::endl;
1589 std::cout << "--timeout, -t : timeout" << std::endl;
1590 std::cout << "--compress, -c : compression mode (yes|no|asym)" << std::endl;
1591 std::cout << "--pk-password, -z : private key password" << std::endl;
1592 std::cout << "--tvm-override, -M : tls-version-min override (disabled, default, tls_1_x)" << std::endl;
1593 std::cout << "--legacy-algorithms, -L: Enable legacy algorithm (OpenSSL legacy provider)" << std::endl;
1594 std::cout << "--non-preferred-algorithms, -Q: Enables non preferred data channel algorithms" << std::endl;
1595 std::cout << "--tcprof-override, -X : tls-cert-profile override ("
1596 <<
1597#ifdef OPENVPN_ALLOW_INSECURE_CERTPROFILE
1598 "insecure, "
1599 <<
1600#endif
1601 "legacy, preferred, etc.)" << std::endl;
1602 std::cout << "--proxy-host, -h : HTTP proxy hostname/IP" << std::endl;
1603 std::cout << "--proxy-port, -q : HTTP proxy port" << std::endl;
1604 std::cout << "--proxy-username, -U : HTTP proxy username" << std::endl;
1605 std::cout << "--proxy-password, -W : HTTP proxy password" << std::endl;
1606 std::cout << "--proxy-basic, -B : allow HTTP basic auth" << std::endl;
1607 std::cout << "--alt-proxy, -A : enable alternative proxy module" << std::endl;
1608#if defined(ENABLE_KOVPN) || defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)
1609 std::cout << "--no-dco, -d : disable data channel offload" << std::endl;
1610#endif
1611 std::cout << "--cache-password, -C : cache password" << std::endl;
1612 std::cout << "--no-cert, -x : disable client certificate" << std::endl;
1613 std::cout << "--def-keydir, -k : default key direction ('bi', '0', or '1')" << std::endl;
1614 std::cout << "--ssl-debug : SSL debug level" << std::endl;
1615 std::cout << "--google-dns, -g : enable Google DNS fallback" << std::endl;
1616 std::cout << "--auto-sess, -a : request autologin session" << std::endl;
1617 std::cout << "--auth-retry, -Y : retry connection on auth failure" << std::endl;
1618 std::cout << "--persist-tun, -j : keep TUN interface open across reconnects" << std::endl;
1619 std::cout << "--wintun, -w : use WinTun instead of TAP-Windows6 on Windows" << std::endl;
1620 std::cout << "--peer-info, -I : peer info key/value list in the form K1=V1,K2=V2,... or @kv.json" << std::endl;
1621 std::cout << "--gremlin, -G : gremlin info (send_delay_ms, recv_delay_ms, send_drop_prob, recv_drop_prob)" << std::endl;
1622 std::cout << "--epki-ca : simulate external PKI cert supporting intermediate/root certs" << std::endl;
1623 std::cout << "--epki-cert : simulate external PKI cert" << std::endl;
1624 std::cout << "--epki-key : simulate external PKI private key" << std::endl;
1625 std::cout << "--sso-methods : auth pending methods to announce via IV_SSO" << std::endl;
1626 std::cout << "--write-url, -Z : write INFO URL to file" << std::endl;
1627 std::cout << "--tbc : generate INFO_JSON/TUN_BUILDER_CAPTURE event" << std::endl;
1628 std::cout << "--app-custom-protocols, -K : ACC protocols to advertise" << std::endl;
1629 std::cout << "--certcheck-cert, -o : path to certificate PEM for certcheck" << std::endl;
1630 std::cout << "--certcheck-pkey, -O : path to decrypted pkey PEM for certcheck" << std::endl;
1631 std::cout << "--certcheck-clientca, -b : path to decrypted pkey PEM for certcheck CA" << std::endl;
1632 ret = 2;
1633 }
1634 return ret;
1635}
1636
1637#ifndef OPENVPN_OVPNCLI_OMIT_MAIN
1638
1639int main(int argc, char *argv[])
1640{
1641 int ret = 0;
1642
1643#ifdef OPENVPN_LOG_LOGBASE_H
1644 LogBaseSimple log;
1645#endif
1646
1647#if defined(OPENVPN_PLATFORM_WIN)
1648 SetConsoleOutputCP(CP_UTF8);
1649#endif
1650
1651 try
1652 {
1653 ret = openvpn_client(argc, argv, nullptr);
1654 }
1655 catch (const std::exception &e)
1656 {
1657 std::cout << "Main thread exception: " << e.what() << std::endl;
1658 ret = 1;
1659 }
1660 return ret;
1661}
1662
1663#endif
bool socket_protect(openvpn_io::detail::socket_type socket, std::string remote, bool ipv6) override
Definition cli.cpp:199
void handle_dpc1_protocol(const ClientAPI::AppCustomControlMessageEvent &acev)
Definition cli.cpp:341
void print_stats()
Definition cli.cpp:251
std::string epki_cert
Definition cli.cpp:234
void handle_certcheck_request()
Begin a cck1 (certcheck) handshake in response to a dpc1 server request.
Definition cli.cpp:390
boolean pause_on_connection_timeout()
Definition Client.java:123
void log(ClientAPI_LogInfo loginfo)
Definition Client.java:106
ClockTickAction
Definition cli.cpp:214
@ CT_RECONNECT
Definition cli.cpp:217
@ CT_STATS
Definition cli.cpp:220
@ CT_PAUSE
Definition cli.cpp:218
@ CT_STOP
Definition cli.cpp:216
@ CT_UNDEF
Definition cli.cpp:215
@ CT_RESUME
Definition cli.cpp:219
void stop()
Definition Client.java:68
std::string certcheck_cert
file name of properly encoded server cert
Definition cli.cpp:243
void connect()
Definition Client.java:60
void open_url(std::string url_str)
Definition cli.cpp:422
std::string dynamic_challenge_cookie()
Definition cli.cpp:228
std::string dc_cookie
Definition cli.cpp:805
std::string write_url_fn
Definition cli.cpp:813
void acc_event(const openvpn::ClientAPI::AppCustomControlMessageEvent &event) override
Definition openvpn.cpp:1037
volatile ClockTickAction clock_tick_action
Definition cli.cpp:807
static constexpr char certcheck_init_verb[]
Definition cli.cpp:210
void event(const ClientAPI::Event &ev) override
Definition cli.cpp:279
void clock_tick() override
Definition cli.cpp:469
std::string epki_ca
Definition cli.cpp:233
StrongRandomAPI::Ptr rng
Definition cli.cpp:806
std::mutex log_mutex
Definition cli.cpp:804
void set_write_url_fn(const std::string &fn)
Definition cli.cpp:272
void external_pki_cert_request(ClientAPI_ExternalPKICertRequest req)
Definition Client.java:93
static int rng_callback(void *arg, unsigned char *data, size_t len)
Definition cli.cpp:751
void external_pki_sign_request(ClientAPI_ExternalPKISignRequest req)
Definition Client.java:100
bool is_dynamic_challenge() const
Definition cli.cpp:223
std::string certcheck_clientca_fn
file name of properly encoded client ca
Definition cli.cpp:244
void set_clock_tick_action(const ClockTickAction action)
Definition cli.cpp:246
std::string to_string() const
Definition argv.hpp:30
std::string encode(const V &data) const
Definition base64.hpp:139
size_t decode(void *data, size_t len, const std::string &str) const
Definition base64.hpp:186
EvalConfig eval_config(const Config &config)
Definition ovpncli.cpp:761
static bool parse_dynamic_challenge(const std::string &cookie, DynamicChallenge &dc)
Definition ovpncli.cpp:812
void send_app_control_channel_msg(const std::string &protocol, const std::string &msg)
Definition ovpncli.cpp:1322
void post_cc_msg(const std::string &msg)
Definition ovpncli.cpp:1312
void start_cert_check_epki(const std::string &alias, const std::optional< const std::string > &ca)
Start up the cert check handshake using the given epki_alias string.
Definition ovpncli.cpp:1369
virtual void remote_override(RemoteOverride &)
Definition ovpncli.cpp:1155
std::vector< long long > stats_bundle() const
Definition ovpncli.cpp:1184
void start_cert_check(const std::string &client_cert, const std::string &clientkey, const std::optional< const std::string > &ca=std::nullopt)
Start up the cert check handshake using the given certs and key.
Definition ovpncli.cpp:1352
static std::string stats_name(int index)
Definition ovpncli.cpp:1164
void pause(const std::string &reason)
Definition ovpncli.cpp:1282
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1194
size_t length() const
Returns the length of the buffer.
Definition buffer.hpp:1188
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1242
T * data()
Get a mutable pointer to the start of the array.
Definition buffer.hpp:1450
mbedtls_pk_context * get() const
Definition pkctx.hpp:133
std::string render_pem() const
Definition pkctx.hpp:117
::EVP_PKEY * obj() const
Definition pkey.hpp:86
bool defined() const
Definition pkey.hpp:82
static const char * to_string(const mbedtls_md_type_t t)
Definition pkcs1.hpp:32
T alg_from_prefix(Buffer &buf) const
Definition pkcs1.hpp:101
const std::string & error() const
Definition merge.hpp:71
const char * status_string() const
Definition merge.hpp:95
const std::string & profile_content() const
Definition merge.hpp:83
Status status() const
Definition merge.hpp:67
void reset() noexcept
Points this RCPtr<T> to nullptr safely.
Definition rc.hpp:290
virtual bool rand_bytes_noexcept(unsigned char *buf, size_t size)=0
Fill a buffer with random bytes without throwing exceptions.
virtual bool tun_builder_set_dns_options(const DnsOptions &dns)
Callback to set DNS related options to VPN interface.
Definition base.hpp:195
virtual bool tun_builder_add_address(const std::string &address, int prefix_length, const std::string &gateway, bool ipv6, bool net30)
Callback to add a network address to the VPN interface.
Definition base.hpp:101
virtual bool tun_builder_add_route(const std::string &address, int prefix_length, int metric, bool ipv6)
Callback to add a route to the VPN interface.
Definition base.hpp:158
virtual void tun_builder_teardown(bool disconnect)
Indicates that tunnel is being torn down.
Definition base.hpp:417
virtual bool tun_builder_reroute_gw(bool ipv4, bool ipv6, unsigned int flags)
Callback to reroute the default gateway to the VPN interface.
Definition base.hpp:139
virtual int tun_builder_establish()
Callback to establish the VPN tunnel.
Definition base.hpp:361
virtual bool tun_builder_set_mtu(int mtu)
Callback to set the MTU of the VPN interface.
Definition base.hpp:211
virtual bool tun_builder_set_remote_address(const std::string &address, bool ipv6)
Callback to set the address of the remote server.
Definition base.hpp:83
virtual bool tun_builder_set_session_name(const std::string &name)
Sets the session name for the TunBuilder.
Definition base.hpp:226
virtual bool tun_builder_new()
Callback to construct a new TunBuilder. This function should be called first.
Definition base.hpp:50
int main(int argc, char *argv[])
Definition cli.cpp:1639
static std::string read_profile(const char *fn, const std::string *profile_content)
Definition cli.cpp:846
static Client * the_client
Definition cli.cpp:820
static void handler(int signum)
Definition cli.cpp:961
static void worker_thread()
Definition cli.cpp:822
int openvpn_client(int argc, char *argv[], const std::string *profile_content)
Definition cli.cpp:1022
static void start_thread(Client &client)
Definition cli.cpp:1001
#define OPENVPN_SIMPLE_EXCEPTION(C)
Definition exception.hpp:75
#define OPENVPN_THROW_EXCEPTION(stuff)
#define OPENVPN_LOG(args)
#define OPENVPN_LOG_STRING(str)
constexpr BufferFlags GROW(1u<< 2)
if enabled, buffer will grow (otherwise buffer_full exception will be thrown)
constexpr BufferFlags ARRAY(1u<< 3)
if enabled, use as array
char * utf8(wchar_t *str)
Definition unicode.hpp:57
int strcasecmp(const char *s1, const char *s2)
Definition string.hpp:29
bool starts_with(const STRING &str, const std::string &prefix)
Definition string.hpp:79
std::vector< T > split(const T &str, const typename T::value_type sep, const int maxsplit=-1)
Definition string.hpp:492
std::string first_line(const std::string &str)
Definition string.hpp:272
std::string render_hex_generic(const V &data, const bool caps=false)
Definition hexstr.hpp:230
std::string read_text_utf8(const std::string &filename, const std::uint64_t max_size=0)
Definition file.hpp:136
std::string to_string(const T &t)
Convert a value to a string.
Definition to_string.hpp:45
std::string openssl_error()
Definition error.hpp:235
CleanupType< F > Cleanup(F method) noexcept
Definition cleanup.hpp:43
const Base64 * base64
Definition base64.hpp:299
void write_string(const std::string &filename, const std::string &str)
Definition file.hpp:199
std::string get_password(const char *prompt)
Definition getpw.hpp:29
std::string date_time()
Definition timestr.hpp:139
int system_cmd(const std::string &cmd, const Argv &argv, RedirectBase *redir, const Environ *env, const sigset_t *sigmask)
Definition process.hpp:90
Struct containing configuration details parsed from an OpenVPN configuration file.
Definition ovpncli.hpp:56
std::vector< ServerEntry > serverList
Definition ovpncli.hpp:103
All DNS options set with the –dns or –dhcp-option directive.
static void parse_flexible(const std::string &src, SET &dest)
Definition peerinfo.hpp:97
const auto metric
reroute_gw flags
proxy_host_port port
const TunBuilderCapture::Ptr tbc(new TunBuilderCapture)
proxy_autoconfig_url url
remote_address ipv6
remote_address address
reroute_gw ipv4
std::string ret
std::ostringstream os
static const char config[]
static std::stringstream out
Definition test_path.cpp:10