XMLRPCResponder constructor was missing.
This fixes the following crash signature:
VIEWER : 2012-12-12T14:25:13Z INFO("AppInit"): setStartupState: Startup state changing from STATE_LOGIN_NO_DATA_YET to STATE_LOGIN_DOWNLOADING
VIEWER : 2012-12-12T14:25:14Z INFO("AppInit"): transferRate: Buffer size: 1308 B
VIEWER : 2012-12-12T14:25:14Z INFO("AppInit"): transferRate: Transfer rate: 11 kb/s
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff33df7f5 in XMLRPC_RequestGetData () from /usr/lib/x86_64-linux-gnu/libxmlrpc-epi.so.0
(gdb) bt
Reason was that if the server replied with some error code then
mResponse was undefined.
I also added code to retry three times in that case;
the http status code that I get was 503, which really
mean "Service *temporarily* unavailable", so a retry
is in order.
426 lines
13 KiB
C++
426 lines
13 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", 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
|
|
// ^^^^^^^^^^^^^^^^^^^^
|
|
// 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();
|
|
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;
|
|
}
|
|
|
|
|
|
|