1 /*****************************************************************************
2 *  Copyright 2005 Alt-N Technologies, Ltd.
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  This code incorporates intellectual property owned by Yahoo! and licensed
11 *  pursuant to the Yahoo! DomainKeys Patent License Agreement.
12 *
13 *  Unless required by applicable law or agreed to in writing, software
14 *  distributed under the License is distributed on an "AS IS" BASIS,
15 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 *  See the License for the specific language governing permissions and
17 *  limitations under the License.
18 *
19 *****************************************************************************/
20 
21 #ifdef WIN32
22 #include <windows.h>
23 #pragma warning( disable: 4786 )
24 #pragma warning( disable: 4503 )
25 #else
26 #define _strnicmp strncasecmp
27 #define _stricmp strcasecmp
28 #define LOWORD(l) ((unsigned)(l) & 0xffff)
29 #define HIWORD(l) ((unsigned)(l) >> 16)
30 #endif
31 
32 #include <string.h>
33 #include <map>
34 
35 #include "dkim.h"
36 #include "dkimsign.h"
37 
38 
CDKIMSign()39 CDKIMSign::CDKIMSign()
40 {
41 	m_EmptyLineCount = 0;
42 	m_pfnHdrCallback = NULL;
43 
44 	m_allman_sha1ctx = EVP_MD_CTX_create();
45 	m_Hdr_ietf_sha1ctx = EVP_MD_CTX_create();
46 	m_Hdr_ietf_sha256ctx = EVP_MD_CTX_create();
47 	m_Bdy_ietf_sha1ctx = EVP_MD_CTX_create();
48 	m_Bdy_ietf_sha256ctx = EVP_MD_CTX_create();
49 
50 	EVP_SignInit( m_allman_sha1ctx, EVP_sha1() );
51 	EVP_SignInit( m_Hdr_ietf_sha1ctx, EVP_sha1() );
52 	EVP_SignInit( m_Hdr_ietf_sha256ctx, EVP_sha256() );
53 	EVP_DigestInit( m_Bdy_ietf_sha1ctx, EVP_sha1() );
54 	EVP_DigestInit( m_Bdy_ietf_sha256ctx, EVP_sha256() );
55 }
56 
~CDKIMSign()57 CDKIMSign::~CDKIMSign()
58 {
59 	EVP_MD_CTX_destroy( m_allman_sha1ctx );
60 	EVP_MD_CTX_destroy( m_Hdr_ietf_sha1ctx );
61 	EVP_MD_CTX_destroy( m_Hdr_ietf_sha256ctx );
62 	EVP_MD_CTX_destroy( m_Bdy_ietf_sha1ctx );
63 	EVP_MD_CTX_destroy( m_Bdy_ietf_sha256ctx );
64 }
65 
66 ////////////////////////////////////////////////////////////////////////////////
67 //
68 // Init - save the options
69 //
70 ////////////////////////////////////////////////////////////////////////////////
Init(DKIMSignOptions * pOptions)71 int CDKIMSign::Init( DKIMSignOptions* pOptions )
72 {
73 	int nRet = CDKIMBase::Init();
74 
75 	m_Canon = pOptions->nCanon;
76 
77 	// as of draft 01, these are the only allowed signing types:
78 	if(     (m_Canon != DKIM_SIGN_SIMPLE_RELAXED)
79 		 && (m_Canon != DKIM_SIGN_RELAXED)
80 		 && (m_Canon != DKIM_SIGN_RELAXED_SIMPLE) )
81 	{
82 		m_Canon = DKIM_SIGN_SIMPLE;
83 	}
84 
85 	sSelector.assign( pOptions->szSelector );
86 
87 	m_pfnHdrCallback = pOptions->pfnHeaderCallback;
88 
89 	sDomain.assign( pOptions->szDomain );
90 
91 	m_IncludeBodyLengthTag = ( pOptions->nIncludeBodyLengthTag != 0 );
92 
93 	m_nBodyLength = 0;
94 
95 	m_ExpireTime = pOptions->expireTime;
96 
97 	sIdentity.assign( pOptions->szIdentity );
98 
99 	m_nIncludeTimeStamp = pOptions->nIncludeTimeStamp;
100 	m_nIncludeQueryMethod = pOptions->nIncludeQueryMethod;
101 	m_nIncludeCopiedHeaders = pOptions->nIncludeCopiedHeaders;
102 	m_nIncludeBodyHash = pOptions->nIncludeBodyHash;
103 
104 	// NOTE: the following line is not backwards compatible with MD 8.0.3
105 	// because the szRequiredHeaders member was added after the release
106 	//sRequiredHeaders.assign( pOptions->szRequiredHeaders );
107 
108 	//make sure there is a colon after the last header in the list
109 	if( (sRequiredHeaders.size() > 0) && sRequiredHeaders.at(sRequiredHeaders.size()-1) != ':' )
110 	{
111 		sRequiredHeaders.append( ":" );
112 	}
113 
114 	m_nHash = pOptions->nHash;
115 
116 	m_bReturnedSigAssembled = false;
117 
118 	m_sCopiedHeaders.erase();
119 
120 	return nRet;
121 }
122 
123 //FILE* fpdebug = NULL;
124 
125 
126 ////////////////////////////////////////////////////////////////////////////////
127 //
128 // Hash - update the hash
129 //
130 ////////////////////////////////////////////////////////////////////////////////
Hash(const char * szBuffer,int nBufLength,bool bHdr,bool bAllmanOnly)131 void CDKIMSign::Hash( const char* szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly )
132 {
133 	/** START DEBUG CODE **
134 	if( nBufLength == 2 && szBuffer[0] == '\r' && szBuffer[1] == '\n' )
135 	{
136 		printf( "[CRLF]\n" );
137 	}
138 	else
139 	{
140 		char* szDbg = new char[nBufLength+1];
141 		strncpy( szDbg, szBuffer, nBufLength );
142 		szDbg[nBufLength] = '\0';
143 		printf( "[%s]\n", szDbg );
144 	} ***
145 
146 	if( fpdebug == NULL )
147 	{
148 		fpdebug = fopen( "canon.msg", "wb" );
149 	}
150 
151 	fwrite( szBuffer, 1, nBufLength, fpdebug );
152 
153 	/** END DEBUG CODE **/
154 
155 	if( bAllmanOnly )
156 	{
157 		if( m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1 )
158 		{
159 			EVP_SignUpdate( m_allman_sha1ctx, szBuffer, nBufLength );
160 		}
161 	}
162 	else
163 	{
164 		if( m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1 )
165 		{
166 			EVP_SignUpdate( m_allman_sha1ctx, szBuffer, nBufLength );
167 		}
168 		else if( m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1 )
169 		{
170 			if( m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1 )
171 			{
172 				EVP_SignUpdate( m_allman_sha1ctx, szBuffer, nBufLength );
173 			}
174 			if( m_nHash & DKIM_HASH_SHA256 )
175 			{
176 				if( bHdr )
177 					EVP_SignUpdate( m_Hdr_ietf_sha256ctx, szBuffer, nBufLength );
178 				else
179 					EVP_DigestUpdate( m_Bdy_ietf_sha256ctx, szBuffer, nBufLength );
180 			}
181 			if( m_nHash != DKIM_HASH_SHA256 )
182 			{
183 				if( bHdr )
184 					EVP_SignUpdate( m_Hdr_ietf_sha1ctx, szBuffer, nBufLength );
185 				else
186 					EVP_DigestUpdate( m_Bdy_ietf_sha1ctx, szBuffer, nBufLength );
187 			}
188 		}
189 	}
190 }
191 
192 
193 ////////////////////////////////////////////////////////////////////////////////
194 //
195 // SignThisTag - return boolean whether or not to sign this tag
196 //
197 ////////////////////////////////////////////////////////////////////////////////
SignThisTag(const string & sTag)198 bool CDKIMSign::SignThisTag( const string& sTag )
199 {
200 	bool bRet = true;
201 
202 	if( _strnicmp( sTag.c_str(), "X-", 2 ) == 0 ||
203 		_stricmp( sTag.c_str(), "Authentication-Results:" ) == 0 ||
204 		_stricmp( sTag.c_str(), "Return-Path:" ) == 0 )
205 	{
206 		bRet = false;
207 	}
208 
209 	return bRet;
210 }
211 
212 
ConvertHeaderToQuotedPrintable(const char * source,char * dest)213 bool ConvertHeaderToQuotedPrintable(const char *source, char* dest)
214 {
215 	bool bConvert = false;
216 
217 	// do quoted printable
218 	static unsigned char hexchars[16] = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
219 
220 	unsigned char *d = (unsigned char*)dest;
221 	for (const unsigned char *s = (const unsigned char *)source; *s != '\0'; s++)
222 	{
223 		if (*s >= 33 && *s <= 126 && *s != '=' && *s != ':' && *s != ';' && *s != '|')
224 		{
225 			*d++ = *s;
226 		}
227 		else
228 		{
229 			bConvert = true;
230 			*d++ = '=';
231 			*d++ = hexchars[*s >> 4];
232 			*d++ = hexchars[*s & 15];
233 		}
234 	}
235 	*d = '\0';
236 
237 	return bConvert;
238 }
239 
240 ////////////////////////////////////////////////////////////////////////////////
241 //
242 // GetHeaderParams - Extract any needed header parameters
243 //
244 ////////////////////////////////////////////////////////////////////////////////
GetHeaderParams(const string & sHdr)245 void CDKIMSign::GetHeaderParams( const string& sHdr )
246 {
247 	if ( _strnicmp( sHdr.c_str(), "X", 1) == 0)
248 		return;
249 
250 	if( _strnicmp( sHdr.c_str(), "From:", 5 ) == 0 )
251 	{
252 		sFrom.assign( sHdr.c_str() + 5 );
253 	}
254 	if( _strnicmp( sHdr.c_str(), "Sender:", 7 ) == 0 )
255 	{
256 		sSender.assign( sHdr.c_str() + 7 );
257 	}
258 
259 	if( m_nIncludeCopiedHeaders )
260 	{
261 		string::size_type pos = sHdr.find( ':' );
262 
263 		if( pos != string::npos )
264 		{
265 			string sTag, sValue;
266 			char* workBuffer = new char[sHdr.size() * 3 + 1];
267 
268 			sTag.assign( sHdr.substr( 0, pos ) );
269 			sValue.assign( sHdr.substr( pos+1, string::npos ) );
270 
271 			ConvertHeaderToQuotedPrintable( sTag.c_str(), workBuffer );
272 
273 			if( !m_sCopiedHeaders.empty() )
274 			{
275 				m_sCopiedHeaders.append( "|" );
276 			}
277 
278 			m_sCopiedHeaders.append( workBuffer );
279 			m_sCopiedHeaders.append( ":" );
280 
281 			ConvertHeaderToQuotedPrintable( sValue.c_str(), workBuffer );
282 
283 			m_sCopiedHeaders.append( workBuffer );
284 
285 			delete[] workBuffer;
286 		}
287 	}
288 }
289 
290 
291 ////////////////////////////////////////////////////////////////////////////////
292 //
293 // ProcessHeaders - sign headers and save needed parameters
294 //
295 ////////////////////////////////////////////////////////////////////////////////
ProcessHeaders(void)296 int CDKIMSign::ProcessHeaders(void)
297 {
298 	map<string,list<string>::reverse_iterator> IterMap;
299 	map<string,list<string>::reverse_iterator>::iterator IterMapIter;
300 	list<string>::reverse_iterator riter;
301 	list<string>::iterator iter;
302 	string sTag;
303 	bool bFromHeaderFound = false;
304 
305 	// walk the header list
306 	for( iter = HeaderList.begin(); iter != HeaderList.end(); iter++ )
307 	{
308 		sTag.assign( *iter );
309 
310 		// look for a colon
311 		string::size_type pos = sTag.find( ':' );
312 
313 		if (pos != string::npos)
314 		{
315 			int nSignThisTag = 1;
316 
317 			// hack off anything past the colon
318 			sTag.erase( pos + 1, string::npos );
319 
320 			// is this the From: header?
321 			if( _stricmp( sTag.c_str(), "From:" ) == 0 )
322 			{
323 				bFromHeaderFound = true;
324 				nSignThisTag = 1;
325 				IsRequiredHeader( sTag );  // remove from required header list
326 			}
327 			// is this in the list of headers that must be signed?
328 			else if( IsRequiredHeader( sTag ) )
329 			{
330 				nSignThisTag = 1;
331 			}
332 			else
333 			{
334 				if( m_pfnHdrCallback )
335 				{
336 					nSignThisTag = m_pfnHdrCallback( iter->c_str() );
337 				}
338 				else
339 				{
340 					nSignThisTag = SignThisTag( sTag ) ? 1 : 0;
341 				}
342 			}
343 
344 			// save header parameters
345 			GetHeaderParams( *iter );
346 
347 			if( nSignThisTag > 0 )
348 			{
349 				// add this tag to h=
350 				hParam.append( sTag );
351 
352 				IterMapIter = IterMap.find( sTag );
353 
354 				riter = ( IterMapIter == IterMap.end() ) ? HeaderList.rbegin() : IterMapIter->second;
355 
356 				// walk the list in reverse looking for the last instance of this header
357 				while ( riter != HeaderList.rend() )
358 				{
359 					if( _strnicmp( riter->c_str(), sTag.c_str(), sTag.size() ) == 0 )
360 					{
361 						ProcessHeader( *riter );
362 
363 						// save the reverse iterator position for this tag
364 						riter++;
365 						IterMap[sTag] = riter;
366 						break;
367 					}
368 					riter++;
369 				}
370 			}
371 		}
372 	}
373 
374 	Hash( "\r\n", 2, true, true ); // only for Allman sig
375 
376 	if( !bFromHeaderFound )
377 	{
378 		string sFrom( "From:" );
379 		hParam.append( sFrom );
380 		IsRequiredHeader( sFrom ); // remove from required header list
381 //		Hash( "\r\n", 2 );
382 	}
383 
384 	hParam.append( sRequiredHeaders );
385 
386 //	string::size_type end = sRequiredHeaders.find( ':' );
387 //	while (end != string::npos)
388 //	{
389 //		Hash( "\r\n", 2 );
390 //		end = sRequiredHeaders.find( ':', end+1 );
391 //	}
392 
393 	// remove the last colon from h=
394 	if( hParam.at( hParam.size() - 1 ) == ':' )
395 		hParam.erase( hParam.size() - 1, string::npos );
396 
397 	return DKIM_SUCCESS;
398 }
399 
400 
401 
ProcessHeader(const string & sHdr)402 void CDKIMSign::ProcessHeader( const string& sHdr )
403 {
404 	switch( HIWORD( m_Canon ) )
405 	{
406 	case DKIM_CANON_SIMPLE:
407 		Hash( sHdr.c_str(), sHdr.size(), true );
408 		Hash( "\r\n", 2, true );
409 		break;
410 
411 	case DKIM_CANON_NOWSP:
412 		{
413 			string sTemp = sHdr;
414 			RemoveSWSP( sTemp );
415 
416 			// convert characters before ':' to lower case
417 			for( char* s = (char*)sTemp.c_str(); *s != '\0' && *s != ':'; s++ )
418 			{
419 				if( *s >= 'A' && *s <= 'Z' )
420 					*s += 'a'-'A';
421 			}
422 
423 			Hash( sTemp.c_str(), sTemp.size(), true );
424 			Hash( "\r\n", 2, true );
425 		}
426 		break;
427 
428 	case DKIM_CANON_RELAXED:
429 		{
430 			string sTemp = RelaxHeader( sHdr );
431 			Hash( sTemp.c_str(), sTemp.length(), true );
432 			Hash( "\r\n", 2, true );
433 		}
434 		break;
435 	}
436 }
437 
438 
ProcessBody(char * szBuffer,int nBufLength,bool bEOF)439 int CDKIMSign::ProcessBody( char* szBuffer, int nBufLength, bool bEOF )
440 {
441 	switch( LOWORD( m_Canon ) )
442 	{
443 	case DKIM_CANON_SIMPLE:
444 		if( nBufLength > 0 )
445 		{
446 			while( m_EmptyLineCount > 0 )
447 			{
448 				Hash( "\r\n", 2, false );
449 				m_nBodyLength += 2;
450 				m_EmptyLineCount--;
451 			}
452 			Hash( szBuffer, nBufLength, false );
453 			Hash( "\r\n", 2, false );
454 			m_nBodyLength += nBufLength + 2;
455 		}
456 		else
457 		{
458 			m_EmptyLineCount++;
459 			if( bEOF )
460 			{
461 				Hash( "\r\n", 2, false );
462 				m_nBodyLength += 2;
463 			}
464 		}
465 		break;
466 
467 	case DKIM_CANON_NOWSP:
468 		RemoveSWSP( szBuffer, nBufLength );
469 		if( nBufLength > 0 )
470 		{
471 			Hash( szBuffer, nBufLength, false );
472 			m_nBodyLength += nBufLength;
473 		}
474 		break;
475 
476 	case DKIM_CANON_RELAXED:
477 		CompressSWSP( szBuffer, nBufLength );
478 		if( nBufLength > 0 )
479 		{
480 			while( m_EmptyLineCount > 0 )
481 			{
482 				Hash( "\r\n", 2, false );
483 				m_nBodyLength += 2;
484 				m_EmptyLineCount--;
485 			}
486 			Hash( szBuffer, nBufLength, false );
487 			m_nBodyLength += nBufLength;
488 			if ( !bEOF )
489 			{
490 				Hash( "\r\n", 2, false );
491 				m_nBodyLength += 2;
492 			}
493 		}
494 		else
495 			m_EmptyLineCount++;
496 		break;
497 	}
498 
499 	return DKIM_SUCCESS;
500 }
501 
ParseFromAddress(void)502 bool CDKIMSign::ParseFromAddress( void )
503 {
504 	string::size_type pos;
505 	string sAddress;
506 
507 	if( !sFrom.empty() )
508 	{
509 		sAddress.assign( sFrom );
510 	}
511 	else if( !sSender.empty() )
512 	{
513 		sAddress.assign( sSender );
514 	}
515 	else
516 	{
517 		return false;
518 	}
519 
520 	// simple for now, beef it up later
521 
522 	// remove '<' and anything before it
523 	pos = sAddress.find( '<' );
524 	if( pos != string::npos )
525 		sAddress.erase( 0, pos );
526 
527 	// remove '>' and anything after it
528 	pos = sAddress.find( '>' );
529 	if( pos != string::npos )
530 		sAddress.erase( pos, string::npos );
531 
532 	// look for '@' symbol
533 	pos = sAddress.find( '@' );
534 	if( pos == string::npos )
535 		return false;
536 
537 	if( sDomain.empty() )
538 	{
539 		sDomain.assign( sAddress.c_str() + pos + 1 );
540 		RemoveSWSP( sDomain );
541 	}
542 
543 	return true;
544 }
545 
546 ////////////////////////////////////////////////////////////////////////////////
547 //
548 // InitSig - initialize signature folding algorithm
549 //
550 ////////////////////////////////////////////////////////////////////////////////
InitSig(void)551 void CDKIMSign::InitSig(void)
552 {
553 	m_sSig.reserve( 1024 );
554 	m_sSig.assign( "DKIM-Signature:" );
555 	m_nSigPos = m_sSig.size();
556 }
557 
558 ////////////////////////////////////////////////////////////////////////////////
559 //
560 // AddTagToSig - add tag and value to signature folding if necessary
561 //               if bFold, fold at cbrk char
562 //
563 ////////////////////////////////////////////////////////////////////////////////
AddTagToSig(char * Tag,const string & sValue,char cbrk,bool bFold)564 void CDKIMSign::AddTagToSig( char* Tag, const string &sValue, char cbrk, bool bFold )
565 {
566 	int nTagLen = strlen(Tag);
567 
568 	AddInterTagSpace( (!bFold) ? sValue.size() + nTagLen + 2 : nTagLen + 2 );
569 
570 	m_sSig.append( Tag );
571 	m_sSig.append( "=" );
572 	m_nSigPos += 1 + nTagLen;
573 
574 	if( !bFold )
575 	{
576 		m_sSig.append( sValue );
577 		m_nSigPos += sValue.size();
578 	}
579 	else
580 	{
581 		AddFoldedValueToSig( sValue, cbrk );
582 	}
583 	m_sSig.append( ";" );
584 	m_nSigPos++;
585 }
586 
587 ////////////////////////////////////////////////////////////////////////////////
588 //
589 // AddTagToSig - add tag and numeric value to signature folding if necessary
590 //
591 ////////////////////////////////////////////////////////////////////////////////
AddTagToSig(char * Tag,unsigned long nValue)592 void CDKIMSign::AddTagToSig( char* Tag, unsigned long nValue )
593 {
594 	char szValue[64];
595 	sprintf( szValue, "%u", nValue );
596 	AddTagToSig( Tag, szValue, 0, false );
597 }
598 
599 ////////////////////////////////////////////////////////////////////////////////
600 //
601 // AddInterTagSpace - add space or fold here
602 //
603 ////////////////////////////////////////////////////////////////////////////////
AddInterTagSpace(int nSizeOfNextTag)604 void CDKIMSign::AddInterTagSpace( int nSizeOfNextTag )
605 {
606 	if( m_nSigPos + nSizeOfNextTag + 1 > OptimalHeaderLineLength )
607 	{
608 		m_sSig.append( "\r\n\t" );
609 		m_nSigPos = 1;
610 	}
611 	else
612 	{
613 		m_sSig.append( " " );
614 		m_nSigPos++;
615 	}
616 }
617 
618 ////////////////////////////////////////////////////////////////////////////////
619 //
620 // AddTagToSig - add value to signature folding if necessary
621 //               if cbrk == 0 fold anywhere, otherwise fold only at cbrk
622 //
623 ////////////////////////////////////////////////////////////////////////////////
AddFoldedValueToSig(const string & sValue,char cbrk)624 void CDKIMSign::AddFoldedValueToSig( const string &sValue, char cbrk )
625 {
626 	string::size_type pos = 0;
627 
628 	if( cbrk == 0 )
629 	{
630 		// fold anywhere
631 		while( pos < sValue.size() )
632 		{
633 			string::size_type len = OptimalHeaderLineLength - m_nSigPos;
634 			if( len > sValue.size() - pos )
635 				len = sValue.size() - pos;
636 			m_sSig.append( sValue.substr( pos, len ) );
637 			m_nSigPos += len;
638 			pos += len;
639 
640 			if( pos < sValue.size() )
641 			{
642 				m_sSig.append( "\r\n\t" );
643 				m_nSigPos = 1;
644 			}
645 		}
646 	}
647 	else
648 	{
649 		// fold only at cbrk
650 		while( pos < sValue.size() )
651 		{
652 			string::size_type len = OptimalHeaderLineLength - m_nSigPos;
653 			string::size_type brkpos;
654 
655 			if( sValue.size() - pos < len )
656 			{
657 				brkpos = sValue.size() - 1;
658 			}
659 			else
660 			{
661 				brkpos = sValue.rfind( cbrk, pos + len );
662 			}
663 
664 			if( brkpos == string::npos || brkpos < pos )
665 			{
666 				brkpos = sValue.find( cbrk, pos );
667 				if( brkpos == string::npos )
668 				{
669 					brkpos = sValue.size();
670 				}
671 			}
672 
673 			len = brkpos - pos + 1;
674 
675 			m_sSig.append( sValue.substr( pos, len ) );
676 
677 			m_nSigPos += len;
678 			pos += len;
679 
680 			if( pos < sValue.size() )
681 			{
682 				m_sSig.append( "\r\n\t" );
683 				m_nSigPos = 1;
684 			}
685 		}
686 	}
687 }
688 
689 
690 ////////////////////////////////////////////////////////////////////////////////
691 //
692 // GetSig - compute hash and return signature header in szSignature
693 //
694 ////////////////////////////////////////////////////////////////////////////////
GetSig(char * szPrivKey,char * szSignature,int nSigLength)695 int CDKIMSign::GetSig( char* szPrivKey, char* szSignature, int nSigLength )
696 {
697 	if( szPrivKey == NULL )
698 	{
699 		return DKIM_BAD_PRIVATE_KEY;
700 	}
701 
702 	if( szSignature == NULL )
703 	{
704 		return DKIM_BUFFER_TOO_SMALL;
705 	}
706 
707 	int nRet = AssembleReturnedSig( szPrivKey );
708 
709 	if( nRet != DKIM_SUCCESS )
710 		return nRet;
711 
712 	if( m_sReturnedSig.size() + 1 < nSigLength )
713 	{
714 		strcpy( szSignature, m_sReturnedSig.c_str() );
715 	}
716 	else
717 	{
718 		return DKIM_BUFFER_TOO_SMALL;
719 	}
720 
721 	return DKIM_SUCCESS;
722 }
723 
724 
725 ////////////////////////////////////////////////////////////////////////////////
726 //
727 // GetSig - compute hash and return signature header in szSignature
728 //
729 ////////////////////////////////////////////////////////////////////////////////
GetSig2(char * szPrivKey,char ** pszSignature)730 int CDKIMSign::GetSig2( char* szPrivKey, char** pszSignature )
731 {
732 	if( szPrivKey == NULL )
733 	{
734 		return DKIM_BAD_PRIVATE_KEY;
735 	}
736 
737 	if( pszSignature == NULL )
738 	{
739 		return DKIM_BUFFER_TOO_SMALL;
740 	}
741 
742 	int nRet = AssembleReturnedSig( szPrivKey );
743 
744 	if( nRet != DKIM_SUCCESS )
745 		return nRet;
746 
747 	*pszSignature = (char*)m_sReturnedSig.c_str();
748 
749 	return DKIM_SUCCESS;
750 
751 }
752 
753 
754 ////////////////////////////////////////////////////////////////////////////////
755 //
756 // IsRequiredHeader - Check if header in required list. If so, delete
757 //                    header from list.
758 //
759 ////////////////////////////////////////////////////////////////////////////////
IsRequiredHeader(const string & sTag)760 bool CDKIMSign::IsRequiredHeader( const string& sTag )
761 {
762 	string::size_type start = 0;
763 	string::size_type end = sRequiredHeaders.find( ':' );
764 
765 	while (end != string::npos)
766 	{
767 		// check for a zero-length header
768 		if( start == end )
769 		{
770 			sRequiredHeaders.erase( start, 1 );
771 		}
772 		else
773 		{
774 			if( _stricmp( sTag.c_str(), sRequiredHeaders.substr( start, end - start + 1 ).c_str() ) == 0 )
775 			{
776 				sRequiredHeaders.erase( start, end - start + 1 );
777 				return true;
778 			}
779 			else
780 			{
781 				start = end + 1;
782 			}
783 		}
784 
785 		end = sRequiredHeaders.find( ':', start );
786 	}
787 
788 	return false;
789 }
790 
791 
792 
ConstructSignature(char * szPrivKey,bool bUseIetfBodyHash,bool bUseSha256)793 int CDKIMSign::ConstructSignature( char* szPrivKey, bool bUseIetfBodyHash, bool bUseSha256 )
794 {
795 	string sSignedSig;
796 	unsigned char* sig;
797     EVP_PKEY *pkey;
798     BIO *bio, *b64;
799 	unsigned int siglen;
800 	int size;
801 	int len;
802 	char* buf;
803 	int pos = 0;
804 
805 	// construct the DKIM-Signature: header and add to hash
806 	InitSig();
807 
808 	if( bUseIetfBodyHash )
809 	{
810 		AddTagToSig( "v", "1", 0, false );
811 	}
812 
813 	AddTagToSig( "a", bUseSha256 ? "rsa-sha256" : "rsa-sha1", 0, false );
814 
815 	switch( m_Canon )
816 	{
817 	case DKIM_SIGN_SIMPLE:
818 		AddTagToSig( "c", "simple", 0, false );
819 		break;
820 	case DKIM_SIGN_SIMPLE_RELAXED:
821 		AddTagToSig( "c", "simple/relaxed", 0, false );
822 		break;
823 	case DKIM_SIGN_RELAXED:
824 		AddTagToSig( "c", "relaxed/relaxed", 0, false );
825 		break;
826 	case DKIM_SIGN_RELAXED_SIMPLE:
827 		AddTagToSig( "c", "relaxed", 0, false );
828 		break;
829 	}
830 
831 	AddTagToSig( "d", sDomain, 0, false );
832 
833 	AddTagToSig( "s", sSelector, 0, false );
834 
835 	if( m_IncludeBodyLengthTag )
836 	{
837 		AddTagToSig( "l", m_nBodyLength );
838 	}
839 
840 	if( m_nIncludeTimeStamp != 0 )
841 	{
842 		time_t t;
843 		time( &t );
844 		AddTagToSig( "t", t );
845 	}
846 
847 	if( m_ExpireTime != 0 )
848 	{
849 		AddTagToSig( "x", m_ExpireTime );
850 	}
851 
852 	if( !sIdentity.empty() )
853 	{
854 		AddTagToSig( "i", sIdentity, 0, false );
855 	}
856 
857 	if( m_nIncludeQueryMethod )
858 	{
859 		AddTagToSig( "q", bUseIetfBodyHash ? "dns/txt" : "dns" , 0, false );
860 	}
861 
862 	AddTagToSig( "h", hParam, ':', true );
863 
864 	if( m_nIncludeCopiedHeaders )
865 	{
866 		AddTagToSig( "z", m_sCopiedHeaders, 0, true );
867 	}
868 
869 	if( bUseIetfBodyHash )
870 	{
871 		unsigned char Hash[EVP_MAX_MD_SIZE];
872 		unsigned int nHashLen = 0;
873 
874 		EVP_DigestFinal( bUseSha256 ? m_Bdy_ietf_sha256ctx : m_Bdy_ietf_sha1ctx, Hash, &nHashLen );
875 
876 		bio = BIO_new(BIO_s_mem());
877 		if (!bio) {
878 		  return DKIM_OUT_OF_MEMORY;
879 		}
880 		b64 = BIO_new(BIO_f_base64());
881 		if (!b64)
882 		{
883 			BIO_free(bio);
884 			return DKIM_OUT_OF_MEMORY;
885 		}
886 		BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
887 		BIO_push(b64, bio);
888 		if (BIO_write(b64, Hash, nHashLen) < nHashLen)
889 		{
890 		  BIO_free_all(b64);
891 		  return DKIM_OUT_OF_MEMORY;
892 		}
893 		BIO_flush(b64);
894 
895 		len = nHashLen * 2;
896 		buf = new char[len];
897 
898 		if( buf == NULL )
899 		{
900 			BIO_free_all(b64);
901 			return DKIM_OUT_OF_MEMORY;
902 		}
903 
904 		size = BIO_read(bio, buf, len);
905 		BIO_free_all(b64);
906 
907 		// this should never happen
908 		if (size >= len)
909 		{
910 			delete[] buf;
911 			return DKIM_OUT_OF_MEMORY;
912 		}
913 
914 		buf[size] = '\0';
915 
916 		AddTagToSig( "bh", buf, 0, true );
917 
918 		delete[] buf;
919 	}
920 
921 	AddInterTagSpace( 3 );
922 
923 	m_sSig.append( "b=" );
924 	m_nSigPos += 2;
925 
926 	// Force a full copy - no reference copies please
927 	sSignedSig.assign( m_sSig.c_str() );
928 
929 	// note that since we're not calling hash here, need to dump this
930 	// to the debug file if you want the full canonical form
931 
932 	string sTemp;
933 
934 	if( HIWORD(m_Canon) == DKIM_CANON_RELAXED )
935 	{
936 		sTemp = RelaxHeader( sSignedSig );
937 	}
938 	else
939 	{
940 		sTemp = sSignedSig.c_str();
941 	}
942 
943 	if( bUseIetfBodyHash )
944 	{
945 		EVP_SignUpdate( bUseSha256 ? m_Hdr_ietf_sha256ctx : m_Hdr_ietf_sha1ctx, sTemp.c_str(), sTemp.size() );
946 	}
947 	else
948 	{
949 		EVP_SignUpdate( m_allman_sha1ctx, sTemp.c_str(), sTemp.size() );
950 	}
951 
952 	bio = BIO_new_mem_buf(szPrivKey, -1);
953 	if( bio == NULL )
954 		return DKIM_OUT_OF_MEMORY;
955 
956     pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
957     BIO_free(bio);
958 
959     if (!pkey)
960 	{
961 		return DKIM_BAD_PRIVATE_KEY;
962 	}
963 
964     siglen = EVP_PKEY_size(pkey);
965     int nSignRet;
966 
967     sig = (unsigned char*) OPENSSL_malloc(siglen);
968 	if( sig == NULL )
969 	{
970 		EVP_PKEY_free(pkey);
971 		return DKIM_OUT_OF_MEMORY;
972 	}
973 
974 	if( bUseIetfBodyHash )
975 	{
976 		nSignRet = EVP_SignFinal( bUseSha256 ? m_Hdr_ietf_sha256ctx : m_Hdr_ietf_sha1ctx, sig, &siglen, pkey);
977 	}
978 	else
979 	{
980 		nSignRet = EVP_SignFinal( m_allman_sha1ctx, sig, &siglen, pkey);
981 	}
982 
983     EVP_PKEY_free(pkey);
984 
985 	if( !nSignRet )
986 	{
987 		OPENSSL_free(sig);
988 		return DKIM_BAD_PRIVATE_KEY; // key too small
989 	}
990 
991     bio = BIO_new(BIO_s_mem());
992     if (!bio) {
993       return DKIM_OUT_OF_MEMORY;
994     }
995     b64 = BIO_new(BIO_f_base64());
996     if (!b64) {
997       BIO_free(bio);
998       return DKIM_OUT_OF_MEMORY;
999     }
1000     BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
1001     BIO_push(b64, bio);
1002     if (BIO_write(b64, sig, siglen) < siglen)
1003 	{
1004       OPENSSL_free(sig);
1005       BIO_free_all(b64);
1006       return DKIM_OUT_OF_MEMORY;
1007     }
1008     BIO_flush(b64);
1009     OPENSSL_free(sig);
1010 
1011 	len = siglen * 2;
1012 	buf = new char[len];
1013 
1014 	if( buf == NULL )
1015 	{
1016 		BIO_free_all(b64);
1017 		return DKIM_OUT_OF_MEMORY;
1018 	}
1019 
1020     size = BIO_read(bio, buf, len);
1021     BIO_free_all(b64);
1022 
1023 	// this should never happen
1024     if (size >= len)
1025 	{
1026 		delete[] buf;
1027 		return DKIM_OUT_OF_MEMORY;
1028 	}
1029 
1030     buf[size] = '\0';
1031 
1032 	AddFoldedValueToSig( buf, 0 );
1033 
1034 	delete[] buf;
1035 
1036 	return DKIM_SUCCESS;
1037 }
1038 
1039 
1040 
AssembleReturnedSig(char * szPrivKey)1041 int CDKIMSign::AssembleReturnedSig( char* szPrivKey )
1042 {
1043 	int nRet;
1044 
1045 	if( m_bReturnedSigAssembled )
1046 		return DKIM_SUCCESS;
1047 
1048 	ProcessFinal();
1049 
1050 	if( ParseFromAddress() == false )
1051 	{
1052 		//return DKIM_NO_SENDER;
1053 	}
1054 
1055 	Hash( "\r\n", 2, true, true ); // only for Allman sig
1056 
1057 	string allmansha1sig, ietfsha256Sig, ietfsha1Sig;
1058 
1059 	if( m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1 )
1060 	{
1061 		nRet = ConstructSignature( szPrivKey, false, false );
1062 		if( nRet == DKIM_SUCCESS )
1063 		{
1064 			allmansha1sig.assign( m_sSig );
1065 		}
1066 		else
1067 		{
1068 			return nRet;
1069 		}
1070 	}
1071 	else if( m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1 )
1072 	{
1073 		if( m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1 )
1074 		{
1075 			nRet = ConstructSignature( szPrivKey, false, false );
1076 			if( nRet == DKIM_SUCCESS )
1077 			{
1078 				allmansha1sig.assign( m_sSig );
1079 			}
1080 			else
1081 			{
1082 				return nRet;
1083 			}
1084 		}
1085 		if( m_nHash & DKIM_HASH_SHA256 )
1086 		{
1087 			nRet = ConstructSignature( szPrivKey, true, true );
1088 			if( nRet == DKIM_SUCCESS )
1089 			{
1090 				ietfsha256Sig.assign( m_sSig );
1091 			}
1092 			else
1093 			{
1094 				return nRet;
1095 			}
1096 		}
1097 		if( m_nHash != DKIM_HASH_SHA256 )
1098 		{
1099 			nRet = ConstructSignature( szPrivKey, true, false );
1100 
1101 			if( nRet == DKIM_SUCCESS )
1102 			{
1103 				ietfsha1Sig.assign( m_sSig );
1104 			}
1105 			else
1106 			{
1107 				return nRet;
1108 			}
1109 		}
1110 	}
1111 
1112 
1113 //	fclose( fpdebug );
1114 //	fpdebug = NULL;
1115 
1116 	m_sReturnedSig.assign( allmansha1sig );
1117 
1118 	if( !ietfsha1Sig.empty() )
1119 	{
1120 		if( !m_sReturnedSig.empty() )
1121 		{
1122 			m_sReturnedSig.append( "\r\n" );
1123 		}
1124 		m_sReturnedSig.append( ietfsha1Sig );
1125 	}
1126 
1127 	if( !ietfsha256Sig.empty() )
1128 	{
1129 		if( !m_sReturnedSig.empty() )
1130 		{
1131 			m_sReturnedSig.append( "\r\n" );
1132 		}
1133 		m_sReturnedSig.append( ietfsha256Sig );
1134 	}
1135 
1136 	m_bReturnedSigAssembled = true;
1137 
1138 	return DKIM_SUCCESS;
1139 }
1140