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.

alert.cpp 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. // Copyright (c) 2010 Satoshi Nakamoto
  2. // Copyright (c) 2009-2015 The Bitcoin Core developers
  3. // Distributed under the MIT software license, see the accompanying
  4. // file COPYING or http://www.opensource.org/licenses/mit-license.php.
  5. #include "alert.h"
  6. #include "clientversion.h"
  7. #include "net.h"
  8. #include "pubkey.h"
  9. #include "timedata.h"
  10. #include "ui_interface.h"
  11. #include "util.h"
  12. #include "utilstrencodings.h"
  13. #include <stdint.h>
  14. #include <algorithm>
  15. #include <map>
  16. #include <boost/algorithm/string/classification.hpp>
  17. #include <boost/algorithm/string/replace.hpp>
  18. #include <boost/foreach.hpp>
  19. #include <boost/thread.hpp>
  20. using namespace std;
  21. map<uint256, CAlert> mapAlerts;
  22. CCriticalSection cs_mapAlerts;
  23. void CUnsignedAlert::SetNull()
  24. {
  25. nVersion = 1;
  26. nRelayUntil = 0;
  27. nExpiration = 0;
  28. nID = 0;
  29. nCancel = 0;
  30. setCancel.clear();
  31. nMinVer = 0;
  32. nMaxVer = 0;
  33. setSubVer.clear();
  34. nPriority = 0;
  35. strComment.clear();
  36. strStatusBar.clear();
  37. strReserved.clear();
  38. }
  39. std::string CUnsignedAlert::ToString() const
  40. {
  41. std::string strSetCancel;
  42. BOOST_FOREACH(int n, setCancel)
  43. strSetCancel += strprintf("%d ", n);
  44. std::string strSetSubVer;
  45. BOOST_FOREACH(const std::string& str, setSubVer)
  46. strSetSubVer += "\"" + str + "\" ";
  47. return strprintf(
  48. "CAlert(\n"
  49. " nVersion = %d\n"
  50. " nRelayUntil = %d\n"
  51. " nExpiration = %d\n"
  52. " nID = %d\n"
  53. " nCancel = %d\n"
  54. " setCancel = %s\n"
  55. " nMinVer = %d\n"
  56. " nMaxVer = %d\n"
  57. " setSubVer = %s\n"
  58. " nPriority = %d\n"
  59. " strComment = \"%s\"\n"
  60. " strStatusBar = \"%s\"\n"
  61. ")\n",
  62. nVersion,
  63. nRelayUntil,
  64. nExpiration,
  65. nID,
  66. nCancel,
  67. strSetCancel,
  68. nMinVer,
  69. nMaxVer,
  70. strSetSubVer,
  71. nPriority,
  72. strComment,
  73. strStatusBar);
  74. }
  75. void CAlert::SetNull()
  76. {
  77. CUnsignedAlert::SetNull();
  78. vchMsg.clear();
  79. vchSig.clear();
  80. }
  81. bool CAlert::IsNull() const
  82. {
  83. return (nExpiration == 0);
  84. }
  85. uint256 CAlert::GetHash() const
  86. {
  87. return Hash(this->vchMsg.begin(), this->vchMsg.end());
  88. }
  89. bool CAlert::IsInEffect() const
  90. {
  91. return (GetAdjustedTime() < nExpiration);
  92. }
  93. bool CAlert::Cancels(const CAlert& alert) const
  94. {
  95. if (!IsInEffect())
  96. return false; // this was a no-op before 31403
  97. return (alert.nID <= nCancel || setCancel.count(alert.nID));
  98. }
  99. bool CAlert::AppliesTo(int nVersion, const std::string& strSubVerIn) const
  100. {
  101. // TODO: rework for client-version-embedded-in-strSubVer ?
  102. return (IsInEffect() &&
  103. nMinVer <= nVersion && nVersion <= nMaxVer &&
  104. (setSubVer.empty() || setSubVer.count(strSubVerIn)));
  105. }
  106. bool CAlert::AppliesToMe() const
  107. {
  108. return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector<std::string>()));
  109. }
  110. bool CAlert::RelayTo(CNode* pnode) const
  111. {
  112. if (!IsInEffect())
  113. return false;
  114. // don't relay to nodes which haven't sent their version message
  115. if (pnode->nVersion == 0)
  116. return false;
  117. // returns true if wasn't already contained in the set
  118. if (pnode->setKnown.insert(GetHash()).second)
  119. {
  120. if (AppliesTo(pnode->nVersion, pnode->strSubVer) ||
  121. AppliesToMe() ||
  122. GetAdjustedTime() < nRelayUntil)
  123. {
  124. pnode->PushMessage("alert", *this);
  125. return true;
  126. }
  127. }
  128. return false;
  129. }
  130. bool CAlert::CheckSignature(const std::vector<unsigned char>& alertKey) const
  131. {
  132. CPubKey key(alertKey);
  133. if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig))
  134. return error("CAlert::CheckSignature(): verify signature failed");
  135. // Now unserialize the data
  136. CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION);
  137. sMsg >> *(CUnsignedAlert*)this;
  138. return true;
  139. }
  140. CAlert CAlert::getAlertByHash(const uint256 &hash)
  141. {
  142. CAlert retval;
  143. {
  144. LOCK(cs_mapAlerts);
  145. map<uint256, CAlert>::iterator mi = mapAlerts.find(hash);
  146. if(mi != mapAlerts.end())
  147. retval = mi->second;
  148. }
  149. return retval;
  150. }
  151. bool CAlert::ProcessAlert(const std::vector<unsigned char>& alertKey, bool fThread)
  152. {
  153. if (!CheckSignature(alertKey))
  154. return false;
  155. if (!IsInEffect())
  156. return false;
  157. // alert.nID=max is reserved for if the alert key is
  158. // compromised. It must have a pre-defined message,
  159. // must never expire, must apply to all versions,
  160. // and must cancel all previous
  161. // alerts or it will be ignored (so an attacker can't
  162. // send an "everything is OK, don't panic" version that
  163. // cannot be overridden):
  164. int maxInt = std::numeric_limits<int>::max();
  165. if (nID == maxInt)
  166. {
  167. if (!(
  168. nExpiration == maxInt &&
  169. nCancel == (maxInt-1) &&
  170. nMinVer == 0 &&
  171. nMaxVer == maxInt &&
  172. setSubVer.empty() &&
  173. nPriority == maxInt &&
  174. strStatusBar == "URGENT: Alert key compromised, upgrade required"
  175. ))
  176. return false;
  177. }
  178. {
  179. LOCK(cs_mapAlerts);
  180. // Cancel previous alerts
  181. for (map<uint256, CAlert>::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();)
  182. {
  183. const CAlert& alert = (*mi).second;
  184. if (Cancels(alert))
  185. {
  186. LogPrint("alert", "cancelling alert %d\n", alert.nID);
  187. uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
  188. mapAlerts.erase(mi++);
  189. }
  190. else if (!alert.IsInEffect())
  191. {
  192. LogPrint("alert", "expiring alert %d\n", alert.nID);
  193. uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED);
  194. mapAlerts.erase(mi++);
  195. }
  196. else
  197. mi++;
  198. }
  199. // Check if this alert has been cancelled
  200. BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts)
  201. {
  202. const CAlert& alert = item.second;
  203. if (alert.Cancels(*this))
  204. {
  205. LogPrint("alert", "alert already cancelled by %d\n", alert.nID);
  206. return false;
  207. }
  208. }
  209. // Add to mapAlerts
  210. mapAlerts.insert(make_pair(GetHash(), *this));
  211. // Notify UI and -alertnotify if it applies to me
  212. if(AppliesToMe())
  213. {
  214. uiInterface.NotifyAlertChanged(GetHash(), CT_NEW);
  215. Notify(strStatusBar, fThread);
  216. }
  217. }
  218. LogPrint("alert", "accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe());
  219. return true;
  220. }
  221. void
  222. CAlert::Notify(const std::string& strMessage, bool fThread)
  223. {
  224. std::string strCmd = GetArg("-alertnotify", "");
  225. if (strCmd.empty()) return;
  226. // Alert text should be plain ascii coming from a trusted source, but to
  227. // be safe we first strip anything not in safeChars, then add single quotes around
  228. // the whole string before passing it to the shell:
  229. std::string singleQuote("'");
  230. std::string safeStatus = SanitizeString(strMessage);
  231. safeStatus = singleQuote+safeStatus+singleQuote;
  232. boost::replace_all(strCmd, "%s", safeStatus);
  233. if (fThread)
  234. boost::thread t(runCommand, strCmd); // thread runs free
  235. else
  236. runCommand(strCmd);
  237. }