Beispiel #1
0
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;
}
Beispiel #2
0
/*
 * 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);
	}
}