1
2 /**
3 * Copyright (C) 2018-present MongoDB, Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the Server Side Public License, version 1,
7 * as published by MongoDB, Inc.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * Server Side Public License for more details.
13 *
14 * You should have received a copy of the Server Side Public License
15 * along with this program. If not, see
16 * <http://www.mongodb.com/licensing/server-side-public-license>.
17 *
18 * As a special exception, the copyright holders give permission to link the
19 * code of portions of this program with the OpenSSL library under certain
20 * conditions as described in each individual source file and distribute
21 * linked combinations including the program with the OpenSSL library. You
22 * must comply with the Server Side Public License in all respects for
23 * all of the code used other than as permitted herein. If you modify file(s)
24 * with this exception, you may extend this exception to your version of the
25 * file(s), but you are not obligated to do so. If you do not wish to do so,
26 * delete this exception statement from your version. If you delete this
27 * exception statement from all source files in the program, then also delete
28 * it in the license file.
29 */
30
31 #define MONGO_LOG_DEFAULT_COMPONENT ::mongo::logger::LogComponent::kControl
32
33 #include "mongo/platform/basic.h"
34
35 #include "mongo/util/net/ssl_options.h"
36
37 #include <boost/filesystem/operations.hpp>
38
39 #include "mongo/base/status.h"
40 #include "mongo/db/server_options.h"
41 #include "mongo/util/log.h"
42 #include "mongo/util/options_parser/startup_options.h"
43 #include "mongo/util/text.h"
44
45 namespace mongo {
46
47 namespace moe = mongo::optionenvironment;
48
49 using std::string;
50
addSSLServerOptions(moe::OptionSection * options)51 Status addSSLServerOptions(moe::OptionSection* options) {
52 options
53 ->addOptionChaining("net.ssl.sslOnNormalPorts",
54 "sslOnNormalPorts",
55 moe::Switch,
56 "use ssl on configured ports")
57 .setSources(moe::SourceAllLegacy)
58 .incompatibleWith("net.ssl.mode");
59
60 options->addOptionChaining(
61 "net.ssl.mode",
62 "sslMode",
63 moe::String,
64 "set the SSL operation mode (disabled|allowSSL|preferSSL|requireSSL)");
65
66 options->addOptionChaining(
67 "net.ssl.PEMKeyFile", "sslPEMKeyFile", moe::String, "PEM file for ssl");
68
69 options
70 ->addOptionChaining(
71 "net.ssl.PEMKeyPassword", "sslPEMKeyPassword", moe::String, "PEM file password")
72 .setImplicit(moe::Value(std::string("")));
73
74 options->addOptionChaining("net.ssl.clusterFile",
75 "sslClusterFile",
76 moe::String,
77 "Key file for internal SSL authentication");
78
79 options
80 ->addOptionChaining("net.ssl.clusterPassword",
81 "sslClusterPassword",
82 moe::String,
83 "Internal authentication key file password")
84 .setImplicit(moe::Value(std::string("")));
85
86 options->addOptionChaining(
87 "net.ssl.CAFile", "sslCAFile", moe::String, "Certificate Authority file for SSL");
88
89 options->addOptionChaining("net.ssl.clusterCAFile",
90 "sslClusterCAFile",
91 moe::String,
92 "CA used for verifying remotes during outbound connections");
93
94 options->addOptionChaining(
95 "net.ssl.CRLFile", "sslCRLFile", moe::String, "Certificate Revocation List file for SSL");
96
97 options
98 ->addOptionChaining("net.ssl.sslCipherConfig",
99 "sslCipherConfig",
100 moe::String,
101 "OpenSSL cipher configuration string")
102 .hidden();
103
104 options->addOptionChaining(
105 "net.ssl.disabledProtocols",
106 "sslDisabledProtocols",
107 moe::String,
108 "Comma separated list of TLS protocols to disable [TLS1_0,TLS1_1,TLS1_2]");
109
110 options
111 ->addOptionChaining(
112 "net.tls.logVersions",
113 "tlsLogVersions",
114 moe::String,
115 "Comma separated list of TLS protocols to log on connect [TLS1_0,TLS1_1,TLS1_2]")
116 .hidden();
117
118 options->addOptionChaining("net.ssl.weakCertificateValidation",
119 "sslWeakCertificateValidation",
120 moe::Switch,
121 "allow client to connect without "
122 "presenting a certificate");
123
124 // Alias for --sslWeakCertificateValidation.
125 options->addOptionChaining("net.ssl.allowConnectionsWithoutCertificates",
126 "sslAllowConnectionsWithoutCertificates",
127 moe::Switch,
128 "allow client to connect without presenting a certificate");
129
130 options->addOptionChaining("net.ssl.allowInvalidHostnames",
131 "sslAllowInvalidHostnames",
132 moe::Switch,
133 "Allow server certificates to provide non-matching hostnames");
134
135 options->addOptionChaining("net.ssl.allowInvalidCertificates",
136 "sslAllowInvalidCertificates",
137 moe::Switch,
138 "allow connections to servers with invalid certificates");
139
140 options->addOptionChaining(
141 "net.ssl.FIPSMode", "sslFIPSMode", moe::Switch, "activate FIPS 140-2 mode at startup");
142
143 return Status::OK();
144 }
145
storeTLSLogVersion(const std::string & loggedProtocols)146 Status storeTLSLogVersion(const std::string& loggedProtocols) {
147 // The tlsLogVersion field is composed of a comma separated list of protocols to
148 // log. First, tokenize the field.
149 const auto tokens = StringSplitter::split(loggedProtocols, ",");
150
151 // All universally accepted tokens, and their corresponding enum representation.
152 const std::map<std::string, SSLParams::Protocols> validConfigs{
153 {"TLS1_0", SSLParams::Protocols::TLS1_0},
154 {"TLS1_1", SSLParams::Protocols::TLS1_1},
155 {"TLS1_2", SSLParams::Protocols::TLS1_2},
156 {"TLS1_3", SSLParams::Protocols::TLS1_3},
157 };
158
159 // Map the tokens to their enum values, and push them onto the list of logged protocols.
160 for (const std::string& token : tokens) {
161 auto mappedToken = validConfigs.find(token);
162 if (mappedToken != validConfigs.end()) {
163 sslGlobalParams.tlsLogVersions.push_back(mappedToken->second);
164 continue;
165 }
166
167 return Status(ErrorCodes::BadValue, "Unrecognized tlsLogVersions '" + token + "'");
168 }
169
170 return Status::OK();
171 }
172
173
addSSLClientOptions(moe::OptionSection * options)174 Status addSSLClientOptions(moe::OptionSection* options) {
175 options->addOptionChaining("ssl", "ssl", moe::Switch, "use SSL for all connections");
176
177 options
178 ->addOptionChaining(
179 "ssl.CAFile", "sslCAFile", moe::String, "Certificate Authority file for SSL")
180 .requires("ssl");
181
182 options
183 ->addOptionChaining(
184 "ssl.PEMKeyFile", "sslPEMKeyFile", moe::String, "PEM certificate/key file for SSL")
185 .requires("ssl");
186
187 options
188 ->addOptionChaining("ssl.PEMKeyPassword",
189 "sslPEMKeyPassword",
190 moe::String,
191 "password for key in PEM file for SSL")
192 .requires("ssl");
193
194 options
195 ->addOptionChaining(
196 "ssl.CRLFile", "sslCRLFile", moe::String, "Certificate Revocation List file for SSL")
197 .requires("ssl")
198 .requires("ssl.CAFile");
199
200 options
201 ->addOptionChaining("net.ssl.allowInvalidHostnames",
202 "sslAllowInvalidHostnames",
203 moe::Switch,
204 "allow connections to servers with non-matching hostnames")
205 .requires("ssl");
206
207 options
208 ->addOptionChaining("ssl.allowInvalidCertificates",
209 "sslAllowInvalidCertificates",
210 moe::Switch,
211 "allow connections to servers with invalid certificates")
212 .requires("ssl");
213
214 options->addOptionChaining(
215 "ssl.FIPSMode", "sslFIPSMode", moe::Switch, "activate FIPS 140-2 mode at startup");
216
217 options
218 ->addOptionChaining(
219 "ssl.disabledProtocols",
220 "sslDisabledProtocols",
221 moe::String,
222 "Comma separated list of TLS protocols to disable [TLS1_0,TLS1_1,TLS1_2]")
223 .hidden();
224
225
226 return Status::OK();
227 }
228
validateSSLServerOptions(const moe::Environment & params)229 Status validateSSLServerOptions(const moe::Environment& params) {
230 #ifdef _WIN32
231 if (params.count("install") || params.count("reinstall")) {
232 if (params.count("net.ssl.PEMKeyFile") &&
233 !boost::filesystem::path(params["net.ssl.PEMKeyFile"].as<string>()).is_absolute()) {
234 return Status(ErrorCodes::BadValue,
235 "PEMKeyFile requires an absolute file path with Windows services");
236 }
237
238 if (params.count("net.ssl.clusterFile") &&
239 !boost::filesystem::path(params["net.ssl.clusterFile"].as<string>()).is_absolute()) {
240 return Status(ErrorCodes::BadValue,
241 "clusterFile requires an absolute file path with Windows services");
242 }
243
244 if (params.count("net.ssl.CAFile") &&
245 !boost::filesystem::path(params["net.ssl.CAFile"].as<string>()).is_absolute()) {
246 return Status(ErrorCodes::BadValue,
247 "CAFile requires an absolute file path with Windows services");
248 }
249
250 if (params.count("net.ssl.CRLFile") &&
251 !boost::filesystem::path(params["net.ssl.CRLFile"].as<string>()).is_absolute()) {
252 return Status(ErrorCodes::BadValue,
253 "CRLFile requires an absolute file path with Windows services");
254 }
255 }
256 #endif
257
258 return Status::OK();
259 }
260
canonicalizeSSLServerOptions(moe::Environment * params)261 Status canonicalizeSSLServerOptions(moe::Environment* params) {
262 if (params->count("net.ssl.sslOnNormalPorts") &&
263 (*params)["net.ssl.sslOnNormalPorts"].as<bool>() == true) {
264 Status ret = params->set("net.ssl.mode", moe::Value(std::string("requireSSL")));
265 if (!ret.isOK()) {
266 return ret;
267 }
268 ret = params->remove("net.ssl.sslOnNormalPorts");
269 if (!ret.isOK()) {
270 return ret;
271 }
272 }
273
274 return Status::OK();
275 }
276
277 /**
278 * Older versions of mongod/mongos accepted --sslDisabledProtocols values
279 * in the form 'noTLS1_0,noTLS1_1'. kAcceptNegativePrefix allows us to
280 * continue accepting this format on mongod/mongos while only supporting
281 * the "standard" TLS1_X format in the shell.
282 */
283 enum DisabledProtocolsMode {
284 kStandardFormat,
285 kAcceptNegativePrefix,
286 };
287
storeDisabledProtocols(const std::string & disabledProtocols,DisabledProtocolsMode mode=kStandardFormat)288 Status storeDisabledProtocols(const std::string& disabledProtocols,
289 DisabledProtocolsMode mode = kStandardFormat) {
290 if (disabledProtocols == "none") {
291 // Allow overriding the default behavior below of implicitly disabling TLS 1.0.
292 return Status::OK();
293 }
294
295 // The disabledProtocols field is composed of a comma separated list of protocols to
296 // disable. First, tokenize the field.
297 const auto tokens = StringSplitter::split(disabledProtocols, ",");
298
299 // All universally accepted tokens, and their corresponding enum representation.
300 const std::map<std::string, SSLParams::Protocols> validConfigs{
301 {"TLS1_0", SSLParams::Protocols::TLS1_0},
302 {"TLS1_1", SSLParams::Protocols::TLS1_1},
303 {"TLS1_2", SSLParams::Protocols::TLS1_2},
304 {"TLS1_3", SSLParams::Protocols::TLS1_3},
305 };
306
307 // These noTLS* tokens exist for backwards compatibility.
308 const std::map<std::string, SSLParams::Protocols> validNoConfigs{
309 {"noTLS1_0", SSLParams::Protocols::TLS1_0},
310 {"noTLS1_1", SSLParams::Protocols::TLS1_1},
311 {"noTLS1_2", SSLParams::Protocols::TLS1_2},
312 {"noTLS1_3", SSLParams::Protocols::TLS1_3},
313 };
314
315 // Map the tokens to their enum values, and push them onto the list of disabled protocols.
316 for (const std::string& token : tokens) {
317 auto mappedToken = validConfigs.find(token);
318 if (mappedToken != validConfigs.end()) {
319 sslGlobalParams.sslDisabledProtocols.push_back(mappedToken->second);
320 continue;
321 }
322
323 if (mode == DisabledProtocolsMode::kAcceptNegativePrefix) {
324 auto mappedNoToken = validNoConfigs.find(token);
325 if (mappedNoToken != validNoConfigs.end()) {
326 sslGlobalParams.sslDisabledProtocols.push_back(mappedNoToken->second);
327 continue;
328 }
329 }
330
331 return Status(ErrorCodes::BadValue, "Unrecognized disabledProtocols '" + token + "'");
332 }
333
334 return Status::OK();
335 }
336
storeSSLServerOptions(const moe::Environment & params)337 Status storeSSLServerOptions(const moe::Environment& params) {
338 if (params.count("net.ssl.mode")) {
339 std::string sslModeParam = params["net.ssl.mode"].as<string>();
340 if (sslModeParam == "disabled") {
341 sslGlobalParams.sslMode.store(SSLParams::SSLMode_disabled);
342 } else if (sslModeParam == "allowSSL") {
343 sslGlobalParams.sslMode.store(SSLParams::SSLMode_allowSSL);
344 } else if (sslModeParam == "preferSSL") {
345 sslGlobalParams.sslMode.store(SSLParams::SSLMode_preferSSL);
346 } else if (sslModeParam == "requireSSL") {
347 sslGlobalParams.sslMode.store(SSLParams::SSLMode_requireSSL);
348 } else {
349 return Status(ErrorCodes::BadValue, "unsupported value for sslMode " + sslModeParam);
350 }
351 }
352
353 if (params.count("net.ssl.PEMKeyFile")) {
354 sslGlobalParams.sslPEMKeyFile =
355 boost::filesystem::absolute(params["net.ssl.PEMKeyFile"].as<string>()).generic_string();
356 }
357
358 if (params.count("net.ssl.PEMKeyPassword")) {
359 sslGlobalParams.sslPEMKeyPassword = params["net.ssl.PEMKeyPassword"].as<string>();
360 }
361
362 if (params.count("net.ssl.clusterFile")) {
363 sslGlobalParams.sslClusterFile =
364 boost::filesystem::absolute(params["net.ssl.clusterFile"].as<string>())
365 .generic_string();
366 }
367
368 if (params.count("net.ssl.clusterPassword")) {
369 sslGlobalParams.sslClusterPassword = params["net.ssl.clusterPassword"].as<string>();
370 }
371
372 if (params.count("net.ssl.CAFile")) {
373 sslGlobalParams.sslCAFile =
374 boost::filesystem::absolute(params["net.ssl.CAFile"].as<std::string>())
375 .generic_string();
376 }
377
378 if (params.count("net.ssl.clusterCAFile")) {
379 sslGlobalParams.sslClusterCAFile =
380 boost::filesystem::absolute(params["net.ssl.clusterCAFile"].as<std::string>())
381 .generic_string();
382 }
383
384 if (params.count("net.ssl.CRLFile")) {
385 sslGlobalParams.sslCRLFile =
386 boost::filesystem::absolute(params["net.ssl.CRLFile"].as<std::string>())
387 .generic_string();
388 }
389
390 if (params.count("net.ssl.sslCipherConfig")) {
391 warning()
392 << "net.ssl.sslCipherConfig is deprecated. It will be removed in a future release.";
393 if (!sslGlobalParams.sslCipherConfig.empty()) {
394 return Status(ErrorCodes::BadValue,
395 "net.ssl.sslCipherConfig is incompatible with the openSSLCipherConfig "
396 "setParameter");
397 }
398 sslGlobalParams.sslCipherConfig = params["net.ssl.sslCipherConfig"].as<string>();
399 }
400
401 if (params.count("net.ssl.disabledProtocols")) {
402 const auto status = storeDisabledProtocols(params["net.ssl.disabledProtocols"].as<string>(),
403 DisabledProtocolsMode::kAcceptNegativePrefix);
404 if (!status.isOK()) {
405 return status;
406 }
407 }
408
409 if (params.count("net.tls.logVersions")) {
410 const auto status = storeTLSLogVersion(params["net.tls.logVersions"].as<string>());
411 if (!status.isOK()) {
412 return status;
413 }
414 }
415
416 if (params.count("net.ssl.weakCertificateValidation")) {
417 sslGlobalParams.sslWeakCertificateValidation =
418 params["net.ssl.weakCertificateValidation"].as<bool>();
419 } else if (params.count("net.ssl.allowConnectionsWithoutCertificates")) {
420 sslGlobalParams.sslWeakCertificateValidation =
421 params["net.ssl.allowConnectionsWithoutCertificates"].as<bool>();
422 }
423 if (params.count("net.ssl.allowInvalidHostnames")) {
424 sslGlobalParams.sslAllowInvalidHostnames =
425 params["net.ssl.allowInvalidHostnames"].as<bool>();
426 }
427 if (params.count("net.ssl.allowInvalidCertificates")) {
428 sslGlobalParams.sslAllowInvalidCertificates =
429 params["net.ssl.allowInvalidCertificates"].as<bool>();
430 }
431 if (params.count("net.ssl.FIPSMode")) {
432 sslGlobalParams.sslFIPSMode = params["net.ssl.FIPSMode"].as<bool>();
433 }
434
435 int clusterAuthMode = serverGlobalParams.clusterAuthMode.load();
436 if (sslGlobalParams.sslMode.load() != SSLParams::SSLMode_disabled) {
437 if (sslGlobalParams.sslPEMKeyFile.size() == 0) {
438 return Status(ErrorCodes::BadValue, "need sslPEMKeyFile when SSL is enabled");
439 }
440 if (!sslGlobalParams.sslCRLFile.empty() && sslGlobalParams.sslCAFile.empty()) {
441 return Status(ErrorCodes::BadValue, "need sslCAFile with sslCRLFile");
442 }
443 std::string sslCANotFoundError(
444 "No SSL certificate validation can be performed since"
445 " no CA file has been provided; please specify an"
446 " sslCAFile parameter");
447
448 if (sslGlobalParams.sslCAFile.empty() &&
449 clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509) {
450 return Status(ErrorCodes::BadValue, sslCANotFoundError);
451 }
452 } else if (sslGlobalParams.sslPEMKeyFile.size() || sslGlobalParams.sslPEMKeyPassword.size() ||
453 sslGlobalParams.sslClusterFile.size() || sslGlobalParams.sslClusterPassword.size() ||
454 sslGlobalParams.sslCAFile.size() || sslGlobalParams.sslCRLFile.size() ||
455 sslGlobalParams.sslCipherConfig.size() ||
456 sslGlobalParams.sslDisabledProtocols.size() ||
457 sslGlobalParams.sslWeakCertificateValidation) {
458 return Status(ErrorCodes::BadValue,
459 "need to enable SSL via the sslMode flag when "
460 "using SSL configuration parameters");
461 }
462 if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendKeyFile ||
463 clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509 ||
464 clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509) {
465 if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_disabled) {
466 return Status(ErrorCodes::BadValue, "need to enable SSL via the sslMode flag");
467 }
468 }
469 if (sslGlobalParams.sslMode.load() == SSLParams::SSLMode_allowSSL) {
470 // allowSSL and x509 is valid only when we are transitioning to auth.
471 if (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_sendX509 ||
472 (clusterAuthMode == ServerGlobalParams::ClusterAuthMode_x509 &&
473 !serverGlobalParams.transitionToAuth)) {
474 return Status(ErrorCodes::BadValue,
475 "cannot have x.509 cluster authentication in allowSSL mode");
476 }
477 }
478 return Status::OK();
479 }
480
storeSSLClientOptions(const moe::Environment & params)481 Status storeSSLClientOptions(const moe::Environment& params) {
482 if (params.count("ssl") && params["ssl"].as<bool>() == true) {
483 sslGlobalParams.sslMode.store(SSLParams::SSLMode_requireSSL);
484 }
485 if (params.count("ssl.PEMKeyFile")) {
486 sslGlobalParams.sslPEMKeyFile = params["ssl.PEMKeyFile"].as<std::string>();
487 }
488 if (params.count("ssl.PEMKeyPassword")) {
489 sslGlobalParams.sslPEMKeyPassword = params["ssl.PEMKeyPassword"].as<std::string>();
490 }
491 if (params.count("ssl.CAFile")) {
492 sslGlobalParams.sslCAFile = params["ssl.CAFile"].as<std::string>();
493 }
494 if (params.count("ssl.CRLFile")) {
495 sslGlobalParams.sslCRLFile = params["ssl.CRLFile"].as<std::string>();
496 }
497 if (params.count("net.ssl.allowInvalidHostnames")) {
498 sslGlobalParams.sslAllowInvalidHostnames =
499 params["net.ssl.allowInvalidHostnames"].as<bool>();
500 }
501 if (params.count("ssl.allowInvalidCertificates")) {
502 sslGlobalParams.sslAllowInvalidCertificates = true;
503 }
504 if (params.count("ssl.FIPSMode")) {
505 sslGlobalParams.sslFIPSMode = true;
506 }
507 if (params.count("ssl.disabledProtocols")) {
508 const auto status =
509 storeDisabledProtocols(params["ssl.disabledProtocols"].as<std::string>());
510 if (!status.isOK()) {
511 return status;
512 }
513 }
514
515 return Status::OK();
516 }
517
518 } // namespace mongo
519