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