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