summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kleshchev <andreykproductengine@lindenlab.com>2022-05-04 20:10:29 +0300
committerAndrey Kleshchev <andreykproductengine@lindenlab.com>2022-05-04 20:10:29 +0300
commitf3add82a4c7f34bfff285302c34d952902451cad (patch)
tree2e02d64853c341d42efa098055dc5e4b65b84d72
parent1a6eee4ea27923016fac790b47161ce00f2c9b4f (diff)
parentbc7e2fb7e7f7527b522bdf6f4c791f3b660485e2 (diff)
Merge branch master (D550) into DRTVWR-546
# Conflicts: # indra/newview/llappviewer.h
-rw-r--r--indra/cmake/Boost.cmake16
-rw-r--r--indra/newview/app_settings/settings.xml11
-rw-r--r--indra/newview/llappviewer.cpp5
-rw-r--r--indra/newview/llappviewer.h2
-rw-r--r--indra/newview/lllogininstance.cpp68
-rw-r--r--indra/newview/llsecapi.h3
-rw-r--r--indra/newview/llsechandler_basic.cpp5
-rw-r--r--indra/newview/llsechandler_basic.h3
-rw-r--r--indra/newview/llstartup.cpp45
-rw-r--r--indra/newview/skins/default/xui/en/floater_mfa.xml49
-rw-r--r--indra/newview/skins/default/xui/en/notifications.xml21
-rw-r--r--indra/newview/skins/default/xui/en/strings.xml4
-rw-r--r--indra/newview/tests/lllogininstance_test.cpp8
-rw-r--r--indra/newview/tests/llsecapi_test.cpp1
-rw-r--r--indra/viewer_components/login/lllogin.cpp33
15 files changed, 247 insertions, 27 deletions
diff --git a/indra/cmake/Boost.cmake b/indra/cmake/Boost.cmake
index 06a7ab6d75..26604d4913 100644
--- a/indra/cmake/Boost.cmake
+++ b/indra/cmake/Boost.cmake
@@ -103,28 +103,28 @@ else (USESYSTEMLIBS)
elseif (DARWIN)
set(BOOST_CONTEXT_LIBRARY
optimized boost_context-mt${addrsfx}
- debug boost_context-mt${addrsfx}-d)
+ debug boost_context-mt${addrsfx})
set(BOOST_FIBER_LIBRARY
optimized boost_fiber-mt${addrsfx}
- debug boost_fiber-mt${addrsfx}-d)
+ debug boost_fiber-mt${addrsfx})
set(BOOST_FILESYSTEM_LIBRARY
optimized boost_filesystem-mt${addrsfx}
- debug boost_filesystem-mt${addrsfx}-d)
+ debug boost_filesystem-mt${addrsfx})
set(BOOST_PROGRAM_OPTIONS_LIBRARY
optimized boost_program_options-mt${addrsfx}
- debug boost_program_options-mt${addrsfx}-d)
+ debug boost_program_options-mt${addrsfx})
set(BOOST_REGEX_LIBRARY
optimized boost_regex-mt${addrsfx}
- debug boost_regex-mt${addrsfx}-d)
+ debug boost_regex-mt${addrsfx})
set(BOOST_SIGNALS_LIBRARY
optimized boost_signals-mt${addrsfx}
- debug boost_signals-mt${addrsfx}-d)
+ debug boost_signals-mt${addrsfx})
set(BOOST_SYSTEM_LIBRARY
optimized boost_system-mt${addrsfx}
- debug boost_system-mt${addrsfx}-d)
+ debug boost_system-mt${addrsfx})
set(BOOST_THREAD_LIBRARY
optimized boost_thread-mt${addrsfx}
- debug boost_thread-mt${addrsfx}-d)
+ debug boost_thread-mt${addrsfx})
endif (WINDOWS)
endif (USESYSTEMLIBS)
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index 1a75456725..0000ae18bd 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -16829,5 +16829,16 @@
<key>Value</key>
<integer>0</integer>
</map>
+ <key>MFAHash</key>
+ <map>
+ <key>Comment</key>
+ <string>Override MFA state hash for authentication</string>
+ <key>Persist</key>
+ <integer>0</integer>
+ <key>Type</key>
+ <string>String</string>
+ <key>Value</key>
+ <string></string>
+ </map>
</map>
</llsd>
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 8255eb882c..3bcd4f9a49 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -3132,6 +3132,11 @@ bool LLAppViewer::initWindow()
return true;
}
+bool LLAppViewer::isUpdaterMissing()
+{
+ return mUpdaterNotFound;
+}
+
void LLAppViewer::writeDebugInfo(bool isStatic)
{
#if LL_WINDOWS && LL_BUGSPLAT
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index a86fa7d873..68c04d450b 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -105,7 +105,7 @@ public:
bool quitRequested() { return mQuitRequested; }
bool logoutRequestSent() { return mLogoutRequestSent; }
bool isSecondInstance() { return mSecondInstance; }
- bool isUpdaterMissing() { return mUpdaterNotFound; }
+ bool isUpdaterMissing(); // In use by tests
void writeDebugInfo(bool isStatic=true);
diff --git a/indra/newview/lllogininstance.cpp b/indra/newview/lllogininstance.cpp
index 531f0b172d..a3d0eb5796 100644
--- a/indra/newview/lllogininstance.cpp
+++ b/indra/newview/lllogininstance.cpp
@@ -61,6 +61,7 @@
#include "lltrans.h"
#include <boost/scoped_ptr.hpp>
+#include <boost/regex.hpp>
#include <sstream>
const S32 LOGIN_MAX_RETRIES = 0; // Viewer should not autmatically retry login
@@ -224,8 +225,9 @@ 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"] = "";
- // log request_params _before_ adding the credentials
+ // log request_params _before_ adding the credentials or sensitive MFA hash data
LL_DEBUGS("LLLogin") << "Login parameters: " << LLSDOStreamer<LLSDNotationFormatter>(request_params) << LL_ENDL;
// Copy the credentials into the request after logging the rest
@@ -238,6 +240,33 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia
request_params[it->first] = it->second;
}
+ std::string mfa_hash = gSavedSettings.getString("MFAHash"); //non-persistent to enable testing
+ std::string grid(LLGridManager::getInstance()->getGridId());
+ std::string user_id = user_credential->userID();
+ if (gSecAPIHandler)
+ {
+ if (mfa_hash.empty())
+ {
+ // normal execution, mfa_hash was not set from debug setting so load from protected store
+ LLSD data_map = gSecAPIHandler->getProtectedData("mfa_hash", grid);
+ if (data_map.isMap() && data_map.has(user_id))
+ {
+ mfa_hash = data_map[user_id].asString();
+ }
+ }
+ else
+ {
+ // SL-16888 the mfa_hash is being overridden for testing so save it for consistency for future login requests
+ gSecAPIHandler->addToProtectedMap("mfa_hash", grid, user_id, mfa_hash);
+ }
+ }
+ else
+ {
+ LL_WARNS() << "unable to access protected store for mfa_hash" << LL_ENDL;
+ }
+
+ request_params["mfa_hash"] = mfa_hash;
+
// Specify desired timeout/retry options
LLSD http_params;
F32 srv_timeout = llclamp(gSavedSettings.getF32("LoginSRVTimeout"), LOGIN_SRV_TIMEOUT_MIN, LOGIN_SRV_TIMEOUT_MAX);
@@ -250,6 +279,11 @@ void LLLoginInstance::constructAuthParams(LLPointer<LLCredential> user_credentia
mRequestData["params"] = request_params;
mRequestData["options"] = requested_options;
mRequestData["http_params"] = http_params;
+#if LL_RELEASE_FOR_DOWNLOAD
+ mRequestData["wait_for_updater"] = !gSavedSettings.getBOOL("CmdLineSkipUpdater") && !LLAppViewer::instance()->isUpdaterMissing();
+#else
+ mRequestData["wait_for_updater"] = false;
+#endif
}
bool LLLoginInstance::handleLoginEvent(const LLSD& event)
@@ -406,6 +440,38 @@ 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;
+
+ if (gViewerWindow)
+ {
+ gViewerWindow->setShowProgress(FALSE);
+ }
+
+ LLSD args(llsd::map( "MESSAGE", LLTrans::getString(response["message_id"]) ));
+ LLSD payload;
+ LLNotificationsUtil::add("PromptMFAToken", args, payload, [=](LLSD const & notif, LLSD const & response) {
+ bool continue_clicked = response["continue"].asBoolean();
+ std::string token = response["token"].asString();
+ LL_DEBUGS("LLLogin") << "PromptMFAToken: response: " << response << " continue_clicked" << continue_clicked << LL_ENDL;
+
+ // strip out whitespace - SL-17034/BUG-231938
+ token = boost::regex_replace(token, boost::regex("\\s"), "");
+
+ if (continue_clicked && !token.empty())
+ {
+ LL_INFOS("LLLogin") << "PromptMFAToken: token submitted" << LL_ENDL;
+
+ // Set the request data to true and retry login.
+ mRequestData["params"]["token"] = token;
+ reconnect();
+ } else {
+ LL_INFOS("LLLogin") << "PromptMFAToken: no token, attemptComplete" << LL_ENDL;
+ attemptComplete();
+ }
+ });
+ }
else if( reason_response == "key"
|| reason_response == "presence"
|| reason_response == "connect"
diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h
index e1320375ab..d8831fee93 100644
--- a/indra/newview/llsecapi.h
+++ b/indra/newview/llsecapi.h
@@ -485,6 +485,9 @@ public:
const std::string& data_id,
const std::string& map_elem)=0;
+ // ensure protected store's map is written to storage
+ virtual void syncProtectedMap() = 0;
+
public:
virtual LLPointer<LLCredential> createCredential(const std::string& grid,
const LLSD& identifier,
diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp
index 6b06abaf99..d0da3387ec 100644
--- a/indra/newview/llsechandler_basic.cpp
+++ b/indra/newview/llsechandler_basic.cpp
@@ -1608,6 +1608,11 @@ void LLSecAPIBasicHandler::removeFromProtectedMap(const std::string& data_type,
}
}
+void LLSecAPIBasicHandler::syncProtectedMap()
+{
+ // TODO - consider unifing these functions
+ _writeProtectedData();
+}
//
// Create a credential object from an identifier and authenticator. credentials are
// per grid.
diff --git a/indra/newview/llsechandler_basic.h b/indra/newview/llsechandler_basic.h
index 17e9f72f07..bd1a8f640c 100644
--- a/indra/newview/llsechandler_basic.h
+++ b/indra/newview/llsechandler_basic.h
@@ -278,6 +278,9 @@ public:
const std::string& data_id,
const std::string& map_elem);
+ // ensure protected store's map is written to storage
+ virtual void syncProtectedMap();
+
// credential management routines
virtual LLPointer<LLCredential> createCredential(const std::string& grid,
diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp
index 5df8cd9cb0..5c0bb36d31 100644
--- a/indra/newview/llstartup.cpp
+++ b/indra/newview/llstartup.cpp
@@ -134,6 +134,7 @@
#include "llproxy.h"
#include "llproductinforequest.h"
#include "llqueryflags.h"
+#include "llsecapi.h"
#include "llselectmgr.h"
#include "llsky.h"
#include "llstatview.h"
@@ -1116,10 +1117,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"))
{
@@ -2363,8 +2364,31 @@ void show_release_notes_if_required()
&& gSavedSettings.getBOOL("UpdaterShowReleaseNotes")
&& !gSavedSettings.getBOOL("FirstLoginThisInstall"))
{
- LLSD info(LLAppViewer::instance()->getViewerInfo());
- LLWeb::loadURLInternal(info["VIEWER_RELEASE_NOTES_URL"]);
+
+#if LL_RELEASE_FOR_DOWNLOAD
+ if (!gSavedSettings.getBOOL("CmdLineSkipUpdater")
+ && !LLAppViewer::instance()->isUpdaterMissing())
+ {
+ // Instantiate a "relnotes" listener which assumes any arriving event
+ // is the release notes URL string. Since "relnotes" is an
+ // LLEventMailDrop, this listener will be invoked whether or not the
+ // URL has already been posted. If so, it will fire immediately;
+ // otherwise it will fire whenever the URL is (later) posted. Either
+ // way, it will display the release notes as soon as the URL becomes
+ // available.
+ LLEventPumps::instance().obtain("relnotes").listen(
+ "showrelnotes",
+ [](const LLSD& url) {
+ LLWeb::loadURLInternal(url.asString());
+ return false;
+ });
+ }
+ else
+#endif // LL_RELEASE_FOR_DOWNLOAD
+ {
+ LLSD info(LLAppViewer::instance()->getViewerInfo());
+ LLWeb::loadURLInternal(info["VIEWER_RELEASE_NOTES_URL"]);
+ }
release_notes_shown = true;
}
}
@@ -3654,6 +3678,17 @@ bool process_login_success_response()
LLViewerMedia::getInstance()->openIDSetup(openid_url, openid_token);
}
+
+ // Only save mfa_hash for future logins if the user wants their info remembered.
+ if(response.has("mfa_hash") && gSavedSettings.getBOOL("RememberUser") && gSavedSettings.getBOOL("RememberPassword"))
+ {
+ std::string grid(LLGridManager::getInstance()->getGridId());
+ std::string user_id(gUserCredential->userID());
+ gSecAPIHandler->addToProtectedMap("mfa_hash", grid, user_id, response["mfa_hash"]);
+ // TODO(brad) - related to SL-17223 consider building a better interface that sync's automatically
+ gSecAPIHandler->syncProtectedMap();
+ }
+
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/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/skins/default/xui/en/notifications.xml b/indra/newview/skins/default/xui/en/notifications.xml
index 3add211247..aa93601669 100644
--- a/indra/newview/skins/default/xui/en/notifications.xml
+++ b/indra/newview/skins/default/xui/en/notifications.xml
@@ -11798,4 +11798,25 @@ Unpacking: [UNPACK_TIME]s [USIZE]KB
<tag>fail</tag>
</notification>
+ <notification
+ icon="alertmodal.tga"
+ label="Prompt for MFA Token"
+ name="PromptMFAToken"
+ type="alertmodal">
+ [MESSAGE]
+ <tag>confirm</tag>
+ <form name="form">
+ <input name="token" type="text" width="400" />
+ <button
+ default="true"
+ index="0"
+ name="continue"
+ text="Continue"/>
+ <button
+ index="1"
+ name="cancel"
+ text="Cancel"/>
+ </form>
+ </notification>
+
</notifications>
diff --git a/indra/newview/skins/default/xui/en/strings.xml b/indra/newview/skins/default/xui/en/strings.xml
index f26ee06e6b..866196a760 100644
--- a/indra/newview/skins/default/xui/en/strings.xml
+++ b/indra/newview/skins/default/xui/en/strings.xml
@@ -133,6 +133,7 @@ http://secondlife.com/viewer-access-faq</string>
Please check to make sure you entered the right
* Username (like bobsmith12 or steller.sunshine)
* Password
+ * Second Factor Token (if enabled)
Also, please make sure your Caps Lock key is off.</string>
<string name="LoginFailedPasswordChanged">As a security precaution your password has been changed.
Please go to your account page at http://secondlife.com/password
@@ -192,7 +193,8 @@ Please try logging in again in a minute.</string>
Please try logging in again in a minute.</string>
<string name="LoginFailedLoggingOutSession">The system has begun logging out your last session.
Please try logging in again in a minute.</string>
-
+ <string name="LoginFailedAuthenticationMFARequired">To continue logging in, enter a new token from your multifactor authentication app.
+If you feel this is an error, please contact support@secondlife.com</string>
<!-- Disconnection -->
<string name="AgentLostConnection">This region may be experiencing trouble. Please check your connection to the Internet.</string>
diff --git a/indra/newview/tests/lllogininstance_test.cpp b/indra/newview/tests/lllogininstance_test.cpp
index 8d1956957c..a52c3dcef9 100644
--- a/indra/newview/tests/lllogininstance_test.cpp
+++ b/indra/newview/tests/lllogininstance_test.cpp
@@ -186,6 +186,10 @@ std::string LLGridManager::getAppSLURLBase(const std::string& grid_name)
{
return "myappslurl";
}
+std::string LLGridManager::getGridId(const std::string& grid)
+{
+ return std::string();
+}
//-----------------------------------------------------------------------------
#include "../llviewercontrol.h"
@@ -218,6 +222,7 @@ bool llHashedUniqueID(unsigned char* id)
//-----------------------------------------------------------------------------
#include "../llappviewer.h"
void LLAppViewer::forceQuit(void) {}
+bool LLAppViewer::isUpdaterMissing() { return true; }
LLAppViewer * LLAppViewer::sInstance = 0;
//-----------------------------------------------------------------------------
@@ -226,6 +231,8 @@ LLAppViewer * LLAppViewer::sInstance = 0;
static std::string gTOSType;
static LLEventPump * gTOSReplyPump = NULL;
+LLPointer<LLSecAPIHandler> gSecAPIHandler;
+
//static
LLFloater* LLFloaterReg::showInstance(const std::string& name, const LLSD& key, BOOL focus)
{
@@ -343,6 +350,7 @@ namespace tut
gSavedSettings.declareString("ClientSettingsFile", "test_settings.xml", "", LLControlVariable::PERSIST_NO);
gSavedSettings.declareString("NextLoginLocation", "", "", LLControlVariable::PERSIST_NO);
gSavedSettings.declareBOOL("LoginLastLocation", FALSE, "", LLControlVariable::PERSIST_NO);
+ gSavedSettings.declareBOOL("CmdLineSkipUpdater", TRUE, "", LLControlVariable::PERSIST_NO);
LLSD authenticator = LLSD::emptyMap();
LLSD identifier = LLSD::emptyMap();
diff --git a/indra/newview/tests/llsecapi_test.cpp b/indra/newview/tests/llsecapi_test.cpp
index 37fbbb449b..7d2a9a436f 100644
--- a/indra/newview/tests/llsecapi_test.cpp
+++ b/indra/newview/tests/llsecapi_test.cpp
@@ -62,6 +62,7 @@ LLPointer<LLCertificateStore> LLSecAPIBasicHandler::getCertificateStore(const st
void LLSecAPIBasicHandler::setProtectedData(const std::string& data_type, const std::string& data_id, const LLSD& data) {}
void LLSecAPIBasicHandler::addToProtectedMap(const std::string& data_type, const std::string& data_id, const std::string& map_elem, const LLSD& data) {}
void LLSecAPIBasicHandler::removeFromProtectedMap(const std::string& data_type, const std::string& data_id, const std::string& map_elem) {}
+void LLSecAPIBasicHandler::syncProtectedMap() {}
LLSD LLSecAPIBasicHandler::getProtectedData(const std::string& data_type, const std::string& data_id) { return LLSD(); }
void LLSecAPIBasicHandler::deleteProtectedData(const std::string& data_type, const std::string& data_id) {}
LLPointer<LLCredential> LLSecAPIBasicHandler::createCredential(const std::string& grid, const LLSD& identifier, const LLSD& authenticator) { return NULL; }
diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp
index 168880dc12..8a7e6407ed 100644
--- a/indra/viewer_components/login/lllogin.cpp
+++ b/indra/viewer_components/login/lllogin.cpp
@@ -251,20 +251,31 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
// Since sSyncPoint is an LLEventMailDrop, we DEFINITELY want to
// consume the posted event.
LLCoros::OverrideConsuming oc(true);
- // Timeout should produce the isUndefined() object passed here.
- LL_DEBUGS("LLLogin") << "Login failure, waiting for sync from updater" << LL_ENDL;
- LLSD updater = llcoro::suspendUntilEventOnWithTimeout(sSyncPoint, 10, LLSD());
- if (updater.isUndefined())
- {
- LL_WARNS("LLLogin") << "Failed to hear from updater, proceeding with fail.login"
- << LL_ENDL;
- }
- else
+ LLSD responses(mAuthResponse["responses"]);
+ LLSD updater;
+
+ if (printable_params["wait_for_updater"].asBoolean())
{
- LL_DEBUGS("LLLogin") << "Got responses from updater and login.cgi" << LL_ENDL;
+ std::string reason_response = responses["data"]["reason"].asString();
+ if (reason_response == "update") // No point waiting if not an update
+ {
+ // Timeout should produce the isUndefined() object passed here.
+ LL_INFOS("LLLogin") << "Login failure, waiting for sync from updater" << LL_ENDL;
+ updater = llcoro::suspendUntilEventOnWithTimeout(sSyncPoint, 10, LLSD());
+
+ if (updater.isUndefined())
+ {
+ LL_WARNS("LLLogin") << "Failed to hear from updater, proceeding with fail.login"
+ << LL_ENDL;
+ }
+ else
+ {
+ LL_DEBUGS("LLLogin") << "Got responses from updater and login.cgi" << LL_ENDL;
+ }
+ }
}
+
// Let the fail.login handler deal with empty updater response.
- LLSD responses(mAuthResponse["responses"]);
responses["updater"] = updater;
sendProgressEvent("offline", "fail.login", responses);
}