Files
SingularityViewer/indra/newview/lluserauth.cpp
Aleric Inglewood 7b9f854c66 Fix for 'with active responder' llerrs crash.
This fixes at least one case (crash report 8407), which comes
down to not cleanly informing a responder of failure when the
request url is empty (or so badly formed that it isn't a valid
url). As a result, the statemachine would abort() without
informing the responder - which is bad, sort of.

The previous cases where the responder needed to be informed
of a failure, namely "statemachine timed_out()" and "bad_socket()"
when a socket suddenly becomes bad for unknown reason, have been
replaced with the more general 'aborted()' function, which must
be called before the statemachine calls abort(). Clearly this
has been done for all cases of abort() now, so that if the
llerrs fires again in the future then that would have to be
after the statemachine calls finish(), which is still as "impossible"
as it was - hence the llerrs is still there to make sure.

The reason that this seldom happened on SL, and more often on
opensim, even more often on home-brew test grids, seems plausible:
malformed urls happen more in those cases.

I also took the opportunity to improve the robustness of cases
where the curl error code is checked: it makes no sense to check
what curl gives as error code when an internal error occurred.
2013-11-18 18:16:26 +01:00

444 lines
14 KiB
C++

/**
* @file lluserauth.cpp
* @brief LLUserAuth class implementation
*
* $LicenseInfo:firstyear=2003&license=viewergpl$
*
* Copyright (c) 2003-2009, Linden Research, Inc.
*
* Second Life Viewer Source Code
* The source code in this file ("Source Code") is provided by Linden Lab
* to you under the terms of the GNU General Public License, version 2.0
* ("GPL"), unless you have obtained a separate licensing agreement
* ("Other License"), formally executed by you and Linden Lab. Terms of
* the GPL can be found in doc/GPL-license.txt in this distribution, or
* online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
*
* There are special exceptions to the terms and conditions of the GPL as
* it is applied to this Source Code. View the full text of the exception
* in the file doc/FLOSS-exception.txt in this software distribution, or
* online at
* http://secondlifegrid.net/programs/open_source/licensing/flossexception
*
* By copying, modifying or distributing this software, you acknowledge
* that you have read and understood your obligations described above,
* and agree to abide by those obligations.
*
* ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
* WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
* COMPLETENESS OR PERFORMANCE.
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "lluserauth.h"
#include <sstream>
#include <iterator>
#include "lldir.h"
#include "sgversion.h"
#include "llappviewer.h"
#include "llviewerbuild.h"
#include "llviewercontrol.h"
#include "llxmlrpcresponder.h"
#include "llsdutil.h"
#include "llhttpclient.h"
#include "stringize.h"
// NOTE: MUST include these after otherincludes since queue gets redefined!?!!
#include <xmlrpc-epi/xmlrpc.h>
#include <curl/curl.h>
#ifdef DEBUG_CURLIO
#include "debug_libcurl.h"
#endif
// Don't define PLATFORM_STRING for unknown platforms - they need
// to get added to the login cgi script, so we want this to cause an
// error if we get compiled for a different platform.
// *FIX: This is misreporting on linux. Change this so that linux is
// in fact reporting linux.
#if LL_WINDOWS || LL_LINUX
static const char* PLATFORM_STRING = "Win";
#elif LL_DARWIN
static const char* PLATFORM_STRING = "Mac";
#elif LL_LINUX
static const char* PLATFORM_STRING = "Lnx";
#elif LL_SOLARIS
static const char* PLATFORM_STRING = "Sol";
#else
#error("Unknown platform defined!")
#endif
LLUserAuth::LLUserAuth() :
mLastTransferRateBPS(0),
mResult(LLSD())
{
mAuthResponse = E_NO_RESPONSE_YET;
}
LLUserAuth::~LLUserAuth()
{
reset();
}
void LLUserAuth::reset()
{
mResponder = NULL;
mResponses.clear();
mResult.clear();
}
void LLUserAuth::authenticate(
const std::string& auth_uri,
const std::string& method,
const std::string& firstname,
const std::string& lastname,
LLUUID web_login_key,
const std::string& start,
BOOL skip_optional,
BOOL accept_tos,
BOOL accept_critical_message,
BOOL last_exec_froze,
const std::vector<const char*>& requested_options,
const std::string& hashed_mac,
const std::string& hashed_volume_serial)
{
LL_INFOS2("AppInit", "Authentication") << "Authenticating: " << firstname << " " << lastname << ", "
<< /*dpasswd.c_str() <<*/ LL_ENDL;
std::ostringstream option_str;
option_str << "Options: ";
std::ostream_iterator<const char*> appender(option_str, ", ");
std::copy(requested_options.begin(), requested_options.end(), appender);
option_str << "END";
LL_INFOS2("AppInit", "Authentication") << option_str.str() << LL_ENDL;
mAuthResponse = E_NO_RESPONSE_YET;
//mDownloadTimer.reset();
// create the request
XMLRPC_REQUEST request = XMLRPC_RequestNew();
XMLRPC_RequestSetMethodName(request, method.c_str());
XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
// stuff the parameters
XMLRPC_VALUE params = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
XMLRPC_VectorAppendString(params, "first", firstname.c_str(), 0);
XMLRPC_VectorAppendString(params, "last", lastname.c_str(), 0);
XMLRPC_VectorAppendString(params, "web_login_key", web_login_key.getString().c_str(), 0);
XMLRPC_VectorAppendString(params, "start", start.c_str(), 0);
XMLRPC_VectorAppendString(params, "version", gCurrentVersion.c_str(), 0); // Includes channel name
XMLRPC_VectorAppendString(params, "channel", gVersionChannel, 0);
XMLRPC_VectorAppendString(params, "platform", PLATFORM_STRING, 0);
XMLRPC_VectorAppendString(params, "mac", hashed_mac.c_str(), 0);
// A bit of security through obscurity: id0 is volume_serial
XMLRPC_VectorAppendString(params, "id0", hashed_volume_serial.c_str(), 0);
if (skip_optional)
{
XMLRPC_VectorAppendString(params, "skipoptional", "true", 0);
}
if (accept_tos)
{
XMLRPC_VectorAppendString(params, "agree_to_tos", "true", 0);
}
if (accept_critical_message)
{
XMLRPC_VectorAppendString(params, "read_critical", "true", 0);
}
XMLRPC_VectorAppendInt(params, "last_exec_event", (int) last_exec_froze);
// append optional requests in an array
XMLRPC_VALUE options = XMLRPC_CreateVector("options", xmlrpc_vector_array);
std::vector<const char*>::const_iterator it = requested_options.begin();
std::vector<const char*>::const_iterator end = requested_options.end();
for( ; it < end; ++it)
{
XMLRPC_VectorAppendString(options, NULL, (*it), 0);
}
XMLRPC_AddValueToVector(params, options);
// put the parameters on the request
XMLRPC_RequestSetData(request, params);
mResponder = new XMLRPCResponder;
LLHTTPClient::postXMLRPC(auth_uri, request, mResponder);
LL_INFOS2("AppInit", "Authentication") << "LLUserAuth::authenticate: uri=" << auth_uri << LL_ENDL;
}
// Legacy version of constructor
// passwd is already MD5 hashed by the time we get to it.
void LLUserAuth::authenticate(
const std::string& auth_uri,
const std::string& method,
const std::string& firstname,
const std::string& lastname,
const std::string& passwd,
const std::string& start,
BOOL skip_optional,
BOOL accept_tos,
BOOL accept_critical_message,
BOOL last_exec_froze,
const std::vector<const char*>& requested_options,
const std::string& hashed_mac,
const std::string& hashed_volume_serial)
{
std::string dpasswd("$1$");
dpasswd.append(passwd);
LL_INFOS2("AppInit", "Authentication") << "Authenticating: " << firstname << " " << lastname << ", "
<< /*dpasswd.c_str() <<*/ LL_ENDL;
std::ostringstream option_str;
option_str << "Options: ";
std::ostream_iterator<const char*> appender(option_str, ", ");
std::copy(requested_options.begin(), requested_options.end(), appender);
option_str << "END";
LL_INFOS2("AppInit", "Authentication") << option_str.str().c_str() << LL_ENDL;
mAuthResponse = E_NO_RESPONSE_YET;
//mDownloadTimer.reset();
// create the request
XMLRPC_REQUEST request = XMLRPC_RequestNew();
XMLRPC_RequestSetMethodName(request, method.c_str());
XMLRPC_RequestSetRequestType(request, xmlrpc_request_call);
// stuff the parameters
XMLRPC_VALUE params = XMLRPC_CreateVector(NULL, xmlrpc_vector_struct);
XMLRPC_VectorAppendString(params, "first", firstname.c_str(), 0);
XMLRPC_VectorAppendString(params, "last", lastname.c_str(), 0);
XMLRPC_VectorAppendString(params, "passwd", dpasswd.c_str(), 0);
XMLRPC_VectorAppendString(params, "start", start.c_str(), 0);
XMLRPC_VectorAppendString(params, "version", llformat("%d.%d.%d.%d", gVersionMajor, gVersionMinor, gVersionPatch, gVersionBuild).c_str(), 0);
// Singu Note: At the request of Linden Lab we change channel sent to the login server in the following way:
// * If channel is "Singularity" we change it to "Singularity Release", due to their statistics system
// not being able to distinguish just the release version
// * We append "64" to channel name on 64-bit for systems for the LL stats system to be able to produce independent
// crash statistics depending on the architecture
std::string chan(gVersionChannel);
if (chan == "Singularity")
{
chan += " Release";
}
#if defined(_WIN64) || defined(__x86_64__)
chan += " 64";
#endif
XMLRPC_VectorAppendString(params, "channel", chan.c_str(), 0);
XMLRPC_VectorAppendString(params, "platform", PLATFORM_STRING, 0);
XMLRPC_VectorAppendString(params, "mac", hashed_mac.c_str(), 0);
// A bit of security through obscurity: id0 is volume_serial
// ^^^^^^^^^^^^^^^^^^^^
// you fucking idiot - charbl
XMLRPC_VectorAppendString(params, "id0", hashed_volume_serial.c_str(), 0);
if (skip_optional)
{
XMLRPC_VectorAppendString(params, "skipoptional", "true", 0);
}
if (accept_tos)
{
XMLRPC_VectorAppendString(params, "agree_to_tos", "true", 0);
}
if (accept_critical_message)
{
XMLRPC_VectorAppendString(params, "read_critical", "true", 0);
}
XMLRPC_VectorAppendInt(params, "last_exec_event", (int) last_exec_froze);
// append optional requests in an array
XMLRPC_VALUE options = XMLRPC_CreateVector("options", xmlrpc_vector_array);
std::vector<const char*>::const_iterator it = requested_options.begin();
std::vector<const char*>::const_iterator end = requested_options.end();
for( ; it < end; ++it)
{
XMLRPC_VectorAppendString(options, NULL, (*it), 0);
}
XMLRPC_AddValueToVector(params, options);
// put the parameters on the request
XMLRPC_RequestSetData(request, params);
// Post the XML RPC.
mResponder = new XMLRPCResponder;
LLHTTPClient::postXMLRPC(auth_uri, request, mResponder);
LL_INFOS2("AppInit", "Authentication") << "LLUserAuth::authenticate: uri=" << auth_uri << LL_ENDL;
}
LLUserAuth::UserAuthcode LLUserAuth::authResponse()
{
if (!mResponder)
{
return mAuthResponse;
}
bool done = mResponder->is_finished();
if (!done) {
if (mResponder->is_downloading())
{
mAuthResponse = E_DOWNLOADING;
}
return mAuthResponse;
}
mLastTransferRateBPS = mResponder->transferRate();
mErrorMessage = mResponder->reason();
// if curl was ok, parse the download area.
CURLcode result = mResponder->result_code();
if (is_internal_http_error(mResponder->http_status()))
{
// result can be a meaningless CURLE_OK in the case of an internal error.
result = CURLE_FAILED_INIT; // Just some random error to get the default case below.
}
switch (result)
{
case CURLE_OK:
mAuthResponse = parseResponse();
break;
case CURLE_COULDNT_RESOLVE_HOST:
mAuthResponse = E_COULDNT_RESOLVE_HOST;
break;
case CURLE_SSL_PEER_CERTIFICATE:
mAuthResponse = E_SSL_PEER_CERTIFICATE;
break;
case CURLE_SSL_CACERT:
mAuthResponse = E_SSL_CACERT;
break;
case CURLE_SSL_CONNECT_ERROR:
mAuthResponse = E_SSL_CONNECT_ERROR;
break;
default:
mAuthResponse = E_UNHANDLED_ERROR;
break;
}
LL_INFOS2("AppInit", "Authentication") << "Processed response: " << result << LL_ENDL;
// We're done with this data.
mResponder = NULL;
return mAuthResponse;
}
LLUserAuth::UserAuthcode LLUserAuth::parseResponse()
{
// The job of this function is to parse sCurlDownloadArea and
// extract every member into either the mResponses or
// mOptions. For now, we will only be looking at mResponses, which
// will all be string => string pairs.
UserAuthcode rv = E_UNHANDLED_ERROR;
XMLRPC_REQUEST response = mResponder->response();
if(!response)
{
U32 status = mResponder->http_status();
// Is it an HTTP error?
if (!(200 <= status && status < 400))
{
rv = E_HTTP_SERVER_ERROR;
}
return rv;
}
// clear out any old parsing
mResponses.clear();
// Now, parse everything
XMLRPC_VALUE param = XMLRPC_RequestGetData(response);
if (! param)
{
lldebugs << "Response contains no data" << LL_ENDL;
return rv;
}
// Now, parse everything
mResponses = parseValues(rv, "", param);
return rv;
}
LLSD LLUserAuth::parseValues(UserAuthcode &auth_code, const std::string& key_pfx, XMLRPC_VALUE param)
{
auth_code = E_OK;
LLSD responses;
for(XMLRPC_VALUE current = XMLRPC_VectorRewind(param); current;
current = XMLRPC_VectorNext(param))
{
std::string key(XMLRPC_GetValueID(current));
lldebugs << "key: " << key_pfx << key << llendl;
XMLRPC_VALUE_TYPE_EASY type = XMLRPC_GetValueTypeEasy(current);
if(xmlrpc_type_string == type)
{
LLSD::String val(XMLRPC_GetValueString(current));
lldebugs << "val: " << val << llendl;
responses.insert(key,val);
}
else if(xmlrpc_type_int == type)
{
LLSD::Integer val(XMLRPC_GetValueInt(current));
lldebugs << "val: " << val << llendl;
responses.insert(key,val);
}
else if (xmlrpc_type_double == type)
{
LLSD::Real val(XMLRPC_GetValueDouble(current));
lldebugs << "val: " << val << llendl;
responses.insert(key,val);
}
else if(xmlrpc_type_array == type)
{
// We expect this to be an array of submaps. Walk the array,
// recursively parsing each submap and collecting them.
LLSD array;
int i = 0; // for descriptive purposes
for (XMLRPC_VALUE row = XMLRPC_VectorRewind(current); row;
row = XMLRPC_VectorNext(current), ++i)
{
// Recursive call. For the lower-level key_pfx, if 'key'
// is "foo", pass "foo[0]:", then "foo[1]:", etc. In the
// nested call, a subkey "bar" will then be logged as
// "foo[0]:bar", and so forth.
// Parse the scalar subkey/value pairs from this array
// entry into a temp submap. Collect such submaps in 'array'.
std::string key_prefix = key_pfx;
array.append(parseValues(auth_code,
STRINGIZE(key_pfx << key << '[' << i << "]:"),
row));
}
// Having collected an 'array' of 'submap's, insert that whole
// 'array' as the value of this 'key'.
responses.insert(key, array);
}
else if (xmlrpc_type_struct == type)
{
LLSD submap = parseValues(auth_code,
STRINGIZE(key_pfx << key << ':'),
current);
responses.insert(key, submap);
}
else
{
// whoops - unrecognized type
llwarns << "Unhandled xmlrpc type " << type << " for key "
<< key_pfx << key << LL_ENDL;
responses.insert(key, STRINGIZE("<bad XMLRPC type " << type << '>'));
auth_code = E_UNHANDLED_ERROR;
}
}
return responses;
}