summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/newview/CMakeLists.txt2
-rw-r--r--indra/newview/app_settings/settings_per_account.xml11
-rw-r--r--indra/newview/llfloatermfa.cpp101
-rw-r--r--indra/newview/llfloatermfa.h50
-rw-r--r--indra/newview/lllogininstance.cpp43
-rw-r--r--indra/newview/lllogininstance.h1
-rw-r--r--indra/newview/llstartup.cpp11
-rw-r--r--indra/newview/llviewerfloaterreg.cpp2
-rw-r--r--indra/newview/skins/default/xui/en/floater_mfa.xml49
-rw-r--r--indra/newview/tests/lllogininstance_test.cpp1
10 files changed, 268 insertions, 3 deletions
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 5a06106de3..6c976e3f2d 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -275,6 +275,7 @@ set(viewer_SOURCE_FILES
llfloatermap.cpp
llfloatermediasettings.cpp
llfloatermemleak.cpp
+ llfloatermfa.cpp
llfloatermodelpreview.cpp
llfloatermodeluploadbase.cpp
llfloatermyscripts.cpp
@@ -918,6 +919,7 @@ set(viewer_HEADER_FILES
llfloatermarketplacelistings.h
llfloatermediasettings.h
llfloatermemleak.h
+ llfloatermfa.h
llfloatermodelpreview.h
llfloatermodeluploadbase.h
llfloatermyscripts.h
diff --git a/indra/newview/app_settings/settings_per_account.xml b/indra/newview/app_settings/settings_per_account.xml
index 537744b44c..d7d008689c 100644
--- a/indra/newview/app_settings/settings_per_account.xml
+++ b/indra/newview/app_settings/settings_per_account.xml
@@ -436,5 +436,16 @@
<key>Value</key>
<integer>2</integer>
</map>
+ <key>SLMFAHash</key>
+ <map>
+ <key>Comment</key>
+ <string>MFA state hash for authentication</string>
+ <key>Persist</key>
+ <integer>1</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string></string>
+ </map>
</map>
</llsd>
diff --git a/indra/newview/llfloatermfa.cpp b/indra/newview/llfloatermfa.cpp
new file mode 100644
index 0000000000..7710817c23
--- /dev/null
+++ b/indra/newview/llfloatermfa.cpp
@@ -0,0 +1,101 @@
+/**
+ * @file llfloatermfa.cpp
+ * @brief Multi-Factor Auth token submission dialog
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2021, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#include "llviewerprecompiledheaders.h"
+
+#include "llfloatermfa.h"
+
+// viewer includes
+#include "llevents.h"
+
+
+LLFloaterMFA::LLFloaterMFA(const LLSD& data)
+: LLModalDialog("mfa_challenge"),
+ mMessage(data["message"].asStringRef()),
+ mReplyPumpName(data["reply_pump"].asStringRef())
+{
+
+}
+
+LLFloaterMFA::~LLFloaterMFA()
+{
+}
+
+BOOL LLFloaterMFA::postBuild()
+{
+ centerOnScreen();
+
+ childSetAction("continue_btn", onContinue, this);
+ childSetAction("cancel_btn", onCancel, this);
+ childSetCommitCallback("token_edit", [](LLUICtrl*, void* userdata) { onContinue(userdata);}, this);
+
+ // this displays the prompt message
+ LLUICtrl *token_prompt = getChild<LLUICtrl>("token_prompt_text");
+ token_prompt->setEnabled( FALSE );
+ token_prompt->setValue(LLSD(mMessage));
+
+ LLUICtrl *token_edit = getChild<LLUICtrl>("token_edit");
+ token_edit->setFocus(TRUE);
+
+ return TRUE;
+}
+
+// static
+void LLFloaterMFA::onContinue(void* userdata )
+{
+ LLFloaterMFA* self = static_cast<LLFloaterMFA*>(userdata);
+
+ LLUICtrl *token_ctrl = self->getChild<LLUICtrl>("token_edit");
+
+ std::string token(token_ctrl->getValue().asStringRef());
+
+ if (!token.empty())
+ {
+ LL_INFOS("MFA") << "User submits MFA token for challenge." << LL_ENDL;
+ if(self->mReplyPumpName != "")
+ {
+ LLEventPumps::instance().obtain(self->mReplyPumpName).post(LLSD(token));
+ }
+
+ self->closeFloater(); // destroys this object
+ }
+}
+
+// static
+void LLFloaterMFA::onCancel(void* userdata)
+{
+ LLFloaterMFA* self = static_cast<LLFloaterMFA*>(userdata);
+ LL_INFOS("MFA") << "User cancels MFA challenge attempt." << LL_ENDL;
+
+ if(self->mReplyPumpName != "")
+ {
+ LL_DEBUGS("MFA") << self->mReplyPumpName << LL_ENDL;
+ LLEventPumps::instance().obtain(self->mReplyPumpName).post(LLSD());
+ }
+
+ // destroys this object
+ self->closeFloater();
+}
diff --git a/indra/newview/llfloatermfa.h b/indra/newview/llfloatermfa.h
new file mode 100644
index 0000000000..9332b35877
--- /dev/null
+++ b/indra/newview/llfloatermfa.h
@@ -0,0 +1,50 @@
+/**
+ * @file llfloatermfa.h
+ * @brief Multi-Factor Auth token submission dialog
+ *
+ * $LicenseInfo:firstyear=2021&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2021, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#ifndef LL_LLFLOATERMFA_H
+#define LL_LLFLOATERMFA_H
+
+#include "llmodaldialog.h"
+
+
+class LLFloaterMFA :
+ public LLModalDialog
+{
+public:
+ LLFloaterMFA(const LLSD& data);
+ virtual ~LLFloaterMFA();
+
+ BOOL postBuild();
+
+ static void onContinue(void* userdata);
+ static void onCancel(void* userdata);
+
+private:
+ std::string mMessage;
+ std::string mReplyPumpName;
+};
+
+#endif // LL_FLOATERMFA_H
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index e81d2cc082..902510c294 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -75,6 +75,8 @@ public:
static const char * const TOS_REPLY_PUMP = "lllogininstance_tos_callback";
static const char * const TOS_LISTENER_NAME = "lllogininstance_tos";
+static const char * const MFA_REPLY_PUMP = "lllogininstance_mfa_callback";
+static const char * const MFA_LISTENER_NAME = "lllogininstance_mfa";
std::string construct_start_string();
@@ -225,6 +227,8 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia
request_params["id0"] = mSerialNumber;
request_params["host_id"] = gSavedSettings.getString("HostID");
request_params["extended_errors"] = true; // request message_id and message_args
+ request_params["token"] = "";
+ request_params["slmfa_hash"] = gSavedPerAccountSettings.getString("SLMFAHash");
// log request_params _before_ adding the credentials
LL_DEBUGS("LLLogin") << "Login parameters: " << LLSDOStreamer<LLSDNotationFormatter>(request_params) << LL_ENDL;
@@ -407,6 +411,23 @@ void LLLoginInstance::handleLoginFailure(const LLSD& event)
boost::bind(&LLLoginInstance::syncWithUpdater, this, resp, _1, _2));
}
}
+ else if(reason_response == "mfa_challenge")
+ {
+ LL_DEBUGS("LLLogin") << " MFA challenge" << LL_ENDL;
+
+ LLSD data(LLSD::emptyMap());
+ data["message"] = message_response;
+ data["reply_pump"] = MFA_REPLY_PUMP;
+ if (gViewerWindow)
+ {
+ gViewerWindow->setShowProgress(FALSE);
+ }
+ LLFloaterReg::showInstance("message_mfa", data);
+ LLEventPumps::instance().obtain(MFA_REPLY_PUMP)
+ .listen(MFA_LISTENER_NAME, [=](const LLSD& token) {
+ return this->handleMFAResponse(token, "token");
+ });
+ }
else if( reason_response == "key"
|| reason_response == "presence"
|| reason_response == "connect"
@@ -501,6 +522,28 @@ bool LLLoginInstance::handleTOSResponse(bool accepted, const std::string& key)
return true;
}
+bool LLLoginInstance::handleMFAResponse(const std::string& token, const std::string& key)
+{
+ LLEventPumps::instance().obtain(MFA_REPLY_PUMP).stopListening(MFA_LISTENER_NAME);
+
+ if(!token.empty())
+ {
+ LL_INFOS("LLLogin") << "LLLoginInstance::handleMFAResponse: token submitted" << LL_ENDL;
+
+ // Set the request data to true and retry login.
+ mRequestData["params"][key] = token;
+ reconnect();
+ }
+ else
+ {
+ LL_INFOS("LLLogin") << "LLLoginInstance::handleMFAResponse: no token, attemptComplete" << LL_ENDL;
+
+ attemptComplete();
+ }
+
+ return true;
+}
+
std::string construct_start_string()
{
std::string start;
diff --git a/indra/newview/lllogininstance.h b/indra/newview/lllogininstance.h
index b759b43474..ce64e9e3be 100644
--- a/indra/newview/lllogininstance.h
+++ b/indra/newview/lllogininstance.h
@@ -84,6 +84,7 @@ private:
void syncWithUpdater(ResponsePtr resp, const LLSD& notification, const LLSD& response);
bool handleTOSResponse(bool v, const std::string& key);
+ bool handleMFAResponse(const std::string& v, const std::string& key);
void attemptComplete() { mAttemptComplete = true; } // In the future an event?
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 54f3e6305c..8e81843153 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -1109,10 +1109,10 @@ bool idle_startup()
}
else
{
- if (reason_response != "tos")
+ if (reason_response != "tos" && reason_response != "mfa_challenge")
{
- // Don't pop up a notification in the TOS case because
- // LLFloaterTOS::onCancel() already scolded the user.
+ // Don't pop up a notification in the TOS or MFA cases because
+ // the specialized floater has already scolded the user.
std::string error_code;
if(response.has("errorcode"))
{
@@ -3597,6 +3597,11 @@ bool process_login_success_response()
LLViewerMedia::getInstance()->openIDSetup(openid_url, openid_token);
}
+ if(response.has("slmfa_hash"))
+ {
+ gSavedPerAccountSettings.setString("SLMFAHash", response["slmfa_hash"]);
+ }
+
bool success = false;
// JC: gesture loading done below, when we have an asset system
// in place. Don't delete/clear gUserCredentials until then.
diff --git a/indra/newview/llviewerfloaterreg.cpp b/indra/newview/llviewerfloaterreg.cpp
index 62d73063aa..677b55e49f 100644
--- a/indra/newview/llviewerfloaterreg.cpp
+++ b/indra/newview/llviewerfloaterreg.cpp
@@ -92,6 +92,7 @@
#include "llfloatermarketplacelistings.h"
#include "llfloatermediasettings.h"
#include "llfloatermemleak.h"
+#include "llfloatermfa.h"
#include "llfloatermodelpreview.h"
#include "llfloatermyscripts.h"
#include "llfloatermyenvironment.h"
@@ -288,6 +289,7 @@ void LLViewerFloaterReg::registerFloaters()
LLFloaterReg::add("marketplace_validation", "floater_marketplace_validation.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMarketplaceValidation>);
LLFloaterReg::add("message_critical", "floater_critical.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
LLFloaterReg::add("message_tos", "floater_tos.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterTOS>);
+ LLFloaterReg::add("message_mfa", "floater_mfa.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMFA>);
LLFloaterReg::add("moveview", "floater_moveview.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMove>);
LLFloaterReg::add("mute_object_by_name", "floater_mute_object.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterGetBlockedObjectName>);
LLFloaterReg::add("mini_map", "floater_map.xml", (LLFloaterBuildFunc)&LLFloaterReg::build<LLFloaterMap>);
diff --git a/indra/newview/skins/default/xui/en/floater_mfa.xml b/indra/newview/skins/default/xui/en/floater_mfa.xml
new file mode 100644
index 0000000000..a649cc6d47
--- /dev/null
+++ b/indra/newview/skins/default/xui/en/floater_mfa.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
+<floater
+ title="MFA Token Requred"
+ legacy_header_height="18"
+ can_minimize="false"
+ can_close="false"
+ height="110"
+ layout="topleft"
+ name="mfa_challenge"
+ help_topic="mfa_challenge"
+ width="550">
+ <text
+ type="string"
+ word_wrap="true"
+ length="1"
+ follows="top|left"
+ height="15"
+ layout="topleft"
+ left="10"
+ name="token_prompt_text"
+ top="20">
+ token prompt
+ </text>
+ <line_editor
+ follows="left|top|right"
+ height="19"
+ layout="topleft"
+ bottom_delta="40"
+ name="token_edit"
+ width="100" />
+ <button
+ follows="top|left"
+ height="20"
+ label="Continue"
+ layout="topleft"
+ left="10"
+ name="continue_btn"
+ bottom_delta="30"
+ width="64" />
+ <button
+ follows="top|left"
+ height="20"
+ label="Cancel"
+ layout="topleft"
+ left_pad="5"
+ name="cancel_btn"
+ bottom_delta="0"
+ width="64" />
+</floater>
diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp
index 8d1956957c..43f0e89222 100644
--- a/indra/newview/tests/lllogininstance_test.cpp
+++ b/indra/newview/tests/lllogininstance_test.cpp
@@ -190,6 +190,7 @@ std::string LLGridManager::getAppSLURLBase(const std::string& grid_name)
//-----------------------------------------------------------------------------
#include "../llviewercontrol.h"
LLControlGroup gSavedSettings("Global");
+LLControlGroup gSavedPerAccountSettings("PerAccount");
LLControlGroup::LLControlGroup(const std::string& name) :
LLInstanceTracker<LLControlGroup, std::string>(name){}