// // Alert system // #include #include #include #include #include #include "alert.h" #include "key.h" #include "net.h" #include "sync.h" #include "ui_interface.h" using namespace std; map mapAlerts; CCriticalSection cs_mapAlerts; static const char* pszMainKey = "04877149dfeaf42a32d7eae8ecbd3cd7b5f6f4cdbf81af727c16d5daee3cb8c67198c4e163818feac24d182950f75bd39471178d4400abe60a5cce558431d08571"; static const char* pszTestKey = "049feb2168f7ec2401d28fb41674b80b1b6e12a43de326686d0d5a8fff9d45e96be58d185304d0228fb8e9b24bfa85c1c62995babf11da8bc1620a48abe715ecd3"; void CUnsignedAlert::SetNull() { nVersion = 1; nRelayUntil = 0; nExpiration = 0; nID = 0; nCancel = 0; setCancel.clear(); nMinVer = 0; nMaxVer = 0; setSubVer.clear(); nPriority = 0; strComment.clear(); strStatusBar.clear(); strReserved.clear(); } std::string CUnsignedAlert::ToString() const { std::string strSetCancel; BOOST_FOREACH(int n, setCancel) strSetCancel += strprintf("%d ", n); std::string strSetSubVer; BOOST_FOREACH(std::string str, setSubVer) strSetSubVer += "\"" + str + "\" "; return strprintf( "CAlert(\n" " nVersion = %d\n" " nRelayUntil = %" PRI64d "\n" " nExpiration = %" PRI64d "\n" " nID = %d\n" " nCancel = %d\n" " setCancel = %s\n" " nMinVer = %d\n" " nMaxVer = %d\n" " setSubVer = %s\n" " nPriority = %d\n" " strComment = \"%s\"\n" " strStatusBar = \"%s\"\n" ")\n", nVersion, nRelayUntil, nExpiration, nID, nCancel, strSetCancel.c_str(), nMinVer, nMaxVer, strSetSubVer.c_str(), nPriority, strComment.c_str(), strStatusBar.c_str()); } void CUnsignedAlert::print() const { printf("%s", ToString().c_str()); } void CAlert::SetNull() { CUnsignedAlert::SetNull(); vchMsg.clear(); vchSig.clear(); } bool CAlert::IsNull() const { return (nExpiration == 0); } uint256 CAlert::GetHash() const { return Hash(this->vchMsg.begin(), this->vchMsg.end()); } bool CAlert::IsInEffect() const { return (GetAdjustedTime() < nExpiration); } bool CAlert::Cancels(const CAlert& alert) const { if (!IsInEffect()) return false; // this was a no-op before 31403 return (alert.nID <= nCancel || setCancel.count(alert.nID)); } bool CAlert::AppliesTo(int nVersion, std::string strSubVerIn) const { // TODO: rework for client-version-embedded-in-strSubVer ? return (IsInEffect() && nMinVer <= nVersion && nVersion <= nMaxVer && (setSubVer.empty() || setSubVer.count(strSubVerIn))); } bool CAlert::AppliesToMe() const { return AppliesTo(PROTOCOL_VERSION, FormatSubVersion(CLIENT_NAME, CLIENT_VERSION, std::vector())); } bool CAlert::RelayTo(CNode* pnode) const { if (!IsInEffect()) return false; // returns true if wasn't already contained in the set if (pnode->setKnown.insert(GetHash()).second) { if (AppliesTo(pnode->nVersion, pnode->strSubVer) || AppliesToMe() || GetAdjustedTime() < nRelayUntil) { pnode->PushMessage("alert", *this); return true; } } return false; } bool CAlert::CheckSignature() const { CPubKey key(ParseHex(fTestNet ? pszTestKey : pszMainKey)); if (!key.Verify(Hash(vchMsg.begin(), vchMsg.end()), vchSig)) return error("CAlert::CheckSignature() : verify signature failed"); // Now unserialize the data CDataStream sMsg(vchMsg, SER_NETWORK, PROTOCOL_VERSION); sMsg >> *(CUnsignedAlert*)this; return true; } CAlert CAlert::getAlertByHash(const uint256 &hash) { CAlert retval; { LOCK(cs_mapAlerts); map::iterator mi = mapAlerts.find(hash); if(mi != mapAlerts.end()) retval = mi->second; } return retval; } bool CAlert::ProcessAlert(bool fThread) { if (!CheckSignature()) return false; if (!IsInEffect()) return false; // alert.nID=max is reserved for if the alert key is // compromised. It must have a pre-defined message, // must never expire, must apply to all versions, // and must cancel all previous // alerts or it will be ignored (so an attacker can't // send an "everything is OK, don't panic" version that // cannot be overridden): int maxInt = std::numeric_limits::max(); if (nID == maxInt) { if (!( nExpiration == maxInt && nCancel == (maxInt-1) && nMinVer == 0 && nMaxVer == maxInt && setSubVer.empty() && nPriority == maxInt && strStatusBar == "URGENT: Alert key compromised, upgrade required" )) return false; } { LOCK(cs_mapAlerts); // Cancel previous alerts for (map::iterator mi = mapAlerts.begin(); mi != mapAlerts.end();) { const CAlert& alert = (*mi).second; if (Cancels(alert)) { printf("cancelling alert %d\n", alert.nID); uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); mapAlerts.erase(mi++); } else if (!alert.IsInEffect()) { printf("expiring alert %d\n", alert.nID); uiInterface.NotifyAlertChanged((*mi).first, CT_DELETED); mapAlerts.erase(mi++); } else mi++; } // Check if this alert has been cancelled BOOST_FOREACH(PAIRTYPE(const uint256, CAlert)& item, mapAlerts) { const CAlert& alert = item.second; if (alert.Cancels(*this)) { printf("alert already cancelled by %d\n", alert.nID); return false; } } // Add to mapAlerts mapAlerts.insert(make_pair(GetHash(), *this)); // Notify UI and -alertnotify if it applies to me if(AppliesToMe()) { uiInterface.NotifyAlertChanged(GetHash(), CT_NEW); std::string strCmd = GetArg("-alertnotify", ""); if (!strCmd.empty()) { // Alert text should be plain ascii coming from a trusted source, but to // be safe we first strip anything not in safeChars, then add single quotes around // the whole string before passing it to the shell: std::string singleQuote("'"); // safeChars chosen to allow simple messages/URLs/email addresses, but avoid anything // even possibly remotely dangerous like & or > std::string safeChars("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890 .,;_/:?@"); std::string safeStatus; for (std::string::size_type i = 0; i < strStatusBar.size(); i++) { if (safeChars.find(strStatusBar[i]) != std::string::npos) safeStatus.push_back(strStatusBar[i]); } safeStatus = singleQuote+safeStatus+singleQuote; boost::replace_all(strCmd, "%s", safeStatus); if (fThread) boost::thread t(runCommand, strCmd); // thread runs free else runCommand(strCmd); } } } printf("accepted alert %d, AppliesToMe()=%d\n", nID, AppliesToMe()); return true; }