diff options
author | Shaun Verch <shaun.verch@10gen.com> | 2013-09-18 14:02:36 -0400 |
---|---|---|
committer | Shaun Verch <shaun.verch@10gen.com> | 2013-10-04 16:58:54 -0400 |
commit | 6fc951d492881a32754bc3e38e8b5eca78929197 (patch) | |
tree | 73c2afe3120a8a87cf3bc8379e53461b2cf05b1e /src/mongo/util/cmdline_utils | |
parent | 99372153dfe9dd9251e49371d872b5f27dfd6f2c (diff) | |
download | mongo-6fc951d492881a32754bc3e38e8b5eca78929197.tar.gz |
SERVER-8510 Get rid of CmdLine struct and use new option handling style
Diffstat (limited to 'src/mongo/util/cmdline_utils')
-rw-r--r-- | src/mongo/util/cmdline_utils/SConscript | 10 | ||||
-rw-r--r-- | src/mongo/util/cmdline_utils/censor_cmdline.cpp | 161 | ||||
-rw-r--r-- | src/mongo/util/cmdline_utils/censor_cmdline.h | 49 | ||||
-rw-r--r-- | src/mongo/util/cmdline_utils/censor_cmdline_test.cpp | 339 |
4 files changed, 559 insertions, 0 deletions
diff --git a/src/mongo/util/cmdline_utils/SConscript b/src/mongo/util/cmdline_utils/SConscript new file mode 100644 index 00000000000..6cdfe310ef7 --- /dev/null +++ b/src/mongo/util/cmdline_utils/SConscript @@ -0,0 +1,10 @@ +# -*- mode: python -*- + +Import("env") + +env.StaticLibrary('cmdline_utils', ['censor_cmdline.cpp'], + LIBDEPS=['$BUILD_DIR/mongo/bson']) + +env.CppUnitTest('censor_cmdline_test', + 'censor_cmdline_test.cpp', + LIBDEPS=['cmdline_utils', '$BUILD_DIR/mongo/unittest/unittest']) diff --git a/src/mongo/util/cmdline_utils/censor_cmdline.cpp b/src/mongo/util/cmdline_utils/censor_cmdline.cpp new file mode 100644 index 00000000000..e50b7018c99 --- /dev/null +++ b/src/mongo/util/cmdline_utils/censor_cmdline.cpp @@ -0,0 +1,161 @@ +// cmdline.cpp + +/** +* Copyright (C) 2008 10gen 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. If not, see <http://www.gnu.org/licenses/>. +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects for +* all of the code used other than as permitted herein. If you modify file(s) +* with this exception, you may extend this exception to your version of the +* file(s), but you are not obligated to do so. If you do not wish to do so, +* delete this exception statement from your version. If you delete this +* exception statement from all source files in the program, then also delete +* it in the license file. +*/ + +#include "mongo/util/cmdline_utils/censor_cmdline.h" + +#include "mongo/util/mongoutils/str.h" + +namespace mongo { + + namespace cmdline_utils { + + static bool _isPasswordArgument(char const* argumentName); + static bool _isPasswordSwitch(char const* switchName); + + static bool _isPasswordArgument(const char* argumentName) { + static const char* const passwordArguments[] = { + "sslPEMKeyPassword", + "ssl.PEMKeyPassword", + "servicePassword", + NULL // Last entry sentinel. + }; + for (const char* const* current = passwordArguments; *current; ++current) { + if (mongoutils::str::equals(argumentName, *current)) + return true; + } + return false; + } + + static bool _isPasswordSwitch(const char* switchName) { + if (switchName[0] != '-') + return false; + size_t i = 1; + if (switchName[1] == '-') + i = 2; + switchName += i; + + return _isPasswordArgument(switchName); + } + + static void _redact(char* arg) { + for (; *arg; ++arg) + *arg = 'x'; + } + + void censorBSONObjRecursive(const BSONObj& params, // Object we are censoring + const std::string& parentPath, // Set if this is a sub object + bool isArray, + BSONObjBuilder* result) { + BSONObjIterator paramsIterator(params); + while (paramsIterator.more()) { + BSONElement param = paramsIterator.next(); + std::string dottedName = (parentPath.empty() ? param.fieldName() + : isArray ? parentPath + : parentPath + '.' + param.fieldName()); + if (param.type() == Array) { + BSONObjBuilder subArray(result->subarrayStart(param.fieldName())); + censorBSONObjRecursive(param.Obj(), dottedName, true, &subArray); + subArray.done(); + } + else if (param.type() == Object) { + BSONObjBuilder subObj(result->subobjStart(param.fieldName())); + censorBSONObjRecursive(param.Obj(), dottedName, false, &subObj); + subObj.done(); + } + else if (param.type() == String) { + if (_isPasswordArgument(dottedName.c_str())) { + result->append(param.fieldName(), "<password>"); + } + else { + result->append(param); + } + } + else { + result->append(param); + } + } + } + + void censorBSONObj(BSONObj* params) { + BSONObjBuilder builder; + censorBSONObjRecursive(*params, "", false, &builder); + *params = builder.obj(); + } + + void censorArgsVector(std::vector<std::string>* args) { + for (size_t i = 0; i < args->size(); ++i) { + std::string& arg = args->at(i); + const std::string::iterator endSwitch = std::find(arg.begin(), arg.end(), '='); + std::string switchName(arg.begin(), endSwitch); + if (_isPasswordSwitch(switchName.c_str())) { + if (endSwitch == arg.end()) { + if (i + 1 < args->size()) { + args->at(i + 1) = "<password>"; + } + } + else { + arg = switchName + "=<password>"; + } + } + } + } + + void censorArgvArray(int argc, char** argv) { + // Algorithm: For each arg in argv: + // Look for an equal sign in arg; if there is one, temporarily nul it out. + // check to see if arg is a password switch. If so, overwrite the value + // component with xs. + // restore the nul'd out equal sign, if any. + for (int i = 0; i < argc; ++i) { + + char* const arg = argv[i]; + char* const firstEqSign = strchr(arg, '='); + if (NULL != firstEqSign) { + *firstEqSign = '\0'; + } + + if (_isPasswordSwitch(arg)) { + if (NULL == firstEqSign) { + if (i + 1 < argc) { + _redact(argv[i + 1]); + } + } + else { + _redact(firstEqSign + 1); + } + } + + if (NULL != firstEqSign) { + *firstEqSign = '='; + } + } + } + } // namespace cmdline_utils +} diff --git a/src/mongo/util/cmdline_utils/censor_cmdline.h b/src/mongo/util/cmdline_utils/censor_cmdline.h new file mode 100644 index 00000000000..7ab29429697 --- /dev/null +++ b/src/mongo/util/cmdline_utils/censor_cmdline.h @@ -0,0 +1,49 @@ +/** +* Copyright (C) 2008 10gen 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. If not, see <http://www.gnu.org/licenses/>. +* +* As a special exception, the copyright holders give permission to link the +* code of portions of this program with the OpenSSL library under certain +* conditions as described in each individual source file and distribute +* linked combinations including the program with the OpenSSL library. You +* must comply with the GNU Affero General Public License in all respects for +* all of the code used other than as permitted herein. If you modify file(s) +* with this exception, you may extend this exception to your version of the +* file(s), but you are not obligated to do so. If you do not wish to do so, +* delete this exception statement from your version. If you delete this +* exception statement from all source files in the program, then also delete +* it in the license file. +*/ + +#pragma once + +#include <string> +#include <vector> + +#include "mongo/db/jsobj.h" + +namespace mongo { + + namespace cmdline_utils { + + /** + * Blot out sensitive fields in the argv array. + */ + void censorArgvArray(int argc, char** argv); + void censorArgsVector(std::vector<std::string>* args); + void censorBSONObj(BSONObj* params); + + } // namespace cmdline_utils +} + diff --git a/src/mongo/util/cmdline_utils/censor_cmdline_test.cpp b/src/mongo/util/cmdline_utils/censor_cmdline_test.cpp new file mode 100644 index 00000000000..7648f607ecd --- /dev/null +++ b/src/mongo/util/cmdline_utils/censor_cmdline_test.cpp @@ -0,0 +1,339 @@ +/** + * Copyright (C) 2012 10gen 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. If not, see <http://www.gnu.org/licenses/>. + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include <boost/range/size.hpp> +#include <string> +#include <vector> + +#include "mongo/db/jsobj.h" +#include "mongo/unittest/unittest.h" +#include "mongo/util/cmdline_utils/censor_cmdline.h" +#include "mongo/util/options_parser/environment.h" + +namespace mongo { + + +#ifdef MONGO_SSL + Status storeSSLServerOptions(const optionenvironment::Environment& params) { + return Status(ErrorCodes::InternalError, + "This is a storeSSLServerOptions stub and should not be called"); + } +#endif // ifdef MONGO_SSL + +namespace { + + namespace moe = mongo::optionenvironment; + + void testCensoringArgv(const char* const * expected, + const char* const * toCensor, + int elementCount) { + + std::vector<std::string> toCensorStringVec(toCensor, toCensor + elementCount); + std::vector<char*> arrayStandin; + for (size_t i = 0; i < toCensorStringVec.size(); ++i) + arrayStandin.push_back(&*toCensorStringVec[i].begin()); + + char** argv = &*arrayStandin.begin(); + + cmdline_utils::censorArgvArray(elementCount, argv); + + for (int i = 0; i < elementCount; ++i) { + ASSERT_EQUALS(std::string(expected[i]), std::string(argv[i])); + } + } + + void testCensoringVector(const char* const * expected, + const char* const * toCensor, + int elementCount) { + + std::vector<std::string> actual(toCensor, toCensor + elementCount); + + cmdline_utils::censorArgsVector(&actual); + + for (int i = 0; i < elementCount; ++i) { + ASSERT_EQUALS(std::string(expected[i]), actual[i]); + } + } + + TEST(ArgvCensorTests, NothingCensored) { + const char* const argv[] = { + "first", + "second", + "sslPEMKeyPassword=KEEP", + "---sslPEMKeyPassword=KEEP", + "sslPEMKeyPassword", + "KEEP", + "servicePassword=KEEP", + "--servicePassword-", + "KEEP", + "--servicePasswordFake=KEEP" + }; + const int argc = boost::size(argv); + testCensoringArgv(argv, argv, argc); + } + + TEST(ArgvCensorTests, SomeStuffCensoredDoubleHyphen) { + const char* const argv[] = { + "first", + "second", + "--sslPEMKeyPassword=LOSEME", + "--sslPEMKeyPassword", + "Really, loose me!", + "--servicePassword=bad news", + "--servicePassword-", + "KEEP", + "--servicePassword", + "get out of dodge" + }; + const int argc = boost::size(argv); + + const char* const expected[] = { + "first", + "second", + "--sslPEMKeyPassword=xxxxxx", + "--sslPEMKeyPassword", + "xxxxxxxxxxxxxxxxx", + "--servicePassword=xxxxxxxx", + "--servicePassword-", + "KEEP", + "--servicePassword", + "xxxxxxxxxxxxxxxx" + }; + ASSERT_EQUALS(static_cast<int>(boost::size(expected)), argc); + + testCensoringArgv(expected, argv, argc); + } + + TEST(ArgvCensorTests, SomeStuffCensoredSingleHyphen) { + const char* const argv[] = { + "first", + "second", + "-sslPEMKeyPassword=LOSEME", + "-sslPEMKeyPassword", + "Really, loose me!", + "-servicePassword=bad news", + "-servicePassword-", + "KEEP", + "-servicePassword", + "get out of dodge" + }; + const int argc = boost::size(argv); + + const char* const expected[] = { + "first", + "second", + "-sslPEMKeyPassword=xxxxxx", + "-sslPEMKeyPassword", + "xxxxxxxxxxxxxxxxx", + "-servicePassword=xxxxxxxx", + "-servicePassword-", + "KEEP", + "-servicePassword", + "xxxxxxxxxxxxxxxx" + }; + ASSERT_EQUALS(static_cast<int>(boost::size(expected)), argc); + + testCensoringArgv(expected, argv, argc); + } + + TEST(VectorCensorTests, NothingCensored) { + const char* const argv[] = { + "first", + "second", + "sslPEMKeyPassword=KEEP", + "---sslPEMKeyPassword=KEEP", + "sslPEMKeyPassword", + "KEEP", + "servicePassword=KEEP", + "--servicePassword-", + "KEEP", + "--servicePasswordFake=KEEP" + }; + const int argc = boost::size(argv); + testCensoringVector(argv, argv, argc); + } + + TEST(VectorCensorTests, SomeStuffCensoredDoubleHyphen) { + const char* const argv[] = { + "first", + "second", + "--sslPEMKeyPassword=LOSEME", + "--sslPEMKeyPassword", + "Really, loose me!", + "--servicePassword=bad news", + "--servicePassword-", + "KEEP", + "--servicePassword", + "get out of dodge" + }; + const int argc = boost::size(argv); + + const char* const expected[] = { + "first", + "second", + "--sslPEMKeyPassword=<password>", + "--sslPEMKeyPassword", + "<password>", + "--servicePassword=<password>", + "--servicePassword-", + "KEEP", + "--servicePassword", + "<password>" + }; + ASSERT_EQUALS(static_cast<int>(boost::size(expected)), argc); + + testCensoringVector(expected, argv, argc); + } + + TEST(VectorCensorTests, SomeStuffCensoredSingleHyphen) { + const char* const argv[] = { + "first", + "second", + "-sslPEMKeyPassword=LOSEME", + "-sslPEMKeyPassword", + "Really, loose me!", + "-servicePassword=bad news", + "-servicePassword-", + "KEEP", + "-servicePassword", + "get out of dodge" + }; + const int argc = boost::size(argv); + + const char* const expected[] = { + "first", + "second", + "-sslPEMKeyPassword=<password>", + "-sslPEMKeyPassword", + "<password>", + "-servicePassword=<password>", + "-servicePassword-", + "KEEP", + "-servicePassword", + "<password>" + }; + ASSERT_EQUALS(static_cast<int>(boost::size(expected)), argc); + + testCensoringVector(expected, argv, argc); + } + + TEST(BSONObjCensorTests, Strings) { + BSONObj obj = BSON("firstarg" << "not a password" << + "sslPEMKeyPassword" << "this password should be censored" << + "middlearg" << "also not a password" << + "servicePassword" << "this password should also be censored" << + "lastarg" << false << + "servicePassword" << true); + + BSONObj res = BSON("firstarg" << "not a password" << + "sslPEMKeyPassword" << "<password>" << + "middlearg" << "also not a password" << + "servicePassword" << "<password>" << + "lastarg" << false << + "servicePassword" << true); + + cmdline_utils::censorBSONObj(&obj); + ASSERT_EQUALS(res, obj); + } + + TEST(BSONObjCensorTests, Arrays) { + BSONObj obj = BSON("firstarg" << "not a password" << + "sslPEMKeyPassword" << BSON_ARRAY("first censored password" << + "next censored password") << + "middlearg" << "also not a password" << + "servicePassword" << BSON_ARRAY("first censored password" << + "next censored password") << + "lastarg" << false << + "servicePassword" << true); + + BSONObj res = BSON("firstarg" << "not a password" << + "sslPEMKeyPassword" << BSON_ARRAY("<password>" << + "<password>") << + "middlearg" << "also not a password" << + "servicePassword" << BSON_ARRAY("<password>" << + "<password>") << + "lastarg" << false << + "servicePassword" << true); + + cmdline_utils::censorBSONObj(&obj); + ASSERT_EQUALS(res, obj); + } + + TEST(BSONObjCensorTests, SubObjects) { + BSONObj obj = BSON("firstarg" << "not a password" << + "ssl" << BSON("PEMKeyPassword" << BSON_ARRAY("first censored password" << + "next censored password") << + "PEMKeyPassword" << "should be censored too") << + "lastarg" << false << + "servicePassword" << true); + + BSONObj res = BSON("firstarg" << "not a password" << + "ssl" << BSON("PEMKeyPassword" << BSON_ARRAY("<password>" << + "<password>") << + "PEMKeyPassword" << "<password>") << + "lastarg" << false << + "servicePassword" << true); + + cmdline_utils::censorBSONObj(&obj); + ASSERT_EQUALS(res, obj); + } + + TEST(BSONObjCensorTests, Full) { + BSONObj obj = BSON("firstarg" << "not a password" << + "sslPEMKeyPassword" << "this password should be censored" << + "sslPEMKeyPassword" << BSON_ARRAY("first censored password" << + "next censored password") << + "middlearg" << "also not a password" << + "servicePassword" << "this password should also be censored" << + "servicePassword" << BSON_ARRAY("first censored password" << + "next censored password") << + "ssl" << BSON("PEMKeyPassword" << BSON_ARRAY("first censored password" << + "next censored password") << + "PEMKeyPassword" << "should be censored too") << + "lastarg" << false << + "servicePassword" << true); + + BSONObj res = BSON("firstarg" << "not a password" << + "sslPEMKeyPassword" << "<password>" << + "sslPEMKeyPassword" << BSON_ARRAY("<password>" << + "<password>") << + "middlearg" << "also not a password" << + "servicePassword" << "<password>" << + "servicePassword" << BSON_ARRAY("<password>" << + "<password>") << + "ssl" << BSON("PEMKeyPassword" << BSON_ARRAY("<password>" << + "<password>") << + "PEMKeyPassword" << "<password>") << + "lastarg" << false << + "servicePassword" << true); + + cmdline_utils::censorBSONObj(&obj); + ASSERT_EQUALS(res, obj); + } +} // namespace +} // namespace mongo |