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/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index cc81d718c3..32c568aee0 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -6.5.5 +6.5.6 diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml index 41bccc941e..05d04e38db 100644 --- a/indra/newview/app_settings/settings.xml +++ b/indra/newview/app_settings/settings.xml @@ -16826,5 +16826,16 @@ Value 1 + MFAHash + + Comment + Override MFA state hash for authentication + Persist + 0 + Type + String + Value + + diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index c898dd5179..716d52bfc5 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -3086,6 +3086,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 0f06889d20..21fa23dfa8 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -98,7 +98,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 +#include #include const S32 LOGIN_MAX_RETRIES = 0; // Viewer should not autmatically retry login @@ -224,8 +225,9 @@ void LLLoginInstance::constructAuthParams(LLPointer 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(request_params) << LL_ENDL; // Copy the credentials into the request after logging the rest @@ -238,6 +240,33 @@ void LLLoginInstance::constructAuthParams(LLPointer 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 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 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 createCredential(const std::string& grid, diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index cdf8a12acf..9a191b31bb 100644 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -133,6 +133,7 @@ #include "llproxy.h" #include "llproductinforequest.h" #include "llqueryflags.h" +#include "llsecapi.h" #include "llselectmgr.h" #include "llsky.h" #include "llstatview.h" @@ -1109,10 +1110,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")) { @@ -2349,8 +2350,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; } } @@ -3640,6 +3664,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 @@ + + + + token prompt + + +