diff options
author | Jonathan Reams <jbreams@mongodb.com> | 2018-07-23 16:30:01 -0400 |
---|---|---|
committer | Jonathan Reams <jbreams@mongodb.com> | 2018-07-30 11:43:18 -0400 |
commit | 35898a0c48b0bb1bcb0a69f7db646d2fda4ec5de (patch) | |
tree | b452f2cc23aca0d132d79e1de9840380ea7803c1 /src | |
parent | 43bde3838e8b29476c40c0a2894873f41feca0d5 (diff) | |
download | mongo-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.cpp | 110 | ||||
-rw-r--r-- | src/mongo/client/mongo_uri.h | 11 | ||||
-rw-r--r-- | src/mongo/shell/dbshell.cpp | 7 |
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); } } |