예제 #1
0
/*
 * GetSignature()
 *
 * Get the signature from supported header iterator
 */
void Validatory::GetSignature(const Message::HeaderList::const_iterator& headerIter,
		DKIM::Signature& sig)	
	throw (DKIM::PermanentError)
{
	sig.Parse((*headerIter)->GetHeader().substr((*headerIter)->GetValueOffset()));

	// create signature for our body (message data)
	switch (sig.GetAlgorithm())
	{
		case DKIM::DKIM_A_SHA1:
			EVP_DigestInit(&m_ctx_body, EVP_sha1());
			break;
		case DKIM::DKIM_A_SHA256:
			EVP_DigestInit(&m_ctx_body, EVP_sha256());
			break;
	}

	CanonicalizationBody canonicalbody(sig.GetCanonModeBody());

	// if we should limit the size of the body we hash
	bool limitBody = sig.GetBodySizeLimit();
	size_t bodySize = sig.GetBodySize();

	// if we have a message: seek to GetBodyOffset()
	if (m_msg.GetBodyOffset() != -1)
	{
		m_file.clear();
		m_file.seekg(m_msg.GetBodyOffset(), std::istream::beg);

		std::string s;
		while (std::getline(m_file, s) || m_file.peek() != EOF)
		{
			// double dots (postfix file may have .., instead of .)
			if (m_doubleDots && s.substr(0, 2) == "..")
			{
				s.erase(0, 1);
			}

			// remove possible \r (if not removed by getline *probably not*)
			if (s.size() > 0 && s[s.size()-1] == '\r')
				s.erase(s.size()-1);

			// canonical body
			std::vector<std::string> output;
			if (canonicalbody.FilterLine(s, output))
			{
				for (std::vector<std::string>::const_iterator i = output.begin();
						i != output.end(); ++i)
				{
					if (limitBody && bodySize == 0) break;
#ifdef DEBUG
					if (*i == "\r\n") printf("[CRLF]\n");
					else printf("[%s]\n", i->c_str());
#endif
					EVP_DigestUpdate(&m_ctx_body, i->c_str(),
							limitBody?std::min(i->size(), bodySize):i->size());
					bodySize -= std::min(i->size(), bodySize);
				}
			}
		}
	}

	// else call (Done) -- which may insert a last CRLF if the body was empty
	if (m_msg.GetBodyOffset() != -1 || m_file.peek() == EOF)
	{
		std::vector<std::string> output;
		if (canonicalbody.Done(output))
		{
			for (std::vector<std::string>::const_iterator i = output.begin();
					i != output.end(); ++i)
			{
				if (limitBody && bodySize == 0) break;
#ifdef DEBUG
				if (*i == "\r\n") printf("[CRLF]\n");
				else printf("[%s]\n", i->c_str());
#endif
				EVP_DigestUpdate(&m_ctx_body, i->c_str(),
						limitBody?std::min(i->size(), bodySize):i->size());
				bodySize -= std::min(i->size(), bodySize);
			}
		}
	}

	unsigned char md_value[EVP_MAX_MD_SIZE];
	unsigned int md_len;
	EVP_DigestFinal_ex(&m_ctx_body, md_value, &md_len);
	EVP_MD_CTX_cleanup(&m_ctx_body);

	if (sig.GetBodyHash().size() != md_len ||
			memcmp(sig.GetBodyHash().c_str(), md_value, md_len) != 0)
	{
		throw DKIM::PermanentError("Body hash did not verify");
	}
}
예제 #2
0
/*
 * CheckSignature()
 *
 * Validate the message according to rfc4871
 */
