9462 lines
285 KiB

# --- SDE-COPYRIGHT-NOTE-BEGIN ---
# This copyright note is auto-generated by ./scripts/Create-CopyPatch.
#
# Filename: package/.../qmail/qmail-dkim.diff
# Copyright (C) 2009 The OpenSDE Project
#
# More information can be found in the files COPYING and README.
#
# This patch file is dual-licensed. It is available under the license the
# patched project is licensed under, as long as it is an OpenSource license
# as defined at http://www.opensource.org/ (e.g. BSD, X11) or under the terms
# of the GNU General Public License as published by the Free Software
# Foundation; either version 2 of the License, or (at your option) any later
# version.
# --- SDE-COPYRIGHT-NOTE-END ---
This patch is an adaptation of the work of Manvendra Bhangui <mbhangui@gmail.com>
http://downloads.sourceforge.net/indimail/dkim-netqmail-1.06.patch-1.0.gz?use_mirror=osdn
--- qmail-1.03.orig/hier.c 2009-10-15 20:38:23.426352000 +0300
+++ qmail-1.03/hier.c 2009-10-16 00:11:06.477232936 +0300
@@ -188,10 +188,15 @@
c(auto_qmail,"bin","qmail-start",auto_uido,auto_gidq,0700);
c(auto_qmail,"bin","qmail-getpw",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-local",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","spawn-filter",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","dk-filter",auto_uido,auto_gidq,0555);
c(auto_qmail,"bin","qmail-remote",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-rspawn",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-clean",auto_uido,auto_gidq,0711);
c(auto_qmail,"bin","qmail-send",auto_uido,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-dk",auto_uidq,auto_gidq,0711);
+ c(auto_qmail,"bin","qmail-dkim",auto_uidq,auto_gidq,0711);
+ c(auto_qmail,"bin","dkimtest",auto_uidq,auto_gidq,0711);
#ifdef EXTERNAL_TODO
c(auto_qmail,"bin","qmail-todo",auto_uido,auto_gidq,0711);
#endif
@@ -301,6 +306,14 @@
c(auto_qmail,"man/cat1","tcp-env.0",auto_uido,auto_gidq,0644);
c(auto_qmail,"man/man8","qmail-local.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-dk.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-dk.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","qmail-dkim.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","qmail-dkim.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","dk-filter.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","dk-filter.0",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/man8","spawn-filter.8",auto_uido,auto_gidq,0644);
+ c(auto_qmail,"man/cat8","spawn-filter.0",auto_uido,auto_gidq,0644);
c(auto_qmail,"man/cat8","qmail-local.0",auto_uido,auto_gidq,0644);
c(auto_qmail,"man/man8","qmail-lspawn.8",auto_uido,auto_gidq,0644);
c(auto_qmail,"man/cat8","qmail-lspawn.0",auto_uido,auto_gidq,0644);
diff -Naur qmail-1.03.org/dk-filter.9 qmail-1.03/dk-filter.9
--- qmail-1.03.org/dk-filter.9 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dk-filter.9 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,107 @@
+.TH dk-filter 8
+.SH NAME
+dk-filter \- sign/verify using DK/DKIM (SSP/ADSP optionally) and deliver a mail message for delivery
+.SH SYNOPSIS
+.B FILTERARGS=QMAILHOME/bin/dk-filter
+.SH DESCRIPTION
+.B dk-filter
+is a qfilter which can be set as a filter for
+.BR spawn-filter(8) .
+The filter can be set either as
+.B FILTERARGS
+or in the control file
+.BR filterargs .
+
+.B dk-filter
+supports DK/DKIM signing and verification and can optionally use
+.B Sender Signing Practice (SSP)
+or
+.B Author Domain Signing Practice.
+It uses the libdkim and OpenSSL libraries. To sign a message, set the
+.B DKIMSIGN
+or
+.B DKSIGIN
+environment variables to the pathname of the private key that will be
+used to sign the message. If there is a % character in the environment
+variable, it is removed and replaced by the domain name in the From: header.
+If, after substituting the %, that file does not exist, the message will not be signed.
+If there is no % and the file does not exist, the message will be rejected with error 35.
+The selector (s=) will be taken from the basename of the file.
+The private key should be created by
+.BR dknewkey(8) .
+
+You can set various DK options in getopt style, by setting the environment variable DKSIGNOPTIONS
+ b <advice_length> Length of Advice
+ c <canonicalization> simple, nofws
+ s <privkeyfile>
+ h show headers included
+ r Skip Duplicate Headers
+.EX
+ DKSIGNOPTIONS="-h -r -c nofws"
+ sets the h= tag, skips duplicate headers and sets nofws canonicalization
+.EE
+
+You can set various DKIM options in getopt style, by setting the environment variable DKIMSIGNOPTIONS
+
+ b <standard> 1 - allman, 2 - ietf or 3 - both
+ c <canonicalization> r for relaxed [DEFAULT], s - simple,
+ t relaxed/simple, u - simple/relaxed
+ l include body length tag
+ q include query method tag;
+ t include a timestamp tag
+ h
+ i <identity> the identity, if not provided it will not be included
+ x <expire_time> the expire time in seconds since epoch
+ ( DEFAULT = current time + 604800)
+ if set to - then it will not be included
+ z <hash> 1 for sha1, 2 for sha256, 3 for both
+ s <privkeyfile>
+ y <selector>
+
+.EX
+ DKIMSIGNOPTIONS="-b 1 -c r -q"
+ sets allman standard, with relaxed canonicalization and include query method tag
+.EE
+
+.B dk-filter
+uses the domain found in the Sender: header to set the domain tag. If not it uses the From: header. You can override this by
+setting
+.B DKIMDOMAIN
+environment variable.
+.B DKIMDOMAIN
+can be set to an email address or a domain (without the at sign).
+To verify a message, set the
+.B DKIMVERIFY
+or
+.B DKVERIFY
+environment variables
+.B dk-filter
+always inserts the
+.B DKIM-Status
+or
+.B DomainKey-Status
+header, so that messages can be
+rejected later at delivery time, or in the mail reader. In that case you may set
+.B DKIMVERIFY
+or
+.B DKVERIFY
+to an empty string.
+.B dk-filter
+does not use any signing practice byd default. You can override this by setting the SIGN_PRACTICE to ssp or adsp (lowercase).
+
+.SH "EXIT CODES"
+0 for success, non-zero failure
+
+.SH "SEE ALSO"
+dknewkey(8),
+dktest(8),
+dkim(8),
+spawn-filter(8)
+
+.SH "AUTHORS"
+
+Manvendra Bhangui.
+.SH PROBLEMS
+Problems with
+.B dk-filter
+should be forwarded to "Manvendra Bhangui" <mbhangui@gmail.com>
diff -Naur qmail-1.03.org/dk-filter.sh qmail-1.03/dk-filter.sh
--- qmail-1.03.org/dk-filter.sh 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dk-filter.sh 2009-04-21 21:35:56.000000000 +0530
@@ -0,0 +1,247 @@
+#
+# $Log: dk-filter.sh,v $
+# Revision 1.8 2009-04-21 20:42:44+05:30 Cprogrammer
+# added check for dktest, dkim executables
+#
+# Revision 1.7 2009-04-20 10:06:58+05:30 Cprogrammer
+# added DKSIGNOPTS
+#
+# Revision 1.6 2009-04-19 13:38:24+05:30 Cprogrammer
+# added full set of dkim options
+# replaced indimail/bin/echo with echo 1>&2
+#
+# Revision 1.5 2009-04-06 16:37:50+05:30 Cprogrammer
+# added SIGN_PRACTICE
+# use ietf standard insted of allman so that Yahoo verification does not fail
+#
+# Revision 1.4 2009-04-03 14:39:00+05:30 Cprogrammer
+# added return status
+#
+# Revision 1.3 2009-04-03 08:55:29+05:30 Cprogrammer
+# print error messages to stderr
+#
+# Revision 1.2 2009-04-02 20:36:25+05:30 Cprogrammer
+# added -h option to dktest
+# added -x - option to dkim
+#
+# Revision 1.1 2009-04-02 14:52:27+05:30 Cprogrammer
+# Initial revision
+#
+#
+if [ -z $QMAILREMOTE -a -z $QMAILLOCAL ]; then
+ echo "dk-filter should be run by spawn-filter" 1>&2
+ exit 1
+fi
+if [ -z "$DKSIGN" -a -z "$DKIMSIGN" -a -z "$DKVERIFY" -a -z "$DKIMVERIFY" ] ; then
+ echo "Must provide at least one of DKSIGN, DKIMSIGN, DKVERIFY, DKIMVERIFY" 1>&2
+ exit 1
+fi
+dksign=0
+dkimsign=0
+dkverify=0
+dkimverify=0
+if [ ! -z $DKSIGN ] ; then
+ if [ ! -f QMAILHOME/bin/dktest ] ; then
+ echo "QMAILHOME/bin/dktest: No such file or directory" 1>&2
+ exit 1
+ fi
+ dksign=1
+ if [ ! " $DOMAIN" = " " ] ; then
+ # replace '%' in filename with domain
+ dkkeyfn=`echo $DKSIGN | sed s{%{$DOMAIN{g`
+ else
+ dkkeyfn=$DKSIGN
+ fi
+ dkselector=`basename $dkkeyfn`
+fi
+if [ ! -z $DKIMSIGN ] ; then
+ if [ ! -f QMAILHOME/bin/dkimtest ] ; then
+ echo "QMAILHOME/bin/dkimtest: No such file or directory" 1>&2
+ exit 1
+ fi
+ dkimsign=1
+ if [ ! " $DOMAIN" = " " ] ; then
+ # replace '%' in filename with domain
+ dkimkeyfn=`echo $DKIMSIGN | sed s{%{$DOMAIN{g`
+ else
+ dkimkeyfn=$DKIMSIGN
+ fi
+ dkimselector=`basename $dkimkeyfn`
+fi
+if [ ! -z $DKVERIFY ] ; then
+ if [ ! -f QMAILHOME/bin/dktest ] ; then
+ echo "QMAILHOME/bin/dktest: No such file or directory" 1>&2
+ exit 1
+ fi
+ dkverify=1
+fi
+if [ ! -z $DKIMVERIFY ] ; then
+ if [ ! -f QMAILHOME/bin/dkimtest ] ; then
+ echo "QMAILHOME/bin/dkimtest: No such file or directory" 1>&2
+ exit 1
+ fi
+ dkimverify=1
+fi
+cat > /tmp/dk.$$
+if [ $dksign -eq 1 ] ; then
+ #dktest: [-f] [-b advice_length] [-c nofws|simple] [-v|-s selector] [-h] [-t#] [-r] [-T][-d dnsrecord]
+ # DKSIGNOPTIONS="-z 1 -b 2 -x - -y $dkimselector -s $dkimkeyfn"
+ set -- `getopt hrb:c:s: $DKSIGNOPTIONS`
+ dkopts="QMAILHOME/bin/dktest"
+ sopt=0
+ while [ $1 != -- ]
+ do
+ case $1 in
+ -h)
+ dkopts="$dkopts -h"
+ ;;
+ -r)
+ dkopts="$dkopts -r"
+ ;;
+
+ -b)
+ dkopts="$dkopts -b $2"
+ shift
+ ;;
+
+ -c)
+ dkopts="$dkopts -c $2"
+ shift
+ ;;
+
+ -s)
+ sopt=1
+ dkopts="$dkopts -s $2"
+ shift
+ ;;
+ esac
+ shift # next flag
+ done
+ if [ $sopt -eq 0 ] ; then
+ dkopts="$dkopts -s $dkkeyfn"
+ fi
+ exec 0</tmp/dk.$$
+ #QMAILHOME/bin/dktest -h -s $dkkeyfn
+ eval $dkopts
+ if [ $? -ne 0 ] ; then
+ /bin/rm -f /tmp/dk.$$
+ exit 1
+ fi
+fi
+if [ $dkimsign -eq 1 ] ; then
+ # DKIMSIGNOPTIONS="-z 1 -b 2 -x - -y $dkimselector -s $dkimkeyfn"
+ set -- `getopt lqthb:c:d:i:x:z:y:s: $DKIMSIGNOPTIONS`
+ bopt=0
+ xopt=0
+ zopt=0
+ yopt=0
+ sopt=0
+ dkimopts="QMAILHOME/bin/dkimtest"
+ while [ $1 != -- ]
+ do
+ case $1 in
+ -l)
+ dkimopts="$dkimopts -l"
+ ;;
+ -q)
+ dkimopts="$dkimopts -q"
+ ;;
+ -t)
+ dkimopts="$dkimopts -t"
+ ;;
+ -h)
+ dkimopts="$dkimopts -h"
+ ;;
+
+ -b)
+ bopt=1
+ dkimopts="$dkimopts -b $2"
+ shift
+ ;;
+
+ -c)
+ dkimopts="$dkimopts -c $2"
+ shift
+ ;;
+
+ -i)
+ dkimopts="$dkimopts -i $2"
+ shift
+ ;;
+
+ -x)
+ xopt=1
+ dkimopts="$dkimopts -x $2"
+ shift
+ ;;
+
+ -z)
+ zopt=1
+ dkimopts="$dkimopts -z $2"
+ shift
+ ;;
+
+ -y)
+ yopt=1
+ dkimopts="$dkimopts -y $2"
+ shift
+ ;;
+
+ -s)
+ sopt=1
+ dkimopts="$dkimopts -s $2"
+ shift
+ ;;
+ esac
+ shift # next flag
+ done
+ if [ $zopt -eq 0 ] ; then
+ dkimopts="$dkimopts -z 1"
+ fi
+ if [ $bopt -eq 0 ] ; then
+ dkimopts="$dkimopts -b 2"
+ fi
+ if [ $xopt -eq 0 ] ; then
+ dkimopts="$dkimopts -x -"
+ fi
+ if [ $yopt -eq 0 ] ; then
+ dkimopts="$dkimopts -y $dkimselector"
+ fi
+ if [ $sopt -eq 0 ] ; then
+ dkimopts="$dkimopts -s $dkimkeyfn"
+ fi
+ exec 0</tmp/dk.$$
+ eval $dkimopts
+ if [ $? -ne 0 ] ; then
+ /bin/rm -f /tmp/dk.$$
+ exit 1
+ fi
+fi
+if [ $dkimverify -eq 1 ] ; then
+ practice=$SIGN_PRACTICE
+ if [ " $practice" = " " ] ; then
+ practice=0
+ elif [ " $practice" = " ssp" ] ; then
+ practice=1
+ elif [ " $practice" = " adsp" ] ; then
+ practice=2
+ fi
+ exec 0</tmp/dk.$$
+ QMAILHOME/bin/dkimtest -p $practice -v
+ if [ $? -ne 0 ] ; then
+ /bin/rm -f /tmp/dk.$$
+ exit 1
+ fi
+fi
+if [ $dkverify -eq 1 ] ; then
+ exec 0</tmp/dk.$$
+ QMAILHOME/bin/dktest -v
+ if [ $? -ne 0 ] ; then
+ /bin/rm -f /tmp/dk.$$
+ exit 1
+ fi
+fi
+exec 0</tmp/dk.$$
+/bin/rm -f /tmp/dk.$$
+cat
+exit $?
diff -Naur qmail-1.03.org/dkimbase.cpp qmail-1.03/dkimbase.cpp
--- qmail-1.03.org/dkimbase.cpp 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimbase.cpp 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,333 @@
+/*
+ * $Log: dkimbase.cpp,v $
+ * Revision 1.3 2009-03-26 15:10:32+05:30 Cprogrammer
+ * fixed indentation
+ *
+ * Revision 1.2 2009-03-25 08:37:27+05:30 Cprogrammer
+ * fixed indentation
+ *
+ * Revision 1.1 2009-03-21 08:43:08+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "dkim.h"
+#include "dkimbase.h"
+#include <string.h>
+#include <algorithm>
+
+CDKIMBase::CDKIMBase()
+{
+ m_From = NULL;
+ m_Sender = NULL;
+ m_hTag = NULL;
+ m_hTagSize = 0;
+ m_hTagPos = 0;
+ m_Line = NULL;
+ m_LineSize = 0;
+ m_LinePos = 0;
+ m_InHeaders = true;
+}
+
+CDKIMBase::~CDKIMBase()
+{
+ Free(m_Line);
+ Free(m_From);
+ Free(m_Sender);
+ Free(m_hTag);
+}
+
+int
+CDKIMBase::Init(void)
+{
+ return DKIM_SUCCESS;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Alloc - allocate buffer
+//
+////////////////////////////////////////////////////////////////////////////////
+int CDKIMBase::Alloc(char *&szBuffer, int nRequiredSize)
+{
+ szBuffer = new char[nRequiredSize];
+
+ return (szBuffer == NULL) ? DKIM_OUT_OF_MEMORY : DKIM_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ReAlloc - extend buffer if necessary, leaving room for future expansion
+//
+////////////////////////////////////////////////////////////////////////////////
+int CDKIMBase::ReAlloc(char *&szBuffer, int &nBufferSize, int nRequiredSize)
+{
+ if (nRequiredSize > nBufferSize) {
+ char *
+ newp;
+ int
+ nNewSize = nRequiredSize + BUFFER_ALLOC_INCREMENT;
+
+ if (Alloc(newp, nNewSize) == DKIM_SUCCESS) {
+ if (szBuffer != NULL && nBufferSize > 0) {
+ memcpy(newp, szBuffer, nBufferSize);
+ delete[]szBuffer;
+ }
+ szBuffer = newp;
+ nBufferSize = nNewSize;
+ } else {
+ return DKIM_OUT_OF_MEMORY; // memory alloc error!
+ }
+ }
+ return DKIM_SUCCESS;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Process - split buffers into lines without any CRs or LFs at the end.
+//
+////////////////////////////////////////////////////////////////////////////////
+void CDKIMBase::Free(char *szBuffer)
+{
+ if (szBuffer)
+ delete[]szBuffer;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Process - split buffers into lines without any CRs or LFs at the end.
+//
+////////////////////////////////////////////////////////////////////////////////
+int CDKIMBase::Process(char *szBuffer, int nBufLength, bool bEOF)
+{
+ char *p = szBuffer;
+ char *e = szBuffer + nBufLength;
+
+ while (p < e) {
+ if (*p != '\n' && *p != '\r') {
+ if (m_LinePos >= m_LineSize) {
+ int nRet = ReAlloc(m_Line, m_LineSize, m_LinePos + 1);
+ if (nRet != DKIM_SUCCESS)
+ /*
+ * How to distinguish between
+ * DKIM_FINISHED_BODY & DKIM_OUT_OF_MEMORY
+ */
+ return nRet;
+ }
+ m_Line[m_LinePos++] = *p;
+ } else {
+ if (*p == '\r' && p + 1 < e && *(p + 1) == '\n')
+ p++;
+ if (m_InHeaders) {
+ // process header line
+ if (m_LinePos == 0) {
+ m_InHeaders = false;
+ int Result = ProcessHeaders();
+ if (Result != DKIM_SUCCESS)
+ return Result;
+ } else {
+ // append the header to the headers list
+ if (m_Line[0] != ' ' && m_Line[0] != '\t') {
+ HeaderList.push_back(string(m_Line, m_LinePos));
+ } else {
+ if (!HeaderList.empty()) {
+ HeaderList.back().append("\r\n", 2).append(m_Line, m_LinePos);
+ } else {
+ // no header to append to...
+ }
+ }
+ }
+ } else {
+ // process body line
+ int Result = ProcessBody(m_Line, m_LinePos, bEOF);
+ if (Result != DKIM_SUCCESS) {
+ m_LinePos = 0;
+ return Result;
+ }
+ }
+ m_LinePos = 0;
+ }
+ p++;
+ }
+ return DKIM_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ProcessFinal - process leftovers if stopping before the body or mid-line
+//
+////////////////////////////////////////////////////////////////////////////////
+int CDKIMBase::ProcessFinal(void)
+{
+ if (m_LinePos > 0) {
+ Process((char *) "\r\n", 2, true);
+ }
+
+ if (m_InHeaders) {
+ m_InHeaders = false;
+ ProcessHeaders();
+ ProcessBody((char *) "", 0, true);
+ }
+
+ return DKIM_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ProcessHeaders - process the headers (to be implemented by derived class)
+//
+////////////////////////////////////////////////////////////////////////////////
+int CDKIMBase::ProcessHeaders()
+{
+ return DKIM_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// ProcessBody - process body line (to be implemented by derived class)
+//
+////////////////////////////////////////////////////////////////////////////////
+int CDKIMBase::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
+{
+ return DKIM_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// RemoveSWSP - remove streaming white space from buffer/string inline
+//
+////////////////////////////////////////////////////////////////////////////////
+
+struct isswsp {
+ bool
+ operator() (char ch) {
+ return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
+ }
+};
+
+void CDKIMBase::RemoveSWSP(char *szBuffer)
+{
+ *remove_if(szBuffer, szBuffer + strlen(szBuffer), isswsp()) = '\0';
+}
+
+void CDKIMBase::RemoveSWSP(char *pBuffer, int &nBufLength)
+{
+ nBufLength = remove_if(pBuffer, pBuffer + nBufLength, isswsp()) - pBuffer;
+}
+
+void CDKIMBase::RemoveSWSP(string &sBuffer)
+{
+ sBuffer.erase(remove_if(sBuffer.begin(), sBuffer.end(), isswsp()), sBuffer.end());
+}
+
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+// CompressSWSP - compress streaming white space into single spaces from buffer/string inline
+//
+//////////////////////////////////////////////////////////////////////////////////////////
+
+void CDKIMBase::CompressSWSP(char *pBuffer, int &nBufLength)
+{
+ char *pSrc = pBuffer;
+ char *pDst = pBuffer;
+ char *pEnd = pBuffer + nBufLength;
+
+ while (pSrc != pEnd) {
+ if (isswsp()(*pSrc)) {
+ do {
+ ++pSrc;
+ } while (pSrc != pEnd && isswsp()(*pSrc));
+ if (pSrc == pEnd)
+ break;
+ *pDst++ = ' ';
+ }
+ *pDst++ = *pSrc++;
+ }
+ nBufLength = pDst - pBuffer;
+}
+
+void CDKIMBase::CompressSWSP(string &sBuffer)
+{
+ string::iterator iSrc = sBuffer.begin();
+ string::iterator iDst = sBuffer.begin();
+ string::iterator iEnd = sBuffer.end();
+
+ while (iSrc != iEnd) {
+ if (isswsp()(*iSrc)) {
+ do {
+ ++iSrc;
+ } while (iSrc != iEnd && isswsp()(*iSrc));
+
+ if (iSrc == iEnd)
+ break;
+ *iDst++ = ' ';
+ }
+ *iDst++ = *iSrc++;
+ }
+ sBuffer.erase(iDst, iEnd);
+}
+
+//////////////////////////////////////////////////////////////////////////////////////////
+//
+// RelaxHeader - relax a header field (lower case the name, remove swsp before and after :)
+//
+// modified 4/21/06 STB to remove white space before colon
+//
+//////////////////////////////////////////////////////////////////////////////////////////
+
+string CDKIMBase::RelaxHeader(const string &sHeader)
+{
+ string sTemp = sHeader;
+
+ CompressSWSP(sTemp);
+
+ unsigned cpos = sTemp.find(':');
+ if (cpos == -1) {
+ // no colon?!
+ } else {
+ // lower case the header field name
+ for (unsigned i = 0; i < cpos; i++) {
+ if (sTemp[i] >= 'A' && sTemp[i] <= 'Z')
+ sTemp[i] += 'a' - 'A';
+ }
+ // remove the space after the :
+ if (cpos + 1 < sTemp.length() && sTemp[cpos + 1] == ' ')
+ sTemp.erase(cpos + 1, 1);
+ // remove the space before the :
+ if (cpos > 0 && sTemp[cpos - 1] == ' ')
+ sTemp.erase(cpos - 1, 1);
+ }
+ return sTemp;
+}
+
+void
+getversion_dkimbase_cpp()
+{
+ static char *x = (char *) "$Id: dkimbase.cpp,v 1.3 2009-03-26 15:10:32+05:30 Cprogrammer Exp mbhangui $";
+
+ x++;
+}
diff -Naur qmail-1.03.org/dkimbase.h qmail-1.03/dkimbase.h
--- qmail-1.03.org/dkimbase.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimbase.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,72 @@
+/*
+ * $Log: dkimbase.h,v $
+ * Revision 1.1 2009-03-21 08:50:18+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef DKIMBASE_H
+#define DKIMBASE_H
+
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+#define BUFFER_ALLOC_INCREMENT 256
+
+#include <string>
+#include <list>
+
+using namespace std;
+class CDKIMBase {
+public:
+
+ CDKIMBase();
+ ~CDKIMBase();
+
+ int Init(void);
+ int Process(char *szBuffer, int nBufLength, bool bEOF);
+ int ProcessFinal(void);
+ int Alloc(char *&szBuffer, int nRequiredSize);
+ int ReAlloc(char *&szBuffer, int &nBufferLength, int nRequiredSize);
+ void Free(char *szBuffer);
+ static void RemoveSWSP(char *szBuffer);
+ static void RemoveSWSP(char *pBuffer, int &nBufLength);
+ static void RemoveSWSP(string & sBuffer);
+ static void CompressSWSP(char *pBuffer, int &nBufLength);
+ static void CompressSWSP(string & sBuffer);
+ static string RelaxHeader(const string & sHeader);
+ virtual int ProcessHeaders(void);
+ virtual int ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
+
+protected:
+ char *m_From;
+ char *m_Sender;
+ char *m_hTag;
+ int m_hTagSize;
+ int m_hTagPos;
+ char *m_Line;
+ int m_LineSize;
+ int m_LinePos;
+ bool m_InHeaders;
+ list < string > HeaderList;
+};
+#endif /*- DKIMBASE_H */
diff -Naur qmail-1.03.org/dkimdns.cpp qmail-1.03/dkimdns.cpp
--- qmail-1.03.org/dkimdns.cpp 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimdns.cpp 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,127 @@
+/*
+ * $Log: dns.cpp,v $
+ * Revision 1.3 2009-03-27 19:22:45+05:30 Cprogrammer
+ * dns functions
+ *
+ */
+#include <netdb.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <string.h>
+#include "dkimdns.h"
+
+static unsigned short
+getshort(unsigned char *cp)
+{
+ return (cp[0] << 8) | cp[1];
+}
+
+/*
+ * we always return a null-terminated string which has been malloc'ed. The string
+ * is always in the tag=value form. If a temporary or permanent error occurs,
+ * the string will be exactly "e=perm;" or "e=temp;".
+ * Note that it never returns NULL.
+ */
+char *
+dns_text(char *dn)
+{
+ u_char response[PACKETSZ + 1]; /* response */
+ int responselen; /* buffer length */
+ int rc; /* misc variables */
+ int ancount, qdcount; /* answer count and query count */
+ u_short type, rdlength; /* fields of records returned */
+ u_char *eom, *cp;
+ u_char buf[PACKETSZ + 1]; /* we're storing a TXT record here, not just a DNAME */
+ u_char *bufptr;
+
+ responselen = res_query(dn, C_IN, T_TXT, response, sizeof (response));
+ if (responselen < 0) {
+ if (h_errno == TRY_AGAIN)
+ return strdup("e=temp;");
+ else
+ return strdup("e=perm;");
+ }
+ qdcount = getshort(response + 4); /* http://crynwr.com/rfc1035/rfc1035.html#4.1.1. */
+ ancount = getshort(response + 6);
+ eom = response + responselen;
+ cp = response + HFIXEDSZ;
+ while (qdcount-- > 0 && cp < eom)
+ {
+ rc = dn_expand(response, eom, cp, (char *) buf, MAXDNAME);
+ if (rc < 0)
+ return strdup("e=perm;");
+ cp += rc + QFIXEDSZ;
+ }
+ while (ancount-- > 0 && cp < eom)
+ {
+ if ((rc = dn_expand(response, eom, cp, (char *) buf, MAXDNAME)) < 0)
+ return strdup("e=perm;");
+ cp += rc;
+ if (cp + RRFIXEDSZ >= eom)
+ return strdup("e=perm;");
+ type = getshort(cp + 0); /* http://crynwr.com/rfc1035/rfc1035.html#4.1.3. */
+ rdlength = getshort(cp + 8);
+ cp += RRFIXEDSZ;
+ if (type != T_TXT)
+ {
+ cp += rdlength;
+ continue;
+ }
+ bufptr = buf;
+ while (rdlength && cp < eom)
+ {
+ unsigned int cnt;
+
+ cnt = *cp++; /* http://crynwr.com/rfc1035/rfc1035.html#3.3.14. */
+ if (bufptr - buf + cnt + 1 >= PACKETSZ)
+ return strdup("e=perm;");
+ if (cp + cnt > eom)
+ return strdup("e=perm;");
+ memcpy((char *) bufptr, (char *) cp, cnt);
+ rdlength -= cnt + 1;
+ bufptr += cnt;
+ cp += cnt;
+ *bufptr = '\0';
+ }
+ return (char *) strdup((char *) buf);
+ }
+ return strdup("e=perm;");
+}
+
+int
+DNSGetTXT(const char *domain, char *buffer, int maxlen)
+{
+ char *results;
+ int len;
+
+ results = dns_text((char *) domain);
+ if (!strcmp(results, "e=perm;"))
+ {
+ free(results);
+ return DNSRESP_PERM_FAIL;
+ } else
+ if (!strcmp(results, "e=temp;"))
+ {
+ free(results);
+ return DNSRESP_TEMP_FAIL;
+ }
+ if (strlen(results) > maxlen - 1)
+ {
+ free(results);
+ return DNSRESP_DOMAIN_NAME_TOO_LONG;
+ }
+ strncpy(buffer, results, maxlen);
+ free(results);
+ return DNSRESP_SUCCESS;
+}
+
+void
+getversion_dkimdns_cpp()
+{
+ static char *x = (char *) "$Id: dns.cpp,v 1.3 2009-03-27 19:22:45+05:30 Cprogrammer Exp mbhangui $";
+
+ x++;
+}
diff -Naur qmail-1.03.org/dkimdns.h qmail-1.03/dkimdns.h
--- qmail-1.03.org/dkimdns.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimdns.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,47 @@
+/*
+ * $Log: dns.h,v $
+ * Revision 1.3 2009-03-27 20:40:16+05:30 Cprogrammer
+ * removed windows definitions
+ *
+ * Revision 1.2 2009-03-27 19:23:01+05:30 Cprogrammer
+ * added dns_text()
+ *
+ * Revision 1.1 2009-03-21 08:50:24+05:30 Cprogrammer
+ * Initial revision
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+// These DNS resolution routines are encapsulated by the API below
+
+// return values for DNS functions:
+
+
+#define MAX_DOMAIN 254
+
+#define DNSRESP_SUCCESS 0 // DNS lookup returned sought after records
+#define DNSRESP_TEMP_FAIL 1 // No response from DNS server
+#define DNSRESP_PERM_FAIL 2 // DNS server returned error or no records
+#define DNSRESP_DOMAIN_NAME_TOO_LONG 3 // Domain name too long
+#define DNSRESP_NXDOMAIN 4 // DNS server returned Name Error
+#define DNSRESP_EMPTY 5 // DNS server returned successful response but no records
+
+// Pass in the FQDN to get the TXT record
+int DNSGetTXT(const char *szFQDN, char *Buffer, int nBufLen);
+char *dns_text(char *szFQDN);
diff -Naur qmail-1.03.org/dkimfuncs.cpp qmail-1.03/dkimfuncs.cpp
--- qmail-1.03.org/dkimfuncs.cpp 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimfuncs.cpp 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,224 @@
+/*
+ * $Log: dkimfuncs.cpp,v $
+ * Revision 1.2 2009-03-26 15:11:12+05:30 Cprogrammer
+ * added GetDomain(), ADSP
+ *
+ * Revision 1.1 2009-03-21 08:43:10+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "dkim.h"
+#include "dkimsign.h"
+#include "dkimverify.h"
+#include <string.h>
+
+#define DKIMID ('D' | 'K'<<8 | 'I'<<16 | 'M'<<24)
+
+static void
+InitContext(DKIMContext * pContext, bool bSign, void *pObject)
+{
+ pContext->reserved1 = DKIMID;
+ pContext->reserved2 = bSign ? 1 : 0;
+ pContext->reserved3 = pObject;
+}
+
+static void *
+ValidateContext(DKIMContext * pContext, bool bSign)
+{
+ if (pContext->reserved1 != DKIMID)
+ return NULL;
+ if (pContext->reserved2 != (unsigned int) (bSign ? 1 : 0))
+ return NULL;
+ return pContext->reserved3;
+}
+
+int DKIM_CALL
+DKIMSignInit(DKIMContext * pSignContext, DKIMSignOptions * pOptions)
+{
+ int nRet = DKIM_OUT_OF_MEMORY;
+ CDKIMSign *pSign = new CDKIMSign;
+
+ if (pSign) {
+ nRet = pSign->Init(pOptions);
+ if (nRet != DKIM_SUCCESS)
+ delete pSign;
+ }
+ if (nRet == DKIM_SUCCESS)
+ InitContext(pSignContext, true, pSign);
+ return nRet;
+}
+
+int DKIM_CALL
+DKIMSignProcess(DKIMContext * pSignContext, char *szBuffer, int nBufLength)
+{
+ CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
+ if (pSign)
+ return pSign->Process(szBuffer, nBufLength, false);
+ return DKIM_INVALID_CONTEXT;
+}
+
+int DKIM_CALL
+DKIMSignGetSig(DKIMContext * pSignContext, char *szPrivKey, char *szSignature, int nSigLength)
+{
+ CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
+ if (pSign)
+ return pSign->GetSig(szPrivKey, szSignature, nSigLength);
+ return DKIM_INVALID_CONTEXT;
+}
+
+int DKIM_CALL
+DKIMSignGetSig2(DKIMContext * pSignContext, char *szPrivKey, char **pszSignature)
+{
+ CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
+ if (pSign)
+ return pSign->GetSig2(szPrivKey, pszSignature);
+ return DKIM_INVALID_CONTEXT;
+}
+
+char *DKIM_CALL
+DKIMSignGetDomain(DKIMContext *pSignContext)
+{
+ CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
+ if (pSign)
+ return pSign->GetDomain();
+ return ((char *) 0);
+}
+
+void DKIM_CALL
+DKIMSignFree(DKIMContext * pSignContext)
+{
+ CDKIMSign *pSign = (CDKIMSign *) ValidateContext(pSignContext, true);
+ if (pSign) {
+ delete pSign;
+ pSignContext->reserved3 = NULL;
+ }
+}
+
+int DKIM_CALL
+DKIMVerifyInit(DKIMContext * pVerifyContext, DKIMVerifyOptions * pOptions)
+{
+ int nRet = DKIM_OUT_OF_MEMORY;
+ CDKIMVerify *pVerify = new CDKIMVerify;
+ if (pVerify) {
+ nRet = pVerify->Init(pOptions);
+ if (nRet != DKIM_SUCCESS)
+ delete pVerify;
+ }
+ if (nRet == DKIM_SUCCESS)
+ InitContext(pVerifyContext, false, pVerify);
+ return nRet;
+}
+
+int DKIM_CALL
+DKIMVerifyProcess(DKIMContext * pVerifyContext, char *szBuffer, int nBufLength)
+{
+ CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
+ if (pVerify)
+ return pVerify->Process(szBuffer, nBufLength, false);
+ return DKIM_INVALID_CONTEXT;
+}
+
+int DKIM_CALL
+DKIMVerifyResults( DKIMContext *pVerifyContext , int *sCount, int *sSize)
+{
+ CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
+ if (pVerify)
+ return pVerify->GetResults(sCount, sSize);
+ return DKIM_INVALID_CONTEXT;
+}
+
+int DKIM_CALL
+DKIMVerifyGetDetails(DKIMContext * pVerifyContext, int *nSigCount, DKIMVerifyDetails ** pDetails, char *szPractices)
+{
+ szPractices[0] = '\0';
+ CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
+ if (pVerify) {
+ strcpy(szPractices, pVerify->GetPractices());
+ return pVerify->GetDetails(nSigCount, pDetails);
+ }
+ return DKIM_INVALID_CONTEXT;
+}
+
+void DKIM_CALL
+DKIMVerifyFree(DKIMContext *pVerifyContext)
+{
+ CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
+ if (pVerify) {
+ delete pVerify;
+ pVerifyContext->reserved3 = NULL;
+ }
+}
+
+char *DKIM_CALL
+DKIMVerifyGetDomain(DKIMContext *pVerifyContext)
+{
+ CDKIMVerify *pVerify = (CDKIMVerify *) ValidateContext(pVerifyContext, false);
+ if (pVerify)
+ return pVerify->GetDomain();
+ return ((char *) 0);
+}
+
+char *DKIM_CALL
+DKIMVersion()
+{
+ return (char *) "1.1";
+}
+
+static char *DKIMErrorStrings[-1 - DKIM_MAX_ERROR] = {
+ (char *) "DKIM_FAIL",
+ (char *) "DKIM_BAD_SYNTAX",
+ (char *) "DKIM_SIGNATURE_BAD",
+ (char *) "DKIM_SIGNATURE_BAD_BUT_TESTING",
+ (char *) "DKIM_SIGNATURE_EXPIRED",
+ (char *) "DKIM_SELECTOR_INVALID",
+ (char *) "DKIM_SELECTOR_GRANULARITY_MISMATCH",
+ (char *) "DKIM_SELECTOR_KEY_REVOKED",
+ (char *) "DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG",
+ (char *) "DKIM_SELECTOR_DNS_TEMP_FAILURE",
+ (char *) "DKIM_SELECTOR_DNS_PERM_FAILURE",
+ (char *) "DKIM_SELECTOR_PUBLIC_KEY_INVALID",
+ (char *) "DKIM_NO_SIGNATURES",
+ (char *) "DKIM_NO_VALID_SIGNATURES",
+ (char *) "DKIM_BODY_HASH_MISMATCH",
+ (char *) "DKIM_SELECTOR_ALGORITHM_MISMATCH",
+ (char *) "DKIM_STAT_INCOMPAT"
+};
+
+char *DKIM_CALL
+DKIMGetErrorString(int ErrorCode)
+{
+ if (ErrorCode >= 0 || ErrorCode <= DKIM_MAX_ERROR)
+ return (char *) "Unknown";
+
+ else
+ return DKIMErrorStrings[-1 - ErrorCode];
+}
+
+void
+getversion_dkimfuncs_cpp()
+{
+ static char *x = (char *) "$Id: dkimfuncs.cpp,v 1.2 2009-03-26 15:11:12+05:30 Cprogrammer Exp mbhangui $";
+
+ x++;
+}
diff -Naur qmail-1.03.org/dkim.h qmail-1.03/dkim.h
--- qmail-1.03.org/dkim.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkim.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,179 @@
+/*
+ * $Log: dkim.h,v $
+ * Revision 1.5 2009-03-27 20:19:05+05:30 Cprogrammer
+ * major changes made for incorporating ADSP
+ *
+ * Revision 1.4 2009-03-26 19:28:15+05:30 Cprogrammer
+ * removed DKIM_3PS_PARTIAL_SUCCESS
+ *
+ * Revision 1.3 2009-03-26 15:11:33+05:30 Cprogrammer
+ * added ADSP
+ *
+ * Revision 1.2 2009-03-25 08:37:58+05:30 Cprogrammer
+ * changed definitions of constants to avoid clash between error and success
+ *
+ * Revision 1.1 2009-03-21 08:50:19+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#define DKIM_CALL
+#define MAKELONG(a,b) ((long)(((unsigned)(a) & 0xffff) | (((unsigned)(b) & 0xffff) << 16)))
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// DKIM Body hash versions
+#define DKIM_BODYHASH_ALLMAN_1 1
+#define DKIM_BODYHASH_IETF_1 2
+#define DKIM_BODYHASH_BOTH DKIM_BODYHASH_ALLMAN_1 | DKIM_BODYHASH_IETF_1
+
+// DKIM hash algorithms
+#define DKIM_HASH_SHA1 1
+#define DKIM_HASH_SHA256 2
+#define DKIM_HASH_SHA1_AND_256 DKIM_HASH_SHA1 | DKIM_HASH_SHA256
+
+// DKIM canonicalization methods
+#define DKIM_CANON_SIMPLE 1
+#define DKIM_CANON_NOWSP 2
+#define DKIM_CANON_RELAXED 3
+
+#define DKIM_SIGN_SIMPLE MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_SIMPLE)
+#define DKIM_SIGN_SIMPLE_RELAXED MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_SIMPLE)
+#define DKIM_SIGN_RELAXED MAKELONG(DKIM_CANON_RELAXED,DKIM_CANON_RELAXED)
+#define DKIM_SIGN_RELAXED_SIMPLE MAKELONG(DKIM_CANON_SIMPLE,DKIM_CANON_RELAXED)
+
+// DKIM Error codes
+#define DKIM_OUT_OF_MEMORY 6 // memory allocation failed
+#define DKIM_INVALID_CONTEXT 7 // DKIMContext structure invalid for this operation
+#define DKIM_NO_SENDER 8 // Could not find From: or Sender: header in message
+#define DKIM_BAD_PRIVATE_KEY 9 // Could not parse private key
+#define DKIM_BUFFER_TOO_SMALL 10 // Buffer passed in is not large enough
+
+// DKIM_SUCCESS // verify result: all signatures verified
+// signature result: signature verified
+#define DKIM_SUCCESS 0 // operation successful
+#define DKIM_FINISHED_BODY 1 // process result: no more message body is needed
+#define DKIM_PARTIAL_SUCCESS 2 // verify result: at least one but not all signatures verified
+#define DKIM_NEUTRAL 3 // verify result: no signatures verified but message is not suspicous
+#define DKIM_SUCCESS_BUT_EXTRA 4 // signature result: signature verified but it did not include all of the body
+#define DKIM_3PS_SIGNATURE 5 // 3rd-party signature
+
+// DKIM Verification Error codes
+#define DKIM_FAIL -1 // verify error: message is suspicious
+#define DKIM_BAD_SYNTAX -2 // signature error: DKIM-Signature could not parse or has bad tags/values
+#define DKIM_SIGNATURE_BAD -3 // signature error: RSA verify failed
+#define DKIM_SIGNATURE_BAD_BUT_TESTING -4 // signature error: RSA verify failed but testing
+#define DKIM_SIGNATURE_EXPIRED -5 // signature error: x= is old
+#define DKIM_SELECTOR_INVALID -6 // signature error: selector doesn't parse or contains invalid values
+#define DKIM_SELECTOR_GRANULARITY_MISMATCH -7 // signature error: selector g= doesn't match i=
+#define DKIM_SELECTOR_KEY_REVOKED -8 // signature error: selector p= empty
+#define DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG -9 // signature error: selector domain name too long to request
+#define DKIM_SELECTOR_DNS_TEMP_FAILURE -10 // signature error: temporary dns failure requesting selector
+#define DKIM_SELECTOR_DNS_PERM_FAILURE -11 // signature error: permanent dns failure requesting selector
+#define DKIM_SELECTOR_PUBLIC_KEY_INVALID -12 // signature error: selector p= value invalid or wrong format
+#define DKIM_NO_SIGNATURES -13 // process error, no sigs
+#define DKIM_NO_VALID_SIGNATURES -14 // process error, no valid sigs
+#define DKIM_BODY_HASH_MISMATCH -15 // sigature verify error: message body does not hash to bh value
+#define DKIM_SELECTOR_ALGORITHM_MISMATCH -16 // signature error: selector h= doesn't match signature a=
+#define DKIM_STAT_INCOMPAT -17 // signature error: incompatible v=
+#define DKIM_MAX_ERROR -18 // set this to 1 greater than the highest error code (but negative)
+
+#define DKIM_SSP_UNKNOWN 1 /*- some messages may be signed */
+#define DKIM_SSP_ALL 2 /*- all messages are signed, 3rd party allowed */
+#define DKIM_SSP_STRICT 3 /*- all messages are signed, no 3rd party allowed */
+#define DKIM_SSP_SCOPE 4 /*- the domain should be considered invalid */
+#define DKIM_SSP_TEMPFAIL 5 /*- Temporary Error */
+
+#define DKIM_ADSP_UNKNOWN 1 /*- some messages may be signed */
+#define DKIM_ADSP_ALL 2 /*- All message are signed with author signature */
+#define DKIM_ADSP_DISCARDABLE 3 /*- messages which fail verification are Discardable */
+#define DKIM_ADSP_SCOPE 4 /*- domain is out of scope */
+#define DKIM_ADSP_TEMPFAIL 5 /*- Temporary Error */
+
+
+// This function is called once for each header in the message
+// return 1 to include this header in the signature and 0 to exclude.
+typedef int (DKIM_CALL * DKIMHEADERCALLBACK) (const char *szHeader);
+
+// This function is called to retrieve a TXT record from DNS
+typedef int (DKIM_CALL * DKIMDNSCALLBACK) (const char *szFQDN, char *szBuffer, int nBufLen);
+
+typedef struct DKIMContext_t {
+ unsigned int reserved1;
+ unsigned int reserved2;
+ void *reserved3;
+} DKIMContext;
+
+typedef struct DKIMSignOptions_t {
+ int nCanon; // canonization
+ int nIncludeBodyLengthTag; // 0 = don't include l= tag, 1 = include l= tag
+ int nIncludeTimeStamp; // 0 = don't include t= tag, 1 = include t= tag
+ int nIncludeQueryMethod; // 0 = don't include q= tag, 1 = include q= tag
+ char szSelector[80]; // selector - required
+ char szDomain[256]; // domain - optional - if empty, domain is computed from sender
+ char szIdentity[256]; // for i= tag, if empty tag will not be included in sig
+ unsigned long expireTime; // for x= tag, if 0 tag will not be included in sig
+ DKIMHEADERCALLBACK pfnHeaderCallback; // header callback
+ char szRequiredHeaders[256]; // colon-separated list of headers that must be signed
+ int nHash; // use one of the DKIM_HASH_xx constants here
+ // even if not present in the message
+ int nIncludeCopiedHeaders; // 0 = don't include z= tag, 1 = include z= tag
+ int nIncludeBodyHash; // use one of the DKIM_BODYHASH_xx constants here
+} DKIMSignOptions;
+
+typedef struct DKIMVerifyOptions_t {
+ DKIMDNSCALLBACK pfnSelectorCallback; // selector record callback
+ DKIMDNSCALLBACK pfnPracticesCallback; // SSP record callback
+ int nHonorBodyLengthTag; // 0 = ignore l= tag, 1 = use l= tag to limit the amount of body verified
+ int nCheckPractices; // 0 = use default (unknown) practices, 1 = request and use sender's signing practices
+ int nSubjectRequired; // 0 = subject is required to be signed, 1 = not required
+ int nSaveCanonicalizedData; // 0 = canonicalized data is not saved, 1 = canonicalized data is saved
+ int nAccept3ps; // 0 = don't check 3rd party signature(s), 1 = check 3rd party signature(s)
+} DKIMVerifyOptions;
+
+typedef struct DKIMVerifyDetails_t {
+ char *szSignature;
+ char *DNS;
+ char *szCanonicalizedData;
+ int nResult;
+} DKIMVerifyDetails;
+
+int DKIM_CALL DKIMSignInit(DKIMContext *pSignContext, DKIMSignOptions * pOptions);
+int DKIM_CALL DKIMSignProcess(DKIMContext *pSignContext, char *szBuffer, int nBufLength);
+int DKIM_CALL DKIMSignGetSig(DKIMContext *pSignContext, char *szPrivKey, char *szSignature, int nSigLength);
+int DKIM_CALL DKIMSignGetSig2(DKIMContext *pSignContext, char *szPrivKey, char **pszSignature);
+void DKIM_CALL DKIMSignFree(DKIMContext *pSignContext);
+char *DKIM_CALL DKIMSignGetDomain(DKIMContext *pSignContext);
+
+int DKIM_CALL DKIMVerifyInit(DKIMContext *pVerifyContext, DKIMVerifyOptions * pOptions);
+int DKIM_CALL DKIMVerifyProcess(DKIMContext *pVerifyContext, char *szBuffer, int nBufLength);
+int DKIM_CALL DKIMVerifyResults(DKIMContext *pVerifyContext , int *sCount, int *sSize);
+int DKIM_CALL DKIMVerifyGetDetails(DKIMContext *pVerifyContext, int *nSigCount, DKIMVerifyDetails **pDetails, char *szPractices);
+char *DKIM_CALL DKIMVerifyGetDomain(DKIMContext *pVerifyContext);
+void DKIM_CALL DKIMVerifyFree(DKIMContext *pVerifyContext);
+char *DKIM_CALL DKIMVersion();
+char *DKIM_CALL DKIMGetErrorString(int ErrorCode);
+#include "macros.h"
+#ifdef __cplusplus
+}
+#endif
diff -Naur qmail-1.03.org/dkimsign.cpp qmail-1.03/dkimsign.cpp
--- qmail-1.03.org/dkimsign.cpp 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimsign.cpp 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,906 @@
+/*
+ * $Log: dkimsign.cpp,v $
+ * Revision 1.4 2009-04-15 21:32:12+05:30 Cprogrammer
+ * added DKIM-Signature, Received to list of excluded headers
+ *
+ * Revision 1.3 2009-03-26 15:11:46+05:30 Cprogrammer
+ * added GetDomain
+ *
+ * Revision 1.2 2009-03-21 11:57:19+05:30 Cprogrammer
+ * fixed indentation
+ *
+ * Revision 1.1 2009-03-21 08:43:11+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#define _strnicmp strncasecmp
+#define _stricmp strcasecmp
+#define LOWORD(l) ((unsigned)(l) & 0xffff)
+#define HIWORD(l) ((unsigned)(l) >> 16)
+#define HAVE_EVP_SHA256
+
+#include <string.h>
+#include <map>
+#include "dkim.h"
+#include "dkimsign.h"
+
+CDKIMSign::CDKIMSign()
+{
+ m_EmptyLineCount = 0;
+ m_pfnHdrCallback = NULL;
+ EVP_SignInit(&m_allman_sha1ctx, EVP_sha1());
+ EVP_SignInit(&m_Hdr_ietf_sha1ctx, EVP_sha1());
+ EVP_DigestInit(&m_Bdy_ietf_sha1ctx, EVP_sha1());
+#ifdef HAVE_EVP_SHA256
+ EVP_SignInit(&m_Hdr_ietf_sha256ctx, EVP_sha256());
+ EVP_DigestInit(&m_Bdy_ietf_sha256ctx, EVP_sha256());
+#endif
+}
+
+CDKIMSign::~CDKIMSign()
+{
+ EVP_MD_CTX_cleanup(&m_allman_sha1ctx);
+ EVP_MD_CTX_cleanup(&m_Hdr_ietf_sha1ctx);
+ EVP_MD_CTX_cleanup(&m_Bdy_ietf_sha1ctx);
+#ifdef HAVE_EVP_SHA256
+ EVP_MD_CTX_cleanup(&m_Hdr_ietf_sha256ctx);
+ EVP_MD_CTX_cleanup(&m_Bdy_ietf_sha256ctx);
+#endif
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Init - save the options
+//
+////////////////////////////////////////////////////////////////////////////////
+int
+CDKIMSign::Init(DKIMSignOptions * pOptions)
+{
+ int nRet = CDKIMBase::Init();
+ m_Canon = pOptions->nCanon;
+
+// as of draft 01, these are the only allowed signing types:
+ if ((m_Canon != DKIM_SIGN_SIMPLE_RELAXED) && (m_Canon != DKIM_SIGN_RELAXED) && (m_Canon != DKIM_SIGN_RELAXED_SIMPLE)) {
+ m_Canon = DKIM_SIGN_SIMPLE;
+ }
+ sSelector.assign(pOptions->szSelector);
+ m_pfnHdrCallback = pOptions->pfnHeaderCallback;
+ sDomain.assign(pOptions->szDomain);
+ m_IncludeBodyLengthTag = (pOptions->nIncludeBodyLengthTag != 0);
+ m_nBodyLength = 0;
+ m_ExpireTime = pOptions->expireTime;
+ sIdentity.assign(pOptions->szIdentity);
+ m_nIncludeTimeStamp = pOptions->nIncludeTimeStamp;
+ m_nIncludeQueryMethod = pOptions->nIncludeQueryMethod;
+ m_nIncludeCopiedHeaders = pOptions->nIncludeCopiedHeaders;
+ m_nIncludeBodyHash = pOptions->nIncludeBodyHash;
+
+// NOTE: the following line is not backwards compatible with MD 8.0.3
+// because the szRequiredHeaders member was added after the release
+//sRequiredHeaders.assign( pOptions->szRequiredHeaders );
+
+//make sure there is a colon after the last header in the list
+ if ((sRequiredHeaders.size() > 0) && sRequiredHeaders.at(sRequiredHeaders.size() - 1) != ':')
+ sRequiredHeaders.append(":");
+ m_nHash = pOptions->nHash;
+ m_bReturnedSigAssembled = false;
+ m_sCopiedHeaders.erase();
+ return nRet;
+}
+
+
+// Hash - update the hash
+void
+CDKIMSign::Hash(const char *szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly)
+{
+ if (bAllmanOnly) {
+ if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1)
+ EVP_SignUpdate(&m_allman_sha1ctx, szBuffer, nBufLength);
+ } else {
+ if (m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1)
+ EVP_SignUpdate(&m_allman_sha1ctx, szBuffer, nBufLength);
+ else
+ if (m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1) {
+ if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1)
+ EVP_SignUpdate(&m_allman_sha1ctx, szBuffer, nBufLength);
+#ifdef HAVE_EVP_SHA256
+ if (m_nHash & DKIM_HASH_SHA256) {
+ if (bHdr)
+ EVP_SignUpdate(&m_Hdr_ietf_sha256ctx, szBuffer, nBufLength);
+ else
+ EVP_DigestUpdate(&m_Bdy_ietf_sha256ctx, szBuffer, nBufLength);
+ }
+ if (m_nHash != DKIM_HASH_SHA256) {
+ if (bHdr)
+ EVP_SignUpdate(&m_Hdr_ietf_sha1ctx, szBuffer, nBufLength);
+ else
+ EVP_DigestUpdate(&m_Bdy_ietf_sha1ctx, szBuffer, nBufLength);
+ }
+#else
+ if (bHdr)
+ EVP_SignUpdate(&m_Hdr_ietf_sha1ctx, szBuffer, nBufLength);
+ else
+ EVP_DigestUpdate(&m_Bdy_ietf_sha1ctx, szBuffer, nBufLength);
+#endif
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// SignThisTag - return boolean whether or not to sign this tag
+//
+////////////////////////////////////////////////////////////////////////////////
+bool CDKIMSign::SignThisTag(const string &sTag)
+{
+ bool bRet = true;
+
+ if (_strnicmp(sTag.c_str(), "X-", 2) == 0
+ || _stricmp(sTag.c_str(), "Authentication-Results:") == 0
+ || _stricmp(sTag.c_str(), "DKIM-Signature:") == 0
+ || _stricmp(sTag.c_str(), "Received:") == 0
+ || _stricmp(sTag.c_str(), "Return-Path:") == 0)
+ {
+ bRet = false;
+ }
+ return bRet;
+}
+
+bool
+ConvertHeaderToQuotedPrintable(const char *source, char *dest)
+{
+ bool bConvert = false;
+
+// do quoted printable
+ static unsigned char hexchars[16] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+ unsigned char *d = (unsigned char *) dest;
+ for (const unsigned char *s = (const unsigned char *)source; *s != '\0'; s++) {
+ if (*s >= 33 && *s <= 126 && *s != '=' && *s != ':' && *s != ';' && *s != '|') {
+ *d++ = *s;
+ }
+
+ else {
+ bConvert = true;
+ *d++ = '=';
+ *d++ = hexchars[*s >> 4];
+ *d++ = hexchars[*s & 15];
+ }
+ }
+ *d = '\0';
+ return bConvert;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// GetHeaderParams - Extract any needed header parameters
+//
+////////////////////////////////////////////////////////////////////////////////
+void
+CDKIMSign::GetHeaderParams(const string & sHdr)
+{
+ if (_strnicmp(sHdr.c_str(), "X", 1) == 0)
+ return;
+ if (_strnicmp(sHdr.c_str(), "From:", 5) == 0)
+ sFrom.assign(sHdr.c_str() + 5);
+ if (_strnicmp(sHdr.c_str(), "Sender:", 7) == 0)
+ sSender.assign(sHdr.c_str() + 7);
+#if 1
+ if (_strnicmp(sHdr.c_str(), "Return-Path:", 12) == 0)
+ sReturnPath.assign(sHdr.c_str() + 12);
+#endif
+ if (m_nIncludeCopiedHeaders) {
+ string::size_type pos = sHdr.find(':');
+ if (pos != string::npos) {
+ string sTag, sValue;
+ char *workBuffer = new char[sHdr.size() * 3 + 1];
+ sTag.assign(sHdr.substr(0, pos));
+ sValue.assign(sHdr.substr(pos + 1, string::npos));
+ ConvertHeaderToQuotedPrintable(sTag.c_str(), workBuffer);
+ if (!m_sCopiedHeaders.empty()) {
+ m_sCopiedHeaders.append("|");
+ }
+ m_sCopiedHeaders.append(workBuffer);
+ m_sCopiedHeaders.append(":");
+ ConvertHeaderToQuotedPrintable(sValue.c_str(), workBuffer);
+ m_sCopiedHeaders.append(workBuffer);
+ delete[]workBuffer;
+ }
+ }
+}
+
+// ProcessHeaders - sign headers and save needed parameters
+int
+CDKIMSign::ProcessHeaders(void)
+{
+ map <string, list <string>::reverse_iterator> IterMap;
+ map <string, list <string>::reverse_iterator>::iterator IterMapIter;
+ list <string>::reverse_iterator riter;
+ list <string>::iterator iter;
+ string sTag;
+ bool bFromHeaderFound = false;
+
+ // walk the header list
+ for (iter = HeaderList.begin(); iter != HeaderList.end(); iter++) {
+ sTag.assign(*iter);
+ // look for a colon
+ string::size_type pos = sTag.find(':');
+ if (pos != string::npos) {
+ int nSignThisTag = 1;
+ // hack off anything past the colon
+ sTag.erase(pos + 1, string::npos);
+ // is this the From: header?
+ if (_stricmp(sTag.c_str(), "From:") == 0) {
+ bFromHeaderFound = true;
+ nSignThisTag = 1;
+ IsRequiredHeader(sTag); // remove from required header list
+ }
+ // is this in the list of headers that must be signed?
+ else
+ if (IsRequiredHeader(sTag))
+ nSignThisTag = 1;
+ else {
+ if (m_pfnHdrCallback)
+ nSignThisTag = m_pfnHdrCallback(iter->c_str());
+ else
+ nSignThisTag = SignThisTag(sTag) ? 1 : 0;
+ }
+ // save header parameters
+ GetHeaderParams(*iter);
+ if (nSignThisTag > 0) {
+ // add this tag to h=
+ hParam.append(sTag);
+ IterMapIter = IterMap.find(sTag);
+ riter = (IterMapIter == IterMap.end())? HeaderList.rbegin() : IterMapIter->second;
+ // walk the list in reverse looking for the last instance of this header
+ while (riter != HeaderList.rend()) {
+ if (_strnicmp(riter->c_str(), sTag.c_str(), sTag.size()) == 0) {
+ ProcessHeader(*riter);
+ // save the reverse iterator position for this tag
+ riter++;
+ IterMap[sTag] = riter;
+ break;
+ }
+ riter++;
+ }
+ }
+ }
+ }
+ Hash("\r\n", 2, true, true); // only for Allman sig
+ if (!bFromHeaderFound) {
+ string sFrom("From:");
+ hParam.append(sFrom);
+ IsRequiredHeader(sFrom); // remove from required header list
+ }
+ hParam.append(sRequiredHeaders);
+ if (hParam.at(hParam.size() - 1) == ':')
+ hParam.erase(hParam.size() - 1, string::npos);
+ return DKIM_SUCCESS;
+}
+
+char *DKIM_CALL
+CDKIMSign::GetDomain(void)
+{
+ if (ParseFromAddress() == false)
+ return ((char *) 0);
+ return ((char *) sDomain.c_str());
+}
+
+void
+CDKIMSign::ProcessHeader(const string & sHdr)
+{
+ switch (HIWORD(m_Canon)) {
+ case DKIM_CANON_SIMPLE:
+ Hash(sHdr.c_str(), sHdr.size(), true);
+ Hash("\r\n", 2, true);
+ break;
+ case DKIM_CANON_NOWSP:
+ {
+ string sTemp = sHdr;
+ RemoveSWSP(sTemp);
+ // convert characters before ':' to lower case
+ for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) {
+ if (*s >= 'A' && *s <= 'Z')
+ *s += 'a' - 'A';
+ }
+ Hash(sTemp.c_str(), sTemp.size(), true);
+ Hash("\r\n", 2, true);
+ }
+ break;
+ case DKIM_CANON_RELAXED:
+ {
+ string sTemp = RelaxHeader(sHdr);
+ Hash(sTemp.c_str(), sTemp.length(), true);
+ Hash("\r\n", 2, true);
+ }
+ break;
+ }
+}
+
+int CDKIMSign::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
+{
+ switch (LOWORD(m_Canon)) {
+ case DKIM_CANON_SIMPLE:
+ if (nBufLength > 0) {
+ while (m_EmptyLineCount > 0) {
+ Hash("\r\n", 2, false);
+ m_nBodyLength += 2;
+ m_EmptyLineCount--;
+ }
+ Hash(szBuffer, nBufLength, false);
+ Hash("\r\n", 2, false);
+ m_nBodyLength += nBufLength + 2;
+ } else {
+ m_EmptyLineCount++;
+ if (bEOF) {
+ Hash("\r\n", 2, false);
+ m_nBodyLength += 2;
+ }
+ }
+ break;
+ case DKIM_CANON_NOWSP:
+ RemoveSWSP(szBuffer, nBufLength);
+ if (nBufLength > 0) {
+ Hash(szBuffer, nBufLength, false);
+ m_nBodyLength += nBufLength;
+ }
+ break;
+ case DKIM_CANON_RELAXED:
+ CompressSWSP(szBuffer, nBufLength);
+ if (nBufLength > 0) {
+ while (m_EmptyLineCount > 0) {
+ Hash("\r\n", 2, false);
+ m_nBodyLength += 2;
+ m_EmptyLineCount--;
+ }
+ Hash(szBuffer, nBufLength, false);
+ m_nBodyLength += nBufLength;
+ if (!bEOF) {
+ Hash("\r\n", 2, false);
+ m_nBodyLength += 2;
+ }
+ } else
+ m_EmptyLineCount++;
+ break;
+ }
+ return DKIM_SUCCESS;
+}
+
+bool CDKIMSign::ParseFromAddress(void)
+{
+ string::size_type pos;
+ string sAddress;
+ char *p, *at;
+
+#if 1
+ /* thanks to fred */
+ if (!sReturnPath.empty())
+ sAddress.assign(sReturnPath);
+ else
+#endif
+ if (!sSender.empty())
+ sAddress.assign(sSender);
+ else
+ if (!sFrom.empty())
+ sAddress.assign(sFrom);
+ else
+ return false;
+ // simple for now, beef it up later
+ // remove '<' and anything before it
+ pos = sAddress.find('<');
+ if (pos != string::npos)
+ sAddress.erase(0, pos);
+ // remove '>' and anything after it
+ pos = sAddress.find('>');
+ if (pos != string::npos)
+ sAddress.erase(pos, string::npos);
+ // look for '@' symbol
+ pos = sAddress.find('@');
+ if (pos == string::npos)
+ return false;
+ if (sDomain.empty()) {
+ p = getenv("DKIMDOMAIN");
+ if (p && *p)
+ {
+ if (!(at = strchr(p, '@')))
+ at = p;
+ else
+ at++;
+ sDomain.assign(at);
+ } else
+ sDomain.assign(sAddress.c_str() + pos + 1);
+ RemoveSWSP(sDomain);
+ }
+ return true;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// InitSig - initialize signature folding algorithm
+//
+////////////////////////////////////////////////////////////////////////////////
+void
+CDKIMSign::InitSig(void)
+{
+ m_sSig.reserve(1024);
+ m_sSig.assign("DKIM-Signature:");
+ m_nSigPos = m_sSig.size();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AddTagToSig - add tag and value to signature folding if necessary
+// if bFold, fold at cbrk char
+//
+////////////////////////////////////////////////////////////////////////////////
+void CDKIMSign::AddTagToSig(char *Tag, const string & sValue, char cbrk, bool bFold)
+{
+ int
+ nTagLen = strlen(Tag);
+ AddInterTagSpace((!bFold) ? sValue.size() + nTagLen + 2 : nTagLen + 2);
+ m_sSig.append(Tag);
+ m_sSig.append("=");
+ m_nSigPos += 1 + nTagLen;
+ if (!bFold) {
+ m_sSig.append(sValue);
+ m_nSigPos += sValue.size();
+ }
+
+ else {
+ AddFoldedValueToSig(sValue, cbrk);
+ }
+ m_sSig.append(";");
+ m_nSigPos++;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AddTagToSig - add tag and numeric value to signature folding if necessary
+//
+////////////////////////////////////////////////////////////////////////////////
+void CDKIMSign::AddTagToSig(char *Tag, unsigned long nValue)
+{
+ char szValue[64];
+ sprintf(szValue, "%u", nValue);
+ AddTagToSig(Tag, szValue, 0, false);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AddInterTagSpace - add space or fold here
+//
+////////////////////////////////////////////////////////////////////////////////
+void CDKIMSign::AddInterTagSpace(int nSizeOfNextTag)
+{
+ if (m_nSigPos + nSizeOfNextTag + 1 > OptimalHeaderLineLength) {
+ m_sSig.append("\n\t");
+ m_nSigPos = 1;
+ }
+
+ else {
+ m_sSig.append(" ");
+ m_nSigPos++;
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// AddTagToSig - add value to signature folding if necessary
+// if cbrk == 0 fold anywhere, otherwise fold only at cbrk
+//
+////////////////////////////////////////////////////////////////////////////////
+void CDKIMSign::AddFoldedValueToSig(const string & sValue, char cbrk)
+{
+ string::size_type pos = 0;
+ if (cbrk == 0) {
+
+ // fold anywhere
+ while (pos < sValue.size()) {
+ string::size_type len = OptimalHeaderLineLength - m_nSigPos;
+ if (len > sValue.size() - pos)
+ len = sValue.size() - pos;
+ m_sSig.append(sValue.substr(pos, len));
+ m_nSigPos += len;
+ pos += len;
+ if (pos < sValue.size()) {
+ m_sSig.append("\n\t");
+ m_nSigPos = 1;
+ }
+ }
+ }
+
+ else {
+
+ // fold only at cbrk
+ while (pos < sValue.size()) {
+ string::size_type len = OptimalHeaderLineLength - m_nSigPos;
+ string::size_type brkpos;
+ if (sValue.size() - pos < len) {
+ brkpos = sValue.size() - 1;
+ }
+
+ else {
+ brkpos = sValue.rfind(cbrk, pos + len);
+ }
+ if (brkpos == string::npos || brkpos < pos) {
+ brkpos = sValue.find(cbrk, pos);
+ if (brkpos == string::npos) {
+ brkpos = sValue.size();
+ }
+ }
+ len = brkpos - pos + 1;
+ m_sSig.append(sValue.substr(pos, len));
+ m_nSigPos += len;
+ pos += len;
+ if (pos < sValue.size()) {
+
+ //m_sSig.append( "\r\n\t" );
+ m_sSig.append("\n\t");
+ m_nSigPos = 1;
+ }
+ }
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// GetSig - compute hash and return signature header in szSignature
+//
+////////////////////////////////////////////////////////////////////////////////
+int CDKIMSign::GetSig(char *szPrivKey, char *szSignature, int nSigLength)
+{
+ if (szPrivKey == NULL) {
+ return DKIM_BAD_PRIVATE_KEY;
+ }
+ if (szSignature == NULL) {
+ return DKIM_BUFFER_TOO_SMALL;
+ }
+ int nRet = AssembleReturnedSig(szPrivKey);
+ if (nRet != DKIM_SUCCESS)
+ return nRet;
+ if (m_sReturnedSig.size() + 1 < nSigLength)
+ strcpy(szSignature, m_sReturnedSig.c_str());
+ else
+ return DKIM_BUFFER_TOO_SMALL;
+ return DKIM_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// GetSig - compute hash and return signature header in szSignature
+//
+////////////////////////////////////////////////////////////////////////////////
+int CDKIMSign::GetSig2(char *szPrivKey, char **pszSignature)
+{
+ if (szPrivKey == NULL) {
+ return DKIM_BAD_PRIVATE_KEY;
+ }
+ if (pszSignature == NULL) {
+ return DKIM_BUFFER_TOO_SMALL;
+ }
+ int nRet = AssembleReturnedSig(szPrivKey);
+ if (nRet != DKIM_SUCCESS)
+ return nRet;
+ *pszSignature = (char *) m_sReturnedSig.c_str();
+ return DKIM_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// IsRequiredHeader - Check if header in required list. If so, delete
+// header from list.
+//
+////////////////////////////////////////////////////////////////////////////////
+bool CDKIMSign::IsRequiredHeader(const string & sTag)
+{
+ string::size_type start = 0;
+ string::size_type end = sRequiredHeaders.find(':');
+ while (end != string::npos) {
+
+ // check for a zero-length header
+ if (start == end) {
+ sRequiredHeaders.erase(start, 1);
+ }
+
+ else {
+ if (_stricmp(sTag.c_str(), sRequiredHeaders.substr(start, end - start + 1).c_str()) == 0) {
+ sRequiredHeaders.erase(start, end - start + 1);
+ return true;
+ }
+
+ else {
+ start = end + 1;
+ }
+ }
+ end = sRequiredHeaders.find(':', start);
+ }
+ return false;
+}
+
+int CDKIMSign::ConstructSignature(char *szPrivKey, bool bUseIetfBodyHash, bool bUseSha256)
+{
+ string sSignedSig;
+ unsigned char *sig;
+ EVP_PKEY *pkey;
+ BIO *bio, *b64;
+ unsigned int siglen;
+ int size;
+ int len;
+ char *buf;
+ int pos = 0;
+
+// construct the DKIM-Signature: header and add to hash
+ InitSig();
+ if (bUseIetfBodyHash) {
+ AddTagToSig((char *) "v", (char *) "1", 0, false);
+ }
+#ifdef HAVE_EVP_SHA256
+ AddTagToSig((char *) "a", bUseSha256 ? "rsa-sha256" : "rsa-sha1", 0, false);
+#else
+ AddTagToSig((char *) "a", "rsa-sha1", 0, false);
+#endif
+ switch (m_Canon) {
+ case DKIM_SIGN_SIMPLE:
+ AddTagToSig((char *) "c", "simple", 0, false);
+ break;
+ case DKIM_SIGN_SIMPLE_RELAXED:
+ AddTagToSig((char *) "c", "simple/relaxed", 0, false);
+ break;
+ case DKIM_SIGN_RELAXED:
+ AddTagToSig((char *) "c", "relaxed/relaxed", 0, false);
+ break;
+ case DKIM_SIGN_RELAXED_SIMPLE:
+ AddTagToSig((char *) "c", "relaxed", 0, false);
+ break;
+ }
+ AddTagToSig((char *) "d", sDomain, 0, false);
+ AddTagToSig((char *) "s", sSelector, 0, false);
+ if (m_IncludeBodyLengthTag) {
+ AddTagToSig((char *) "l", m_nBodyLength);
+ }
+ if (m_nIncludeTimeStamp != 0) {
+ time_t t;
+ time(&t);
+ AddTagToSig((char *) "t", t);
+ }
+ if (m_ExpireTime != 0) {
+ AddTagToSig((char *) "x", m_ExpireTime);
+ }
+ if (!sIdentity.empty()) {
+ AddTagToSig((char *) "i", sIdentity, 0, false);
+ }
+ if (m_nIncludeQueryMethod) {
+ AddTagToSig((char *) "q", bUseIetfBodyHash ? "dns/txt" : "dns", 0, false);
+ }
+ AddTagToSig((char *) "h", hParam, ':', true);
+ if (m_nIncludeCopiedHeaders) {
+ AddTagToSig((char *) "z", m_sCopiedHeaders, 0, true);
+ }
+ if (bUseIetfBodyHash) {
+ unsigned char Hash[EVP_MAX_MD_SIZE];
+ unsigned int nHashLen = 0;
+#ifdef HAVE_EVP_SHA256
+ EVP_DigestFinal(bUseSha256 ? &m_Bdy_ietf_sha256ctx : &m_Bdy_ietf_sha1ctx, Hash, &nHashLen);
+#else
+ EVP_DigestFinal(&m_Bdy_ietf_sha1ctx, Hash, &nHashLen);
+#endif
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ return DKIM_OUT_OF_MEMORY;
+ }
+ b64 = BIO_new(BIO_f_base64());
+ if (!b64) {
+ BIO_free(bio);
+ return DKIM_OUT_OF_MEMORY;
+ }
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ BIO_push(b64, bio);
+ if (BIO_write(b64, Hash, nHashLen) < nHashLen) {
+ BIO_free_all(b64);
+ return DKIM_OUT_OF_MEMORY;
+ }
+ BIO_flush(b64);
+ len = nHashLen * 2;
+ buf = new char[len];
+ if (buf == NULL) {
+ BIO_free_all(b64);
+ return DKIM_OUT_OF_MEMORY;
+ }
+ size = BIO_read(bio, buf, len);
+ BIO_free_all(b64);
+ // this should never happen
+ if (size >= len) {
+ delete[]buf;
+ return DKIM_OUT_OF_MEMORY;
+ }
+ buf[size] = '\0';
+ AddTagToSig((char *) "bh", buf, 0, true);
+ delete[]buf;
+ }
+ AddInterTagSpace(3);
+ m_sSig.append("b=");
+ m_nSigPos += 2;
+ // Force a full copy - no reference copies please
+ sSignedSig.assign(m_sSig.c_str());
+ // note that since we're not calling hash here, need to dump this
+ // to the debug file if you want the full canonical form
+ string sTemp;
+ if (HIWORD(m_Canon) == DKIM_CANON_RELAXED)
+ sTemp = RelaxHeader(sSignedSig);
+ else
+ sTemp = sSignedSig.c_str();
+ if (bUseIetfBodyHash) {
+#ifdef HAVE_EVP_SHA256
+ EVP_SignUpdate(bUseSha256 ? &m_Hdr_ietf_sha256ctx : &m_Hdr_ietf_sha1ctx, sTemp.c_str(), sTemp.size());
+#else
+ EVP_SignUpdate(&m_Hdr_ietf_sha1ctx, sTemp.c_str(), sTemp.size());
+#endif
+ } else
+ EVP_SignUpdate(&m_allman_sha1ctx, sTemp.c_str(), sTemp.size());
+ if (!(bio = BIO_new_mem_buf(szPrivKey, -1)))
+ return DKIM_OUT_OF_MEMORY;
+ pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+ if (!pkey) {
+ return DKIM_BAD_PRIVATE_KEY;
+ }
+ siglen = EVP_PKEY_size(pkey);
+ int nSignRet;
+ sig = (unsigned char *) OPENSSL_malloc(siglen);
+ if (sig == NULL) {
+ EVP_PKEY_free(pkey);
+ return DKIM_OUT_OF_MEMORY;
+ }
+ if (bUseIetfBodyHash) {
+#ifdef HAVE_EVP_SHA256
+ nSignRet = EVP_SignFinal(bUseSha256 ? &m_Hdr_ietf_sha256ctx : &m_Hdr_ietf_sha1ctx, sig, &siglen, pkey);
+#else
+ nSignRet = EVP_SignFinal(&m_Hdr_ietf_sha1ctx, sig, &siglen, pkey);
+#endif
+ } else
+ nSignRet = EVP_SignFinal(&m_allman_sha1ctx, sig, &siglen, pkey);
+ EVP_PKEY_free(pkey);
+ if (!nSignRet) {
+ OPENSSL_free(sig);
+ return DKIM_BAD_PRIVATE_KEY; // key too small
+ }
+ bio = BIO_new(BIO_s_mem());
+ if (!bio) {
+ return DKIM_OUT_OF_MEMORY;
+ }
+ b64 = BIO_new(BIO_f_base64());
+ if (!b64) {
+ BIO_free(bio);
+ return DKIM_OUT_OF_MEMORY;
+ }
+ BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
+ BIO_push(b64, bio);
+ if (BIO_write(b64, sig, siglen) < siglen) {
+ OPENSSL_free(sig);
+ BIO_free_all(b64);
+ return DKIM_OUT_OF_MEMORY;
+ }
+ BIO_flush(b64);
+ OPENSSL_free(sig);
+ len = siglen * 2;
+ buf = new char[len];
+ if (buf == NULL) {
+ BIO_free_all(b64);
+ return DKIM_OUT_OF_MEMORY;
+ }
+ size = BIO_read(bio, buf, len);
+ BIO_free_all(b64);
+ // this should never happen
+ if (size >= len) {
+ delete[]buf;
+ return DKIM_OUT_OF_MEMORY;
+ }
+ buf[size] = '\0';
+ AddFoldedValueToSig(buf, 0);
+ delete[]buf;
+ return DKIM_SUCCESS;
+}
+
+int CDKIMSign::AssembleReturnedSig(char *szPrivKey)
+{
+ int nRet;
+ if (m_bReturnedSigAssembled)
+ return DKIM_SUCCESS;
+ ProcessFinal();
+ if (ParseFromAddress() == false) {
+ //return DKIM_NO_SENDER;
+ }
+ Hash("\r\n", 2, true, true); // only for Allman sig
+ string allmansha1sig,
+#ifdef HAVE_EVP_SHA256
+ ietfsha256Sig,
+#endif
+ ietfsha1Sig;
+ if (m_nIncludeBodyHash < DKIM_BODYHASH_IETF_1) {
+ nRet = ConstructSignature(szPrivKey, false, false);
+ if (nRet == DKIM_SUCCESS)
+ allmansha1sig.assign(m_sSig);
+ else
+ return nRet;
+ } else
+ if (m_nIncludeBodyHash & DKIM_BODYHASH_IETF_1) {
+ if (m_nIncludeBodyHash & DKIM_BODYHASH_ALLMAN_1) {
+ if ((nRet = ConstructSignature(szPrivKey, false, false)) == DKIM_SUCCESS)
+ allmansha1sig.assign(m_sSig);
+ else
+ return nRet;
+ }
+#ifdef HAVE_EVP_SHA256
+ if (m_nHash & DKIM_HASH_SHA256) {
+ if ((nRet = ConstructSignature(szPrivKey, true, true)) == DKIM_SUCCESS)
+ ietfsha256Sig.assign(m_sSig);
+ else
+ return nRet;
+ }
+ if (m_nHash != DKIM_HASH_SHA256) {
+ if ((nRet = ConstructSignature(szPrivKey, true, false)) == DKIM_SUCCESS)
+ ietfsha1Sig.assign(m_sSig);
+ else
+ return nRet;
+ }
+#else
+ if ((nRet = ConstructSignature(szPrivKey, true, false)) == DKIM_SUCESS)
+ ietfsha1Sig.assign(m_sSig);
+ else
+ return nRet;
+#endif
+ }
+ m_sReturnedSig.assign(allmansha1sig);
+ if (!ietfsha1Sig.empty()) {
+ if (!m_sReturnedSig.empty())
+ m_sReturnedSig.append("\n");
+ m_sReturnedSig.append(ietfsha1Sig);
+ }
+#ifdef HAVE_EVP_SHA256
+ if (!ietfsha256Sig.empty()) {
+ if (!m_sReturnedSig.empty())
+ m_sReturnedSig.append("\n");
+ m_sReturnedSig.append(ietfsha256Sig);
+ }
+#endif
+ m_bReturnedSigAssembled = true;
+ return DKIM_SUCCESS;
+}
+
+void
+getversion_dkimsign_cpp()
+{
+ static char *x = (char *) "$Id: dkimsign.cpp,v 1.4 2009-04-15 21:32:12+05:30 Cprogrammer Exp mbhangui $";
+
+ x++;
+}
diff -Naur qmail-1.03.org/dkimsign.h qmail-1.03/dkimsign.h
--- qmail-1.03.org/dkimsign.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimsign.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,93 @@
+/*
+ * $Log: dkimsign.h,v $
+ * Revision 1.2 2009-03-26 15:11:57+05:30 Cprogrammer
+ * fixed indentation
+ *
+ * Revision 1.1 2009-03-21 08:50:21+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef DKIMSIGN_H
+#define DKIMSIGN_H
+
+#include "dkimbase.h"
+
+class CDKIMSign:public CDKIMBase {
+public:
+
+ CDKIMSign();
+ ~CDKIMSign();
+ int Init(DKIMSignOptions * pOptions);
+ int GetSig(char *szPrivKey, char *szSignature, int nSigLength);
+ int GetSig2(char *szPrivKey, char **pszSignature);
+ virtual int ProcessHeaders(void);
+ virtual int ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
+ enum CKDKIMConstants { OptimalHeaderLineLength = 65 };
+ char *DKIM_CALL GetDomain(void);
+
+protected:
+ void Hash(const char *szBuffer, int nBufLength, bool bHdr, bool bAllmanOnly = false);
+ bool SignThisTag(const string & sTag);
+ void GetHeaderParams(const string & sHdr);
+ void ProcessHeader(const string & sHdr);
+ bool ParseFromAddress(void);
+ void InitSig(void);
+ void AddTagToSig(char *Tag, const string & sValue, char cbrk, bool bFold);
+ void AddTagToSig(char *Tag, unsigned long nValue);
+ void AddInterTagSpace(int nSizeOfNextTag);
+ void AddFoldedValueToSig(const string & sValue, char cbrk);
+ bool IsRequiredHeader(const string & sTag);
+ int ConstructSignature(char *szPrivKey, bool bUseIetfBodyHash, bool bUseSha256);
+ int AssembleReturnedSig(char *szPrivKey);
+ EVP_MD_CTX m_Hdr_ietf_sha1ctx; /* the header hash for ietf sha1 */
+ EVP_MD_CTX m_Hdr_ietf_sha256ctx; /* the header hash for ietf sha256 */
+ EVP_MD_CTX m_Bdy_ietf_sha1ctx; /* the body hash for ietf sha1 */
+ EVP_MD_CTX m_Bdy_ietf_sha256ctx; /* the body hash for ietf sha256 */
+ EVP_MD_CTX m_allman_sha1ctx; /* the hash for allman sha1 */
+ int m_Canon; // canonization method
+ int m_EmptyLineCount;
+ string hParam;
+ string sFrom;
+ string sSender;
+ string sSelector;
+ string sReturnPath;
+ string sDomain;
+ string sIdentity; // for i= tag, if empty tag will not be included in sig
+ string sRequiredHeaders;
+ bool m_IncludeBodyLengthTag;
+ int m_nBodyLength;
+ time_t m_ExpireTime;
+ int m_nIncludeTimeStamp; // 0 = don't include t= tag, 1 = include t= tag
+ int m_nIncludeQueryMethod; // 0 = don't include q= tag, 1 = include q= tag
+ int m_nHash; // use one of the DKIM_HASH_xx constants here
+ int m_nIncludeCopiedHeaders; // 0 = don't include z= tag, 1 = include z= tag
+ int m_nIncludeBodyHash; // 0 = calculate sig using draft 0, 1 = include bh= tag and
+ // use new signature computation algorithm
+ DKIMHEADERCALLBACK m_pfnHdrCallback;
+ string m_sSig;
+ int m_nSigPos;
+ string m_sReturnedSig;
+ bool m_bReturnedSigAssembled;
+ string m_sCopiedHeaders;
+};
+
+#endif // DKIMSIGN_H
diff -Naur qmail-1.03.org/dkimtest.c qmail-1.03/dkimtest.c
--- qmail-1.03.org/dkimtest.c 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimtest.c 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,850 @@
+/*
+ * $Log: dkim.c,v $
+ * Revision 1.10 2009-04-15 21:30:32+05:30 Cprogrammer
+ * added DKIM-Signature to list of excluded headers
+ *
+ * Revision 1.9 2009-04-15 20:45:04+05:30 Cprogrammer
+ * corrected usage
+ *
+ * Revision 1.8 2009-04-05 19:04:44+05:30 Cprogrammer
+ * improved formating of usage
+ *
+ * Revision 1.7 2009-04-03 12:05:25+05:30 Cprogrammer
+ * minor changes on usage display
+ *
+ * Revision 1.6 2009-03-28 20:15:23+05:30 Cprogrammer
+ * invoke DKIMVerifyGetDetails()
+ *
+ * Revision 1.5 2009-03-27 20:43:48+05:30 Cprogrammer
+ * added HAVE_OPENSSL_EVP_H conditional
+ *
+ * Revision 1.4 2009-03-27 20:19:28+05:30 Cprogrammer
+ * added ADSP
+ *
+ * Revision 1.3 2009-03-26 15:10:53+05:30 Cprogrammer
+ * added ADSP
+ *
+ * Revision 1.2 2009-03-25 08:37:45+05:30 Cprogrammer
+ * added dkim_error
+ *
+ * Revision 1.1 2009-03-21 08:24:47+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * (cat /tmp/test.msg|./dkimtest -z 2 -b 1 -y private \
+ * -s /var/indimail/control/domainkeys/private ;cat /tmp/test.msg )|./dkimtest -v
+ */
+#ifndef __cplusplus
+#error A C++ compiler is required!
+#endif
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+#define HAVE_EVP_SHA256
+#endif
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <time.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "dkim.h"
+#include "dkimdns.h"
+
+#ifdef HAVE_OPENSSL_EVP_H
+#include <openssl/evp.h>
+#define DKIM_MALLOC(s) OPENSSL_malloc(s)
+#define DKIM_MFREE(s) OPENSSL_free(s); s = NULL;
+#else
+#define DKIM_MALLOC(s) malloc(s)
+#define DKIM_MFREE(s) free(s); s = NULL;
+#endif
+
+int DKIM_CALL
+SignThisHeader(const char *szHeader)
+{
+ if (!strncasecmp(szHeader, "X-", 2)
+ || !strncasecmp(szHeader, "Received:", 9)
+ || !strncasecmp(szHeader, "DKIM-Signature:", 15)
+ || !strncasecmp(szHeader, "Authentication-Results:", 23)
+ || !strncasecmp(szHeader, "DomainKey-Signature", 19)
+ || !strncasecmp(szHeader, "Return-Path:", 12))
+ {
+ return 0;
+ }
+ return 1;
+}
+
+char *program;
+
+void
+usage()
+{
+#ifdef HAVE_EVP_SHA256
+ fprintf(stderr, "usage: %s [-lqthvH] [-b <1|2|3>] [-c <r|s|t|u>] [-p <1|2>]\n\t[-d domain] [-i you@domain] [-x expire_time] [-z hash] [-y selector] -s privkeyfile\n", program);
+#else
+ fprintf(stderr, "usage: %s [-lqthvH] [-b <1|2|3>] [-c <r|s|t|u>] [-p <1|2>]\n\t[-d domain] [-i you@domain] [-x expire_time] [-y selector] -s privkeyfile\n", program);
+#endif
+ fprintf(stderr, "p <ssp|adsp> 0 - disable practice, 1- SSP, or 2 - ADSP verification\n");
+ fprintf(stderr, "l include body length tag\n");
+ fprintf(stderr, "q include query method tag\n");
+ fprintf(stderr, "t include a timestamp tag\n");
+ fprintf(stderr, "h include Copied Headers\n");
+ fprintf(stderr, "v verify the message\n");
+ fprintf(stderr, "b <standard> 1 - allman, 2 - ietf or 3 - both\n");
+ fprintf(stderr, "c <canonicalization> r for relaxed [DEFAULT], s - simple, t relaxed/simple, u - simple/relaxed\n");
+ fprintf(stderr, "d <domain> the domain tag, if not provided, determined from the sender/from header\n");
+ fprintf(stderr, "i <identity> the identity, if not provided it will not be included\n");
+ fprintf(stderr, "x <expire_time> the expire time in seconds since epoch ( DEFAULT = current time + 604800)\n");
+ fprintf(stderr, " if set to - then it will not be included\n");
+#ifdef HAVE_EVP_SHA256
+ fprintf(stderr, "z <hash> 1 for sha1, 2 for sha256, 3 for both\n");
+#endif
+ fprintf(stderr, "y <selector> the selector tag DEFAULT=private\n");
+ fprintf(stderr, "s <privkeyfile> sign the message using the private key in privkeyfile\n");
+ fprintf(stderr, "H this help\n");
+ exit(1);
+}
+
+unsigned int str_chr(char *s, int c)
+{
+ register char ch;
+ register char *t;
+
+ ch = c;
+ t = s;
+ for (;;)
+ {
+ if (!*t)
+ break;
+ if (*t == ch)
+ break;
+ ++t;
+ if (!*t)
+ break;
+ if (*t == ch)
+ break;
+ ++t;
+ if (!*t)
+ break;
+ if (*t == ch)
+ break;
+ ++t;
+ if (!*t)
+ break;
+ if (*t == ch)
+ break;
+ ++t;
+ }
+ return t - s;
+}
+
+void
+dkim_error(int e)
+{
+ switch (e)
+ {
+ case DKIM_OUT_OF_MEMORY:
+ fprintf(stderr, "memory allocation failed\n");
+ break;
+ case DKIM_INVALID_CONTEXT:
+ fprintf(stderr, "DKIMContext structure invalid for this operation\n");
+ break;
+ case DKIM_NO_SENDER:
+ fprintf(stderr, "Could not find From: or Sender: header in message\n");
+ break;
+ case DKIM_BAD_PRIVATE_KEY:
+ fprintf(stderr, "Could not parse private key\n");
+ break;
+ case DKIM_BUFFER_TOO_SMALL:
+ fprintf(stderr, "Buffer passed in is not large enough");
+ break;
+ }
+}
+
+/*
+ * Allows you to add the headers contain the results and DKIM ADSP
+ */
+int writeHeader(int ret, int resDKIMSSP, int resDKIMADSP, int useSSP, int useADSP )
+{
+ char *dkimStatus, *sspStatus, *adspStatus;
+
+ dkimStatus = sspStatus = adspStatus = (char *) "";
+ switch (ret)
+ {
+ case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
+ dkimStatus = (char *) "signature result: signature verified but it did not include all of the body";
+ break;
+ case DKIM_NEUTRAL: /*- 3 verify result: no signatures verified but message is not suspicious */
+ dkimStatus = (char *) "verify result: no signatures verified but message is not suspicious";
+ break;
+ case DKIM_PARTIAL_SUCCESS: /*- 2 verify result: at least one but not all signatures verified */
+ dkimStatus = (char *) "verify result: at least none but not all signatures verified";
+ break;
+ case DKIM_FINISHED_BODY: /*- 1 process result: no more message body is needed */
+ dkimStatus = (char *) "process result: no more message body is needed";
+ break;
+ case DKIM_SUCCESS:
+ dkimStatus = (char *) "good ";
+ break;
+ case DKIM_FAIL:
+ dkimStatus = (char *) "failed ";
+ break;
+ case DKIM_BAD_SYNTAX:
+ dkimStatus = (char *) "signature error: DKIM-Signature could not parse or has bad tags/values";
+ break;
+ case DKIM_SIGNATURE_BAD:
+ dkimStatus = (char *) "signature error: RSA verify failed";
+ break;
+ case DKIM_SIGNATURE_BAD_BUT_TESTING:
+ dkimStatus = (char *) "signature error: RSA verify failed but testing";
+ break;
+ case DKIM_SIGNATURE_EXPIRED:
+ dkimStatus = (char *) "signature error: x= is old";
+ break;
+ case DKIM_SELECTOR_INVALID:
+ dkimStatus = (char *) "signature error: selector doesn't parse or contains invalid values";
+ break;
+ case DKIM_SELECTOR_GRANULARITY_MISMATCH:
+ dkimStatus = (char *) "signature error: selector g= doesn't match i=";
+ break;
+ case DKIM_SELECTOR_KEY_REVOKED:
+ dkimStatus = (char *) "signature error: selector p= empty";
+ break;
+ case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
+ dkimStatus = (char *) "signature error: selector domain name too long to request";
+ break;
+ case DKIM_SELECTOR_DNS_TEMP_FAILURE:
+ dkimStatus = (char *) "signature error: temporary dns failure requesting selector";
+ break;
+ case DKIM_SELECTOR_DNS_PERM_FAILURE:
+ dkimStatus = (char *) "signature error: permanent dns failure requesting selector";
+ break;
+ case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
+ dkimStatus = (char *) "signature error: selector p= value invalid or wrong format";
+ break;
+ case DKIM_NO_SIGNATURES:
+ dkimStatus = (char *) "process error, no sigs";
+ break;
+ case DKIM_NO_VALID_SIGNATURES:
+ dkimStatus = (char *) "process error, no valid sigs";
+ break;
+ case DKIM_BODY_HASH_MISMATCH:
+ dkimStatus = (char *) "sigature verify error: message body does not hash to bh value";
+ break;
+ case DKIM_SELECTOR_ALGORITHM_MISMATCH:
+ dkimStatus = (char *) "signature error: selector h= doesn't match signature a=";
+ break;
+ case DKIM_STAT_INCOMPAT:
+ dkimStatus = (char *) "signature error: incompatible v=";
+ break;
+ default:
+ dkimStatus = (char *) "error";
+ break;
+ }
+ if (useSSP && resDKIMSSP != -1)
+ {
+ switch(resDKIMSSP)
+ {
+ case DKIM_SSP_ALL:
+ sspStatus = (char *) "all;";
+ break;
+ case DKIM_SSP_STRICT:
+ sspStatus = (char *) "strict;";
+ break;
+ case DKIM_SSP_SCOPE:
+ sspStatus = (char *) "out of scope;";
+ break;
+ case DKIM_SSP_TEMPFAIL:
+ sspStatus = (char *) "temporary failure;";
+ break;
+ case DKIM_SSP_UNKNOWN:
+ default:
+ sspStatus = (char *) "unknown;";
+ break;
+ }
+ }
+ if (useADSP && resDKIMADSP != -1)
+ {
+ switch(resDKIMADSP)
+ {
+ case DKIM_ADSP_ALL:
+ adspStatus = (char *) "all;";
+ break;
+ case DKIM_ADSP_DISCARDABLE:
+ adspStatus = (char *) "discardable;";
+ break;
+ case DKIM_ADSP_SCOPE:
+ adspStatus = (char *) "out of scope;";
+ break;
+ case DKIM_ADSP_TEMPFAIL:
+ adspStatus = (char *) "temporary failure;";
+ break;
+ case DKIM_ADSP_UNKNOWN:
+ default:
+ adspStatus = (char *) "unknown ;";
+ break;
+ }
+ }
+ printf("DKIM-Status: %s\n", dkimStatus);
+ if (useSSP && *sspStatus)
+ printf("X-DKIM-SSP: %s\n", sspStatus);
+ if (useADSP && *adspStatus)
+ printf("X-DKIM-ADSP: %s\n", adspStatus);
+}
+
+int
+ParseTagValues(char *list, char *letters[], char *values[])
+{
+ char *tmp, *ptr, *key;
+ int i;
+
+ /*- start with all args unset */
+ for (i = 0; letters[i]; i++)
+ values[i] = 0;
+ key = 0;
+ for(ptr = list;*ptr;)
+ {
+ if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) /*- FWS */
+ *ptr++ = 0;
+ if (!key)
+ key = ptr;
+ if (*ptr == '=')
+ {
+ *ptr = 0;
+ for (i = 0;letters[i];i++)
+ {
+ if (!strcmp(letters[i], key))
+ {
+ ptr++;
+ for (;*ptr;)
+ {
+ if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n'))
+ {
+ ptr++;
+ continue;
+ }
+ break;
+ }
+ values[i] = ptr;
+ for(;*ptr && *ptr != ';';ptr++);
+ tmp = ptr;
+ if (*ptr)
+ *ptr++ = 0;
+ for(;tmp != values[i];tmp--) /*- RFC 4871 3.2 */
+ {
+ if ((*tmp == ' ') || (*tmp == '\t') || (*tmp == '\r') || (*tmp == '\n'))
+ {
+ *tmp = 0;
+ continue;
+ }
+ break;
+ }
+ key = 0;
+ break;
+ }
+ }
+ } else
+ ptr++;
+ }
+ return (0);
+}
+
+int
+GetSSP(char *domain, int *bTesting)
+{
+ char *query, *results;
+ char *tags[] = { (char *) "dkim", (char *) "t", (char *) 0};
+ char *values[2];
+ int bIsParentSSP = 0, iSSP = DKIM_SSP_UNKNOWN;
+
+ *bTesting = 0;
+ if (!(query = (char *) DKIM_MALLOC(strlen("_ssp._domainkey.") + strlen(domain) + 1)))
+ {
+ fprintf(stderr, "malloc: %d: %s\n", strlen("_ssp._domainkey.") + strlen(domain) + 1,
+ strerror(errno));
+ exit(1);
+ }
+ sprintf(query, "_ssp._domainkey.%s", domain);
+ results = dns_text(query);
+ DKIM_MFREE(query);
+ if (!strcmp(results, "e=temp;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_SSP_TEMPFAIL;
+ } else
+ if (!strcmp(results, "e=perm;"))
+ {
+ DKIM_MFREE(results);
+ results = dns_text(domain);
+ if (!strcmp(results, "e=temp;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_SSP_TEMPFAIL;
+ } else
+ if (!strcmp(results, "e=perm;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_SSP_SCOPE;
+ }
+ bIsParentSSP = 1;
+ }
+ if (!ParseTagValues(results, tags, values))
+ {
+ DKIM_MFREE(results);
+ return DKIM_SSP_UNKNOWN;
+ }
+ DKIM_MFREE(results);
+ if (values[0] != NULL) {
+ if (strcasecmp(values[0], "all") == 0)
+ iSSP = DKIM_SSP_ALL;
+ else
+ if (strcasecmp(values[0], "strict") == 0)
+ iSSP = DKIM_SSP_STRICT;
+ }
+ // flags
+ if (values[1] != NULL) {
+ char *s, *p;
+ for (p = values[1], s = values[1]; *p; p++)
+ {
+ if (*p == '|')
+ *p = 0;
+ else
+ continue;
+ if (!strcmp(s, "y"))
+ *bTesting = 1;
+ else
+ if (!strcmp(s, "s")) {
+ if (bIsParentSSP) {
+ /*
+ * this is a parent's SSP record that should not apply to subdomains
+ * the message is non-suspicious
+ */
+ *bTesting = 0;
+ return (DKIM_SSP_UNKNOWN);
+ }
+ }
+ s = p + 1;
+ }
+ }
+ return iSSP; /*- No ADSP Record */
+}
+
+int
+GetADSP(char *domain)
+{
+ char *query, *results;
+ char *tags[] = {(char *) "dkim", (char *) 0};
+ char *values[1];
+
+ results = dns_text(domain);
+ if (!strcmp(results, "e=perm;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_SCOPE;
+ } else
+ if (!strcmp(results, "e=temp;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_TEMPFAIL;
+ }
+ if (!(query = (char *) DKIM_MALLOC(strlen((char *) "_adsp._domainkey.") + strlen(domain) + 1)))
+ {
+ fprintf(stderr, "malloc: %d: %s\n", strlen("_adsp._domainkey.") + strlen(domain) + 1,
+ strerror(errno));
+ exit(1);
+ }
+ sprintf(query, "_adsp._domainkey.%s", domain);
+ results = dns_text(query);
+ DKIM_MFREE(query);
+ if (!strcmp(results, "e=perm;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_SCOPE;
+ } else
+ if (!strcmp(results, "e=temp;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_TEMPFAIL;
+ }
+ if (!ParseTagValues(results, tags, values))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_UNKNOWN;
+ }
+ DKIM_MFREE(results);
+ if (values[0] != NULL) {
+ if (strcasecmp(values[0], "all") == 0)
+ return (DKIM_ADSP_ALL);
+ else
+ if (strcasecmp(values[0], "discardable") == 0)
+ return (DKIM_ADSP_DISCARDABLE);
+ }
+ return DKIM_ADSP_UNKNOWN; /*- No ADSP Record */
+}
+
+int
+main(int argc, char **argv)
+{
+ char *PrivKey, *PrivKeyFile = NULL, *pSig = NULL, *dkimverify;
+ int i, ret, ch, nPrivKeyLen, PrivKeyFD, verbose = 0;
+ int bSign = 1, nSigCount = 0, useSSP = 0, useADSP = 0, accept3ps = 0;
+ int sCount = 0, sSize = 0, resDKIMSSP = -1, resDKIMADSP = -1;
+ char Buffer[1024], szPolicy[512];
+ time_t t;
+ struct stat statbuf;
+ DKIMContext ctxt;
+ DKIMSignOptions opts = { 0 };
+ DKIMVerifyDetails *pDetails;
+ DKIMVerifyOptions vopts = { 0 };
+
+ if (!(program = strrchr(argv[0], '/')))
+ program = argv[0];
+ else
+ program++;
+ t = time(0);
+#ifdef HAVE_EVP_SHA256
+ opts.nHash = DKIM_HASH_SHA1_AND_256;
+#else
+ opts.nHash = DKIM_HASH_SHA1;
+#endif
+ opts.nCanon = DKIM_SIGN_RELAXED;
+ opts.nIncludeBodyLengthTag = 0;
+ opts.nIncludeQueryMethod = 0;
+ opts.nIncludeTimeStamp = 0;
+ opts.expireTime = t + 604800; // expires in 1 week
+ opts.nIncludeCopiedHeaders = 0;
+ opts.nIncludeBodyHash = DKIM_BODYHASH_BOTH;
+ strcpy(opts.szSelector, "private");
+ strcpy(opts.szRequiredHeaders, "NonExistent");
+ opts.pfnHeaderCallback = SignThisHeader;
+ while (1)
+ {
+#ifdef HAVE_EVP_SHA256
+ if ((ch = getopt(argc, argv, "lqthHvVp:b:c:d:i:s:x:y:z:")) == -1)
+#else
+ if ((ch = getopt(argc, argv, "lqthHvVp:b:c:d:i:s:x:y:")) == -1)
+#endif
+ break;
+ switch (ch)
+ {
+ case 'l': /*- body length tag */
+ opts.nIncludeBodyLengthTag = 1;
+ break;
+ case 'q': /*- query method tag */
+ opts.nIncludeQueryMethod = 1;
+ break;
+ case 't': /*- timestamp tag */
+ opts.nIncludeTimeStamp = 1;
+ break;
+ case 'h':
+ opts.nIncludeCopiedHeaders = 1;
+ break;
+ case 'H':
+ usage();
+ break;
+ case 'v': /*- verify */
+ bSign = 0;
+ break;
+ case 'V': /*- verbose */
+ verbose = 1;
+ break;
+ case 'p':
+ switch(*optarg)
+ {
+ case '0':
+ accept3ps = 0;
+ useSSP = 0;
+ useADSP = 0;
+ break;
+ case '1':
+ accept3ps = 1;
+ useSSP = 1;
+ useADSP = 0;
+ break;
+ case '2':
+ accept3ps = 1;
+ useSSP = 0;
+ useADSP = 1;
+ break;
+ }
+ break;
+ case 'b': /*- allman or ietf draft 1 or both */
+ switch (*optarg)
+ {
+ case '1':
+ opts.nIncludeBodyHash = DKIM_BODYHASH_ALLMAN_1;
+ break;
+ case '2':
+ opts.nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
+ break;
+ case '3':
+ opts.nIncludeBodyHash = DKIM_BODYHASH_BOTH;
+ break;
+ default:
+ fprintf(stderr, "%s: unrecognized standard.\n", program);
+ return (1);
+ }
+ break;
+ case 'c':
+ switch (*optarg)
+ {
+ case 'r':
+ opts.nCanon = DKIM_SIGN_RELAXED;
+ break;
+ case 's':
+ opts.nCanon = DKIM_SIGN_SIMPLE;
+ break;
+ case 't':
+ opts.nCanon = DKIM_SIGN_RELAXED_SIMPLE;
+ break;
+ case 'u':
+ opts.nCanon = DKIM_SIGN_SIMPLE_RELAXED;
+ break;
+ default:
+ fprintf(stderr, "%s: unrecognized canonicalization.\n", program);
+ return (1);
+ }
+ break;
+ case 'd':
+ strncpy(opts.szDomain, optarg, sizeof (opts.szDomain) - 1);
+ break;
+ case 'i': /*- identity */
+ if (*optarg == '-')
+ opts.szIdentity[0] = '\0';
+ else
+ strncpy(opts.szIdentity, optarg, sizeof (opts.szIdentity) - 1);
+ break;
+ case 's': /*- sign */
+ bSign = 1;
+ PrivKeyFile = optarg;
+ break;
+ case 'x': /*- expire time */
+ if (*optarg == '-')
+ opts.expireTime = 0;
+ else
+ opts.expireTime = t + atoi(optarg);
+ break;
+ case 'y':
+ strncpy(opts.szSelector, optarg, sizeof (opts.szSelector) - 1);
+ break;
+#ifdef HAVE_EVP_SHA256
+ case 'z': /*- sign w/ sha1, sha256 or both */
+ switch (*optarg)
+ {
+ case '1':
+ opts.nHash = DKIM_HASH_SHA1;
+ break;
+ case '2':
+ opts.nHash = DKIM_HASH_SHA256;
+ break;
+ case '3':
+ opts.nHash = DKIM_HASH_SHA1_AND_256;
+ break;
+ default:
+ fprintf(stderr, "%s: unrecognized hash.\n", program);
+ return (1);
+ }
+ break;
+#endif
+ } /*- switch (ch) */
+ }
+ if (bSign) { /*- sign */
+ if (!PrivKeyFile)
+ {
+ fprintf(stderr, "Private Key not provided\n");
+ usage();
+ return (1);
+ }
+ if ((PrivKeyFD = open(PrivKeyFile, O_RDONLY)) == -1) {
+ fprintf(stderr, "%s: %s\n", PrivKeyFile, strerror(errno));
+ return (1);
+ }
+ if (fstat(PrivKeyFD, &statbuf) == -1)
+ {
+ fprintf(stderr, "fstat: %s: %s\n", PrivKeyFile, strerror(errno));
+ return (1);
+ }
+ if (!(PrivKey = (char *) DKIM_MALLOC(sizeof(char) * ((nPrivKeyLen = statbuf.st_size) + 1))))
+ {
+ fprintf(stderr, "malloc: %ld bytes: %s\n", statbuf.st_size + 1, strerror(errno));
+ return (1);
+ }
+ if (read(PrivKeyFD, PrivKey, nPrivKeyLen) != nPrivKeyLen)
+ {
+ fprintf(stderr, "%s: read: %s\n", strerror(errno), program);
+ return (1);
+ }
+ close(PrivKeyFD);
+ PrivKey[nPrivKeyLen] = '\0';
+ if (DKIMSignInit(&ctxt, &opts) != DKIM_SUCCESS)
+ {
+ fprintf(stderr, "DKIMSignInit: failed to initialize signature %s\n", PrivKeyFile);
+ return (1);
+ }
+ for (;;)
+ {
+ if ((ret = read(0, Buffer, sizeof(Buffer) - 1)) == -1)
+ {
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ DKIMSignFree(&ctxt);
+ return (1);
+ } else
+ if (!ret)
+ break;
+ if (DKIMSignProcess(&ctxt, Buffer, ret) == DKIM_INVALID_CONTEXT)
+ {
+ fprintf(stderr, "DKIMSignProcess: DKIMContext structure invalid for this operation\n");
+ DKIMSignFree(&ctxt);
+ return (1);
+ }
+ }
+ if (DKIMSignGetSig2(&ctxt, PrivKey, &pSig) == DKIM_INVALID_CONTEXT)
+ {
+ fprintf(stderr, "DKIMSignProcess: DKIMContext structure invalid for this operation\n");
+ DKIMSignFree(&ctxt);
+ return (1);
+ }
+ if (pSig)
+ {
+ fwrite(pSig, 1, strlen(pSig), stdout);
+ fwrite("\n", 1, 1, stdout);
+ }
+ DKIMSignFree(&ctxt);
+ return 0;
+ } else { /*- verify */
+ if (useADSP)
+ vopts.nCheckPractices = useADSP;
+ else
+ if (useSSP)
+ vopts.nCheckPractices = useSSP;
+ else
+ vopts.nCheckPractices = 0;
+ vopts.nAccept3ps = accept3ps;
+ vopts.pfnSelectorCallback = NULL; /*- SelectorCallback; */
+ DKIMVerifyInit(&ctxt, &vopts); /*- this is always successful */
+ for (;;)
+ {
+ if ((i = read(0, Buffer, sizeof(Buffer) - 1)) == -1)
+ {
+ fprintf(stderr, "read: %s\n", strerror(errno));
+ DKIMVerifyFree(&ctxt);
+ return (1);
+ } else
+ if (!i)
+ break;
+ ret = DKIMVerifyProcess(&ctxt, Buffer, i);
+ dkim_error(ret);
+ if (ret > 0 && ret < DKIM_FINISHED_BODY)
+ ret = DKIM_FAIL;
+ if (ret)
+ break;
+ }
+ if (!ret)
+ {
+ if ((ret = DKIMVerifyResults(&ctxt, &sCount, &sSize)) != DKIM_SUCCESS)
+ dkim_error(ret);
+ if ((ret = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy)) != DKIM_SUCCESS)
+ dkim_error(ret);
+ else
+ {
+ for (ret = 0,i = 0; i < nSigCount; i++) {
+ if (verbose)
+ printf("Signature # %02d: ", i + 1);
+ if (pDetails[i].nResult >= 0)
+ {
+ if (verbose)
+ printf("Success\n");
+ continue;
+ } else
+ {
+ ret = pDetails[i].nResult;
+ if (verbose)
+ printf("Failure %d\n", ret);
+ }
+ }
+ if (!nSigCount)
+ ret = DKIM_NO_SIGNATURES;
+ }
+ }
+ if (ret < 0 || ret == DKIM_3PS_SIGNATURE) {
+ if (useADSP)
+ {
+ char *domain;
+
+ if ((domain = DKIMVerifyGetDomain(&ctxt)))
+ resDKIMADSP = GetADSP(domain);
+ if (sCount > 0) {
+ if (resDKIMADSP == DKIM_ADSP_UNKNOWN || resDKIMADSP == DKIM_ADSP_ALL)
+ ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
+ }
+ /* if the message should be signed, return fail */
+ if (resDKIMADSP == DKIM_ADSP_DISCARDABLE)
+ ret = DKIM_FAIL;
+ ret = DKIM_NEUTRAL;
+ } else
+ if (useSSP)
+ {
+ int bTestingPractices = 0;
+ char *domain;
+
+ if ((domain = DKIMVerifyGetDomain(&ctxt)))
+ resDKIMSSP = GetSSP(domain, &bTestingPractices);
+ if (sCount > 0) {
+ if ((resDKIMSSP == DKIM_SSP_UNKNOWN || resDKIMSSP == DKIM_SSP_ALL))
+ ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
+ }
+ // if the SSP is testing, return neutral
+ if (bTestingPractices)
+ return(DKIM_NEUTRAL);
+ /* if the message should be signed, return fail */
+ if (resDKIMSSP == DKIM_SSP_ALL || resDKIMSSP == DKIM_SSP_STRICT)
+ return(DKIM_FAIL);
+ ret = DKIM_NEUTRAL;
+ }
+ }
+ DKIMVerifyFree(&ctxt);
+ writeHeader(ret, resDKIMSSP, resDKIMADSP, useSSP, useADSP);
+ if ((dkimverify = getenv("DKIMVERIFY")))
+ {
+ if (ret < 0)
+ {
+ if (dkimverify[str_chr(dkimverify, 'E' - ret)])
+ ret = 14; /*- return permanent error */
+ if (dkimverify[str_chr(dkimverify, 'e' - ret)])
+ ret = 88; /*- return temporary error */
+ } else
+ {
+ if (dkimverify[str_chr(dkimverify, 'A' + ret)])
+ ret = 14; /*- return permanent error */
+ if (dkimverify[str_chr(dkimverify, 'a' + ret)])
+ ret = 88; /*- return temporary error */
+ }
+ }
+ return (ret);
+ }
+ /*- Not Reached */
+ _exit(0);
+}
+
+void
+getversion_dkim_c()
+{
+ static char *x = (char *) "$Id: dkim.c,v 1.10 2009-04-15 21:30:32+05:30 Cprogrammer Exp mbhangui $";
+
+ x++;
+}
diff -Naur qmail-1.03.org/dkimverify.cpp qmail-1.03/dkimverify.cpp
--- qmail-1.03.org/dkimverify.cpp 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimverify.cpp 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,1155 @@
+/*
+ * $Log: dkimverify.cpp,v $
+ * Revision 1.5 2009-03-27 20:19:58+05:30 Cprogrammer
+ * added ADSP code
+ *
+ * Revision 1.4 2009-03-26 15:12:05+05:30 Cprogrammer
+ * added ADSP code
+ *
+ * Revision 1.3 2009-03-25 08:38:20+05:30 Cprogrammer
+ * fixed indentation
+ *
+ * Revision 1.2 2009-03-21 11:57:40+05:30 Cprogrammer
+ * fixed indentation
+ *
+ * Revision 1.1 2009-03-21 08:43:13+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#else
+#define HAVE_EVP_SHA256
+#endif
+#define _strnicmp strncasecmp
+#define _stricmp strcasecmp
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <vector>
+#include <algorithm>
+#include "dkim.h"
+#include "dkimverify.h"
+#include "dkimdns.h"
+
+#define MAX_SIGNATURES 10 // maximum number of DKIM signatures to process in a message
+
+SignatureInfo::SignatureInfo(bool s)
+{
+ VerifiedBodyCount = 0;
+ UnverifiedBodyCount = 0;
+ EVP_MD_CTX_init(&m_Hdr_ctx);
+ EVP_MD_CTX_init(&m_Bdy_ctx);
+ m_pSelector = NULL;
+ Status = DKIM_SUCCESS;
+ m_nHash = 0;
+ EmptyLineCount = 0;
+ m_SaveCanonicalizedData = s;
+}
+
+SignatureInfo::~SignatureInfo()
+{
+ EVP_MD_CTX_cleanup(&m_Hdr_ctx);
+ EVP_MD_CTX_cleanup(&m_Bdy_ctx);
+}
+
+inline bool
+isswsp(char ch)
+{
+ return (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n');
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Parse a DKIM tag-list. Returns true for success
+//
+////////////////////////////////////////////////////////////////////////////////
+bool
+ParseTagValueList(char *tagvaluelist, const char *wanted[], char *values[])
+{
+ char *s = tagvaluelist;
+
+ for (;;) {
+ // skip whitespace
+ while (isswsp(*s))
+ s++;
+ // if at the end of the string, return success. note: this allows a list with no entries
+ if (*s == '\0')
+ return true;
+ // get tag name
+ if (!isalpha(*s))
+ return false;
+ char *tag = s;
+ do {
+ s++;
+ } while (isalnum(*s) || *s == '-');
+ char *endtag = s;
+ // skip whitespace before equals
+ while (isswsp(*s))
+ s++;
+ // next character must be equals
+ if (*s != '=')
+ return false;
+ s++;
+ // null-terminate tag name
+ *endtag = '\0';
+ // skip whitespace after equals
+ while (isswsp(*s))
+ s++;
+ // get tag value
+ char *value = s;
+ while (*s != ';' && ((*s == '\t' || *s == '\r' || *s == '\n') || (*s >= ' ' && *s <= '~')))
+ s++;
+ char *e = s;
+ // make sure the next character is the null terminator (which means we're done) or a semicolon (not done)
+ bool done = false;
+ if (*s == '\0')
+ done = true;
+ else {
+ if (*s != ';')
+ return false;
+ s++;
+ }
+ // skip backwards past any trailing whitespace
+ while (e > value && isswsp(e[-1]))
+ e--;
+ // null-terminate tag value
+ *e = '\0';
+ // check to see if we want this tag
+ for (unsigned i = 0; wanted[i] != NULL; i++) {
+ if (strcmp(wanted[i], tag) == 0) {
+ // return failure if we already have a value for this tag (duplicates not allowed)
+ if (values[i] != NULL)
+ return false;
+ values[i] = value;
+ break;
+ }
+ }
+ if (done)
+ return true;
+ }
+}
+
+// Convert hex char to value (0-15)
+char
+tohex(char ch)
+{
+ if (ch >= '0' && ch <= '9')
+ return (ch - '0');
+ else
+ if (ch >= 'A' && ch <= 'F')
+ return (ch - 'A' + 10);
+ else
+ if (ch >= 'a' && ch <= 'f')
+ return (ch - 'a' + 10);
+ else {
+ assert(0);
+ return 0;
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Decode quoted printable string in-place
+//
+////////////////////////////////////////////////////////////////////////////////
+void
+DecodeQuotedPrintable(char *ptr)
+{
+ char *s = ptr;
+ while (*s != '\0' && *s != '=')
+ s++;
+ if (*s == '\0')
+ return;
+ char *d = s;
+ do {
+ if (*s == '=' && isxdigit(s[1]) && isxdigit(s[2])) {
+ *d++ = (tohex(s[1]) << 4) | tohex(s[2]);
+ s += 3;
+ } else {
+ *d++ = *s++;
+ }
+ } while (*s != '\0');
+ *d = '\0';
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Decode base64 string in-place, returns number of bytes output
+//
+////////////////////////////////////////////////////////////////////////////////
+unsigned
+DecodeBase64(char *ptr)
+{
+ static const char base64_table[256] =
+ { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1,
+ -1, -1,
+ -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1,
+ -1, -1,
+ 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
+ -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, -1,
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
+ };
+ unsigned char *s = (unsigned char *) ptr;
+ unsigned char *d = (unsigned char *) ptr;
+ unsigned b64accum = 0;
+ unsigned char b64shift = 0;
+ while (*s != '\0') {
+ unsigned char value = base64_table[*s++];
+ if ((signed char) value >= 0) {
+ b64accum = (b64accum << 6) | value;
+ b64shift += 6;
+ if (b64shift >= 8) {
+ b64shift -= 8;
+ *d++ = (b64accum >> b64shift);
+ }
+ }
+ }
+ return (char *) d - ptr;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Match a string with a pattern (used for g= value)
+// Supports a single, optional "*" wildcard character.
+//
+////////////////////////////////////////////////////////////////////////////////
+bool
+WildcardMatch(const char *p, const char *s)
+{
+ // special case: An empty "g=" value never matches any addresses
+ if (*p == '\0')
+ return false;
+ const char *wildcard = strchr(p, '*');
+ if (wildcard == NULL)
+ return strcmp(s, p) == 0;
+ else {
+ unsigned beforewildcardlen = wildcard - p;
+ unsigned afterwildcardlen = strlen(wildcard + 1);
+ unsigned slen = strlen(s);
+ return (slen >= beforewildcardlen + afterwildcardlen) && (strncmp(s, p, beforewildcardlen) == 0)
+ && strcmp(s + slen - afterwildcardlen, wildcard + 1) == 0;
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Parse addresses from a string. Returns true if at least one address found
+//
+////////////////////////////////////////////////////////////////////////////////
+bool
+ParseAddresses(string str, vector < string > &Addresses)
+{
+ char *s = (char *) str.c_str();
+ while (*s != '\0') {
+ char *start = s;
+ char *from = s;
+ char *to = s;
+ char *lt = NULL; // pointer to less than character (<) which starts the address if found
+ while (*from != '\0') {
+ if (*from == '(') {
+ // skip over comment
+ from++;
+ for (int depth = 1; depth != 0; from++) {
+ if (*from == '\0')
+ break;
+ else
+ if (*from == '(')
+ depth++;
+ else
+ if (*from == ')')
+ depth--;
+ else
+ if (*from == '\\' && from[1] != '\0')
+ from++;
+ }
+ }
+ else
+ if (*from == ')') {
+ // ignore closing parenthesis outside of comment
+ from++;
+ }
+
+ else
+ if (*from == ',' || *from == ';') {
+ // comma/selicolon ends the address
+ from++;
+ break;
+ }
+ else
+ if (*from == ' ' || *from == '\t' || *from == '\r' || *from == '\n') {
+ // ignore whitespace
+ from++;
+ }
+ else
+ if (*from == '"') {
+ // copy the contents of a quoted string
+ from++;
+ while (*from != '\0') {
+ if (*from == '"') {
+ from++;
+ break;
+ }
+ else
+ if (*from == '\\' && from[1] != '\0')
+ *to++ = *from++;
+ *to++ = *from++;
+ }
+ }
+ else
+ if (*from == '\\' && from[1] != '\0') {
+ // copy quoted-pair
+ *to++ = *from++;
+ *to++ = *from++;
+ } else {
+ // copy any other char
+ *to = *from++;
+ // save pointer to '<' for later...
+ if (*to == '<')
+ lt = to;
+ to++;
+ }
+ }
+ *to = '\0';
+ // if there's < > get what's inside
+ if (lt != NULL) {
+ start = lt + 1;
+ char *gt = strchr(start, '>');
+ if (gt != NULL)
+ *gt = '\0';
+ } else {
+ // look for and strip group name
+ char *colon = strchr(start, ':');
+ if (colon != NULL) {
+ char *at = strchr(start, '@');
+ if (at == NULL || colon < at)
+ start = colon + 1;
+ }
+ }
+ if (*start != '\0' && strchr(start, '@') != NULL)
+ Addresses.push_back(start);
+ s = from;
+ }
+ return !Addresses.empty();
+}
+
+CDKIMVerify::CDKIMVerify()
+{
+ m_pfnSelectorCallback = NULL;
+ m_pfnPracticesCallback = NULL;
+ m_HonorBodyLengthTag = false;
+ m_CheckPractices = false;
+ m_Accept3ps = false;
+ m_SubjectIsRequired = true;
+ m_SaveCanonicalizedData = false;
+}
+
+CDKIMVerify::~CDKIMVerify()
+{
+}
+
+// Init - save the options
+int
+CDKIMVerify::Init(DKIMVerifyOptions *pOptions)
+{
+ int nRet = CDKIMBase::Init();
+ m_pfnSelectorCallback = pOptions->pfnSelectorCallback;
+ m_pfnPracticesCallback = pOptions->pfnPracticesCallback;
+
+ m_HonorBodyLengthTag = pOptions->nHonorBodyLengthTag != 0;
+ m_CheckPractices = pOptions->nCheckPractices != 0;
+ m_SubjectIsRequired = pOptions->nSubjectRequired == 0;
+ m_Accept3ps = pOptions->nAccept3ps != 0; //TBS(Luc)
+ m_SaveCanonicalizedData = pOptions->nSaveCanonicalizedData != 0;
+ return nRet;
+}
+
+// GetResults - return the pass/fail/neutral verification result
+int
+CDKIMVerify::GetResults(int *sCount, int *sSize)
+{
+ ProcessFinal();
+ int SuccessCount = 0;
+ int TestingFailures = 0;
+ int RealFailures = 0;
+ list <string> SuccessfulDomains; // can contain duplicates
+ /* get the From address's domain if we might need it */
+ string sFromDomain;
+
+ for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
+ if (i->Status == DKIM_SUCCESS) {
+ if (!i->BodyHashData.empty()) {
+ // check the body hash
+ unsigned char md[EVP_MAX_MD_SIZE];
+ unsigned len = 0;
+ int res = EVP_DigestFinal(&i->m_Bdy_ctx, md, &len);
+ if (!res || len != i->BodyHashData.length() || memcmp(i->BodyHashData.data(), md, len) != 0) {
+ // body hash mismatch
+ // if the selector is in testing mode...
+ if (i->m_pSelector->Testing) {
+ i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING; // todo: make a new error code for this?
+ TestingFailures++;
+ } else {
+ i->Status = DKIM_BODY_HASH_MISMATCH;
+ RealFailures++;
+ }
+ continue;
+ }
+ } else {
+ // hash CRLF separating the body from the signature
+ i->Hash("\r\n", 2);
+ }
+ // check the header hash
+ string sSignedSig = i->Header;
+ string sSigValue = sSignedSig.substr(sSignedSig.find(':') + 1);
+ static const char *tags[] = { "b", NULL };
+ char *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
+ char *pSigValue = (char *) sSigValue.c_str();
+ if (ParseTagValueList(pSigValue, tags, values) && values[0] != NULL) {
+ sSignedSig.erase(15 + values[0] - pSigValue, strlen(values[0]));
+ }
+ if (i->HeaderCanonicalization == DKIM_CANON_RELAXED) {
+ sSignedSig = RelaxHeader(sSignedSig);
+ } else
+ if (i->HeaderCanonicalization == DKIM_CANON_NOWSP) {
+ RemoveSWSP(sSignedSig);
+ // convert "DKIM-Signature" to lower case
+ sSignedSig.replace(0, 14, "dkim-signature", 14);
+ }
+ i->Hash(sSignedSig.c_str(), sSignedSig.length());
+ assert(i->m_pSelector != NULL);
+ int res = EVP_VerifyFinal(&i->m_Hdr_ctx, (unsigned char *) i->SignatureData.data(),
+ i->SignatureData.length(), i->m_pSelector->PublicKey);
+ if (res == 1) {
+ if (i->UnverifiedBodyCount == 0)
+ i->Status = DKIM_SUCCESS;
+ else
+ i->Status = DKIM_SUCCESS_BUT_EXTRA;
+ SuccessCount++;
+ SuccessfulDomains.push_back(i->Domain);
+ } else {
+ // if the selector is in testing mode...
+ if (i->m_pSelector->Testing) {
+ i->Status = DKIM_SIGNATURE_BAD_BUT_TESTING;
+ TestingFailures++;
+ } else {
+ i->Status = DKIM_SIGNATURE_BAD;
+ RealFailures++;
+ }
+ }
+ } else
+ if (i->Status == DKIM_SELECTOR_GRANULARITY_MISMATCH
+ || i->Status == DKIM_SELECTOR_ALGORITHM_MISMATCH
+ || i->Status == DKIM_SELECTOR_KEY_REVOKED) {
+ // treat these as failures
+ // todo: maybe see if the selector is in testing mode?
+ RealFailures++;
+ }
+ }
+ if (SuccessCount > 0 || m_CheckPractices) {
+ for (list < string >::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
+ if (_strnicmp(i->c_str(), "From", 4) == 0) {
+ // skip over whitespace between the header name and :
+ const char *s = i->c_str() + 4;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == ':') {
+ vector <string> Addresses;
+ if (ParseAddresses(s + 1, Addresses)) {
+ unsigned atpos = Addresses[0].find('@');
+ sFromDomain = Addresses[0].substr(atpos + 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+ // if a signature from the From domain verified successfully, return success now
+ // without checking the sender signing practices
+ if (SuccessCount > 0 && !sFromDomain.empty()) {
+ for (list < string >::iterator i = SuccessfulDomains.begin(); i != SuccessfulDomains.end(); ++i) {
+ // see if the successful domain is the same as or a parent of the From domain
+ if (i->length() > sFromDomain.length())
+ continue;
+ if (_stricmp(i->c_str(), sFromDomain.c_str() + sFromDomain.length() - i->length()) != 0)
+ continue;
+ if (i->length() == sFromDomain.length() || sFromDomain.c_str()[sFromDomain.length() - i->length() - 1] == '.')
+ return ((SuccessCount == Signatures.size()) ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
+ }
+ }
+ if (!m_Accept3ps)
+ return DKIM_NEUTRAL;
+ *sCount = SuccessCount;
+ *sSize = Signatures.size();
+ return DKIM_3PS_SIGNATURE;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Hash - update the hash
+//
+////////////////////////////////////////////////////////////////////////////////
+void
+SignatureInfo::Hash(const char *szBuffer, unsigned nBufLength, bool IsBody)
+{
+
+ if (IsBody && BodyLength != -1) {
+ VerifiedBodyCount += nBufLength;
+ if (VerifiedBodyCount > BodyLength) {
+ nBufLength = BodyLength - (VerifiedBodyCount - nBufLength);
+ UnverifiedBodyCount += VerifiedBodyCount - BodyLength;
+ VerifiedBodyCount = BodyLength;
+ if (nBufLength == 0)
+ return;
+ }
+ }
+ if (IsBody && !BodyHashData.empty()) {
+ EVP_DigestUpdate(&m_Bdy_ctx, szBuffer, nBufLength);
+ } else {
+ EVP_VerifyUpdate(&m_Hdr_ctx, szBuffer, nBufLength);
+ }
+ if (m_SaveCanonicalizedData) {
+ CanonicalizedData.append(szBuffer, nBufLength);
+ }
+}
+
+
+// ProcessHeaders - Look for DKIM-Signatures and start processing them
+int
+CDKIMVerify::ProcessHeaders(void)
+{
+
+ // look for DKIM-Signature header(s)
+ for (list < string >::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
+ if (_strnicmp(i->c_str(), "DKIM-Signature", 14) == 0) {
+ // skip over whitespace between the header name and :
+ const char *s = i->c_str() + 14;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == ':') {
+ // found
+ SignatureInfo sig(m_SaveCanonicalizedData);
+ sig.Status = ParseDKIMSignature(*i, sig);
+ Signatures.push_back(sig);
+ if (Signatures.size() >= MAX_SIGNATURES)
+ break;
+ }
+ }
+ }
+ if (Signatures.empty())
+ return DKIM_NO_SIGNATURES;
+ bool ValidSigFound = false;
+ for (list < SignatureInfo >::iterator s = Signatures.begin(); s != Signatures.end(); ++s) {
+ SignatureInfo & sig = *s;
+ if (sig.Status != DKIM_SUCCESS)
+ continue;
+ SelectorInfo & sel = GetSelector(sig.Selector, sig.Domain);
+ sig.m_pSelector = &sel;
+ if (sel.Status != DKIM_SUCCESS) {
+ sig.Status = sel.Status;
+ return (sig.Status);
+ } else {
+ // check the granularity
+ if (!WildcardMatch(sel.Granularity.c_str(), sig.IdentityLocalPart.c_str()))
+ sig.Status = DKIM_SELECTOR_GRANULARITY_MISMATCH; // this error causes the signature to fail
+ // check the hash algorithm
+#ifdef HAVE_EVP_SHA256
+ if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1) || (sig.m_nHash == DKIM_HASH_SHA256 && !sel.AllowSHA256))
+#else
+ if ((sig.m_nHash == DKIM_HASH_SHA1 && !sel.AllowSHA1))
+#endif
+ sig.Status = DKIM_SELECTOR_ALGORITHM_MISMATCH; // causes signature to fail
+ // check for same domain
+ if (sel.SameDomain && _stricmp(sig.Domain.c_str(), sig.IdentityDomain.c_str()) != 0)
+ sig.Status = DKIM_BAD_SYNTAX;
+ }
+ if (sig.Status != DKIM_SUCCESS)
+ continue;
+ // initialize the hashes
+#ifdef HAVE_EVP_SHA256
+ if (sig.m_nHash == DKIM_HASH_SHA256) {
+ EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha256());
+ EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha256());
+ } else {
+ EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1());
+ EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1());
+ }
+#else
+ EVP_VerifyInit(&sig.m_Hdr_ctx, EVP_sha1());
+ EVP_DigestInit(&sig.m_Bdy_ctx, EVP_sha1());
+#endif
+ // compute the hash of the header
+ vector < list < string >::reverse_iterator > used;
+ for (vector < string >::iterator x = sig.SignedHeaders.begin(); x != sig.SignedHeaders.end(); ++x) {
+ list < string >::reverse_iterator i;
+ for (i = HeaderList.rbegin(); i != HeaderList.rend(); ++i) {
+ if (_strnicmp(i->c_str(), x->c_str(), x->length()) == 0) {
+ // skip over whitespace between the header name and :
+ const char *s = i->c_str() + x->length();
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == ':' && find(used.begin(), used.end(), i) == used.end())
+ break;
+ }
+ }
+ if (i != HeaderList.rend()) {
+ used.push_back(i);
+ // hash this header
+ if (sig.HeaderCanonicalization == DKIM_CANON_SIMPLE) {
+ sig.Hash(i->c_str(), i->length());
+ }
+ else
+ if (sig.HeaderCanonicalization == DKIM_CANON_RELAXED) {
+ string sTemp = RelaxHeader(*i);
+ sig.Hash(sTemp.c_str(), sTemp.length());
+ }
+ else
+ if (sig.HeaderCanonicalization == DKIM_CANON_NOWSP) {
+ string sTemp = *i;
+ RemoveSWSP(sTemp);
+ // convert characters before ':' to lower case
+ for (char *s = (char *)sTemp.c_str(); *s != '\0' && *s != ':'; s++) {
+ if (*s >= 'A' && *s <= 'Z')
+ *s += 'a' - 'A';
+ }
+ sig.Hash(sTemp.c_str(), sTemp.length());
+ }
+ sig.Hash("\r\n", 2);
+ }
+ }
+ if (sig.BodyHashData.empty()) {
+ // hash CRLF separating headers from body
+ sig.Hash("\r\n", 2);
+ }
+ ValidSigFound = true;
+ } /*- for (list < SignatureInfo >::iterator s = Signatures.begin(); s != Signatures.end(); ++s) { */
+ if (!ValidSigFound)
+ return DKIM_NO_VALID_SIGNATURES;
+ return DKIM_SUCCESS;
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Strictly parse an unsigned integer. Don't allow spaces, negative sign,
+// 0x prefix, etc. Values greater than 2^32-1 are capped at 2^32-1
+//
+////////////////////////////////////////////////////////////////////////////////
+bool
+ParseUnsigned(const char *s, int *result)
+{
+ unsigned temp = 0, last = 0;
+ bool overflowed = false;
+
+ do {
+ if (*s < '0' || *s > '9')
+ return false; // returns false for an initial '\0'
+ temp = temp * 10 + (*s - '0');
+ if (temp < last)
+ overflowed = true;
+ last = temp;
+ s++;
+ } while (*s != '\0');
+ if (overflowed)
+ *result = -1;
+ else
+ *result = temp;
+ return true;
+}
+
+
+// ParseDKIMSignature - Parse a DKIM-Signature header field
+int
+CDKIMVerify::ParseDKIMSignature(const string & sHeader, SignatureInfo & sig)
+{
+
+ // save header for later
+ sig.Header = sHeader;
+ string sValue = sHeader.substr(sHeader.find(':') + 1);
+ static const char *tags[] = { "v", "a", "b", "d", "h", "s", "c", "i", "l", "q", "t", "x", "bh", NULL };
+ char *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
+
+ if (!ParseTagValueList((char *) sValue.c_str(), tags, values))
+ return DKIM_BAD_SYNTAX;
+ // check signature version
+ if (values[0] != NULL) {
+ if (strcmp(values[0], "1") == 0 || strcmp(values[0], "0.5") == 0 || strcmp(values[0], "0.4") == 0
+ || strcmp(values[0], "0.3") == 0 || strcmp(values[0], "0.2") == 0) {
+ sig.Version = DKIM_SIG_VERSION_02_PLUS;
+ } else {
+ // unknown version
+ return DKIM_STAT_INCOMPAT;
+ }
+ } else {
+ // Note: DKIM Interop 1 pointed out that v= is now required, but we do
+ // not enforce that in order to verify signatures made by older drafts.
+
+ // prior to 0.2, there MUST NOT have been a v=
+ // (optionally) support these signatures, for backwards compatibility
+ if (true) {
+ sig.Version = DKIM_SIG_VERSION_PRE_02;
+ } else {
+ return DKIM_BAD_SYNTAX;
+ }
+ }
+ // signature MUST have a=, b=, d=, h=, s=
+ if (values[1] == NULL || values[2] == NULL || values[3] == NULL || values[4] == NULL || values[5] == NULL)
+ return DKIM_BAD_SYNTAX;
+ // algorithm can be "rsa-sha1" or "rsa-sha256"
+ if (strcmp(values[1], "rsa-sha1") == 0) {
+ sig.m_nHash = DKIM_HASH_SHA1;
+ }
+#ifdef HAVE_EVP_SHA256
+ else if (strcmp(values[1], "rsa-sha256") == 0) {
+ sig.m_nHash = DKIM_HASH_SHA256;
+ }
+#endif
+ else {
+ return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown algorithm
+ }
+ // make sure the signature data is not empty
+ unsigned SigDataLen = DecodeBase64(values[2]);
+ if (SigDataLen == 0)
+ return DKIM_BAD_SYNTAX;
+ sig.SignatureData.assign(values[2], SigDataLen);
+ // check for body hash
+ if (values[12] == NULL) {
+ // use the old single hash way for backwards compatibility
+ if (sig.Version != DKIM_SIG_VERSION_PRE_02)
+ return DKIM_BAD_SYNTAX;
+ } else {
+ unsigned BodyHashLen = DecodeBase64(values[12]);
+ if (BodyHashLen == 0)
+ return DKIM_BAD_SYNTAX;
+ sig.BodyHashData.assign(values[12], BodyHashLen);
+ }
+ // domain must not be empty
+ if (*values[3] == '\0')
+ return DKIM_BAD_SYNTAX;
+ sig.Domain = values[3];
+ // signed headers must not be empty (more verification is done later)
+ if (*values[4] == '\0')
+ return DKIM_BAD_SYNTAX;
+ // selector must not be empty
+ if (*values[5] == '\0')
+ return DKIM_BAD_SYNTAX;
+ sig.Selector = values[5];
+ // canonicalization
+ if (values[6] == NULL) {
+ sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_SIMPLE;
+ }
+ else
+ if (sig.Version == DKIM_SIG_VERSION_PRE_02 && strcmp(values[6], "nowsp") == 0) {
+ // for backwards compatibility
+ sig.HeaderCanonicalization = sig.BodyCanonicalization = DKIM_CANON_NOWSP;
+ } else {
+ char *slash = strchr(values[6], '/');
+ if (slash != NULL)
+ *slash = '\0';
+ if (strcmp(values[6], "simple") == 0)
+ sig.HeaderCanonicalization = DKIM_CANON_SIMPLE;
+ else
+ if (strcmp(values[6], "relaxed") == 0)
+ sig.HeaderCanonicalization = DKIM_CANON_RELAXED;
+ else
+ return DKIM_BAD_SYNTAX;
+ if (slash == NULL || strcmp(slash + 1, "simple") == 0)
+ sig.BodyCanonicalization = DKIM_CANON_SIMPLE;
+ else
+ if (strcmp(slash + 1, "relaxed") == 0)
+ sig.BodyCanonicalization = DKIM_CANON_RELAXED;
+ else
+ return DKIM_BAD_SYNTAX;
+ }
+ // identity
+ if (values[7] == NULL) {
+ sig.IdentityLocalPart.erase();
+ sig.IdentityDomain = sig.Domain;
+ } else {
+ // quoted-printable decode the value
+ DecodeQuotedPrintable(values[7]);
+ // must have a '@' separating the local part from the domain
+ char *at = strchr(values[7], '@');
+ if (at == NULL)
+ return DKIM_BAD_SYNTAX;
+ *at = '\0';
+ char *ilocalpart = values[7];
+ char *idomain = at + 1;
+ // i= domain must be the same as or a subdomain of the d= domain
+ int idomainlen = strlen(idomain);
+ int ddomainlen = strlen(values[3]);
+
+ // todo: maybe create a new error code for invalid identity domain
+ if (idomainlen < ddomainlen)
+ return DKIM_BAD_SYNTAX;
+ if (_stricmp(idomain + idomainlen - ddomainlen, values[3]) != 0)
+ return DKIM_BAD_SYNTAX;
+ if (idomainlen > ddomainlen && idomain[idomainlen - ddomainlen - 1] != '.')
+ return DKIM_BAD_SYNTAX;
+ sig.IdentityLocalPart = ilocalpart;
+ sig.IdentityDomain = idomain;
+ }
+ // body count
+ if (values[8] == NULL || !m_HonorBodyLengthTag) {
+ sig.BodyLength = -1;
+ } else {
+ if (!ParseUnsigned(values[8], &sig.BodyLength))
+ return DKIM_BAD_SYNTAX;
+ }
+ // query methods
+ if (values[9] != NULL) {
+
+ // make sure "dns" is in the list
+ bool HasDNS = false;
+ char *s = strtok(values[9], ":");
+ while (s != NULL) {
+ if (strncmp(s, "dns", 3) == 0 && (s[3] == '\0' || s[3] == '/')) {
+ HasDNS = true;
+ break;
+ }
+ s = strtok(NULL, ": \t");
+ }
+ if (!HasDNS)
+ return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for unknown query method
+ }
+ // signature time
+ int SignedTime = -1;
+ if (values[10] != NULL) {
+ if (!ParseUnsigned(values[10], &SignedTime))
+ return DKIM_BAD_SYNTAX;
+ }
+ // expiration time
+ if (values[11] == NULL) {
+ sig.ExpireTime = -1;
+ } else {
+ if (!ParseUnsigned(values[11], &sig.ExpireTime))
+ return DKIM_BAD_SYNTAX;
+ if (sig.ExpireTime != -1) {
+ // the value of x= MUST be greater than the value of t= if both are present
+ if (SignedTime != -1 && sig.ExpireTime <= SignedTime)
+ return DKIM_BAD_SYNTAX;
+ // todo: if possible, use the received date/time instead of the current time
+ unsigned curtime = time(NULL);
+ if (curtime > sig.ExpireTime)
+ return DKIM_SIGNATURE_EXPIRED;
+ }
+ }
+ // parse the signed headers list
+ bool HasFrom = false, HasSubject = false;
+ RemoveSWSP(values[4]); // header names shouldn't have spaces in them so this should be ok...
+ char *s = strtok(values[4], ":");
+ while (s != NULL) {
+ if (_stricmp(s, "From") == 0)
+ HasFrom = true;
+ else
+ if (_stricmp(s, "Subject") == 0)
+ HasSubject = true;
+ sig.SignedHeaders.push_back(s);
+ s = strtok(NULL, ":");
+ }
+ if (!HasFrom)
+ return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing From
+ if (m_SubjectIsRequired && !HasSubject)
+ return DKIM_BAD_SYNTAX; // todo: maybe create a new error code for h= missing Subject
+ return DKIM_SUCCESS;
+}
+
+
+// ProcessBody - Process message body data
+int
+CDKIMVerify::ProcessBody(char *szBuffer, int nBufLength, bool bEOF)
+{
+ bool MoreBodyNeeded = false;
+
+ for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
+ if (i->Status == DKIM_SUCCESS) {
+ if (i->BodyCanonicalization == DKIM_CANON_SIMPLE) {
+ if (nBufLength > 0) {
+ while (i->EmptyLineCount > 0) {
+ i->Hash("\r\n", 2, true);
+ i->EmptyLineCount--;
+ }
+ i->Hash(szBuffer, nBufLength, true);
+ i->Hash("\r\n", 2, true);
+ } else {
+ i->EmptyLineCount++;
+ if (bEOF)
+ i->Hash("\r\n", 2, true);
+ }
+ } else
+ if (i->BodyCanonicalization == DKIM_CANON_RELAXED) {
+ CompressSWSP(szBuffer, nBufLength);
+ if (nBufLength > 0) {
+ while (i->EmptyLineCount > 0) {
+ i->Hash("\r\n", 2, true);
+ i->EmptyLineCount--;
+ }
+ i->Hash(szBuffer, nBufLength, true);
+ if (!bEOF)
+ i->Hash("\r\n", 2, true);
+ } else
+ i->EmptyLineCount++;
+ } else
+ if (i->BodyCanonicalization == DKIM_CANON_NOWSP) {
+ RemoveSWSP(szBuffer, nBufLength);
+ i->Hash(szBuffer, nBufLength, true);
+ }
+ if (i->UnverifiedBodyCount == 0)
+ MoreBodyNeeded = true;
+ }
+ }
+ if (!MoreBodyNeeded)
+ return DKIM_FINISHED_BODY;
+ return DKIM_SUCCESS;
+}
+
+SelectorInfo::SelectorInfo(const string & sSelector, const string & sDomain):Selector(sSelector), Domain(sDomain)
+{
+ AllowSHA1 = true;
+#ifdef HAVE_EVP_SHA256
+ AllowSHA256 = true;
+#else
+ AllowSHA256 = false;
+#endif
+ PublicKey = NULL;
+ Testing = false;
+ SameDomain = false;
+ Status = DKIM_SUCCESS;
+} SelectorInfo::~SelectorInfo()
+{
+ if (PublicKey != NULL) {
+ EVP_PKEY_free(PublicKey);
+ }
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+//
+// Parse - Parse a DKIM selector
+//
+////////////////////////////////////////////////////////////////////////////////
+int
+SelectorInfo::Parse(char *Buffer)
+{
+ static const char *tags[] = { "v", "g", "h", "k", "p", "s", "t", "n", NULL };
+ char *values[sizeof (tags) / sizeof (tags[0])] = { NULL };
+ if (!ParseTagValueList(Buffer, tags, values))
+ return DKIM_SELECTOR_INVALID;
+ if (values[0] != NULL) {
+
+ // make sure the version is "DKIM1"
+ if (strcmp(values[0], "DKIM1") != 0)
+ return DKIM_SELECTOR_INVALID; // todo: maybe create a new error code for unsupported selector version
+ // make sure v= is the first tag in the response // todo: maybe don't enforce this, it seems unnecessary
+ for (int j = 1; j < sizeof (values) / sizeof (values[0]); j++) {
+ if (values[j] != NULL && values[j] < values[0]) {
+ return DKIM_SELECTOR_INVALID;
+ }
+ }
+ }
+ // selector MUST have p= tag
+ if (values[4] == NULL)
+ return DKIM_SELECTOR_INVALID;
+ // granularity
+ if (values[1] == NULL)
+ Granularity = "*";
+
+ else
+ Granularity = values[1];
+ // hash algorithm
+ if (values[2] == NULL) {
+ AllowSHA1 = true;
+#ifdef HAVE_EVP_SHA256
+ AllowSHA256 = true;
+#else
+ AllowSHA256 = false;
+#endif
+ } else {
+ // MUST include "sha1" or "sha256"
+ char *s = strtok(values[2], ":");
+ while (s != NULL) {
+ if (strcmp(s, "sha1") == 0)
+ AllowSHA1 = true;
+#ifdef HAVE_EVP_SHA256
+ else if (strcmp(s, "sha256") == 0)
+ AllowSHA256 = true;
+#endif
+ s = strtok(NULL, ":");
+ }
+#ifdef HAVE_EVP_SHA256
+ if (!(AllowSHA1 || AllowSHA256))
+#else
+ if (!AllowSHA1)
+#endif
+ return DKIM_SELECTOR_INVALID; // todo: maybe create a new error code for unsupported hash algorithm
+ }
+ // key type
+ if (values[3] != NULL) {
+ // key type MUST be "rsa"
+ if (strcmp(values[3], "rsa") != 0)
+ return DKIM_SELECTOR_INVALID;
+ }
+ // service type
+ if (values[5] != NULL) {
+ // make sure "*" or "email" is in the list
+ bool ServiceTypeMatch = false;
+ char *s = strtok(values[5], ":");
+ while (s != NULL) {
+ if (strcmp(s, "*") == 0 || strcmp(s, "email") == 0) {
+ ServiceTypeMatch = true;
+ break;
+ }
+ s = strtok(NULL, ":");
+ }
+ if (!ServiceTypeMatch)
+ return DKIM_SELECTOR_INVALID;
+ }
+ // flags
+ if (values[6] != NULL) {
+ char *s = strtok(values[6], ":");
+ while (s != NULL) {
+ if (strcmp(s, "y") == 0) {
+ Testing = true;
+ } else
+ if (strcmp(s, "s") == 0) {
+ SameDomain = true;
+ }
+ s = strtok(NULL, ":");
+ }
+ }
+ // public key data
+ unsigned
+ PublicKeyLen = DecodeBase64(values[4]);
+ if (PublicKeyLen == 0) {
+ return DKIM_SELECTOR_KEY_REVOKED; // this error causes the signature to fail
+ } else {
+ const unsigned char *PublicKeyData = (unsigned char *) values[4];
+ EVP_PKEY *pkey = d2i_PUBKEY(NULL, (const unsigned char **) &PublicKeyData, PublicKeyLen);
+ if (pkey == NULL)
+ return DKIM_SELECTOR_PUBLIC_KEY_INVALID;
+ // make sure public key is the correct type (we only support rsa)
+ if (pkey->type == EVP_PKEY_RSA || pkey->type == EVP_PKEY_RSA2) {
+ PublicKey = pkey;
+ } else {
+ EVP_PKEY_free(pkey);
+ return DKIM_SELECTOR_PUBLIC_KEY_INVALID;
+ }
+ }
+ return DKIM_SUCCESS;
+}
+
+// GetSelector - Get a DKIM selector for a domain
+SelectorInfo & CDKIMVerify::GetSelector(const string & sSelector, const string & sDomain)
+{
+
+ // see if we already have this selector
+ for (list < SelectorInfo >::iterator i = Selectors.begin(); i != Selectors.end(); ++i) {
+ if (_stricmp(i->Selector.c_str(), sSelector.c_str()) == 0 && _stricmp(i->Domain.c_str(), sDomain.c_str()) == 0) {
+ return *i;
+ }
+ }
+ Selectors.push_back(SelectorInfo(sSelector, sDomain));
+ SelectorInfo & sel = Selectors.back();
+ string sFQDN = sSelector;
+ sFQDN += "._domainkey.";
+ sFQDN += sDomain;
+ char Buffer[1024];
+ int DNSResult;
+
+ if (m_pfnSelectorCallback)
+ DNSResult = m_pfnSelectorCallback(sFQDN.c_str(), Buffer, sizeof(Buffer));
+ else
+ DNSResult = DNSGetTXT(sFQDN.c_str(), Buffer, sizeof(Buffer));
+ switch (DNSResult) {
+ case DNSRESP_SUCCESS:
+ sel.Status = sel.Parse(Buffer);
+ break;
+ case DNSRESP_TEMP_FAIL:
+ sel.Status = DKIM_SELECTOR_DNS_TEMP_FAILURE;
+ break;
+ case DNSRESP_PERM_FAIL:
+ default:
+ sel.Status = DKIM_SELECTOR_DNS_PERM_FAILURE;
+ break;
+ case DNSRESP_DOMAIN_NAME_TOO_LONG:
+ sel.Status = DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG;
+ break;
+ }
+ return sel;
+}
+
+// GetDetails - Get DKIM verification details (per signature)
+int
+CDKIMVerify::GetDetails(int *nSigCount, DKIMVerifyDetails ** pDetails)
+{
+ Details.clear();
+ for (list < SignatureInfo >::iterator i = Signatures.begin(); i != Signatures.end(); ++i) {
+ DKIMVerifyDetails d;
+ d.szSignature = (char *) i->Header.c_str();
+ d.nResult = i->Status;
+ d.szCanonicalizedData = (char *) i->CanonicalizedData.c_str();
+ Details.push_back(d);
+ } *nSigCount = Details.size();
+ *pDetails = (*nSigCount != 0) ? &Details[0] : NULL;
+ return DKIM_SUCCESS;
+}
+
+char *DKIM_CALL
+CDKIMVerify::GetDomain(void)
+{
+ string sFromDomain;
+ for (list <string>::iterator i = HeaderList.begin(); i != HeaderList.end(); ++i) {
+ if (_strnicmp(i->c_str(), "From", 4) == 0) {
+ // skip over whitespace between the header name and :
+ const char *s = i->c_str() + 4;
+ while (*s == ' ' || *s == '\t')
+ s++;
+ if (*s == ':') {
+ vector <string> Addresses;
+ if (ParseAddresses(s + 1, Addresses)) {
+ unsigned atpos = Addresses[0].find('@');
+ sFromDomain = Addresses[0].substr(atpos + 1);
+ break;
+ }
+ }
+ }
+ }
+ return ((char *) sFromDomain.c_str());
+}
+
+void
+getversion_dkimverify_cpp()
+{
+ static char *x = (char *) "$Id: dkimverify.cpp,v 1.5 2009-03-27 20:19:58+05:30 Cprogrammer Exp mbhangui $";
+
+ x++;
+}
diff -Naur qmail-1.03.org/dkimverify.h qmail-1.03/dkimverify.h
--- qmail-1.03.org/dkimverify.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dkimverify.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,118 @@
+/*
+ * $Log: dkimverify.h,v $
+ * Revision 1.2 2009-03-26 15:12:15+05:30 Cprogrammer
+ * changes for ADSP
+ *
+ * Revision 1.1 2009-03-21 08:50:22+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * Copyright 2005 Alt-N Technologies, Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * This code incorporates intellectual property owned by Yahoo! and licensed
+ * pursuant to the Yahoo! DomainKeys Patent License Agreement.
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#ifndef DKIMVERIFY_H
+#define DKIMVERIFY_H
+
+#include "dkimbase.h"
+#include <vector>
+
+#define DKIM_SIG_VERSION_PRE_02 0
+#define DKIM_SIG_VERSION_02_PLUS 1
+
+class SelectorInfo {
+ public:
+ SelectorInfo(const string & sSelector, const string & sDomain);
+ ~SelectorInfo();
+
+ string Domain;
+ string Selector;
+ string Granularity;
+ bool AllowSHA1;
+ bool AllowSHA256;
+ EVP_PKEY *PublicKey; /* the public key */
+ bool Testing;
+ bool SameDomain;
+ int Status;
+ int Parse(char *Buffer);
+};
+
+class SignatureInfo {
+public:
+ SignatureInfo(bool SaveCanonicalizedData);
+ ~SignatureInfo();
+
+ void Hash(const char *szBuffer, unsigned nBufLength, bool IsBody = false);
+ string Header;
+ unsigned Version;
+ string Domain;
+ string Selector;
+ string SignatureData;
+ string BodyHashData;
+ string IdentityLocalPart;
+ string IdentityDomain;
+ string CanonicalizedData;
+ vector <string> SignedHeaders;
+ int BodyLength;
+ unsigned HeaderCanonicalization;
+ unsigned BodyCanonicalization;
+ int ExpireTime;
+ unsigned VerifiedBodyCount;
+ unsigned UnverifiedBodyCount;
+ EVP_MD_CTX m_Hdr_ctx;
+ EVP_MD_CTX m_Bdy_ctx;
+ SelectorInfo *m_pSelector;
+ int Status;
+ int m_nHash; // use one of the DKIM_HASH_xxx constants here
+ unsigned EmptyLineCount;
+ bool m_SaveCanonicalizedData;
+};
+
+class CDKIMVerify:public CDKIMBase {
+public:
+
+ CDKIMVerify();
+ ~CDKIMVerify();
+
+ int Init(DKIMVerifyOptions * pOptions);
+ int GetResults(int *sCount, int *sSize);
+ int GetDetails(int *nSigCount, DKIMVerifyDetails ** pDetails);
+ virtual int ProcessHeaders(void);
+ virtual int ProcessBody(char *szBuffer, int nBufLength, bool bEOF);
+ const char *GetPractices() {return Practices.c_str();}
+ char *DKIM_CALL GetDomain(void);
+
+protected:
+ int ParseDKIMSignature(const string & sHeader, SignatureInfo & sig);
+ SelectorInfo &GetSelector(const string & sSelector, const string & sDomain);
+ int GetADSP(const string &sDomain, int &iADSP);
+ int GetSSP(const string &sDomain, int &iSSP, bool & bTesting);
+ list <SignatureInfo> Signatures;
+ list <SelectorInfo> Selectors;
+ DKIMDNSCALLBACK m_pfnSelectorCallback; // selector record callback
+ DKIMDNSCALLBACK m_pfnPracticesCallback; // SSP record callback
+ bool m_HonorBodyLengthTag;
+ bool m_CheckPractices;
+ bool m_Accept3ps; //TBS(Luc) : accept 3rd party signature(s)
+ bool m_SubjectIsRequired;
+ bool m_SaveCanonicalizedData;
+ vector <DKIMVerifyDetails> Details;
+ string Practices;
+};
+
+#endif /*- DKIMVERIFY_H */
diff -Naur qmail-1.03.org/dktrace.h qmail-1.03/dktrace.h
--- qmail-1.03.org/dktrace.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/dktrace.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,26 @@
+/* $Id: dktrace.h,v 1.3 2005/06/27 18:47:57 ted46045 Exp $ */
+
+#ifndef _DK_TRACE_H
+#define _DK_TRACE_H
+
+typedef struct {
+ int ccounts_h[256];
+ int ccounts_H[256];
+ int ccounts_b[256];
+ int ccounts_B[256];
+} DK_TRACE;
+
+typedef enum { DKT_RAW_HEADER='h', DKT_CANON_HEADER='H',
+ DKT_RAW_BODY='b', DKT_CANON_BODY='B' } DK_TRACE_TYPE;
+
+#define dkt_init(s) memset(s,0,sizeof(DK_TRACE))
+
+//extern void dkt_init(DK_TRACE *dkp);
+extern void dkt_add(DK_TRACE *dkp, DK_TRACE_TYPE type, const unsigned char *data, int dataLength);
+extern int dkt_diff(DK_TRACE *dka, DK_TRACE *dkb, DK_TRACE_TYPE type, DK_TRACE *table);
+extern void dkt_quickadd(DK_TRACE *dkp, DK_TRACE_TYPE type, int index, int count);
+extern int dkt_getcount(DK_TRACE *dkp, DK_TRACE_TYPE type, int index, int count);
+extern int dkt_generate(DK_TRACE *dkp, DK_TRACE_TYPE type, char *buffer, int maxBufferSize);
+extern int dkt_hdrtotrace(char *ptr, DK_TRACE *store);
+
+#endif
diff -Naur qmail-1.03.org/domainkeys.h qmail-1.03/domainkeys.h
--- qmail-1.03.org/domainkeys.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/domainkeys.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,377 @@
+/* This file is automatically created from the corresponding .c file */
+/* Do not change this file; change the .c file instead. */
+/* This is libdomainkeys. It's Copyright (c) 2004 Yahoo, Inc.
+ * This code incorporates intellectual property owned by
+ * Yahoo! and licensed pursuant to the Yahoo! DomainKeys Public License
+ * Agreement: http://domainkeys.sourceforge.net/license/softwarelicense1-0.html
+ */
+#include <openssl/evp.h>
+#include <openssl/pem.h>
+#include <openssl/err.h>
+
+#ifdef SWIG
+%module domainkeys
+%{
+#include "domainkeys.h"
+%}
+#endif
+
+#include "dktrace.h"
+
+/* Performance/Debug options.
+ * Uncomment below or use -D switch in gcc
+ * DK_DEBUG Dumps whatever dkhash() hashes in to stderr and turns on
+ * some debug warnings that should never happen
+ * DK_HASH_BUFF Enables code that uses a buffer when processing the
+ * canocalized message, reducing calls to the crypto library (from dkhash()),
+ * but can use up slightly more memory
+*/
+//#define DK_DEBUG 1
+#define DK_HASH_BUFF 1
+
+
+#define DKMARK ('D' | 'K'<<8 | 'E'<<16 | 'Y'<<24)
+#define DK_SIGNING_SIGN 0
+#define DK_SIGNING_VERIFY 1
+#define DK_SIGNING_NOSIGN 2
+#define DK_SIGNING_NOVERIFY 3
+#define DK_MALLOC(s) OPENSSL_malloc(s)
+#define DK_MFREE(s) OPENSSL_free(s); s = NULL;
+#define DKERR(x) ((dk->errline=__LINE__),(dk->errfile=__FILE__),(x))
+#define DK_BLOCK 1024 //default size of malloc'd block
+
+/*
+ * Option Flags for dk_setopts
+ * OR together or run dk_setopts several times
+ * All option flags are OFF by default
+*/
+#define DKOPT_TRACE_h 0x01 //enables tracking character count in pre-canon header
+#define DKOPT_TRACE_H 0x02 //enables tracking character count in post-canon header
+#define DKOPT_TRACE_b 0x04 //enables tracking character count in pre-canon body
+#define DKOPT_TRACE_B 0x08 //enables tracking character count in post-canon header
+#define DKOPT_RDUPE 0x10 //enables skipping duplicate headers when generateing a signature
+
+typedef enum
+{
+ DK_STAT_OK, /* Function completed successfully */
+ DK_STAT_BADSIG, /* Signature was available but failed to verify against domain specified key */
+ DK_STAT_NOSIG, /* No signature available in message */
+ DK_STAT_NOKEY, /* No public key available (permanent failure) */
+ DK_STAT_BADKEY, /* Unusable key, public if verifying, private if signing */
+ DK_STAT_CANTVRFY, /* Cannot get domain key to verify signature (temporary failure) */
+ DK_STAT_SYNTAX, /* Message is not valid syntax. Signature could not be created/checked */
+ DK_STAT_NORESOURCE, /* Could not get critical resource (temporary failure) */
+ DK_STAT_ARGS, /* Arguments are not usable. */
+ DK_STAT_REVOKED, /* Key has been revoked. */
+ DK_STAT_INTERNAL, /* cannot call this routine in this context. Internal error. */
+ DK_STAT_GRANULARITY, /* Granularity mismatch: sender doesn't match g= option. */
+} DK_STAT;
+
+typedef enum
+{
+ DK_FLAG_TESTING = 1, /* set when in testing mode. */
+ DK_FLAG_SIGNSALL = 2, /* domain signs all outgoing email. */
+ DK_FLAG_SET = 4, /* flags set from a successful DNS query */
+ DK_FLAG_G = 8, /* g tag was present in the selector. */
+} DK_FLAGS;
+typedef enum
+{
+ DK_TXT_KEY = 0,
+ DK_TXT_POLICY
+} DK_TXT;
+
+typedef enum
+{
+ DK_CANON_SIMPLE = 0,
+ DK_CANON_NOFWS = 1,
+} DK_CANON;
+/* STARTSTRUCT */
+typedef struct
+{
+
+} DK_LIB;
+/* STOPSTRUCT */
+
+//UnixWare Fix -Tim
+/* STARTSTRUCT */
+typedef struct
+{
+} DK;
+/* STOPSTRUCT */
+
+
+/* returns the source file from which an error was returned. */
+char * dk_errfile(DK *dk)
+;
+
+
+/* returns the source line number from which an error was returned. */
+int dk_errline(DK *dk)
+;
+
+
+/* Per-process, one-time initialization
+ * Returns library structure for subsequent dk_sign or dk_verify calls.
+ * Consult statp before using.
+ *
+ * When terminating the PROCESS its a good idea to call dk_shutdown()
+ * When terminating a THREAD it's a good idea to call ERR_remove_state(0); defined in <openssl/err.h>
+ * NOTE: DK_LIB pointers are safe to use over multiple threads
+ * DK pointers are NOT safe to use over multiple threads
+ */
+DK_LIB *dk_init(DK_STAT *statp)
+;
+
+
+/* Per-process, one-time cleanup
+ * Should be called just before the application ends.
+ * the dklib pointer is not valid anymore after this call
+ * This function should be called even if dk_init failed.
+ * It's safe to call dk_shutdown with a NULL pointer
+ */
+void dk_shutdown(DK_LIB * dklib)
+;
+
+
+/* Set dk options, use instead of dk_remdupe and dk_enable_trace
+ * Can be called multiple times.
+ * use after dk_sign()/dk_verify()
+ *
+ * the bits field can be an OR of any of the following
+ *DKOPT_TRACE_h Trace pre-canon header
+ *DKOPT_TRACE_H Trace post-canon header
+ *DKOPT_TRACE_b Trace pre-canon body
+ *DKOPT_TRACE_B Trace post-canon body
+ *DKOPT_RDUPE Exclude duplicate headers from hash (Signing only)
+ */
+DK_STAT dk_setopts(DK *dk, int bits)
+;
+
+
+/* returns the int holding the options set
+ * See dk_setopts for bit flags
+ */
+int dk_getopts(DK *dk)
+;
+
+
+/* DEPRECATED in favor of calling dk_setopts().
+ * Enables character trace tracking
+ *
+ * use after dk_sign()/dk_verify()
+ */
+DK_STAT dk_enable_trace(DK *dk)
+;
+
+
+/* Prints trace table to *store variable (char string)
+ * *dk is the container for the table
+ * *store is a pointer to a character array to output to
+ * store_size is the size of the character array *store
+ *
+ */
+DK_STAT dk_get_trace(DK *dk, DK_TRACE_TYPE type, char *store, int store_size)
+;
+
+
+/* Prints difference trace table to *store variable (char string)
+ * *dk is the container for the table
+ * *store is a pointer to a character array to output to
+ * store_size is the size of the character array *store
+ * return DK_STAT_NOSIG if no DK-Trace header was found
+ */
+DK_STAT dk_compare_trace(DK *dk, DK_TRACE_TYPE type, char *store, int store_size)
+;
+
+
+/* Sets the DNS key/policy record manually (no DNS lookup)
+ * txtrecord needs to be set to "e=perm;" to force a permanent DNS failure
+ * txtrecord needs to be set to "e=temp;" to force a temporary DNS failure
+ * Valid DK_TXT types are:
+ * DK_TXT_KEY (normal selector record; for <selctor>._domainkey.<domain>)
+ * DK_TXT_POLICY (domain policy record; for _domainkey.<domain>)
+ */
+DK_STAT dk_settxt(DK *dk, DK_TXT recordtype, const char *txtrecord)
+;
+
+
+/* Per-message, may be threaded.
+ * canon is one of DK_CANON_*.
+ * Returns state structure for operation. Consult statp before using.
+ */
+DK *dk_sign(DK_LIB *dklib, DK_STAT *statp, int canon)
+;
+
+
+/* Per-message, may be threaded.
+ * Returns state structure for operation. Consult statp before using.
+ */
+DK *dk_verify(DK_LIB *dklib, DK_STAT *statp)
+;
+
+
+/* DEPRECATED in favor of calling dk_setopts()
+ * set option to remove dupe headers
+ * should be called after dk_sign();
+ * any int NOT 0 turns dupe removal on
+ */
+DK_STAT dk_remdupe(DK *dk,int i)
+;
+
+
+/* Returns the policy flags belonging to the signing domain.
+ * Sender: overrides From:, and the d= entry in the DK-Sig overrides both.
+ * If the policy flags were not successfully fetched, DK_FLAG_SET will not
+ * be set.
+ */
+DK_FLAGS dk_policy(DK *dk)
+;
+
+
+/* Copies the header names that were signed into the pointer.
+ * Returns the number of bytes copied.
+ * ptr may be NULL, in which case the bytes are just counted, not copied.
+ * Feel free to call this twice; once to get the length, and again to
+ * copy the data.
+ * NOTE: If the return value is 0 then an error occured.
+ * It's a good idea to check for this
+ */
+int dk_headers(DK *dk, char *ptr)
+;
+
+
+/* Must NOT include dots inserted for SMTP encapsulation.
+ * Must NOT include CRLF.CRLF which terminates the message.
+ * Otherwise must be exactly that which is sent or received over the SMTP session.
+ * May be called multiple times (not necessary to read an entire message into memory).
+ */
+DK_STAT dk_message(DK *dk, const unsigned char *ptr, size_t len)
+;
+
+
+/* DEPRECATED in favor of calling dk_address().
+ * Returns a pointer to a null-terminated domain name portion of an RFC 2822 address.
+ * If a Sender: was encountered, it returns that domain. Otherwise,
+ * if a From: was encountered, it returns that domain. Otherwise,
+ * return NULL.
+ * return NULL if no domain name found in the address.
+ * return NULL if the dk is unusable for any reason.
+ * return NULL if the address is unusable for any reason.
+ */
+char *dk_from(DK *dk)
+;
+
+
+/* Returns a pointer to the selector name used or NULL if there isn't one
+ * Added by rjp
+ */
+const char *dk_selector(DK *dk)
+;
+
+
+/* Returns a pointer to the domain name used or NULL if there isn't one
+ */
+const char *dk_domain(DK *dk)
+;
+
+
+/*
+ * Returns a pointer to a string which begins with "N", "S", or "F",
+ * corresponding to None, Sender: and From:, respectively.
+ * This single character is followed by a null-terminated RFC 2822 address.
+ * The first character is "N" if no valid address has been seen yet,
+ * "S" if the address came from the Sender: field, and "F" if the
+ * address came from the From: field.
+ */
+char *dk_address(DK *dk)
+;
+
+
+/*
+ * Returns a pointer to a null-terminated string containing the granularity
+ * value found in the selector DNS record, if any, but only after dk_end
+ * has been called. Otherwise returns NULL.
+ */
+char *dk_granularity(DK *dk)
+;
+
+
+/*
+ * Called at end-of-message (before response to DATA-dot, if synchronous with SMTP session).
+ * If verifying, returns signature validity.
+ * This does not calculate the signature. Call dk_getsig() for that.
+ * Flags are returned indirectly through dkf.
+ * If you pass in NULL for dkf, the flags will not be fetched.
+ * If there is a DK-Sig line, the d= entry will be used to fetch the flags.
+ * Otherwise the Sender: domain will be used to fetch the flags.
+ * Otherwise the From: domain will be used to fetch the flags.
+ *
+ * NOTE: If for some reason dk_end() returns an error (!DK_STAT_OK) dk_policy() should be called
+ * to get the domain signing policy (o=) and handle accordingly.
+ * dkf (selector flags) wont be set if dk_end() returns
+ * DK_STAT_NOSIG
+ * DK_STAT_NOKEY
+ * DK_STAT_SYNTAX
+ * DK_STAT_NORESOURCE
+ * DK_STAT_BADKEY
+ * DK_STAT_CANTVERIFY
+ */
+DK_STAT dk_end(DK *dk, DK_FLAGS *dkf)
+;
+
+
+/*
+ * DEPRECATED in favor of calling dk_end and dk_policy() directly.
+ * If you pass in NULL for dkf, the policy flags will not be fetched.
+ * If the message verified okay, the policy flags will not be fetched.
+ */
+DK_STAT dk_eom(DK *dk, DK_FLAGS *dkf)
+;
+
+
+/*
+ *
+ * privatekey is the private key used to create the signature; It should contain
+ * the entire contents of a PEM-format private key file, thusly it will begin with
+ * -----BEGIN RSA PRIVATE KEY-----. It should be null-terminated.
+ */
+size_t dk_siglen(void *privatekey)
+;
+
+
+/*
+ * Sets buf to a null-terminated string.
+ * If the message is being signed, signature is stored in the buffer.
+ * If the message is being verified, returns DK_STAT_INTERNAL.
+ * privatekey is the private key used to create the signature; It should contain
+ * the entire contents of a PEM-format private key file, thus it will begin with
+ * -----BEGIN RSA PRIVATE KEY-----. It should be null-terminated.
+ * If you pass in NULL for buf, you'll get back DK_STAT_NORESOURCE.
+ * If len is not big enough, you'll get back DK_STAT_NORESOURCE.
+ */
+DK_STAT dk_getsig(DK *dk, void *privatekey, unsigned char buf[], size_t len)
+;
+
+
+/*
+ * Free all resources associated with this message.
+ * dk is no longer usable.
+ * if doClearErrState != 0, the OpenSSL ErrorState is freed.
+ * Set clearErrState=0 if you use other openssl functions and
+ * want to call openssl's ERR_remove_state(0) by yourself
+ * ERR_remove_state(0) is declared in <openssl/err.h>
+ */
+DK_STAT dk_free(DK *dk, int doClearErrState)
+;
+
+
+/*
+ * return a pointer to a string which describes st.
+ * The string is structured. All the characters up to the first colon
+ * contain the name of the DK_STAT constant. From there to the end of
+ * string is a human-readable description of the error.
+ */
+const char *DK_STAT_to_string(DK_STAT st)
+;
+
+
diff -Naur qmail-1.03.org/macros.h qmail-1.03/macros.h
--- qmail-1.03.org/macros.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/macros.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,25 @@
+/*
+ * $Log: macros.h,v $
+ * Revision 1.1 2009-03-21 08:50:25+05:30 Cprogrammer
+ * Initial revision
+ *
+ *
+ * macros.h: Useful macros
+ *
+ * Author:
+ * Dick Porter (dick@ximian.com)
+ *
+ * (C) 2002 Ximian, Inc.
+ */
+
+#ifndef _WAPI_MACROS_H_
+#define _WAPI_MACROS_H_
+
+#include <sys/types.h>
+
+#define MAKEWORD(low, high) ((__uint16_t)(((__uint8_t)(low)) | \
+ ((__uint16_t)((__uint8_t)(high))) << 8))
+#define LOBYTE(i16) ((__uint8_t)((i16) & 0xFF))
+#define HIBYTE(i16) ((__uint8_t)(((__uint16_t)(i16) >> 8) & 0xFF))
+
+#endif /* _WAPI_MACROS_H_ */
diff -Naur qmail-1.03.org/MakeArgs.c qmail-1.03/MakeArgs.c
--- qmail-1.03.org/MakeArgs.c 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/MakeArgs.c 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,144 @@
+/*
+ * $Log: MakeArgs.c,v $
+ * Revision 2.8 2007-12-21 14:35:42+05:30 Cprogrammer
+ * included env.h to prevent compiler warning
+ *
+ * Revision 2.7 2007-12-20 12:45:28+05:30 Cprogrammer
+ * expand environment variables with '$' sign
+ *
+ * Revision 2.6 2005-08-23 17:31:28+05:30 Cprogrammer
+ * removed sccsid variable
+ *
+ * Revision 2.5 2005-04-02 19:06:02+05:30 Cprogrammer
+ * djb version
+ *
+ * Revision 2.4 2005-03-30 22:52:47+05:30 Cprogrammer
+ * BUG - Incorrect free
+ *
+ * Revision 2.3 2004-07-12 22:47:58+05:30 Cprogrammer
+ * bug fix. Free all allocated members
+ *
+ * Revision 2.2 2002-12-21 18:21:09+05:30 Cprogrammer
+ * added functionality of escaping text via quotes
+ *
+ * Revision 2.1 2002-08-13 20:35:44+05:30 Cprogrammer
+ * addition spaces were not getting skipped
+ *
+ * Revision 1.2 2002-03-03 17:23:05+05:30 Cprogrammer
+ * replaced strcpy with scopy
+ *
+ * Revision 1.1 2001-12-13 01:46:09+05:30 Cprogrammer
+ * Initial revision
+ *
+ */
+#include "alloc.h"
+#include "str.h"
+#include "stralloc.h"
+#include "env.h"
+#include <ctype.h>
+
+#define isEscape(ch) ((ch) == '"' || (ch) == '\'')
+
+/*
+ * function to expand a string into command line
+ * arguments. To free memory allocated by this
+ * function the following should be done
+ *
+ * free(argv);
+ *
+ */
+char **
+MakeArgs(char *cmmd)
+{
+ char *ptr, *marker;
+ char **argv;
+ int argc, idx;
+ static stralloc sptr = { 0 };
+
+ for (ptr = cmmd;*ptr && isspace((int) *ptr);ptr++);
+ idx = str_len(ptr);
+ if (!stralloc_copys(&sptr, ptr))
+ return((char **) 0);
+ if (!stralloc_0(&sptr))
+ return((char **) 0);
+ /*-
+ * Get the number of arguments by counting
+ * white spaces. Allow escape via the double
+ * quotes character at the first word
+ */
+ for (argc = 0, ptr = sptr.s;*ptr;)
+ {
+ for (;*ptr && isspace((int) *ptr);ptr++);
+ if (!*ptr)
+ break;
+ argc++;
+ marker = ptr;
+ /*- Move till you hit the next white space */
+ for (;*ptr && !isspace((int) *ptr);ptr++)
+ {
+ /*-
+ * 1. If escape char is encounted skip till you
+ * hit the terminating escape char
+ * 2. If terminating escape char is missing, come
+ * back to the start escape char
+ */
+ if (ptr == marker && isEscape(*ptr))
+ {
+ for (ptr++;*ptr && !isEscape(*ptr);ptr++);
+ if (!*ptr)
+ ptr = marker;
+ }
+ } /*- for(;*ptr && !isspace((int) *ptr);ptr++) */
+ } /*- for (argc = 0, ptr = sptr.s;*ptr;) */
+ /*
+ * Allocate memory to store the arguments
+ * Do not bother extra bytes occupied by
+ * white space characters.
+ */
+ if (!(argv = (char **) alloc((argc + 1) * sizeof(char *))))
+ return ((char **) 0);
+ for (idx = 0, ptr = sptr.s;*ptr;)
+ {
+ for (;*ptr && isspace((int) *ptr);ptr++)
+ *ptr = 0;
+ if (!*ptr)
+ break;
+ if (*ptr == '$')
+ argv[idx++] = env_get(ptr + 1);
+ else
+ argv[idx++] = ptr;
+ marker = ptr;
+ for (;*ptr && !isspace((int) *ptr);ptr++)
+ {
+ if (ptr == marker && isEscape(*ptr))
+ {
+ for (ptr++;*ptr && !isEscape(*ptr);ptr++);
+ if (!*ptr)
+ ptr = marker;
+ else /*- Remove the quotes */
+ {
+ argv[idx - 1] += 1;
+ *ptr = 0;
+ }
+ }
+ }
+ } /*- for (idx = 0, ptr = sptr.s;*ptr;) */
+ argv[idx++] = (char *) 0;
+ return (argv);
+}
+
+void
+FreeMakeArgs(char **argv)
+{
+ alloc_free(argv);
+ return;
+}
+
+void
+getversion_MakeArgs__c()
+{
+ static char *x = "$Id: MakeArgs.c,v 2.8 2007-12-21 14:35:42+05:30 Cprogrammer Stab mbhangui $";
+ x++;
+ x--;
+ return;
+}
--- qmail-1.03.orig/Makefile 2009-10-15 20:38:22.838352000 +0300
+++ qmail-1.03/Makefile 2009-10-16 00:05:47.238647935 +0300
@@ -1045,6 +1045,7 @@
dnsptr dnsip dnsmxip dnsfq hostname ipmeprint qreceipt qsmhook qbiff \
forward preline condredirect bouncesaying except maildirmake \
maildir2mbox maildirwatch qail elq pinq idedit install-big \
+qmail-dk qmail-dkim dkimtest spawn-filter dk-filter \
install instcheck home home+df proc proc+df binm1 \
binm1+df binm2 binm2+df binm3 binm3+df
@@ -1204,7 +1205,7 @@
maildir2mbox.0 maildirwatch.0 qmail.0 qmail-limits.0 qmail-log.0 \
qmail-control.0 qmail-header.0 qmail-users.0 dot-qmail.0 \
qmail-command.0 tcp-environ.0 maildir.0 mbox.0 addresses.0 \
-envelopes.0 forgeries.0
+envelopes.0 forgeries.0 qmail-dk.0 qmail-dkim.0 dk-filter.0 spawn-filter.0
mbox.0: \
mbox.5
@@ -1475,6 +1476,48 @@
| sed s}BREAK}"`head -1 conf-break`"}g \
| sed s}SPAWN}"`head -1 conf-spawn`"}g \
> qmail-control.5
+qmail-dk: \
+load qmail-dk.o triggerpull.o fmtqfn.o now.o date822fmt.o \
+subgetopt.o MakeArgs.o datetime.a seek.a ndelay.a open.a sig.a alloc.a substdio.a error.a \
+str.a case.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \
+/usr/lib/libdomainkeys.a env.a getln.a control.o stralloc.a
+ ./load qmail-dk triggerpull.o fmtqfn.o now.o \
+ date822fmt.o datetime.a seek.a ndelay.a open.a sig.a \
+ subgetopt.o MakeArgs.o substdio.a error.a fs.a auto_qmail.o \
+ auto_split.o auto_uids.o \
+ fd.a wait.a /usr/lib/libdomainkeys.a -lcrypto -lresolv env.a control.o open.a getln.a \
+ stralloc.a alloc.a substdio.a str.a case.a
+
+qmail-dk.0: \
+qmail-dk.8
+ nroff -man qmail-dk.8 > qmail-dk.0
+
+qmail-dk.o: \
+compile qmail-dk.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
+qmail.h alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
+env.h wait.h fd.h fork.h str.h \
+auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
+ ./compile qmail-dk.c
+
+qmail-dkim: \
+load qmail-dkim.o triggerpull.o fmtqfn.o now.o date822fmt.o \
+subgetopt.o MakeArgs.o dkimdns.o datetime.a seek.a ndelay.a \
+open.a sig.a alloc.a substdio.a error.a \
+str.a case.a fs.a auto_qmail.o auto_split.o auto_uids.o fd.a wait.a \
+/usr/lib/libdomainkeys.a env.a getln.a control.o stralloc.a libdkim.a
+ g++ -o qmail-dkim qmail-dkim.o triggerpull.o fmtqfn.o now.o \
+ subgetopt.o MakeArgs.o date822fmt.o dkimdns.o datetime.a seek.a ndelay.a \
+ open.a sig.a substdio.a error.a fs.a auto_qmail.o \
+ auto_split.o auto_uids.o fd.a wait.a \
+ /usr/lib/libdomainkeys.a -lcrypto -lresolv env.a control.o open.a getln.a \
+ stralloc.a alloc.a substdio.a scan_ulong.o str.a case.a libdkim.a
+
+qmail-dkim.o: \
+compile qmail-dkim.c readwrite.h sig.h exit.h open.h seek.h fmt.h \
+qmail.h alloc.h substdio.h datetime.h now.h datetime.h triggerpull.h extra.h \
+sgetopt.h env.h wait.h fd.h fork.h str.h dkim.h \
+auto_qmail.h auto_uids.h date822fmt.h fmtqfn.h
+ ./compile qmail-dkim.c
qmail-forward: \
load qmail-forward.o qmail.o control.o now.o env.a fd.a wait.a open.a getln.a \
@@ -1506,6 +1549,20 @@
| sed s}SPAWN}"`head -1 conf-spawn`"}g \
> qmail-getpw.8
+qmail-dkim.0: qmail-dkim.8
+ nroff -man qmail-dkim.8 > qmail-dkim.0
+qmail-dkim.8: qmail-dkim.9
+ cat qmail-dkim.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ > qmail-dkim.8
+
+dk-filter.0: dk-filter.8
+ nroff -man dk-filter.8 > dk-filter.0
+dk-filter.8: dk-filter.9
+ cat dk-filter.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ > dk-filter.8
+
qmail-getpw.o: \
compile qmail-getpw.c readwrite.h substdio.h subfd.h substdio.h \
error.h exit.h byte.h str.h case.h fmt.h auto_usera.h auto_break.h \
@@ -1971,11 +2028,11 @@
qmail-rspawn: \
load qmail-rspawn.o spawn.o tcpto_clean.o now.o coe.o sig.a open.a \
seek.a lock.a wait.a fd.a stralloc.a alloc.a substdio.a error.a str.a \
-auto_qmail.o auto_uids.o auto_spawn.o
+auto_qmail.o auto_uids.o auto_spawn.o envread.o str_diffn.o
./load qmail-rspawn spawn.o tcpto_clean.o now.o coe.o \
sig.a open.a seek.a lock.a wait.a fd.a stralloc.a alloc.a \
substdio.a error.a str.a auto_qmail.o auto_uids.o \
- auto_spawn.o
+ auto_spawn.o envread.o str_diffn.o
qmail-rspawn.0: \
qmail-rspawn.8
@@ -2501,11 +2558,11 @@
./compile splogger.c
str.a: \
-makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_chr.o \
+makelib str_len.o str_diff.o str_diffn.o str_cpy.o str_cpyb.o str_chr.o \
str_rchr.o str_start.o byte_chr.o byte_rchr.o byte_diff.o byte_copy.o \
byte_cr.o byte_zero.o byte_repl.o
./makelib str.a str_len.o str_diff.o str_diffn.o str_cpy.o \
- str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \
+ str_cpyb.o str_chr.o str_rchr.o str_start.o byte_chr.o byte_rchr.o \
byte_diff.o byte_copy.o byte_cr.o byte_zero.o byte_repl.o
str_chr.o: \
@@ -2516,6 +2573,10 @@
compile str_cpy.c str.h
./compile str_cpy.c
+str_cpyb.o: \
+compile str_cpyb.c str.h
+ ./compile str_cpyb.c
+
str_diff.o: \
compile str_diff.c str.h
./compile str_diff.c
@@ -2729,6 +2790,62 @@
compile wait_pid.c error.h haswaitp.h
./compile wait_pid.c
+MakeArgs.o: compile MakeArgs.c alloc.h str.h alloc.h stralloc.h
+ ./compile MakeArgs.c
+
+spawn-filter: \
+load spawn-filter.o \
+fmt_ulong.o scan_ulong.o control.o open_read.o wildmat.o qregex.o MakeArgs.o \
+case_lowerb.o constmap.o byte_chr.o byte_cr.o case_diffb.o \
+error.a env.a stralloc.a wait.a strerr.a str.a getln.a substdio.a alloc.a
+ ./load spawn-filter \
+ fmt_ulong.o scan_ulong.o control.o open_read.o wildmat.o qregex.o MakeArgs.o \
+ case_lowerb.o constmap.o byte_chr.o byte_cr.o case_diffb.o \
+ error.a env.a stralloc.a wait.a strerr.a str.a getln.a substdio.a alloc.a
+
+spawn-filter.o: \
+compile spawn-filter.c fmt.h str.h strerr.h env.h substdio.h stralloc.h error.h \
+wait.h qregex.h
+ ./compile spawn-filter.c
+
+qregex.o: \
+compile qregex.c case.h stralloc.h constmap.h substdio.h byte.h env.h
+ ./compile qregex.c
+
+wildmat.o: \
+compile wildmat.c
+ ./compile wildmat.c
+
+spawn-filter.0: \
+spawn-filter.8
+ nroff -man spawn-filter.8 > spawn-filter.0
+
+spawn-filter.8: \
+spawn-filter.9
+ cat spawn-filter.9 \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ > spawn-filter.8
+
+dk-filter: \
+warn-auto.sh dk-filter.sh conf-qmail
+ cat warn-auto.sh dk-filter.sh \
+ | sed s}QMAILHOME}"`head -1 conf-qmail`"}g \
+ > dk-filter
+
+DKIMHDRS = dkim.h dkimdns.h dkimbase.h dkimsign.h dkimverify.h
+DKIMSRCS = dkimfuncs.cpp dkimbase.cpp dkimsign.cpp dkimverify.cpp
+DKIMOBJS = $(DKIMSRCS:.cpp=.o)
+dkimtest : libdkim.a dkimtest.o dkimdns.o
+ g++ -g -o dkimtest $(LFLAGS) -L. dkimtest.o dkimdns.o libdkim.a -lcrypto -lresolv
+
+dkimtest.o: dkimtest.c $(DKIMHDRS)
+ g++ -I. -c dkimtest.c
+libdkim.a: $(DKIMOBJS) makelib
+ rm -f libdkim.a
+ ./makelib libdkim.a $(DKIMOBJS)
+.cpp.o:
+ g++ -I. -g $(CFLAGS) $(INCL) -c $<
+
xtext.o: \
compile xtext.c xtext.h stralloc.h
./compile xtext.c
--- qmail-1.03.orig/qmail.c 2009-10-15 20:38:23.998352000 +0300
+++ qmail-1.03/qmail.c 2009-10-16 00:18:36.984355926 +0300
@@ -32,6 +32,7 @@
{
int pim[2];
int pie[2];
+ int pic[2];
#ifdef ALTQUEUE
setup_qqargs();
@@ -39,17 +40,21 @@
if (pipe(pim) == -1) return -1;
if (pipe(pie) == -1) { close(pim[0]); close(pim[1]); return -1; }
-
+ if (pipe(pic) == -1) { close(pim[0]); close(pim[1]); close(pie[0]); close(pie[1]); return -1; }
+
switch(qq->pid = vfork()) {
case -1:
close(pim[0]); close(pim[1]);
close(pie[0]); close(pie[1]);
+ close(pic[0]); close(pic[1]);
return -1;
case 0:
close(pim[1]);
close(pie[1]);
+ close(pic[0]); /*- we want to receive data */
if (fd_move(0,pim[0]) == -1) _exit(120);
if (fd_move(1,pie[0]) == -1) _exit(120);
+ if (fd_move(CUSTOM_ERR_FD,pic[1]) == -1) _exit(120);
if (chdir(auto_qmail) == -1) _exit(61);
execv(*binqqargs,binqqargs);
_exit(120);
@@ -57,6 +62,7 @@
qq->fdm = pim[1]; close(pim[0]);
qq->fde = pie[1]; close(pie[0]);
+ qq->fdc = pic[0]; close(pic[1]);
substdio_fdbuf(&qq->ss,subwrite,qq->fdm,qq->buf,sizeof(qq->buf));
qq->flagerr = 0;
return 0;
@@ -141,10 +147,19 @@
{
int wstat;
int exitcode;
+ int len = 0;
+ char ch;
+ static char errstr[256];
qmail_put(qq,"",1);
if (!qq->flagerr) if (substdio_flush(&qq->ss) == -1) qq->flagerr = 1;
close(qq->fde);
+ substdio_fdbuf(&qq->ss, read, qq->fdc, qq->buf, sizeof(qq->buf));
+ while (substdio_bget(&qq->ss, &ch, 1) && len < 255)
+ errstr[len] = ch; len++;
+ if (len > 0)
+ errstr[len] = 0; /* add str-term */
+ close(qq->fdc);
if ((unsigned long)wait_pid(&wstat,qq->pid) != qq->pid)
return "Zqq waitpid surprise (#4.3.0)";
@@ -156,6 +171,7 @@
case 115: /* compatibility */
case 11: return "Denvelope address too long for qq (#5.1.3)";
case 31: return "Dmail server permanently rejected message (#5.3.0)";
+ case 32: return "DPrivate key file does not exist (#5.3.5)";
case 51: return "Zqq out of memory (#4.3.0)";
case 52: return "Zqq timeout (#4.3.0)";
case 53: return "Zqq write error or disk full (#4.3.0)";
@@ -175,6 +191,9 @@
case 74: return "Zcommunication with mail server failed (#4.4.2)";
case 91: /* fall through */
case 81: return "Zqq internal bug (#4.3.0)";
+ case 88: /*- custom error */
+ if (len > 2)
+ return errstr;
case 120: return "Zunable to exec qq (#4.3.0)";
default:
if ((exitcode >= 11) && (exitcode <= 40))
diff -Naur qmail-1.03.org/qmail-dk.8 qmail-1.03/qmail-dk.8
--- qmail-1.03.org/qmail-dk.8 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/qmail-dk.8 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,141 @@
+.TH qmail-dk 8
+.SH NAME
+qmail-dk \- sign/verify and queue a mail message for delivery
+.SH SYNOPSIS
+.B qmail-dk
+.SH DESCRIPTION
+.B qmail-dk
+has the same interface as
+.B qmail-queue
+except that it inserts an appropriate DomainKeys header before it
+queues the message. To invoke
+.BR qmail-dk ,
+set QMAILQUEUE to point to qmail-dk in the environment when
+you send or receive email. qmail-dk will call
+.BR qmail-queue .
+To invoke an executable other than
+.B qmail-queue
+set DKQUEUE=bin/qmail-queue for example.
+
+.B qmail-dk
+supports DomainKey signing and verification. It uses the libdomainkey
+and OpenSSL libraries. To sign a message, set the
+.B DKSIGN
+environment variable to the pathname to the private key that will be
+used to sign the message. If there is a % character in the environment
+variable, it is removed and replaced by the domain name in the From: header.
+If, after substituting the %, that file does not exist, the message will not be signed.
+If there is no % and the file does not exist, the message will be rejected with error 32.
+The selector will be taken from the
+basename of the file. The private key should be created by
+.BR dknewkey ,
+which comes with libdomainkey.
+
+To verify a message, set the
+.B DKVERIFY
+environment variable to a desired set of letters. Precisely, if you
+want a libdomainkey return status to generate an error, include that
+letter, where A is the first return status (DK_STAT_OK), B is the
+second (DK_STAT_BADSIG), etc. The letter should be uppercase if you
+want a permanent error to be returned (exit code 13), and lowercase if
+you want a temporary error to be returned (exit code 84).
+The complete set of letters with the corresponding return status is given below
+
+ A - DK_STAT_OK - Function completed successfully
+ B - DK_STAT_BADSIG - Signature was available but failed to verify against
+ domain specified key
+ C - DK_STAT_NOSIG - No signature available in message
+ D - DK_STAT_NOKEY - No public key available (permanent failure)
+ E - DK_STAT_BADKEY - Unusable key, public if verifying, private if signing
+ F - DK_STAT_CANTVRFY - Cannot get domain key to verify signature
+ (temporary failure)
+ G - DK_STAT_SYNTAX - Message is not valid syntax. Signature could not be
+ created/checked
+ H - DK_STAT_NORESOURCE - Could not get critical resource (temporary failure)
+ I - DK_STAT_ARGS - Arguments are not usable.
+ J - DK_STAT_REVOKED - Key has been revoked.
+ K - DK_STAT_INTERNAL - cannot call this routine in this context. Internal error.
+ L - DK_STAT_GRANULARITY - Granularity mismatch: sender doesn't match g= option.
+
+For example, if you want to permanently reject messages that have a
+signature that has been revoked, include the letter 'K' in the
+.B DKVERIFY
+environment variable. A conservative set of letters is
+.BR DEGIJKfh .
+Reject permanently BADSIG, NOKEY, BADKEY, SYNTAX, ARGS, REVOKED, and
+INTERNAL errors, and temporarily CANTVRFY and NORESOURCE. Add in
+.B B
+if you want to reject messages that have a signature that doesn't
+verify (presumably because the message is a forgery or has been
+damaged in transit. Note that
+.B qmail-dk
+always inserts the
+.B DomainKey-Status
+header, so that messages can be
+rejected at delivery time, or in the mail reader.
+
+Typically, you would sign messages generated on-host by setting
+.B DKSIGN
+in the environment before running an email program. DKSIGN will be carried
+through qmail's sendmail emulation through
+.B qmail-inject
+to
+.BR qmail-dk .
+You would also set it for
+.B qmail-smtpd
+at the same time
+.B RELAYCLIENT
+is set, most often in the tcpserver cdb file. If a host is authorized
+to relay, you probably want to sign messages sent by that host.
+.B DKVERIFY
+should be set for all other hosts.
+
+If neither
+.B DKSIGN
+nor
+.B DKVERIFY
+are set, then
+.B DKSIGN
+will be set to /var/qmail/control/domainkeys/%/default. If such a private key exists, it will be used to sign the domain.
+
+By default
+.B qmail-dk
+will use all of the headers when signing a message.
+The environment variable
+.B DKEXCLUDEHEADERS
+may be set to a colon-separated list of headers that are to be excluded from signing.
+
+.B qmail-dk
+will ordinarily spawn
+.BR qmail-queue ,
+but if DKQUEUE is set in the environment,
+the program that it points to will be executed instead.
+
+.SH "EXIT CODES"
+.B qmail-dk
+returns the same exit codes as qmail-queue with these additions:
+.TP 5
+.B 35
+The private key file does not exist.
+.TP 5
+.B 57
+Trouble waiting for qmail-queue to exit.
+.TP 5
+.B 58
+Unable to vfork.
+.TP 5
+.B 59
+Unable to create a pipe to qmail-queue.
+.SH "SEE ALSO"
+addresses(5),
+envelopes(5),
+qmail-header(5),
+dknewkey(8),
+dktest(8),
+qmail-inject(8),
+qmail-qmqpc(8),
+qmail-queue(8),
+qmail-send(8),
+qmail-smtpd(8),
+domain-keys(5)
+
diff -Naur qmail-1.03.org/qmail-dk.c qmail-1.03/qmail-dk.c
--- qmail-1.03.org/qmail-dk.c 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/qmail-dk.c 2009-04-21 19:58:19.000000000 +0530
@@ -0,0 +1,772 @@
+/*
+ * $Log: qmail-dk.c,v $
+ * Revision 1.26 2009-04-21 09:05:14+05:30 Cprogrammer
+ * return relevant error message for reading private key
+ *
+ * Revision 1.25 2009-04-21 08:56:03+05:30 Cprogrammer
+ * return temp errors for temporary failures
+ *
+ * Revision 1.24 2009-04-21 08:15:39+05:30 Cprogrammer
+ * moved stralloc variable outside block
+ *
+ * Revision 1.23 2009-04-20 22:20:40+05:30 Cprogrammer
+ * added DKSIGNOPTIONS
+ *
+ * Revision 1.22 2009-04-07 11:38:02+05:30 Cprogrammer
+ * use TMPDIR env variable for tmp directory
+ *
+ * Revision 1.21 2009-04-05 12:52:10+05:30 Cprogrammer
+ * added preprocessor warning
+ *
+ * Revision 1.20 2009-03-31 08:21:12+05:30 Cprogrammer
+ * set dksign when RELAYCLIENT is defined when both dksign, dkverify are undefined
+ *
+ * Revision 1.19 2009-03-28 22:26:35+05:30 Cprogrammer
+ * use DKSIGN,DKVERIFY env variables if RELAYCLIENT is not set
+ *
+ * Revision 1.18 2009-03-28 22:02:32+05:30 Cprogrammer
+ * removed extra white space
+ *
+ * Revision 1.17 2009-03-28 11:34:51+05:30 Cprogrammer
+ * BUG fix. corrected setting of dksign, dkverify variables
+ *
+ * Revision 1.16 2009-03-22 16:58:13+05:30 Cprogrammer
+ * report custom errors to qmail-queue through custom error interface
+ *
+ * Revision 1.15 2009-03-21 15:15:56+05:30 Cprogrammer
+ * improved logic
+ *
+ * Revision 1.14 2009-03-20 22:35:24+05:30 Cprogrammer
+ * fix for multi-line headers
+ *
+ * Revision 1.13 2009-03-19 08:28:12+05:30 Cprogrammer
+ * added EXCLUDEHEADERS
+ *
+ * Revision 1.12 2009-03-14 17:11:32+05:30 Cprogrammer
+ * added DK_STAT_GRANULARITY
+ *
+ * Revision 1.11 2009-03-14 08:52:54+05:30 Cprogrammer
+ * look for domainkey in control/domainkeys
+ *
+ * Revision 1.10 2005-08-23 17:33:37+05:30 Cprogrammer
+ * gcc 4 compliance
+ *
+ * Revision 1.9 2005-04-01 21:42:04+05:30 Cprogrammer
+ * added DK_STAT_SYNTAX
+ * changed error codes
+ *
+ * Revision 1.8 2004-11-02 09:15:53+05:30 Cprogrammer
+ * commented out writing of Comments: header
+ *
+ * Revision 1.7 2004-10-24 21:32:00+05:30 Cprogrammer
+ * removed unecessary header files
+ *
+ * Revision 1.6 2004-10-22 20:28:18+05:30 Cprogrammer
+ * added RCS id
+ *
+ * Revision 1.5 2004-10-22 15:36:45+05:30 Cprogrammer
+ * removed readwrite.h
+ *
+ * Revision 1.4 2004-10-22 14:44:26+05:30 Cprogrammer
+ * use control_readnativefile to avoid skipping signure with '#' char
+ *
+ * Revision 1.3 2004-10-20 20:08:53+05:30 Cprogrammer
+ * libdomainkeys-0.62
+ *
+ * Revision 1.2 2004-09-23 22:55:32+05:30 Cprogrammer
+ * removed uneccessary header files
+ *
+ * Revision 1.1 2004-08-28 01:02:16+05:30 Cprogrammer
+ * Initial revision
+ *
+ */
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sgetopt.h"
+#include "substdio.h"
+#include "open.h"
+#include "qmail.h"
+#include "sig.h"
+#include "fmt.h"
+#include "fd.h"
+#include "alloc.h"
+#include "str.h"
+#include "getln.h"
+#include "case.h"
+#include "stralloc.h"
+#include "datetime.h"
+#include "now.h"
+#include "wait.h"
+#include "auto_qmail.h"
+#include "env.h"
+#include "scan.h"
+#include "control.h"
+#include "domainkeys.h"
+
+#define DEATH 86400 /*- 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
+#define ADDR 1003
+#define ADVICE_BUF 2048
+
+char inbuf[2048];
+struct substdio ssin;
+char outbuf[256];
+struct substdio ssout;
+struct substdio sserr;
+char errbuf[256];
+
+datetime_sec starttime;
+struct datetime dt;
+unsigned long mypid;
+unsigned long uid;
+char *pidfn;
+int messfd;
+int readfd;
+
+char **MakeArgs(char *);
+void FreeMakeArgs(char **);
+
+void
+die(e)
+ int e;
+{
+ _exit(e);
+}
+
+void
+die_write()
+{
+ die(53);
+}
+
+void
+die_read()
+{
+ die(54);
+}
+
+void
+sigalrm()
+{
+ /*- thou shalt not clean up here */
+ die(52);
+}
+
+void
+sigbug()
+{
+ die(81);
+}
+
+void
+custom_error(char *flag, char *status, char *code)
+{
+ char *c;
+
+ if (substdio_put(&sserr, flag, 1) == -1)
+ die_write();
+ if (substdio_put(&sserr, "qmail-dk: ", 10) == -1)
+ die_write();
+ if (substdio_puts(&sserr, status) == -1)
+ die_write();
+ if (code)
+ {
+ if (substdio_put(&sserr, " (#", 3) == -1)
+ die_write();
+ c = (*flag == 'Z') ? "4" : "5";
+ if (substdio_put(&sserr, c, 1) == -1)
+ die_write();
+ if (substdio_put(&sserr, code + 1, 4) == -1)
+ die_write();
+ if (substdio_put(&sserr, ")", 1) == -1)
+ die_write();
+ }
+ if (substdio_flush(&sserr) == -1)
+ die_write();
+ return;
+}
+
+void
+maybe_die_dk(e)
+ DK_STAT e;
+{
+ switch (e)
+ {
+ case DK_STAT_NORESOURCE:
+ _exit(51);
+ case DK_STAT_INTERNAL:
+ _exit(81);
+ case DK_STAT_ARGS:
+ custom_error("Z", "Arguments are not usable. (#4.3.5)", 0);
+ _exit(88);
+ case DK_STAT_SYNTAX:
+ custom_error("Z", "Message is not valid syntax. Signature could not be created/checked (#4.6.0)", 0);
+ _exit(88);
+ case DK_STAT_CANTVRFY:
+ custom_error("Z", "Cannot get domainkeys to verify signature (#5.7.5)", 0);
+ _exit(88);
+ case DK_STAT_BADKEY:
+ if (env_get("DKVERIFY"))
+ custom_error("Z", "Unusable public key for verifying (#5.7.5)", 0);
+ else
+ custom_error("Z", "Unusable private key for signing (#5.7.15", 0);
+ _exit(88);
+ default:
+ return;
+ }
+}
+
+unsigned int
+pidfmt(s, seq)
+ char *s;
+ unsigned long seq;
+{
+ unsigned int i;
+ unsigned int len;
+ char *tmpdir;
+
+ if (!(tmpdir = env_get("TMPDIR")))
+ tmpdir = "/tmp";
+ len = 0;
+ i = fmt_str(s, tmpdir);
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_str(s, "/qmail-dk.");
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_ulong(s, mypid);
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_str(s, ".");
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_ulong(s, starttime);
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_str(s, ".");
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_ulong(s, seq);
+ len += i;
+ if (s)
+ s += i;
+ ++len;
+ if (s)
+ *s++ = 0;
+
+ return len;
+}
+
+void
+pidopen()
+{
+ unsigned int len;
+ unsigned long seq;
+
+ seq = 1;
+ len = pidfmt((char *) 0, seq);
+ if (!(pidfn = alloc(len)))
+ die(51);
+ for (seq = 1; seq < 10; ++seq)
+ {
+ if (pidfmt((char *) 0, seq) > len)
+ die(81); /*- paranoia */
+ pidfmt(pidfn, seq);
+ if ((messfd = open_excl(pidfn)) != -1)
+ return;
+ }
+ die(63);
+}
+
+char tmp[FMT_ULONG];
+DK_LIB *dklib;
+DK *dk;
+DK_STAT st;
+char *dksign = 0;
+char *dkverify = 0;
+stralloc dkoutput = { 0 }; /*- Domainkey-Signature */
+stralloc dksignature = { 0 }; /*- content of private signature */
+stralloc dkopts = { 0 };
+char *dkqueue = 0;
+char *dkexcludeheaders;
+
+static void
+write_signature(DK *dk, char *dk_selector, char *keyfn,
+ int advicelen, int opth, char *canon)
+{
+ unsigned char advice[ADVICE_BUF];
+ char *selector, *from, *tmp;
+ static stralloc keyfnfrom = { 0 };
+ int i;
+
+ from = dk_from(dk);
+ i = str_chr(keyfn, '%');
+ if (keyfn[i])
+ {
+ if (!stralloc_copyb(&keyfnfrom, keyfn, i))
+ die(51);
+ if (!stralloc_cats(&keyfnfrom, from))
+ die(51);
+ if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
+ die(51);
+ } else
+ if (!stralloc_copys(&keyfnfrom, keyfn))
+ die(51);
+ if (!stralloc_0(&keyfnfrom))
+ die(51);
+ switch (control_readfile(&dksignature, keyfnfrom.s, 0))
+ {
+ case 0: /*- file not present */
+ if (keyfn[i])
+ return;
+ die(32);
+ case 1:
+ break;
+ default:
+ custom_error("Z", "Unable to read private key. (#4.3.0)", 0);
+ _exit(88);
+ }
+ for (i = 0; i < dksignature.len; i++)
+ {
+ if (dksignature.s[i] == '\0')
+ dksignature.s[i] = '\n';
+ }
+ if (!stralloc_0(&dksignature))
+ die(51);
+ st = dk_getsig(dk, dksignature.s, advice, advicelen);
+ maybe_die_dk(st);
+ if (!dk_selector)
+ {
+ selector = keyfn;
+ while (*keyfn)
+ {
+ if (*keyfn == '/')
+ selector = keyfn + 1;
+ keyfn++;
+ }
+ } else
+ selector = dk_selector;
+ if (!stralloc_cats(&dkoutput,
+#if 0
+ "Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys\n"
+#endif
+ "DomainKey-Signature: a=rsa-sha1; q=dns; c="))
+ die(51);
+ if (!stralloc_cats(&dkoutput, canon))
+ die(51);
+ if (!stralloc_cats(&dkoutput, ";\n"))
+ die(51);
+ if (!stralloc_cats(&dkoutput, " s="))
+ die(51);
+ if (!stralloc_cats(&dkoutput, selector))
+ die(51);
+ if (!stralloc_cats(&dkoutput, "; d="))
+ die(51);
+ if (from)
+ {
+ if (!stralloc_cats(&dkoutput, from))
+ die(51);
+ } else
+ if (!stralloc_cats(&dkoutput, "unknown"))
+ die(51);
+ if (!stralloc_cats(&dkoutput, ";\n"))
+ die(51);
+ if (!stralloc_cats(&dkoutput, " b="))
+ die(51);
+ if (!stralloc_cats(&dkoutput, (char *) advice))
+ die(51);
+ if (dkexcludeheaders || opth)
+ {
+ if ((i = dk_headers(dk, NULL)) > 0)
+ {
+ if (!(tmp = alloc(i)))
+ die(51);
+ if (!dk_headers(dk, tmp))
+ die(51);
+ if (!stralloc_cats(&dkoutput, ";\n h="))
+ die(51);
+ if (!stralloc_cats(&dkoutput, tmp))
+ die(51);
+ alloc_free(tmp);
+ }
+ }
+ if (!stralloc_cats(&dkoutput, ";\n"))
+ die(51);
+}
+
+int
+find_header(stralloc *line)
+{
+ static stralloc headers = { 0 };
+ int n, i, j;
+
+ for (n = 0; n < line->len; ++n)
+ {
+ if (line->s[n] == ':')
+ break;
+ }
+ if (n == line->len)
+ return -1;
+ if (!headers.len)
+ {
+ if (!stralloc_copys(&headers, ""))
+ die(51);
+ if (dkexcludeheaders)
+ {
+ if (!stralloc_cats(&headers, dkexcludeheaders))
+ die(51);
+ if (!stralloc_append(&headers, ":"))
+ die(51);
+ }
+ }
+ if (!headers.len)
+ return 0;
+ for (i = j = 0; i < headers.len; ++i)
+ {
+ if (headers.s[i] != ':')
+ continue;
+ if (i - j == n && !case_diffb(headers.s + j, n, line->s))
+ return 1;
+ j = i + 1;
+ }
+ return 0;
+}
+
+int
+dk_setoptions(char **selector, int *advicelen, int *opth, int *optr, int *optc,
+ char **canon, char *signOptions)
+{
+ char **argv;
+ int ch, argc;
+
+ *opth = 0;
+ *optr = 0;
+ *optc = DK_CANON_NOFWS;
+ *canon = "nofws";
+ *selector = 0;
+ if (!signOptions)
+ return (0);
+ if (!stralloc_copys(&dkopts, "qmail-dk "))
+ die(51);
+ if (!stralloc_cats(&dkopts, signOptions))
+ die(51);
+ if (!stralloc_0(&dkopts))
+ die(51);
+ if (!(argv = MakeArgs(dkopts.s)))
+ die(51);
+ for (argc = 0;argv[argc];argc++);
+ while ((ch = sgopt(argc, argv, "hrb:c:s:")) != sgoptdone)
+ {
+ switch (ch)
+ {
+ case 'h':
+ *opth = 1;
+ break;
+ case 'r':
+ *optr = 1;
+ *opth = 1;
+ break;
+ case 'b':
+ *advicelen = atoi(optarg);
+ if (*advicelen > ADVICE_BUF);
+ *advicelen = ADVICE_BUF;
+ break;
+ case 'c':
+ if (!str_diffn("simple\0", optarg, 7))
+ {
+ *optc = DK_CANON_SIMPLE;
+ *canon = "simple";
+ }
+ break;
+ case 's':
+ *selector = optarg;
+ break;
+ default:
+ FreeMakeArgs(argv);
+ return (1);
+ }
+ } /*- while ((ch = sgopt(argc, argv, "hrb:c:s:")) != sgoptdone) */
+ FreeMakeArgs(argv);
+ return (0);
+}
+
+static char *binqqargs[2] = { "bin/qmail-queue", 0 };
+int
+main(int argc, char *argv[])
+{
+ int errfd, pim[2];
+ int wstat, match, opth = 0, optr = 0, optc = DK_CANON_NOFWS,
+ advicelen = ADVICE_BUF;
+ char *x, *relayclient, *canon = "nofws", *selector = 0;
+ stralloc line = {0};
+ unsigned long pid;
+
+ sig_blocknone();
+ umask(033);
+ if (chdir(auto_qmail) == -1)
+ die(61);
+ if (!dksign)
+ dksign = env_get("DKSIGN");
+ if (!dkverify)
+ dkverify = env_get("DKVERIFY");
+ if (!dksign && !dkverify && (relayclient = env_get("RELAYCLIENT")))
+ dksign = "control/domainkeys/%/default";
+ dkqueue = env_get("DKQUEUE");
+ if (dkqueue && *dkqueue)
+ binqqargs[0] = dkqueue;
+ if (dksign || dkverify)
+ {
+ if (!(dklib = dk_init(&st)))
+ {
+ maybe_die_dk(st);
+ custom_error("Z", "dk initialization failed (#4.3.0)", 0);
+ _exit(88);
+ }
+ }
+ /*- Initialization */
+ if (dksign)
+ {
+ if (dk_setoptions(&selector, &advicelen, &opth, &optr, &optc, &canon, env_get("DKSIGNOPTIONS")))
+ {
+ custom_error("Z", "Invalid DKSIGNOPTIONS (#4.3.0)", 0);
+ _exit(88);
+ }
+ if (!(dk = dk_sign(dklib, &st, optc)))
+ {
+ maybe_die_dk(st);
+ custom_error("Z", "dk_sign failed (#4.3.0)", 0);
+ _exit(88);
+ }
+ if (optr && dk_setopts(dk, DKOPT_RDUPE) != DK_STAT_OK)
+ {
+ custom_error("Z", "DKOPT_RDUPE failed (#4.3.0)", 0);
+ _exit(88);
+ }
+ } else
+ if (dkverify)
+ {
+ if (!(dk = dk_verify(dklib, &st)))
+ {
+ maybe_die_dk(st);
+ custom_error("Z", "dk_verify failed (#4.3.0)", 0);
+ _exit(88);
+ }
+ }
+ mypid = getpid();
+ uid = getuid();
+ starttime = now();
+ datetime_tai(&dt, starttime);
+ sig_pipeignore();
+ sig_miscignore();
+ sig_alarmcatch(sigalrm);
+ sig_bugcatch(sigbug);
+ alarm(DEATH);
+ pidopen();
+ if ((readfd = open_read(pidfn)) == -1)
+ die(63);
+ if (unlink(pidfn) == -1)
+ die(63);
+ substdio_fdbuf(&ssout, write, messfd, outbuf, sizeof(outbuf));
+ substdio_fdbuf(&ssin, read, 0, inbuf, sizeof(inbuf));
+ if (!(x = env_get("ERROR_FD")))
+ errfd = CUSTOM_ERR_FD;
+ else
+ scan_int(x, &errfd);
+ substdio_fdbuf(&sserr, write, errfd, errbuf, sizeof(errbuf));
+ dkexcludeheaders = env_get("DKEXCLUDEHEADERS");
+ if (dkexcludeheaders)
+ {
+ int hdr_continue, in_header = 1;
+
+ hdr_continue = 0;
+ for (;;)
+ {
+
+ if (getln(&ssin, &line, &match, '\n') == -1)
+ die_read();
+ if (!match && line.len == 0)
+ break;
+ if (substdio_put(&ssout, line.s, line.len) == -1)
+ die_write();
+ if (!dksign && !dkverify)
+ continue;
+ if (in_header)
+ {
+ if (line.s[0] == ' ' || line.s[0] == '\t')
+ {
+ if (hdr_continue)
+ continue;
+ } else
+ if (find_header(&line) == 1) {
+ hdr_continue = 1;
+ continue;
+ } else
+ hdr_continue = 0;
+ }
+ if (match)
+ {
+ st = dk_message(dk, (unsigned char *) line.s, line.len - 1);
+ maybe_die_dk(st);
+ st = dk_message(dk, (unsigned char *) "\r\n", 2);
+ } else
+ st = dk_message(dk, (unsigned char *) line.s, line.len);
+ maybe_die_dk(st);
+ }
+ } else
+ for (;;)
+ {
+ register int n;
+ register char *x;
+ int i;
+
+ if ((n = substdio_feed(&ssin)) < 0)
+ die_read();
+ if (!n)
+ break;
+ x = substdio_PEEK(&ssin);
+ if (dksign || dkverify)
+ {
+ for (i = 0; i < n; i++)
+ {
+ if (x[i] == '\n')
+ st = dk_message(dk, (unsigned char *) "\r\n", 2);
+ else
+ st = dk_message(dk, (unsigned char *) x + i, 1);
+ maybe_die_dk(st);
+ }
+ }
+ if (substdio_put(&ssout, x, n) == -1)
+ die_write();
+ substdio_SEEK(&ssin, n);
+ }
+ if (substdio_flush(&ssout) == -1)
+ die_write();
+ if (dksign || dkverify)
+ {
+ st = dk_eom(dk, (void *) 0);
+ maybe_die_dk(st);
+ if (dksign)
+ write_signature(dk, selector, dksign, advicelen, opth, canon);
+ else
+ if (dkverify)
+ {
+ char *status = 0, *code = 0;
+
+ if (!stralloc_copys(&dkoutput, "DomainKey-Status: "))
+ die(51);
+ switch (st)
+ {
+ case DK_STAT_OK:
+ status = "good ";
+ break;
+ case DK_STAT_BADSIG:
+ status = "bad ";
+ code = "X.7.5";
+ break;
+ case DK_STAT_NOSIG:
+ status = "no signature";
+ code = "X.7.5";
+ break;
+ case DK_STAT_NOKEY:
+ case DK_STAT_CANTVRFY:
+ status = "no key ";
+ code = "X.7.0";
+ break;
+ case DK_STAT_BADKEY:
+ status = "bad key ";
+ code = "X.7.5";
+ break;
+ case DK_STAT_INTERNAL:
+ status = "bad format ";
+ code = "X.3.0";
+ break;
+ case DK_STAT_ARGS:
+ status = "bad format ";
+ code = "X.3.5";
+ break;
+ case DK_STAT_SYNTAX:
+ status = "bad format ";
+ code = "X.6.0";
+ break;
+ case DK_STAT_NORESOURCE:
+ status = "no resources";
+ code = "X.3.0";
+ break;
+ case DK_STAT_REVOKED:
+ status = "revoked ";
+ code = "X.7.5";
+ break;
+ case DK_STAT_GRANULARITY:
+ status = "bad sender ";
+ code = "X.7.5";
+ break;
+ }
+ if (!stralloc_cats(&dkoutput, status))
+ die(51);
+ if (!stralloc_cats(&dkoutput, "\n"))
+ die(51);
+ if (dkverify[str_chr(dkverify, 'A' + st)])
+ {
+ custom_error("D", status, code); /*- return permanent error */
+ die(88);
+ }
+ if (dkverify[str_chr(dkverify, 'a' + st)])
+ {
+ custom_error("Z", status, code); /*- return temporary error */
+ die(88);
+ }
+ }
+ }
+ if (pipe(pim) == -1)
+ die(59);
+ switch (pid = vfork())
+ {
+ case -1:
+ close(pim[0]);
+ close(pim[1]);
+ die(58);
+ case 0:
+ close(pim[1]);
+ if (fd_move(0, pim[0]) == -1)
+ die(120);
+ execv(*binqqargs, binqqargs);
+ die(120);
+ }
+ close(pim[0]);
+ substdio_fdbuf(&ssin, read, readfd, inbuf, sizeof(inbuf));
+ substdio_fdbuf(&ssout, write, pim[1], outbuf, sizeof(outbuf));
+ if (substdio_bput(&ssout, dkoutput.s, dkoutput.len) == -1) /*- write DK signature */
+ die_write();
+ switch (substdio_copy(&ssout, &ssin))
+ {
+ case -2:
+ die_read();
+ case -3:
+ die_write();
+ }
+ if (substdio_flush(&ssout) == -1)
+ die_write();
+ close(pim[1]);
+ if (wait_pid(&wstat, pid) != pid)
+ die(57);
+ if (wait_crashed(wstat))
+ die(57);
+ die(wait_exitcode(wstat));
+ /*- Not Reached */
+ exit(0);
+}
+
+void
+getversion_qmail_dk_c()
+{
+ static char *x = "$Id: qmail-dk.c,v 1.26 2009-04-21 09:05:14+05:30 Cprogrammer Exp mbhangui $";
+
+ x++;
+}
diff -Naur qmail-1.03.org/qmail-dkim.9 qmail-1.03/qmail-dkim.9
--- qmail-1.03.org/qmail-dkim.9 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/qmail-dkim.9 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,303 @@
+.TH qmail-dk 8
+.SH NAME
+qmail-dkim \- sign/verify using DKIM (SSP/ADSP optionally) and queue a mail message for delivery
+.SH SYNOPSIS
+.B qmail-dkim
+.SH DESCRIPTION
+.B qmail-dkim
+has the same interface as
+.B qmail-queue
+except that it inserts an appropriate DKIM header (rfc4871) before it
+queues the message. To invoke
+.BR qmail-dkim ,
+set QMAILQUEUE to point to qmail-dkim in the environment when
+you send or receive email. qmail-dkim will call
+.BR qmail-queue .
+To invoke an executable other than
+.B qmail-queue
+set DKIMQUEUE=bin/qmail-dk for example.
+
+.B qmail-dkim
+supports DKIM signing and verification and can optionally use
+.B Sender Signing Practice (SSP)
+or
+.B Author Domain Signing Practice.
+It uses the libdkim and OpenSSL libraries. To sign a message, set the
+.B DKIMSIGN
+environment variable to the pathname of the private key that will be
+used to sign the message. If there is a % character in the environment
+variable, it is removed and replaced by the domain name in the From: header.
+If, after substituting the %, that file does not exist, the message will not be signed.
+If there is no % and the file does not exist, the message will be rejected with error 32.
+The selector (s=) will be taken from the
+basename of the file. The private key should be created by
+.BR dknewkey(8) .
+You can set various DKIM options in getopt style, by setting the environment variable
+.B DKIMSIGNOPTIONS
+
+ b <standard> 1 - allman, 2 - ietf or 3 - both
+ c <canonicalization> r for relaxed [DEFAULT], s - simple,
+ t relaxed/simple, u - simple/relaxed
+ l include body length tag
+ q include query method tag;
+ t include a timestamp tag
+ h
+ i <identity> the identity, if not provided it will not be included
+ x <expire_time> the expire time in seconds since epoch
+ ( DEFAULT = current time + 604800)
+ if set to - then it will not be included
+ z <hash> 1 for sha1, 2 for sha256, 3 for both
+
+.EX
+ DKIMSIGNOPTIONS="-b 1 -c r -q"
+ sets allman standard, with relaxed canonicalization and include query method tag
+.EE
+
+Apart from setting
+.BR DKIMSIGNOPTIONS ,
+you can set the identity and the expire time by setting
+.B DKIMIDENTITY
+and
+.B DKIMEXPIRE
+respectively.
+.B DKIMIDENTITY
+takes precedence over -i option specified in
+.BR DKIMSIGNOPTIONS.
+Similarly,
+.B DKIMEXPIRE
+takes precedence over -x option specifed in
+.BR DKIMSIGNOPTIONS.
+.B qmail-dkim
+uses the domain found in the Sender: header. If not it uses the domain in the From: header. You can override this by
+setting
+.B DKIMDOMAIN
+environment variable.
+.B DKIMDOMAIN
+can be set to an email address or a domain (without the at sign).
+
+To verify a message, set the
+.B DKIMVERIFY
+environment variable to a desired set of letters. Precisely, if you
+want a libdkim return status to generate an error, include that
+letter, where A is the first return status (DKIM_SUCCESS), B is the
+second (DKIM_FINISHED_BODY), etc. The letter should be uppercase if you
+want a permanent error to be returned (exit code 13), and lowercase if
+you want a temporary error to be returned (exit code 89).
+The complete set of letters with the corresponding return status is given below
+
+ A - DKIM_SUCCESS - Function executed successfully
+ B - DKIM_FINISHED_BODY - process result: no more message
+ body is needed
+ C - DKIM_PARTIAL_SUCCESS - verify result: at least one
+ but not all signatures verified
+ D - DKIM_NEUTRAL - verify result: no signatures
+ verified but message is
+ not suspicious
+ E - DKIM_SUCCESS_BUT_EXTRA - signature result: signature
+ verified but it did not
+ include all of the body
+ F - DKIM_3PS_SIGNATURE - 3rd-party signature
+ G - DKIM_FAIL - Function failed to execute
+ H - DKIM_BAD_SYNTAX - signature error: DKIM-Signature
+ could not parse or has bad
+ tags/values
+ I - DKIM_SIGNATURE_BAD - signature error: RSA verify
+ failed
+ J - DKIM_SIGNATURE_BAD_BUT_TESTING - signature error: RSA verify
+ failed but testing
+ K - DKIM_SIGNATURE_EXPIRED - signature error: x= is old
+ L - DKIM_SELECTOR_INVALID - signature error: selector doesn't
+ parse or contains invalid values
+ M - DKIM_SELECTOR_GRANULARITY_MISMATCH - signature error: selector
+ g= doesn't match i=
+ N - DKIM_SELECTOR_KEY_REVOKED - signature error: selector
+ p= empty
+ O - DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG - signature error: selector domain
+ name too long to request
+ P - DKIM_SELECTOR_DNS_TEMP_FAILURE - signature error: temporary dns
+ failure requesting selector
+ Q - DKIM_SELECTOR_DNS_PERM_FAILURE - signature error: permanent dns
+ failure requesting selector
+ R - DKIM_SELECTOR_PUBLIC_KEY_INVALID - signature error: selector
+ p= value invalid or wrong format
+ S - DKIM_NO_SIGNATURES - process error, no sigs
+ T - DKIM_NO_VALID_SIGNATURES - process error, no valid sigs
+ U - DKIM_BODY_HASH_MISMATCH - sigature verify error: message
+ body does not hash to bh value
+ V - DKIM_SELECTOR_ALGORITHM_MISMATCH - signature error: selector
+ h= doesn't match signature a=
+ W - DKIM_STAT_INCOMPAT - signature error: incompatible v=
+
+For example, if you want to permanently reject messages that have a
+signature that is expired, include the letter 'K' in the
+.B DKIMVERIFY
+environment variable. A conservative set of letters is
+.BR FGHIKLMNOQRTUVWjp .
+Reject permanently 3PS, FAILURE, SYNTAX, SIGNATURE_BAD, SIGNATURE_EXPIRED, SELECTOR_INVALID,
+GRANULARITY_MISMATCH, SELECTOR_KEY_REVOKED, DOMAIN_NAME_TOO_LONG, SELECTOR_PUBLIC_KEY_INVALID,
+NO_VALID_SIGNATURES and BODY_HASH_MISMATCH errors, and temporarily SIGNATURE_BAD_BUT_TESTING and DNS_TEMP_FAILURE .
+Add in
+.B S
+if you want to reject messages that do not have a DKIM signature. You can use the control files
+signaturedomains and nosignature domains (See Below) to further fine tune the action to be
+taken when a mail arrives with no DKIM signature. Note that
+.B qmail-dkim
+always inserts the
+.B DKIM-Status
+header, so that messages can be
+rejected later at delivery time, or in the mail reader. In that case you may set
+.B DKIMVERIFY
+to an empty string.
+
+qmail-dkim supports signing practice which can be additonall checked when a signature
+verifcation fails -
+
+.BR "SSP - Sender Signing Practice"
+
+and
+
+.BR "ADSP - Author Domain Signing Practice" .
+
+When a signature fails to verify for a message, you can use SSP/ADSP to determine if the message is suspicious or not.
+To verify a message against SSP/ADSP, set the
+.B DKIMPRACTICE
+environment variable to the desired set of letters allowed for DKIMVERIFY environment variable.
+SSP/ADSP should be used only when signature verification fails. SSP/ADSP gets invoked only when libdkim returns the
+error codes (F,G,H,I,J,K,L,M,N,P,Q,R,S,T,U,V,W) for signature verification. In case you want to test
+against SSP/ADSP only for DKIM_NO_SIGNATURE and DKIM_NO_VALID_SIGNATURE
+set the environment variable
+.BR DKIMPRACTICE="ST" .
+The default is "FGHIJKLMNPQRSTUVW". If you want automatic behaviour, set DKIMADSPERROR to an empty string.
+.B qmail-dkim
+uses ADSP as the default signing practice. You can override this by setting the SIGN_PRACTICE to ssp, adsp, local (lowercase).
+if you set SIGN_PRACTICE to \fIlocal\fB,
+.B qmail-dkim
+will check the domain against the control file
+.I signaturedomains
+(See Below). If the domain is found listed in
+.I signaturedomains
+.B qmail-dkim
+will bypass ADSP/SSP and return DKIM_FAIL if signature fails to verify. Setting SIGN_PRACTICE
+to anything else will cause
+.B qmail-dkim
+to disable Signing Practice.
+
+If ADSP or SSP is checked,
+.B qmail-dkim
+will insert the
+.B X-DKIM-ADSP
+or
+.B X-DKIM-SSP
+header as given below
+
+ A - DKIM_SUCCESS - Message passes ADSP test
+ B - DKIM_ADSP_UNKNOWN - some messages may be signed
+ C - DKIM_ADSP_ALL - All message are signed with author signature
+ D - DKIM_ADSP_DISCARDABLE - messages which fail verification are Discardable
+ E - DKIM_ADSP_SCOPE - domain is out of scope
+ F - DKIM_ADSP_TEMPFAIL - Temporary Error
+
+ or
+
+ A - DKIM_SUCCESS - Message passes ADSP test
+ B - DKIM_SSP_UNKNOWN - some messages may be signed
+ C - DKIM_SSP_ALL - All message are signed with author signature
+ D - DKIM_SSP_STRICT - messages which fail verification are Discardable
+ E - DKIM_SSP_SCOPE - domain is out of scope
+ F - DKIM_SSP_TEMPFAIL - Temporary Error
+
+You can have a control file
+.I signaturedomains
+containing a list of domains which you know are sure to sign messages using DKIM. If a message comes
+from a domain listed in
+.IR signaturedomains ,
+and the signature fails verification (any of DKIM failure status),
+.B qmail-dkim
+will bypass ADSP/SSP checks and return DKIM_FAIL. The name of this control file can be
+overriden by the environment variable
+.BR SIGNATUREDOMAINS .
+
+You can have a control file
+.I nosignaturedomains
+containing a list of domains which you know are sure not to sign messages using DKIM.
+If a message comes from a domain listed in
+.IR signaturedomains ,
+and does not have a DKIM-Signature header,
+.B qmail-dkim
+will bypass ADSP/SSP checks and return DKIM_NEUTRAL. The wildcard entry '*' in this file, will
+result in all mails which do not have a signature to pass DKIM test (unless the domain is listed
+in the control file
+.BR signaturedomains ).
+The name of this control file can be overriden by the environment variable
+.BR NOSIGNATUREDOMAINS .
+
+Typically, you would sign messages generated on-host by setting
+.B DKIMSIGN
+in the environment before running an email program. DKIMSIGN will be carried
+through qmail's sendmail emulation through
+.B qmail-inject
+to
+.BR qmail-dkim .
+You would also set it for
+.B qmail-smtpd
+at the same time
+.B RELAYCLIENT
+is set, most often in the tcpserver cdb file. If a host is authorized
+to relay, you probably want to sign messages sent by that host.
+.B DKIMVERIFY
+should be set for all other hosts.
+
+If neither
+.B DKIMSIGN
+nor
+.B DKIMVERIFY
+are set, then
+.B DKIMSIGN
+will be set to QMAILHOME/control/domainkeys/private. If such a private key exists, it will be used to sign the domain.
+
+By default
+.B qmail-dkim
+will use all of the headers when signing a message.
+
+.B qmail-dkim
+will ordinarily spawn
+.BR qmail-queue ,
+but if DKIMQUEUE is set in the environment,
+the program that it points to will be executed instead.
+
+.SH "EXIT CODES"
+.B qmail-dkim
+returns the same exit codes as qmail-queue with these additions:
+.TP 5
+.B 32
+The private key file does not exist.
+.TP 5
+.B 57
+Trouble waiting for qmail-queue to exit.
+.TP 5
+.B 58
+Unable to vfork.
+.TP 5
+.B 59
+Unable to create a pipe to qmail-queue.
+.SH "SEE ALSO"
+addresses(5),
+envelopes(5),
+qmail-header(5),
+dknewkey(8),
+dktest(8),
+qmail-inject(8),
+qmail-qmqpc(8),
+qmail-queue(8),
+qmail-send(8),
+qmail-smtpd(8),
+qmail-dk(8),
+domain-keys(5)
+
+.SH "AUTHORS"
+
+Manvendra Bhangui.
+.SH PROBLEMS
+Problems with
+.B qmail-dkim
+should be forwarded to "Manvendra Bhangui" <mbhangui@gmail.com>
diff -Naur qmail-1.03.org/qmail-dkim.c qmail-1.03/qmail-dkim.c
--- qmail-1.03.org/qmail-dkim.c 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/qmail-dkim.c 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,1273 @@
+/*
+ * $Log: qmail-dkim.c,v $
+ * Revision 1.18 2009-04-16 13:48:32+05:30 Cprogrammer
+ * added dkim_setoptions() to set all DKIM options
+ *
+ * Revision 1.17 2009-04-07 11:36:56+05:30 Cprogrammer
+ * use TMPDIR env variable for tmp directory
+ *
+ * Revision 1.16 2009-04-05 12:52:17+05:30 Cprogrammer
+ * added preprocessor warning
+ *
+ * Revision 1.15 2009-04-04 00:33:44+05:30 Cprogrammer
+ * removed dk_strdup()
+ *
+ * Revision 1.14 2009-03-31 08:21:58+05:30 Cprogrammer
+ * set dkimsign when RELAYCLIENT is defined when both dkimsign and dkimverify are undefined
+ *
+ * Revision 1.13 2009-03-30 22:25:54+05:30 Cprogrammer
+ * made DKIM messages friendlier
+ *
+ * Revision 1.12 2009-03-30 14:47:59+05:30 Cprogrammer
+ * added descriptive text for original dkim error
+ *
+ * Revision 1.11 2009-03-29 19:20:43+05:30 Cprogrammer
+ * added nosignaturedomains
+ *
+ * Revision 1.10 2009-03-28 22:27:02+05:30 Cprogrammer
+ * use DKIMSIGN, DKIMVERIFY if RELAYCLIENT is not set
+ *
+ * Revision 1.9 2009-03-28 22:03:05+05:30 Cprogrammer
+ * fixed DKIM return codes
+ *
+ * Revision 1.8 2009-03-28 13:37:37+05:30 Cprogrammer
+ * call DKIMVerifyGetDetails() always
+ *
+ * Revision 1.7 2009-03-28 11:39:23+05:30 Cprogrammer
+ * set automatic setting of dkimsign, dkimverify variables based on RELAYCLIENT
+ *
+ * Revision 1.6 2009-03-28 11:35:58+05:30 Cprogrammer
+ * added ADSP/SSP
+ *
+ * Revision 1.5 2009-03-22 17:39:38+05:30 Cprogrammer
+ * set identity using basename of signature or environment variable DKIMIDENTITY
+ *
+ * Revision 1.4 2009-03-22 16:58:38+05:30 Cprogrammer
+ * fixed bug with verification
+ * report custom errors to qmail-queue through custom error interface
+ *
+ * Revision 1.3 2009-03-21 12:34:38+05:30 Cprogrammer
+ * use hasdkim.h for conditional compilation of dkim
+ *
+ * Revision 1.2 2009-03-20 22:35:57+05:30 Cprogrammer
+ * set error to DKIM_NO_SIGNATURE when DKIM-Signature is not present
+ *
+ * Revision 1.1 2009-03-18 13:54:49+05:30 Cprogrammer
+ * Initial revision
+ *
+ */
+#include <unistd.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "sgetopt.h"
+#include "substdio.h"
+#include "open.h"
+#include "qmail.h"
+#include "sig.h"
+#include "case.h"
+#include "fmt.h"
+#include "fd.h"
+#include "alloc.h"
+#include "str.h"
+#include "stralloc.h"
+#include "datetime.h"
+#include "now.h"
+#include "wait.h"
+#include "auto_qmail.h"
+#include "env.h"
+#include "control.h"
+#include "dkim.h"
+
+#define DEATH 86400 /*- 24 hours; _must_ be below q-s's OSSIFIED (36 hours) */
+#define ADDR 1003
+#define HAVE_EVP_SHA256
+#define strncasecmp(x,y,z) case_diffb((x), (z), (y))
+#define strcasecmp(x,y) case_diffs((x), (y))
+
+char inbuf[2048];
+char outbuf[256];
+char errbuf[256];
+struct substdio ssin;
+struct substdio ssout;
+struct substdio sserr;
+
+datetime_sec starttime;
+struct datetime dt;
+unsigned long mypid;
+unsigned long uid;
+char *pidfn;
+int messfd;
+int readfd;
+
+char **MakeArgs(char *);
+void FreeMakeArgs(char **);
+
+void
+die(e)
+ int e;
+{
+ _exit(e);
+}
+
+void
+die_write()
+{
+ die(53);
+}
+
+void
+die_read()
+{
+ die(54);
+}
+
+void
+sigalrm()
+{
+ /*- thou shalt not clean up here */
+ die(52);
+}
+
+void
+sigbug()
+{
+ die(81);
+}
+
+void
+custom_error(char *flag, char *status, char *code)
+{
+ char *c;
+
+ if (substdio_put(&sserr, flag, 1) == -1)
+ die_write();
+ if (substdio_put(&sserr, "qmail-dkim: ", 12) == -1)
+ die_write();
+ if (substdio_puts(&sserr, status) == -1)
+ die_write();
+ if (code)
+ {
+ if (substdio_put(&sserr, " (#", 3) == -1)
+ die_write();
+ c = (*flag == 'Z') ? "4" : "5";
+ if (substdio_put(&sserr, c, 1) == -1)
+ die_write();
+ if (substdio_put(&sserr, code + 1, 4) == -1)
+ die_write();
+ if (substdio_put(&sserr, ")", 1) == -1)
+ die_write();
+ }
+ if (substdio_flush(&sserr) == -1)
+ die_write();
+ return;
+}
+
+int DKIM_CALL
+SignThisHeader(const char *szHeader)
+{
+ if (strncasecmp((char *) szHeader, "X-", 2) == 0
+ || strncasecmp((char *) szHeader, "Received:", 9) == 0
+ || strncasecmp((char *) szHeader, "Authentication-Results:", 23) == 0
+ || strncasecmp((char *) szHeader, "Return-Path:", 12) == 0)
+ {
+ return 0;
+ }
+ return 1;
+}
+
+void
+maybe_die_dkim(e)
+ int e;
+{
+ switch (e)
+ {
+ case DKIM_OUT_OF_MEMORY:
+ case DKIM_BUFFER_TOO_SMALL:
+ _exit (51);
+ case DKIM_INVALID_CONTEXT:
+ custom_error("Z", "DKIMContext structure invalid for this operation (#4.3.0)", 0);
+ _exit(88);
+ case DKIM_NO_SENDER:
+ custom_error("Z", "Could not find From: or Sender: header in message (#5.1.7)", 0);
+ _exit(88);
+ case DKIM_BAD_PRIVATE_KEY:
+ custom_error("D", "Could not parse private key (#5.7.5)", 0);
+ _exit(88);
+ default:
+ return;
+ }
+}
+
+unsigned int
+pidfmt(s, seq)
+ char *s;
+ unsigned long seq;
+{
+ unsigned int i;
+ unsigned int len;
+ char *tmpdir;
+
+ if (!(tmpdir = env_get("TMPDIR")))
+ tmpdir = "/tmp";
+ len = 0;
+ i = fmt_str(s, tmpdir);
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_str(s, "/qmail-dkim.");
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_ulong(s, mypid);
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_str(s, ".");
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_ulong(s, starttime);
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_str(s, ".");
+ len += i;
+ if (s)
+ s += i;
+ i = fmt_ulong(s, seq);
+ len += i;
+ if (s)
+ s += i;
+ ++len;
+ if (s)
+ *s++ = 0;
+
+ return len;
+}
+
+void
+pidopen()
+{
+ unsigned int len;
+ unsigned long seq;
+
+ seq = 1;
+ len = pidfmt((char *) 0, seq);
+ if (!(pidfn = alloc(len)))
+ die(51);
+ for (seq = 1; seq < 10; ++seq)
+ {
+ if (pidfmt((char *) 0, seq) > len)
+ die(81); /*- paranoia */
+ pidfmt(pidfn, seq);
+ if ((messfd = open_excl(pidfn)) != -1)
+ return;
+ }
+ die(63);
+}
+
+char tmp[FMT_ULONG];
+DKIMContext ctxt;
+char *dkimsign = 0;
+char *dkimverify = 0;
+char *dkimadspverify = 0, *dkimpractice = "FGHIJKLMNPQRSTUVW";
+stralloc dkimoutput = { 0 }; /*- DKIM-Signature */
+stralloc dksignature = { 0 }; /*- content of private signature */
+stralloc sigdomains = { 0 }; /*- domains which must have signatures */
+stralloc nsigdomains = { 0 }; /*- domains which do not have signatures */
+stralloc dkimopts = { 0 };
+char *dkimqueue = 0;
+
+static void
+write_signature(char *domain, char *keyfn)
+{
+ char *pSig;
+ int i;
+ static stralloc keyfnfrom = { 0 };
+
+ i = str_chr(keyfn, '%');
+ if (keyfn[i])
+ {
+ if (!stralloc_copyb(&keyfnfrom, keyfn, i))
+ die(51);
+ if (!stralloc_cats(&keyfnfrom, domain))
+ die(51);
+ if (!stralloc_cats(&keyfnfrom, keyfn + i + 1))
+ die(51);
+ } else
+ if (!stralloc_copys(&keyfnfrom, keyfn))
+ die(51);
+ if (!stralloc_0(&keyfnfrom))
+ die(51);
+ switch (control_readfile(&dksignature, keyfnfrom.s, 0))
+ {
+ case 0: /*- missing signature file */
+ if (keyfn[i])
+ return;
+ die(32);
+ case 1:
+ break;
+ default:
+ custom_error("Z", "Unable to read private key. (#4.3.0)", 0);
+ _exit(88);
+ }
+ for (i = 0; i < dksignature.len; i++)
+ {
+ if (dksignature.s[i] == '\0')
+ dksignature.s[i] = '\n';
+ }
+ if (!stralloc_0(&dksignature))
+ die(51);
+ i = DKIMSignGetSig2(&ctxt, dksignature.s, &pSig);
+ maybe_die_dkim(i);
+ if (pSig)
+ {
+ if (!stralloc_catb(&dkimoutput, pSig, str_len(pSig)))
+ die(51);
+ if (!stralloc_cats(&dkimoutput, "\n"))
+ die(51);
+ }
+ DKIMSignFree(&ctxt);
+}
+
+#include <openssl/evp.h>
+#define DKIM_MALLOC(s) OPENSSL_malloc(s)
+#define DKIM_MFREE(s) OPENSSL_free(s); s = NULL;
+char *dns_text(char *);
+
+int
+ParseTagValues(char *list, char *letters[], char *values[])
+{
+ char *tmp, *ptr, *key;
+ int i;
+
+ /*- start with all args unset */
+ for (i = 0; letters[i]; i++)
+ values[i] = 0;
+ key = 0;
+ for(ptr = list;*ptr;)
+ {
+ if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n')) /*- FWS */
+ *ptr++ = 0;
+ if (!key)
+ key = ptr;
+ if (*ptr == '=')
+ {
+ *ptr = 0;
+ for (i = 0;letters[i];i++)
+ {
+ if (!str_diff(letters[i], key))
+ {
+ ptr++;
+ for (;*ptr;)
+ {
+ if ((*ptr == ' ') || (*ptr == '\t') || (*ptr == '\r') || (*ptr == '\n'))
+ {
+ ptr++;
+ continue;
+ }
+ break;
+ }
+ values[i] = ptr;
+ for(;*ptr && *ptr != ';';ptr++);
+ tmp = ptr;
+ if (*ptr)
+ *ptr++ = 0;
+ for(;tmp != values[i];tmp--) /*- RFC 4871 3.2 */
+ {
+ if ((*tmp == ' ') || (*tmp == '\t') || (*tmp == '\r') || (*tmp == '\n'))
+ {
+ *tmp = 0;
+ continue;
+ }
+ break;
+ }
+ key = 0;
+ break;
+ }
+ }
+ } else
+ ptr++;
+ }
+ return (0);
+}
+
+int
+checkSSP(char *domain, int *bTesting)
+{
+ char *query, *results;
+ char *tags[] = { "dkim", "t", 0};
+ char *values[2];
+ int bIsParentSSP = 0, iSSP = DKIM_SSP_UNKNOWN;
+
+ *bTesting = 0;
+ if (!(query = DKIM_MALLOC(str_len("_ssp._domainkey.") + str_len(domain) + 1)))
+ die(51);
+ sprintf(query, "_ssp._domainkey.%s", domain);
+ results = dns_text(query);
+ DKIM_MFREE(query);
+ if (!str_diff(results, "e=temp;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_SSP_TEMPFAIL;
+ } else
+ if (!str_diff(results, "e=perm;"))
+ {
+ DKIM_MFREE(results);
+ results = dns_text(domain);
+ if (!str_diff(results, "e=temp;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_SSP_TEMPFAIL;
+ } else
+ if (!str_diff(results, "e=perm;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_SSP_SCOPE;
+ }
+ bIsParentSSP = 1;
+ }
+ if (!ParseTagValues(results, tags, values))
+ return DKIM_SSP_UNKNOWN;
+ if (values[0] != NULL) {
+ if (strcasecmp(values[0], "all") == 0)
+ iSSP = DKIM_SSP_ALL;
+ else
+ if (strcasecmp(values[0], "strict") == 0)
+ iSSP = DKIM_SSP_STRICT;
+ }
+ // flags
+ if (values[1] != NULL) {
+ char *s, *p;
+ for (p = values[1], s = values[1]; *p; p++)
+ {
+ if (*p == '|')
+ *p = 0;
+ else
+ continue;
+ if (!str_diff(s, "y"))
+ *bTesting = 1;
+ else
+ if (!str_diff(s, "s")) {
+ if (bIsParentSSP) {
+ /*
+ * this is a parent's SSP record that should not apply to subdomains
+ * the message is non-suspicious
+ */
+ *bTesting = 0;
+ return (DKIM_SSP_UNKNOWN);
+ }
+ }
+ s = p + 1;
+ }
+ }
+ return iSSP;
+}
+
+int
+checkADSP(char *domain)
+{
+ char *query, *results;
+ char *tags[] = { "dkim", 0};
+ char *values[1];
+
+ results = dns_text(domain);
+ if (!str_diff(results, "e=perm;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_SCOPE;
+ }
+ else
+ if (!str_diff(results, "e=temp;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_TEMPFAIL;
+ }
+ if (!(query = DKIM_MALLOC(str_len("_adsp._domainkey.") + str_len(domain) + 1)))
+ die(51);
+ sprintf(query, "_adsp._domainkey.%s", domain);
+ results = dns_text(query);
+ DKIM_MFREE(query);
+ if (!str_diff(results, "e=perm;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_SCOPE;
+ } else
+ if (!str_diff(results, "e=temp;"))
+ {
+ DKIM_MFREE(results);
+ return DKIM_ADSP_TEMPFAIL;
+ }
+ DKIM_MFREE(results);
+ if (!ParseTagValues(results, tags, values))
+ return DKIM_SSP_UNKNOWN;
+ if (values[0] != NULL) {
+ if (strcasecmp(values[0], "all") == 0)
+ return (DKIM_ADSP_ALL);
+ else
+ if (strcasecmp(values[0], "discardable") == 0)
+ return (DKIM_ADSP_DISCARDABLE);
+ }
+ return DKIM_ADSP_UNKNOWN; /*- No ADSP Record */
+}
+
+void
+dkimverify_exit(int dkimRet, char *status, char *code)
+{
+ if (dkimRet < 0)
+ {
+ if (dkimverify[str_chr(dkimverify, 'F' - dkimRet)])
+ {
+ custom_error("D", status, code);
+ die(88);
+ }
+ if (dkimverify[str_chr(dkimverify, 'f' - dkimRet)])
+ {
+ custom_error("Z", status, code);
+ die(88);
+ }
+ } else
+ {
+ if (dkimverify[str_chr(dkimverify, 'A' + dkimRet)])
+ {
+ custom_error("D", status, code);
+ die(88);
+ }
+ if (dkimverify[str_chr(dkimverify, 'a' + dkimRet)])
+ {
+ custom_error("Z", status, code);
+ die(88);
+ }
+ }
+}
+
+void
+writeHeaderNexit(int ret, int origRet, int resDKIMSSP, int resDKIMADSP, int useSSP, int useADSP)
+{
+ char *dkimStatus = 0, *sspStatus = 0, *adspStatus = 0, *code = 0, *orig = 0;
+ char strnum[FMT_ULONG];
+
+ switch (ret)
+ {
+ case DKIM_SUCCESS: /*- 0 */ /*- A */
+ dkimStatus = "good ";
+ code = "X.7.0";
+ break;
+ case DKIM_FINISHED_BODY: /*- 1 process result: no more message body is needed */
+ dkimStatus = "process result: no more message body is needed";
+ code = "X.7.0";
+ break;
+ case DKIM_PARTIAL_SUCCESS: /*- 2 verify result: at least one but not all signatures verified */
+ dkimStatus = "verify result: at least none but not all signatures verified";
+ code = "X.7.0";
+ break;
+ case DKIM_NEUTRAL: /*- 3 verify result: no signatures verified but message is not suspicious */
+ dkimStatus = "verify result: no signatures verified but message is not suspicious";
+ code = "X.7.0";
+ break;
+ case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
+ dkimStatus = "signature result: signature verified but it did not include all of the body";
+ code = "X.7.0";
+ break;
+ case DKIM_FAIL: /*- -1 */ /*- F */
+ dkimStatus = "DKIM Signature verification failed";
+ code = "X.7.0";
+ break;
+ case DKIM_BAD_SYNTAX: /*- -2 */ /*- G */
+ dkimStatus = "signature error: DKIM-Signature could not parse or has bad tags/values";
+ code = "X.7.5";
+ break;
+ case DKIM_SIGNATURE_BAD: /*- -3 */
+ dkimStatus = "signature error: RSA verify failed";
+ code = "X.7.5";
+ break;
+ case DKIM_SIGNATURE_BAD_BUT_TESTING:
+ dkimStatus = "signature error: RSA verify failed but testing";
+ code = "X.7.5";
+ break;
+ case DKIM_SIGNATURE_EXPIRED:
+ dkimStatus = "signature error: x= is old";
+ code = "X.7.5";
+ break;
+ case DKIM_SELECTOR_INVALID:
+ dkimStatus = "signature error: selector doesn't parse or contains invalid values";
+ code = "X.7.5";
+ break;
+ case DKIM_SELECTOR_GRANULARITY_MISMATCH:
+ dkimStatus = "signature error: selector g= doesn't match i=";
+ code = "X.7.5";
+ break;
+ case DKIM_SELECTOR_KEY_REVOKED:
+ dkimStatus = "signature error: selector p= empty";
+ code = "X.7.5";
+ break;
+ case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
+ dkimStatus = "signature error: selector domain name too long to request";
+ code = "X.7.0";
+ break;
+ case DKIM_SELECTOR_DNS_TEMP_FAILURE:
+ dkimStatus = "signature error: temporary dns failure requesting selector";
+ code = "X.7.0";
+ break;
+ case DKIM_SELECTOR_DNS_PERM_FAILURE:
+ dkimStatus = "signature error: permanent dns failure requesting selector";
+ code = "X.7.0";
+ break;
+ case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
+ dkimStatus = "signature error: selector p= value invalid or wrong format";
+ code = "X.7.5";
+ break;
+ case DKIM_NO_SIGNATURES:
+ dkimStatus = "no signatures";
+ code = "X.7.5";
+ break;
+ case DKIM_NO_VALID_SIGNATURES:
+ dkimStatus = "no valid signatures";
+ code = "X.7.5";
+ break;
+ case DKIM_BODY_HASH_MISMATCH:
+ dkimStatus = "sigature verify error: message body does not hash to bh value";
+ code = "X.7.7";
+ break;
+ case DKIM_SELECTOR_ALGORITHM_MISMATCH:
+ dkimStatus = "signature error: selector h= doesn't match signature a=";
+ code = "X.7.7";
+ break;
+ case DKIM_STAT_INCOMPAT:
+ dkimStatus = "signature error: incompatible v=";
+ code = "X.7.6";
+ break;
+ default:
+ dkimStatus = "error";
+ code = "X.3.0";
+ break;
+ }
+ if (useSSP && resDKIMSSP != -1)
+ {
+ switch(resDKIMSSP)
+ {
+ case DKIM_SSP_ALL:
+ sspStatus = (char *) "all;";
+ break;
+ case DKIM_SSP_STRICT:
+ sspStatus = (char *) "strict;";
+ break;
+ case DKIM_SSP_SCOPE:
+ sspStatus = (char *) "out of scope;";
+ break;
+ case DKIM_SSP_TEMPFAIL:
+ sspStatus = (char *) "temporary failure;";
+ break;
+ case DKIM_SSP_UNKNOWN:
+ default:
+ sspStatus = (char *) "unknown;";
+ break;
+ }
+ }
+ if (useADSP && resDKIMADSP != -1)
+ {
+ switch(resDKIMADSP)
+ {
+ case DKIM_ADSP_ALL:
+ adspStatus = (char *) "all;";
+ break;
+ case DKIM_ADSP_DISCARDABLE:
+ adspStatus = (char *) "discardable;";
+ break;
+ case DKIM_ADSP_SCOPE:
+ adspStatus = (char *) "out of scope;";
+ break;
+ case DKIM_ADSP_TEMPFAIL:
+ adspStatus = (char *) "temporary failure;";
+ break;
+ case DKIM_ADSP_UNKNOWN:
+ default:
+ adspStatus = (char *) "unknown ;";
+ break;
+ }
+ }
+ if (!stralloc_copys(&dkimoutput, "DKIM-Status: "))
+ die(51);
+ if (!stralloc_cats(&dkimoutput, dkimStatus))
+ die(51);
+ if (origRet != DKIM_MAX_ERROR && ret != origRet)
+ {
+ if (!stralloc_cats(&dkimoutput, "\n\t(old="))
+ die(51);
+ switch (origRet)
+ {
+ case DKIM_SUCCESS: /*- 0 */ /*- A */
+ orig = "SUCCESS";
+ break;
+ case DKIM_FINISHED_BODY: /*- 1 process result: no more message body is needed */
+ orig = "FINISHED BODY";
+ break;
+ case DKIM_PARTIAL_SUCCESS: /*- 2 verify result: at least one but not all signatures verified */
+ orig = "PARTIAL SUCCESS";
+ break;
+ case DKIM_NEUTRAL: /*- 3 verify result: no signatures verified but message is not suspicious */
+ orig = "NEUTRAL";
+ break;
+ case DKIM_SUCCESS_BUT_EXTRA:/*- 4 signature result: signature verified but it did not include all of the body */
+ orig = "SUCCESS(BUT EXTRA)";
+ break;
+ case DKIM_FAIL: /*- -1 */ /*- F */
+ orig = "FAIL";
+ break;
+ case DKIM_BAD_SYNTAX: /*- -2 */ /*- G */
+ orig = "BAD SYNTAX";
+ break;
+ case DKIM_SIGNATURE_BAD: /*- -3 */
+ orig = "SIGNATURE BAD";
+ break;
+ case DKIM_SIGNATURE_BAD_BUT_TESTING:
+ orig = "SIGNATURE BAD (TESTING)";
+ break;
+ case DKIM_SIGNATURE_EXPIRED:
+ orig = "SIGNATURE EXPIRED";
+ break;
+ case DKIM_SELECTOR_INVALID:
+ orig = "SELECTOR INVALID";
+ break;
+ case DKIM_SELECTOR_GRANULARITY_MISMATCH:
+ orig = "SELECTOR GRANULARITY MISMATCH";
+ break;
+ case DKIM_SELECTOR_KEY_REVOKED:
+ orig = "SELECTOR KEY REVOKED";
+ break;
+ case DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG:
+ orig = "DOMAIN NAME TOO LONG";
+ break;
+ case DKIM_SELECTOR_DNS_TEMP_FAILURE:
+ orig = "DNS TEMP FAILURE";
+ break;
+ case DKIM_SELECTOR_DNS_PERM_FAILURE:
+ orig = "DNS PERM FAILURE";
+ break;
+ case DKIM_SELECTOR_PUBLIC_KEY_INVALID:
+ orig = "PUBLIC KEY INVALID";
+ break;
+ case DKIM_NO_SIGNATURES:
+ orig = "NO SIGNATURES";
+ break;
+ case DKIM_NO_VALID_SIGNATURES:
+ orig = "NO VALID SIGNATURES";
+ break;
+ case DKIM_BODY_HASH_MISMATCH:
+ orig = "BODY HASH MISMATCH";
+ break;
+ case DKIM_SELECTOR_ALGORITHM_MISMATCH:
+ orig = "ALGORITHM MISMATCH";
+ break;
+ case DKIM_STAT_INCOMPAT:
+ orig = "STAT INCOMPAT";
+ break;
+ default:
+ orig = "Unkown error";
+ break;
+ }
+ if (!stralloc_cats(&dkimoutput, orig))
+ die(51);
+ if (!stralloc_cats(&dkimoutput, ":"))
+ die(51);
+ if (origRet < 0)
+ {
+ if (!stralloc_cats(&dkimoutput, "-"))
+ die(51);
+ strnum[fmt_ulong(strnum, 0 - origRet)] = 0;
+ } else
+ strnum[fmt_ulong(strnum, origRet)] = 0;
+ if (!stralloc_cats(&dkimoutput, strnum))
+ die(51);
+ if (!stralloc_cats(&dkimoutput, ")"))
+ die(51);
+ }
+ if (!stralloc_cats(&dkimoutput, "\n"))
+ die(51);
+ if (useSSP && sspStatus) {
+ if (!stralloc_cats(&dkimoutput, "X-DKIM-SSP: "))
+ die(51);
+ if (!stralloc_cats(&dkimoutput, sspStatus))
+ die(51);
+ if (!stralloc_cats(&dkimoutput, "\n"))
+ die(51);
+ }
+ if (useADSP && adspStatus) {
+ if (!stralloc_cats(&dkimoutput, "X-DKIM-ADSP: "))
+ die(51);
+ if (!stralloc_cats(&dkimoutput, adspStatus))
+ die(51);
+ if (!stralloc_cats(&dkimoutput, "\n"))
+ die(51);
+ }
+ dkimverify_exit(ret, dkimStatus, code);
+ return;
+}
+
+int
+checkPractice(int dkimRet)
+{
+ char *ptr;
+
+ if (!(ptr = env_get("DKIMPRACTICE")))
+ return (0);
+ else
+ dkimpractice = ptr;
+ if (!*ptr)
+ {
+ if (dkimRet < 0 || dkimRet == DKIM_3PS_SIGNATURE)
+ return (1);
+ return (0);
+ }
+ if (dkimRet < 0)
+ {
+ if (dkimpractice[str_chr(dkimpractice, 'F' - dkimRet)])
+ return (1);
+ if (dkimpractice[str_chr(dkimpractice, 'f' - dkimRet)])
+ return (1);
+ } else
+ {
+ if (dkimpractice[str_chr(dkimpractice, 'A' + dkimRet)])
+ return (1);
+ if (dkimpractice[str_chr(dkimpractice, 'a' + dkimRet)])
+ return (1);
+ }
+ return (0);
+}
+
+static char *binqqargs[2] = { "bin/qmail-queue", 0 };
+
+int
+dkim_setoptions(DKIMSignOptions *opts, char *signOptions)
+{
+ int ch, argc;
+ char **argv;
+
+ opts->nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
+ opts->nCanon = DKIM_SIGN_RELAXED; /*- c */
+ opts->nIncludeBodyLengthTag = 0; /*- l */
+ opts->nIncludeQueryMethod = 0; /*- q */
+ opts->nIncludeTimeStamp = 0; /*- t */
+ opts->nIncludeCopiedHeaders = 0; /*- h */
+ opts->szIdentity[0] = '\0';
+ opts->expireTime = starttime + 604800; // expires in 1 week
+ opts->nHash = DKIM_HASH_SHA1;
+ str_copy(opts->szRequiredHeaders, "NonExistent");
+ if (!signOptions)
+ return (0);
+ if (!stralloc_copys(&dkimopts, "dkim "))
+ die(51);
+ if (!stralloc_cats(&dkimopts, signOptions))
+ die(51);
+ if (!stralloc_0(&dkimopts))
+ die(51);
+ if (!(argv = MakeArgs(dkimopts.s)))
+ die(51);
+ for (argc = 0;argv[argc];argc++);
+#ifdef HAVE_EVP_SHA256
+ while ((ch = sgopt(argc, argv, "b:c:li:qthx:z:")) != sgoptdone)
+#else
+ while ((ch = sgopt(argc, argv, "b:c:li:qthx:")) != sgoptdone)
+#endif
+ {
+ switch (ch)
+ {
+ case 'b':
+ switch (*optarg)
+ {
+ case '1':
+ opts->nIncludeBodyHash = DKIM_BODYHASH_ALLMAN_1;
+ break;
+ case '2':
+ opts->nIncludeBodyHash = DKIM_BODYHASH_IETF_1;
+ break;
+ case '3':
+ opts->nIncludeBodyHash = DKIM_BODYHASH_BOTH;
+ break;
+ default:
+ FreeMakeArgs(argv);
+ return (1);
+ }
+ break;
+ case 'c':
+ switch (*optarg)
+ {
+ case 'r':
+ opts->nCanon = DKIM_SIGN_RELAXED;
+ break;
+ case 's':
+ opts->nCanon = DKIM_SIGN_SIMPLE;
+ break;
+ case 't':
+ opts->nCanon = DKIM_SIGN_RELAXED_SIMPLE;
+ break;
+ case 'u':
+ opts->nCanon = DKIM_SIGN_SIMPLE_RELAXED;
+ break;
+ default:
+ FreeMakeArgs(argv);
+ return (1);
+ }
+ break;
+ case 'l': /*- body length tag */
+ opts->nIncludeBodyLengthTag = 1;
+ break;
+ case 'q': /*- query method tag */
+ opts->nIncludeQueryMethod = 1;
+ break;
+ case 't': /*- timestamp tag */
+ opts->nIncludeTimeStamp = 1;
+ break;
+ case 'h':
+ opts->nIncludeCopiedHeaders = 1;
+ break;
+ case 'i': /*- identity */
+ if (*optarg == '-') /* do not use i= tag */
+ opts->szIdentity[0] = '\0';
+ else
+ str_copyb(opts->szIdentity, optarg, sizeof(opts->szIdentity) - 1);
+ break;
+ case 'x': /*- expire time */
+ if (*optarg == '-')
+ opts->expireTime = 0;
+ else
+ opts->expireTime = starttime + atoi(optarg);
+ break;
+#ifdef HAVE_EVP_SHA256
+ case 'z': /*- sign w/ sha1, sha256 or both */
+ switch (*optarg)
+ {
+ case '1':
+ opts->nHash = DKIM_HASH_SHA1;
+ break;
+ case '2':
+ opts->nHash = DKIM_HASH_SHA256;
+ break;
+ case '3':
+ opts->nHash = DKIM_HASH_SHA1_AND_256;
+ break;
+ default:
+ FreeMakeArgs(argv);
+ return (1);
+ }
+ break;
+#endif
+ default:
+ FreeMakeArgs(argv);
+ return (1);
+ } /*- switch (ch) */
+ } /*- while (1) */
+ FreeMakeArgs(argv);
+ return (0);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int pim[2];
+ int wstat;
+ int resDKIMSSP = -1, resDKIMADSP = -1, useSSP = 0, useADSP = 0, accept3ps = 0;
+ int sCount = 0, sSize = 0;
+ int ret = 0, origRet = DKIM_MAX_ERROR, i, nSigCount = 0, len, token_len;
+ unsigned long pid;
+ char *selector, *p;
+ DKIMSignOptions opts = { 0 };
+ DKIMVerifyDetails *pDetails;
+ DKIMVerifyOptions vopts = { 0 };
+
+ starttime = now();
+ sig_blocknone();
+ umask(033);
+ if (chdir(auto_qmail) == -1)
+ die(61);
+ if (!dkimsign)
+ dkimsign = env_get("DKIMSIGN");
+ if (!dkimverify)
+ dkimverify = env_get("DKIMVERIFY");
+ if (!dkimsign && !dkimverify && (p = env_get("RELAYCLIENT")))
+ dkimsign = "control/domainkeys/%/default";
+ dkimqueue = env_get("DKIMQUEUE");
+ if (dkimqueue && *dkimqueue)
+ binqqargs[0] = dkimqueue;
+ substdio_fdbuf(&sserr, write, CUSTOM_ERR_FD, errbuf, sizeof(errbuf));
+ if (dkimsign)
+ {
+ /* selector */
+ p = dkimsign;
+ selector = p;
+ while (*p)
+ {
+ if (*p == '/' && *(p + 1))
+ selector = p + 1;
+ p++;
+ }
+ str_copyb(opts.szSelector, selector, sizeof(opts.szSelector) - 1);
+
+ if (dkim_setoptions(&opts, env_get("DKIMSIGNOPTIONS")))
+ {
+ custom_error("Z", "Invalid DKIMSIGNOPTIONS (#4.3.0)", 0);
+ _exit(88);
+ }
+ p = env_get("DKIMIDENTITY");
+ if (p && *p)
+ str_copyb(opts.szIdentity, p, sizeof(opts.szIdentity) - 1);
+ p = env_get("DKIMEXPIRE");
+ if (p && *p)
+ opts.expireTime = starttime + atol(p);
+ else
+ if (p)
+ opts.expireTime = 0;
+ opts.pfnHeaderCallback = SignThisHeader;
+ if (DKIMSignInit(&ctxt, &opts) != DKIM_SUCCESS) /*- failed to initialize signature */
+ die(51);
+ } else
+ {
+ char *x;
+
+ if (!(x = env_get("SIGN_PRACTICE")))
+ x = "adsp";
+ if (!str_diffn("adsp", x, 4))
+ {
+ useADSP = 1;
+ accept3ps = 1;
+ }
+ else
+ if (!str_diffn("ssp", x, 3))
+ {
+ useSSP = 1;
+ accept3ps = 1;
+ } else
+ if (!str_diffn("local", x, 5))
+ {
+ useSSP = 0;
+ useADSP = 0;
+ accept3ps = 1;
+ }
+ if (useADSP)
+ vopts.nCheckPractices = useADSP;
+ else
+ if (useSSP)
+ vopts.nCheckPractices = useSSP;
+ else
+ vopts.nCheckPractices = 0;
+ vopts.nAccept3ps = accept3ps;
+ vopts.pfnSelectorCallback = NULL; /*- SelectorCallback; */
+ DKIMVerifyInit(&ctxt, &vopts); /*- this is always successful */
+ }
+ /*- Initialization */
+ mypid = getpid();
+ uid = getuid();
+ datetime_tai(&dt, starttime);
+ sig_pipeignore();
+ sig_miscignore();
+ sig_alarmcatch(sigalrm);
+ sig_bugcatch(sigbug);
+ alarm(DEATH);
+ pidopen(); /*- fd = messfd */
+ if ((readfd = open_read(pidfn)) == -1)
+ die(63);
+ if (unlink(pidfn) == -1)
+ die(63);
+ substdio_fdbuf(&ssout, write, messfd, outbuf, sizeof(outbuf));
+ substdio_fdbuf(&ssin, read, 0, inbuf, sizeof(inbuf));
+ for (;;)
+ {
+ register int n;
+ register char *x;
+ int i, j;
+
+ if ((n = substdio_feed(&ssin)) < 0)
+ die_read();
+ if (!n)
+ break;
+ x = substdio_PEEK(&ssin);
+ if (dkimsign || dkimverify)
+ {
+ for (i = 0; i < n; i++)
+ {
+ if (x[i] == '\n')
+ {
+ if (dkimsign)
+ j = DKIMSignProcess(&ctxt, "\r\n", 2);
+ else
+ j = DKIMVerifyProcess(&ctxt, "\r\n", 2);
+ } else
+ {
+ if (dkimsign)
+ j = DKIMSignProcess(&ctxt, x + i, 1);
+ else
+ j = DKIMVerifyProcess(&ctxt, x + i, 1);
+ }
+ maybe_die_dkim(j);
+ if (j > 0 && j < DKIM_FINISHED_BODY)
+ break;
+ if (!ret && j)
+ ret = j;
+ }
+ } /*- for(;;) */
+ if (substdio_put(&ssout, x, n) == -1)
+ die_write();
+ substdio_SEEK(&ssin, n);
+ }
+ if (substdio_flush(&ssout) == -1)
+ die_write();
+ if (dkimsign || dkimverify)
+ {
+ if (dkimsign)
+ write_signature(DKIMSignGetDomain(&ctxt), dkimsign);
+ else
+ if (dkimverify)
+ {
+ char szPolicy[512];
+
+ if (!ret)
+ {
+ if ((ret = DKIMVerifyResults(&ctxt, &sCount, &sSize)) != DKIM_SUCCESS)
+ maybe_die_dkim(ret);
+ if ((ret = DKIMVerifyGetDetails(&ctxt, &nSigCount, &pDetails, szPolicy)) != DKIM_SUCCESS)
+ maybe_die_dkim(ret);
+ else
+ for (ret = DKIM_SUCCESS,i = 0; i < nSigCount; i++) {
+ if (pDetails[i].nResult < 0)
+ {
+ ret = pDetails[i].nResult;
+ break; /*- don't know if it is right to break */
+ }
+ }
+ if (!nSigCount)
+ ret = DKIM_NO_SIGNATURES;
+ }
+ if (checkPractice(ret)) {
+ char *domain;
+
+ origRet = ret;
+ if ((domain = DKIMVerifyGetDomain(&ctxt)))
+ {
+ if (!(p = env_get("SIGNATUREDOMAINS")))
+ {
+ if (control_readfile(&sigdomains, "control/signaturedomains", 0) == -1)
+ die(55);
+ } else
+ if (!stralloc_copys(&sigdomains, p))
+ die(51);
+ for (len = 0, p = sigdomains.s;len < sigdomains.len;)
+ {
+ len += ((token_len = str_len(p)) + 1); /*- next domain */
+ if (!case_diffb(p, token_len, domain))
+ {
+ ret = DKIM_FAIL;
+ useADSP = 0;
+ useSSP = 0;
+ break;
+ }
+ p = sigdomains.s + len;
+ }
+ if (!(p = env_get("NOSIGNATUREDOMAINS")))
+ {
+ if (control_readfile(&nsigdomains, "control/nosignaturedomains", 0) == -1)
+ die(55);
+ } else
+ if (!stralloc_copys(&nsigdomains, p))
+ die(51);
+ for (len = 0, p = nsigdomains.s;len < nsigdomains.len;)
+ {
+ len += ((token_len = str_len(p)) + 1); /*- next domain */
+ if (*p == '*' || !case_diffb(p, token_len, domain))
+ {
+ ret = DKIM_NEUTRAL;
+ useADSP = 0;
+ useSSP = 0;
+ break;
+ }
+ p = nsigdomains.s + len;
+ }
+ }
+ if (!domain || !*domain)
+ ; /*- do nothing ? */
+ else
+ if (useADSP)
+ {
+ resDKIMADSP = checkADSP(domain);
+ if (sCount > 0) {
+ if (resDKIMADSP == DKIM_ADSP_UNKNOWN || resDKIMADSP == DKIM_ADSP_ALL)
+ ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
+ }
+ /* if the message should be signed, return fail */
+ if (resDKIMADSP == DKIM_ADSP_DISCARDABLE)
+ ret = DKIM_FAIL;
+ else
+ ret = DKIM_NEUTRAL;
+ } else
+ if (useSSP)
+ {
+ int bTestingPractices = 0;
+ char *domain;
+
+ if ((domain = DKIMVerifyGetDomain(&ctxt)))
+ resDKIMSSP = checkSSP(domain, &bTestingPractices);
+ if (sCount > 0) {
+ if ((resDKIMSSP == DKIM_SSP_UNKNOWN || resDKIMSSP == DKIM_SSP_ALL))
+ ret = (sCount == sSize ? DKIM_SUCCESS : DKIM_PARTIAL_SUCCESS);
+ }
+ // if the SSP is testing, return neutral
+ if (bTestingPractices)
+ ret = DKIM_NEUTRAL;
+ /* if the message should be signed, return fail */
+ if (resDKIMSSP == DKIM_SSP_ALL || resDKIMSSP == DKIM_SSP_STRICT)
+ ret = DKIM_FAIL;
+ else
+ ret = DKIM_NEUTRAL;
+ }
+ }
+ DKIMVerifyFree(&ctxt);
+ writeHeaderNexit(ret, origRet, resDKIMSSP, resDKIMADSP, useSSP, useADSP);
+ } /*- if (dkimverify) */
+ }
+ if (pipe(pim) == -1)
+ die(59);
+ switch (pid = vfork())
+ {
+ case -1:
+ close(pim[0]);
+ close(pim[1]);
+ die(58);
+ case 0:
+ close(pim[1]);
+ if (fd_move(0, pim[0]) == -1)
+ die(120);
+ execv(*binqqargs, binqqargs);
+ die(120);
+ }
+ close(pim[0]);
+ substdio_fdbuf(&ssin, read, readfd, inbuf, sizeof(inbuf));
+ substdio_fdbuf(&ssout, write, pim[1], outbuf, sizeof(outbuf));
+ if (substdio_bput(&ssout, dkimoutput.s, dkimoutput.len) == -1) /*- write DKIM signature */
+ die_write();
+ switch (substdio_copy(&ssout, &ssin))
+ {
+ case -2:
+ die_read();
+ case -3:
+ die_write();
+ }
+ if (substdio_flush(&ssout) == -1)
+ die_write();
+ close(pim[1]);
+ if (wait_pid(&wstat, pid) != pid)
+ die(57);
+ if (wait_crashed(wstat))
+ die(57);
+ die(wait_exitcode(wstat));
+ /*- Not Reached */
+ exit(0);
+}
+
+void
+getversion_qmail_dkim_c()
+{
+ static char *x = "$Id: qmail-dkim.c,v 1.18 2009-04-16 13:48:32+05:30 Cprogrammer Exp mbhangui $";
+
+ x++;
+}
--- qmail-1.03.orig/qmail.h 2009-10-15 20:38:24.006353000 +0300
+++ qmail-1.03/qmail.h 2009-10-16 00:27:32.250352161 +0300
@@ -3,11 +3,13 @@
#include "substdio.h"
+#define CUSTOM_ERR_FD 2
struct qmail {
int flagerr;
unsigned long pid;
int fdm;
int fde;
+ int fdc;
substdio ss;
char buf[1024];
} ;
--- qmail-1.03.orig/qmail-lspawn.c 2009-10-15 20:38:23.890352000 +0300
+++ qmail-1.03/qmail-lspawn.c 2009-10-16 00:24:38.748439191 +0300
@@ -713,6 +713,7 @@
char *s; char *r; unsigned int at;
{
int f;
+ char *ptr;
if (!(f = fork()))
{
@@ -865,7 +866,10 @@
check_home(args[3], aliasempty);
#endif
- execv(*args,args);
+ if(!(ptr = env_get("QMAILLOCAL")))
+ execv(*args, args);
+ else
+ execv(ptr, args);
if (error_temp(errno)) _exit(QLX_EXECSOFT);
_exit(QLX_EXECHARD);
}
--- qmail-1.03.orig/qmail-rspawn.c 2009-10-15 20:38:23.946351000 +0300
+++ qmail-1.03/qmail-rspawn.c 2009-10-16 00:29:46.282544374 +0300
@@ -89,7 +89,7 @@
char *s; char *r; unsigned int at;
{
int f;
- char *(args[5]);
+ char *(args[5]), *ptr;
args[0] = (char *)"qmail-remote";
args[1] = r + at + 1;
@@ -102,7 +102,10 @@
if (fd_move(0,fdmess) == -1) _exit(111);
if (fd_move(1,fdout) == -1) _exit(111);
if (fd_copy(2,1) == -1) _exit(111);
- execvp(*args,args);
+ if(!(ptr = env_get("QMAILREMOTE")))
+ execvp(*args, args);
+ else
+ execvp(ptr, args);
if (error_temp(errno)) _exit(111);
_exit(100);
}
diff -Naur qmail-1.03.org/qregex.c qmail-1.03/qregex.c
--- qmail-1.03.org/qregex.c 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/qregex.c 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,239 @@
+/*
+ * $Log: qregex.c,v $
+ * Revision 1.13 2007-12-20 13:51:04+05:30 Cprogrammer
+ * removed compiler warning
+ *
+ * Revision 1.12 2005-08-23 17:41:36+05:30 Cprogrammer
+ * regex to be turned on only of QREGEX is defined to non-zero value
+ *
+ * Revision 1.11 2005-04-02 19:07:25+05:30 Cprogrammer
+ * use internal wildmat version
+ *
+ * Revision 1.10 2005-01-22 00:39:04+05:30 Cprogrammer
+ * added missing error handling
+ *
+ * Revision 1.9 2004-10-22 20:29:45+05:30 Cprogrammer
+ * added RCS id
+ *
+ * Revision 1.8 2004-09-21 23:48:18+05:30 Cprogrammer
+ * made matchregex() visible
+ * introduced dotChar (configurable dot char)
+ *
+ * Revision 1.7 2004-02-05 18:48:48+05:30 Cprogrammer
+ * changed curregex to static
+ *
+ * Revision 1.6 2003-12-23 23:22:53+05:30 Cprogrammer
+ * implicitly use wildcard if address starts with '@'
+ *
+ * Revision 1.5 2003-12-22 18:33:12+05:30 Cprogrammer
+ * added address_match()
+ *
+ * Revision 1.4 2003-12-22 13:21:08+05:30 Cprogrammer
+ * added text and pattern as part of error message
+ *
+ * Revision 1.3 2003-12-22 10:04:04+05:30 Cprogrammer
+ * conditional compilation of qregex
+ *
+ * Revision 1.2 2003-12-21 15:32:18+05:30 Cprogrammer
+ * added regerror
+ *
+ * Revision 1.1 2003-12-20 13:17:16+05:30 Cprogrammer
+ * Initial revision
+ *
+ * qregex (v2)
+ * $Id: qregex.c,v 1.13 2007-12-20 13:51:04+05:30 Cprogrammer Stab mbhangui $
+ *
+ * Author : Evan Borgstrom (evan at unixpimps dot org)
+ * Created : 2001/12/14 23:08:16
+ * Modified: $Date: 2007-12-20 13:51:04+05:30 $
+ * Revision: $Revision: 1.13 $
+ *
+ * Do POSIX regex matching on addresses for anti-relay / spam control.
+ * It logs to the maillog
+ * See the qregex-readme file included with this tarball.
+ * If you didn't get this file in a tarball please see the following URL:
+ * http://www.unixpimps.org/software/qregex
+ *
+ * qregex.c is released under a BSD style copyright.
+ * See http://www.unixpimps.org/software/qregex/copyright.html
+ *
+ * Note: this revision follows the coding guidelines set forth by the rest of
+ * the qmail code and that described at the following URL.
+ * http://cr.yp.to/qmail/guarantee.html
+ *
+ */
+#include "case.h"
+#include "scan.h"
+#include "stralloc.h"
+#include "constmap.h"
+#include "substdio.h"
+#include "byte.h"
+#include "env.h"
+#include <sys/types.h>
+#include <regex.h>
+#include <unistd.h>
+
+static int wildmat_match(stralloc *, int, struct constmap *, int, stralloc *);
+static int regex_match(stralloc *, int, stralloc *);
+int wildmat_internal(char *, char *);
+
+static char sserrbuf[512];
+static substdio sserr = SUBSTDIO_FDBUF(write, 2, sserrbuf, sizeof(sserrbuf));
+static char dotChar = '@';
+
+int
+address_match(stralloc *addr, int bhfok, stralloc *bhf,
+ struct constmap *mapbhf, int bhpok, stralloc *bhp)
+{
+ char *ptr;
+ int x = 0;
+
+ case_lowerb(addr->s, addr->len); /*- convert into lower case */
+ if ((ptr = env_get("QREGEX")))
+ scan_int(ptr, &x);
+ if (ptr && x)
+ return (regex_match(addr, bhfok, bhf));
+ else
+ return (wildmat_match(addr, bhfok, mapbhf, bhpok, bhp));
+}
+
+int
+matchregex(char *text, char *regex)
+{
+ regex_t qreg;
+ char errbuf[512];
+ int retval = 0;
+
+#define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
+ /*- build the regex */
+ if ((retval = REGCOMP(qreg, regex)) != 0)
+ {
+ regerror(retval, &qreg, errbuf, sizeof(errbuf));
+ regfree(&qreg);
+ if (substdio_puts(&sserr, text) == -1)
+ return (-retval);
+ if (substdio_puts(&sserr, ": ") == -1)
+ return (-retval);
+ if (substdio_puts(&sserr, regex) == -1)
+ return (-retval);
+ if (substdio_puts(&sserr, ": ") == -1)
+ return (-retval);
+ if (substdio_puts(&sserr, errbuf) == -1)
+ return (-retval);
+ if (substdio_puts(&sserr, "\n") == -1)
+ return (-retval);
+ if (substdio_flush(&sserr) == -1)
+ return (-retval);
+ return (-retval);
+ }
+ /*- execute the regex */
+#define REGEXEC(X,Y) regexec(&X, Y, (size_t) 0, (regmatch_t *) 0, (int) 0)
+ retval = REGEXEC(qreg, text);
+ regfree(&qreg);
+ return (retval == REG_NOMATCH ? 0 : 1);
+}
+
+static int
+wildmat_match(stralloc * addr, int mapfile, struct constmap *ptrmap, int patfile, stralloc *wildcard)
+{
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ char subvalue;
+
+ if (mapfile)
+ {
+ if (constmap(ptrmap, addr->s, addr->len - 1))
+ return 1;
+ if ((j = byte_rchr(addr->s, addr->len, dotChar)) < addr->len)
+ {
+ if (constmap(ptrmap, addr->s + j, addr->len - j - 1))
+ return 1;
+ }
+ }
+ /*- Include control file control/xxxxpatterns and evaluate with Wildmat check */
+ if (patfile && wildcard)
+ {
+ i = 0;
+ for (j = 0; j < wildcard->len; ++j)
+ {
+ if (!wildcard->s[j])
+ {
+ subvalue = wildcard->s[i] != '!';
+ if (!subvalue)
+ i++;
+ if ((k != subvalue) && wildmat_internal(addr->s, wildcard->s + i))
+ k = subvalue;
+ i = j + 1;
+ }
+ }
+ return k;
+ }
+ return (0);
+}
+
+static int
+regex_match(stralloc * addr, int mapfile, stralloc *map)
+{
+ int i = 0;
+ int j = 0;
+ int k = 0;
+ int negate = 0, match;
+ static stralloc curregex = { 0 };
+
+ match = 0;
+ if (mapfile)
+ {
+ while (j < map->len)
+ {
+ i = j;
+ while ((map->s[i] != '\0') && (i < map->len))
+ i++;
+ if (map->s[j] == '!')
+ {
+ negate = 1;
+ j++;
+ }
+ if (*(map->s + j) == dotChar)
+ {
+ if (!stralloc_copys(&curregex, ".*"))
+ return(-1);
+ if (!stralloc_catb(&curregex, map->s + j, (i - j)))
+ return(-1);
+ } else
+ if (!stralloc_copyb(&curregex, map->s + j, (i - j)))
+ return(-1);
+ if (!stralloc_0(&curregex))
+ return(-1);
+ if((k = matchregex(addr->s, curregex.s)) == 1)
+ {
+ if (negate)
+ return(0);
+ match = 1;
+ }
+ j = i + 1;
+ negate = 0;
+ }
+ }
+ return (match);
+}
+
+void
+setdotChar(c)
+ char c;
+{
+ dotChar = c;
+ return;
+}
+
+void
+getversion_qregex_c()
+{
+ static char *x = "$Id: qregex.c,v 1.13 2007-12-20 13:51:04+05:30 Cprogrammer Stab mbhangui $";
+
+#ifdef INDIMAIL
+ x = sccsidh;
+#else
+ x++;
+#endif
+}
diff -Naur qmail-1.03.org/qregex.h qmail-1.03/qregex.h
--- qmail-1.03.org/qregex.h 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/qregex.h 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,24 @@
+/*
+ * $Log: qregex.h,v $
+ * Revision 1.3 2004-09-21 23:49:02+05:30 Cprogrammer
+ * added matchregex() and setdotChar()
+ *
+ * Revision 1.2 2003-12-22 18:35:26+05:30 Cprogrammer
+ * added address_match() function
+ *
+ * Revision 1.1 2003-12-20 13:17:45+05:30 Cprogrammer
+ * Initial revision
+ *
+ */
+/*
+ * simple header file for the matchregex prototype
+ */
+#ifndef _QREGEX_H_
+#define _QREGEX_H_
+#include "constmap.h"
+#include "stralloc.h"
+
+int address_match(stralloc *, int, stralloc *, struct constmap *, int, stralloc *);
+int matchregex(char *, char *);
+void setdotChar(char);
+#endif
--- qmail-1.03.orig/scan_ulong.c 2009-10-15 20:38:24.150352000 +0300
+++ qmail-1.03/scan_ulong.c 2009-10-16 00:40:58.551137909 +0300
@@ -9,3 +9,43 @@
{ result = result * 10 + c; ++pos; }
*u = result; return pos;
}
+
+unsigned int
+scan_int(s, i)
+ register char *s;
+ register int *i;
+{
+ register unsigned int pos;
+ register int result;
+ register unsigned char c;
+ int sign;
+
+ pos = 0;
+ result = 0;
+ sign = 1;
+ /*-
+ * determine sign of the number
+ */
+ switch (s[0])
+ {
+ case '\0':
+ return 0;
+ case '-':
+ ++pos;
+ sign = -1;
+ break;
+ case '+':
+ ++pos;
+ sign = 1;
+ break;
+ default:
+ break;
+ }
+ while ((c = (unsigned char)(s[pos] - '0')) < 10)
+ {
+ result = result * 10 + c;
+ ++pos;
+ }
+ *i = result * sign;
+ return pos;
+}
diff -Naur qmail-1.03.org/softwarelicense1-1.html qmail-1.03/softwarelicense1-1.html
--- qmail-1.03.org/softwarelicense1-1.html 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/softwarelicense1-1.html 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,59 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
+<html>
+<head>
+<title>Yahoo! DomainKeys Public License Agreement v1.0</title>
+<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
+</head>
+
+<body>
+Yahoo! DomainKeys Public License Agreement v1.1<br>
+(this &quot;Agreement&quot;)
+<p>Copyright (c) 2004, Yahoo! Inc.<br>
+ All rights reserved.</p>
+
+<br>
+<a href=http://domainkeys.sourceforge.net/license/softwarelicense1-1.html>(Available online)</a>
+<br>
+
+
+<p>This Agreement is between Licensor and You. You agree to be bound by all the terms and conditions set forth below, and, subject to those terms and conditions, You may use the intellectual property described below. </p>
+<p>1. LICENSE GRANT. </p>
+<p>1.1. Subject to the terms and conditions of this Agreement, each DomainKeys Developer hereby grants You a royalty-free, perpetual, worldwide, sublicensable, non-exclusive license to use, reproduce, modify, publicly display, publicly perform, and distribute the Licensed Code.</p>
+<p>1.2. Subject to the terms and conditions of this Agreement, Licensor hereby grants You a royalty-free, perpetual, worldwide, sublicensable, non-exclusive license under its rights to the Yahoo! Patent Claims to make, use, sell, offer for sale, and/or import the Licensed Code for the sole purpose of implementing a sender verification solution in connection with e-mail. </p>
+<p>2. DEFINITIONS. </p>
+<p>2.1. &quot;Contributions&quot; means any modifications to the Licensed Code, and/or any portions thereof, that are distributed under this Agreement. A Contribution includes, without limitation, any addition to or deletion from the contents of a file containing any Licensed Code, or any new file that contains any part of the Licensed Code.</p>
+<p>2.2. &quot;DomainKeys Developer(s)&quot; means Yahoo, Inc. (&quot;Yahoo!&quot;), Licensor, and/or any other individual or entity who distributes code under this Agreement.</p>
+<p>2.3. &quot;Licensed Code&quot; means the Original Code, any Contributions (whether made by You or any DomainKeys Developer other than You), and the combination of Original Code and any such Contributions.</p>
+<p>2.4. &quot;Licensor&quot; means Yahoo! or any other individual or entity that elects to use this Agreement to license intellectual property to any licensee.</p>
+<p>2.5. &quot;Original Code&quot; means the source code and binary code that is based on the Specifications and distributed by or on behalf of Yahoo! under this Agreement for the sole purpose of implementing a sender verification solution in connection with e-mail, including any updates or upgrades to such code made available by Yahoo!.</p>
+<p>2.6. &quot;Specifications&quot; means the specification having submission ID &quot;draft-delany-domainkeys-base-01.txt&quot; dated Aug 2004 published through the IETF (Internet Engineering Task Force). The Specifications may be found at the following link: <br>
+<a href="http://antispam.yahoo.com/domainkeys/draft-delany-domainkeys-base-02.txt">http://antispam.yahoo.com/domainkeys/draft-delany-domainkeys-base-02.txt</a></p>
+<p>
+ 2.7. &quot;Yahoo! Patent Claims&quot; shall mean those claims of all Yahoo! foreign and domestic patents and patent applications that base their priority on U.S. Provisional Patent Application Ser. Nos. 60/497,794, filed Aug. 26, 2003, or 60/553,300, filed Mar. 15, 2004, or U.S. Patent Application Ser. Nos. 10/671,319, filed Sep. 24, 2003, or 10/805,181, filed Mar. 19, 2004. </p>
+<p>2.8. &quot;You&quot; or &quot;Your&quot; means an individual, company, or other legal entity exercising any rights under this Agreement. Any individual who accepts the terms and conditions of this Agreement on behalf of a company or other legal entity represents and warrants that the individual has the authority to enter into this Agreement on behalf of the company or other legal entity. </p>
+<p>3. TERMS. </p>
+<p>3.1. You agree not to assert against Yahoo!, any other DomainKeys Developer or any of their respective licensees under Section 3.4, a patent infringement claim based on the manufacture, use, sale, offer for sale and/or importation of any of the specific portions of a hardware or software implementation expressly required to be compliant with the Specifications for the sole purpose of implementing a sender verification solution in connection with e-mail (&quot;Licensed Code IP Claim&quot;).</p>
+<p>3.2. To indicate your assent to the terms and conditions of this Agreement and in order to obtain a license to use, reproduce, modify, publicly display, publicly perform, distribute, and sublicense Licensed Code, You must:</p>
+<p>(a) include, attach or preserve the following prominently displayed statement in the Licensed Code: &quot;This code incorporates intellectual property owned by Yahoo! and licensed pursuant to the Yahoo! DomainKeys Public License Agreement.&quot;; </p>
+<p>(b) preserve the copyright and other proprietary notices and disclaimers of DomainKeys Developers as they appear in the Licensed Code; and </p>
+<p>(c) if the Licensed Code developed by You is distributed in source form, You must identify Yourself, in the source code of such Licensed Code, as the originator of any modifications in a manner that reasonably allows subsequent DomainKeys Developers or their licensees to identify the originator of the modifications.</p>
+<p>3.3. You will not use the name of Yahoo! to endorse or promote any products, services, or Licensed Code without specific prior written permission of Yahoo!. &quot;DomainKeys&quot; is a trademark of Yahoo!. However, You may state Your Licensed Code is &quot;DomainKeys compliant&quot;, &quot;supports DomainKeys&quot;, or is &quot;DomainKeys-enabled&quot;, without citation to Yahoo!. You must create Your own product or service names or trademarks for Your Licensed Code and You agree not to use the term &quot;DomainKeys&quot; in or as part of a name or trademark for Your Licensed Code.</p>
+<p>3.4. You may choose to distribute Licensed Code or modifications under this Agreement or a different agreement, provided that:</p>
+<p>(a) a copy of this Agreement or the different agreement is included with each copy of the Licensed Code or modifications along with the following prominently displayed statement: &quot;By using, reproducing, modifying, publicly displaying, publicly performing, distributing, and/or sublicensing this code as permitted, you agree to the terms and conditions of the Yahoo! DomainKeys Public License Agreement or other agreement contained herein.&quot;; and</p>
+<p>(b) if distributed under a different agreement, such different agreement contains terms and conditions that (i) provide no fewer rights, privileges and immunities to DomainKeys Developers than the terms and conditions of this Agreement, including, without limitation, Sections 1.2, 3.1, 3.4, 3.7, 4.1, 4.2, and 4.3, except that You may alter the terms and conditions of Section 1.1 and (ii) apply such terms and conditions to the Licensed Code and/or modifications made by You.</p>
+<p>3.5. You acknowledge that Licensed Code may be subject to U.S. export restriction and other applicable national and international laws. You agree to comply with all export, re-export, or import restrictions, laws, or regulations. </p>
+<p>3.6. Yahoo!, and only Yahoo!, may, from time to time and at its sole discretion, update or modify the terms of this Agreement. If Yahoo! makes any such modifications, You may continue under the terms and conditions of this Agreement or agree to the updated or modified terms and conditions. For the most recent version of this Agreement please contact Yahoo!.</p>
+<p>3.7. This Agreement and the rights hereunder will terminate: <br>
+ (a) automatically without notice from Yahoo!, if You at any time during the term of this Agreement assert any Licensed Code IP Claim against Yahoo!; </p>
+<p>(b) upon written notice from Yahoo!, if You at any time during the term of this Agreement assert any Licensed Code IP Claim against any DomainKeys Developer (other than Yahoo!) or any licensees of any DomainKeys Developer; or </p>
+<p>(c) where (a) or (b) do not apply, automatically without notice from Yahoo!, if You fail to comply with any term(s) of this Agreement and fail to cure such breach within 30 days of You becoming aware of such breach. </p>
+<p>3.8. This Agreement constitutes the entire agreement between the parties with respect to the subject matter hereof. This Agreement shall be governed by and construed under the laws of the United States and the State of California without giving effect to California conflict of law provisions or to construction provisions favoring either party. All actions arising out of or relating to this Agreement that involve Yahoo! as a party will be adjudicated exclusively by the Superior Court of the State of California for the County of Santa Clara or the United States District Court for the Northern District of California. </p>
+<p>3.9. In the event that any provision of this Agreement is deemed to be invalid, such invalidity shall not affect the remainder of this Agreement.</p>
+<p>4. <b>LEGAL DISCLAIMERS. </b></p>
+<p>4.1. <b>THE YAHOO! PATENT CLAIMS, THIS AGREEMENT, LICENSED CODE, THE DOMAINKEYS TRADEMARK, AND THE SPECIFICATIONS ARE PROVIDED ON AN &quot;AS IS&quot; BASIS, WITHOUT REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY REPRESENTATIONS, WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.</b> You are solely responsible for determining the appropriateness of exercising any rights under this Agreement and using the Specifications, Licensed Code, and the DomainKeys trademark and assume all risks associated in connection therewith, including, but not limited to, the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. </p>
+<p>4.2. You expressly acknowledge and agree that no assurances are provided by DomainKeys Developers with respect to the validity of the Yahoo! Patent Claims or that the Specifications, Licensed Code, the DomainKeys trademark or any implementations related to the Specifications, Licensed Code or the DomainKeys trademark do not infringe or misappropriate the patent, trademark or other intellectual property rights of any other entity. DomainKeys Developers disclaim any liability to You for claims brought by any other person or entity based on infringement or misappropriation of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, You hereby assume sole responsibility to secure any other intellectual property rights needed. </p>
+<p>4.3. <b>DOMAINKEYS DEVELOPERS SHALL NOT HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE EXERCISE OF ANY RIGHTS UNDER THIS AGREEMENT, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES, AND EVEN IF THE REMEDIES PROVIDED FOR IN THIS AGREEMENT FAIL OF THEIR ESSENTIAL PURPOSE.</b></p>
+
+</body>
+</html>
+
diff -Naur qmail-1.03.org/spawn-filter.9 qmail-1.03/spawn-filter.9
--- qmail-1.03.org/spawn-filter.9 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/spawn-filter.9 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,103 @@
+.TH spawn-filter 8
+.SH NAME
+spawn-filter \- Helper for running filters for qmail-local and qmail-remote
+.SH SYNOPSIS
+.B spawn-filter args
+.SH DESCRIPTION
+.B spawn-filter
+is a utility to help qmail run any filter during local or remote delivery. It
+can run any filter which expects to read mess on fd 0 and writes back the message on fd 1.
+The filter can be turned on individually for local and remote mails by defining
+.B QMAILLOCAL
+and
+.B QMAILREMOTE
+environment variables respectively in
+.B qmail-send
+supervise or rc script. If spawn-filter is invoked as qmail-local, it executes the
+original
+.B qmail-local
+after runing the mail through the filter. If spawn-filter is invoked as qmail-remote, it
+executes the original
+.B qmail-remote
+after running the mail through the filter. Hence QMAILLOCAL should be set as QMAILHOME/bin/spawn-filter
+for filtering local mails and QMAILREMOTE as QMAILHOME/bin/spawn-filter for filtering
+remote mails.
+
+Filters can be run by setting the environment variable
+.B FILTERARGS
+or by using a control file
+.BR filterargs.
+The environment variable overrides the control file.
+.B spawn-filter
+uses /bin/sh to run the filter (with arguments) specified by the FILTERARGS environment variable or the control file
+.BR filterargs .
+The environment variable FILTERARGS apply to both local and remote mails. For individual domain level control,
+it is best to set using the control file filterargs.
+
+.TP 5
+.I filterargs
+The format of this file is of the form
+.B domain:args
+for both local and remote mails.
+.B domain:remote:args
+for remote mails and
+.B domain:local:args
+for local mails.
+
+.EX
+indimail.org:remote:QMAILHOME/bin/dk-filter
+.EE
+
+.TP 0
+The sequence in which the filter program is run is given below
+
+.TP 5
+1. create two pipes and fork
+.TP 5
+2. dup write end of the first pipe to descriptor 1, dup write end of the second pipe to descriptor 2 in the child and exec the filter program
+.TP 5
+3. dup read end of the pipe to descriptor 0 in parent and exec qmail-local for local mails and qmail-remote for remote mails.
+.TP 5
+4. Wait for filter to exit and read read end of second pipe for any error messages.
+.TP 5
+5. Report success or failure
+.TP 0
+
+This gives the ability for the any filter program to read the mail message from descriptor 0 before
+passing it to qmail-local/qmail-remote through the pipe.
+
+.B spawn-filter
+will attempt to make the descriptor 0 seekable if the environment variable MAKE_SEEKABLE
+is defined. This may be necessary for certain filter programs which could do lseek().
+
+.B spawn-filter
+sets the environment variable
+.B DOMAIN
+to the recipient domain. This can be conveniently used in programs/scripts which get invoked by
+setting
+.B FILTERARGS
+environment variable or by rules in the control file
+.BR filterargs .
+
+.SH "EXIT CODES"
+.B spawn-filter
+exits 111 for any error or if it is not able to exec
+QMAILHOME/bin/qmail-local (for local mails) or
+QMAILHOME/bin/qmail-remote (for remote mails).
+
+.SH "SEE ALSO"
+qmail-lspawn(8),
+qmail-rspawn(8),
+qmail-local(8),
+qmail-remote(8),
+qmail-smtpd(8),
+qmail-control(5),
+qmail-queue(8)
+
+.SH "AUTHORS"
+
+Manvendra Bhangui.
+.SH PROBLEMS
+Problems with
+.B spawn-filter
+should be forwarded to "Manvendra Bhangui" <mbhangui@gmail.com>
diff -Naur qmail-1.03.org/spawn-filter.c qmail-1.03/spawn-filter.c
--- qmail-1.03.org/spawn-filter.c 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/spawn-filter.c 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,456 @@
+/*
+ * qmail-version without spam filter
+ *
+ * $Log: spawn-filter.c,v $
+ * Revision 1.41 2009-04-03 11:42:48+05:30 Cprogrammer
+ * create pipe for error messages
+ *
+ * Revision 1.40 2009-04-02 15:17:54+05:30 Cprogrammer
+ * unset QMAILLOCAL in qmail-remote and unset QMAILREMOTE in qmail-local
+ *
+ * Revision 1.39 2008-06-12 08:40:55+05:30 Cprogrammer
+ * added rulesfile argument
+ *
+ * Revision 1.38 2008-05-25 17:16:43+05:30 Cprogrammer
+ * made message more readable by adding a blank space
+ *
+ * Revision 1.37 2007-12-20 13:51:54+05:30 Cprogrammer
+ * avoid loops with FILTERARGS, SPAMFILTERARGS
+ * removed compiler warning
+ *
+ * Revision 1.36 2006-06-07 14:11:28+05:30 Cprogrammer
+ * added SPAMEXT, SPAMHOST, SPAMSENDER, QQEH environment variable
+ * unset FILTERARGS before calling filters
+ *
+ * Revision 1.35 2006-01-22 10:14:45+05:30 Cprogrammer
+ * BUG fix for spam mails wrongly getting blackholed
+ *
+ * Revision 1.34 2005-08-23 17:36:48+05:30 Cprogrammer
+ * gcc 4 compliance
+ * delete sender in spam notification
+ *
+ * Revision 1.33 2005-04-02 19:07:47+05:30 Cprogrammer
+ * use internal wildmat version
+ *
+ * Revision 1.32 2004-11-22 19:50:53+05:30 Cprogrammer
+ * include regex.h after sys/types.h to avoid compilation prob on RH 7.3
+ *
+ * Revision 1.31 2004-10-22 20:30:35+05:30 Cprogrammer
+ * added RCS id
+ *
+ * Revision 1.30 2004-10-21 21:56:21+05:30 Cprogrammer
+ * change for two additional arguments to strerr_die()
+ *
+ * Revision 1.29 2004-10-11 14:06:14+05:30 Cprogrammer
+ * use control_readulong instead of control_readint
+ *
+ * Revision 1.28 2004-09-22 23:14:20+05:30 Cprogrammer
+ * replaced atoi() with scan_int()
+ *
+ * Revision 1.27 2004-09-08 10:54:49+05:30 Cprogrammer
+ * incorrect exit code in report() function for remote
+ * mails. Caused qmail-rspawn to report "Unable to run qmail-remote"
+ *
+ * Revision 1.26 2004-07-17 21:23:31+05:30 Cprogrammer
+ * change qqeh code in qmail-remote
+ *
+ * Revision 1.25 2004-07-15 23:40:46+05:30 Cprogrammer
+ * fixed compilation warning
+ *
+ * Revision 1.24 2004-07-02 16:15:25+05:30 Cprogrammer
+ * override control files rejectspam, spamredirect by
+ * environment variables REJECTSPAM and SPAMREDIRECT
+ * allow patterns in domain specification in the control files
+ * spamfilterargs, filterargs, rejectspam and spamredirect
+ *
+ * Revision 1.23 2004-06-03 22:58:34+05:30 Cprogrammer
+ * fixed compilation problem without indimail
+ *
+ * Revision 1.22 2004-05-23 22:18:17+05:30 Cprogrammer
+ * added envrules filename as argument
+ *
+ * Revision 1.21 2004-05-19 23:15:07+05:30 Cprogrammer
+ * added comments
+ *
+ * Revision 1.20 2004-05-12 22:37:47+05:30 Cprogrammer
+ * added check DATALIMIT check
+ *
+ * Revision 1.19 2004-05-03 22:17:36+05:30 Cprogrammer
+ * use QUEUE_BASE instead of auto_qmail
+ *
+ * Revision 1.18 2004-02-13 14:51:24+05:30 Cprogrammer
+ * added envrules
+ *
+ * Revision 1.17 2004-01-20 06:56:56+05:30 Cprogrammer
+ * unset FILTERARGS for notifications
+ *
+ * Revision 1.16 2004-01-20 01:52:08+05:30 Cprogrammer
+ * report string length corrected
+ *
+ * Revision 1.15 2004-01-10 09:44:36+05:30 Cprogrammer
+ * added comment for exit codes of bogofilter
+ *
+ * Revision 1.14 2004-01-08 00:32:49+05:30 Cprogrammer
+ * use TMPDIR environment variable for temporary directory
+ * send spam reports to central spam logger
+ *
+ * Revision 1.13 2003-12-30 00:44:42+05:30 Cprogrammer
+ * set argv[0] from spamfilterprog
+ *
+ * Revision 1.12 2003-12-22 18:34:25+05:30 Cprogrammer
+ * replaced spfcheck() with address_match()
+ *
+ * Revision 1.11 2003-12-20 01:35:06+05:30 Cprogrammer
+ * added wait_pid to prevent zombies
+ *
+ * Revision 1.10 2003-12-17 23:33:39+05:30 Cprogrammer
+ * improved logic for getting remote/local tokens
+ *
+ * Revision 1.9 2003-12-16 10:38:24+05:30 Cprogrammer
+ * fixed incorrect address being returned if filterargs contained local: or
+ * remote: directives
+ *
+ * Revision 1.8 2003-12-15 20:46:19+05:30 Cprogrammer
+ * added case 100 to bounce mail
+ *
+ * Revision 1.7 2003-12-15 13:51:44+05:30 Cprogrammer
+ * code to run additional filters using /bin/sh
+ *
+ * Revision 1.6 2003-12-14 11:36:18+05:30 Cprogrammer
+ * added option to blackhole spammers
+ *
+ * Revision 1.5 2003-12-13 21:08:46+05:30 Cprogrammer
+ * extensive rewrite
+ * common report() function for local/remote mails to report errors
+ *
+ * Revision 1.4 2003-12-12 20:20:55+05:30 Cprogrammer
+ * use -a option to prevent using header addresses
+ *
+ * Revision 1.3 2003-12-09 23:37:16+05:30 Cprogrammer
+ * change for spawn-filter to be called as qmail-local or qmail-remote
+ *
+ * Revision 1.2 2003-12-08 23:48:23+05:30 Cprogrammer
+ * new function getDomainToken() to retrieve domain specific values
+ * read rejectspam and spamredirect only if SPAMEXITCODE is set
+ *
+ * Revision 1.1 2003-12-07 13:02:00+05:30 Cprogrammer
+ * Initial revision
+ *
+ */
+#include "fmt.h"
+#include "str.h"
+#include "strerr.h"
+#include "env.h"
+#include "substdio.h"
+#include "subfd.h"
+#include "stralloc.h"
+#include "error.h"
+#include "control.h"
+#include "wait.h"
+#include "qregex.h"
+#include <regex.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#define REGCOMP(X,Y) regcomp(&X, Y, REG_EXTENDED|REG_ICASE)
+#define REGEXEC(X,Y) regexec(&X, Y, (size_t) 0, (regmatch_t *) 0, (int) 0)
+
+static int mkTempFile(int);
+static void report(int, char *, char *, char *, char *, char *, char *);
+char *getDomainToken(char *, stralloc *);
+static int run_mailfilter(char *, char *, char **);
+int wildmat_internal(char *, char *);
+
+static int remotE;
+
+static void
+report(int errCode, char *s1, char *s2, char *s3, char *s4, char *s5, char *s6)
+{
+ if (!remotE) /*- strerr_die does not return */
+ strerr_die(errCode, s1, s2, s3, s4, s5, s6, 0, 0, (struct strerr *) 0);
+ /*- h - hard, s - soft */
+ if (substdio_put(subfdoutsmall, errCode == 111 ? "s" : "h", 1) == -1)
+ _exit(111);
+ if (s1 && substdio_puts(subfdoutsmall, s1) == -1)
+ _exit(111);
+ if (s2 && substdio_puts(subfdoutsmall, s2) == -1)
+ _exit(111);
+ if (s3 && substdio_puts(subfdoutsmall, s3) == -1)
+ _exit(111);
+ if (s4 && substdio_puts(subfdoutsmall, s4) == -1)
+ _exit(111);
+ if (s5 && substdio_puts(subfdoutsmall, s5) == -1)
+ _exit(111);
+ if (s6 && substdio_puts(subfdoutsmall, s6) == -1)
+ _exit(111);
+ if (substdio_put(subfdoutsmall, "\0", 1) == -1)
+ _exit(111);
+ if (substdio_puts(subfdoutsmall,
+ errCode == 111 ? "Zspawn-filter said: Message deferred" : "DGiving up on spawn-filter\n") == -1)
+ _exit(111);
+ if (substdio_put(subfdoutsmall, "\0", 1) == -1)
+ _exit(111);
+ substdio_flush(subfdoutsmall);
+ /*- For qmail-rspawn to stop complaining unable to run qmail-remote */
+ _exit(0);
+}
+
+static int
+run_mailfilter(char *domain, char *mailprog, char **argv)
+{
+ char strnum[FMT_ULONG];
+ pid_t filt_pid;
+ int pipefd[2], pipefe[2];
+ int wstat, filt_exitcode, len = 0;
+ char *filterargs;
+ static stralloc filterdefs = { 0 };
+ static char errstr[1024];
+ char inbuf[1024];
+ char ch;
+ static substdio errbuf;
+
+ if (!(filterargs = env_get("FILTERARGS")))
+ {
+ if (control_readfile(&filterdefs, "control/filterargs", 0) == -1)
+ report(111, "spawn-filter: Unable to read filterargs: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ filterargs = getDomainToken(domain, &filterdefs);
+ }
+ if (!filterargs)
+ {
+ execv(mailprog, argv);
+ report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
+ _exit(111); /*- To make compiler happy */
+ }
+ if (pipe(pipefd) == -1)
+ report(111, "spawn-filter: Trouble creating pipes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if (pipe(pipefe) == -1)
+ report(111, "spawn-filter: Trouble creating pipes: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ switch ((filt_pid = fork()))
+ {
+ case -1:
+ report(111, "spawn-filter: Trouble creating child filter: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ case 0: /*- Filter Program */
+ if (!env_put2("DOMAIN", domain))
+ report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ /*- Mail content read from fd 0 */
+ if (mkTempFile(0))
+ report(111, "spawn-filter: lseek error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ /*- stdout will go here */
+ if (dup2(pipefd[1], 1) == -1 || close(pipefd[0]) == -1)
+ report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if (pipefd[1] != 1)
+ close(pipefd[1]);
+ /*- stderr will go here */
+ if (dup2(pipefe[1], 2) == -1 || close(pipefe[0]) == -1)
+ report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if (pipefe[1] != 2)
+ close(pipefe[1]);
+ /*- Avoid loop if program(s) defined by FILTERARGS call qmail-inject, etc */
+ if (!env_unset("FILTERARGS") || !env_unset("SPAMFILTER"))
+ report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ execl("/bin/sh", "IndiMailfilter", "-c", filterargs, (char *) 0);
+ report(111, "spawn-filter: could not exec /bin/sh: ", filterargs, ": ", error_str(errno), ". (#4.3.0)", 0);
+ default:
+ close(pipefe[1]);
+ close(pipefd[1]);
+ if (dup2(pipefd[0], 0))
+ {
+ close(pipefd[0]);
+ close(pipefe[0]);
+ wait_pid(&wstat, filt_pid);
+ report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ }
+ if (pipefd[0] != 0)
+ close(pipefd[0]);
+ if (mkTempFile(0))
+ {
+ close(0);
+ close(pipefe[0]);
+ wait_pid(&wstat, filt_pid);
+ report(111, "spawn-filter: lseek error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ }
+ break;
+ }
+ /*- Process message if exit code is 0, bounce if 100 */
+ if (wait_pid(&wstat, filt_pid) != filt_pid)
+ {
+ close(0);
+ close(pipefe[0]);
+ report(111, "spawn-filter: waitpid surprise: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ }
+ if (wait_crashed(wstat))
+ {
+ close(0);
+ close(pipefe[0]);
+ report(111, "spawn-filter: filter crashed: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ }
+ switch (filt_exitcode = wait_exitcode(wstat))
+ {
+ case 0:
+ execv(mailprog, argv);
+ report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
+ case 100:
+ report(100, "Mail Rejected (#5.7.1)", 0, 0, 0, 0, 0);
+ default:
+ substdio_fdbuf(&errbuf, read, pipefe[0], inbuf, sizeof(inbuf));
+ for (len = 0; substdio_bget(&errbuf, &ch, 1) && len < (sizeof(errstr) - 1); len++)
+ errstr[len] = ch;
+ errstr[len] = 0;
+ strnum[fmt_ulong(strnum, filt_exitcode)] = 0;
+ report(111, filterargs, ": (spawn-filter) exit code: ", strnum, *errstr ? ": " : 0, *errstr ? errstr : 0, ". (#4.3.0)");
+ }
+ /*- Not reached */
+ return(111);
+}
+
+char *
+getDomainToken(char *domain, stralloc *sa)
+{
+ regex_t qreg;
+ int len, n, retval;
+ char *ptr, *p;
+ char errbuf[512];
+
+ for (len = 0, ptr = sa->s;len < sa->len;)
+ {
+ len += ((n = str_len(ptr)) + 1);
+ for (p = ptr;*p && *p != ':';p++);
+ if (*p == ':')
+ {
+ *p = 0;
+ /*- build the regex */
+ if ((retval = str_diff(ptr, domain)))
+ {
+ if (env_get("QREGEX"))
+ {
+ if ((retval = REGCOMP(qreg, ptr)) != 0)
+ {
+ regerror(retval, &qreg, errbuf, sizeof(errbuf));
+ regfree(&qreg);
+ report(111, "spawn-filter: ", ptr, ": ", errbuf, ". (#4.3.0)", 0);
+ }
+ retval = REGEXEC(qreg, domain);
+ regfree(&qreg);
+ } else
+ retval = !wildmat_internal(domain, ptr);
+ }
+ if (!retval)
+ {
+ *p = ':';
+ /* check for local/remote directives */
+ if (remotE)
+ {
+ if (!str_diffn(p + 1, "remote:", 7))
+ return (p + 8);
+ if (!str_diffn(p + 1, "local:", 6))
+ return((char *) 0);
+ } else
+ {
+ if (!str_diffn(p + 1, "remote:", 7))
+ return((char *) 0);
+ if (!str_diffn(p + 1, "local:", 6))
+ return (p + 7);
+ }
+ return (p + 1);
+ }
+ *p = ':';
+ }
+ ptr = sa->s + len;
+ }
+ return ((char *) 0);
+}
+
+int
+mkTempFile(int seekfd)
+{
+ char inbuf[2048], outbuf[2048], strnum[FMT_ULONG];
+ char *tmpdir;
+ static stralloc tmpFile = {0};
+ struct substdio _ssin;
+ struct substdio _ssout;
+ int fd;
+
+ if (lseek(seekfd, 0, SEEK_SET) == 0)
+ return (0);
+ if (errno == EBADF)
+ {
+ strnum[fmt_ulong(strnum, seekfd)] = 0;
+ report(111, "spawn-filter: fd ", strnum, ": ", error_str(errno), ". (#4.3.0)", 0);
+ }
+ if (!(tmpdir = env_get("TMPDIR")))
+ tmpdir = "/tmp";
+ if (!stralloc_copys(&tmpFile, tmpdir))
+ report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if (!stralloc_cats(&tmpFile, "/qmailFilterXXX"))
+ report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if (!stralloc_catb(&tmpFile, strnum, fmt_ulong(strnum, (unsigned long) getpid())))
+ report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if (!stralloc_0(&tmpFile))
+ report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if ((fd = open(tmpFile.s, O_RDWR | O_EXCL | O_CREAT, 0600)) == -1)
+ report(111, "spawn-filter: ", tmpFile.s, ": ", error_str(errno), ". (#4.3.0)", 0);
+ unlink(tmpFile.s);
+ substdio_fdbuf(&_ssout, write, fd, outbuf, sizeof(outbuf));
+ substdio_fdbuf(&_ssin, read, seekfd, inbuf, sizeof(inbuf));
+ switch (substdio_copy(&_ssout, &_ssin))
+ {
+ case -2: /*- read error */
+ report(111, "spawn-filter: read error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ case -3: /*- write error */
+ report(111, "spawn-filter: write error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ }
+ if (substdio_flush(&_ssout) == -1)
+ report(111, "spawn-filter: write error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if (dup2(fd, seekfd) == -1)
+ report(111, "spawn-filter: dup2 error: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ if (lseek(seekfd, 0, SEEK_SET) != 0)
+ report(111, "spawn-filter: lseek: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ return (0);
+}
+int
+main(int argc, char **argv)
+{
+ char *ptr, *mailprog, *domain;
+ int len;
+
+ len = str_len(argv[0]);
+ for (ptr = argv[0] + len;*ptr != '/' && ptr != argv[0];ptr--);
+ if (*ptr && *ptr == '/')
+ ptr++;
+ ptr += 6;
+ if (*ptr == 'l') /*- qmail-local Filter */
+ {
+ mailprog = "bin/qmail-local";
+ remotE = 0;
+ domain = argv[7];
+ if (!env_unset("QMAILREMOTE"))
+ report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ run_mailfilter(domain, mailprog, argv);
+ report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
+ } else
+ if (*ptr == 'r') /*- qmail-remote Filter */
+ {
+ mailprog = "bin/qmail-remote";
+ remotE = 1;
+ domain = argv[1];
+ if (!env_unset("QMAILLOCAL"))
+ report(111, "spawn-filter: out of mem: ", error_str(errno), ". (#4.3.0)", 0, 0, 0);
+ run_mailfilter(domain, mailprog, argv);
+ report(111, "spawn-filter: could not exec ", mailprog, ": ", error_str(errno), ". (#4.3.0)", 0);
+ } else
+ {
+ report(111, "spawn-filter: Incorrect usage. ", argv[0], " (#4.3.0)", 0, 0, 0);
+ _exit(111);
+ }
+ _exit(111);
+ /*- Not reached */
+ return(0);
+}
+
+void
+getversion_qmail_spawn_filter_c()
+{
+ static char *x = "$Id: spawn-filter.c,v 1.41 2009-04-03 11:42:48+05:30 Cprogrammer Stab mbhangui $";
+
+ x++;
+}
diff -Naur qmail-1.03.org/str_cpyb.c qmail-1.03/str_cpyb.c
--- qmail-1.03.org/str_cpyb.c 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/str_cpyb.c 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,53 @@
+/*
+ * $Log: str_cpyb.c,v $
+ * Revision 1.2 2004-10-22 20:30:54+05:30 Cprogrammer
+ * added RCS id
+ *
+ * Revision 1.1 2004-08-15 19:52:35+05:30 Cprogrammer
+ * Initial revision
+ *
+ */
+#include "str.h"
+
+unsigned int
+str_copyb(s, t, max)
+ register char *s;
+ register char *t;
+ unsigned int max;
+{
+ register int len;
+
+ len = 0;
+ while (max-- > 0)
+ {
+ if (!(*s = *t))
+ return len;
+ ++s;
+ ++t;
+ ++len;
+ if (!(*s = *t))
+ return len;
+ ++s;
+ ++t;
+ ++len;
+ if (!(*s = *t))
+ return len;
+ ++s;
+ ++t;
+ ++len;
+ if (!(*s = *t))
+ return len;
+ ++s;
+ ++t;
+ ++len;
+ }
+ return len;
+}
+
+void
+getversion_str_cpyb_c()
+{
+ static char *x = "$Id: str_cpyb.c,v 1.2 2004-10-22 20:30:54+05:30 Cprogrammer Stab mbhangui $";
+
+ x++;
+}
--- qmail-1.03.orig/TARGETS 2009-10-15 20:38:22.850353000 +0300
+++ qmail-1.03/TARGETS 2009-10-15 22:54:27.613351566 +0300
@@ -381,6 +381,27 @@
man
setup
check
+qmail-dk
+qmail-dkim
+qmail-dkim.o
+qmail-dk.o
+libdkim.a
+dkimbase.o
+dkimdns.o
+dkim.o
+dkimsign.o
+dkimverify.o
+dkimtest
+dkimtest.o
+qmail-dkim.8
+qmail-dkim.0
+str_cpyb.o
+dkimfuncs.o
+MakeArgs.o
+spawn-filter spawn-filter.o qregex.o wildmat.o
+spawn-filter.8
+qmail-dk.0 spawn-filter.0
+dk-filter echo.o echo dk-filter.0 dk-filter.8
auth_imap
Makefile.cdb-p
auth_imap.o
diff -Naur qmail-1.03.org/wildmat.c qmail-1.03/wildmat.c
--- qmail-1.03.org/wildmat.c 1970-01-01 05:30:00.000000000 +0530
+++ qmail-1.03/wildmat.c 2009-04-21 11:19:07.000000000 +0530
@@ -0,0 +1,173 @@
+/*-** wildmat.c.orig Wed Dec 3 11:46:31 1997
+ * $Revision: 1.6 $
+ * Do shell-style pattern matching for ?, \, [], and * characters.
+ * Might not be robust in face of malformed patterns; e.g., "foo[a-"
+ * could cause a segmentation violation. It is 8bit clean.
+ *
+ * Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
+ * Rich $alz is now <rsalz@osf.org>.
+ * April, 1991: Replaced mutually-recursive calls with in-line code
+ * for the star character.
+ *
+ * Special thanks to Lars Mathiesen <thorinn@diku.dk> for the ABORT code.
+ * This can greatly speed up failing wildcard patterns. For example:
+ * pattern: -*-*-*-*-*-*-12-*-*-*-m-*-*-*
+ * text 1: -adobe-courier-bold-o-normal--12-120-75-75-m-70-iso8859-1
+ * text 2: -adobe-courier-bold-o-normal--12-120-75-75-X-70-iso8859-1
+ * Text 1 matches with 51 calls, while text 2 fails with 54 calls. Without
+ * the ABORT code, it takes 22310 calls to fail. Ugh. The following
+ * explanation is from Lars:
+ * The precondition that must be fulfilled is that DoMatch will consume
+ * at least one character in text. This is true if *p is neither '*' no
+ * '\0'.) The last return has ABORT instead of FALSE to avoid quadratic
+ * behaviour in cases like pattern "*a*b*c*d" with text "abcxxxxx". With
+ * FALSE, each star-loop has to run to the end of the text; with ABORT
+ * only the last one does.
+ *
+ * Once the control of one instance of DoMatch enters the star-loop, that
+ * instance will return either TRUE or ABORT, and any calling instance
+ * will therefore return immediately after (without calling recursively
+ * again). In effect, only one star-loop is ever active. It would be
+ * possible to modify the code to maintain this context explicitly,
+ * eliminating all recursive calls at the cost of some complication and
+ * loss of clarity (and the ABORT stuff seems to be unclear enough by
+ * itself). I think it would be unwise to try to get this into a
+ * released version unless you have a good test data base to try it out
+ * on.
+ */
+#define TRUE 1
+#define FALSE 0
+#define ABORT -1
+
+
+/*- What character marks an inverted character class? */
+#define NEGATE_CLASS '^'
+/*- Is "*" a common pattern? */
+#define OPTIMIZE_JUST_STAR
+/*- Do tar(1) matching rules, which ignore a trailing slash? */
+#undef MATCH_TAR_PATTERN
+
+
+/*- Match text and p, return TRUE, FALSE, or ABORT. */
+static int
+DoMatch(text, p)
+ register char *text;
+ register char *p;
+{
+ register int last;
+ register int matched;
+ register int reverse;
+
+ for (; *p; text++, p++)
+ {
+ if (*text == '\0' && *p != '*')
+ return ABORT;
+ switch (*p)
+ {
+ case '\\': /*- Literal match with following character. */
+ p++;
+ /*- FALLTHROUGH */
+ default:
+ if (*text != *p)
+ return FALSE;
+ continue;
+ case '?': /*- Match anything. */
+ continue;
+ case '*':
+ /*- Consecutive stars act just like one. */
+ while (*++p == '*')
+ continue;
+ /*- Trailing star matches everything. */
+ if (*p == '\0')
+ return TRUE;
+ while (*text)
+ if ((matched = DoMatch(text++, p)) != FALSE)
+ return matched;
+ return ABORT;
+ case '[':
+ reverse = p[1] == NEGATE_CLASS ? TRUE : FALSE;
+ /*- Inverted character class. */
+ if (reverse)
+ p++;
+ matched = FALSE;
+ if (p[1] == ']' || p[1] == '-')
+ {
+ if (*++p == *text)
+ matched = TRUE;
+ }
+ for (last = *p; *++p && *p != ']'; last = *p)
+ {
+ /*- This next line requires a good C compiler. */
+ if (*p == '-' && p[1] != ']' ? *text <= *++p && *text >= last : *text == *p)
+ matched = TRUE;
+ }
+ if (matched == reverse)
+ return FALSE;
+ continue;
+ }
+ }
+
+#ifdef MATCH_TAR_PATTERN
+ if (*text == '/')
+ return TRUE;
+#endif /*- MATCH_TAR_ATTERN */
+ return *text == '\0';
+}
+
+
+/*- User-level routine. Returns TRUE or FALSE. */
+int
+wildmat_internal(text, p)
+ char *text;
+ char *p;
+{
+#ifdef OPTIMIZE_JUST_STAR
+ if (p[0] == '*' && p[1] == '\0')
+ return TRUE;
+#endif /*- OPTIMIZE_JUST_STAR */
+ return DoMatch(text, p) == TRUE;
+}
+
+#if defined(TEST)
+include < stdio.h >
+/*- Yes, we use gets not fgets. Sue me. */
+
+int
+main()
+{
+ char p[80];
+ char text[80];
+
+ printf("Wildmat tester. Enter pattern, then strings to test.\n");
+ printf("A blank line gets prompts for a new pattern; a blank pattern\n");
+ printf("exits the program.\n");
+ for (;;)
+ {
+ printf("\nEnter pattern: ");
+ (void) fflush(stdout);
+ if (gets(p) == NULL || p[0] == '\0')
+ break;
+ for (;;)
+ {
+ printf("Enter text: ");
+ (void) fflush(stdout);
+ if (gets(text) == NULL)
+ exit(0);
+ /*- Blank line; go back and get a new pattern. */
+ if (text[0] == '\0')
+ break;
+ printf(" %s\n", wildmat_internal(text, p) ? "YES" : "NO");
+ }
+ }
+ exit(0);
+ /*- NOTREACHED */
+}
+#endif /*- defined(TEST) */
+
+void
+getversion_wildmat_internal_c()
+{
+ static char *x = "$Id: wildmat.c,v 1.6 2008-08-03 18:26:33+05:30 Cprogrammer Stab mbhangui $";
+ x++;
+ x--;
+}
diff -Naur qmail-1.03.org/strerr.h qmail-1.03/strerr.h
--- qmail-1.03,orig/strerr.h 2009-10-16 15:12:01.786372335 +0300
+++ qmail-1.03/strerr.h 2009-10-16 15:12:34.890473181 +0300
@@ -1,25 +1,32 @@
+/*
+ * $Log: strerr.h,v $
+ * Revision 1.4 2004-10-21 21:50:27+05:30 Cprogrammer
+ * added strerr_warn8,strerr_warn7,strerr_die8,strerr_die7,strerr_die8sys,strerr_die7sys,
+ * strerr_die8x,strerr_die7x
+ *
+ * Revision 1.3 2004-10-11 14:09:04+05:30 Cprogrammer
+ * added function prototypes
+ *
+ * Revision 1.2 2004-06-18 23:01:58+05:30 Cprogrammer
+ * added RCS log
+ *
+ */
#ifndef STRERR_H
#define STRERR_H
struct strerr
- {
- struct strerr *who;
- const char *x;
- const char *y;
- const char *z;
- }
-;
+{
+ struct strerr *who;
+ char *x;
+ char *y;
+ char *z;
+} ;
extern struct strerr strerr_sys;
-extern void strerr_sysinit(void);
-/* XXX not available in qmail-1.03
-extern char *strerr();
-*/
-extern void strerr_warn(const char *, const char *, const char *,
- const char *, const char *, const char *, struct strerr *);
-extern void strerr_die(int, const char *, const char *, const char *,
- const char *, const char *, const char *, struct strerr *);
+void strerr_sysinit(void);
+void strerr_warn(char *, char *, char *, char *, char *, char *, char *, char *, struct strerr *);
+void strerr_die(int, char *, char *, char *, char *, char *, char *, char *, char *, struct strerr *);
#define STRERR(r,se,a) \
{ se.who = 0; se.x = a; se.y = 0; se.z = 0; return r; }
@@ -29,56 +36,72 @@
#define STRERR_SYS3(r,se,a,b,c) \
{ se.who = &strerr_sys; se.x = a; se.y = b; se.z = c; return r; }
+#define strerr_warn8(x1,x2,x3,x4,x5,x6,x7,x8,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8),(struct strerr *) (se))
+#define strerr_warn7(x1,x2,x3,x4,x5,x6,x7,se) \
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(x7),(char *) 0,(struct strerr *) (se))
#define strerr_warn6(x1,x2,x3,x4,x5,x6,se) \
-strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+strerr_warn((x1),(x2),(x3),(x4),(x5),(x6),(char *) 0,(char *) 0,(struct strerr *) (se))
#define strerr_warn5(x1,x2,x3,x4,x5,se) \
-strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_warn4(x1,x2,x3,x4,se) \
-strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_warn3(x1,x2,x3,se) \
-strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_warn2(x1,x2,se) \
-strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_warn1(x1,se) \
-strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_warn((x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
+#define strerr_die8(e,x1,x2,x3,x4,x5,x6,x7,x8,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7), (x8), (struct strerr *) (se))
+#define strerr_die7(e,x1,x2,x3,x4,x5,x6,x7,se) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7), (char *) 0, (struct strerr *) (se))
#define strerr_die6(e,x1,x2,x3,x4,x5,x6,se) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_die5(e,x1,x2,x3,x4,x5,se) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_die4(e,x1,x2,x3,x4,se) \
-strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_die3(e,x1,x2,x3,se) \
-strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_die2(e,x1,x2,se) \
-strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
#define strerr_die1(e,x1,se) \
-strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) (se))
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) (se))
+#define strerr_die8sys(e,x1,x2,x3,x4,x5,x6,x7,x8) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7), (x8), &strerr_sys)
+#define strerr_die7sys(e,x1,x2,x3,x4,x5,x6,x7) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7), (char *) 0, &strerr_sys)
#define strerr_die6sys(e,x1,x2,x3,x4,x5,x6) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),&strerr_sys)
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0, (char *) 0, &strerr_sys)
#define strerr_die5sys(e,x1,x2,x3,x4,x5) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0, (char *) 0, &strerr_sys)
#define strerr_die4sys(e,x1,x2,x3,x4) \
-strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0, (char *) 0, &strerr_sys)
#define strerr_die3sys(e,x1,x2,x3) \
-strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, &strerr_sys)
#define strerr_die2sys(e,x1,x2) \
-strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, &strerr_sys)
#define strerr_die1sys(e,x1) \
-strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,&strerr_sys)
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, &strerr_sys)
+#define strerr_die8x(e,x1,x2,x3,x4,x5,x6,x7,x8) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7),(x8), (struct strerr *) 0)
+#define strerr_die7x(e,x1,x2,x3,x4,x5,x6,x7) \
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(x7), (char *) 0, (struct strerr *) 0)
#define strerr_die6x(e,x1,x2,x3,x4,x5,x6) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(x6),(char *) 0, (char *) 0, (struct strerr *) 0)
#define strerr_die5x(e,x1,x2,x3,x4,x5) \
-strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(x3),(x4),(x5),(char *) 0,(char *) 0, (char *) 0, (struct strerr *) 0)
#define strerr_die4x(e,x1,x2,x3,x4) \
-strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(x3),(x4),(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) 0)
#define strerr_die3x(e,x1,x2,x3) \
-strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(x3),(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) 0)
#define strerr_die2x(e,x1,x2) \
-strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(x2),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) 0)
#define strerr_die1x(e,x1) \
-strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(struct strerr *) 0)
+strerr_die((e),(x1),(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0,(char *) 0, (char *) 0, (struct strerr *) 0)
#endif
diff -Naur qmail-1.03.org/strerr_die.c qmail-1.03/strerr_die.c
--- qmail-1.03,orig/strerr_die.c 2009-10-16 15:12:01.786372335 +0300
+++ qmail-1.03/strerr_die.c 2009-10-16 15:12:34.890473181 +0300
@@ -1,39 +1,85 @@
+/*
+ * $Log: strerr_die.c,v $
+ * Revision 1.4 2004-10-22 20:30:58+05:30 Cprogrammer
+ * added RCS id
+ *
+ * Revision 1.3 2004-10-21 21:53:02+05:30 Cprogrammer
+ * added two more string args to strerr_warn() and strerr_die()
+ *
+ * Revision 1.2 2004-07-17 21:24:18+05:30 Cprogrammer
+ * added RCS log
+ *
+ */
#include "substdio.h"
#include "subfd.h"
#include "exit.h"
#include "strerr.h"
-void strerr_warn(x1,x2,x3,x4,x5,x6,se)
-const char *x1; const char *x2; const char *x3;
-const char *x4; const char *x5; const char *x6;
-struct strerr *se;
+void
+strerr_warn(x1, x2, x3, x4, x5, x6, x7, x8, se)
+ char *x1;
+ char *x2;
+ char *x3;
+ char *x4;
+ char *x5;
+ char *x6;
+ char *x7;
+ char *x8;
+ struct strerr *se;
{
- strerr_sysinit();
-
- if (x1) substdio_puts(subfderr,x1);
- if (x2) substdio_puts(subfderr,x2);
- if (x3) substdio_puts(subfderr,x3);
- if (x4) substdio_puts(subfderr,x4);
- if (x5) substdio_puts(subfderr,x5);
- if (x6) substdio_puts(subfderr,x6);
-
- while(se) {
- if (se->x) substdio_puts(subfderr,se->x);
- if (se->y) substdio_puts(subfderr,se->y);
- if (se->z) substdio_puts(subfderr,se->z);
- se = se->who;
- }
-
- substdio_puts(subfderr,"\n");
- substdio_flush(subfderr);
+ strerr_sysinit();
+
+ if (x1)
+ substdio_puts(subfderr, x1);
+ if (x2)
+ substdio_puts(subfderr, x2);
+ if (x3)
+ substdio_puts(subfderr, x3);
+ if (x4)
+ substdio_puts(subfderr, x4);
+ if (x5)
+ substdio_puts(subfderr, x5);
+ if (x6)
+ substdio_puts(subfderr, x6);
+ if (x7)
+ substdio_puts(subfderr, x7);
+ if (x8)
+ substdio_puts(subfderr, x8);
+ while (se)
+ {
+ if (se->x)
+ substdio_puts(subfderr, se->x);
+ if (se->y)
+ substdio_puts(subfderr, se->y);
+ if (se->z)
+ substdio_puts(subfderr, se->z);
+ se = se->who;
+ }
+ substdio_puts(subfderr, "\n");
+ substdio_flush(subfderr);
}
-void strerr_die(e,x1,x2,x3,x4,x5,x6,se)
-int e;
-const char *x1; const char *x2; const char *x3;
-const char *x4; const char *x5; const char *x6;
-struct strerr *se;
+void
+strerr_die(e, x1, x2, x3, x4, x5, x6, x7, x8, se)
+ int e;
+ char *x1;
+ char *x2;
+ char *x3;
+ char *x4;
+ char *x5;
+ char *x6;
+ char *x7;
+ char *x8;
+ struct strerr *se;
{
- strerr_warn(x1,x2,x3,x4,x5,x6,se);
- _exit(e);
+ strerr_warn(x1, x2, x3, x4, x5, x6, x7, x8, se);
+ _exit(e);
+}
+
+void
+getversion_strerr_die_c()
+{
+ static char *x = "$Id: strerr_die.c,v 1.4 2004-10-22 20:30:58+05:30 Cprogrammer Stab mbhangui $";
+
+ x++;
}