summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorrahuldhodapkar <rahul.m.dhodapkar@gmail.com>2016-01-21 10:30:48 -0500
committerrahuldhodapkar <rahul.m.dhodapkar@gmail.com>2016-02-17 17:17:54 -0500
commit054d07ae6bcbc4eb408c1d7ba070b4dccdaa9cd3 (patch)
tree5a76c2ea69431d63072830f572558f2cbe272e01 /src
parentc666c99bef83a238f0853db3249f5a8543581b4f (diff)
downloadmongo-054d07ae6bcbc4eb408c1d7ba070b4dccdaa9cd3.tar.gz
SERVER-9131 prevent automatic function marshalling in shell
add shell flag --enableJavaScriptProtection and server setParameter javascriptProtection
Diffstat (limited to 'src')
-rw-r--r--src/mongo/scripting/SConscript1
-rw-r--r--src/mongo/scripting/engine.h3
-rw-r--r--src/mongo/scripting/mozjs/code.cpp96
-rw-r--r--src/mongo/scripting/mozjs/code.h55
-rw-r--r--src/mongo/scripting/mozjs/engine.cpp9
-rw-r--r--src/mongo/scripting/mozjs/engine.h3
-rw-r--r--src/mongo/scripting/mozjs/implscope.cpp6
-rw-r--r--src/mongo/scripting/mozjs/implscope.h10
-rw-r--r--src/mongo/scripting/mozjs/internedstring.defs2
-rw-r--r--src/mongo/scripting/mozjs/valuereader.cpp32
-rw-r--r--src/mongo/scripting/mozjs/valuewriter.cpp21
-rw-r--r--src/mongo/shell/db.js7
-rw-r--r--src/mongo/shell/dbshell.cpp1
-rw-r--r--src/mongo/shell/shell_options.cpp8
-rw-r--r--src/mongo/shell/shell_options.h1
15 files changed, 249 insertions, 6 deletions
diff --git a/src/mongo/scripting/SConscript b/src/mongo/scripting/SConscript
index 4f92296f1c7..c27e780985a 100644
--- a/src/mongo/scripting/SConscript
+++ b/src/mongo/scripting/SConscript
@@ -107,6 +107,7 @@ if usemozjs:
'mozjs/base.cpp',
'mozjs/bindata.cpp',
'mozjs/bson.cpp',
+ 'mozjs/code.cpp',
'mozjs/countdownlatch.cpp',
'mozjs/cursor.cpp',
'mozjs/cursor_handle.cpp',
diff --git a/src/mongo/scripting/engine.h b/src/mongo/scripting/engine.h
index dcceb9c07ff..f06333e9a45 100644
--- a/src/mongo/scripting/engine.h
+++ b/src/mongo/scripting/engine.h
@@ -248,6 +248,9 @@ public:
virtual void enableJIT(bool value) = 0;
virtual bool isJITEnabled() const = 0;
+ virtual void enableJavaScriptProtection(bool value) = 0;
+ virtual bool isJavaScriptProtectionEnabled() const = 0;
+
static void setup();
static void dropScopeCache();
diff --git a/src/mongo/scripting/mozjs/code.cpp b/src/mongo/scripting/mozjs/code.cpp
new file mode 100644
index 00000000000..701ed1d0375
--- /dev/null
+++ b/src/mongo/scripting/mozjs/code.cpp
@@ -0,0 +1,96 @@
+/**
+ * Copyright (C) 2015 MongoDB 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/platform/basic.h"
+
+#include "mongo/scripting/mozjs/code.h"
+
+#include "mongo/scripting/mozjs/implscope.h"
+#include "mongo/scripting/mozjs/objectwrapper.h"
+#include "mongo/scripting/mozjs/valuereader.h"
+#include "mongo/scripting/mozjs/valuewriter.h"
+#include "mongo/scripting/mozjs/wrapconstrainedmethod.h"
+#include "mongo/stdx/memory.h"
+#include "mongo/util/mongoutils/str.h"
+
+namespace mongo {
+namespace mozjs {
+
+const JSFunctionSpec CodeInfo::methods[2] = {
+ MONGO_ATTACH_JS_CONSTRAINED_METHOD(toString, CodeInfo),
+ JS_FS_END,
+};
+
+const char* const CodeInfo::className = "Code";
+
+void CodeInfo::Functions::toString::call(JSContext* cx, JS::CallArgs args) {
+ ObjectWrapper o(cx, args.thisv());
+
+ std::string str = str::stream()
+ << "Code({\"code\":\"" << o.getString(InternedString::code) << "\","
+ << "\"scope\":" << o.getObject(InternedString::scope) << "\"})";
+
+ ValueReader(cx, args.rval()).fromStringData(str);
+}
+
+void CodeInfo::construct(JSContext* cx, JS::CallArgs args) {
+ uassert(ErrorCodes::BadValue,
+ "Code needs 0, 1 or 2 arguments",
+ args.length() == 0 || args.length() == 1 || args.length() == 2);
+ auto scope = getScope(cx);
+
+ JS::RootedObject thisv(cx);
+
+ scope->getProto<CodeInfo>().newObject(&thisv);
+ ObjectWrapper o(cx, thisv);
+
+ if (args.length() == 0) {
+ o.setString(InternedString::code, "");
+ }
+ else if (args.length() == 1) {
+ JS::HandleValue codeArg = args.get(0);
+ if (!codeArg.isString())
+ uasserted(ErrorCodes::BadValue, "code must be a string");
+
+ o.setValue(InternedString::code, codeArg);
+ }
+ else {
+ if (!args.get(0).isString())
+ uasserted(ErrorCodes::BadValue, "code must be a string");
+ if (!args.get(1).isObject())
+ uasserted(ErrorCodes::BadValue, "scope must be an object");
+
+ o.setValue(InternedString::code, args.get(0));
+ o.setValue(InternedString::scope, args.get(1));
+ }
+
+ args.rval().setObjectOrNull(thisv);
+}
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/code.h b/src/mongo/scripting/mozjs/code.h
new file mode 100644
index 00000000000..a9d2e5523bb
--- /dev/null
+++ b/src/mongo/scripting/mozjs/code.h
@@ -0,0 +1,55 @@
+/**
+ * Copyright (C) 2015 MongoDB 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 "mongo/scripting/mozjs/wraptype.h"
+
+namespace mongo {
+namespace mozjs {
+
+/**
+ * The "Code" Javascript object.
+ *
+ * Holds a bson Code or CodeWScope
+ */
+
+struct CodeInfo : public BaseInfo {
+ static void construct(JSContext* cx, JS::CallArgs args);
+
+ struct Functions {
+ MONGO_DECLARE_JS_FUNCTION(toString);
+ };
+
+ static const JSFunctionSpec methods[2];
+
+ static const char* const className;
+};
+
+} // namespace mozjs
+} // namespace mongo
diff --git a/src/mongo/scripting/mozjs/engine.cpp b/src/mongo/scripting/mozjs/engine.cpp
index ac4691ddd4e..54c6c5555e6 100644
--- a/src/mongo/scripting/mozjs/engine.cpp
+++ b/src/mongo/scripting/mozjs/engine.cpp
@@ -47,6 +47,7 @@ namespace mongo {
namespace {
MONGO_EXPORT_SERVER_PARAMETER(disableJavaScriptJIT, bool, false);
+MONGO_EXPORT_SERVER_PARAMETER(javascriptProtection, bool, false);
} // namespace
@@ -126,6 +127,14 @@ bool MozJSScriptEngine::isJITEnabled() const {
return !disableJavaScriptJIT.load();
}
+void MozJSScriptEngine::enableJavaScriptProtection(bool value) {
+ javascriptProtection.store(value);
+}
+
+bool MozJSScriptEngine::isJavaScriptProtectionEnabled() const {
+ return javascriptProtection.load();
+}
+
void MozJSScriptEngine::registerOperation(OperationContext* txn, MozJSImplScope* scope) {
stdx::lock_guard<stdx::mutex> giLock(_globalInterruptLock);
diff --git a/src/mongo/scripting/mozjs/engine.h b/src/mongo/scripting/mozjs/engine.h
index 80d2ee8ec0f..9b3178912e7 100644
--- a/src/mongo/scripting/mozjs/engine.h
+++ b/src/mongo/scripting/mozjs/engine.h
@@ -66,6 +66,9 @@ public:
void enableJIT(bool value) override;
bool isJITEnabled() const override;
+ void enableJavaScriptProtection(bool value) override;
+ bool isJavaScriptProtectionEnabled() const override;
+
void registerOperation(OperationContext* ctx, MozJSImplScope* scope);
void unregisterOperation(unsigned int opId);
diff --git a/src/mongo/scripting/mozjs/implscope.cpp b/src/mongo/scripting/mozjs/implscope.cpp
index 52e3af75ca6..9c298b4e52c 100644
--- a/src/mongo/scripting/mozjs/implscope.cpp
+++ b/src/mongo/scripting/mozjs/implscope.cpp
@@ -169,6 +169,10 @@ OperationContext* MozJSImplScope::getOpContext() const {
return _opCtx;
}
+bool MozJSImplScope::isJavaScriptProtectionEnabled() const {
+ return _engine->isJavaScriptProtectionEnabled();
+}
+
bool MozJSImplScope::_interruptCallback(JSContext* cx) {
auto scope = getScope(cx);
@@ -300,6 +304,7 @@ MozJSImplScope::MozJSImplScope(MozJSScriptEngine* engine)
_hasOutOfMemoryException(false),
_binDataProto(_context),
_bsonProto(_context),
+ _codeProto(_context),
_countDownLatchProto(_context),
_cursorProto(_context),
_cursorHandleProto(_context),
@@ -738,6 +743,7 @@ void MozJSImplScope::reset() {
void MozJSImplScope::installBSONTypes() {
_binDataProto.install(_global);
_bsonProto.install(_global);
+ _codeProto.install(_global);
_dbPointerProto.install(_global);
_dbRefProto.install(_global);
_errorProto.install(_global);
diff --git a/src/mongo/scripting/mozjs/implscope.h b/src/mongo/scripting/mozjs/implscope.h
index de496546042..26fd75fb574 100644
--- a/src/mongo/scripting/mozjs/implscope.h
+++ b/src/mongo/scripting/mozjs/implscope.h
@@ -34,6 +34,7 @@
#include "mongo/client/dbclientcursor.h"
#include "mongo/scripting/mozjs/bindata.h"
#include "mongo/scripting/mozjs/bson.h"
+#include "mongo/scripting/mozjs/code.h"
#include "mongo/scripting/mozjs/countdownlatch.h"
#include "mongo/scripting/mozjs/cursor.h"
#include "mongo/scripting/mozjs/cursor_handle.h"
@@ -106,6 +107,8 @@ public:
void gc() override;
+ bool isJavaScriptProtectionEnabled() const;
+
double getNumber(const char* field) override;
int getNumberInt(const char* field) override;
long long getNumberLongLong(const char* field) override;
@@ -262,6 +265,12 @@ public:
}
template <typename T>
+ typename std::enable_if<std::is_same<T, CodeInfo>::value, WrapType<T>&>::type
+ getProto() {
+ return _codeProto;
+ }
+
+ template <typename T>
typename std::enable_if<std::is_same<T, ObjectInfo>::value, WrapType<T>&>::type getProto() {
return _objectProto;
}
@@ -369,6 +378,7 @@ private:
WrapType<BinDataInfo> _binDataProto;
WrapType<BSONInfo> _bsonProto;
+ WrapType<CodeInfo> _codeProto;
WrapType<CountDownLatchInfo> _countDownLatchProto;
WrapType<CursorInfo> _cursorProto;
WrapType<CursorHandleInfo> _cursorHandleProto;
diff --git a/src/mongo/scripting/mozjs/internedstring.defs b/src/mongo/scripting/mozjs/internedstring.defs
index 46bc16b2c54..40bf0b97498 100644
--- a/src/mongo/scripting/mozjs/internedstring.defs
+++ b/src/mongo/scripting/mozjs/internedstring.defs
@@ -8,6 +8,7 @@ MONGO_MOZJS_INTERNED_STRING(arrayAccess, "arrayAccess")
MONGO_MOZJS_INTERNED_STRING(_batchSize, "_batchSize")
MONGO_MOZJS_INTERNED_STRING(bottom, "bottom")
MONGO_MOZJS_INTERNED_STRING(_bson, "_bson")
+MONGO_MOZJS_INTERNED_STRING(code, "code")
MONGO_MOZJS_INTERNED_STRING(_collection, "_collection")
MONGO_MOZJS_INTERNED_STRING(constructor, "constructor")
MONGO_MOZJS_INTERNED_STRING(_cursor, "_cursor")
@@ -39,6 +40,7 @@ MONGO_MOZJS_INTERNED_STRING(_options, "_options")
MONGO_MOZJS_INTERNED_STRING(_query, "_query")
MONGO_MOZJS_INTERNED_STRING(readOnly, "readOnly")
MONGO_MOZJS_INTERNED_STRING(_ro, "_ro")
+MONGO_MOZJS_INTERNED_STRING(scope, "scope")
MONGO_MOZJS_INTERNED_STRING(_shortName, "_shortName")
MONGO_MOZJS_INTERNED_STRING(singleton, "singleton")
MONGO_MOZJS_INTERNED_STRING(_skip, "_skip")
diff --git a/src/mongo/scripting/mozjs/valuereader.cpp b/src/mongo/scripting/mozjs/valuereader.cpp
index c80efc017da..630d446a4d0 100644
--- a/src/mongo/scripting/mozjs/valuereader.cpp
+++ b/src/mongo/scripting/mozjs/valuereader.cpp
@@ -53,13 +53,35 @@ void ValueReader::fromBSONElement(const BSONElement& elem, const BSONObj& parent
switch (elem.type()) {
case mongo::Code:
- scope->newFunction(elem.valueStringData(), _value);
+ // javascriptProtection prevents Code and CodeWScope BSON types from
+ // being automatically marshalled into executable functions.
+ if (scope->isJavaScriptProtectionEnabled()) {
+ JS::AutoValueArray<1> args(_context);
+ ValueReader(_context, args[0]).fromStringData(elem.valueStringData());
+
+ JS::RootedObject obj(_context);
+ scope->getProto<CodeInfo>().newInstance(args, _value);
+ }
+ else {
+ scope->newFunction(elem.valueStringData(), _value);
+ }
return;
case mongo::CodeWScope:
- if (!elem.codeWScopeObject().isEmpty())
- warning() << "CodeWScope doesn't transfer to db.eval";
- scope->newFunction(StringData(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1),
- _value);
+ if (scope->isJavaScriptProtectionEnabled()) {
+ JS::AutoValueArray<2> args(_context);
+
+ ValueReader(_context, args[0]).fromStringData(elem.valueStringData());
+ ValueReader(_context, args[1]).fromBSON(elem.codeWScopeObject(),
+ nullptr, readOnly);
+
+ scope->getProto<CodeInfo>().newInstance(args, _value);
+ }
+ else {
+ if (!elem.codeWScopeObject().isEmpty())
+ warning() << "CodeWScope doesn't transfer to db.eval";
+ scope->newFunction(StringData(elem.codeWScopeCode(), elem.codeWScopeCodeLen() - 1),
+ _value);
+ }
return;
case mongo::Symbol:
case mongo::String:
diff --git a/src/mongo/scripting/mozjs/valuewriter.cpp b/src/mongo/scripting/mozjs/valuewriter.cpp
index 080e8317bbf..967a2947cd8 100644
--- a/src/mongo/scripting/mozjs/valuewriter.cpp
+++ b/src/mongo/scripting/mozjs/valuewriter.cpp
@@ -266,6 +266,27 @@ void ValueWriter::_writeObject(BSONObjBuilder* b,
return;
}
+ if (scope->getProto<CodeInfo>().getJSClass() == jsclass) {
+ if (o.hasOwnField(InternedString::scope) // CodeWScope
+ && o.type(InternedString::scope) == mongo::Object) {
+
+ if (o.type(InternedString::code) != mongo::String) {
+ uasserted(ErrorCodes::BadValue, "code must be a string");
+ }
+
+ b->appendCodeWScope(sd, o.getString(InternedString::code),
+ o.getObject(InternedString::scope));
+ }
+ else { // Code
+ if (o.type(InternedString::code) != mongo::String) {
+ uasserted(ErrorCodes::BadValue, "code must be a string");
+ }
+
+ b->appendCode(sd, o.getString(InternedString::code));
+ }
+ return;
+ }
+
if (scope->getProto<NumberDecimalInfo>().getJSClass() == jsclass) {
b->append(sd, NumberDecimalInfo::ToNumberDecimal(_context, obj));
diff --git a/src/mongo/shell/db.js b/src/mongo/shell/db.js
index e17cfa36858..91892ff55e3 100644
--- a/src/mongo/shell/db.js
+++ b/src/mongo/shell/db.js
@@ -1155,7 +1155,12 @@ DB.prototype.getQueryOptions = function() {
DB.prototype.loadServerScripts = function(){
var global = Function('return this')();
this.system.js.find().forEach(function(u) {
- global[u._id] = u.value;
+ if (u.value.constructor === Code) {
+ global[u._id] = eval("(" + u.value.code + ")");
+ }
+ else {
+ global[u._id] = u.value;
+ }
});
};
diff --git a/src/mongo/shell/dbshell.cpp b/src/mongo/shell/dbshell.cpp
index 22b9e22d91d..c064c292e83 100644
--- a/src/mongo/shell/dbshell.cpp
+++ b/src/mongo/shell/dbshell.cpp
@@ -683,6 +683,7 @@ int _main(int argc, char* argv[], char** envp) {
mongo::ScriptEngine::setup();
mongo::globalScriptEngine->setScopeInitCallback(mongo::shell_utils::initScope);
mongo::globalScriptEngine->enableJIT(!shellGlobalParams.nojit);
+ mongo::globalScriptEngine->enableJavaScriptProtection(shellGlobalParams.javascriptProtection);
auto poolGuard = MakeGuard([] { ScriptEngine::dropScopeCache(); });
diff --git a/src/mongo/shell/shell_options.cpp b/src/mongo/shell/shell_options.cpp
index 62ce19c12a3..5094f01276c 100644
--- a/src/mongo/shell/shell_options.cpp
+++ b/src/mongo/shell/shell_options.cpp
@@ -124,6 +124,11 @@ Status addMongoShellOptions(moe::OptionSection* options) {
moe::Switch,
"disable the Javascript Just In Time compiler");
+ options->addOptionChaining("enableJavaScriptProtection",
+ "enableJavaScriptProtection",
+ moe::Switch,
+ "disable automatic JavaScript function marshalling");
+
Status ret = Status::OK();
#ifdef MONGO_CONFIG_SSL
ret = addSSLClientOptions(options);
@@ -260,6 +265,9 @@ Status storeMongoShellOptions(const moe::Environment& params,
if (params.count("nodb")) {
shellGlobalParams.nodb = true;
}
+ if (params.count("enableJavaScriptProtection")) {
+ shellGlobalParams.javascriptProtection = true;
+ }
if (params.count("norc")) {
shellGlobalParams.norc = true;
}
diff --git a/src/mongo/shell/shell_options.h b/src/mongo/shell/shell_options.h
index 2e94ff92282..4699f3d09f4 100644
--- a/src/mongo/shell/shell_options.h
+++ b/src/mongo/shell/shell_options.h
@@ -62,6 +62,7 @@ struct ShellGlobalParams {
bool nodb;
bool norc;
bool nojit = false;
+ bool javascriptProtection = false;
std::string script;