forked from OpenVPN/openvpn3
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ovpncli.hpp
742 lines (598 loc) · 24.3 KB
/
ovpncli.hpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012-2022 OpenVPN Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License Version 3
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program in the COPYING file.
// If not, see <http://www.gnu.org/licenses/>.
// API for OpenVPN Client, may be used standalone or wrapped by swig.
// Use ovpncli.i to wrap the API for swig.
// The crux of the API is defined in OpenVPNClient (below)
// and TunBuilderBase.
#include <string>
#include <vector>
#include <unordered_set>
#include <utility>
#include <openvpn/tun/builder/base.hpp>
#include <openvpn/tun/extern/fw.hpp>
#include <openvpn/pki/epkibase.hpp>
#include <openvpn/transport/client/extern/fw.hpp>
namespace openvpn {
class OptionList;
class ProfileMerge;
class Stop;
namespace InitProcess {
class Init;
};
namespace ClientAPI {
// Represents an OpenVPN server and its friendly name
// (client reads)
struct ServerEntry
{
std::string server;
std::string friendlyName;
};
// return properties of config
// (client reads)
struct EvalConfig
{
// true if error
bool error = false;
// if error, message given here
std::string message;
// this username must be used with profile
std::string userlockedUsername;
// profile name of config
std::string profileName;
// "friendly" name of config
std::string friendlyName;
// true: no creds required, false: username/password required
bool autologin = false;
// if true, this is an External PKI profile (no cert or key directives)
bool externalPki = false;
// static challenge, may be empty, ignored if autologin
std::string staticChallenge;
// true if static challenge response should be echoed to UI, ignored if autologin
bool staticChallengeEcho = false;
// true if this profile requires a private key password
bool privateKeyPasswordRequired = false;
// true if user is allowed to save authentication password in UI
bool allowPasswordSave = false;
// information about the first remote item in config
std::string remoteHost; // will be overridden by Config::serverOverride if defined
std::string remotePort;
std::string remoteProto;
// optional list of user-selectable VPN servers
std::vector<ServerEntry> serverList;
// optional, values are "tap-windows6" and "wintun"
std::string windowsDriver;
bool dcoCompatible;
std::string dcoIncompatibilityReason;
};
// used to pass credentials to VPN core
// (client writes)
struct ProvideCreds
{
std::string username;
std::string password;
std::string http_proxy_user;
std::string http_proxy_pass;
// response to challenge
std::string response;
// Dynamic challenge/response cookie
std::string dynamicChallengeCookie;
// If true, on successful connect, we will replace the password
// with the session ID we receive from the server (if provided).
// If false, the password will be cached for future reconnects
// and will not be replaced with a session ID, even if the
// server provides one.
bool replacePasswordWithSessionID = false;
// If true, and if replacePasswordWithSessionID is true, and if
// we actually receive a session ID from the server, cache
// the user-provided password for future use before replacing
// the active password with the session ID.
bool cachePassword = false;
};
// used to get session token from VPN core
// (client reads)
struct SessionToken
{
std::string username;
std::string session_id; // an OpenVPN Session ID, used as a proxy for password
};
// used to query challenge/response from user
// (client reads)
struct DynamicChallenge
{
std::string challenge;
bool echo = false;
bool responseRequired = false;
std::string stateID;
};
// a basic key/value pair, used in Config below when OpenVPN profile is
// passed as a dictionary
struct KeyValue
{
KeyValue()
{
}
KeyValue(std::string key_arg, std::string value_arg)
: key(std::move(key_arg)),
value(std::move(value_arg))
{
}
std::string key;
std::string value;
};
// OpenVPN config-file/profile
// (client writes)
struct Config
{
// OpenVPN profile as a string
std::string content;
// OpenVPN profile as series of key/value pairs (may be provided exclusively
// or in addition to content string above).
std::vector<KeyValue> contentList;
// Set to identity OpenVPN GUI version.
// Format should be "<gui_identifier><space><version>"
// Passed to server as IV_GUI_VER.
std::string guiVersion;
// Set to a comma seperated list of supported SSO mechanisms that may
// be signalled via INFO_PRE to the client.
// "openurl" deprecated version of webauth
// "webauth" to continue authentication by opening an url in a browser
// "crtext" gives a challenge response in text format that needs to
// responded via control channel. (
// Passed to the server as IV_SSO
std::string ssoMethods;
// Override the string that is passed as IV_HWADDR to the server
std::string hwAddrOverride;
// Set the string that is passed to the server as IV_PLAT_VER
std::string platformVersion;
// Use a different server than that specified in "remote"
// option of profile
std::string serverOverride;
// Use a different port than that specified in "remote"
// option of profile
std::string portOverride;
// Force a given transport protocol
// Should be tcp, udp, or adaptive.
std::string protoOverride;
// Force transport protocol IP version
// Should be 4 for IPv4 or 6 for IPv6.
int protoVersionOverride = 0;
// allowUnusedAddrFamilies preference
// no -- disable IPv6/IPv4, so tunnel will be IPv4 or IPv6 only if not dualstack
// yes -- Allow continuing using native IPv4/IPv6 connectivity for single IP family tunnel
// default (or empty string) -- leave decision to server/config
std::string allowUnusedAddrFamilies;
// Connection timeout in seconds, or 0 to retry indefinitely
int connTimeout = 0;
// Keep tun interface active during pauses or reconnections
bool tunPersist = false;
// If true and a redirect-gateway profile doesn't also define
// DNS servers, use the standard Google DNS servers.
bool googleDnsFallback = false;
// if true, do synchronous DNS lookup.
bool synchronousDnsLookup = false;
// Enable autologin sessions
bool autologinSessions = true;
// If true, consider AUTH_FAILED to be a non-fatal error,
// and retry the connection after a pause.
bool retryOnAuthFailed = false;
// An ID used for get-certificate and RSA signing callbacks
// for External PKI profiles.
std::string externalPkiAlias;
// If true, don't send client cert/key to peer.
bool disableClientCert = false;
// SSL library debug level
int sslDebugLevel = 0;
// Compression mode, one of:
// yes -- allow compression on both uplink and downlink
// asym -- allow compression on downlink only (i.e. server -> client)
// no (default if empty) -- support compression stubs only
std::string compressionMode;
// private key password (optional)
std::string privateKeyPassword;
// Default key direction parameter for tls-auth (0, 1, or
// -1 (bidirectional -- default)) if no key-direction parameter
// defined in profile. Generally should be -1 (bidirectional)
// for compatibility with 2.x branch
int defaultKeyDirection = -1;
// Override the minimum TLS version:
// Will not lower the minimum if already a higher minimum has been
// specified by tls-version-min in the profile
// disabled -- don't specify a minimum, and disable any minimum
// specified in profile
// default or "" -- use profile minimum or system minimum
// tls_1_0 -- use TLS 1.0 minimum
// tls_1_1 -- use TLS 1.1 minimum
// tls_1_2 -- use TLS 1.2 minimum
// tls_1_3 -- use TLS 1.3 minimum
std::string tlsVersionMinOverride;
// Override or default the tls-cert-profile setting:
// default or "" -- use profile default
// legacy -- allow 1024-bit RSA certs signed with SHA1
// preferred -- require at least 2048-bit RSA certs signed
// with SHA256 or higher
// suiteb -- require NSA Suite-B
// legacy-default -- use legacy as the default if profile
// doesn't specify tls-cert-profile
// preferred-default -- use preferred as the default if profile
// doesn't specify tls-cert-profile
std::string tlsCertProfileOverride;
// Overrides the list of tls ciphers like the tls-cipher option
std::string tlsCipherList;
// Overrides the list of TLS 1.3 ciphersuites like the tls-ciphersuites
// option
std::string tlsCiphersuitesList;
// Pass custom key/value pairs to OpenVPN server.
std::vector<KeyValue> peerInfo;
// HTTP Proxy parameters (optional)
std::string proxyHost; // hostname or IP address of proxy
std::string proxyPort; // port number of proxy
std::string proxyUsername; // proxy credentials (optional)
std::string proxyPassword; // proxy credentials (optional)
bool proxyAllowCleartextAuth = false; // enables HTTP Basic auth
// Custom proxy implementation
bool altProxy = false;
// Enable automatic Data Channel Offload
#if defined(ENABLE_OVPNDCO) || defined(ENABLE_OVPNDCOWIN)
bool dco = true;
#else
bool dco = false;
#endif
// pass through pushed "echo" directives via "ECHO" event
bool echo = false;
// pass through control channel INFO notifications via "INFO" event
bool info = false;
// Allow access to local LAN. This is for platforms like
// Android that disable local LAN access by default.
bool allowLocalLanAccess = false;
#ifdef OPENVPN_PLATFORM_ANDROID
// Instead of setting include and exclude routes, calculate a set of
// include routes only to emulate the lack of excluding routes
// (Android earlier than Tiramisu (Android 13))
bool enableRouteEmulation = true;
#endif
// Periodic convenience clock tick in milliseconds.
// Will call clock_tick() at a frequency defined by this parameter.
// Set to 0 to disable.
unsigned int clockTickMS = 0;
// Gremlin configuration (requires that the core is built with OPENVPN_GREMLIN)
std::string gremlinConfig;
// Use wintun instead of tap-windows6 on Windows
bool wintun = false;
// On Windows allow DNS resolvers on localhost, such as Umbrella Roaming Client
// This disables adding NRPT rule for "." zone and permits DNS requests to localhost
bool allowLocalDnsResolvers = false;
// Allow usage of legacy (cipher) algorithm that are no longer considered safe
// This includes BF-CBC, single DES and RC2 private key encryption.
// With OpenSSL 3.0 this also instructs OpenSSL to load the legacy provider.
bool enableLegacyAlgorithms = false;
// By default modern OpenVPN version (OpenVPN 2.6 and OpenVPN core 3.7) will only allow
// preferred algorithms (AES-GCM, Chacha20-Poly1305) that also work with the newer DCO
// implementations. If this is enabled, we fall back to allowing all algorithms (if these are
// supported by the crypto library)
bool enableNonPreferredDCAlgorithms = false;
// Generate an INFO_JSON/TUN_BUILDER_CAPTURE event
// with all tun builder properties pushed by server.
// Currently only implemented on Linux.
bool generate_tun_builder_capture_event = false;
};
// used to communicate VPN events such as connect, disconnect, etc.
// (client reads)
struct Event
{
bool error = false; // true if error (fatal or nonfatal)
bool fatal = false; // true if fatal error (will disconnect)
std::string name; // event name
std::string info; // additional event info
};
// used to communicate extra details about successful connection
// (client reads)
struct ConnectionInfo
{
bool defined = false;
std::string user;
std::string serverHost;
std::string serverPort;
std::string serverProto;
std::string serverIp;
std::string vpnIp4;
std::string vpnIp6;
std::string vpnMtu;
std::string gw4;
std::string gw6;
std::string clientIp;
std::string tunName;
};
// returned by some methods as a status/error indication
// (client reads)
struct Status
{
bool error = false; // true if error
std::string status; // an optional short error label that identifies the error
std::string message; // if error, message given here
};
// used to pass log lines
// (client reads)
struct LogInfo
{
LogInfo()
{
}
LogInfo(std::string str)
: text(std::move(str))
{
}
std::string text; // log output (usually but not always one line)
};
// receives log messages
struct LogReceiver
{
virtual void log(const LogInfo &) = 0;
virtual ~LogReceiver()
{
}
};
// used to pass stats for an interface
struct InterfaceStats
{
long long bytesIn;
long long packetsIn;
long long errorsIn;
long long bytesOut;
long long packetsOut;
long long errorsOut;
};
// used to pass basic transport stats
struct TransportStats
{
long long bytesIn;
long long bytesOut;
long long packetsIn;
long long packetsOut;
// number of binary milliseconds (1/1024th of a second) since
// last packet was received, or -1 if undefined
int lastPacketReceived;
};
// return value of merge_config methods
struct MergeConfig
{
std::string status; // ProfileMerge::Status codes rendered as string
std::string errorText; // error string (augments status)
std::string basename; // profile basename
std::string profileContent; // unified profile
std::vector<std::string> refPathList; // list of all reference paths successfully read
};
// base class for External PKI queries
struct ExternalPKIRequestBase
{
bool error = false; // true if error occurred (client writes)
std::string errorText; // text describing error (client writes)
bool invalidAlias = false; // true if the error is caused by an invalid alias (client writes)
std::string alias; // the alias string, used to query cert/key (client reads)
};
// used to query for External PKI certificate
struct ExternalPKICertRequest : public ExternalPKIRequestBase
{
// leaf cert
std::string cert; // (client writes)
// chain of intermediates and root (optional)
std::string supportingChain; // (client writes)
};
// Used to request an external certificate signature.
// algorithm will determinate what signature is expected:
// algorithm, hashalg and saltlen together determine what
// should be used. hashalg and saltlen can be empty
// - ECDSA
// do a ECDSA signature.
// - ECDSA hashalg=ALG
// Use hashAlg for the ECDSA signature (e.g. SHA512withECDSA in Java)
// - RSA_PKCS1_PADDING
// data will be prefixed by an optional PKCS#1 digest prefix per RFC 3447.
// - RSA_PKCS1_PSS_PADDING
// Use PSS padding for the signature
// - RSA_PKCS1_PSS_PADDING,saltlen=digest,hashalg=ALG
// Use a PSS padding with hash algorithm ALG and a salt of length
// of the digest (hash ALG).
struct ExternalPKISignRequest : public ExternalPKIRequestBase
{
std::string data; // data rendered as base64 (client reads)
std::string sig; // RSA signature, rendered as base64 (client writes)
std::string algorithm;
std::string hashalg; // If non-empty use this algorith for hashing (e.g. SHA384)
std::string saltlen;
};
// used to override "remote" directives
struct RemoteOverride
{
// components of "remote" directive (client writes),
std::string host; // either one of host
std::string ip; // or ip must be defined (or both)
std::string port;
std::string proto;
std::string error; // if non-empty, indicates an error
};
namespace Private {
class ClientState;
};
/**
* Helper class for OpenVPN clients. Provider helper method to be used with
* the \sa OpenVPNClient class.
*/
class OpenVPNClientHelper
{
/* To call parse_config */
friend class OpenVPNClient;
public:
OpenVPNClientHelper();
~OpenVPNClientHelper();
OpenVPNClientHelper(OpenVPNClientHelper &) = delete;
// Read an OpenVPN profile that might contain external
// file references, returning a unified profile.
MergeConfig merge_config(const std::string &path, bool follow_references);
// Read an OpenVPN profile that might contain external
// file references, returning a unified profile.
MergeConfig merge_config_string(const std::string &config_content);
// Parse profile and determine needed credentials statically.
EvalConfig eval_config(const Config &config);
// Maximum size of profile that should be allowed
static long max_profile_size();
// Parse a dynamic challenge cookie, placing the result in dc.
// Return true on success or false if parse error.
static bool parse_dynamic_challenge(const std::string &cookie, DynamicChallenge &dc);
// Do a crypto library self test
std::string crypto_self_test();
// Returns platform description string
static std::string platform();
// Returns core copyright
static std::string copyright();
// If those options are present, dco cannot be used
inline static std::unordered_set<std::string> dco_incompatible_opts = {
"http-proxy",
"compress",
"comp-lzo"};
private:
static MergeConfig build_merge_config(const ProfileMerge &);
static void parse_config(const Config &, EvalConfig &, OptionList &);
/* including initprocess.hpp here break since it pulls in logging
* (OPENVPN_LOG) which not setup when including this header, so break that
* cycle here with a pointer instead a normal member, std::unique_ptr
* and std::unique_ptr because they still need to have the initprocess.hpp
* included in the same compilation unit which breaks in the swig wrapped
* class, so we use a plain pointer and new/delete in constructor/destructor */
InitProcess::Init *init;
/* Checks if there are dco-incompatible options in options list or config has
* dco-incompatible settings. Sets dcoCompatible flag and dcoIncompatibilityReason
* string property (if applicable) in EvalConfig object */
static void check_dco_compatibility(const Config &, EvalConfig &, OptionList &);
};
// Top-level OpenVPN client class.
class OpenVPNClient : public TunBuilderBase, // expose tun builder virtual methods
public LogReceiver, // log message notification
public ExternalTun::Factory, // low-level tun override
public ExternalTransport::Factory, // low-level transport override
private ExternalPKIBase
{
public:
OpenVPNClient();
virtual ~OpenVPNClient();
// Parse OpenVPN configuration file.
EvalConfig eval_config(const Config &);
// Provide credentials and other options. Call before connect().
Status provide_creds(const ProvideCreds &);
// Callback to "protect" a socket from being routed through the tunnel.
// Will be called from the thread executing connect().
// The remote and ipv6 are the remote host this socket will connect to
virtual bool socket_protect(int socket, std::string remote, bool ipv6);
// Primary VPN client connect method, doesn't return until disconnect.
// Should be called by a worker thread. This method will make callbacks
// to event() and log() functions. Make sure to call eval_config()
// and possibly provide_creds() as well before this function.
Status connect();
// Return information about the most recent connection. Should be called
// after an event of type "CONNECTED".
ConnectionInfo connection_info();
// Writes current session token to tok and returns true.
// If session token is unavailable, false is returned and
// tok is unmodified.
bool session_token(SessionToken &tok);
// Stop the client. Only meaningful when connect() is running.
// May be called asynchronously from a different thread
// when connect() is running.
void stop();
// Pause the client -- useful to avoid continuous reconnection attempts
// when network is down. May be called from a different thread
// when connect() is running.
void pause(const std::string &reason);
// Resume the client after it has been paused. May be called from a
// different thread when connect() is running.
void resume();
// Do a disconnect/reconnect cycle n seconds from now. May be called
// from a different thread when connect() is running.
void reconnect(int seconds);
// When a connection is close to timeout, the core will call this
// method. If it returns false, the core will disconnect with a
// CONNECTION_TIMEOUT event. If true, the core will enter a PAUSE
// state.
virtual bool pause_on_connection_timeout() = 0;
// Get stats/error info. May be called from a different thread
// when connect() is running.
// number of stats
static int stats_n();
// return a stats name, index should be >= 0 and < stats_n()
static std::string stats_name(int index);
// return a stats value, index should be >= 0 and < stats_n()
long long stats_value(int index) const;
// return all stats in a bundle
std::vector<long long> stats_bundle() const;
// return tun stats only
InterfaceStats tun_stats() const;
// return transport stats only
TransportStats transport_stats() const;
// post control channel message
void post_cc_msg(const std::string &msg);
// Callback for delivering events during connect() call.
// Will be called from the thread executing connect().
virtual void event(const Event &) = 0;
// Callback for logging.
// Will be called from the thread executing connect().
virtual void log(const LogInfo &) override = 0;
// External PKI callbacks
// Will be called from the thread executing connect().
virtual void external_pki_cert_request(ExternalPKICertRequest &) = 0;
virtual void external_pki_sign_request(ExternalPKISignRequest &) = 0;
// Remote override callback (disabled by default).
virtual bool remote_override_enabled();
virtual void remote_override(RemoteOverride &);
// Periodic convenience clock tick, controlled by Config::clockTickMS
virtual void clock_tick();
// Hide protected methods/data from SWIG
#ifdef SWIGJAVA
private:
#else
protected:
#endif
Status do_connect();
virtual void connect_attach();
virtual void connect_pre_run();
virtual void connect_run();
virtual void connect_session_stop();
virtual Stop *get_async_stop();
Private::ClientState *state;
private:
void connect_setup(Status &, bool &);
void do_connect_async();
static Status status_from_exception(const std::exception &);
void parse_extras(const Config &, EvalConfig &);
void external_pki_error(const ExternalPKIRequestBase &, const size_t);
void process_epki_cert_chain(const ExternalPKICertRequest &);
friend class MyClientEvents;
void on_disconnect();
// from ExternalPKIBase
bool sign(const std::string &data,
std::string &sig,
const std::string &algorithm,
const std::string &hashalg,
const std::string &saltlen) override;
// disable copy and assignment
OpenVPNClient(const OpenVPNClient &) = delete;
OpenVPNClient &operator=(const OpenVPNClient &) = delete;
};
} // namespace ClientAPI
} // namespace openvpn