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

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