void PublicKey::Parse(const std::string& signature) throw (DKIM::PermanentError) { m_tagList.Parse(signature); /** * Validate Signature according to RFC-6376 */ // Version TagListEntry v; if (m_tagList.GetTag("v", v)) { if (v.GetValue() != "DKIM1") throw DKIM::PermanentError(StringFormat("Unsupported version %s (v supports DKIM1)", v.GetValue().c_str() ) ); } // Acceptable hash algorithms TagListEntry h; if (m_tagList.GetTag("h", h)) { if (h.GetValue().empty()) throw DKIM::PermanentError("Acceptable hash algorithms is empty (h)"); std::list<std::string> algo = DKIM::Tokenizer::ValueList(h.GetValue()); for (std::list<std::string>::const_iterator a = algo.begin(); a != algo.end(); ++a) { if (*a == "sha256") m_digestAlgorithms.push_back(DKIM_A_SHA256); else if (*a == "sha1") m_digestAlgorithms.push_back(DKIM_A_SHA1); } } // Key type TagListEntry k; if (m_tagList.GetTag("k", k)) { if (k.GetValue() == "rsa") m_signatureAlgorithm = DKIM_SA_RSA; else if (k.GetValue() == "ed25519") m_signatureAlgorithm = DKIM_SA_ED25519; else throw DKIM::PermanentError(StringFormat("Unsupported key type %s (k supports rsa and ed25519)", k.GetValue().c_str() ) ); } // Public-key data TagListEntry p; if (!m_tagList.GetTag("p", p)) throw DKIM::PermanentError("Missing public key (p)"); if (p.GetValue().empty()) throw DKIM::PermanentError("Public key is revoked (p)"); std::string ptmp = p.GetValue(); ptmp.erase(remove_if(ptmp.begin(), ptmp.end(), isspace), ptmp.end()); switch (m_signatureAlgorithm) { case DKIM_SA_RSA: { std::string tmp = Base64_Decode(ptmp); const unsigned char *tmp2 = (const unsigned char*)tmp.c_str(); EVP_PKEY* publicKey = d2i_PUBKEY(nullptr, &tmp2, tmp.size()); if (publicKey == nullptr) throw DKIM::PermanentError("Public key could not be loaded (invalid DER data)"); #if OPENSSL_VERSION_NUMBER < 0x10100000 if (publicKey->type != EVP_PKEY_RSA && publicKey->type != EVP_PKEY_RSA2) #else if (EVP_PKEY_base_id(publicKey) != EVP_PKEY_RSA && EVP_PKEY_base_id(publicKey) != EVP_PKEY_RSA2) #endif { EVP_PKEY_free(publicKey); throw DKIM::PermanentError("Public key could not be loaded (key type must be RSA/RSA2)"); } m_publicKeyRSA = EVP_PKEY_get1_RSA(publicKey); EVP_PKEY_free(publicKey); } break; case DKIM_SA_ED25519: { std::string tmp = Base64_Decode(ptmp); if (tmp.size() != 32) throw DKIM::PermanentError("Public ed25519 key could not be loaded"); m_publicKeyED25519 = tmp; } break; } // Service Type TagListEntry s; if (m_tagList.GetTag("s", s)) { if (s.GetValue().empty()) throw DKIM::PermanentError("Service type is empty (s)"); std::list<std::string> type = DKIM::Tokenizer::ValueList(s.GetValue()); for (std::list<std::string>::const_iterator a = type.begin(); a != type.end(); ++a) { if (*a == "email") m_serviceType.push_back(DKIM_S_EMAIL); else if (*a == "*") { m_serviceType.clear(); break; } } } // Flags TagListEntry t; if (m_tagList.GetTag("t", t)) { m_flags = DKIM::Tokenizer::ValueList(t.GetValue()); } return; }
/* * GetADSP() * * Get ADSP status for message rfc5617 */ void Validatory::GetADSP(std::list<DKIM::ADSP>& adsp) throw (DKIM::PermanentError, DKIM::TemporaryError) { /** * Start by collecting all From: <> addresses (this runtime-time free...) */ std::list<std::string> senders; for (DKIM::Message::HeaderList::const_iterator i = m_msg.GetHeaders().begin(); i != m_msg.GetHeaders().end(); ++i) { std::string headerName = (*i)->GetName(); transform(headerName.begin(), headerName.end(), headerName.begin(), tolower); // find from: <...> header(s)... if (headerName == "from") { std::string header = (*i)->GetHeader().substr((*i)->GetValueOffset()); header = DKIM::Conversion::EncodedWord::Decode(header); std::list<std::string> addrlist = DKIM::Tokenizer::ParseAddressList(header); for (std::list<std::string>::const_iterator aIter = addrlist.begin(); aIter != addrlist.end(); ++aIter) { // if no address is claimed to follow a ADSP, try the next one if (aIter->empty()) continue; size_t atSign = aIter->rfind("@"); if (atSign == std::string::npos) throw DKIM::PermanentError("Found invalid sender address: " + *aIter); std::string host = aIter->substr(atSign + 1); transform(host.begin(), host.end(), host.begin(), tolower); senders.push_back(host); } } } std::map<std::string, std::pair<int, std::string> > dkimResult; for (Validatory::SignatureList::const_iterator i = GetSignatures().begin(); i != GetSignatures().end(); ++i) { DKIM::PublicKey pub; DKIM::Signature sig; try { GetSignature(i, sig); GetPublicKey(sig, pub); CheckSignature(i, sig, pub); dkimResult[sig.GetDomain()] = std::make_pair(1, "pass"); } catch (DKIM::TemporaryError& e) { dkimResult[sig.GetDomain()] = std::make_pair(-1, e.what()); } catch (DKIM::PermanentError& e) { if (pub.SoftFail()) dkimResult[sig.GetDomain()] = std::make_pair(1, e.what()); else dkimResult[sig.GetDomain()] = std::make_pair(0, e.what()); } } for (std::list<std::string>::const_iterator i = senders.begin(); i != senders.end(); ++i) { std::string query = "_adsp._domainkey." + *i; std::string adspRecord; ADSP tmp; tmp.SetDomain(*i); std::map<std::string, std::pair<int, std::string> >::const_iterator dIter = dkimResult.find(*i); if (dIter != dkimResult.end() && dIter->second.first == -1) { tmp.SetResult(ADSP::DKIM_ADSP_TEMPERROR, dIter->second.second); } else if (dIter != dkimResult.end() && dIter->second.first == 1) { tmp.SetResult(ADSP::DKIM_ADSP_PASS, dIter->second.second); } else { std::string error; if (dIter != dkimResult.end()) error = dIter->second.second; else error = "no dkim signature found for d=" + *i; if ((CustomDNSResolver? CustomDNSResolver(query, adspRecord, CustomDNSData): DKIM::Util::Resolver().GetTXT(query, adspRecord) )) { // only bad queries goes here.. if (!adspRecord.empty()) { try { TagList tagList; tagList.Parse(adspRecord); TagListEntry v; if (tagList.GetTag("dkim", v)) { if (v.GetValue() == "all") { tmp.SetResult(ADSP::DKIM_ADSP_FAIL, error); } else if (v.GetValue() == "discardable") { tmp.SetResult(ADSP::DKIM_ADSP_DISCARD, error); } else { tmp.SetResult(ADSP::DKIM_ADSP_UNKNOWN, error); } } else { tmp.SetResult(ADSP::DKIM_ADSP_UNKNOWN, error); } } catch (DKIM::TemporaryError& e) { tmp.SetResult(ADSP::DKIM_ADSP_TEMPERROR, e.what()); } catch (DKIM::PermanentError& e) { tmp.SetResult(ADSP::DKIM_ADSP_PERMERROR, e.what()); } } else { tmp.SetResult(ADSP::DKIM_ADSP_NONE, error); } } else { tmp.SetResult(ADSP::DKIM_ADSP_TEMPERROR, DKIM::Util::StringFormat("dns query failed for %s", query.c_str())); } } adsp.push_back(tmp); } }