OpenVPN 3 Core Library
Loading...
Searching...
No Matches
test_cliopt.cpp
Go to the documentation of this file.
1#include <iostream>
2#include "test_common.hpp"
3
4/* if initproces.hpp is not included, mingw/windows compilation fails with
5 * weird stack struct creation related errors in OpenSSL */
8
9#include <client/ovpncli.hpp>
11
12using namespace openvpn;
13
14/* The config parser checks for valid certificates, provide valid ones */
15std::string dummysecp256cert = "-----BEGIN CERTIFICATE-----\n"
16 "MIIBETCBuAIJAImY2B4ODlQuMAoGCCqGSM49BAMCMBExDzANBgNVBAMMBnNlcnZl\n"
17 "cjAeFw0yMjA4MzAxNTA3NDJaFw0zMjA4MjcxNTA3NDJaMBExDzANBgNVBAMMBnNl\n"
18 "cnZlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDwU0GWKxTxYXP/L448OlaQr\n"
19 "fhF2p83eg/55LJB7Aiq7xckQImGa3w2heo01hFQXQ/4mK3wsLZr7ZZl7IDC4hhMw\n"
20 "CgYIKoZIzj0EAwIDSAAwRQIhAKDmwivsD4qjRtbaXmUNc3src6oFOCus32ZRZw0p\n"
21 "Oz9zAiBZ47YdsJ985ID5COg1+nCKk+0d7jWjICbPcODHyzH4fg==\n"
22 "-----END CERTIFICATE-----\n";
23
24std::string dummysecp256key = "-----BEGIN PRIVATE KEY-----\n"
25 "MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgbzZUYL0jZM05vB2O\n"
26 "kIKcA1OxSKw9ZVQ8UnlUCf6l/8ChRANCAAQ8FNBlisU8WFz/y+OPDpWkK34RdqfN\n"
27 "3oP+eSyQewIqu8XJECJhmt8NoXqNNYRUF0P+Jit8LC2a+2WZeyAwuIYT\n"
28 "-----END PRIVATE KEY-----\n";
29
30std::string certconfig = "<ca>\n"
31 + dummysecp256cert + "</ca>\n"
32 + "<cert>\n"
33 + dummysecp256cert + "</cert>\n"
34 + "<key>\n" + dummysecp256key
35 + "</key>\n";
36
37std::string minimalConfig = certconfig + "\n"
38 + "client\n"
39 "remote wooden.box\n";
40
41class ValidConfigs : public testing::TestWithParam<std::string>
42{
43};
44typedef std::pair<std::string, std::string> config_error;
45class InvalidConfigs : public testing::TestWithParam<config_error>
46{
47};
48
49void load_client_config(const std::string &config_content)
50{
51 OptionList options;
53 config.clientconf.dco = true;
54 config.proto_context_options.reset(new ProtoContextCompressionOptions());
55
57 ParseClientConfig conf = ParseClientConfig::parse(config_content);
58
59 auto parsed_config = ParseClientConfig::parse(config_content, nullptr, options);
60
61 ClientOptions cliopt(options, config);
62}
63
64TEST_P(ValidConfigs, valid_config)
65{
66 load_client_config(GetParam());
67}
68
69TEST_P(InvalidConfigs, config_throws_option_error)
70{
72 load_client_config(GetParam().first),
73 option_error,
74 GetParam().second);
75}
76
77TEST(config, missingRequiredOption)
78{
79 ParseClientConfig conf = ParseClientConfig::parse("mode server");
80 EXPECT_EQ(conf.error(), true);
81 EXPECT_TRUE(conf.message().find("option_error: remote option not specified") != std::string::npos);
82}
83
85 optionError,
87 testing::Values(
88 config_error{std::string("remote wooden.box\n") + "mode server"
89 + "\n<ca>\n" + dummysecp256cert + "</ca>\n",
90 "option 'cert' not found"},
91 config_error{minimalConfig + "mode", "Only 'mode p2p' supported"},
92 config_error{minimalConfig + "mode server", "Only 'mode p2p' supported"},
93 config_error{minimalConfig + "key-method 1", "Only 'key-method 2' is supported"},
94 config_error{minimalConfig + "fragment", "sorry, 'fragment' directive is not supported"}));
95
96TEST(config, parse_unknown_option)
97{
99 load_client_config(minimalConfig + "bikeshed-color green"),
100 ErrorCode,
101 "UNKNOWN/UNSUPPORTED OPTIONS");
102}
103
105 minimalConfigs,
107 testing::Values(
108 /* A duplicate option should not cause our parser to fail */
109 minimalConfig + "cipher AES-192-CBC\ncipher AES-256-GCM\n",
110 /* Bikeshed colour is ignored should throw no error */
111 minimalConfig + "ignore-unknown-option bikeshed-colour bikeshed-color\n"
112 "ignore-unknown-option danish axe phk\n"
113 "bikeshed-colour green",
114 minimalConfig + "setenv opt bikeshed-paint silver with sparkling",
115 /* warnings, but no errors */
116 minimalConfig + "tun-ipv6\n",
117 minimalConfig + "opt-verify\n"));
118
119TEST(config, parse_management)
120{
122 load_client_config(minimalConfig + "management-is-blue"),
123 ErrorCode,
124 "OpenVPN management interface is not supported by this client");
125
127 load_client_config(minimalConfig + "management"),
128 ErrorCode,
129 "OpenVPN management interface is not supported by this client");
130}
131
132TEST(config, duplicate_options_sets)
133{
134 /* Do the whole dance to get a ClientOption object to access the list */
135 OptionList options;
137 config.clientconf.dco = false;
138 config.proto_context_options = new ProtoContextCompressionOptions();
139
140 ClientAPI::OpenVPNClientHelper client_helper;
141
143
144 auto parsed_config = ParseClientConfig::parse(minimalConfig, nullptr, options);
145
146 ClientOptions cliopt(options, config);
147
148 std::vector<std::unordered_set<std::string>> allsets = {
157
158 std::unordered_set<std::string> allOptions;
159
160 for (auto set : allsets)
161 {
162 for (const auto &optname : set)
163 {
164 /* Use an expection instead of an assert to get the name of the option
165 * that is a duplicate */
166 if (allOptions.contains(optname))
167 throw std::runtime_error("duplicate element: " + optname);
168 allOptions.insert(optname);
169 }
170 }
171}
172
173TEST(config, dco_compatibility)
174{
176 {
177 ClientAPI::Config api_config;
178
179 /* If we just use http-proxy without argument, we will bail out for
180 * missing parameter instead */
181 if (optname == "http-proxy")
182 optname = "proto tcp\nhttp-proxy 1.1.1.1 8080";
183
184 api_config.dco = true;
185 api_config.content = minimalConfig + optname;
186 ClientAPI::OpenVPNClientHelper client_helper;
187 auto eval = client_helper.eval_config(api_config);
188
189 EXPECT_FALSE(eval.dcoCompatible);
190
193 option_error,
194 "ERR_INVALID_CONFIG: option_error: dco_compatibility: config/options are not compatible with dco");
195 }
196}
197
198TEST(config, server_cert_in_eval)
199{
200 ClientAPI::Config api_config;
201 api_config.content = minimalConfig;
202
203 ClientAPI::OpenVPNClientHelper client_helper;
204 auto eval = client_helper.eval_config(api_config);
205
206 EXPECT_FALSE(eval.vpnCa.empty());
207}
208
209
210TEST(config, server_options_present_in_error_msg)
211{
212 std::vector<std::string> server_options = {"server 10.0.0.0 255.255.255.0",
213 "push \"foo bar\""};
214
215 for (auto &option : server_options)
216 {
217 auto optname = option.substr(0, option.find(' '));
218 auto expected_error_string = "Server only option: " + optname;
219
222 ErrorCode,
223 expected_error_string);
224 }
225}
226
227TEST(config, unknown_options_present_in_error_msg)
228{
229 std::vector<std::string> server_options = {"make-a-lot-of-noise", "water-the-plants"};
230
231 for (auto &option : server_options)
232 {
233 auto optname = option.substr(0, option.find(' '));
234 auto expected_error_string = "UNKNOWN/UNSUPPORTED OPTIONS: " + optname;
235
238 ErrorCode,
239 expected_error_string);
240 }
241}
242
243TEST(config, multiple_option_errors)
244{
245 std::ostringstream os;
246 os << "OpenVPN management interface is not supported by this client: management\n";
247 os << "UNKNOWN/UNSUPPORTED OPTIONS: lol,lal";
248
250 load_client_config(minimalConfig + "management\nlol\nlal"),
251 ErrorCode,
252 os.str());
253}
254
256 clientPull,
258 testing::Values(
259 /* Should not trigger an error, even without --client in place */
260 certconfig + "\nremote 1.2.3.4\ntls-client\npull\n",
261 /* Should not trigger an error. Redundant options are no problem */
262 certconfig + "\nremote 1.2.3.4\ntls-client\npull\nclient\n",
263 certconfig + "\nremote 1.2.3.4\npull\nclient\n",
264 certconfig + "\nremote 1.2.3.4\nclient\ntls-client\n"));
265
267 clientPull,
269 testing::Values(
270 config_error{certconfig + "\nremote 1.2.3.4\n",
271 "option_error: Neither 'client' nor both 'tls-client' and 'pull' options declared. OpenVPN3 client only supports --client mode."},
272 config_error{certconfig + "\nremote 1.2.3.4\ntls-client\n",
273 "option_error: Neither 'client' nor both 'tls-client' and 'pull' options declared. OpenVPN3 client only supports --client mode."},
274 config_error{certconfig + "\nremote 1.2.3.4\npull\n",
275 "option_error: Neither 'client' nor both 'tls-client' and 'pull' options declared. OpenVPN3 client only supports --client mode."}));
276
277TEST(config, meta_option_in_content)
278{
279 OptionList options;
280 auto cfg = minimalConfig + "\n# OVPN_ACCESS_SERVER_AAA=BBB";
281
283 kvl.push_back(new OptionList::KeyValue("OVPN_ACCESS_SERVER_CCC", "DDD"));
284
285 auto parsed_config = ParseClientConfig::parse(cfg, &kvl, options);
286
288 config.clientconf.dco = true;
289 config.proto_context_options.reset(new ProtoContextCompressionOptions());
290 ClientOptions cliopt(options, config);
291
292 auto opt = options.get("AAA");
293 ASSERT_TRUE(opt.meta());
294 ASSERT_EQ(opt.get(1, 256), "BBB");
295
296 opt = options.get("CCC");
297 ASSERT_TRUE(opt.meta());
298 ASSERT_EQ(opt.get(1, 256), "DDD");
299}
EvalConfig eval_config(const Config &config)
Definition ovpncli.cpp:761
std::unordered_set< std::string > settings_standalone_options
Definition cliopt.hpp:847
std::unordered_set< std::string > settings_ignoreSilently
Definition cliopt.hpp:863
std::unordered_set< std::string > settings_feature_not_implemented_warn
Definition cliopt.hpp:796
std::unordered_set< std::string > settings_ignoreWithWarning
Definition cliopt.hpp:676
std::unordered_set< std::string > settings_removedOptions
Definition cliopt.hpp:860
std::unordered_set< std::string > settings_serverOnlyOptions
Definition cliopt.hpp:723
static std::unordered_set< std::string > dco_incompatible_opts
Definition cliopt.hpp:590
std::unordered_set< std::string > settings_pushonlyoptions
Definition cliopt.hpp:812
std::unordered_set< std::string > settings_feature_not_implemented_fatal
Definition cliopt.hpp:764
const Option & get(const std::string &name) const
Definition options.hpp:1254
static ParseClientConfig parse(const std::string &content)
const std::string & message() const
ClientConfigParsed clientconf
Definition cliopt.hpp:157
std::ostringstream os
INSTANTIATE_TEST_SUITE_P(optionError, InvalidConfigs, testing::Values(config_error{std::string("remote wooden.box\n")+"mode server"+"\n<ca>\n"+dummysecp256cert+"</ca>\n", "option 'cert' not found"}, config_error{minimalConfig+"mode", "Only 'mode p2p' supported"}, config_error{minimalConfig+"mode server", "Only 'mode p2p' supported"}, config_error{minimalConfig+"key-method 1", "Only 'key-method 2' is supported"}, config_error{minimalConfig+"fragment", "sorry, 'fragment' directive is not supported"}))
TEST_P(ValidConfigs, valid_config)
std::string dummysecp256key
void load_client_config(const std::string &config_content)
std::string dummysecp256cert
std::string minimalConfig
TEST(config, missingRequiredOption)
std::pair< std::string, std::string > config_error
std::string certconfig
#define OVPN_EXPECT_THROW(statement, expected_exception, expected_text)
static const char config[]
const std::string optname