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#ifdef USE_MBEDTLS
74#elif defined(USE_OPENSSL)
75#include <openssl/evp.h>
76#include <openssl/opensslv.h>
77#if OPENSSL_VERSION_NUMBER >= 0x30000000L
78#include <openssl/core_names.h>
79#endif
80#endif
81
82#ifdef OPENVPN_PLATFORM_WIN
84#include <shellapi.h>
85#endif
86
87#ifdef USE_NETCFG
88#include "client/core-client-netcfg.hpp"
89#endif
90
91#ifdef OPENVPN_PLATFORM_LINUX
92
94
95// we use a static polymorphism and define a
96// platform-specific TunSetup class, responsible
97// for setting up tun device
98#define TUN_CLASS_SETUP TunLinuxSetup::Setup<TUN_LINUX>
100#elif defined(OPENVPN_PLATFORM_MAC)
102#define TUN_CLASS_SETUP TunMac::Setup
103#endif
104
105using namespace openvpn;
106
107namespace {
109}
110
111#ifdef USE_TUN_BUILDER
113{
114 public:
115 bool tun_builder_new() override
116 {
117 return true;
118 }
119
120 int tun_builder_establish() override
121 {
122 tun.reset(new TUN_CLASS_SETUP());
123
124 TUN_CLASS_SETUP::Config config;
125 config.layer = Layer(Layer::Type::OSI_LAYER_3);
126 // no need to add bypass routes on establish since we do it on socket_protect
127 config.add_bypass_routes_on_establish = false;
128 return tun->establish(tbc, &config, nullptr, std::cout);
129 }
130
131 bool tun_builder_add_address(const std::string &address,
132 int prefix_length,
133 const std::string &gateway, // optional
134 bool ipv6,
135 bool net30) override
136 {
137 return tbc.tun_builder_add_address(address, prefix_length, gateway, ipv6, net30);
138 }
139
140 bool tun_builder_add_route(const std::string &address,
141 int prefix_length,
142 int metric,
143 bool ipv6) override
144 {
145 return tbc.tun_builder_add_route(address, prefix_length, metric, ipv6);
146 }
147
149 bool ipv6,
150 unsigned int flags) override
151 {
152 return tbc.tun_builder_reroute_gw(ipv4, ipv6, flags);
153 }
154
155 bool tun_builder_set_remote_address(const std::string &address,
156 bool ipv6) override
157 {
158 return tbc.tun_builder_set_remote_address(address, ipv6);
159 }
160
161 bool tun_builder_set_session_name(const std::string &name) override
162 {
163 return tbc.tun_builder_set_session_name(name);
164 }
165
166 void tun_builder_teardown(bool disconnect) override
167 {
168 std::ostringstream os;
169 auto os_print = Cleanup([&os]()
170 { OPENVPN_LOG_STRING(os.str()); });
171 tun->destroy(os);
172 }
173
174 bool socket_protect([[maybe_unused]] openvpn_io::detail::socket_type socket, std::string remote, bool ipv6) override
175 {
176 std::ostringstream os;
177 auto os_print = Cleanup([&os]()
178 { OPENVPN_LOG_STRING(os.str()); });
179 return tun->add_bypass_route(remote, ipv6, os);
180 }
181
182 bool tun_builder_set_dns_options(const DnsOptions &dns) override
183 {
184 return tbc.tun_builder_set_dns_options(dns);
185 }
186
187 bool tun_builder_set_mtu(int mtu) override
188 {
189 return tbc.tun_builder_set_mtu(mtu);
190 }
191
192 private:
193 TUN_CLASS_SETUP::Ptr tun = new TUN_CLASS_SETUP();
195};
196#else // USE_TUN_BUILDER
198{
199 public:
200 bool socket_protect(openvpn_io::detail::socket_type socket, std::string remote, bool ipv6) override
201 {
202 std::cout << "NOT IMPLEMENTED: *** socket_protect " << socket << " " << remote << "\n";
203 return true;
204 }
205};
206#endif
207
208class Client : public ClientBase
209{
210 // This is the protocol tag that appcontrol expects to trigger the certcheck start.
211 static constexpr char certcheck_init_verb[] = "dpc1"; // TODO: std::string in C++ '20
212
213 public:
223
225 {
226 return !dc_cookie.empty();
227 }
228
230 {
231 return dc_cookie;
232 }
233
234 std::string epki_ca;
235 std::string epki_cert;
236#ifdef USE_MBEDTLS
237 MbedTLSPKI::PKContext epki_ctx; // external PKI context
238 MbedTLSPKI::PKContext certcheck_pkey; // external PKI context
239#elif defined(USE_OPENSSL)
241 openvpn::OpenSSLPKI::PKey certcheck_pkey;
242#endif
243
244 std::string certcheck_cert;
246
248 {
249 clock_tick_action = action;
250 }
251
253 {
254 const int n = stats_n();
255 std::vector<long long> stats = stats_bundle();
256
257 std::cout << "STATS:\n";
258 for (int i = 0; i < n; ++i)
259 {
260 const long long value = stats[i];
261 if (value)
262 std::cout << " " << stats_name(i) << " : " << value << "\n";
263 }
264 }
265
266#ifdef OPENVPN_REMOTE_OVERRIDE
267 void set_remote_override_cmd(const std::string &cmd)
268 {
269 remote_override_cmd = cmd;
270 }
271#endif
272
273 void set_write_url_fn(const std::string &fn)
274 {
275 write_url_fn = fn;
276 }
277
278
279 private:
280 void event(const ClientAPI::Event &ev) override
281 {
282 std::cout << date_time() << " EVENT: " << ev.name;
283 if (!ev.info.empty())
284 std::cout << ' ' << ev.info;
285 if (ev.fatal)
286 std::cout << " [FATAL-ERR]";
287 else if (ev.error)
288 std::cout << " [ERR]";
289 std::cout << "\n";
290 if (ev.name == "DYNAMIC_CHALLENGE")
291 {
292 dc_cookie = ev.info;
293
296 {
297 std::cout << "DYNAMIC CHALLENGE\n";
298 std::cout << "challenge: " << dc.challenge << "\n";
299 std::cout << "echo: " << dc.echo << "\n";
300 std::cout << "responseRequired: " << dc.responseRequired << "\n";
301 std::cout << "stateID: " << dc.stateID << "\n";
302 }
303 }
304 else if (ev.name == "PROXY_NEED_CREDS")
305 {
306 std::cout << "PROXY_NEED_CREDS " << ev.info << "\n";
307 }
308 else if (ev.name == "INFO")
309 {
310 if (ev.info.starts_with("OPEN_URL:"))
311 {
312 open_url(ev.info.substr(9));
313 }
314 else if (ev.info.starts_with("WEB_AUTH:"))
315 {
316 auto extra = ev.info.substr(9);
317 size_t flagsend = extra.find(':');
318 if (flagsend != std::string::npos)
319 {
320 open_url(extra.substr(flagsend + 1));
321 }
322 }
323 else if (ev.info.starts_with("CR_TEXT:"))
324 {
325 std::string cr_response;
326 std::cout << "\n\n"
327 << ev.info.substr(8) << ": ";
328 std::getline(std::cin, cr_response);
329 post_cc_msg("CR_RESPONSE," + base64->encode(cr_response));
330 }
331 else
332 {
333 std::cout << "Unrecognized INFO/INFO_PRE message: " << ev.info << "\n";
334 }
335 }
336 else
337 {
338 std::cout << "Received event " << ev.name << " " << ev.info << "\n";
339 }
340 }
341
343 {
344 if (acev.payload.starts_with("{\"dpc_request\"") && acev.payload.find("certificate") != std::string::npos)
345 {
346 std::cout << "ACC CERTCHECK challenge initiated\n";
348 }
349 else if (acev.payload.starts_with("{\"dpc_request\"") && acev.payload.find("client_info") != std::string::npos)
350 {
351
352 std::string fakeResponse{R"({"dpc_response": {
353 "client_info" : {
354 "os" : {"type" : "Windows", "version" : "10.0.19045" }
355 }
356 }})"};
357
358 std::cout << "ACC DPC1: sending fake client info:" << fakeResponse << "\n";
359 send_app_control_channel_msg("dpc1", fakeResponse);
360 }
361 else if (acev.payload.starts_with("{\"dpc_request\""))
362 {
363 std::cout << "Cannot parse dpc request message:" << acev.payload << "\n";
364 }
365 else
366 {
367 std::cout << "Cannot parse device posture message:" << acev.payload << "\n";
368 }
369 }
370
375 void acc_event(const ClientAPI::AppCustomControlMessageEvent &acev) override
376 {
377 if (acev.protocol == "internal:supported_protocols")
378 {
379 std::cout << "Client/server common app custom control protocols: " << acev.payload << "\n";
380 }
381 else if (acev.protocol == certcheck_init_verb)
382 {
384 }
385 else
386 {
387 std::cout << "received unhandled app custom control message for protocol " << acev.protocol
388 << " msg payload: " << acev.payload << "\n";
389 }
390 }
396 {
397 if (certcheck_cert.empty() || !certcheck_pkey.defined())
398 {
399 std::cout << "ACC CERTCHECK FAILED: MISSING PARAMETERS\n";
400 return;
401 }
402
403 std::string clientca_pem = "";
404
405 if (!certcheck_clientca_fn.empty())
407
408 if constexpr (false)
409 {
410 /* cli.cpp typically goes the long complicated way of using the epki API to do its checks */
411
412 if (!clientca_pem.empty())
413 {
414 start_cert_check(certcheck_cert, certcheck_pkey.render_pem(), clientca_pem);
415 }
416 else
417 {
418 start_cert_check(certcheck_cert, certcheck_pkey.render_pem());
419 }
420 }
421 else
422 {
423 start_cert_check_epki("certcheck", clientca_pem);
424 }
425 }
426
427 void open_url(const std::string &url_str)
428 {
429 if (url_str.starts_with("http://") || url_str.starts_with("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 << "\n";
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 << "\n";
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\n";
482 stop();
483 break;
484 case CT_RECONNECT:
485 std::cout << "signal: CT_RECONNECT\n";
486 reconnect(0);
487 break;
488 case CT_PAUSE:
489 std::cout << "signal: CT_PAUSE\n";
490 pause("clock-tick pause");
491 break;
492 case CT_RESUME:
493 std::cout << "signal: CT_RESUME\n";
494 resume();
495 break;
496 case CT_STATS:
497 std::cout << "signal: CT_STATS\n";
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#ifdef 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#ifndef 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...\n";
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 << ": \n";
836 std::cout << connect_status.message << "\n";
837 }
838 }
839 catch (const std::exception &e)
840 {
841 std::cout << "Connect thread exception: " << e.what() << "\n";
842 }
843 std::cout << "Thread finished\n";
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
851 ProfileMerge pm(fn,
852 "ovpn",
853 "",
858 OPENVPN_THROW_EXCEPTION("merge config error: " << pm.status_string() << " : " << pm.error());
859 return pm.profile_content();
860}
861
862#ifdef OPENVPN_PLATFORM_WIN
863
864static void start_thread(Client &client)
865{
866 // Set Windows title bar
867 const std::string title_text = "F2:Stats F3:Reconnect F4:Stop F5:Pause";
869 Win::Console::Input console{false};
870
871 // start connect thread
872 std::unique_ptr<std::thread> thread;
873 volatile bool thread_exit = false;
874 the_client = &client;
875 thread.reset(new std::thread([&thread_exit]()
876 {
878 thread_exit = true; }));
879
880 // wait for connect thread to exit, also check for keypresses
881 while (!thread_exit)
882 {
883 while (true)
884 {
885 const unsigned int c = console.get();
886 if (!c)
887 break;
888 else if (c == 0x3C) // F2
890 else if (c == 0x3D) // F3
892 else if (c == 0x3E) // F4
893 the_client->stop();
894 else if (c == 0x3F) // F5
895 the_client->pause("user-pause");
896 }
897 Sleep(1000);
898 }
899
900 // wait for connect thread to exit
901 thread->join();
902
903 the_client = nullptr;
904}
905
906#elif defined(OPENVPN_OVPNCLI_SINGLE_THREAD)
907
908static void handler(int signum)
909{
910 switch (signum)
911 {
912 case SIGTERM:
913 case SIGINT:
914 if (the_client)
916 break;
917 case SIGHUP:
918 if (the_client)
920 break;
921 case SIGUSR1:
922 if (the_client)
924 break;
925 case SIGUSR2:
926 {
927 // toggle pause/resume
928 static bool hup = false;
929 if (the_client)
930 {
931 if (hup)
933 else
935 hup = !hup;
936 }
937 }
938 break;
939 default:
940 break;
941 }
942}
943
944static void start_thread(Client &client)
945{
946 the_client = &client;
947
948 // capture signals that might occur while we're in worker_thread
950
951 // run the client
953
954 the_client = nullptr;
955}
956
957#else
958
959static void handler(int signum)
960{
961 switch (signum)
962 {
963 case SIGTERM:
964 case SIGINT:
965 std::cout << "received stop signal " << signum << "\n";
966 if (the_client)
967 the_client->stop();
968 break;
969 case SIGHUP:
970 std::cout << "received reconnect signal " << signum << "\n";
971 if (the_client)
973 break;
974 case SIGUSR1:
975 if (the_client)
977 break;
978 case SIGUSR2:
979 {
980 // toggle pause/resume
981 static bool hup = false;
982 std::cout << "received pause/resume toggle signal " << signum << "\n";
983 if (the_client)
984 {
985 if (hup)
987 else
988 the_client->pause("pause-resume-signal");
989 hup = !hup;
990 }
991 }
992 break;
993 default:
994 std::cout << "received unknown signal " << signum << "\n";
995 break;
996 }
997}
998
999static void start_thread(Client &client)
1000{
1001 std::unique_ptr<std::thread> thread;
1002
1003 // start connect thread
1004 the_client = &client;
1005 thread.reset(new std::thread([]()
1006 { worker_thread(); }));
1007
1008 {
1009 // catch signals that might occur while we're in join()
1011
1012 // wait for connect thread to exit
1013 thread->join();
1014 }
1015 the_client = nullptr;
1016}
1017
1018#endif
1019
1020int openvpn_client(int argc, char *argv[], const std::string *profile_content)
1021{
1022 // clang-format off
1023 static const struct option longopts[] = {
1024 {.name = "username", .has_arg = required_argument, .flag = nullptr, .val = 'u'},
1025 {.name = "password", .has_arg = required_argument, .flag = nullptr, .val = 'p'},
1026 {.name = "response", .has_arg = required_argument, .flag = nullptr, .val = 'r'},
1027 {.name = "dc", .has_arg = required_argument, .flag = nullptr, .val = 'D'},
1028 {.name = "proto", .has_arg = required_argument, .flag = nullptr, .val = 'P'},
1029 {.name = "ipv6", .has_arg = required_argument, .flag = nullptr, .val = '6'},
1030 {.name = "server", .has_arg = required_argument, .flag = nullptr, .val = 's'},
1031 {.name = "port", .has_arg = required_argument, .flag = nullptr, .val = 'R'},
1032 {.name = "timeout", .has_arg = required_argument, .flag = nullptr, .val = 't'},
1033 {.name = "compress", .has_arg = required_argument, .flag = nullptr, .val = 'c'},
1034 {.name = "pk-password", .has_arg = required_argument, .flag = nullptr, .val = 'z'},
1035 {.name = "tvm-override", .has_arg = required_argument, .flag = nullptr, .val = 'M'},
1036 {.name = "proxy-host", .has_arg = required_argument, .flag = nullptr, .val = 'h'},
1037 {.name = "proxy-port", .has_arg = required_argument, .flag = nullptr, .val = 'q'},
1038 {.name = "proxy-username", .has_arg = required_argument, .flag = nullptr, .val = 'U'},
1039 {.name = "proxy-password", .has_arg = required_argument, .flag = nullptr, .val = 'W'},
1040 {.name = "peer-info", .has_arg = required_argument, .flag = nullptr, .val = 'I'},
1041 {.name = "acc-protos", .has_arg = required_argument, .flag = nullptr, .val = 'K'},
1042 {.name = "gremlin", .has_arg = required_argument, .flag = nullptr, .val = 'G'},
1043 {.name = "proxy-basic", .has_arg = no_argument, .flag = nullptr, .val = 'B'},
1044 {.name = "alt-proxy", .has_arg = no_argument, .flag = nullptr, .val = 'A'},
1045#if defined(ENABLE_KOVPN) || defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)
1046 {.name = "no-dco", .has_arg = no_argument, .flag = nullptr, .val = 'd'},
1047#endif
1048 {.name = "eval", .has_arg = no_argument, .flag = nullptr, .val = 'e'},
1049 {.name = "self-test", .has_arg = no_argument, .flag = nullptr, .val = 'T'},
1050 {.name = "cache-password", .has_arg = no_argument, .flag = nullptr, .val = 'C'},
1051 {.name = "no-cert", .has_arg = no_argument, .flag = nullptr, .val = 'x'},
1052 {.name = "force-aes-cbc", .has_arg = no_argument, .flag = nullptr, .val = 'f'},
1053 {.name = "google-dns", .has_arg = no_argument, .flag = nullptr, .val = 'g'},
1054 {.name = "persist-tun", .has_arg = no_argument, .flag = nullptr, .val = 'j'},
1055 {.name = "wintun", .has_arg = no_argument, .flag = nullptr, .val = 'w'},
1056 {.name = "allow-local-dns-resolvers", .has_arg = no_argument, .flag = nullptr, .val = 'l'},
1057 {.name = "def-keydir", .has_arg = required_argument, .flag = nullptr, .val = 'k'},
1058 {.name = "merge", .has_arg = no_argument, .flag = nullptr, .val = 'm'},
1059 {.name = "version", .has_arg = no_argument, .flag = nullptr, .val = 'v'},
1060 {.name = "auto-sess", .has_arg = no_argument, .flag = nullptr, .val = 'a'},
1061 {.name = "auth-retry", .has_arg = no_argument, .flag = nullptr, .val = 'Y'},
1062 {.name = "tcprof-override", .has_arg = required_argument, .flag = nullptr, .val = 'X'},
1063 {.name = "write-url", .has_arg = required_argument, .flag = nullptr, .val = 'Z'},
1064 {.name = "sso-methods", .has_arg = required_argument, .flag = nullptr, .val = 'S'},
1065 {.name = "ssl-debug", .has_arg = required_argument, .flag = nullptr, .val = 1 },
1066 {.name = "epki-cert", .has_arg = required_argument, .flag = nullptr, .val = 2 },
1067 {.name = "epki-ca", .has_arg = required_argument, .flag = nullptr, .val = 3 },
1068 {.name = "epki-key", .has_arg = required_argument, .flag = nullptr, .val = 4 },
1069 {.name = "legacy-algorithms", .has_arg = no_argument, .flag = nullptr, .val = 'L'},
1070 {.name = "non-preferred-algorithms", .has_arg = no_argument, .flag = nullptr, .val = 'Q'},
1071#ifdef OPENVPN_REMOTE_OVERRIDE
1072 {.name = "remote-override", .has_arg = required_argument, .flag = nullptr, .val = 5 },
1073#endif
1074 {.name = "tbc", .has_arg = no_argument, .flag = nullptr, .val = 6 },
1075 {.name = "app-custom-protocols", .has_arg = required_argument, .flag = nullptr, .val = 'K'},
1076 {.name = "certcheck-cert", .has_arg = required_argument, .flag = nullptr, .val = 'o'},
1077 {.name = "certcheck-pkey", .has_arg = required_argument, .flag = nullptr, .val = 'O'},
1078 {.name = "certcheck-clientca", .has_arg = required_argument, .flag = nullptr, .val = 'b'},
1079 {.name = nullptr, .has_arg = 0, .flag = nullptr, .val = 0 }
1080 };
1081 // clang-format on
1082
1083 int ret = 0;
1084 auto cleanup = Cleanup([]()
1085 { the_client = nullptr; });
1086
1087 try
1088 {
1089 if (argc >= 2)
1090 {
1091 std::string username;
1092 std::string password;
1093 std::string response;
1094 std::string dynamicChallengeCookie;
1095 std::string proto;
1096 std::string allowUnusedAddrFamilies;
1097 std::string server;
1098 std::string port;
1099 int timeout = 0;
1100 std::string compress;
1101 std::string privateKeyPassword;
1102 std::string tlsVersionMinOverride;
1103 std::string tlsCertProfileOverride;
1104 std::string proxyHost;
1105 std::string proxyPort;
1106 std::string proxyUsername;
1107 std::string proxyPassword;
1108 std::string peer_info;
1109 std::string gremlin;
1110 std::string ssoMethods;
1111 std::string appCustomProtocols;
1112 std::string certcheck_cert_fn;
1113 std::string certcheck_pkey_fn;
1114 std::string certcheck_clientca;
1115 bool eval = false;
1116 bool self_test = false;
1117 bool disableClientCert = false;
1118 bool proxyAllowCleartextAuth = false;
1119 int defaultKeyDirection = -1;
1120 int sslDebugLevel = 0;
1121 bool googleDnsFallback = false;
1122 bool autologinSessions = false;
1123 bool retryOnAuthFailed = false;
1124 bool tunPersist = false;
1125 bool wintun = false;
1126 bool allowLocalDnsResolvers = false;
1127 bool enableLegacyAlgorithms = false;
1128 bool enableNonPreferredDCO = false;
1129 bool merge = false;
1130 bool version = false;
1131 bool altProxy = false;
1132#if defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)
1133 bool dco = true;
1134#else
1135 bool dco = false;
1136#endif
1137 bool generateTunBuilderCaptureEvent = false;
1138 std::string epki_cert_fn;
1139 std::string epki_ca_fn;
1140 std::string epki_key_fn;
1141
1142#ifdef OPENVPN_REMOTE_OVERRIDE
1143 std::string remote_override_cmd;
1144#endif
1145 std::string write_url_fn;
1146
1147 int ch;
1148 optind = 1;
1149
1150 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)
1151 {
1152 switch (ch)
1153 {
1154 case 1: // ssl-debug
1155 sslDebugLevel = ::atoi(optarg);
1156 break;
1157 case 2: // --epki-cert
1158 epki_cert_fn = optarg;
1159 break;
1160 case 3: // --epki-ca
1161 epki_ca_fn = optarg;
1162 break;
1163 case 4: // --epki-key
1164 epki_key_fn = optarg;
1165 break;
1166#ifdef OPENVPN_REMOTE_OVERRIDE
1167 case 5: // --remote-override
1168 remote_override_cmd = optarg;
1169 break;
1170#endif
1171 case 6: // --tbc
1172 generateTunBuilderCaptureEvent = true;
1173 break;
1174 case 'e':
1175 eval = true;
1176 break;
1177 case 'T':
1178 self_test = true;
1179 break;
1180 case 'x':
1181 disableClientCert = true;
1182 break;
1183 case 'u':
1184 username = optarg;
1185 break;
1186 case 'p':
1187 password = optarg;
1188 break;
1189 case 'r':
1190 response = optarg;
1191 break;
1192 case 'P':
1193 proto = optarg;
1194 break;
1195 case '6':
1196 allowUnusedAddrFamilies = optarg;
1197 break;
1198 case 's':
1199 server = optarg;
1200 break;
1201 case 'R':
1202 port = optarg;
1203 break;
1204 case 'S':
1205 ssoMethods = optarg;
1206 break;
1207 case 't':
1208 timeout = ::atoi(optarg);
1209 break;
1210 case 'c':
1211 compress = optarg;
1212 break;
1213 case 'z':
1214 privateKeyPassword = optarg;
1215 break;
1216 case 'M':
1217 tlsVersionMinOverride = optarg;
1218 break;
1219 case 'X':
1220 tlsCertProfileOverride = optarg;
1221 break;
1222 case 'h':
1223 proxyHost = optarg;
1224 break;
1225 case 'q':
1226 proxyPort = optarg;
1227 break;
1228 case 'Q':
1229 enableNonPreferredDCO = true;
1230 break;
1231 case 'U':
1232 proxyUsername = optarg;
1233 break;
1234 case 'W':
1235 proxyPassword = optarg;
1236 break;
1237 case 'B':
1238 proxyAllowCleartextAuth = true;
1239 break;
1240 case 'A':
1241 altProxy = true;
1242 break;
1243 case 'd':
1244 dco = false;
1245 break;
1246 case 'g':
1247 googleDnsFallback = true;
1248 break;
1249 case 'a':
1250 autologinSessions = true;
1251 break;
1252 case 'Y':
1253 retryOnAuthFailed = true;
1254 break;
1255 case 'j':
1256 tunPersist = true;
1257 break;
1258 case 'w':
1259 wintun = true;
1260 break;
1261 case 'l':
1262 allowLocalDnsResolvers = true;
1263 break;
1264 case 'm':
1265 merge = true;
1266 break;
1267 case 'v':
1268 version = true;
1269 break;
1270 case 'k':
1271 {
1272 const std::string arg = optarg;
1273 if (arg == "bi" || arg == "bidirectional")
1274 defaultKeyDirection = -1;
1275 else if (arg == "0")
1276 defaultKeyDirection = 0;
1277 else if (arg == "1")
1278 defaultKeyDirection = 1;
1279 else
1280 OPENVPN_THROW_EXCEPTION("bad default key-direction: " << arg);
1281 }
1282 break;
1283 case 'D':
1284 dynamicChallengeCookie = optarg;
1285 break;
1286 case 'I':
1287 peer_info = optarg;
1288 break;
1289 case 'G':
1290 gremlin = optarg;
1291 break;
1292 case 'K':
1293 appCustomProtocols = optarg;
1294 break;
1295 case 'o':
1296 certcheck_cert_fn = optarg;
1297 break;
1298 case 'O':
1299 certcheck_pkey_fn = optarg;
1300 break;
1301 case 'b':
1302 certcheck_clientca = optarg;
1303 break;
1304 case 'L':
1305 enableLegacyAlgorithms = true;
1306 break;
1307 case 'Z':
1308 write_url_fn = optarg;
1309 break;
1310 default:
1311 throw usage();
1312 }
1313 }
1314 argc -= optind;
1315 argv += optind;
1316
1317 if (version)
1318 {
1319 std::cout << "OpenVPN cli 1.0\n";
1320 std::cout << ClientAPI::OpenVPNClientHelper::platform() << "\n";
1321 std::cout << ClientAPI::OpenVPNClientHelper::copyright() << "\n";
1322 }
1323 else if (self_test)
1324 {
1326 std::cout << clihelper.crypto_self_test();
1327 }
1328 else if (merge)
1329 {
1330 if (argc != 1)
1331 throw usage();
1332 std::cout << read_profile(argv[0], profile_content);
1333 }
1334 else
1335 {
1336 if (argc < 1)
1337 throw usage();
1338
1339 bool retry;
1340 do
1341 {
1342 retry = false;
1343
1345 config.guiVersion = "cli 1.0";
1346#ifdef OPENVPN_PLATFORM_WIN
1347 int nargs = 0;
1348 auto argvw = CommandLineToArgvW(GetCommandLineW(), &nargs);
1349 UTF8 utf8(Win::utf8(argvw[nargs - 1]));
1350 config.content = read_profile(utf8.get(), profile_content);
1351#else
1352 config.content = read_profile(argv[0], profile_content);
1353#endif
1354 for (int i = 1; i < argc; ++i)
1355 {
1356 config.content += argv[i];
1357 config.content += '\n';
1358 }
1359 config.serverOverride = server;
1360 config.portOverride = port;
1361 config.protoOverride = proto;
1362 config.connTimeout = timeout;
1363 config.compressionMode = compress;
1364 config.allowUnusedAddrFamilies = allowUnusedAddrFamilies;
1365 config.privateKeyPassword = privateKeyPassword;
1366 config.tlsVersionMinOverride = tlsVersionMinOverride;
1367 config.tlsCertProfileOverride = tlsCertProfileOverride;
1368 config.disableClientCert = disableClientCert;
1369 config.proxyHost = proxyHost;
1370 config.proxyPort = proxyPort;
1371 config.proxyUsername = proxyUsername;
1372 config.proxyPassword = proxyPassword;
1373 config.proxyAllowCleartextAuth = proxyAllowCleartextAuth;
1374 config.altProxy = altProxy;
1375 config.dco = dco;
1376 config.generateTunBuilderCaptureEvent = generateTunBuilderCaptureEvent;
1377 config.defaultKeyDirection = defaultKeyDirection;
1378 config.sslDebugLevel = sslDebugLevel;
1379 config.googleDnsFallback = googleDnsFallback;
1380 config.autologinSessions = autologinSessions;
1381 config.retryOnAuthFailed = retryOnAuthFailed;
1382 config.tunPersist = tunPersist;
1383 config.gremlinConfig = gremlin;
1384 config.info = true;
1385 config.wintun = wintun;
1386 config.allowLocalDnsResolvers = allowLocalDnsResolvers;
1387 config.enableLegacyAlgorithms = enableLegacyAlgorithms;
1388 config.enableNonPreferredDCAlgorithms = enableNonPreferredDCO;
1389 config.ssoMethods = ssoMethods;
1390 config.appCustomProtocols = appCustomProtocols;
1391#ifdef OPENVPN_OVPNCLI_SINGLE_THREAD
1392 config.clockTickMS = 250;
1393#endif
1394
1395 if (!epki_cert_fn.empty())
1396 config.externalPkiAlias = "epki"; // dummy string
1397
1398 PeerInfo::Set::parse_flexible(peer_info, config.peerInfo);
1399
1400 // allow -s server override to reference a friendly name
1401 // in the config.
1402 // setenv SERVER <HOST>/<FRIENDLY_NAME>
1403 if (!config.serverOverride.empty())
1404 {
1406 const ClientAPI::EvalConfig cfg_eval = clihelper.eval_config(config);
1407 for (auto &se : cfg_eval.serverList)
1408 {
1409 if (config.serverOverride == se.friendlyName)
1410 {
1411 config.serverOverride = se.server;
1412 break;
1413 }
1414 }
1415 }
1416
1417 if (eval)
1418 {
1420 const ClientAPI::EvalConfig cfg_eval = clihelper.eval_config(config);
1421 std::cout << "EVAL PROFILE\n";
1422 std::cout << "error=" << cfg_eval.error << "\n";
1423 std::cout << "message=" << cfg_eval.message << "\n";
1424 std::cout << "userlockedUsername=" << cfg_eval.userlockedUsername << "\n";
1425 std::cout << "profileName=" << cfg_eval.profileName << "\n";
1426 std::cout << "friendlyName=" << cfg_eval.friendlyName << "\n";
1427 std::cout << "autologin=" << cfg_eval.autologin << "\n";
1428 std::cout << "externalPki=" << cfg_eval.externalPki << "\n";
1429 std::cout << "staticChallenge=" << cfg_eval.staticChallenge << "\n";
1430 std::cout << "staticChallengeEcho=" << cfg_eval.staticChallengeEcho << "\n";
1431 std::cout << "privateKeyPasswordRequired=" << cfg_eval.privateKeyPasswordRequired << "\n";
1432 std::cout << "allowPasswordSave=" << cfg_eval.allowPasswordSave << "\n";
1433
1434 if (!config.serverOverride.empty())
1435 std::cout << "server=" << config.serverOverride << "\n";
1436
1437 for (size_t i = 0; i < cfg_eval.serverList.size(); ++i)
1438 {
1439 const ClientAPI::ServerEntry &se = cfg_eval.serverList[i];
1440 std::cout << '[' << i << "] " << se.server << '/' << se.friendlyName << "\n";
1441 }
1442 }
1443 else
1444 {
1445#ifdef USE_NETCFG
1446 auto dbus_system = DBus::Connection::Create(DBus::BusType::SYSTEM);
1447 NetCfgTunBuilder<Client> client(dbus_system);
1448#else
1449 Client client;
1450#endif
1451 const ClientAPI::EvalConfig eval = client.eval_config(config);
1452 if (eval.error)
1453 OPENVPN_THROW_EXCEPTION("eval config error: " << eval.message);
1454 if (eval.autologin)
1455 {
1456 if (!username.empty() || !password.empty())
1457 std::cout << "NOTE: creds were not needed\n";
1458
1459 // still provide proxy credentials if given
1460 if (!proxyUsername.empty())
1461 {
1463 creds.http_proxy_user = proxyUsername;
1464 creds.http_proxy_pass = proxyPassword;
1465 ClientAPI::Status creds_status = client.provide_creds(creds);
1466 if (creds_status.error)
1467 OPENVPN_THROW_EXCEPTION("creds error: " << creds_status.message);
1468 }
1469 }
1470 else
1471 {
1472 if (username.empty())
1473 OPENVPN_THROW_EXCEPTION("need creds");
1475 if (password.empty() && dynamicChallengeCookie.empty())
1476 password = get_password("Password:");
1477 creds.username = username;
1478 creds.password = password;
1479 creds.http_proxy_user = proxyUsername;
1480 creds.http_proxy_pass = proxyPassword;
1481 creds.response = response;
1482 creds.dynamicChallengeCookie = dynamicChallengeCookie;
1483 ClientAPI::Status creds_status = client.provide_creds(creds);
1484 if (creds_status.error)
1485 OPENVPN_THROW_EXCEPTION("creds error: " << creds_status.message);
1486 }
1487
1488 client.certcheck_clientca_fn = certcheck_clientca;
1489
1490 // external PKI
1491 if (!epki_cert_fn.empty())
1492 {
1493 client.epki_cert = read_text_utf8(epki_cert_fn);
1494 if (!epki_ca_fn.empty())
1495 client.epki_ca = read_text_utf8(epki_ca_fn);
1496#if defined(USE_MBEDTLS) || defined(USE_OPENSSL)
1497 if (!epki_key_fn.empty())
1498 {
1499 const std::string epki_key_txt = read_text_utf8(epki_key_fn);
1500#ifdef USE_MBEDTLS
1501
1502 auto mbedrng = std::make_unique<MbedTLSRandom>();
1503 client.epki_ctx.parse(epki_key_txt, "EPKI", privateKeyPassword, *mbedrng);
1504#else
1505 client.epki_pkey.parse_pem(epki_key_txt, "epki private key", nullptr);
1506#endif
1507 }
1508 else
1509 OPENVPN_THROW_EXCEPTION("--epki-key must be specified");
1510#endif
1511 }
1512
1513 // Certcheck
1514 if (!certcheck_cert_fn.empty())
1515 {
1516 client.certcheck_cert = read_text_utf8(certcheck_cert_fn);
1517 const std::string epki_key_txt = read_text_utf8(certcheck_pkey_fn);
1518#ifdef USE_OPENSSL
1519 client.certcheck_pkey.parse_pem(epki_key_txt, "certcheck private key", nullptr);
1520#else
1521 auto mbedrng = std::make_unique<MbedTLSRandom>();
1522 client.certcheck_pkey.parse(epki_key_txt, "Certcheck", privateKeyPassword, *mbedrng);
1523#endif
1524 }
1525
1526#ifdef OPENVPN_REMOTE_OVERRIDE
1527 client.set_remote_override_cmd(remote_override_cmd);
1528#endif
1529
1530 client.set_write_url_fn(write_url_fn);
1531
1532 std::cout << "CONNECTING...\n";
1533
1534 // start the client thread
1535 start_thread(client);
1536
1537 // Get dynamic challenge response
1538 if (client.is_dynamic_challenge())
1539 {
1540 std::cout << "ENTER RESPONSE\n";
1541 std::getline(std::cin, response);
1542 if (!response.empty())
1543 {
1544 dynamicChallengeCookie = client.dynamic_challenge_cookie();
1545 retry = true;
1546 }
1547 }
1548 else
1549 {
1550 // print closing stats
1551 client.print_stats();
1552 }
1553 }
1554#ifdef USE_OPENSSL
1555 /* Since this is a debug/test program we check if the
1556 * internal OpenSSL error stack is empty on exit and
1557 * print the warnings otherwise */
1558 if (ERR_peek_error())
1559 {
1560 throw OpenSSLException{};
1561 }
1562#endif
1563 } while (retry);
1564 }
1565 }
1566 else
1567 throw usage();
1568 }
1569 catch (const usage &)
1570 {
1571 std::cout << "OpenVPN Client (ovpncli)\n";
1572 std::cout << "usage: cli [options] <config-file> [extra-config-directives...]\n";
1573 std::cout << "--version, -v : show version info\n";
1574 std::cout << "--eval, -e : evaluate profile only (standalone)\n";
1575 std::cout << "--merge, -m : merge profile into unified format (standalone)\n";
1576 std::cout << "--username, -u : username\n";
1577 std::cout << "--password, -p : password\n";
1578 std::cout << "--response, -r : static response\n";
1579 std::cout << "--dc, -D : dynamic challenge/response cookie\n";
1580 std::cout << "--proto, -P : protocol override (udp|tcp)\n";
1581 std::cout << "--server, -s : server override\n";
1582 std::cout << "--port, -R : port override\n";
1583#ifdef OPENVPN_REMOTE_OVERRIDE
1584 std::cout << "--remote-override : command to run to generate next remote (returning host,ip,port,proto)\n";
1585#endif
1586 std::cout << "--allowAF, -6 : Allow unused address families (yes|no|default)\n";
1587 std::cout << "--timeout, -t : timeout\n";
1588 std::cout << "--compress, -c : compression mode (yes|no|asym)\n";
1589 std::cout << "--pk-password, -z : private key password\n";
1590 std::cout << "--tvm-override, -M : tls-version-min override (disabled, default, tls_1_x)\n";
1591 std::cout << "--legacy-algorithms, -L: Enable legacy algorithm (OpenSSL legacy provider)\n";
1592 std::cout << "--non-preferred-algorithms, -Q: Enables non preferred data channel algorithms\n";
1593 std::cout << "--tcprof-override, -X : tls-cert-profile override ("
1594 <<
1595#ifdef OPENVPN_ALLOW_INSECURE_CERTPROFILE
1596 "insecure, "
1597 <<
1598#endif
1599 "legacy, preferred, etc.)\n";
1600 std::cout << "--proxy-host, -h : HTTP proxy hostname/IP\n";
1601 std::cout << "--proxy-port, -q : HTTP proxy port\n";
1602 std::cout << "--proxy-username, -U : HTTP proxy username\n";
1603 std::cout << "--proxy-password, -W : HTTP proxy password\n";
1604 std::cout << "--proxy-basic, -B : allow HTTP basic auth\n";
1605 std::cout << "--alt-proxy, -A : enable alternative proxy module\n";
1606#if defined(ENABLE_KOVPN) || defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)
1607 std::cout << "--no-dco, -d : disable data channel offload\n";
1608#endif
1609 std::cout << "--cache-password, -C : cache password\n";
1610 std::cout << "--no-cert, -x : disable client certificate\n";
1611 std::cout << "--def-keydir, -k : default key direction ('bi', '0', or '1')\n";
1612 std::cout << "--ssl-debug : SSL debug level\n";
1613 std::cout << "--google-dns, -g : enable Google DNS fallback\n";
1614 std::cout << "--auto-sess, -a : request autologin session\n";
1615 std::cout << "--auth-retry, -Y : retry connection on auth failure\n";
1616 std::cout << "--persist-tun, -j : keep TUN interface open across reconnects\n";
1617 std::cout << "--wintun, -w : use WinTun instead of TAP-Windows6 on Windows\n";
1618 std::cout << "--peer-info, -I : peer info key/value list in the form K1=V1,K2=V2,... or @kv.json\n";
1619 std::cout << "--gremlin, -G : gremlin info (send_delay_ms, recv_delay_ms, send_drop_prob, recv_drop_prob)\n";
1620 std::cout << "--epki-ca : simulate external PKI cert supporting intermediate/root certs\n";
1621 std::cout << "--epki-cert : simulate external PKI cert\n";
1622 std::cout << "--epki-key : simulate external PKI private key\n";
1623 std::cout << "--sso-methods : auth pending methods to announce via IV_SSO\n";
1624 std::cout << "--write-url, -Z : write INFO URL to file\n";
1625 std::cout << "--tbc : generate INFO_JSON/TUN_BUILDER_CAPTURE event\n";
1626 std::cout << "--app-custom-protocols, -K : ACC protocols to advertise\n";
1627 std::cout << "--certcheck-cert, -o : path to certificate PEM for certcheck\n";
1628 std::cout << "--certcheck-pkey, -O : path to decrypted pkey PEM for certcheck\n";
1629 std::cout << "--certcheck-clientca, -b : path to decrypted pkey PEM for certcheck CA\n";
1630 ret = 2;
1631 }
1632 return ret;
1633}
1634
1635#ifndef OPENVPN_OVPNCLI_OMIT_MAIN
1636
1637int main(int argc, char *argv[])
1638{
1639 int ret = 0;
1640
1641#ifdef OPENVPN_LOG_LOGBASE_H
1642 LogBaseSimple log;
1643#endif
1644
1645#ifdef OPENVPN_PLATFORM_WIN
1646 SetConsoleOutputCP(CP_UTF8);
1647#endif
1648
1649 try
1650 {
1651 ret = openvpn_client(argc, argv, nullptr);
1652 }
1653 catch (const std::exception &e)
1654 {
1655 std::cout << "Main thread exception: " << e.what() << "\n";
1656 ret = 1;
1657 }
1658 return ret;
1659}
1660
1661#endif
bool socket_protect(openvpn_io::detail::socket_type socket, std::string remote, bool ipv6) override
Definition cli.cpp:200
void event(const ClientAPI::Event &ev) override
Definition cli.cpp:280
void handle_certcheck_request()
Begin a cck1 (certcheck) handshake in response to a dpc1 server request.
Definition cli.cpp:391
void external_pki_cert_request(ClientAPI::ExternalPKICertRequest &certreq) override
Definition openvpn.cpp:1027
std::string epki_cert
Definition cli.cpp:235
std::string certcheck_clientca_fn
file name of properly encoded client ca
Definition cli.cpp:245
volatile ClockTickAction clock_tick_action
Definition cli.cpp:807
ClockTickAction
Definition cli.cpp:215
@ CT_RESUME
Definition cli.cpp:220
@ CT_UNDEF
Definition cli.cpp:216
@ CT_PAUSE
Definition cli.cpp:219
@ CT_STATS
Definition cli.cpp:221
@ CT_STOP
Definition cli.cpp:217
@ CT_RECONNECT
Definition cli.cpp:218
void set_write_url_fn(const std::string &fn)
Definition cli.cpp:273
void set_clock_tick_action(const ClockTickAction action)
Definition cli.cpp:247
void acc_event(const openvpn::ClientAPI::AppCustomControlMessageEvent &event) override
Definition openvpn.cpp:1037
void external_pki_sign_request(ClientAPI::ExternalPKISignRequest &signreq) override
Definition openvpn.cpp:1032
std::string write_url_fn
Definition cli.cpp:813
std::string dc_cookie
Definition cli.cpp:805
void handle_dpc1_protocol(const ClientAPI::AppCustomControlMessageEvent &acev)
Definition cli.cpp:342
bool pause_on_connection_timeout() override
Definition openvpn.cpp:67
std::string dynamic_challenge_cookie()
Definition cli.cpp:229
bool is_dynamic_challenge() const
Definition cli.cpp:224
static int rng_callback(void *arg, unsigned char *data, size_t len)
Definition cli.cpp:751
void print_stats()
Definition cli.cpp:252
void open_url(const std::string &url_str)
Definition cli.cpp:423
StrongRandomAPI::Ptr rng
Definition cli.cpp:806
void log(const ClientAPI::LogInfo &msg) override
Definition openvpn.cpp:1022
std::string certcheck_cert
file name of properly encoded server cert
Definition cli.cpp:244
std::string epki_ca
Definition cli.cpp:234
void clock_tick() override
Definition cli.cpp:469
static constexpr char certcheck_init_verb[]
Definition cli.cpp:211
std::mutex log_mutex
Definition cli.cpp:804
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:753
static bool parse_dynamic_challenge(const std::string &cookie, DynamicChallenge &dc)
Definition ovpncli.cpp:804
void send_app_control_channel_msg(const std::string &protocol, const std::string &msg)
Definition ovpncli.cpp:1312
void post_cc_msg(const std::string &msg)
Definition ovpncli.cpp:1302
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:1359
virtual void remote_override(RemoteOverride &)
Definition ovpncli.cpp:1145
std::vector< long long > stats_bundle() const
Definition ovpncli.cpp:1174
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:1342
static std::string stats_name(int index)
Definition ovpncli.cpp:1154
void pause(const std::string &reason)
Definition ovpncli.cpp:1272
const T * c_data() const
Returns a const pointer to the start of the buffer.
Definition buffer.hpp:1193
size_t length() const
Returns the length of the buffer.
Definition buffer.hpp:1187
size_t size() const
Returns the size of the buffer in T objects.
Definition buffer.hpp:1241
T * data()
Get a mutable pointer to the start of the array.
Definition buffer.hpp:1447
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:87
bool defined() const
Definition pkey.hpp:83
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:1637
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:959
static void worker_thread()
Definition cli.cpp:822
int openvpn_client(int argc, char *argv[], const std::string *profile_content)
Definition cli.cpp:1020
static void start_thread(Client &client)
Definition cli.cpp:999
#define OPENVPN_SIMPLE_EXCEPTION(C)
Definition exception.hpp:74
#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:33
std::vector< T > split(const T &str, const typename T::value_type sep, const int maxsplit=-1)
Definition string.hpp:452
std::string first_line(const std::string &str)
Definition string.hpp:220
std::string render_hex_generic(const V &data, const bool caps=false)
Definition hexstr.hpp:228
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:227
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
reroute_gw ipv4
std::string ret
remote_address address
std::ostringstream os
static const char config[]
static std::stringstream out
Definition test_path.cpp:10