summaryrefslogtreecommitdiff
path: root/src/mongo/util/cmdline_utils
diff options
context:
space:
mode:
authorShaun Verch <shaun.verch@10gen.com>2013-09-18 14:02:36 -0400
committerShaun Verch <shaun.verch@10gen.com>2013-10-04 16:58:54 -0400
commit6fc951d492881a32754bc3e38e8b5eca78929197 (patch)
tree73c2afe3120a8a87cf3bc8379e53461b2cf05b1e /src/mongo/util/cmdline_utils
parent99372153dfe9dd9251e49371d872b5f27dfd6f2c (diff)
downloadmongo-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/SConscript10
-rw-r--r--src/mongo/util/cmdline_utils/censor_cmdline.cpp161
-rw-r--r--src/mongo/util/cmdline_utils/censor_cmdline.h49
-rw-r--r--src/mongo/util/cmdline_utils/censor_cmdline_test.cpp339
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