void Validatory::CheckSignature(const Message::HeaderList::const_iterator& headerIter,
		const DKIM::Signature& sig,
		const DKIM::PublicKey& pub)
	throw (DKIM::PermanentError)
{
	// multiple signatures (must cleanup)
	EVP_MD_CTX_cleanup(&m_ctx_body);
	EVP_MD_CTX_cleanup(&m_ctx_head);

	// sanity checking (between sig and pub)
	if (pub.GetAlgorithms().size() > 0)
		if (find(pub.GetAlgorithms().begin(), pub.GetAlgorithms().end(), sig.GetAlgorithm()) == pub.GetAlgorithms().end())
			throw DKIM::PermanentError("Algorithm is not allowed");

	if (find(pub.GetFlags().begin(), pub.GetFlags().end(), "s") != pub.GetFlags().end())
		if (sig.GetDomain() != sig.GetMailDomain())
			throw DKIM::PermanentError("Domain must match sub-domain (flag s)");

	/*
	 If a DKIM verifier finds a selector record that has an empty "g" field ("g=;")
	 and it does not have a "v" field ("v=DKIM1;") at its beginning, it is faced with deciding if this record was

	 1. from a DK signer that transitioned to supporting DKIM but forgot to remove the "g"
	 	field (so that it could be used by both DK and DKIM verifiers), or
	 2. from a DKIM signer that truly meant to use the empty "g" field but forgot to put in
	 	the "v" field. It is RECOMMENDED that you treat such records using the first
		interpretation, and treat such records as if the signer did not have a "g" field in the record.
	*/

	// if we have a "g"-tag
	if (find(pub.GetFlags().begin(), pub.GetFlags().end(), "g") != pub.GetFlags().end())
	{
		// if it's empty... and we don't have a version...
		if (pub.GetMailLocalPart().empty() && find(pub.GetFlags().begin(), pub.GetFlags().end(), "v") == pub.GetFlags().end())
		{
			// do the RECOMMENDED interpretation and treat such records as if the signer did not have a "g" field in the record.
		} else
		{
			if (DKIM::Util::MatchWithWildCard(pub.GetMailLocalPart(), sig.GetMailLocalPart()) != true)
				throw DKIM::PermanentError("Unmatched local-part");
		}
	}

	// create signature for our header
	switch (sig.GetAlgorithm())
	{
		case DKIM::DKIM_A_SHA1:
			EVP_VerifyInit(&m_ctx_head, EVP_sha1());
			break;
		case DKIM::DKIM_A_SHA256:
			EVP_VerifyInit(&m_ctx_head, EVP_sha256());
			break;
	}

	CanonicalizationHeader canonicalhead(sig.GetCanonModeHeader());

	// add all headers to our cache (they will be pop of the end)
	std::map<std::string, Message::HeaderList> headerCache;
	for (Message::HeaderList::const_iterator hIter = m_msg.GetHeaders().begin();
			hIter != m_msg.GetHeaders().end();
			++hIter)
	{
		std::string headerName = (*hIter)->GetName();
		transform(headerName.begin(), headerName.end(), headerName.begin(), tolower);
		headerCache[headerName].push_back(*hIter);
	}

	// add all signed headers to our hash
	for (std::list<std::string>::const_iterator hIter = sig.GetSignedHeaders().begin();
			hIter != sig.GetSignedHeaders().end(); ++hIter)
	{
		std::string tmp;
		std::string name = *hIter;
		transform(name.begin(), name.end(), name.begin(), tolower);

		std::map<std::string, Message::HeaderList>::iterator head = headerCache.find(name);

		// if this occurred
		// 1. we do not have a header of that name at all
		// 2. all headers with that name has been included...
		if (head == headerCache.end() || head->second.size() == 0)
			continue;	

#ifdef DEBUG
		printf("[%s]\n", canonicalhead.FilterHeader(head->second.back()->GetHeader()).c_str());
		printf("[CRLF]\n");
#endif
		tmp = canonicalhead.FilterHeader(head->second.back()->GetHeader()) + "\r\n";
		head->second.pop_back();
		EVP_VerifyUpdate(&m_ctx_head, tmp.c_str(), tmp.size());
	}

	// add our dkim-signature to the calculation (remove the "b"-tag)
	std::string h = (*headerIter)->GetHeader().substr(0, (*headerIter)->GetValueOffset());
	std::string v = (*headerIter)->GetHeader().substr((*headerIter)->GetValueOffset());

	DKIM::TagListEntry bTag;
	sig.GetTag("b", bTag);
	v.erase((int)bTag.GetValueOffset(), bTag.GetValue().size());

	std::string tmp = canonicalhead.FilterHeader(h + v);
#ifdef DEBUG
	printf("[%s]\n", tmp.c_str());
#endif
	EVP_VerifyUpdate(&m_ctx_head, tmp.c_str(), tmp.size());

	// verify the header signature
	if (EVP_VerifyFinal(&m_ctx_head,
				(const unsigned char*)sig.GetSignatureData().c_str(),
				sig.GetSignatureData().size(),
				pub.GetPublicKey()
				) != 1)
		throw DKIM::PermanentError("Signature did not verify");
	EVP_MD_CTX_cleanup(&m_ctx_head);

	// success!
}