summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Pulo <kevin.pulo@mongodb.com>2017-04-28 05:43:36 +0000
committerKevin Pulo <kevin.pulo@mongodb.com>2017-05-01 12:48:04 +1000
commit0ee59992ef9627809e2ff13c608a023fc6b8c909 (patch)
tree6085d9064fbce9e61137cb44d5605d90d702c195
parent4e49a2aa5c93235aac0e0ef2b06c4ae02f5f7623 (diff)
downloadmongo-0ee59992ef9627809e2ff13c608a023fc6b8c909.tar.gz
SERVER-25335 SERVER-26489 avoid group and other permissions when creating .dbshell history file
Cherry-picked from: * 035cf2afc04988b22cb67f4ebfd77e9b344cb6e0 * f2c3376d70a3540d3bee5e32abe9d5517f571369 * 4a431ceb4cb1d986ceb54ee26ca86915ef9ffd42 With additional adjustments to handle the inability to pass environment variables to runProgram().
-rw-r--r--jstests/noPassthrough/shell_history.js126
-rw-r--r--src/mongo/shell/linenoise.cpp14
2 files changed, 138 insertions, 2 deletions
diff --git a/jstests/noPassthrough/shell_history.js b/jstests/noPassthrough/shell_history.js
new file mode 100644
index 00000000000..ac760201e62
--- /dev/null
+++ b/jstests/noPassthrough/shell_history.js
@@ -0,0 +1,126 @@
+// Test that when running the shell for the first time creates the ~/.dbshell file, and it has
+// appropriate permissions (where relevant).
+
+(function() {
+ "use strict";
+
+ // Use dataPath because it includes the trailing "/" or "\".
+ var tmpHome = MongoRunner.dataPath;
+ // Ensure it exists and is a dir (eg. if running without resmoke.py and /data/db doesn't exist).
+ mkdir(tmpHome);
+ removeFile(tmpHome + ".dbshell");
+
+ var args = [];
+ var cmdline = "mongo --nodb";
+ var redirection = "";
+ var env = {};
+ if (_isWindows()) {
+ args.push("cmd.exe");
+ args.push("/c");
+
+ // Input is set to NUL. The output must also be redirected to NUL, otherwise running the
+ // jstest manually has strange terminal IO behaviour.
+ redirection = "< NUL > NUL";
+
+ // USERPROFILE set to the tmp homedir.
+ // Since NUL is a character device, isatty() will return true, which means that .mongorc.js
+ // will be created in the HOMEDRIVE + HOMEPATH location, so we must set them also.
+ if (tmpHome.match("^[a-zA-Z]:")) {
+ var tmpHomeDrive = tmpHome.substr(0, 2);
+ var tmpHomePath = tmpHome.substr(2);
+ } else {
+ var _pwd = pwd();
+ assert(_pwd.match("^[a-zA-Z]:"), "pwd must include drive");
+ var tmpHomeDrive = _pwd.substr(0, 2);
+ var tmpHomePath = tmpHome;
+ }
+ env = {
+ USERPROFILE: tmpHome,
+ HOMEDRIVE: tmpHomeDrive,
+ HOMEPATH: tmpHomePath
+ };
+
+ } else {
+ args.push("sh");
+ args.push("-c");
+
+ // Use the mongo shell from the current dir, same as resmoke.py does.
+ // Doesn't handle resmoke's --mongo= option.
+ cmdline = "./" + cmdline;
+
+ // Set umask to 0 prior to running the shell.
+ cmdline = "umask 0 ; " + cmdline;
+
+ // stdin is /dev/null.
+ redirection = "< /dev/null";
+
+ // HOME set to the tmp homedir.
+ if (!tmpHome.startsWith("/")) {
+ tmpHome = pwd() + "/" + tmpHome;
+ }
+ env = {
+ HOME: tmpHome
+ };
+ }
+
+ // Workaround for SERVER-18877 not being fixed in earlier versions.
+ if (_isWindows()) {
+ for (var envvar in env) {
+ cmdline = "set " + envvar + "=" + env[envvar] + "&&" + cmdline;
+ }
+ } else {
+ for (var envvar in env) {
+ cmdline = envvar + "='" + env[envvar] + "' ; " + cmdline;
+ }
+ }
+
+ // Add redirection to cmdline, and add cmdline to args.
+ cmdline += " " + redirection;
+ args.push(cmdline);
+ jsTestLog("Running args:\n " + tojson(args) + "\nwith env:\n " + tojson(env));
+ var rc = runProgram.apply(this, args);
+
+ assert.eq(rc, 0);
+
+ var files = listFiles(tmpHome);
+ jsTestLog(tojson(files));
+
+ var findFile = function(baseName) {
+ for (var i = 0; i < files.length; i++) {
+ if (files[i].baseName === baseName) {
+ return files[i];
+ }
+ }
+ return undefined;
+ };
+
+ var targetFile = ".dbshell";
+ var file = findFile(targetFile);
+
+ assert.neq(typeof(file), "undefined", targetFile + " should exist, but it doesn't");
+ assert.eq(file.isDirectory, false, targetFile + " should not be a directory, but it is");
+ assert.eq(file.size, 0, targetFile + " should be empty, but it isn't");
+
+ if (!_isWindows()) {
+ // On Unix, check that the file has the correct mode (permissions).
+ // The shell has no way to stat a file.
+ // There is no stat utility in POSIX.
+ // `ls -l` is POSIX, so this is the best that we have.
+ // Check for exactly "-rw-------".
+ clearRawMongoProgramOutput();
+ var rc = runProgram("ls", "-l", file.name);
+ assert.eq(rc, 0);
+ // Before SERVER-22992 is fixed:
+ var output = null;
+ assert.soon(function() {
+ output = rawMongoProgramOutput();
+ return output != "";
+ });
+ // After SERVER-22992 is fixed:
+ // var output = rawMongoProgramOutput();
+ var fields = output.split(" ");
+ // First field is the prefix, second field is the `ls -l` permissions.
+ assert.eq(fields[1], "-rw-------", targetFile + " has bad permissions");
+ }
+
+})();
diff --git a/src/mongo/shell/linenoise.cpp b/src/mongo/shell/linenoise.cpp
index 8bd30c9f744..b4e87449b0a 100644
--- a/src/mongo/shell/linenoise.cpp
+++ b/src/mongo/shell/linenoise.cpp
@@ -2764,7 +2764,17 @@ int linenoiseHistorySetMaxLen(int len) {
/* Save the history in the specified file. On success 0 is returned
* otherwise -1 is returned. */
int linenoiseHistorySave(const char* filename) {
- FILE* fp = fopen(filename, "wt");
+ FILE* fp;
+#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE || defined(__APPLE__)
+ int fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
+ if (fd == -1) {
+ // report errno somehow?
+ return -1;
+ }
+ fp = fdopen(fd, "wt");
+#else
+ fp = fopen(filename, "wt");
+#endif // _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE || defined(__APPLE__)
if (fp == NULL) {
return -1;
}
@@ -2774,7 +2784,7 @@ int linenoiseHistorySave(const char* filename) {
fprintf(fp, "%s\n", history[j]);
}
}
- fclose(fp);
+ fclose(fp); // Also causes fd to be closed.
return 0;
}