Opera 12.15 Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

fraud_check.cpp 27KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. /* -*- Mode: c++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4; c-file-style: "stroustrup" -*-
  2. *
  3. * Copyright (C) 2006-2012 Opera Software ASA. All rights reserved.
  4. *
  5. * This file is part of the Opera web browser.
  6. * It may not be distributed under any circumstances.
  7. */
  8. #include "core/pch.h"
  9. #ifdef TRUST_RATING
  10. #include "modules/dochand/fraud_check.h"
  11. #include "modules/dochand/docman.h"
  12. #include "modules/dochand/win.h"
  13. #include "modules/libssl/sslv3.h"
  14. #include "modules/locale/oplanguagemanager.h"
  15. #include "modules/prefs/prefsmanager/collections/pc_core.h"
  16. #include "modules/prefs/prefsmanager/collections/pc_network.h"
  17. #include "modules/url/url2.h"
  18. #include "modules/url/url_rep.h"
  19. #include "modules/util/tempbuf.h"
  20. #include "modules/pi/network/OpSocketAddress.h"
  21. #include "modules/pi/OpSystemInfo.h"
  22. #include "modules/xmlutils/xmltoken.h"
  23. #include "modules/xmlutils/xmlnames.h"
  24. #include "modules/regexp/include/regexp_api.h"
  25. #include "modules/about/operafraudwarning.h"
  26. #include "modules/url/protocols/comm.h"
  27. #include "modules/forms/tempbuffer8.h"
  28. #ifdef CRYPTO_HASH_MD5_SUPPORT
  29. #include "modules/libcrypto/include/CryptoHash.h"
  30. #endif // CRYPTO_HASH_MD5_SUPPORT
  31. #ifdef URL_FILTER
  32. # include "modules/content_filter/content_filter.h"
  33. #endif //URL_FILTER
  34. #include "modules/formats/encoder.h"
  35. #define SITECHECK_LOAD_TIMEOUT (30 * 1000) // 30 seconds
  36. /***************************************************
  37. *
  38. * ServerTrustChecker
  39. *
  40. ***************************************************/
  41. ServerTrustChecker::ServerTrustChecker(UINT id, DocumentManager* docman)
  42. : m_id(id), m_parser(NULL), m_server_name(NULL), m_docman(docman)
  43. {
  44. }
  45. ServerTrustChecker::~ServerTrustChecker()
  46. {
  47. OP_DELETE(m_parser);
  48. }
  49. OP_STATUS ServerTrustChecker::Init(URL& url)
  50. {
  51. m_server_name = const_cast<ServerName *>(static_cast<const ServerName *>(url.GetAttribute(URL::KServerName, NULL)));
  52. if (!m_server_name)
  53. return OpStatus::ERR_NULL_POINTER;
  54. m_initial_url = url;
  55. RETURN_IF_ERROR(AddURL(url));
  56. return OpStatus::OK;
  57. }
  58. OP_STATUS ServerTrustChecker::AddURL(URL& url)
  59. {
  60. OP_ASSERT(URLBelongsToThisServer(url));
  61. OP_ASSERT(!IsCheckingURL(url));
  62. URLCheck* new_check = OP_NEW(URLCheck, ());
  63. if (!new_check)
  64. return OpStatus::ERR_NO_MEMORY;
  65. new_check->url = url;
  66. new_check->Into(&m_checking_urls);
  67. return OpStatus::OK;
  68. }
  69. BOOL ServerTrustChecker::URLBelongsToThisServer(URL& url)
  70. {
  71. return m_initial_url.SameServer(url);
  72. }
  73. BOOL ServerTrustChecker::IsCheckingURL(URL& url)
  74. {
  75. for (URLCheck* c = (URLCheck*)m_checking_urls.First(); c; c = (URLCheck*)c->Suc())
  76. if (c->url == url)
  77. return TRUE;
  78. return FALSE;
  79. }
  80. OP_STATUS ServerTrustChecker::StartCheck(BOOL resolve_first)
  81. {
  82. if (!g_fraud_check_request_throttler.AllowRequestNow())
  83. return CheckDone(FALSE);
  84. m_parser = OP_NEW(TrustInfoParser, (this, g_main_message_handler));
  85. if (!m_parser)
  86. return OpStatus::ERR_NO_MEMORY;
  87. OP_STATUS status = m_parser->Init();
  88. if (status == OpStatus::OK)
  89. {
  90. if (resolve_first)
  91. status = m_parser->ResolveThenCheckURL(m_initial_url, NULL);
  92. else
  93. status = m_parser->CheckURL(m_initial_url, NULL);
  94. }
  95. if (status != OpStatus::OK)
  96. {
  97. OP_DELETE(m_parser);
  98. m_parser = NULL;
  99. }
  100. return status;
  101. }
  102. static OP_STATUS AddAdvisoryInfoForServerName(ServerName* server_name, const uni_char *homepage_url, const uni_char *advisory_url, const uni_char *text, unsigned int src, unsigned int type)
  103. {
  104. // FIXME: Rename the function below with an L suffix since it leaves
  105. TRAPD(status, server_name->AddAdvisoryInfo(homepage_url, advisory_url, text, src, type));
  106. return status;
  107. }
  108. OP_STATUS ServerTrustChecker::CheckDone(BOOL check_successful)
  109. {
  110. if (!m_docman->GetMessageHandler()->PostMessage(MSG_TRUST_CHECK_DONE, m_id, check_successful))
  111. return OpStatus::ERR_NO_MEMORY;
  112. if (check_successful)
  113. {
  114. URLCheck* check_url = (URLCheck*)m_checking_urls.First();
  115. OP_ASSERT(check_url);
  116. if (!check_url)
  117. return OpStatus::ERR_NULL_POINTER;
  118. m_server_name->SetTrustRating(Unknown_Trust); // all checked servers have a unknown rating in version 1.1
  119. URL current_url = m_docman->GetCurrentURL();
  120. if (!m_server_name->FraudListIsEmpty() || m_server_name->GetFirstRegExpItem() || URLBelongsToThisServer(current_url))
  121. {
  122. for (; check_url; check_url = (URLCheck*)check_url->Suc())
  123. {
  124. ServerName* server_name = check_url->url.GetServerName();
  125. if (server_name)
  126. {
  127. OpString url_string;
  128. const uni_char *matching_text = NULL;
  129. TrustInfoParser::Advisory *advisory = static_cast<TrustInfoParser::Advisory*>(m_parser->GetAdvisoryList().First());
  130. for (; advisory; advisory = static_cast<TrustInfoParser::Advisory*>(advisory->Suc()))
  131. RETURN_IF_ERROR(AddAdvisoryInfoForServerName(server_name, advisory->homepage_url, advisory->advisory_url, advisory->text, advisory->id, advisory->type));
  132. if (current_url == check_url->url)
  133. {
  134. RETURN_IF_ERROR(check_url->url.GetAttribute(URL::KUniName, url_string));
  135. OP_STATUS status = TrustInfoParser::MatchesUntrustedURL(url_string.CStr(), server_name, &matching_text);
  136. RETURN_IF_MEMORY_ERROR(status);
  137. if (status == OpBoolean::IS_TRUE)
  138. {
  139. TrustInfoParser::Advisory *elm = m_parser->GetAdvisory(matching_text);
  140. TrustRating rating = (elm->type == SERVER_SIDE_FRAUD_MALWARE) ? Malware
  141. : (elm->type == SERVER_SIDE_FRAUD_PHISHING) ? Phishing
  142. : Unknown_Trust;
  143. m_docman->GetWindow()->SetTrustRating(rating);
  144. return m_docman->GenerateAndShowFraudWarningPage(current_url, elm);
  145. }
  146. else if (m_docman->GetWindow())
  147. m_docman->GetWindow()->SetTrustRating(m_server_name->GetTrustRating());
  148. }
  149. }
  150. }
  151. }
  152. }
  153. else
  154. // Make sure we'll not end up with rating Not_Set as it means the rating hasn't been
  155. // got from the server *yet* which implies it'll be set to something different at some point.
  156. m_docman->GetWindow()->SetTrustRating(Unknown_Trust);
  157. return OpStatus::OK;
  158. }
  159. /* static */
  160. OP_STATUS ServerTrustChecker::GetTrustRating(URL& url, TrustRating& rating, BOOL& needs_online_check)
  161. {
  162. rating = Not_Set;
  163. needs_online_check = FALSE;
  164. ServerName *server_name = (ServerName *) url.GetAttribute(URL::KServerName, NULL);
  165. if (server_name
  166. && (url.Type() == URL_HTTP || url.Type() == URL_HTTPS || url.Type() == URL_FTP))
  167. {
  168. rating = server_name->GetTrustRating(); // only check's if we've already gotten a reply for this server
  169. if (rating != Not_Set) // this server has had the blacklists downloaded
  170. {
  171. OpString url_string;
  172. RETURN_IF_ERROR(url.GetAttribute(URL::KUniName, url_string));
  173. OP_STATUS status = TrustInfoParser::MatchesUntrustedURL(url_string.CStr(), server_name);
  174. RETURN_IF_MEMORY_ERROR(status);
  175. if (status == OpBoolean::IS_TRUE)
  176. rating = Untrusted_Ask_Advisory;
  177. return OpStatus::OK;
  178. }
  179. if (server_name->GetTrustRatingBypassed())
  180. return OpStatus::OK;
  181. // The site check host itself is always trusted
  182. if (op_strcmp(server_name->Name(), SITECHECK_HOST) == 0)
  183. {
  184. rating = Unknown_Trust; // only use trusted rating in version 1
  185. return OpStatus::OK;
  186. }
  187. else
  188. {
  189. needs_online_check = TRUE;
  190. return OpStatus::OK;
  191. }
  192. }
  193. else if (url.Type() == URL_OPERA && url.GetAttribute(URL::KIsGeneratedByOpera))
  194. rating = Domain_Trusted;
  195. return OpStatus::OK;
  196. }
  197. /* static */
  198. OP_STATUS ServerTrustChecker::IsLocalURL(URL& url, BOOL& is_local, BOOL& need_to_resolve)
  199. {
  200. is_local = FALSE;
  201. need_to_resolve = FALSE;
  202. if (ServerName *server_name = (ServerName *) url.GetAttribute(URL::KServerName, NULL))
  203. if (OpSocketAddress* socket_address = server_name->SocketAddress())
  204. {
  205. if (socket_address->GetNetType() == NETTYPE_LOCALHOST || socket_address->GetNetType() == NETTYPE_PRIVATE)
  206. is_local = TRUE;
  207. else if (socket_address->GetNetType() == NETTYPE_UNDETERMINED && !url.GetAttribute(URL::KUseProxy))
  208. need_to_resolve = TRUE;
  209. }
  210. return OpStatus::OK;
  211. }
  212. /***************************************************
  213. *
  214. * TrustInfoParser
  215. *
  216. ***************************************************/
  217. TrustInfoParser::TrustInfoParser(ServerTrustChecker* checker, MessageHandler* mh) :
  218. m_checker(checker), m_xml_parser(NULL), m_current_element(OtherElement),
  219. m_message_handler(mh), m_current_advisory(NULL), m_current_url(NULL)
  220. {
  221. OP_ASSERT(mh);
  222. }
  223. TrustInfoParser::~TrustInfoParser()
  224. {
  225. m_message_handler->UnsetCallBacks(this);
  226. OP_DELETE(m_xml_parser);
  227. UrlListItem *item = static_cast<UrlListItem*>(m_url_list.First());
  228. while (item)
  229. {
  230. UrlListItem *tmp = item;
  231. item = static_cast<UrlListItem*>(item->Suc());
  232. tmp->Out();
  233. OP_DELETE(tmp);
  234. }
  235. Advisory *advisory = static_cast<Advisory*>(m_advisory_list.First());
  236. while (advisory)
  237. {
  238. Advisory *tmp = advisory;
  239. advisory = static_cast<Advisory*>(advisory->Suc());
  240. tmp->Out();
  241. OP_DELETE(tmp);
  242. }
  243. }
  244. OP_STATUS TrustInfoParser::Init()
  245. {
  246. return OpStatus::OK;
  247. }
  248. char* TrustInfoParser::EscapePluses(char* string)
  249. {
  250. if (!string)
  251. return NULL;
  252. UINT plus_count = 0, length = 0;
  253. char* src = string;
  254. while (*src)
  255. {
  256. if (*(src++) == '+')
  257. plus_count++;
  258. length++;
  259. }
  260. // No escapes needed, return original string instead of deleting it
  261. if (plus_count == 0)
  262. return string;
  263. char* result = OP_NEWA(char, length + (2 * plus_count) + 1); // each plus is replaced by a three char escape code i.e. two more chars
  264. if (!result)
  265. {
  266. OP_DELETEA(string);
  267. return NULL;
  268. }
  269. char* dest = result;
  270. for (src = string; *src; src++)
  271. {
  272. if (*src == '+')
  273. { // escape code for + is %2B
  274. *(dest++) = '%';
  275. *(dest++) = '2';
  276. *(dest++) = 'B';
  277. }
  278. else
  279. *(dest++) = *src;
  280. }
  281. *dest = '\0';
  282. OP_DELETEA(string);
  283. return result;
  284. }
  285. /* static */
  286. OP_BOOLEAN TrustInfoParser::RegExpMatchUrl(const uni_char* reg_exp_string, const uni_char* url)
  287. {
  288. OpStackAutoPtr<OpRegExp> reg_exp(NULL);
  289. OpREFlags re_flags;
  290. OpREMatchLoc match;
  291. re_flags.multi_line = FALSE;
  292. re_flags.case_insensitive = TRUE;
  293. re_flags.ignore_whitespace = FALSE;
  294. OpRegExp* tmp_reg_exp;
  295. RETURN_IF_ERROR(OpRegExp::CreateRegExp(&tmp_reg_exp, reg_exp_string, &re_flags));
  296. reg_exp.reset(tmp_reg_exp);
  297. RETURN_IF_ERROR(reg_exp->Match(url, &match));
  298. if (match.matchloc != REGEXP_NO_MATCH)
  299. return OpBoolean::IS_TRUE;
  300. return OpBoolean::IS_FALSE;
  301. }
  302. /* static */
  303. OP_BOOLEAN TrustInfoParser::MatchesUntrustedURL(const uni_char* url_string, ServerName* server_name, const uni_char **matching_text
  304. )
  305. {
  306. // First check if the specific URL is blocked
  307. if (server_name->IsUrlInFraudList(url_string, matching_text))
  308. return OpBoolean::IS_TRUE;
  309. // check if url matches any of the reg-exps
  310. for (FraudListItem* regexp = server_name->GetFirstRegExpItem(); regexp; regexp = (FraudListItem*)regexp->Suc())
  311. {
  312. OP_STATUS status = RegExpMatchUrl(regexp->value.CStr(), url_string);
  313. RETURN_IF_MEMORY_ERROR(status);
  314. if (status == OpBoolean::IS_TRUE)
  315. {
  316. if (matching_text)
  317. *matching_text = regexp->value.CStr();
  318. return OpBoolean::IS_TRUE;
  319. }
  320. }
  321. return OpBoolean::IS_FALSE;
  322. }
  323. /* static */
  324. OP_STATUS TrustInfoParser::CalculateMD5Hash(OpString& input, char*& md5hash, BOOL url_escape_hash)
  325. {
  326. // Calculate MD5 from utf-8 encoding of input
  327. OpString8 buffer;
  328. RETURN_IF_ERROR(buffer.SetUTF8FromUTF16(input.CStr()));
  329. return CalculateMD5Hash(buffer.CStr(), md5hash, url_escape_hash);
  330. }
  331. /* static */
  332. OP_STATUS TrustInfoParser::CalculateMD5Hash(const char* buffer, char*& md5hash, BOOL url_escape_hash)
  333. {
  334. md5hash = NULL;
  335. #ifdef CRYPTO_HASH_MD5_SUPPORT
  336. OpAutoPtr<CryptoHash> hasher(CryptoHash::CreateMD5());
  337. if (!hasher.get())
  338. return OpStatus::ERR_NO_MEMORY;
  339. #else
  340. SSL_Hash_Pointer hasher(SSL_MD5);
  341. #endif // CRYPTO_HASH_MD5_SUPPORT
  342. hasher->InitHash();
  343. hasher->CalculateHash(buffer);
  344. // 128bit MD5 hash == 16 bytes
  345. unsigned char digest[16]; /* ARRAY OK 2008-02-28 jl */
  346. hasher->ExtractHash((unsigned char *) digest);
  347. // Base64 encode the MD5-sum
  348. char* temp_buf = NULL;
  349. int targetlen = 0;
  350. // MIME_Encode_SetStr allocates a buffer of appropriate size for temp_buf, has to be deallocated
  351. MIME_Encode_Error err = MIME_Encode_SetStr(temp_buf, targetlen, (char*)digest, (int)sizeof(digest), NULL, GEN_BASE64_ONELINE);
  352. if (err == MIME_NO_ERROR)
  353. {
  354. if (url_escape_hash)
  355. md5hash = EscapePluses(temp_buf);
  356. else
  357. md5hash = temp_buf;
  358. if (md5hash)
  359. return OpStatus::OK;
  360. else
  361. return OpStatus::ERR_NO_MEMORY;
  362. }
  363. else
  364. {
  365. OP_DELETEA(temp_buf);
  366. return OpStatus::ERR;
  367. }
  368. }
  369. /* static */
  370. OP_STATUS TrustInfoParser::GetNormalizedHostname(URL& url, OpString8& hostname)
  371. {
  372. RETURN_IF_ERROR(hostname.Set(url.GetAttribute(URL::KHostName)));
  373. // trim trailing dots in server name
  374. int length = hostname.Length();
  375. int end = length;
  376. while (end > 0 && hostname[end - 1] == '.')
  377. end--;
  378. if (end < length)
  379. hostname.Delete(end);
  380. return OpStatus::OK;
  381. }
  382. /* static */
  383. OP_STATUS TrustInfoParser::NormalizeURL(URL& url, OpString8& normalized_url8, OpString8& hostname)
  384. {
  385. /* Steps for normalizing and hashing URL
  386. * + url decode (if applicable)
  387. * + remove trailing . from server name
  388. * + lower case
  389. * + strip off protocol
  390. * + trim off parameters(?,#,;)
  391. * + trim off trailing '/'s */
  392. /* arneh, 20061002 - yes, point 5 won't really strip parameters at
  393. * that point, as after decoding we can't really tell what's parameters
  394. * and not. We just strip whatever still looks like parameters after
  395. * decoding. This is correct according to Geotrust.
  396. *
  397. * This might be to guard agains servers (like Apache) which interpret %3F
  398. * as a parameter delimiter if the path would otherwise return a 404.
  399. */
  400. RETURN_IF_ERROR(GetNormalizedHostname(url, hostname));
  401. if (hostname.IsEmpty())
  402. return OpStatus::ERR;
  403. // Get decoded path
  404. OpString path;
  405. RETURN_IF_ERROR(path.Set(url.GetAttribute(URL::KUniPath)));
  406. // trim off parameters (?,;) and trailing /
  407. int length = path.Length();
  408. int end = length;
  409. int param_start = path.FindFirstOf(OpStringC16(UNI_L(";?")));
  410. if (param_start != KNotFound)
  411. end = param_start;
  412. while (end > 0 && path[end - 1] == '/')
  413. end--;
  414. TempBuffer path_buffer;
  415. RETURN_IF_ERROR(path_buffer.Expand(end + 1));
  416. // collapse consecutive slashes - fix for bug #244961
  417. BOOL last_was_slash = FALSE;
  418. for (int i = 0; i < end; i++)
  419. {
  420. OP_ASSERT(path[i]);
  421. if (path[i] == '/')
  422. {
  423. if (last_was_slash)
  424. continue;
  425. else
  426. last_was_slash = TRUE;
  427. }
  428. else
  429. last_was_slash = FALSE;
  430. path_buffer.Append(path[i]);
  431. }
  432. OpString normalized_url16;
  433. RETURN_IF_ERROR(normalized_url16.Append(hostname));
  434. // have to include port number if it is non-standard one
  435. UINT32 port = url.GetAttribute(URL::KServerPort);
  436. if (port) // port != 0 is non-standard
  437. RETURN_IF_ERROR(normalized_url16.AppendFormat(UNI_L(":%d"), port));
  438. RETURN_IF_ERROR(normalized_url16.Append(path_buffer.GetStorage()));
  439. normalized_url16.MakeLower();
  440. // Calculate MD5 from utf-8 encoding of input
  441. RETURN_IF_ERROR(normalized_url8.SetUTF8FromUTF16(normalized_url16.CStr()));
  442. return OpStatus::OK;
  443. }
  444. OP_STATUS TrustInfoParser::GenerateURLHash(URL& url, OpString8& hash)
  445. {
  446. OpString8 normalized_url;
  447. OpString8 hostname;
  448. char* cstr_hash = NULL;
  449. RETURN_IF_ERROR(NormalizeURL(url, normalized_url, hostname));
  450. RETURN_IF_ERROR(CalculateMD5Hash(normalized_url.CStr(), cstr_hash, FALSE));
  451. hash.TakeOver(cstr_hash);
  452. return OpStatus::OK;
  453. }
  454. /* static */
  455. OP_STATUS TrustInfoParser::GenerateRequestURL(URL& url, URL& request_url, BOOL request_info_page, const char* full_path_md5)
  456. {
  457. OpString8 hostname;
  458. OpString8 str_full_path_md5;
  459. RETURN_IF_ERROR(GetNormalizedHostname(url, hostname));
  460. // Take MD5 of hdn value
  461. OpString8 hdn_value;
  462. RETURN_IF_ERROR(hdn_value.Set(hostname));
  463. RETURN_IF_ERROR(hdn_value.Append(SITECHECK_HDN_SUFFIX));
  464. char* hdn_md5 = NULL;
  465. RETURN_IF_ERROR(CalculateMD5Hash(hdn_value.CStr(), hdn_md5, TRUE));
  466. OpString8 str_hdn_md5;
  467. str_hdn_md5.TakeOver(hdn_md5);
  468. /** Final request URL should look like:
  469. * http://sitecheck.opera.com/?host=$hostname&ph=&hash&hdn=$hdn
  470. * where
  471. * $hostname is the hostname part of the URL
  472. * $hash is a MD5 sum of the normalized URL (see below) (only used in version 1)
  473. * $hdn is a MD5 sum of hostname with an encryption string concatenated */
  474. OpString8 request_string;
  475. if (url.Type() == URL_HTTPS)
  476. RETURN_IF_ERROR(request_string.AppendFormat("https://%s/", SITECHECK_HOST));
  477. else
  478. RETURN_IF_ERROR(request_string.AppendFormat("http://%s/", SITECHECK_HOST));
  479. if (request_info_page)
  480. RETURN_IF_ERROR(request_string.Append("info/"));
  481. RETURN_IF_ERROR(request_string.AppendFormat("?host=%s&hdn=%s", hostname.CStr(), hdn_md5));
  482. if (request_info_page)
  483. {
  484. TempBuffer8 phishing_url;
  485. RETURN_IF_ERROR(phishing_url.AppendURLEncoded(url.GetAttribute(URL::KName).CStr()));
  486. RETURN_IF_ERROR(request_string.AppendFormat("&site=%s", phishing_url.GetStorage()));
  487. }
  488. request_url = g_url_api->GetURL(url, request_string.CStr());
  489. request_url.SetAttribute(URL::KBlockUserInteraction, TRUE);
  490. request_url.SetAttribute(URL::KDisableProcessCookies, TRUE);
  491. #ifdef URL_FILTER
  492. request_url.SetAttribute(URL::KSkipContentBlocker, TRUE);
  493. #endif // URL_FILTER
  494. return OpStatus::OK;
  495. }
  496. OP_STATUS TrustInfoParser::ResolveThenCheckURL(URL& url, const char* full_path_hash)
  497. {
  498. m_host_url = url;
  499. if (full_path_hash)
  500. RETURN_IF_ERROR(m_full_path_hash.Set(full_path_hash));
  501. OpString hostname;
  502. RETURN_IF_ERROR(hostname.Set(url.GetAttribute(URL::KUniHostName)));
  503. IPAddress ip_ignored;
  504. RETURN_IF_ERROR(g_secman_instance->ResolveURL(url, ip_ignored, m_state));
  505. if (m_state.suspended)
  506. {
  507. m_message_handler->SetCallBack(this, MSG_COMM_NAMERESOLVED, m_state.host_resolving_comm->Id());
  508. m_message_handler->SetCallBack(this, MSG_COMM_LOADING_FAILED, m_state.host_resolving_comm->Id());
  509. }
  510. else
  511. OnHostResolved(url.GetServerName()->SocketAddress());
  512. return OpStatus::OK;
  513. }
  514. void TrustInfoParser::HandleCallback(OpMessage msg, MH_PARAM_1 par1, MH_PARAM_2 par2)
  515. {
  516. OP_ASSERT(m_state.host_resolving_comm &&
  517. par1 == static_cast<MH_PARAM_1>(m_state.host_resolving_comm->Id()) &&
  518. (msg == MSG_COMM_LOADING_FAILED || msg == MSG_COMM_NAMERESOLVED));
  519. m_message_handler->UnsetCallBacks(this);
  520. m_state.suspended = FALSE;
  521. if (msg == MSG_COMM_LOADING_FAILED)
  522. m_checker->CheckDone(FALSE);
  523. else
  524. OnHostResolved(m_state.host_resolving_comm->HostName()->SocketAddress());
  525. }
  526. OP_STATUS TrustInfoParser::CheckURL(URL& url, const char* full_path_hash)
  527. {
  528. m_host_url = url;
  529. URL request_url;
  530. RETURN_IF_ERROR(GenerateRequestURL(url, request_url, FALSE, full_path_hash));
  531. // Make a parser for result
  532. RETURN_IF_ERROR(XMLParser::Make(m_xml_parser, this,
  533. m_message_handler, this, request_url));
  534. XMLParser::Configuration configuration;
  535. configuration.load_external_entities = XMLParser::LOADEXTERNALENTITIES_NO;
  536. configuration.max_tokens_per_call = 0; // unlimited
  537. m_xml_parser->SetConfiguration(configuration);
  538. URL referer_url;
  539. return m_xml_parser->Load(referer_url, TRUE, SITECHECK_LOAD_TIMEOUT);
  540. }
  541. // Callbacks from XML parser
  542. XMLTokenHandler::Result TrustInfoParser::HandleToken(XMLToken &token)
  543. {
  544. OP_STATUS status = OpStatus::OK;
  545. switch (token.GetType())
  546. {
  547. case XMLToken::TYPE_CDATA :
  548. case XMLToken::TYPE_Text :
  549. status = HandleTextToken(token);
  550. break;
  551. case XMLToken::TYPE_STag :
  552. status = HandleStartTagToken(token);
  553. break;
  554. case XMLToken::TYPE_ETag :
  555. status = HandleEndTagToken(token);
  556. break;
  557. case XMLToken::TYPE_EmptyElemTag :
  558. status = HandleStartTagToken(token);
  559. if (OpStatus::IsSuccess(status))
  560. status = HandleEndTagToken(token);
  561. break;
  562. default:
  563. break;
  564. }
  565. if (OpStatus::IsMemoryError(status))
  566. return XMLTokenHandler::RESULT_OOM;
  567. else if (OpStatus::IsError(status))
  568. return XMLTokenHandler::RESULT_ERROR;
  569. return XMLTokenHandler::RESULT_OK;
  570. }
  571. OP_STATUS TrustInfoParser::HandleTextToken(XMLToken &token)
  572. {
  573. ServerName *sn = (ServerName *) m_host_url.GetAttribute(URL::KServerName, NULL);
  574. if (m_current_element == RegExpElement || m_current_element == UrlElement)
  575. {
  576. if (sn)
  577. {
  578. OpString value;
  579. RETURN_IF_ERROR(value.Set(token.GetLiteralSimpleValue(), token.GetLiteralLength()));
  580. if (m_current_element == UrlElement)
  581. RETURN_IF_LEAVE(sn->AddUrlL(value, m_current_url ? m_current_url->src : 0));
  582. else
  583. RETURN_IF_LEAVE(sn->AddRegExpL(value));
  584. if (m_current_url)
  585. {
  586. m_current_url->url = token.GetLiteralAllocatedValue();
  587. if (!m_current_url->url)
  588. return OpStatus::ERR_NO_MEMORY;
  589. m_current_url = NULL;
  590. }
  591. }
  592. }
  593. else if (m_current_element == SourceElement && m_current_advisory)
  594. {
  595. m_current_advisory->text = token.GetLiteralAllocatedValue();
  596. if (!m_current_advisory->text)
  597. return OpStatus::ERR_NO_MEMORY;
  598. m_current_advisory = NULL;
  599. }
  600. else if (m_current_element == ClientExpireElement)
  601. {
  602. if (sn)
  603. {
  604. uni_char* value = token.GetLiteralAllocatedValue();
  605. if (value)
  606. {
  607. UINT expire_time = uni_atoi(value);
  608. sn->SetTrustTTL(expire_time);
  609. OP_DELETEA(value);
  610. }
  611. }
  612. }
  613. return OpStatus::OK;
  614. }
  615. OP_STATUS TrustInfoParser::HandleStartTagToken(XMLToken &token)
  616. {
  617. // Fetch information about the element.
  618. const XMLCompleteNameN &elemname = token.GetName();
  619. if (uni_strncmp(UNI_L("host"), elemname.GetLocalPart(), elemname.GetLocalPartLength()) == 0)
  620. m_current_element = HostElement;
  621. else if (uni_strncmp(UNI_L("ce"), elemname.GetLocalPart(), elemname.GetLocalPartLength()) == 0)
  622. m_current_element = ClientExpireElement;
  623. else if (uni_strncmp(UNI_L("r"), elemname.GetLocalPart(), elemname.GetLocalPartLength()) == 0)
  624. {
  625. OpAutoPtr<UrlListItem> url_item(OP_NEW(UrlListItem, ()));
  626. if (!url_item.get())
  627. return OpStatus::ERR_NO_MEMORY;
  628. XMLToken::Attribute *xml_attr;
  629. xml_attr = token.GetAttribute(UNI_L("src"));
  630. if (xml_attr)
  631. {
  632. unsigned len = xml_attr->GetValueLength();
  633. uni_char *src_string = OP_NEWA(uni_char, len+1);
  634. if (!src_string)
  635. return OpStatus::ERR_NO_MEMORY;
  636. uni_strncpy(src_string, xml_attr->GetValue(), len);
  637. src_string[len] = 0;
  638. url_item->src = uni_atoi(src_string);
  639. OP_DELETEA(src_string);
  640. }
  641. m_current_url = url_item.release();
  642. m_current_url->Into(&m_url_list);
  643. m_current_element = RegExpElement;
  644. }
  645. else if (uni_strncmp(UNI_L("u"), elemname.GetLocalPart(), elemname.GetLocalPartLength()) == 0)
  646. {
  647. OpAutoPtr<UrlListItem> url_item(OP_NEW(UrlListItem, ()));
  648. if (!url_item.get())
  649. return OpStatus::ERR_NO_MEMORY;
  650. XMLToken::Attribute *xml_attr;
  651. xml_attr = token.GetAttribute(UNI_L("src"));
  652. if (xml_attr)
  653. {
  654. unsigned len = xml_attr->GetValueLength();
  655. uni_char *src_string = OP_NEWA(uni_char, len+1);
  656. if (!src_string)
  657. return OpStatus::ERR_NO_MEMORY;
  658. uni_strncpy(src_string, xml_attr->GetValue(), len);
  659. src_string[len] = 0;
  660. url_item->src = uni_atoi(src_string);
  661. OP_DELETEA(src_string);
  662. }
  663. m_current_url = url_item.release();
  664. m_current_url->Into(&m_url_list);
  665. m_current_element = UrlElement;
  666. }
  667. else if (uni_strncmp(UNI_L("source"), elemname.GetLocalPart(), elemname.GetLocalPartLength()) == 0)
  668. {
  669. XMLToken::Attribute *xml_attr;
  670. OpAutoPtr<Advisory> advisory(OP_NEW(Advisory, ()));
  671. m_current_advisory = advisory.get();
  672. if (!advisory.get())
  673. return OpStatus::ERR_NO_MEMORY;
  674. xml_attr = token.GetAttribute(UNI_L("id"));
  675. if (xml_attr)
  676. {
  677. unsigned len = xml_attr->GetValueLength();
  678. uni_char *id_string = OP_NEWA(uni_char, len+1);
  679. if (!id_string)
  680. return OpStatus::ERR_NO_MEMORY;
  681. uni_strncpy(id_string, xml_attr->GetValue(), len);
  682. id_string[len] = 0;
  683. advisory->id = uni_atoi(id_string);
  684. OP_DELETEA(id_string);
  685. }
  686. xml_attr = token.GetAttribute(UNI_L("type"));
  687. if (xml_attr)
  688. {
  689. unsigned len = xml_attr->GetValueLength();
  690. uni_char *type_string = OP_NEWA(uni_char, len+1);
  691. if (!type_string)
  692. return OpStatus::ERR_NO_MEMORY;
  693. uni_strncpy(type_string, xml_attr->GetValue(), len);
  694. type_string[len] = 0;
  695. advisory->type = uni_atoi(type_string);
  696. OP_DELETEA(type_string);
  697. }
  698. xml_attr = token.GetAttribute(UNI_L("homepage"));
  699. if (xml_attr)
  700. {
  701. unsigned len = xml_attr->GetValueLength();
  702. uni_char *homepage = OP_NEWA(uni_char, len+1);
  703. if (!homepage)
  704. return OpStatus::ERR_NO_MEMORY;
  705. uni_strncpy(homepage, xml_attr->GetValue(), len);
  706. homepage[len] = 0;
  707. advisory->homepage_url = homepage;
  708. }
  709. xml_attr = token.GetAttribute(UNI_L("advisory"));
  710. if (xml_attr)
  711. {
  712. unsigned len = xml_attr->GetValueLength();
  713. uni_char *advisory_url = OP_NEWA(uni_char, len+1);
  714. if (!advisory_url)
  715. return OpStatus::ERR_NO_MEMORY;
  716. uni_strncpy(advisory_url, xml_attr->GetValue(), len);
  717. advisory_url[len] = 0;
  718. advisory->advisory_url = advisory_url;
  719. }
  720. advisory.release()->Into(&m_advisory_list);
  721. m_current_element = SourceElement;
  722. }
  723. return OpStatus::OK;
  724. }
  725. OP_STATUS TrustInfoParser::HandleEndTagToken(XMLToken &token)
  726. {
  727. m_current_element = OtherElement;
  728. return OpStatus::OK;
  729. }
  730. void TrustInfoParser::Continue(XMLParser* parser)
  731. {
  732. }
  733. void TrustInfoParser::Stopped(XMLParser* parser)
  734. {
  735. OP_ASSERT(parser->IsFinished() || parser->IsFailed());
  736. m_xml_parser = NULL; // The XML parser deletes itself, and we can't do it
  737. //here anyway, as it's in a callback from it
  738. if (parser->IsFailed()) // don't retry request until sometime later. Avoids DOS-ing the sitecheck server
  739. g_fraud_check_request_throttler.RequestFailed();
  740. else
  741. g_fraud_check_request_throttler.RequestSucceeded();
  742. m_checker->CheckDone(TRUE);
  743. }
  744. void TrustInfoParser::OnHostResolved(OpSocketAddress* address)
  745. {
  746. OP_ASSERT(address);
  747. OP_STATUS opstatus = OpStatus::OK;
  748. if (address->GetNetType() != NETTYPE_LOCALHOST && address->GetNetType() != NETTYPE_PRIVATE)
  749. if (OpStatus::IsSuccess(opstatus = CheckURL(m_host_url, m_full_path_hash.CStr())))
  750. return; // URL is being checked
  751. if (OpStatus::IsMemoryError(opstatus))
  752. g_memory_manager->RaiseCondition(opstatus);
  753. m_checker->CheckDone(FALSE);
  754. }
  755. TrustInfoParser::Advisory* TrustInfoParser::GetAdvisory(const uni_char *matching_text)
  756. {
  757. UrlListItem *matching_item = NULL;
  758. UrlListItem *item = static_cast<UrlListItem*>(m_url_list.First());
  759. for (; item; item = static_cast<UrlListItem*>(item->Suc()))
  760. {
  761. if (uni_strcmp(item->url, matching_text) == 0)
  762. {
  763. matching_item = item;
  764. break;
  765. }
  766. }
  767. if (matching_item)
  768. {
  769. Advisory *elm = static_cast<Advisory*>(m_advisory_list.First());
  770. for (; elm; elm = static_cast<Advisory*>(elm->Suc()))
  771. {
  772. if (elm->id == matching_item->src)
  773. return elm;
  774. }
  775. }
  776. return NULL;
  777. }
  778. void FraudCheckRequestThrottler::RequestFailed()
  779. {
  780. if (m_grace_period == 0)
  781. m_grace_period = FRAUD_CHECK_MINIMUM_GRACE_PERIOD;
  782. else // increase grace period for each failed attempt
  783. {
  784. m_grace_period *= 2;
  785. if (m_grace_period > FRAUD_CHECK_MAXIMUM_GRACE_PERIOD)
  786. m_grace_period = FRAUD_CHECK_MAXIMUM_GRACE_PERIOD;
  787. }
  788. m_last_failed_request = g_op_time_info->GetRuntimeMS();
  789. }
  790. BOOL FraudCheckRequestThrottler::AllowRequestNow()
  791. {
  792. // Always TRUE if m_grace_period = 0
  793. return (g_op_time_info->GetRuntimeMS() - m_last_failed_request) > m_grace_period;
  794. }
  795. #endif // TRUST_RATING