summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan Reams <jbreams@mongodb.com>2018-07-23 16:30:01 -0400
committerJonathan Reams <jbreams@mongodb.com>2018-07-30 11:43:18 -0400
commit35898a0c48b0bb1bcb0a69f7db646d2fda4ec5de (patch)
treeb452f2cc23aca0d132d79e1de9840380ea7803c1 /src
parent43bde3838e8b29476c40c0a2894873f41feca0d5 (diff)
downloadmongo-35898a0c48b0bb1bcb0a69f7db646d2fda4ec5de.tar.gz
SERVER-30997 Redact passwords and options from MongoURI in shell command line
Diffstat (limited to 'src')
-rw-r--r--src/mongo/client/mongo_uri.cpp110
-rw-r--r--src/mongo/client/mongo_uri.h11
-rw-r--r--src/mongo/shell/dbshell.cpp7
3 files changed, 96 insertions, 32 deletions
diff --git a/src/mongo/client/mongo_uri.cpp b/src/mongo/client/mongo_uri.cpp
index 021b98af098..69187e897e9 100644
--- a/src/mongo/client/mongo_uri.cpp
+++ b/src/mongo/client/mongo_uri.cpp
@@ -234,50 +234,104 @@ MongoURI::OptionsMap addTXTOptions(std::map<std::string, std::string> options,
return {std::make_move_iterator(begin(options)), std::make_move_iterator(end(options))};
}
-} // namespace
-
-MongoURI MongoURI::parseImpl(const std::string& url) {
- const StringData urlSD(url);
- // 1. Validate and remove the scheme prefix `mongodb://` or `mongodb+srv://`
- const bool isSeedlist = urlSD.startsWith(kURISRVPrefix);
- if (!(urlSD.startsWith(kURIPrefix) || isSeedlist)) {
- return MongoURI(uassertStatusOK(ConnectionString::parse(url)));
+// Contains the parts of a MongoURI as unowned StringData's. Any code that needs to break up
+// URIs into their basic components without fully parsing them can use this struct.
+// Internally, MongoURI uses this to do basic parsing of the input URI string.
+struct URIParts {
+ explicit URIParts(StringData uri);
+ StringData scheme;
+ StringData username;
+ StringData password;
+ StringData hostIdentifiers;
+ StringData database;
+ StringData options;
+};
+
+URIParts::URIParts(StringData uri) {
+ // 1. Strip off the scheme ("mongo://")
+ auto schemeEnd = uri.find("://");
+ if (schemeEnd == std::string::npos) {
+ uasserted(ErrorCodes::FailedToParse,
+ str::stream() << "URI must begin with " << kURIPrefix << " or " << kURISRVPrefix
+ << ": "
+ << uri);
}
- const auto uriWithoutPrefix = urlSD.substr(urlSD.find("://") + 3);
+ const auto uriWithoutPrefix = uri.substr(schemeEnd + 3);
+ scheme = uri.substr(0, schemeEnd);
// 2. Split the string by the first, unescaped / (if any), yielding:
// split[0]: User information and host identifers
// split[1]: Auth database and connection options
const auto userAndDb = partitionForward(uriWithoutPrefix, '/');
const auto userAndHostInfo = userAndDb.first;
- const auto databaseAndOptions = userAndDb.second;
// 2.b Make sure that there are no question marks in the left side of the /
// as any options after the ? must still have the / delimeter
- if (databaseAndOptions.empty() && userAndHostInfo.find('?') != std::string::npos) {
+ if (userAndDb.second.empty() && userAndHostInfo.find('?') != std::string::npos) {
uasserted(
ErrorCodes::FailedToParse,
str::stream()
<< "URI must contain slash delimeter between hosts and options for mongodb:// URL: "
- << url);
+ << uri);
}
// 3. Split the user information and host identifiers string by the last, unescaped @,
- // yielding:
- // split[0]: User information
- // split[1]: Host identifiers;
const auto userAndHost = partitionBackward(userAndHostInfo, '@');
const auto userInfo = userAndHost.first;
- const auto hostIdentifiers = userAndHost.second;
+ hostIdentifiers = userAndHost.second;
- // 4. Validate, split (if applicable), and URL decode the user information, yielding:
- // split[0] = username
- // split[1] = password
+ // 4. Split up the username and password
const auto userAndPass = partitionForward(userInfo, ':');
- const auto usernameSD = userAndPass.first;
- const auto passwordSD = userAndPass.second;
+ username = userAndPass.first;
+ password = userAndPass.second;
+
+ // 5. Split the database name from the list of options
+ const auto databaseAndOptions = partitionForward(userAndDb.second, '?');
+ database = databaseAndOptions.first;
+ options = databaseAndOptions.second;
+}
+} // namespace
+
+bool MongoURI::isMongoURI(StringData uri) {
+ return (uri.startsWith(kURIPrefix) || uri.startsWith(kURISRVPrefix));
+}
+
+std::string MongoURI::redact(StringData url) {
+ uassert(50891, "String passed to MongoURI::redact wasn't a MongoURI", isMongoURI(url));
+ URIParts parts(url);
+ std::ostringstream out;
+
+ out << parts.scheme << "://";
+ if (!parts.username.empty()) {
+ out << parts.username << "@";
+ }
+ out << parts.hostIdentifiers;
+ if (!parts.database.empty()) {
+ out << "/" << parts.database;
+ }
+
+ return out.str();
+}
+
+MongoURI MongoURI::parseImpl(const std::string& url) {
+ const StringData urlSD(url);
+
+ // 1. Validate and remove the scheme prefix `mongodb://` or `mongodb+srv://`
+ const bool isSeedlist = urlSD.startsWith(kURISRVPrefix);
+ if (!(urlSD.startsWith(kURIPrefix) || isSeedlist)) {
+ return MongoURI(uassertStatusOK(ConnectionString::parse(url)));
+ }
+
+ // 2. Split up the URI into its components for further parsing and validation
+ URIParts parts(url);
+ const auto hostIdentifiers = parts.hostIdentifiers;
+ const auto usernameSD = parts.username;
+ const auto passwordSD = parts.password;
+ const auto databaseSD = parts.database;
+ const auto connectionOptions = parts.options;
+ // 3. URI decode and validate the username/password
const auto containsColonOrAt = [](StringData str) {
return (str.find(':') != std::string::npos) || (str.find('@') != std::string::npos);
};
@@ -309,7 +363,7 @@ MongoURI MongoURI::parseImpl(const std::string& url) {
<< url);
const auto password = passwordWithStatus.getValue();
- // 5. Validate, split, and URL decode the host identifiers.
+ // 4. Validate, split, and URL decode the host identifiers.
const auto hostIdentifiersStr = hostIdentifiers.toString();
std::vector<HostAndPort> servers;
for (auto i = boost::make_split_iterator(hostIdentifiersStr,
@@ -385,13 +439,7 @@ MongoURI MongoURI::parseImpl(const std::string& url) {
});
}
- // 6. Split the auth database and connection options string by the first, unescaped ?,
- // yielding:
- // split[0] = auth database
- // split[1] = connection options
- const auto dbAndOpts = partitionForward(databaseAndOptions, '?');
- const auto databaseSD = dbAndOpts.first;
- const auto connectionOptions = dbAndOpts.second;
+ // 5. Decode the database name
const auto databaseWithStatus = uriDecode(databaseSD);
if (!databaseWithStatus.isOK()) {
uasserted(ErrorCodes::FailedToParse,
@@ -401,7 +449,7 @@ MongoURI MongoURI::parseImpl(const std::string& url) {
}
const auto database = databaseWithStatus.getValue();
- // 7. Validate the database contains no prohibited characters
+ // 6. Validate the database contains no prohibited characters
// Prohibited characters:
// slash ("/"), backslash ("\"), space (" "), double-quote ("""), or dollar sign ("$")
// period (".") is also prohibited, but drivers MAY allow periods
@@ -414,7 +462,7 @@ MongoURI MongoURI::parseImpl(const std::string& url) {
<< url);
}
- // 8. Validate, split, and URL decode the connection options
+ // 7. Validate, split, and URL decode the connection options
auto options =
addTXTOptions(parseOptions(connectionOptions, url), canonicalHost, url, isSeedlist);
diff --git a/src/mongo/client/mongo_uri.h b/src/mongo/client/mongo_uri.h
index 1c56a4e6579..62d48fc9d12 100644
--- a/src/mongo/client/mongo_uri.h
+++ b/src/mongo/client/mongo_uri.h
@@ -109,6 +109,17 @@ public:
static StatusWith<MongoURI> parse(const std::string& url);
+ /*
+ * Returns true if str starts with one of the uri schemes (e.g. mongodb:// or mongodb+srv://)
+ */
+ static bool isMongoURI(StringData str);
+
+ /*
+ * Returns a copy of the input url as a string with the password and connection options
+ * removed. This may uassert or return a mal-formed string if the input is not a valid URI
+ */
+ static std::string redact(StringData url);
+
DBClientBase* connect(StringData applicationName,
std::string& errmsg,
boost::optional<double> socketTimeoutSecs = boost::none) const;
diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp
index bf0a1d3a9db..5f8b5274215 100644
--- a/src/mongo/shell/dbshell.cpp
+++ b/src/mongo/shell/dbshell.cpp
@@ -758,11 +758,16 @@ int _main(int argc, char* argv[], char** envp) {
// hide password from ps output
for (int i = 0; i < (argc - 1); ++i) {
- if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--password")) {
+ StringData arg(argv[i]);
+ if (arg == "-p"_sd || arg == "--password"_sd) {
char* arg = argv[i + 1];
while (*arg) {
*arg++ = 'x';
}
+ } else if (MongoURI::isMongoURI(arg)) {
+ auto reformedURI = MongoURI::redact(arg);
+ auto length = arg.size();
+ ::strncpy(argv[i], reformedURI.data(), length);
}
}