lluserauth.cpp 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. /**
  2. * @file lluserauth.cpp
  3. * @brief LLUserAuth class implementation
  4. *
  5. * $LicenseInfo:firstyear=2003&license=viewergpl$
  6. *
  7. * Copyright (c) 2003-2009, Linden Research, Inc.
  8. * Copyright (c) 2009-2024, Henri Beauchamp.
  9. *
  10. * Second Life Viewer Source Code
  11. * The source code in this file ("Source Code") is provided by Linden Lab
  12. * to you under the terms of the GNU General Public License, version 2.0
  13. * ("GPL"), unless you have obtained a separate licensing agreement
  14. * ("Other License"), formally executed by you and Linden Lab. Terms of
  15. * the GPL can be found in doc/GPL-license.txt in this distribution, or
  16. * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  17. *
  18. * There are special exceptions to the terms and conditions of the GPL as
  19. * it is applied to this Source Code. View the full text of the exception
  20. * in the file doc/FLOSS-exception.txt in this software distribution, or
  21. * online at
  22. * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  23. *
  24. * By copying, modifying or distributing this software, you acknowledge
  25. * that you have read and understood your obligations described above,
  26. * and agree to abide by those obligations.
  27. *
  28. * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  29. * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  30. * COMPLETENESS OR PERFORMANCE.
  31. * $/LicenseInfo$
  32. */
  33. #include "linden_common.h"
  34. #include "curl/curl.h"
  35. #include "lluserauth.h"
  36. #include "llsdutil.h" // For ll_pretty_print_sd
  37. #include "llxmlrpctransaction.h"
  38. // Do not define PLATFORM_STRING for unknown platforms: they need to get added
  39. // to the login.cgi script, so we want this to cause an error if we get
  40. // compiled for a different platform.
  41. #if LL_WINDOWS
  42. static const char* PLATFORM_STRING = "Win";
  43. #elif LL_DARWIN
  44. static const char* PLATFORM_STRING = "Mac";
  45. #elif LL_LINUX
  46. static const char* PLATFORM_STRING = "Lnx";
  47. #else
  48. # error("Unknown platform !")
  49. #endif
  50. LLUserAuth gUserAuth;
  51. LLUserAuth::LLUserAuth()
  52. : mTransaction(NULL),
  53. mAuthResponse(E_NO_RESPONSE_YET),
  54. mUseMFA(false)
  55. {
  56. }
  57. LLUserAuth::~LLUserAuth()
  58. {
  59. reset();
  60. }
  61. void LLUserAuth::reset()
  62. {
  63. delete mTransaction;
  64. mTransaction = NULL;
  65. mResponses.clear();
  66. }
  67. void LLUserAuth::init(const std::string& platform_version,
  68. const std::string& os_string,
  69. const std::string& version, const std::string& channel,
  70. const std::string& serial_hash,
  71. const std::string& mac_hash)
  72. {
  73. mPlatformVersion = platform_version;
  74. mPlatformOSString = os_string;
  75. mViewerVersion = version;
  76. mViewerChannel = channel;
  77. mHashedSerial = serial_hash;
  78. mHashedMAC = mac_hash;
  79. }
  80. void LLUserAuth::setMFA(bool use_mfa, const std::string& mfa_hash,
  81. const std::string& mfa_token)
  82. {
  83. mUseMFA = use_mfa;
  84. if (!use_mfa)
  85. {
  86. mMFAHash.clear();
  87. mMFAToken.clear();
  88. }
  89. // When replying to a MFA challenge (i.e. the token string is not empty),
  90. // we pass the token and an empty hash. Else, we use any last known good
  91. // MFA hash we got, with an empty token. HB
  92. // See: https://wiki.secondlife.com/wiki/User:Brad_Linden/Login_MFA
  93. else if (mfa_token.empty())
  94. {
  95. mMFAHash = mfa_hash;
  96. mMFAToken.clear();
  97. }
  98. else
  99. {
  100. mMFAHash.clear();
  101. mMFAToken = mfa_token;
  102. }
  103. }
  104. void LLUserAuth::authenticate(const std::string& auth_uri,
  105. const std::string& method,
  106. const std::string& firstname,
  107. const std::string& lastname,
  108. const std::string& passwd,
  109. const std::string& start,
  110. bool skip_optional,
  111. bool accept_tos, bool accept_critical_message,
  112. S32 last_exec_event,
  113. const std::vector<const char*>& req_options)
  114. {
  115. if (mHashedSerial.empty())
  116. {
  117. llerrs << "LLUserAuth was not properly initialized !" << llendl;
  118. }
  119. llinfos << "Authenticating: " << firstname << " " << lastname << llendl;
  120. // NOTE: passwd is already MD5 hashed by the time we get to it.
  121. std::string dpasswd("$1$");
  122. dpasswd.append(passwd);
  123. std::ostringstream option_str;
  124. option_str << "Options: ";
  125. std::ostream_iterator<const char*> appender(option_str, ", ");
  126. std::copy(req_options.begin(), req_options.end(), appender);
  127. option_str << "END";
  128. llinfos << option_str.str().c_str() << llendl;
  129. mResponses.clear();
  130. mAuthResponse = E_NO_RESPONSE_YET;
  131. //mDownloadTimer.reset();
  132. // Create the request parameters bloc
  133. LLSD params;
  134. params["first"] = firstname;
  135. params["last"] = lastname;
  136. params["passwd"] = dpasswd;
  137. params["start"] = start;
  138. params["version"] = mViewerVersion;
  139. params["channel"] = mViewerChannel;
  140. params["platform"] = PLATFORM_STRING;
  141. params["address_size"] = 64;
  142. params["platform_version"] = mPlatformVersion;
  143. params["platform_string"] = mPlatformOSString;
  144. params["mac"] = mHashedMAC;
  145. params["id0"] = mHashedSerial;
  146. if (mUseMFA)
  147. {
  148. params["mfa_hash"] = mMFAHash;
  149. params["token"] = mMFAToken;
  150. }
  151. if (skip_optional)
  152. {
  153. params["skipoptional"] = "true";
  154. }
  155. if (accept_tos)
  156. {
  157. params["agree_to_tos"] = "true";
  158. }
  159. if (accept_critical_message)
  160. {
  161. params["read_critical"] = "true";
  162. }
  163. params["last_exec_event"] = last_exec_event;
  164. // Append optional requests in an array
  165. LLSD options;
  166. for (std::vector<const char*>::const_iterator it = req_options.begin(),
  167. end = req_options.end();
  168. it < end; ++it)
  169. {
  170. options.append(*it);
  171. }
  172. params.insert("options", options);
  173. #if LL_DEBUG
  174. // Note: this shows password and MFA hashes, so only enabled for debug
  175. // builds. HB
  176. LL_DEBUGS("UserAuth") << "Request LLSD:\n" << ll_pretty_print_sd(params)
  177. << LL_ENDL;
  178. #endif
  179. if (mTransaction)
  180. {
  181. delete mTransaction;
  182. }
  183. mTransaction = new LLXMLRPCTransaction(auth_uri, method, params);
  184. llinfos << "URI: " << auth_uri << llendl;
  185. }
  186. LLUserAuth::UserAuthcode LLUserAuth::authResponse()
  187. {
  188. if (!mTransaction)
  189. {
  190. return mAuthResponse;
  191. }
  192. if (!mTransaction->process()) // All done ?
  193. {
  194. if (mTransaction->status(0) == LLXMLRPCTransaction::StatusDownloading)
  195. {
  196. mAuthResponse = E_DOWNLOADING;
  197. }
  198. return mAuthResponse;
  199. }
  200. S32 result;
  201. mTransaction->status(&result);
  202. mErrorMessage = mTransaction->statusMessage();
  203. // If curl was ok, parse the download area.
  204. switch (result)
  205. {
  206. case CURLE_OK:
  207. mResponses = mTransaction->response();
  208. mAuthResponse = E_OK;
  209. LL_DEBUGS("UserAuth") << "Responses LLSD:\n"
  210. << ll_pretty_print_sd(mResponses) << LL_ENDL;
  211. break;
  212. case CURLE_COULDNT_RESOLVE_HOST:
  213. mAuthResponse = E_COULDNT_RESOLVE_HOST;
  214. LL_DEBUGS("UserAuth") << "Could not resolve host" << LL_ENDL;
  215. break;
  216. #if CURLE_SSL_CACERT != CURLE_SSL_PEER_CERTIFICATE
  217. // Note: CURLE_SSL_CACERT and CURLE_SSL_CACERT may expand to the same
  218. // value in recent curl versions (seen with curl v7.68).
  219. case CURLE_SSL_PEER_CERTIFICATE:
  220. mAuthResponse = E_SSL_PEER_CERTIFICATE;
  221. LL_DEBUGS("UserAuth") << "Invalid peer SSL certificate" << LL_ENDL;
  222. break;
  223. #endif
  224. case CURLE_SSL_CACERT:
  225. mAuthResponse = E_SSL_CACERT;
  226. LL_DEBUGS("UserAuth") << "Invalid SSL certificate" << LL_ENDL;
  227. break;
  228. case CURLE_SSL_CONNECT_ERROR:
  229. mAuthResponse = E_SSL_CONNECT_ERROR;
  230. LL_DEBUGS("UserAuth") << "Connection error" << LL_ENDL;
  231. break;
  232. default:
  233. mAuthResponse = E_UNHANDLED_ERROR;
  234. LL_DEBUGS("UserAuth") << "Unhandled error: " << result << LL_ENDL;
  235. }
  236. llinfos << "Processed response: " << result << llendl;
  237. delete mTransaction;
  238. mTransaction = NULL;
  239. return mAuthResponse;
  240. }
  241. const LLSD& LLUserAuth::getResponse1stMap(const std::string& name) const
  242. {
  243. if (mResponses.has(name) && mResponses[name].isArray() &&
  244. mResponses[name][0].isMap())
  245. {
  246. return mResponses[name][0];
  247. }
  248. static const LLSD empty;
  249. return empty;
  250. }