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.4KB

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