]> git.ipfire.org Git - thirdparty/postfix.git/commitdiff
postfix-2.3-20050329
authorWietse Venema <wietse@porcupine.org>
Tue, 29 Mar 2005 05:00:00 +0000 (00:00 -0500)
committerViktor Dukhovni <viktor@dukhovni.org>
Tue, 5 Feb 2013 06:30:58 +0000 (06:30 +0000)
48 files changed:
postfix/.indent.pro
postfix/ENHANCED_STATUS_README
postfix/HISTORY
postfix/README_FILES/TLS_README
postfix/RELEASE_NOTES
postfix/conf/header_checks
postfix/html/TLS_README.html
postfix/html/header_checks.5.html
postfix/man/man5/header_checks.5
postfix/proto/TLS_README.html
postfix/proto/header_checks
postfix/src/cleanup/cleanup_api.c
postfix/src/cleanup/cleanup_message.c
postfix/src/discard/discard.c
postfix/src/error/error.c
postfix/src/global/dsn_util.c
postfix/src/global/dsn_util.h
postfix/src/global/mail_version.h
postfix/src/global/pipe_command.c
postfix/src/lmtp/lmtp.c
postfix/src/lmtp/lmtp.h
postfix/src/lmtp/lmtp_chat.c
postfix/src/lmtp/lmtp_proto.c
postfix/src/local/command.c
postfix/src/local/file.c
postfix/src/local/mailbox.c
postfix/src/local/maildir.c
postfix/src/oqmgr/qmgr_deliver.c
postfix/src/pipe/pipe.c
postfix/src/qmgr/qmgr_deliver.c
postfix/src/smtp/smtp.h
postfix/src/smtp/smtp_chat.c
postfix/src/smtp/smtp_connect.c
postfix/src/smtp/smtp_proto.c
postfix/src/smtpd/smtpd_check.c
postfix/src/tls/tls.h
postfix/src/tls/tls_client.c
postfix/src/tls/tls_mgr.c
postfix/src/tls/tls_mgr.h
postfix/src/tls/tls_misc.c
postfix/src/tls/tls_scache.c
postfix/src/tls/tls_scache.h
postfix/src/tls/tls_server.c
postfix/src/tls/tls_session.c
postfix/src/tls/tls_verify.c
postfix/src/tlsmgr/tlsmgr.c
postfix/src/virtual/mailbox.c
postfix/src/virtual/maildir.c

index 06d5e25382dc3ce6a4b31bcd0cdf3e7cc76ea4f4..08e5e99d6bdb579735d4dbc64b60b43b4e3da7d1 100644 (file)
@@ -77,6 +77,7 @@
 -TDNS_REPLY
 -TDNS_RR
 -TDOMAIN_LIST
+-TDSN_BUF
 -TDSN_SPLIT
 -TDSN_VSTRING
 -TEXPAND_ATTR
index 684591e48ee88e6b1350d3dde99132fbacd34325..bf0207f62db8fb2a96caa9af3ed2d56139bb7d1e 100644 (file)
@@ -48,12 +48,13 @@ EACCES.  The latter happens to work because mbox_open() does not
 need to unlock the output file, so it won't clobber the errno value.
 
 - Avoid passing around strings that combine enhanced status code
-and diagnostic text, because the compiler can't help to enforce
-that everything has a status code.  Currently there are two exceptions
-to this rule: the cleanup server status reply, and the delivery
-agent status reply.  Once these protocols are updated we can remove
-the dns_prepend() routine. The third exception, enhanced status
-codes in external command output, is a feature.
+and diagnostic text. Instead, use separate variables for status
+code and text, so that the compiler can enforce that everything has
+a status code.  Currently there are two exceptions to this rule:
+the cleanup server status reply, and the delivery agent status
+reply.  Once these protocols are updated we can remove the dns_prepend()
+routine. The third exception, enhanced status codes in external
+command output, is a feature.
 
 - The bounce/defer/sent library modules will catch the cases where
 an enhanced status code does not match the reject/defer/success
index 22219e5984ab17d07c2e41bf4c26496caae9e53c..7a23da1d93a2f6a4ed0454854a9b7e9e3890114e 100644 (file)
@@ -10488,7 +10488,7 @@ Apologies for any names omitted.
 20050321-27
 
        Support for RFC 3463 enhanced status codes.  See also the
-       ENHANCED_STATUS_README file for background.
+       ENHANCED_STATUS_README (a hacker's guide) for background.
 
        New module to pass around (status code + text) instead of
        just text.  File: Files: global/dsn_util.c.
@@ -10548,6 +10548,25 @@ Apologies for any names omitted.
        server IP address, TCP Port, and server HELO hostname
        if available. File: smtp/smtp_proto.c.
 
+20050328
+
+       Cleanup: the REPLACE action is no longer implemented as
+       PREPEND+IGNORE. The result remains in the input stream,
+       and is subject to address rewriting and other processing
+       where applicable.  File: cleanup/cleanup_message.c.
+
+       Feature: the TLS server name verification status is moved
+       out of the TLS session cache. This not only simplifies the
+       client-side TLS cache implementation, but also provides
+       better cache support for clients that connect to multiple
+       independent MTAs under the same DNS hostname or IP address,
+       provided that each MTA replies with a unique name in the
+       EHLO response. Patch by Victor Duchovni. Files: tlsmgr/tlsmgr.c,
+       tls/tls_verify.c, tls/tls_session.c, tls/tls_server.c,
+       tls/tls_scache.h, tls/tls_scache.c, tls/tls_misc.c,
+       tls/tls_mgr.h, tls/tls_mgr.c, tls/tls_client.c, tls/tls.h,
+       smtp/smtp_proto.c.
+
 Open problems:
 
        Med: disable header address rewriting after XCLIENT?
index fd270b9cb95b5775addf04c98824629dd35e6c19..1e1a753834a4334382ac99e0395bdd53a0e8c2c9 100644 (file)
@@ -364,7 +364,9 @@ between multiple smtpd(8) processes, a persistent session cache can be used.
 You can specify any database type that can store objects of several kbytes and
 that supports the sequence operator. DBM databases are not suitable because
 they can only store small objects. The cache is maintained by the tlsmgr(8)
-process, so there is no problem with concurrent access.
+process, so there is no problem with concurrent access. Session caching is
+highly recommended, because the cost of repeatedly negotiating TLS session keys
+is high.
 
 Example:
 
@@ -608,7 +610,10 @@ between multiple smtp(8) processes, a persistent session cache can be used. You
 can specify any database type that can store objects of several kbytes and that
 supports the sequence operator. DBM databases are not suitable because they can
 only store small objects. The cache is maintained by the tlsmgr(8) process, so
-there is no problem with concurrent access.
+there is no problem with concurrent access. Session caching is highly
+recommended, because the cost of repeatedly negotiating TLS session keys is
+high. Future Postfix SMTP servers may limit the number of sessions that a
+client is allowed to negotiate per unit time.
 
 Example:
 
index 5f1fcb08dfef468f85e2703a5c71aacfb518e908..8ea1805bc3a792e0afcfa67b59d10c7b86f4122c 100644 (file)
@@ -17,9 +17,19 @@ Incompatibility with Postfix 2.1 and earlier
 If you upgrade from Postfix 2.1 or earlier, read RELEASE_NOTES-2.2
 before proceeding.
 
+Incompatibility with snapshot 20050329
+======================================
+
+If you use TLS, you need to execute "postfix reload" because the
+TLS manager protocol has changed.
+
 Incompatibility with snapshot 20050328
 ======================================
 
+The logging format has changed. Postfix delivery agents now log the
+RFC 3463 enhanced status code as "dsn=x.y.z" where y and z can be
+up to three digits each.
+
 After you upgrade from Postfix 2.2 or 2.3 you need to execute
 "postfix reload", otherwise you will keep running the old Postfix
 queue manager, which gives no special treatment to the enhanced
index b48bac2e24a701406f4871a0c10d7e83d86690c4..10dcea1d0e539b3a70c1179a2b0c787c0a75cb6e 100644 (file)
 #                      line,  immediately  before  the  input  that
 #                      triggered the PREPEND action.
 # 
+#               o      The prepended text is not considered part of
+#                      the input stream. Unlike the result from the
+#                      REPLACE action, prepended text is  not  sub-
+#                      ject   to   header/body  checks  or  address
+#                      rewriting, and does not affect the way  that
+#                      Postfix adds missing message headers.
+# 
 #               o      When prepending text before a message header
-#                      line,  the  prepended text must begin with a
+#                      line, the prepended text must begin  with  a
 #                      valid message header label.
 # 
 #               o      This action cannot be used to prepend multi-
 #               This feature is available in Postfix 2.1 and later.
 # 
 #        REDIRECT user@domain
-#               Write a message redirection request  to  the  queue
-#               file  and  inspect  the  next input line. After the
+#               Write  a  message  redirection request to the queue
+#               file and inspect the next  input  line.  After  the
 #               message is queued, it will be sent to the specified
 #               address instead of the intended recipient(s).
 # 
-#               Note:  this action overrides the FILTER action, and
-#               affects all recipients of the message. If  multiple
-#               REDIRECT  actions  fire,  only the last one is exe-
+#               Note: this action overrides the FILTER action,  and
+#               affects  all recipients of the message. If multiple
+#               REDIRECT actions fire, only the last  one  is  exe-
 #               cuted.
 # 
 #               This feature is available in Postfix 2.1 and later.
 # 
 #        REPLACE text...
-#               Replace  the  current  line with the specified text
+#               Replace the current line with  the  specified  text
 #               and inspect the next input line.
 # 
-#               Note: when replacing a  message  header  line,  the
-#               replacement  text  must  begin  with a valid header
-#               label.
-# 
 #               This feature is available in Postfix 2.2 and later.
+#               The description below applies to Postfix 2.2.2  and
+#               later.
+# 
+#               Notes:
+# 
+#               o      When  replacing  a  message header line, the
+#                      replacement text must  begin  with  a  valid
+#                      header label.
+# 
+#               o      The  replaced text remains part of the input
+#                      stream. Unlike the result from  the  PREPEND
+#                      action,  a  replaced  message  header may be
+#                      subject to address rewriting and may  affect
+#                      the  way  that  Postfix adds missing message
+#                      headers.
 # 
 #        REJECT optional text...
-#               Reject  the  entire  message.  Reply  with optional
+#               Reject the  entire  message.  Reply  with  optional
 #               text... when the optional text is specified, other-
 #               wise reply with a generic error message.
 # 
-#               Note:   this  action  disables  further  header  or
-#               body_checks inspection of the current  message  and
+#               Note:  this  action  disables  further  header   or
+#               body_checks  inspection  of the current message and
 #               affects all recipients.
 # 
 #               Postfix version 2.3 and later support enhanced sta-
 #               enhanced status code of "5.7.1".
 # 
 #        WARN optional text...
-#               Log a warning with the optional text... (or  log  a
-#               generic  message)  and inspect the next input line.
+#               Log  a  warning with the optional text... (or log a
+#               generic message) and inspect the next  input  line.
 #               This action is useful for debugging and for testing
 #               a pattern before applying more drastic actions.
 # 
 # BUGS
-#        Many  people  overlook  the main limitations of header and
-#        body_checks rules.  These rules  operate  on  one  logical
-#        message  header or one body line at a time, and a decision
-#        made for one line is not carried over to  the  next  line.
+#        Many people overlook the main limitations  of  header  and
+#        body_checks  rules.   These  rules  operate on one logical
+#        message header or one body line at a time, and a  decision
+#        made  for  one  line is not carried over to the next line.
 #        If text in the message body is encoded (RFC 2045) then the
-#        rules have to specified for the encoded  form.   Likewise,
+#        rules  have  to specified for the encoded form.  Likewise,
 #        when message headers are encoded (RFC 2047) then the rules
 #        need to be specified for the encoded form.
 # 
-#        Message headers added by the cleanup(8) daemon itself  are
+#        Message  headers added by the cleanup(8) daemon itself are
 #        excluded from inspection. Examples of such message headers
 #        are From:, To:, Message-ID:, Date:.
 # 
-#        Message headers deleted by the cleanup(8) daemon  will  be
+#        Message  headers  deleted by the cleanup(8) daemon will be
 #        examined before they are deleted. Examples are: Bcc:, Con-
 #        tent-Length:, Return-Path:.
 # 
 #        body_checks
 #               Lookup tables with content filter rules for message
 #               body lines.  These filters see one physical line at
-#               a time, in chunks  of  at  most  $line_length_limit
+#               a  time,  in  chunks  of at most $line_length_limit
 #               bytes.
 # 
 #        body_checks_size_limit
-#               The  amount  of  content  per  message body segment
+#               The amount of  content  per  message  body  segment
 #               (attachment) that is subjected to $body_checks fil-
 #               tering.
 # 
 # 
 #        nested_header_checks (default: $header_checks)
 #               Lookup tables with content filter rules for message
-#               header lines: respectively, these  are  applied  to
-#               the  initial  message  headers  (not including MIME
-#               headers), to the MIME headers anywhere in the  mes-
-#               sage,  and  to the initial headers of attached mes-
+#               header  lines:  respectively,  these are applied to
+#               the initial message  headers  (not  including  MIME
+#               headers),  to the MIME headers anywhere in the mes-
+#               sage, and to the initial headers of  attached  mes-
 #               sages.
 # 
-#               Note: these filters see one logical message  header
-#               at  a time, even when a message header spans multi-
-#               ple lines. Message headers  that  are  longer  than
+#               Note:  these filters see one logical message header
+#               at a time, even when a message header spans  multi-
+#               ple  lines.  Message  headers  that are longer than
 #               $header_size_limit characters are truncated.
 # 
 #        disable_mime_input_processing
-#               While  receiving mail, give no special treatment to
-#               MIME related message headers; all  text  after  the
+#               While receiving mail, give no special treatment  to
+#               MIME  related  message  headers; all text after the
 #               initial message headers is considered to be part of
-#               the message body. This means that header_checks  is
-#               applied  to  all  the  initial message headers, and
+#               the  message body. This means that header_checks is
+#               applied to all the  initial  message  headers,  and
 #               that body_checks is applied to the remainder of the
 #               message.
 # 
-#               Note:  when  used  in this manner, body_checks will
-#               process a multi-line message header one line  at  a
+#               Note: when used in this  manner,  body_checks  will
+#               process  a  multi-line message header one line at a
 #               time.
 # 
 # EXAMPLES
-#        Header  pattern  to  block  attachments with bad file name
+#        Header pattern to block attachments  with  bad  file  name
 #        extensions.
 # 
 #        /etc/postfix/main.cf:
 #        RFC 2047, message header encoding for non-ASCII text
 # 
 # README FILES
-#        Use  "postconf  readme_directory" or "postconf html_direc-
+#        Use "postconf readme_directory" or  "postconf  html_direc-
 #        tory" to locate this information.
 #        DATABASE_README, Postfix lookup table overview
 #        CONTENT_INSPECTION_README, Postfix content inspection overview
 #        BACKSCATTER_README, blocking returned forged mail
 # 
 # LICENSE
-#        The Secure Mailer license must be  distributed  with  this
+#        The  Secure  Mailer  license must be distributed with this
 #        software.
 # 
 # AUTHOR(S)
index 8030a819de38eb2fc26aa3e7059c0152ea182dab..054bb40b2f4cb91814287911d41a9a3e1b36c7ad 100644 (file)
@@ -553,7 +553,8 @@ can specify any database type that can store objects of several
 kbytes and that supports the sequence operator. DBM databases are
 not suitable because they can only store small objects. The cache
 is maintained by the <a href="tlsmgr.8.html">tlsmgr(8)</a> process, so there is no problem with
-concurrent access. </p>
+concurrent access. Session caching is highly recommended, because
+the cost of repeatedly negotiating TLS session keys is high.</p>
 
 <p> Example: </p>
  
@@ -904,7 +905,10 @@ can specify any database type that can store objects of several
 kbytes and that supports the sequence operator. DBM databases are
 not suitable because they can only store small objects. The cache
 is maintained by the <a href="tlsmgr.8.html">tlsmgr(8)</a> process, so there is no problem with
-concurrent access. </p>
+concurrent access. Session caching is highly recommended, because
+the cost of repeatedly negotiating TLS session keys is high.  Future
+Postfix SMTP servers may limit the number of sessions that a client
+is allowed to negotiate per unit time.</p>
 
 
 <p> Example: </p>
index d372a9019467706fb38fc9ff125cff02534e4e72..89e533ecb722a14049703ca88b34c27e598bc100 100644 (file)
@@ -208,8 +208,15 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
                      line,  immediately  before  the  input  that
                      triggered the <b>PREPEND</b> action.
 
+              <b>o</b>      The prepended text is not considered part of
+                     the input stream. Unlike the result from the
+                     <b>REPLACE</b> action, prepended text is  not  sub-
+                     ject   to   header/body  checks  or  address
+                     rewriting, and does not affect the way  that
+                     Postfix adds missing message headers.
+
               <b>o</b>      When prepending text before a message header
-                     line,  the  prepended text must begin with a
+                     line, the prepended text must begin  with  a
                      valid message header label.
 
               <b>o</b>      This action cannot be used to prepend multi-
@@ -218,35 +225,46 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
               This feature is available in Postfix 2.1 and later.
 
        <b>REDIRECT</b> <i>user@domain</i>
-              Write a message redirection request  to  the  queue
-              file  and  inspect  the  next input line. After the
+              Write  a  message  redirection request to the queue
+              file and inspect the next  input  line.  After  the
               message is queued, it will be sent to the specified
               address instead of the intended recipient(s).
 
-              Note:  this action overrides the <b>FILTER</b> action, and
-              affects all recipients of the message. If  multiple
-              <b>REDIRECT</b>  actions  fire,  only the last one is exe-
+              Note: this action overrides the <b>FILTER</b> action,  and
+              affects  all recipients of the message. If multiple
+              <b>REDIRECT</b> actions fire, only the last  one  is  exe-
               cuted.
 
               This feature is available in Postfix 2.1 and later.
 
        <b>REPLACE</b> <i>text...</i>
-              Replace  the  current  line with the specified text
+              Replace the current line with  the  specified  text
               and inspect the next input line.
 
-              Note: when replacing a  message  header  line,  the
-              replacement  text  must  begin  with a valid header
-              label.
-
               This feature is available in Postfix 2.2 and later.
+              The description below applies to Postfix 2.2.2  and
+              later.
+
+              Notes:
+
+              <b>o</b>      When  replacing  a  message header line, the
+                     replacement text must  begin  with  a  valid
+                     header label.
+
+              <b>o</b>      The  replaced text remains part of the input
+                     stream. Unlike the result from  the  <b>PREPEND</b>
+                     action,  a  replaced  message  header may be
+                     subject to address rewriting and may  affect
+                     the  way  that  Postfix adds missing message
+                     headers.
 
        <b>REJECT</b> <i>optional text...</i>
-              Reject  the  entire  message.  Reply  with <i>optional</i>
+              Reject the  entire  message.  Reply  with  <i>optional</i>
               <i>text...</i> when the optional text is specified, other-
               wise reply with a generic error message.
 
-              Note:   this  action  disables  further  header  or
-              <a href="postconf.5.html#body_checks">body_checks</a> inspection of the current  message  and
+              Note:  this  action  disables  further  header   or
+              <a href="postconf.5.html#body_checks">body_checks</a>  inspection  of the current message and
               affects all recipients.
 
               Postfix version 2.3 and later support enhanced sta-
@@ -255,26 +273,26 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
               enhanced status code of "5.7.1".
 
        <b>WARN</b> <i>optional text...</i>
-              Log a warning with the <i>optional text...</i> (or  log  a
-              generic  message)  and inspect the next input line.
+              Log  a  warning with the <i>optional text...</i> (or log a
+              generic message) and inspect the next  input  line.
               This action is useful for debugging and for testing
               a pattern before applying more drastic actions.
 
 <b>BUGS</b>
-       Many  people  overlook  the main limitations of header and
-       <a href="postconf.5.html#body_checks">body_checks</a> rules.  These rules  operate  on  one  logical
-       message  header or one body line at a time, and a decision
-       made for one line is not carried over to  the  next  line.
+       Many people overlook the main limitations  of  header  and
+       <a href="postconf.5.html#body_checks">body_checks</a>  rules.   These  rules  operate on one logical
+       message header or one body line at a time, and a  decision
+       made  for  one  line is not carried over to the next line.
        If text in the message body is encoded (<a href="http://www.faqs.org/rfcs/rfc2045.html">RFC 2045</a>) then the
-       rules have to specified for the encoded  form.   Likewise,
+       rules  have  to specified for the encoded form.  Likewise,
        when message headers are encoded (<a href="http://www.faqs.org/rfcs/rfc2047.html">RFC 2047</a>) then the rules
        need to be specified for the encoded form.
 
-       Message headers added by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon itself  are
+       Message  headers added by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon itself are
        excluded from inspection. Examples of such message headers
        are <b>From:</b>, <b>To:</b>, <b>Message-ID:</b>, <b>Date:</b>.
 
-       Message headers deleted by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon  will  be
+       Message  headers  deleted by the <a href="cleanup.8.html"><b>cleanup</b>(8)</a> daemon will be
        examined before they are deleted. Examples are: <b>Bcc:, Con-</b>
        <b>tent-Length:</b>, <b>Return-Path:</b>.
 
@@ -282,11 +300,11 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
        <b><a href="postconf.5.html#body_checks">body_checks</a></b>
               Lookup tables with content filter rules for message
               body lines.  These filters see one physical line at
-              a time, in chunks  of  at  most  <b>$<a href="postconf.5.html#line_length_limit">line_length_limit</a></b>
+              a  time,  in  chunks  of at most <b>$<a href="postconf.5.html#line_length_limit">line_length_limit</a></b>
               bytes.
 
        <b><a href="postconf.5.html#body_checks_size_limit">body_checks_size_limit</a></b>
-              The  amount  of  content  per  message body segment
+              The amount of  content  per  message  body  segment
               (attachment) that is subjected to <b>$<a href="postconf.5.html#body_checks">body_checks</a></b> fil-
               tering.
 
@@ -296,32 +314,32 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
 
        <b><a href="postconf.5.html#nested_header_checks">nested_header_checks</a></b> (default: <b>$<a href="postconf.5.html#header_checks">header_checks</a></b>)
               Lookup tables with content filter rules for message
-              header lines: respectively, these  are  applied  to
-              the  initial  message  headers  (not including MIME
-              headers), to the MIME headers anywhere in the  mes-
-              sage,  and  to the initial headers of attached mes-
+              header  lines:  respectively,  these are applied to
+              the initial message  headers  (not  including  MIME
+              headers),  to the MIME headers anywhere in the mes-
+              sage, and to the initial headers of  attached  mes-
               sages.
 
-              Note: these filters see one logical message  header
-              at  a time, even when a message header spans multi-
-              ple lines. Message headers  that  are  longer  than
+              Note:  these filters see one logical message header
+              at a time, even when a message header spans  multi-
+              ple  lines.  Message  headers  that are longer than
               <b>$<a href="postconf.5.html#header_size_limit">header_size_limit</a></b> characters are truncated.
 
        <b><a href="postconf.5.html#disable_mime_input_processing">disable_mime_input_processing</a></b>
-              While  receiving mail, give no special treatment to
-              MIME related message headers; all  text  after  the
+              While receiving mail, give no special treatment  to
+              MIME  related  message  headers; all text after the
               initial message headers is considered to be part of
-              the message body. This means that <b><a href="postconf.5.html#header_checks">header_checks</a></b>  is
-              applied  to  all  the  initial message headers, and
+              the  message body. This means that <b><a href="postconf.5.html#header_checks">header_checks</a></b> is
+              applied to all the  initial  message  headers,  and
               that <b><a href="postconf.5.html#body_checks">body_checks</a></b> is applied to the remainder of the
               message.
 
-              Note:  when  used  in this manner, <b><a href="postconf.5.html#body_checks">body_checks</a></b> will
-              process a multi-line message header one line  at  a
+              Note: when used in this  manner,  <b><a href="postconf.5.html#body_checks">body_checks</a></b>  will
+              process  a  multi-line message header one line at a
               time.
 
 <b>EXAMPLES</b>
-       Header  pattern  to  block  attachments with bad file name
+       Header pattern to block attachments  with  bad  file  name
        extensions.
 
        /etc/postfix/main.cf:
@@ -359,7 +377,7 @@ HEADER_CHECKS(5)                                              HEADER_CHECKS(5)
        <a href="BACKSCATTER_README.html">BACKSCATTER_README</a>, blocking returned forged mail
 
 <b>LICENSE</b>
-       The Secure Mailer license must be  distributed  with  this
+       The  Secure  Mailer  license must be distributed with this
        software.
 
 <b>AUTHOR(S)</b>
index bd794663d0818052bfcffb625e6cca1ccfe47c1c..e8954116df253ff6f6e90ccb016ebd665af7d12b 100644 (file)
@@ -194,6 +194,12 @@ Notes:
 The prepended text is output on a separate line, immediately
 before the input that triggered the \fBPREPEND\fR action.
 .IP \(bu
+The prepended text is not considered part of the input
+stream. Unlike the result from the \fBREPLACE\fR action,
+prepended text is not subject to header/body checks or
+address rewriting, and does not affect the way that Postfix
+adds missing message headers.
+.IP \(bu
 When prepending text before a message header line, the prepended
 text must begin with a valid message header label.
 .IP \(bu
@@ -216,10 +222,20 @@ This feature is available in Postfix 2.1 and later.
 Replace the current line with the specified text and inspect the next
 input line.
 .sp
-Note: when replacing a message header line, the replacement text
-must begin with a valid header label.
+This feature is available in Postfix 2.2 and later. The
+description below applies to Postfix 2.2.2 and later.
 .sp
-This feature is available in Postfix 2.2 and later.
+Notes:
+.RS
+.IP \(bu
+When replacing a message header line, the replacement text
+must begin with a valid header label.
+.IP \(bu
+The replaced text remains part of the input stream. Unlike
+the result from the \fBPREPEND\fR action, a replaced message
+header may be subject to address rewriting and may affect
+the way that Postfix adds missing message headers.
+.RE
 .IP "\fBREJECT \fIoptional text...\fR
 Reject the entire message. Reply with \fIoptional text...\fR when
 the optional text is specified, otherwise reply with a generic error
index 6b2165685be1d750a8788efed9b3694214a1b575..23f51d28b77ec6a0b353fceb727198ea522fc01f 100644 (file)
@@ -553,7 +553,8 @@ can specify any database type that can store objects of several
 kbytes and that supports the sequence operator. DBM databases are
 not suitable because they can only store small objects. The cache
 is maintained by the tlsmgr(8) process, so there is no problem with
-concurrent access. </p>
+concurrent access. Session caching is highly recommended, because
+the cost of repeatedly negotiating TLS session keys is high.</p>
 
 <p> Example: </p>
  
@@ -904,7 +905,10 @@ can specify any database type that can store objects of several
 kbytes and that supports the sequence operator. DBM databases are
 not suitable because they can only store small objects. The cache
 is maintained by the tlsmgr(8) process, so there is no problem with
-concurrent access. </p>
+concurrent access. Session caching is highly recommended, because
+the cost of repeatedly negotiating TLS session keys is high.  Future
+Postfix SMTP servers may limit the number of sessions that a client
+is allowed to negotiate per unit time.</p>
 
 
 <p> Example: </p>
index 51220d4e60393de7019fcc7c92bd953484ebd37c..45133981cef9f53de3662fcba00c34e43413c759 100644 (file)
 #      The prepended text is output on a separate line, immediately
 #      before the input that triggered the \fBPREPEND\fR action.
 # .IP \(bu
+#      The prepended text is not considered part of the input
+#      stream. Unlike the result from the \fBREPLACE\fR action,
+#      prepended text is not subject to header/body checks or
+#      address rewriting, and does not affect the way that Postfix
+#      adds missing message headers.
+# .IP \(bu
 #      When prepending text before a message header line, the prepended
 #      text must begin with a valid message header label.
 # .IP \(bu
 #      Replace the current line with the specified text and inspect the next
 #      input line.
 # .sp
-#      Note: when replacing a message header line, the replacement text
-#      must begin with a valid header label.
+#      This feature is available in Postfix 2.2 and later. The
+#      description below applies to Postfix 2.2.2 and later.
 # .sp
-#      This feature is available in Postfix 2.2 and later.
+#      Notes: 
+# .RS
+# .IP \(bu
+#      When replacing a message header line, the replacement text
+#      must begin with a valid header label.
+# .IP \(bu
+#      The replaced text remains part of the input stream. Unlike
+#      the result from the \fBPREPEND\fR action, a replaced message
+#      header may be subject to address rewriting and may affect
+#      the way that Postfix adds missing message headers.
+# .RE
 # .IP "\fBREJECT \fIoptional text...\fR
 #      Reject the entire message. Reply with \fIoptional text...\fR when
 #      the optional text is specified, otherwise reply with a generic error
index fdceb52b33b6879cba6c0f7ba81eaefa9bb68831..15fbdc577548100c0e45685d6d1fc0af10f578ec 100644 (file)
@@ -259,7 +259,7 @@ DSN_SPLIT dp;
 
     if (state->errs != 0) {
        if (CAN_BOUNCE()) {
-           if (state->reason) 
+           if (state->reason)
                dsn_split(&dp, "5.0.0", state->reason);
            else
                detail = cleanup_stat_detail(state->errs);
@@ -267,8 +267,8 @@ DSN_SPLIT dp;
                              state->recip ? state->recip : "unknown",
                              state->recip ? state->recip : "unknown",
                              (long) 0, "none",
-                             detail ? detail->dsn : dp.dsn, 
-                             state->time, "%s", 
+                             detail ? detail->dsn : DSN_CODE(dp.dsn),
+                             state->time, "%s",
                              detail ? detail->text : dp.text) == 0
                && bounce_flush(BOUNCE_FLAG_CLEAN, state->queue_name,
                                state->queue_id,
index 3819363cd13aa293515cb5df276c0727c6821f32..51be87e99418e93236f8b7cbcfb32fe863605c96 100644 (file)
@@ -296,22 +296,22 @@ static void cleanup_act_log(CLEANUP_STATE *state,
 
 /* cleanup_act - act upon a header/body match */
 
-static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
-                              const char *value, const char *map_class)
+static const char *cleanup_act(CLEANUP_STATE *state, char *context,
+                                      const char *buf, const char *value,
+                                      const char *map_class)
 {
     const char *optional_text = value + strcspn(value, " \t");
     int     command_len = optional_text - value;
-    VSTRING *bp;
-    CLEANUP_STAT_DETAIL *detail;
 
     while (*optional_text && ISSPACE(*optional_text))
        optional_text++;
 
 #define STREQUAL(x,y,l) (strncasecmp((x), (y), (l)) == 0 && (y)[l] == 0)
-#define CLEANUP_ACT_KEEP 1
 #define CLEANUP_ACT_DROP 0
 
     if (STREQUAL(value, "REJECT", command_len)) {
+       CLEANUP_STAT_DETAIL *detail;
+
        if (state->reason == 0) {
            if (*optional_text) {
                state->reason = dsn_prepend("5.7.1", optional_text);
@@ -323,17 +323,18 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
        state->errs |= CLEANUP_STAT_CONT;
        state->flags &= ~CLEANUP_FLAG_FILTER;
        cleanup_act_log(state, "reject", context, buf, state->reason);
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
     }
     if (STREQUAL(value, "WARN", command_len)) {
        cleanup_act_log(state, "warning", context, buf, optional_text);
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
     }
     if (STREQUAL(value, "FILTER", command_len)) {
        if (*optional_text == 0) {
            msg_warn("missing FILTER command argument in %s map", map_class);
        } else if (strchr(optional_text, ':') == 0) {
-           msg_warn("bad FILTER command %s in %s, need transport:destination",
+           msg_warn("bad FILTER command %s in %s -- "
+                    "need transport:destination",
                     optional_text, map_class);
        } else {
            if (state->filter)
@@ -341,57 +342,52 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
            state->filter = mystrdup(optional_text);
            cleanup_act_log(state, "filter", context, buf, optional_text);
        }
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
     }
     if (STREQUAL(value, "DISCARD", command_len)) {
        cleanup_act_log(state, "discard", context, buf, optional_text);
        state->flags |= CLEANUP_FLAG_DISCARD;
        state->flags &= ~CLEANUP_FLAG_FILTER;
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
     }
     if (STREQUAL(value, "HOLD", command_len)) {
        cleanup_act_log(state, "hold", context, buf, optional_text);
        state->flags |= CLEANUP_FLAG_HOLD;
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
     }
     if (STREQUAL(value, "PREPEND", command_len)) {
        if (*optional_text == 0) {
            msg_warn("PREPEND action without text in %s map", map_class);
        } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0
                   && !is_header(optional_text)) {
-           msg_warn("bad PREPEND header text \"%s\" in %s map, "
+           msg_warn("bad PREPEND header text \"%s\" in %s map -- "
                     "need \"headername: headervalue\"",
                     optional_text, map_class);
        } else {
            cleanup_act_log(state, "prepend", context, buf, optional_text);
            cleanup_out_string(state, REC_TYPE_NORM, optional_text);
        }
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
     }
     if (STREQUAL(value, "REPLACE", command_len)) {
        if (*optional_text == 0) {
            msg_warn("REPLACE action without text in %s map", map_class);
-           return (CLEANUP_ACT_KEEP);
-       } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0) {
-           if (!is_header(optional_text)) {
-               msg_warn("bad REPLACE header text \"%s\" in %s map, "
-                        "need \"headername: headervalue\"",
-                        optional_text, map_class);
-               return (CLEANUP_ACT_KEEP);
-           }
-           /* XXX Impedance mismatch. */
-           bp = vstring_strcpy(vstring_alloc(100), optional_text);
-           cleanup_out_header(state, bp);
-           vstring_free(bp);
+           return (buf);
+       } else if (strcmp(context, CLEANUP_ACT_CTXT_HEADER) == 0
+                  && !is_header(optional_text)) {
+           msg_warn("bad REPLACE header text \"%s\" in %s map -- "
+                    "need \"headername: headervalue\"",
+                    optional_text, map_class);
+           return (buf);
        } else {
-           cleanup_out_string(state, REC_TYPE_NORM, optional_text);
+           cleanup_act_log(state, "replace", context, buf, optional_text);
+           return (mystrdup(optional_text));
        }
-       cleanup_act_log(state, "replace", context, buf, optional_text);
-       return (CLEANUP_ACT_DROP);
     }
     if (STREQUAL(value, "REDIRECT", command_len)) {
        if (strchr(optional_text, '@') == 0) {
-           msg_warn("bad REDIRECT target \"%s\" in %s map, need user@domain",
+           msg_warn("bad REDIRECT target \"%s\" in %s map -- "
+                    "need user@domain",
                     optional_text, map_class);
        } else {
            if (state->redirect)
@@ -400,7 +396,7 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
            cleanup_act_log(state, "redirect", context, buf, optional_text);
            state->flags &= ~CLEANUP_FLAG_FILTER;
        }
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
     }
     /* Allow and ignore optional text after the action. */
 
@@ -408,13 +404,13 @@ static int cleanup_act(CLEANUP_STATE *state, char *context, const char *buf,
        return (CLEANUP_ACT_DROP);
 
     if (STREQUAL(value, "DUNNO", command_len)) /* preferred */
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
 
     if (STREQUAL(value, "OK", command_len))    /* compat */
-       return (CLEANUP_ACT_KEEP);
+       return (buf);
 
     msg_warn("unknown command in %s map: %s", map_class, value);
-    return (CLEANUP_ACT_KEEP);
+    return (buf);
 }
 
 /* cleanup_header_callback - process one complete header line */
@@ -463,10 +459,17 @@ static void cleanup_header_callback(void *context, int header_class,
        const char *value;
 
        if ((value = maps_find(checks, header, 0)) != 0) {
-           if (cleanup_act(state, CLEANUP_ACT_CTXT_HEADER,
-                           header, value, map_class)
-               == CLEANUP_ACT_DROP)
+           const char *result;
+
+           if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_HEADER,
+                                     header, value, map_class))
+               == CLEANUP_ACT_DROP) {
                return;
+           } else if (result != header) {
+               vstring_strcpy(header_buf, result);
+               hdr_opts = header_opts_find(result);
+               myfree((char *) result);
+           }
        }
     }
 
@@ -672,10 +675,17 @@ static void cleanup_body_callback(void *context, int type,
        const char *value;
 
        if ((value = maps_find(cleanup_body_checks, buf, 0)) != 0) {
-           if (cleanup_act(state, CLEANUP_ACT_CTXT_BODY,
-                           buf, value, VAR_BODY_CHECKS)
-               == CLEANUP_ACT_DROP)
+           const char *result;
+
+           if ((result = cleanup_act(state, CLEANUP_ACT_CTXT_BODY,
+                                     buf, value, VAR_BODY_CHECKS))
+               == CLEANUP_ACT_DROP) {
+               return;
+           } else if (result != buf) {
+               cleanup_out(state, type, result, strlen(result));
+               myfree((char *) result);
                return;
+           }
        }
     }
     cleanup_out(state, type, buf, len);
index 6f7fd18693bf88adcace3e9aaa53d07fe9e3e9fd..d5156c19110ac86be0c236626917487d4bf276a2 100644 (file)
@@ -171,8 +171,8 @@ static int deliver_message(DELIVER_REQUEST *request)
        if (rcpt->offset >= 0) {
            status = sent(BOUNCE_FLAGS(request), request->queue_id,
                          rcpt->orig_addr, rcpt->address, rcpt->offset,
-                         "none", dp.dsn, request->arrival_time,
-                         "%s", dp.text);
+                         "none", DSN_CODE(dp.dsn),
+                         request->arrival_time, "%s", dp.text);
            if (status == 0 && (request->flags & DEL_REQ_FLAG_SUCCESS))
                deliver_completed(src, rcpt->offset);
            result |= status;
index 348772af00ecd047278ba3be47cfac6b5abe75ba..9fc83cc145134ab62bd86f8baf70acf720b65629 100644 (file)
@@ -173,7 +173,7 @@ static int deliver_message(DELIVER_REQUEST *request)
        if (rcpt->offset >= 0) {
            status = bounce_append(BOUNCE_FLAGS(request), request->queue_id,
                                   rcpt->orig_addr, rcpt->address,
-                                  rcpt->offset, "none", dp.dsn,
+                                  rcpt->offset, "none", DSN_CODE(dp.dsn),
                                   request->arrival_time,
                                   "%s", dp.text);
            if (status == 0)
index 677dabdae52efb15f778aa5586ba4f02cf10bc0a..740de2cb98925fd037b22844646988ed4e20e53a 100644 (file)
@@ -6,9 +6,24 @@
 /* SYNOPSIS
 /*     #include <dsn_util.h>
 /*
+/*     #define DSN_SIZE ...
+/*
+/*     typedef struct { ... } DSN_BUF;
+/*
+/*     void    DSN_UPDATE(dsn_buf, dsn, len)
+/*     DSN_BUF dsn_buf;
+/*     const char *dsn;
+/*     size_t  len;
+/*
+/*     const char *DSN_CODE(dsn_buf)
+/*     DSN_BUF dsn_buf;
+/*
+/*     char    *DSN_CLASS(dsn_buf)
+/*     DSN_BUF dsn_buf;
+/*
 /*     typedef struct {
 /* .in +4
-/*             char dsn[...];          /* RFC 3463 */
+/*             DSN_BUF dsn;            /* RFC 3463 detail */
 /*             const char *text;       /* Free text */
 /* .in -4
 /*     } DSN_SPLIT;
@@ -24,7 +39,7 @@
 /*
 /*     typedef struct {
 /* .in +4
-/*             char dsn[...];          /* RFC 3463 */
+/*             DSN_BUF dsn;            /* RFC 3463 detail */
 /*             VSTRING *text;          /* Free text */
 /* .in -4
 /*     } DSN_VSTRING;
 /*     dsn_vstring_free() recycles the storage that was allocated
 /*     by dsn_vstring_alloc() and dsn_vstring_update().
 /*
+/*     DSN_UPDATE() is a helper macro to safely update an
+/*     RFC 3463 detail code.
+/*
+/*     DSN_CODE() is a helper macro to safely read an
+/*     RFC 3463 detail code.
+/*
+/*     DSN_CLASS() is a helper macro to safely read or update an
+/*     RFC 3463 detail code class (i.e. the first digit).
+/*
+/*     DSN_SIZE is the maximal length of an enhanced status
+/*     code including the null string terminator.
+/*
 /*     dsn_valid() returns the length of the RFC 3463 detail code
 /*     at the beginning of text, or zero. It does not skip initial
 /*     whitespace.
@@ -167,15 +194,11 @@ DSN_SPLIT *dsn_split(DSN_SPLIT *dp, const char *def_dsn, const char *text)
     while (ISSPACE(*cp))
        cp++;
     if ((len = dsn_valid(cp)) > 0) {
-       if (len >= sizeof(dp->dsn))
-           msg_panic("dsn_split: bad DSN code length %d", len);
-       DSN_BUF_UPDATE(dp->dsn, cp, len);
+       DSN_UPDATE(dp->dsn, cp, len);
        cp += len + 1;
     } else {
        len = strlen(def_dsn);
-       if (len >= sizeof(dp->dsn))
-           msg_panic("dsn_split: bad default DSN code length %d", len);
-       DSN_BUF_UPDATE(dp->dsn, def_dsn, len);
+       DSN_UPDATE(dp->dsn, def_dsn, len);
     }
 
     /*
@@ -195,7 +218,7 @@ char   *dsn_prepend(const char *def_dsn, const char *text)
     DSN_SPLIT dp;
 
     dsn_split(&dp, def_dsn, text);
-    return (concatenate(dp.dsn, " ", dp.text, (char *) 0));
+    return (concatenate(DSN_CODE(dp.dsn), " ", dp.text, (char *) 0));
 }
 
 /* dsn_vstring_alloc - create DSN+string storage */
@@ -205,7 +228,7 @@ DSN_VSTRING *dsn_vstring_alloc(int len)
     DSN_VSTRING *dv;
 
     dv = (DSN_VSTRING *) mymalloc(sizeof(*dv));
-    dv->dsn[0] = 0;
+    DSN_CLASS(dv->dsn) = 0;
     dv->vstring = vstring_alloc(len);
     return(dv);
 }
@@ -227,9 +250,9 @@ DSN_VSTRING *dsn_vstring_update(DSN_VSTRING *dv, const char *dsn,
     size_t  len;
 
     if (dsn && *dsn) {
-       if ((len = dsn_valid(dsn)) == 0 || len >= sizeof(dv->dsn))
+       if ((len = dsn_valid(dsn)) == 0)
            msg_panic("dsn_vstring_update: bad dsn: \"%s\"", dsn);
-       DSN_BUF_UPDATE(dv->dsn, dsn, len);
+       DSN_UPDATE(dv->dsn, dsn, len);
     }
     if (format && *format) {
        va_start(ap, format);
index fcd3622c40de14ae0cda5101e104189778eed59f..f49aff9e2d70985292c8ad1334bf435efcdccdde 100644 (file)
 #define DSN_DIGS2      3               /* middle digits */
 #define DSN_DIGS3      3               /* trailing digits */
 #define DSN_LEN                (DSN_DIGS1 + 1 + DSN_DIGS2 + 1 + DSN_DIGS3)
-#define DSN_BUFSIZE    (DSN_LEN + 1)
+#define DSN_SIZE       (DSN_LEN + 1)
+
+ /*
+  * Storage for an enhanced status code. Avoid using malloc for itty-bitty
+  * strings with a known size limit.
+  */
+typedef struct {
+    char    data[DSN_SIZE];            /* NOT a public interface */
+} DSN_BUF;
+
+#define DSN_UPDATE(dsn_buf, dsn, len) do { \
+       if (len >= sizeof((dsn_buf).data)) \
+           msg_panic("DSN_UPDATE: bad DSN code \"%.*s...\" length %d", \
+               sizeof((dsn_buf).data) - 1, dsn, len); \
+       strncpy((dsn_buf).data, (dsn), (len)); \
+       (dsn_buf).data[len] = 0; \
+    } while (0)
+
+#define DSN_CODE(dsn_buf) ((const char *) (dsn_buf).data)
+
+#define DSN_CLASS(dsn_buf) ((dsn_buf).data[0])
 
  /*
   * Split flat text into detail code and free text.
   */
 typedef struct {
-    char    dsn[DSN_BUFSIZE];          /* RFC 3463 X.XXX.XXX detail */
+    DSN_BUF dsn;                       /* RFC 3463 X.XXX.XXX detail */
     const char *text;                  /* free text */
 } DSN_SPLIT;
 
-#define DSN_BUF_UPDATE(buf, text, len) do { \
-       strncpy((buf), (text), (len)); \
-       (buf)[len] = 0; \
-    } while (0)
-
 extern DSN_SPLIT *dsn_split(DSN_SPLIT *, const char *, const char *);
 extern size_t dsn_valid(const char *);
 
@@ -50,7 +65,7 @@ extern char *dsn_prepend(const char *, const char *);
   * Easy to update pair of detail code and free text.
   */
 typedef struct {
-    char    dsn[DSN_LEN + 1];          /* RFC 3463 X.XXX.XXX detail */
+    DSN_BUF dsn;                       /* RFC 3463 X.XXX.XXX detail */
     VSTRING *vstring;                  /* free text */
 } DSN_VSTRING;
 
index 8a74d56bf53d2dc4cb9f00b58787eba436237638..578989cf63df7c3f058da7434b5e3fd45cc521c7 100644 (file)
@@ -20,7 +20,7 @@
   * Patches change the patchlevel and the release date. Snapshots change the
   * release date only.
   */
-#define MAIL_RELEASE_DATE      "20050328"
+#define MAIL_RELEASE_DATE      "20050329"
 #define MAIL_VERSION_NUMBER    "2.3"
 
 #define VAR_MAIL_VERSION       "mail_version"
index fca7632fa31ff2ac5746fda73fe3ea45aae2e27b..2653056c60e282fc3fdff120266c011039db3761 100644 (file)
@@ -575,8 +575,8 @@ int     pipe_command(VSTREAM *src, DSN_VSTRING *why,...)
            else if (dsn_valid(log_buf) > 0) {
                /* XXX Assumes dsn_split() does not require 5.x.x in log_buf */
                dsn_split(&dp, "5.3.0", log_buf);
-               dsn_vstring_update(why, dp.dsn, "%s", dp.text);
-               return (dp.dsn[0] == '4' ?
+               dsn_vstring_update(why, DSN_CODE(dp.dsn), "%s", dp.text);
+               return (DSN_CLASS(dp.dsn) == '4' ?
                        PIPE_STAT_DEFER : PIPE_STAT_BOUNCE);
            }
            /* Use <sysexits.h> compatible exit status. */
index 79b7ee3c579cef337734e00048fbf6e1875468bf..9eb6d87cbb313797a11983684dafa0ccfdc4aafe 100644 (file)
@@ -401,12 +401,12 @@ static int deliver_message(DELIVER_REQUEST *request, char **unused_argv)
         */
        if ((state->session = lmtp_connect(request->nexthop, why)) == 0) {
            if (lmtp_errno == LMTP_RETRY) {
-               why->dsn[0] = '4';
-               lmtp_site_fail(state, why->dsn, 450,
+               DSN_CLASS(why->dsn) = '4';
+               lmtp_site_fail(state, DSN_CODE(why->dsn), 450,
                               "%s", vstring_str(why->vstring));
            } else {
-               why->dsn[0] = '5';
-               lmtp_site_fail(state, why->dsn, 550,
+               DSN_CLASS(why->dsn) = '5';
+               lmtp_site_fail(state, DSN_CODE(why->dsn), 550,
                               "%s", vstring_str(why->vstring));
            }
        }
index 560a712c7b29d509473686bb126cbae827746989..6e21fac01af87ead0984a08e461ae74facbe6ea6 100644 (file)
@@ -105,7 +105,7 @@ extern int lmtp_rset(LMTP_STATE *);
   */
 typedef struct LMTP_RESP {             /* server response */
     int     code;                      /* status */
-    char    dsn[DSN_BUFSIZE];          /* DSN detail */
+    DSN_BUF dsn;                       /* DSN detail */
     char   *str;                       /* text */
     VSTRING *buf;                      /* origin of text */
 } LMTP_RESP;
index a5c1f5d8c28667d5f61e8f1ff278f5d0ff38f552..0a0c7e0fa61501a40a8fa1de4782e40b02da4122 100644 (file)
@@ -233,16 +233,16 @@ LMTP_RESP *lmtp_chat_resp(LMTP_STATE *state)
      * Extract RFC 821 reply code and RFC 2034 detail code. Use a default
      * detail code if none was given.
      */
-    rdata.dsn[0] = 0;
+    DSN_CLASS(rdata.dsn) = 0;
     if (three_digs != 0) {
        rdata.code = atoi(STR(state->buffer));
        for (cp = STR(state->buffer) + 4; *cp == ' '; cp++)
             /* void */ ;
-       if ((len = dsn_valid(cp)) > 0 && len < sizeof(rdata.dsn)) {
-           DSN_BUF_UPDATE(rdata.dsn, cp, len);
+       if ((len = dsn_valid(cp)) > 0 && len < sizeof(DSN_SIZE)) {
+           DSN_UPDATE(rdata.dsn, cp, len);
        } else if (strchr("245", STR(state->buffer)[0]) != 0) {
-           DSN_BUF_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
-           rdata.dsn[0] = STR(state->buffer)[0];
+           DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
+           DSN_CLASS(rdata.dsn) = STR(state->buffer)[0];
        }
     } else {
        rdata.code = 0;
index f446f1f74ebf5ea675e751ede24c5919fc6361cd..077c5951a1d68909dd0e59b27c8ce7a723a7ee3d 100644 (file)
@@ -224,7 +224,7 @@ int     lmtp_lhlo(LMTP_STATE *state)
      * Read and parse the server's LMTP greeting banner.
      */
     if (((resp = lmtp_chat_resp(state))->code / 100) != 2)
-       return (lmtp_site_fail(state, resp->dsn, resp->code,
+       return (lmtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
                               "host %s refused to talk to me: %s",
                         session->namaddr, translit(resp->str, "\n", " ")));
 
@@ -233,7 +233,7 @@ int     lmtp_lhlo(LMTP_STATE *state)
      */
     lmtp_chat_cmd(state, "LHLO %s", var_myhostname);
     if ((resp = lmtp_chat_resp(state))->code / 100 != 2)
-       return (lmtp_site_fail(state, resp->dsn, resp->code,
+       return (lmtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
                               "host %s refused to talk to me: %s",
                               session->namaddr,
                               translit(resp->str, "\n", " ")));
@@ -594,7 +594,8 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                     */
                case LMTP_STATE_MAIL:
                    if (resp->code / 100 != 2) {
-                       lmtp_mesg_fail(state, resp->dsn, resp->code,
+                       lmtp_mesg_fail(state, DSN_CODE(resp->dsn),
+                                      resp->code,
                                       "host %s said: %s (in reply to %s)",
                                       session->namaddr,
                                       translit(resp->str, "\n", " "),
@@ -634,7 +635,8 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                                && sent(DEL_REQ_TRACE_FLAGS(request->flags),
                                        request->queue_id, rcpt->orig_addr,
                                        rcpt->address, rcpt->offset,
-                                       session->namaddr, resp->dsn,
+                                       session->namaddr,
+                                       DSN_CODE(resp->dsn),
                                        request->arrival_time, "%s",
                                     translit(resp->str, "\n", " ")) == 0) {
                                if (request->flags & DEL_REQ_FLAG_SUCCESS)
@@ -642,7 +644,7 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                                rcpt->offset = 0;       /* in case deferred */
                            }
                        } else {
-                           lmtp_rcpt_fail(state, resp->dsn,
+                           lmtp_rcpt_fail(state, DSN_CODE(resp->dsn),
                                           resp->code, rcpt,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
@@ -665,7 +667,8 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                case LMTP_STATE_DATA:
                    if (resp->code / 100 != 3) {
                        if (nrcpt > 0)
-                           lmtp_mesg_fail(state, resp->dsn, resp->code,
+                           lmtp_mesg_fail(state, DSN_CODE(resp->dsn),
+                                          resp->code,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
@@ -692,7 +695,8 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                                if (sent(DEL_REQ_TRACE_FLAGS(request->flags),
                                         request->queue_id, rcpt->orig_addr,
                                         rcpt->address, rcpt->offset,
-                                        session->namaddr, resp->dsn,
+                                        session->namaddr,
+                                        DSN_CODE(resp->dsn),
                                         request->arrival_time,
                                         "%s", resp->str) == 0) {
                                    if (request->flags & DEL_REQ_FLAG_SUCCESS)
@@ -701,7 +705,7 @@ static int lmtp_loop(LMTP_STATE *state, NOCLOBBER int send_state,
                                }
                            }
                        } else {
-                           lmtp_rcpt_fail(state, resp->dsn,
+                           lmtp_rcpt_fail(state, DSN_CODE(resp->dsn),
                                           resp->code, rcpt,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
index a06cf0208a02d16fa0eeed1b85baa48edb626066..f419a0d3f979bb516e29cb6bbcd2deff3e7761c1 100644 (file)
@@ -232,9 +232,10 @@ int     deliver_command(LOCAL_STATE state, USER_ATTR usr_attr, const char *comma
        break;
     case PIPE_STAT_BOUNCE:
     case PIPE_STAT_DEFER:
-       deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+                         defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, why->dsn),
+            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
             "%s", vstring_str(why->vstring));
        break;
     case PIPE_STAT_CORRUPT:
index 55e72e94e622826dc75901975d48979135d0345a..b644748339a1e852a70380c2ae74f3fc3efa442c 100644 (file)
@@ -183,9 +183,10 @@ int     deliver_file(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+                         defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, why->dsn),
+            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
             "cannot append message to destination file %s: %s",
             path, vstring_str(why->vstring));
     } else {
index a3f2723e2f67d7a8de760bacd2e492e698a64aeb..a289300cd82ecce527a8771f036bae4a3662cd05 100644 (file)
@@ -212,9 +212,10 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+                         defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, why->dsn),
+            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
             "cannot update mailbox %s for user %s. %s",
             mailbox, state.msg_attr.user, vstring_str(why->vstring));
     } else {
index 6d6611e92edcded41a1b3461dc582505c90f899f..b634332e44b0224f3dc0afb0dff56cdd39d2155d 100644 (file)
@@ -225,9 +225,10 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr, char *path)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+                         defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, why->dsn),
+            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
             "maildir delivery failed: %s", vstring_str(why->vstring));
        if (errno == EACCES) {
            msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",
index 67be2d78ec1674bf4aa6902e83aa51faec10ce20..6da11329abac0fd4045c093291e34654331caf18 100644 (file)
@@ -260,7 +260,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
        if (VSTRING_LEN(reason)) {
            /* Sanitize the DSN status from delivery agent. */
            dsn_split(&dp, "4.0.0", printable(vstring_str(reason), '?'));
-           qmgr_queue_throttle(queue, dp.dsn, *dp.text ?
+           qmgr_queue_throttle(queue, DSN_CODE(dp.dsn), *dp.text ?
                                dp.text : "unknown problem");
            if (queue->window == 0)
                qmgr_defer_todo(queue, queue->dsn, queue->reason);
index d4f6107d4edd408fa4b05d6e2366a26e659e0239..4c40d659789acbc1861f5060a63b5c6127c03ca8 100644 (file)
@@ -1076,7 +1076,7 @@ static int deliver_message(DELIVER_REQUEST *request, char *service, char **argv)
     argv_free(export_env);
 
     deliver_status = eval_command_status(command_status, service, request,
-                                        request->fp, why->dsn,
+                                        request->fp, DSN_CODE(why->dsn),
                                         vstring_str(why->vstring));
 
     /*
index 486963d53882ebcb9e4fbf25fe5bb673eb0bb122..fc698e551558ef7fb6d4f3f1b403a0e6843ab8ab 100644 (file)
@@ -265,7 +265,7 @@ static void qmgr_deliver_update(int unused_event, char *context)
        if (VSTRING_LEN(reason)) {
            /* Sanitize the DSN status from delivery agent. */
            dsn_split(&dp, "4.0.0", printable(vstring_str(reason), '?'));
-           qmgr_queue_throttle(queue, dp.dsn, *dp.text ?
+           qmgr_queue_throttle(queue, DSN_CODE(dp.dsn), *dp.text ?
                                dp.text : "unknown problem");
            if (queue->window == 0)
                qmgr_defer_todo(queue, queue->dsn, queue->reason);
index 4328e642675eb2637b49cc211a78e39cf5a4cd86..2302be217141b19d030d258fe68df656ef0a867e 100644 (file)
@@ -248,7 +248,7 @@ extern int smtp_quit(SMTP_STATE *);
   */
 typedef struct SMTP_RESP {             /* server response */
     int     code;                      /* status */
-    char    dsn[DSN_BUFSIZE];          /* DSN detail */
+    DSN_BUF dsn;                       /* DSN detail */
     char   *str;                       /* text */
     VSTRING *buf;                      /* origin of text */
 } SMTP_RESP;
index b25647a6d83d52da55d28b1fd343bc3c5a8318a1..ff9f219adea368deb6dd54b68c21406f3d23bc0c 100644 (file)
@@ -256,16 +256,16 @@ SMTP_RESP *smtp_chat_resp(SMTP_SESSION *session)
      * Extract RFC 821 reply code and RFC 2034 detail. Use a default detail
      * code if none was given.
      */
-    rdata.dsn[0] = 0;
+    DSN_CLASS(rdata.dsn) = 0;
     if (three_digs != 0) {
        rdata.code = atoi(STR(session->buffer));
        for (cp = STR(session->buffer) + 4; *cp == ' '; cp++)
             /* void */ ;
-       if ((len = dsn_valid(cp)) > 0 && len < sizeof(rdata.dsn)) {
-           DSN_BUF_UPDATE(rdata.dsn, cp, len);
+       if ((len = dsn_valid(cp)) > 0 && len < sizeof(DSN_SIZE)) {
+           DSN_UPDATE(rdata.dsn, cp, len);
        } else if (strchr("245", STR(session->buffer)[0]) != 0) {
-           DSN_BUF_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
-           rdata.dsn[0] = STR(session->buffer)[0];
+           DSN_UPDATE(rdata.dsn, "0.0.0", sizeof("0.0.0") - 1);
+           DSN_CLASS(rdata.dsn) = STR(session->buffer)[0];
        }
     } else {
        rdata.code = 0;
index b01c1201fa3a8dbfb9e0167ebbdf19588815a195..db25f3fc85fbf286c175234154addb474c660e38 100644 (file)
@@ -757,11 +757,13 @@ int     smtp_connect(SMTP_STATE *state)
             */
            state->final_server = 1;            /* XXX */
            if (smtp_errno == SMTP_ERR_RETRY) {
-               why->dsn[0] = '4';
-               smtp_site_fail(state, why->dsn, 450, "%s", STR(why->vstring));
+               DSN_CLASS(why->dsn) = '4';
+               smtp_site_fail(state, DSN_CODE(why->dsn), 450,
+                              "%s", STR(why->vstring));
            } else {
-               why->dsn[0] = '5';
-               smtp_site_fail(state, why->dsn, 550, "%s", STR(why->vstring));
+               DSN_CLASS(why->dsn) = '5';
+               smtp_site_fail(state, DSN_CODE(why->dsn), 550,
+                              "%s", STR(why->vstring));
            }
 
            /*
index e98ebed78ffde9fd0aacb2935cc1d4be5b1aa741..1cfbb6815b2d0bbbaa0c7810c32c8fbdf84c1a42 100644 (file)
@@ -275,10 +275,10 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
        case 5:
            if (var_smtp_skip_5xx_greeting) {
                resp->code = 400;
-               resp->dsn[0] = '4';
+               DSN_CLASS(resp->dsn) = '4';
            }
        default:
-           return (smtp_site_fail(state, resp->dsn, resp->code,
+           return (smtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
                                   "host %s refused to talk to me: %s",
                                   session->namaddr,
                                   translit(resp->str, "\n", " ")));
@@ -336,7 +336,7 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
     if ((session->features & SMTP_FEATURE_ESMTP) == 0) {
        smtp_chat_cmd(session, "HELO %s", var_smtp_helo_name);
        if ((resp = smtp_chat_resp(session))->code / 100 != 2)
-           return (smtp_site_fail(state, resp->dsn, resp->code,
+           return (smtp_site_fail(state, DSN_CODE(resp->dsn), resp->code,
                                   "host %s refused to talk to me: %s",
                                   session->namaddr,
                                   translit(resp->str, "\n", " ")));
@@ -520,7 +520,8 @@ int     smtp_helo(SMTP_STATE *state, NOCLOBBER int misc_flags)
             */
            session->features &= ~SMTP_FEATURE_STARTTLS;
            if (session->tls_enforce_tls)
-               return (smtp_site_fail(state, resp->dsn, resp->code,
+               return (smtp_site_fail(state, DSN_CODE(resp->dsn),
+                                      resp->code,
                    "TLS is required, but host %s refused to start TLS: %s",
                                       session->namaddr,
                                       translit(resp->str, "\n", " ")));
@@ -600,9 +601,8 @@ static int smtp_start_tls(SMTP_STATE *state, int misc_flags)
      * use TLS session caching???
      */
     serverid = vstring_alloc(10);
-    vstring_sprintf(serverid, "%s:%u", session->addr, ntohs(session->port));
-    if (session->helo != 0)
-       vstring_sprintf_append(serverid, ":%s", session->helo);
+    vstring_sprintf(serverid, "%s:%u:%s", session->addr,
+                 ntohs(session->port), session->helo ? session->helo : "");
     session->tls_context =
        tls_client_start(smtp_tls_ctx, session->stream,
                         var_smtp_starttls_tmout,
@@ -615,49 +615,6 @@ static int smtp_start_tls(SMTP_STATE *state, int misc_flags)
        return (smtp_site_fail(state, "4.7.5", 450,
                               "Cannot start TLS: handshake failure"));
 
-    /*
-     * Give up when TLS is required, we can parse the server certificate's
-     * CommonName field, but server certificate verification failed.
-     * 
-     * In enforce_peername state, the handshake would already have been
-     * terminated by the certificate verification call-back routine, so the
-     * check here is for logging only.
-     * 
-     * XXX It appears that the CommonName field is used as an indicator that a
-     * server certificate is available. If the latter is what we want, then
-     * we should test for that instead.
-     */
-    if (session->tls_info.peer_CN != NULL) {
-       if (!session->tls_info.peer_verified) {
-           msg_info("Server certificate could not be verified");
-           if (session->tls_enforce_tls) {
-               tls_client_stop(smtp_tls_ctx, session->stream,
-                               var_smtp_starttls_tmout, 1,
-                               &(session->tls_info));
-               return (smtp_site_fail(state, "4.7.5", 450,
-                         "TLS failure: Cannot verify server certificate"));
-           }
-       }
-    }
-
-    /*
-     * Give up when TLS is required but no server certificate is available
-     * (or we could not parse the certificate's CommonName) field.
-     * 
-     * XXX The test below is not accurate: the server hostname verification may
-     * use the dNSNames instead of the CommonName. We really should be
-     * testing if a certificate is available.
-     */
-    else {
-       if (session->tls_enforce_tls) {
-           tls_client_stop(smtp_tls_ctx, session->stream,
-                           var_smtp_starttls_tmout, 1,
-                           &(session->tls_info));
-           return (smtp_site_fail(state, "4.7.5", 450,
-                            "TLS failure: Cannot verify server hostname"));
-       }
-    }
-
     /*
      * At this point we have to re-negotiate the "EHLO" to reget the
      * feature-list.
@@ -1206,7 +1163,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                     */
                case SMTP_STATE_MAIL:
                    if (resp->code / 100 != 2) {
-                       smtp_mesg_fail(state, resp->dsn, resp->code,
+                       smtp_mesg_fail(state, DSN_CODE(resp->dsn), resp->code,
                                       "host %s said: %s (in reply to %s)",
                                       session->namaddr,
                                       translit(resp->str, "\n", " "),
@@ -1233,7 +1190,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
 #ifdef notdef
                        if (resp->code == 552) {
                            resp->code = 452;
-                           resp->dsn[0] = '4';
+                           DSN_CLASS(resp->dsn) = '4';
                        }
 #endif
                        rcpt = request->rcpt_list.info + recv_rcpt;
@@ -1241,10 +1198,10 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                            ++nrcpt;
                            /* If trace-only, mark the recipient done. */
                            if (DEL_REQ_TRACE_ONLY(request->flags))
-                               smtp_rcpt_done(state, resp->dsn,
+                               smtp_rcpt_done(state, DSN_CODE(resp->dsn),
                                               resp->str, rcpt);
                        } else {
-                           smtp_rcpt_fail(state, resp->dsn,
+                           smtp_rcpt_fail(state, DSN_CODE(resp->dsn),
                                           resp->code, rcpt,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
@@ -1267,7 +1224,8 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                case SMTP_STATE_DATA:
                    if (resp->code / 100 != 3) {
                        if (nrcpt > 0)
-                           smtp_mesg_fail(state, resp->dsn, resp->code,
+                           smtp_mesg_fail(state, DSN_CODE(resp->dsn),
+                                          resp->code,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
@@ -1289,7 +1247,8 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                case SMTP_STATE_DOT:
                    if (nrcpt > 0) {
                        if (resp->code / 100 != 2) {
-                           smtp_mesg_fail(state, resp->dsn, resp->code,
+                           smtp_mesg_fail(state, DSN_CODE(resp->dsn),
+                                          resp->code,
                                        "host %s said: %s (in reply to %s)",
                                           session->namaddr,
                                           translit(resp->str, "\n", " "),
@@ -1298,7 +1257,7 @@ static int smtp_loop(SMTP_STATE *state, NOCLOBBER int send_state,
                            for (nrcpt = 0; nrcpt < recv_rcpt; nrcpt++) {
                                rcpt = request->rcpt_list.info + nrcpt;
                                if (!SMTP_RCPT_ISMARKED(rcpt))
-                                   smtp_rcpt_done(state, resp->dsn,
+                                   smtp_rcpt_done(state, DSN_CODE(resp->dsn),
                                                   resp->str, rcpt);
                            }
                        }
index ee3c6d7b1e0ba818e850fbe9a1afe6d2d554ce6c..05069e5e9f014a2f6b3c87e51f56d6691bd2b538 100644 (file)
@@ -1873,7 +1873,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
     if (STREQUAL(value, "REJECT", cmd_len)) {
        dsn_split(&dp, "5.7.1", cmd_text);
        return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
-                                  var_access_map_code, dp.dsn,
+                                  var_access_map_code, DSN_CODE(dp.dsn),
                                   "<%s>: %s rejected: %s",
                                   reply_name, reply_class,
                                   *dp.text ? dp.text : "Access denied"));
@@ -1982,7 +1982,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
     if (STREQUAL(value, DEFER_IF_PERMIT, cmd_len)) {
        dsn_split(&dp, "4.7.1", cmd_text);
        DEFER_IF_PERMIT3(state, MAIL_ERROR_POLICY,
-                        450, dp.dsn,
+                        450, DSN_CODE(dp.dsn),
                         "<%s>: %s rejected: %s",
                         reply_name, reply_class,
                         *dp.text ? dp.text : "Service unavailable");
@@ -1996,7 +1996,7 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
     if (STREQUAL(value, DEFER_IF_REJECT, cmd_len)) {
        dsn_split(&dp, "4.7.1", cmd_text);
        DEFER_IF_REJECT3(state, MAIL_ERROR_POLICY,
-                        450, dp.dsn,
+                        450, DSN_CODE(dp.dsn),
                         "<%s>: %s rejected: %s",
                         reply_name, reply_class,
                         *dp.text ? dp.text : "Service unavailable");
@@ -2045,9 +2045,10 @@ static int check_table_result(SMTPD_STATE *state, const char *table,
        def_dsn[0] = value[0];
        dsn_split(&dp, def_dsn, cmd_text);
        return (smtpd_check_reject(state, MAIL_ERROR_POLICY,
-                                  code, dp.dsn,
+                                  code, DSN_CODE(dp.dsn),
                                   "<%s>: %s rejected: %s",
-                                  reply_name, reply_class, dp.text));
+                                  reply_name, reply_class,
+                                  *dp.text ? dp.text : "Access denied"));
     }
 
     /*
@@ -2892,7 +2893,8 @@ static int rbl_reject_reply(SMTPD_STATE *state, SMTPD_RBL_STATE *rbl,
        code = atoi(STR(why));
        dsn_split(&dp, "4.7.1", STR(why) + 4);
        result = smtpd_check_reject(state, MAIL_ERROR_POLICY,
-                                   code, dp.dsn, "%s", *dp.text ?
+                                   code, DSN_CODE(dp.dsn),
+                                   "%s", *dp.text ?
                                    dp.text : "Service unavailable");
     }
 
index a6514d776526edfec30c359fc4ca7029d9e6e115..26d6efba2853a034f473f28ab9c79be237412fe8 100644 (file)
@@ -59,7 +59,7 @@ typedef struct {
     char    issuer_CN[CCERT_BUFSIZ];
     unsigned char md[EVP_MAX_MD_SIZE];
     char    fingerprint[EVP_MAX_MD_SIZE * 3];
-    char    peername_save[HOST_BUFSIZ + 1];
+    char   *peername;
     int     enforce_verify_errors;
     int     enforce_CN;
     int     hostname_matched;
@@ -68,18 +68,6 @@ typedef struct {
 
 #define TLS_BIO_BUFSIZE        8192
 
-#define NEW_TLS_CONTEXT(p) do { \
-       p = (TLScontext_t *) mymalloc(sizeof(*p)); \
-       memset((char *) p, 0, sizeof(*p)); \
-       p->serverid = 0; \
-    } while (0)
-    
-#define FREE_TLS_CONTEXT(p) do { \
-       if ((p)->serverid) \
-           myfree((p)->serverid); \
-       myfree((char *) (p)); \
-    } while (0)
-
 typedef struct {
     int     peer_verified;
     int     hostname_matched;
@@ -179,10 +167,7 @@ extern RSA *tls_tmp_rsa_cb(SSL *, int, int);
  /*
   * tls_verify.c
   */
-extern int tls_verify_certificate_callback(int, X509_STORE_CTX *, int);
-
-#define TLS_VERIFY_DEFAULT     (0)
-#define TLS_VERIFY_PEERNAME    (1<<0)
+extern int tls_verify_certificate_callback(int, X509_STORE_CTX *);
 
  /*
   * tls_certkey.c
@@ -198,6 +183,8 @@ extern int tls_set_my_certificate_key_info(SSL_CTX *, const char *,
   */
 extern int TLScontext_index;
 
+extern TLScontext_t *tls_alloc_context(int, const char *);
+extern void tls_free_context(TLScontext_t *);
 extern void tls_print_errors(void);
 extern void tls_info_callback(const SSL *, int, int);
 extern long tls_bio_dump_cb(BIO *, int, const char *, int, long, long);
index a47e322dacc79784a5956ce55ac33b584a6b6f93..61b04eadbfb1df0ec0c50a2a826cbf52cc046dec 100644 (file)
@@ -9,8 +9,8 @@
 /*     SSL_CTX *tls_client_init(verifydepth)
 /*     int     verifydepth; /* unused */
 /*
-/*     TLScontext_t *tls_client_start(client_ctx, stream, timeout, 
-/*                                     enforce_peername, peername, 
+/*     TLScontext_t *tls_client_start(client_ctx, stream, timeout,
+/*                                     enforce_peername, peername,
 /*                                     serverid, tls_info)
 /*     SSL_CTX *client_ctx;
 /*     VSTREAM *stream;
@@ -140,38 +140,25 @@ static const char hexcodes[] = "0123456789ABCDEF";
   */
 static int tls_client_cache = 0;
 
-/* client_verify_callback - certificate verification wrapper */
-
-static int client_verify_callback(int ok, X509_STORE_CTX *ctx)
-{
-    return (tls_verify_certificate_callback(ok, ctx, TLS_VERIFY_PEERNAME));
-}
-
 /* load_clnt_session - load session from client cache (non-callback) */
 
-static SSL_SESSION *load_clnt_session(const char *cache_id,
-                                             int enforce_peername)
+static SSL_SESSION *load_clnt_session(const char *cache_id)
 {
     SSL_SESSION *session = 0;
     VSTRING *session_data = vstring_alloc(2048);
-    int     flags = 0;
-
-#define TLS_FLAG_ENFORCE_PEERNAME      (1<<0)
 
     /*
      * Prepare the query.
      */
     if (var_smtp_tls_loglevel >= 3)
        msg_info("looking for session %s in client cache", cache_id);
-    if (enforce_peername)
-       flags |= TLS_FLAG_ENFORCE_PEERNAME;
 
     /*
      * Look up and activate the SSL_SESSION object. Errors are non-fatal,
      * since caching is only an optimization.
      */
-    if (tls_mgr_lookup(tls_client_cache, cache_id, OPENSSL_VERSION_NUMBER,
-                      flags, session_data) == TLS_MGR_STAT_OK) {
+    if (tls_mgr_lookup(tls_client_cache, cache_id,
+                      session_data) == TLS_MGR_STAT_OK) {
        session = tls_session_activate(STR(session_data), LEN(session_data));
        if (session) {
            if (var_smtp_tls_loglevel >= 3)
@@ -194,7 +181,6 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
     TLScontext_t *TLScontext;
     VSTRING *session_data;
     const char *cache_id;
-    int     flags = 0;
 
     /*
      * Look up the cache ID string for this session object.
@@ -205,14 +191,6 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
     if (var_smtp_tls_loglevel >= 3)
        msg_info("save session %s to client cache", cache_id);
 
-    /*
-     * Remember whether peername matching was enforced when the session was
-     * created. If later enforce mode is enabled, we do not want to reuse a
-     * session that was not sufficiently checked.
-     */
-    if (TLScontext->enforce_verify_errors && TLScontext->enforce_CN)
-       flags |= TLS_FLAG_ENFORCE_PEERNAME;
-
 #if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
 
     /*
@@ -235,7 +213,6 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
     session_data = tls_session_passivate(session);
     if (session_data)
        tls_mgr_update(tls_client_cache, cache_id,
-                      OPENSSL_VERSION_NUMBER, flags,
                       STR(session_data), LEN(session_data));
 
     /*
@@ -248,12 +225,24 @@ static int new_client_session_cb(SSL *ssl, SSL_SESSION *session)
     return (1);
 }
 
+/* uncache_session - remove session from the external cache */
+
+static void uncache_session(TLScontext_t *TLScontext)
+{
+    if (TLScontext->serverid == 0)
+       return;
+
+    if (var_smtp_tls_loglevel >= 3)
+       msg_info("remove session %s from client cache", TLScontext->serverid);
+
+    tls_mgr_delete(tls_client_cache, TLScontext->serverid);
+}
+
 /* tls_client_init - initialize client-side TLS engine */
 
 SSL_CTX *tls_client_init(int unused_verifydepth)
 {
     int     off = 0;
-    int     verify_flags = SSL_VERIFY_NONE;
     SSL_CTX *client_ctx;
     int     cache_types;
 
@@ -376,14 +365,20 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
      * Finally, the setup for the server certificate checking, done "by the
      * book".
      */
-    SSL_CTX_set_verify(client_ctx, verify_flags, client_verify_callback);
+    SSL_CTX_set_verify(client_ctx, SSL_VERIFY_NONE,
+                      tls_verify_certificate_callback);
 
     /*
-     * Initialize the session cache. In order to share cached sessions among
-     * multiple SMTP client processes, we use an external cache and set the
-     * internal cache size to a minimum value of 1.
+     * Initialize the session cache.
+     * 
+     * Since the client does not search an internal cache, we simply disable it.
+     * It is only useful for expiring old sessions, but we do that in the
+     * tlsmgr(8).
+     * 
+     * This makes SSL_CTX_remove_session() not useful for flushing broken
+     * sessions from the external cache, so we must delete them directly (not
+     * via a callback).
      */
-    SSL_CTX_sess_set_cache_size(client_ctx, 1);
     SSL_CTX_set_timeout(client_ctx, var_smtp_tls_scache_timeout);
 
     /*
@@ -401,13 +396,13 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
         * OpenSSL can, however, automatically save newly created sessions for
         * us by callback (we create the session name in the call-back
         * function).
-        * 
-        * Disable automatic clearing of cache entries, as the client process
-        * has limited lifetime anyway and we can call the cleanup routine
-        * directly.
         */
        SSL_CTX_set_session_cache_mode(client_ctx,
-                     SSL_SESS_CACHE_CLIENT | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+                                      SSL_SESS_CACHE_CLIENT |
+#ifdef SSL_SESS_CACHE_NO_INTERNAL_STORE
+                                      SSL_SESS_CACHE_NO_INTERNAL_STORE |
+#endif
+                                      SSL_SESS_CACHE_NO_AUTO_CLEAR);
        SSL_CTX_sess_set_new_cb(client_ctx, new_client_session_cb);
     }
 
@@ -422,6 +417,121 @@ SSL_CTX *tls_client_init(int unused_verifydepth)
     return (client_ctx);
 }
 
+/* match_hostname -  match hostname against pattern */
+
+static int match_hostname(const char *pattern, const char *hostname)
+{
+    char   *peername_left;
+
+    return (strcasecmp(hostname, pattern) == 0
+           || (pattern[0] == '*' && pattern[1] == '.' && pattern[2] != 0
+               && (peername_left = strchr(hostname, '.')) != 0
+               && strcasecmp(peername_left + 1, pattern + 2) == 0));
+}
+
+/* verify_extract_peer - verify peer name and extract peer information */
+
+static void verify_extract_peer(const char *peername, X509 * peercert,
+                            TLScontext_t *TLScontext, tls_info_t *tls_info)
+{
+    char    buf[1024];
+    int     i;
+    int     r;
+    int     hostname_matched;
+    int     dNSName_found;
+
+    STACK_OF(GENERAL_NAME) * gens;
+
+    tls_info->peer_verified =
+       (SSL_get_verify_result(TLScontext->con) == X509_V_OK);
+
+    if (TLScontext->enforce_CN != 0 && tls_info->peer_verified != 0) {
+
+       /*
+        * Verify the name(s) in the peer certificate against the peer
+        * hostname. Log peer hostname/certificate mis-matches. If a match is
+        * required but fails, bail out with a verification error.
+        */
+       hostname_matched = dNSName_found = 0;
+
+       gens = X509_get_ext_d2i(peercert, NID_subject_alt_name, 0, 0);
+       if (gens) {
+           for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) {
+               const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
+
+               if (gn->type == GEN_DNS) {
+                   dNSName_found++;
+                   if ((hostname_matched =
+                        match_hostname((char *) gn->d.ia5->data, peername)))
+                       break;
+               }
+           }
+           sk_GENERAL_NAME_free(gens);
+       }
+       if (dNSName_found) {
+           if (!hostname_matched)
+               msg_info("certificate peer name verification failed for "
+                        "%s: %d dNSNames in certificate found, "
+                        "but none matches", peername, dNSName_found);
+       } else {
+           buf[0] = '\0';
+           if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peercert),
+                                          NID_commonName, buf,
+                                          sizeof(buf))) {
+               msg_info("certificate peer name verification failed for"
+                        " %s: cannot parse subject CommonName", peername);
+               tls_print_errors();
+           } else {
+               hostname_matched = match_hostname(buf, peername);
+               if (!hostname_matched)
+                   msg_info("certificate peer name verification failed "
+                            "for %s: CommonName mis-match: %s",
+                            peername, buf);
+           }
+       }
+
+       TLScontext->hostname_matched = hostname_matched;
+    }
+    tls_info->hostname_matched = TLScontext->hostname_matched;
+
+    TLScontext->peer_CN[0] = '\0';
+    if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peercert),
+                                  NID_commonName, TLScontext->peer_CN,
+                                  sizeof(TLScontext->peer_CN))) {
+       msg_info("Could not parse server's subject CN");
+       tls_print_errors();
+    }
+    tls_info->peer_CN = TLScontext->peer_CN;
+
+    TLScontext->issuer_CN[0] = '\0';
+    if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peercert),
+                                  NID_commonName, TLScontext->issuer_CN,
+                                  sizeof(TLScontext->issuer_CN))) {
+       msg_info("Could not parse server's issuer CN");
+       tls_print_errors();
+    }
+    if (!TLScontext->issuer_CN[0]) {
+       /* No issuer CN field, use Organization instead */
+       if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peercert),
+                               NID_organizationName, TLScontext->issuer_CN,
+                                      sizeof(TLScontext->issuer_CN))) {
+           msg_info("Could not parse server's issuer Organization");
+           tls_print_errors();
+       }
+    }
+    tls_info->issuer_CN = TLScontext->issuer_CN;
+
+    if (var_smtp_tls_loglevel >= 1) {
+       if (tls_info->peer_verified
+           && (!TLScontext->enforce_CN || TLScontext->hostname_matched))
+           msg_info("Verified: subject_CN=%s, issuer=%s",
+                    TLScontext->peer_CN, TLScontext->issuer_CN);
+       else
+           msg_info("Unverified: subject_CN=%s, issuer=%s",
+                    TLScontext->peer_CN, TLScontext->issuer_CN);
+    }
+}
+
  /*
   * This is the actual startup routine for the connection. We expect that the
   * buffers are flushed and the "220 Ready to start TLS" was received by us,
@@ -435,10 +545,9 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
                                       tls_info_t *tls_info)
 {
     int     sts;
-    SSL_SESSION *session, *old_session;
+    SSL_SESSION *session;
     SSL_CIPHER *cipher;
-    X509   *peer;
-    int     verify_flags;
+    X509   *peercert;
     TLScontext_t *TLScontext;
 
     if (var_smtp_tls_loglevel >= 1)
@@ -448,30 +557,20 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * Allocate a new TLScontext for the new connection and get an SSL
      * structure. Add the location of TLScontext to the SSL to later retrieve
      * the information inside the tls_verify_certificate_callback().
-     * 
-     * XXX Need a dedicated procedure for consistent initialization of all the
-     * fields in this structure.
      */
-#define PEERNAME_SIZE sizeof(TLScontext->peername_save)
-
-    NEW_TLS_CONTEXT(TLScontext);
-    TLScontext->log_level = var_smtp_tls_loglevel;
-    strncpy(TLScontext->peername_save, peername, PEERNAME_SIZE - 1);
-    TLScontext->peername_save[PEERNAME_SIZE - 1] = 0;
-    (void) lowercase(TLScontext->peername_save);
+    TLScontext = tls_alloc_context(var_smtp_tls_loglevel, peername);
     TLScontext->serverid = mystrdup(serverid);
 
     if ((TLScontext->con = (SSL *) SSL_new(client_ctx)) == NULL) {
        msg_info("Could not allocate 'TLScontext->con' with SSL_new()");
        tls_print_errors();
-       FREE_TLS_CONTEXT(TLScontext);
+       tls_free_context(TLScontext);
        return (0);
     }
     if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
        msg_info("Could not set application data for 'TLScontext->con'");
        tls_print_errors();
-       SSL_free(TLScontext->con);
-       FREE_TLS_CONTEXT(TLScontext);
+       tls_free_context(TLScontext);
        return (0);
     }
 
@@ -480,10 +579,10 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * tls_verify_certificate_callback().
      */
     if (enforce_peername) {
-       verify_flags = SSL_VERIFY_PEER;
        TLScontext->enforce_verify_errors = 1;
        TLScontext->enforce_CN = 1;
-       SSL_set_verify(TLScontext->con, verify_flags, client_verify_callback);
+       SSL_set_verify(TLScontext->con, SSL_VERIFY_PEER,
+                      tls_verify_certificate_callback);
     } else {
        TLScontext->enforce_verify_errors = 0;
        TLScontext->enforce_CN = 0;
@@ -501,11 +600,9 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
                          &TLScontext->network_bio, TLS_BIO_BUFSIZE)) {
        msg_info("Could not obtain BIO_pair");
        tls_print_errors();
-       SSL_free(TLScontext->con);
-       FREE_TLS_CONTEXT(TLScontext);
+       tls_free_context(TLScontext);
        return (0);
     }
-    old_session = NULL;
 
     /*
      * Try to load an existing session from the TLS session cache.
@@ -515,10 +612,10 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
      * will be reused.
      */
     if (tls_client_cache) {
-       old_session = load_clnt_session(serverid, enforce_peername);
-       if (old_session) {
-           SSL_set_session(TLScontext->con, old_session);
-           SSL_SESSION_free(old_session);      /* 200411 */
+       session = load_clnt_session(serverid);
+       if (session) {
+           SSL_set_session(TLScontext->con, session);
+           SSL_SESSION_free(session);          /* 200411 */
 #if (OPENSSL_VERSION_NUMBER < 0x00906011L) || (OPENSSL_VERSION_NUMBER == 0x00907000L)
 
            /*
@@ -532,7 +629,7 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
             * The development version of 0.9.7 can have this bug, too. It
             * has been fixed on 2000/11/29.
             */
-           SSL_set_verify_result(TLScontext->con, old_session->verify_result);
+           SSL_set_verify_result(TLScontext->con, session->verify_result);
 #endif
 
        }
@@ -581,17 +678,17 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
     if (sts <= 0) {
        msg_info("SSL_connect error to %s: %d", peername, sts);
        tls_print_errors();
-       session = SSL_get_session(TLScontext->con);
-       if (session) {
-           SSL_CTX_remove_session(client_ctx, session);
-           if (var_smtp_tls_loglevel >= 2)
-               msg_info("SSL session removed");
-       }
-       SSL_free(TLScontext->con);
-       BIO_free(TLScontext->network_bio);      /* 200411 */
-       FREE_TLS_CONTEXT(TLScontext);
+       uncache_session(TLScontext);
+       tls_free_context(TLScontext);
        return (0);
     }
+
+    /*
+     * The TLS engine is active. Switch to the tls_timed_read/write()
+     * functions and make the TLScontext available to those functions.
+     */
+    tls_stream_start(stream, TLScontext);
+
     if (var_smtp_tls_loglevel >= 3 && SSL_session_reused(TLScontext->con))
        msg_info("Reusing old session");
 
@@ -600,52 +697,18 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
        BIO_set_callback(SSL_get_rbio(TLScontext->con), 0);
 
     /*
-     * Let's see whether a peer certificate is available and what is the
-     * actual information. We want to save it for later use.
+     * Do peername verification if requested and extract useful information
+     * from the certificate for later use.
      */
-    peer = SSL_get_peer_certificate(TLScontext->con);
-    if (peer != NULL) {
-       if (SSL_get_verify_result(TLScontext->con) == X509_V_OK)
-           tls_info->peer_verified = 1;
-
-       tls_info->hostname_matched = TLScontext->hostname_matched;
-
-       TLScontext->peer_CN[0] = '\0';
-       if (!X509_NAME_get_text_by_NID(X509_get_subject_name(peer),
-                                      NID_commonName, TLScontext->peer_CN,
-                                      sizeof(TLScontext->peer_CN))) {
-           msg_info("Could not parse server's subject CN");
-           tls_print_errors();
-       }
-       tls_info->peer_CN = TLScontext->peer_CN;
-
-       TLScontext->issuer_CN[0] = '\0';
-       if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
-                                      NID_commonName, TLScontext->issuer_CN,
-                                      sizeof(TLScontext->issuer_CN))) {
-           msg_info("Could not parse server's issuer CN");
-           tls_print_errors();
-       }
-       if (!TLScontext->issuer_CN[0]) {
-           /* No issuer CN field, use Organization instead */
-           if (!X509_NAME_get_text_by_NID(X509_get_issuer_name(peer),
-                               NID_organizationName, TLScontext->issuer_CN,
-                                          sizeof(TLScontext->issuer_CN))) {
-               msg_info("Could not parse server's issuer Organization");
-               tls_print_errors();
-           }
-       }
-       tls_info->issuer_CN = TLScontext->issuer_CN;
-
-       if (var_smtp_tls_loglevel >= 1) {
-           if (tls_info->peer_verified)
-               msg_info("Verified: subject_CN=%s, issuer=%s",
-                        TLScontext->peer_CN, TLScontext->issuer_CN);
-           else
-               msg_info("Unverified: subject_CN=%s, issuer=%s",
-                        TLScontext->peer_CN, TLScontext->issuer_CN);
-       }
-       X509_free(peer);
+    if ((peercert = SSL_get_peer_certificate(TLScontext->con)) != 0) {
+       verify_extract_peer(peername, peercert, TLScontext, tls_info);
+       X509_free(peercert);
+    }
+    if (enforce_peername && !TLScontext->hostname_matched) {
+       msg_info("Server certificate could not be verified for %s:"
+                " hostname mismatch", peername);
+       tls_client_stop(client_ctx, stream, timeout, 0, tls_info);
+       return (0);
     }
 
     /*
@@ -657,15 +720,10 @@ TLScontext_t *tls_client_start(SSL_CTX *client_ctx, VSTREAM *stream,
     tls_info->cipher_usebits = SSL_CIPHER_get_bits(cipher,
                                               &(tls_info->cipher_algbits));
 
-    /*
-     * The TLS engine is active. Switch to the tls_timed_read/write()
-     * functions and make the TLScontext available to those functions.
-     */
-    tls_stream_start(stream, TLScontext);
-
     if (var_smtp_tls_loglevel >= 1)
-       msg_info("TLS connection established to %s: %s with cipher %s (%d/%d bits)",
-                peername, tls_info->protocol, tls_info->cipher_name,
+       msg_info("TLS connection established to %s: %s with cipher %s"
+                " (%d/%d bits)", peername,
+                tls_info->protocol, tls_info->cipher_name,
                 tls_info->cipher_usebits, tls_info->cipher_algbits);
 
     tls_int_seed();
index 3572b1254635a04068d7ed320a429df0b6e2bd0d..61f45f820aa21b53788d751893e7bce8d3af9a4b 100644 (file)
 /*     int     tls_mgr_policy(cache_types)
 /*     int     *cache_types;
 /*
-/*     int     tls_mgr_update(cache_type, cache_id,
-/*                             openssl_version, flags, buf, len)
+/*     int     tls_mgr_update(cache_type, cache_id, buf, len)
 /*     int     cache_type;
 /*     const char *cache_id;
-/*     long    openssl_version;
-/*     int     flags;
 /*     const char *buf;
 /*     int     len;
 /*
-/*     int     tls_mgr_lookup(cache_type, cache_id,
-/*                             openssl_version, flags, buf)
+/*     int     tls_mgr_lookup(cache_type, cache_id, buf)
 /*     int     cache_type;
 /*     const char *cache_id;
-/*     long    openssl_version;
-/*     int     flags;
 /*     VSTRING *buf;
 /*
 /*     int     tls_mgr_delete(cache_type, cache_id)
 /*     One of TLS_MGR_SCACHE_CLIENT or TLS_MGR_SCACHE_SERVER (see above).
 /* .IP cache_id
 /*     The session cache lookup key.
-/* .IP openssl_version
-/*     The OpenSSL version. Sessions saved by the wrong OpenSSL version are
-/*     deleted, to avoid compatibility problems.
-/* .IP flags
-/*     Flags that must be set in the retrieved cache entry; it not,
-/*     the cache entry is deleted.
 /* .IP buf
 /*     The result or input buffer.
 /* .IP len
@@ -211,8 +199,7 @@ int     tls_mgr_policy(int *policy)
 
 /* tls_mgr_lookup - request cached session */
 
-int     tls_mgr_lookup(int cache_type, const char *cache_id,
-                              long openssl_vsn, int flags, VSTRING *buf)
+int     tls_mgr_lookup(int cache_type, const char *cache_id, VSTRING *buf)
 {
     int     status;
 
@@ -230,8 +217,6 @@ int     tls_mgr_lookup(int cache_type, const char *cache_id,
                        ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_LOOKUP,
                          ATTR_TYPE_NUM, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
-                         ATTR_TYPE_LONG, TLS_MGR_ATTR_VERSION, openssl_vsn,
-                         ATTR_TYPE_NUM, TLS_MGR_ATTR_FLAGS, flags,
                          ATTR_TYPE_END,
                          ATTR_FLAG_MISSING,    /* Reply */
                          ATTR_TYPE_NUM, TLS_MGR_ATTR_STATUS, &status,
@@ -244,7 +229,6 @@ int     tls_mgr_lookup(int cache_type, const char *cache_id,
 /* tls_mgr_update - save session to cache */
 
 int     tls_mgr_update(int cache_type, const char *cache_id,
-                              long openssl_vsn, int flags,
                               const char *buf, int len)
 {
     int     status;
@@ -263,8 +247,6 @@ int     tls_mgr_update(int cache_type, const char *cache_id,
                        ATTR_TYPE_STR, TLS_MGR_ATTR_REQ, TLS_MGR_REQ_UPDATE,
                          ATTR_TYPE_NUM, TLS_MGR_ATTR_CACHE_TYPE, cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
-                         ATTR_TYPE_LONG, TLS_MGR_ATTR_VERSION, openssl_vsn,
-                         ATTR_TYPE_NUM, TLS_MGR_ATTR_FLAGS, flags,
                          ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, len, buf,
                          ATTR_TYPE_END,
                          ATTR_FLAG_MISSING,    /* Reply */
@@ -363,24 +345,18 @@ int     main(int unused_ac, char **av)
            vstream_printf("status=%d seed=%s\n", status, STR(hex));
            vstring_free(hex);
            vstring_free(buf);
-       } else if (COMMAND(argv, "lookup", 5)) {
+       } else if (COMMAND(argv, "lookup", 3)) {
            VSTRING *buf = vstring_alloc(10);
            int     cache_type = atoi(argv->argv[1]);
-           long    openssl_vsn = atol(argv->argv[3]);
-           int     flags = atoi(argv->argv[4]);
 
-           status = tls_mgr_lookup(cache_type, argv->argv[2],
-                                   openssl_vsn, flags, buf);
+           status = tls_mgr_lookup(cache_type, argv->argv[2], buf);
            vstream_printf("status=%d session=%.*s\n",
                           status, LEN(buf), STR(buf));
-       } else if (COMMAND(argv, "update", 6)) {
+       } else if (COMMAND(argv, "update", 4)) {
            int     cache_type = atoi(argv->argv[1]);
-           long    openssl_vsn = atol(argv->argv[3]);
-           int     flags = atoi(argv->argv[4]);
 
            status = tls_mgr_update(cache_type, argv->argv[2],
-                                   openssl_vsn, flags,
-                                   argv->argv[5], strlen(argv->argv[5]));
+                                   argv->argv[3], strlen(argv->argv[3]));
            vstream_printf("status=%d\n", status);
        } else if (COMMAND(argv, "delete", 3)) {
            int     cache_type = atoi(argv->argv[1]);
@@ -391,8 +367,8 @@ int     main(int unused_ac, char **av)
            vstream_printf("usage:\n"
                           "seed byte_count\n"
                           "policy\n"
-                       "lookup cache_type cache_id openssl_version flags\n"
-               "update cache_type cache_id openssl_version flags session\n"
+                          "lookup cache_type cache_id\n"
+                          "update cache_type cache_id session\n"
                           "delete cache_type cache_id\n");
        }
        vstream_fflush(VSTREAM_OUT);
index 3b108a44888ad55b9b5db3c56e3ff895bb4628bf..92aa5f50d1199d7d6e0747771b51716070ef8e3b 100644 (file)
 #define TLS_MGR_ATTR_CACHE_TYPE        "cache_type"
 #define TLS_MGR_ATTR_SEED      "seed"
 #define TLS_MGR_ATTR_CACHE_ID  "cache_id"
-#define TLS_MGR_ATTR_VERSION   "version"
-#define TLS_MGR_ATTR_FLAGS     "flags"
 #define TLS_MGR_ATTR_SESSION   "session"
 #define TLS_MGR_ATTR_SIZE      "size"
 #define TLS_MGR_ATTR_STATUS    "status"
-#define TLS_MGR_ATTR_FLAGS     "flags"
 
  /*
   * TLS manager request status codes.
   */
 extern int tls_mgr_seed(VSTRING *, int);
 extern int tls_mgr_policy(int *);
-extern int tls_mgr_lookup(int, const char *, long, int, VSTRING *);
-extern int tls_mgr_update(int, const char *, long, int, const char *, int);
+extern int tls_mgr_lookup(int, const char *, VSTRING *);
+extern int tls_mgr_update(int, const char *, const char *, int);
 extern int tls_mgr_delete(int, const char *);
 
-#define TLS_MGR_NO_FLAGS       0
-
 /* LICENSE
 /* .ad
 /* .fi
index 03976abc7132e9eca33553f4d6545581b763d804..2efb820d272897472099215bd9e0867851fb6651 100644 (file)
@@ -7,6 +7,13 @@
 /*     #define TLS_INTERNAL
 /*     #include <tls.h>
 /*
+/*     TLScontext_t *tls_alloc_context(log_level, peername)
+/*     int     log_level;
+/*     const char *peername;
+/*
+/*     void    tls_free_context(TLScontext)
+/*     TLScontext_t *TLScontext;
+/*
 /*     void    tls_print_errors()
 /*
 /*     void    tls_info_callback(ssl, where, ret)
 /*     This module implements routines that support the TLS client
 /*     and server internals.
 /*
+/*     tls_alloc_context() creates an initialized TLScontext
+/*     structure with the specified peer name and logging level.
+/*
+/*     tls_free_context() destroys a TLScontext structure
+/*     together with OpenSSL structures that are attached to it.
+/*
 /*     tls_print_errors() queries the OpenSSL error stack,
 /*     logs the error messages, and clears the error stack.
 /*
@@ -69,6 +82,7 @@
 #include <msg.h>
 #include <mymalloc.h>
 #include <vstring.h>
+#include <stringops.h>
 
 /* TLS library. */
 
   */
 int     TLScontext_index = -1;
 
+/* tls_alloc_context - allocate TLScontext */
+
+TLScontext_t *tls_alloc_context(int log_level, const char *peername)
+{
+    TLScontext_t *TLScontext;
+
+    /*
+     * PORTABILITY: Do not assume that null pointers are all-zero bits.
+     * Use explicit assignments to initialize pointers.
+     * 
+     * See the C language FAQ item 5.17, or if you have time to burn,
+     * http://www.google.com/search?q=zero+bit+null+pointer
+     */
+    TLScontext = (TLScontext_t *) mymalloc(sizeof(TLScontext_t));
+    memset((char *) TLScontext, 0, sizeof(*TLScontext));
+    TLScontext->con = 0;
+    TLScontext->internal_bio = 0;
+    TLScontext->network_bio = 0;
+    TLScontext->serverid = 0;
+    TLScontext->log_level = log_level;
+    TLScontext->peername = lowercase(mystrdup(peername));
+
+    return (TLScontext);
+}
+
+/* tls_free_context - deallocate TLScontext and members */
+
+void    tls_free_context(TLScontext_t *TLScontext)
+{
+
+    /*
+     * Free the SSL structure and the BIOs. Warning: the internal_bio is
+     * connected to the SSL structure and is automatically freed with it. Do
+     * not free it again (core dump)!! Only free the network_bio.
+     */
+    if (TLScontext->con != 0)
+       SSL_free(TLScontext->con);
+    if (TLScontext->network_bio)
+       BIO_free(TLScontext->network_bio);
+    if (TLScontext->peername)
+       myfree(TLScontext->peername);
+    if (TLScontext->serverid)
+       myfree(TLScontext->serverid);
+    myfree((char *) TLScontext);
+}
+
 /* tls_print_errors - print and clear the error stack */
 
 void    tls_print_errors(void)
index c00789cc37641f2e900790baaf442a4c970a0ac9..f8a314db4e088934bb3d23b80f0e7938ba883b99 100644 (file)
 /*     void    tls_scache_close(cache)
 /*     TLS_SCACHE *cache;
 /*
-/*     int     tls_scache_lookup(cache, cache_id, openssl_version,
-/*                             flags, out_openssl_version, out_flags,
-/*                             out_session)
+/*     int     tls_scache_lookup(cache, cache_id, out_session)
 /*     TLS_SCACHE *cache;
 /*     const char *cache_id;
-/*     long    openssl_version;
-/*     int     flags;
-/*     long    *out_openssl_version;
-/*     int     *out_flags;
 /*     VSTRING *out_session;
 /*
-/*     int     tls_scache_update(cache, cache_id, openssl_version,
-/*                             flags, session, session_len)
+/*     int     tls_scache_update(cache, cache_id, session, session_len)
 /*     TLS_SCACHE *cache;
 /*     const char *cache_id;
-/*     long    openssl_version;
-/*     int     flags;
 /*     const char *session;
 /*     int     session_len;
 /*
-/*     int     tls_scache_sequence(cache, first_next, openssl_version, flags,
-/*                             out_cache_id, out_openssl_version, out_flags,
+/*     int     tls_scache_sequence(cache, first_next, out_cache_id,
 /*                             VSTRING *out_session)
 /*     TLS_SCACHE *cache;
 /*     int     first_next;
-/*     long    openssl_version;
-/*     int     flags;
 /*     char    **out_cache_id;
-/*     long    *out_openssl_version;
-/*     int     *out_flags;
 /*     VSTRING *out_session;
 /*
 /*     int     tls_scache_delete(cache, cache_id)
 /* DESCRIPTION
 /*     This module maintains Postfix TLS session cache files.
 /*     each session is stored under a lookup key (hostname or
-/*     session ID) together with the OpenSSL version that
-/*     created the session and application-specific flags.
-/*     Upon lookup, the OpenSSL version and flags can be
-/*     specified as optional filters. Entries that don't
-/*     satisfy the filter requirements are silently deleted.
+/*     session ID).
 /*
 /*     tls_scache_open() opens the specified TLS session cache
 /*     and returns a handle that must be used for subsequent
 /*     and releases memory that was allocated by tls_scache_open().
 /*
 /*     tls_scache_lookup() looks up the specified session in the
-/*     specified cache, and applies the session timeout, openssl
-/*     version and flags restrictions. Entries that don't satisfy
-/*     the requirements are silently deleted.
+/*     specified cache, and applies session timeout restrictions.
+/*     Entries that are too old are silently deleted.
 /*
 /*     tls_scache_update() updates the specified TLS session cache
 /*     with the specified session information.
 /*
 /*     tls_scache_sequence() iterates over the specified TLS session
-/*     cache and looks up the first or next entry. If that entry
-/*     matches the session timeout, OpenSSL version and flags
-/*     restrictions, tls_scache_sequence() saves the entry by
-/*     updating the result parameters; otherwise it deletes the
-/*     entry and does not update the result parameters.  Specify
-/*     TLS_SCACHE_SEQUENCE_NOTHING
-/*     as the third and last argument to disable OpenSSL version
-/*     and flags restrictions, and to disable saving of cache
-/*     entry content or cache entry ID information.  This is useful
-/*     when purging expired entries. A result value of zero means
-/*     that the end of the cache was reached.
+/*     cache and either returns the first or next entry that has not
+/*     timed out, or returns no data. Entries that are too old are
+/*     silently deleted. Specify TLS_SCACHE_SEQUENCE_NOTHING as the
+/*     third and last argument to disable saving of cache entry
+/*     content or cache entry ID information. This is useful when
+/*     purging expired entries. A result value of zero means that
+/*     the end of the cache was reached.
 /*
 /*     tls_scache_delete() removes the specified cache entry from
 /*     the specified TLS session cache.
 /*     (next cache element).
 /* .IP cache_id
 /*     Session cache lookup key.
-/* .IP openssl_version
-/*     When storing information, the OpenSSL version that generated a
-/*     session. When retrieving information, delete cache entries that
-/*     don't match the specified OpenSSL version.
-/*
-/*     Specify TLS_SCACHE_ANY_OPENSSL_VSN to match any OpenSSL version.
-/* .IP flags
-/*     When storing information, application flags that specify properties
-/*     of a session. When retrieving information, delete cache entries that
-/*     have the specified flags set.
-/*
-/*     Specify TLS_SCACHE_ANY_FLAGS to match any flags value.
 /* .IP session
 /*     Storage for session information.
 /* .IP session_len
 /*     The size of the session information in bytes.
 /* .IP out_cache_id
-/* .IP out_openssl_version
-/* .IP out_flags
 /* .IP out_session
-/*     Storage for saving the cache_id, openssl_version, flags
-/*     or session information of the current cache entry.
+/*     Storage for saving the cache_id or session information of the
+/*     current cache entry.
 /*
 /*     Specify TLS_SCACHE_DONT_NEED_CACHE_ID to avoid saving
 /*     the session cache ID of the cache entry.
 /*
-/*     Specify TLS_SCACHE_DONT_NEED_OPENSSL_VSN to avoid
-/*     saving the OpenSSL version in the cache entry.
-/*
-/*     Specify TLS_SCACHE_DONT_NEED_FLAGS to avoid
-/*     saving the flags information in the cache entry.
-/*
 /*     Specify TLS_SCACHE_DONT_NEED_SESSION to avoid
 /*     saving the session information in the cache entry.
 /* DIAGNOSTICS
   * database when it is opened.
   */
 typedef struct {
-    long    scache_db_version;         /* obsolete */
-    long    openssl_version;           /* clients may differ... */
     time_t  timestamp;                 /* time when saved */
-    int     flags;                     /* enforcement etc. */
     char    session[1];                        /* actually a bunch of bytes */
 } TLS_SCACHE_ENTRY;
 
-#define TLS_SCACHE_DB_VERSION  0x00000003L
-
  /*
   * SLMs.
   */
@@ -210,7 +162,6 @@ typedef struct {
 /* tls_scache_encode - encode TLS session cache entry */
 
 static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
-                                         long openssl_version, int flags,
                                          const char *session,
                                          int session_len)
 {
@@ -226,10 +177,7 @@ static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
      */
     binary_data_len = session_len + offsetof(TLS_SCACHE_ENTRY, session);
     entry = (TLS_SCACHE_ENTRY *) mymalloc(binary_data_len);
-    entry->scache_db_version = TLS_SCACHE_DB_VERSION;
-    entry->openssl_version = openssl_version;
     entry->timestamp = time((time_t *) 0);
-    entry->flags = flags;
     memcpy(entry->session, session, session_len);
 
     /*
@@ -242,13 +190,8 @@ static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
      * Logging.
      */
     if (cp->log_level >= 3)
-       msg_info("write %s TLS cache entry %s: cache_version=%ld"
-              " openssl_version=0x%lx flags=0x%x time=%ld [data %d bytes]",
-                cp->cache_label, cache_id,
-                (long) entry->scache_db_version,
-                (long) entry->openssl_version,
-                entry->flags,
-                (long) entry->timestamp,
+       msg_info("write %s TLS cache entry %s: time=%ld [data %d bytes]",
+                cp->cache_label, cache_id, (long) entry->timestamp,
                 session_len);
 
     /*
@@ -263,9 +206,6 @@ static VSTRING *tls_scache_encode(TLS_SCACHE *cp, const char *cache_id,
 
 static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
                                     const char *hex_data, int hex_data_len,
-                                    long openssl_version, int flags,
-                                    long *out_openssl_version,
-                                    int *out_flags,
                                     VSTRING *out_session)
 {
     TLS_SCACHE_ENTRY *entry;
@@ -281,8 +221,8 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
     }
 
     /*
-     * Disassemble the TLS session cache entry and enforce the restrictions
-     * specified as version numbers or flags.
+     * Disassemble the TLS session cache entry and enforce version number
+     * restrictions.
      * 
      * No early returns or we have a memory leak.
      */
@@ -294,28 +234,14 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
                 cp->cache_label, cache_id, hex_data);
        FREE_AND_RETURN(bin_data, 0);
     }
-
-    /*
-     * Before doing anything else, verify that the database format version
-     * matches this program.
-     */
     entry = (TLS_SCACHE_ENTRY *) STR(bin_data);
-    if (entry->scache_db_version != TLS_SCACHE_DB_VERSION) {
-       msg_warn("%s TLS cache: cache version mis-match for %s: 0x%lx != 0x%lx",
-                cp->cache_label, cache_id, entry->scache_db_version,
-                TLS_SCACHE_DB_VERSION);
-       FREE_AND_RETURN(bin_data, 0);
-    }
 
     /*
      * Logging.
      */
     if (cp->log_level >= 3)
-       msg_info("read %s TLS cache entry %s: cache_version=%ld"
-              " openssl_version=0x%lx time=%ld flags=0x%x [data %d bytes]",
-                cp->cache_label, cache_id, (long) entry->scache_db_version,
-                (long) entry->openssl_version, (long) entry->timestamp,
-                entry->flags,
+       msg_info("read %s TLS cache entry %s: time=%ld [data %d bytes]",
+                cp->cache_label, cache_id, (long) entry->timestamp,
                 LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session));
 
     /*
@@ -324,28 +250,9 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
     if (entry->timestamp + cp->timeout < time((time_t *) 0))
        FREE_AND_RETURN(bin_data, 0);
 
-    /*
-     * Optional restrictions.
-     */
-    if (openssl_version != 0 && entry->openssl_version != openssl_version) {
-       msg_warn("%s TLS cache: openssl version mis-match for %s: 0x%lx != 0x%lx",
-                cp->cache_label, cache_id, entry->openssl_version,
-                openssl_version);
-       FREE_AND_RETURN(bin_data, 0);
-    }
-    if (flags != 0 && (entry->flags & flags) != flags) {
-       msg_warn("%s TLS cache: flags mis-match for %s: 0x%x is not subset of 0x%x",
-                cp->cache_label, cache_id, entry->flags, flags);
-       FREE_AND_RETURN(bin_data, 0);
-    }
-
     /*
      * Optional output.
      */
-    if (out_openssl_version != 0)
-       *out_openssl_version = entry->openssl_version;
-    if (out_flags != 0)
-       *out_flags = entry->flags;
     if (out_session != 0)
        vstring_memcpy(out_session, entry->session,
                       LEN(bin_data) - offsetof(TLS_SCACHE_ENTRY, session));
@@ -359,8 +266,6 @@ static int tls_scache_decode(TLS_SCACHE *cp, const char *cache_id,
 /* tls_scache_lookup - load session from cache */
 
 int     tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
-                                 long openssl_version, int flags,
-                                 long *out_openssl_version, int *out_flags,
                                  VSTRING *session)
 {
     const char *hex_data;
@@ -369,8 +274,7 @@ int     tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
      * Logging.
      */
     if (cp->log_level >= 3)
-       msg_info("lookup %s session id=%s ssl=0x%lx flags=0x%x",
-                cp->cache_label, cache_id, openssl_version, flags);
+       msg_info("lookup %s session id=%s", cp->cache_label, cache_id);
 
     /*
      * Initialize. Don't leak data.
@@ -385,15 +289,10 @@ int     tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
        return (0);
 
     /*
-     * Decode entry and verify version and flags information.
-     * 
-     * XXX We throw away sessions when flags don't match. If we want to allow
-     * for co-existing cache entries with different flags, the flags would
-     * have to be encoded in the cache lookup key.
+     * Decode entry and verify version information.
      */
     if (tls_scache_decode(cp, cache_id, hex_data, strlen(hex_data),
-                         openssl_version, flags, out_openssl_version,
-                         out_flags, session) == 0) {
+                         session) == 0) {
        tls_scache_delete(cp, cache_id);
        return (0);
     } else {
@@ -404,7 +303,6 @@ int     tls_scache_lookup(TLS_SCACHE *cp, const char *cache_id,
 /* tls_scache_update - save session to cache */
 
 int     tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
-                                 long openssl_version, int flags,
                                  const char *buf, int len)
 {
     VSTRING *hex_data;
@@ -413,14 +311,13 @@ int     tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
      * Logging.
      */
     if (cp->log_level >= 3)
-       msg_info("put %s session id=%s ssl=0x%lx flags=0x%x [data %d bytes]",
-                cp->cache_label, cache_id, openssl_version, flags, len);
+       msg_info("put %s session id=%s [data %d bytes]",
+                cp->cache_label, cache_id, len);
 
     /*
      * Encode the cache entry.
      */
-    hex_data =
-       tls_scache_encode(cp, cache_id, openssl_version, flags, buf, len);
+    hex_data = tls_scache_encode(cp, cache_id, buf, len);
 
     /*
      * Store the cache entry.
@@ -441,11 +338,7 @@ int     tls_scache_update(TLS_SCACHE *cp, const char *cache_id,
 /* tls_scache_sequence - get first/next TLS session cache entry */
 
 int     tls_scache_sequence(TLS_SCACHE *cp, int first_next,
-                                   long openssl_version,
-                                   int flags,
                                    char **out_cache_id,
-                                   long *out_openssl_version,
-                                   int *out_flags,
                                    VSTRING *out_session)
 {
     const char *member;
@@ -468,8 +361,8 @@ int     tls_scache_sequence(TLS_SCACHE *cp, int first_next,
 
     /*
      * Find the first or next database entry. Activate the passivated entry
-     * and check the version, time stamp and flags information. Schedule the
-     * entry for deletion if it is bad or too old.
+     * and check the time stamp. Schedule the entry for deletion if it is too
+     * old.
      * 
      * Save the member (cache id) so that it will not be clobbered by the
      * tls_scache_lookup() call below.
@@ -477,9 +370,7 @@ int     tls_scache_sequence(TLS_SCACHE *cp, int first_next,
     found_entry = (dict_seq(cp->db, first_next, &member, &value) == 0);
     if (found_entry) {
        keep_entry = tls_scache_decode(cp, member, value, strlen(value),
-                                      openssl_version, flags,
-                                      out_openssl_version,
-                                      out_flags, out_session);
+                                      out_session);
        if (keep_entry && out_cache_id)
            *out_cache_id = mystrdup(member);
        saved_member = mystrdup(member);
@@ -494,9 +385,7 @@ int     tls_scache_sequence(TLS_SCACHE *cp, int first_next,
        cp->flags &= ~TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
        saved_cursor = cp->saved_cursor;
        cp->saved_cursor = 0;
-       tls_scache_lookup(cp, saved_cursor, cp->saved_openssl_version,
-                         cp->saved_flags, (long *) 0, (int *) 0,
-                         (VSTRING *) 0);
+       tls_scache_lookup(cp, saved_cursor, (VSTRING *) 0);
        myfree(saved_cursor);
     }
 
@@ -517,11 +406,8 @@ int     tls_scache_sequence(TLS_SCACHE *cp, int first_next,
      */
     if (found_entry) {
        cp->saved_cursor = saved_member;
-       if (keep_entry == 0) {
+       if (keep_entry == 0)
            cp->flags |= TLS_SCACHE_FLAG_DEL_SAVED_CURSOR;
-           cp->saved_openssl_version = openssl_version;
-           cp->saved_flags = flags;
-       }
     }
     return (found_entry);
 }
index 2e51d2803bc1c7db700830017604c245fc5fe72a..dd545d656f7a8b76520c0456a05e020cdded6fcd 100644 (file)
@@ -27,31 +27,22 @@ typedef struct {
     int     log_level;                 /* smtp(d)_tls_log_level */
     int     timeout;                   /* smtp(d)_tls_session_cache_timeout */
     char   *saved_cursor;              /* cursor cache ID */
-    long    saved_openssl_version;     /* cursor OpenSSL version */
-    int     saved_flags;               /* cursor lookup flags */
 } TLS_SCACHE;
 
 #define TLS_SCACHE_FLAG_DEL_SAVED_CURSOR       (1<<0)
 
 extern TLS_SCACHE *tls_scache_open(const char *, const char *, int, int);
 extern void tls_scache_close(TLS_SCACHE *);
-extern int tls_scache_lookup(TLS_SCACHE *, const char *, long, int, long *, int *, VSTRING *);
-extern int tls_scache_update(TLS_SCACHE *, const char *, long, int, const char *, int);
+extern int tls_scache_lookup(TLS_SCACHE *, const char *, VSTRING *);
+extern int tls_scache_update(TLS_SCACHE *, const char *, const char *, int);
 extern int tls_scache_delete(TLS_SCACHE *, const char *);
-extern int tls_scache_sequence(TLS_SCACHE *, int, long, int, char **, long *, int *, VSTRING *);
-
-#define TLS_SCACHE_ANY_OPENSSL_VSN             ((long) 0)
-#define TLS_SCACHE_ANY_FLAGS                   (0)
+extern int tls_scache_sequence(TLS_SCACHE *, int, char **, VSTRING *);
 
 #define TLS_SCACHE_DONT_NEED_CACHE_ID          ((char **) 0)
-#define TLS_SCACHE_DONT_NEED_OPENSSL_VSN       ((long *) 0)
-#define TLS_SCACHE_DONT_NEED_FLAGS             ((int *) 0)
 #define TLS_SCACHE_DONT_NEED_SESSION           ((VSTRING *) 0)
 
 #define TLS_SCACHE_SEQUENCE_NOTHING \
-       TLS_SCACHE_ANY_FLAGS, TLS_SCACHE_ANY_OPENSSL_VSN, \
-       TLS_SCACHE_DONT_NEED_CACHE_ID, TLS_SCACHE_DONT_NEED_OPENSSL_VSN, \
-       TLS_SCACHE_DONT_NEED_FLAGS, TLS_SCACHE_DONT_NEED_SESSION
+       TLS_SCACHE_DONT_NEED_CACHE_ID, TLS_SCACHE_DONT_NEED_SESSION
 
 /* LICENSE
 /* .ad
index d71955eabd11be536fb9975b3b560bb245ab3d5f..488d116b8e4560d94d1db190a4934085504389f2 100644 (file)
@@ -143,13 +143,6 @@ static char server_session_id_context[] = "Postfix/TLS";
 
 static int tls_server_cache = 0;
 
-/* server_verify_callback - server verification wrapper */
-
-static int server_verify_callback(int ok, X509_STORE_CTX *ctx)
-{
-    return (tls_verify_certificate_callback(ok, ctx, TLS_VERIFY_DEFAULT));
-}
-
 /* get_server_session_cb - callback to retrieve session from server cache */
 
 static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
@@ -161,13 +154,10 @@ static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
     VSTRING *session_data = vstring_alloc(2048);
     SSL_SESSION *session = 0;
 
-#define MAKE_SERVER_CACHE_ID(id, len) \
+#define HEX_CACHE_ID(id, len) \
     hex_encode(vstring_alloc(2 * (len) + 1), (char *) (id), (len))
 
-    /*
-     * Encode the session ID.
-     */
-    cache_id = MAKE_SERVER_CACHE_ID(session_id, session_id_length);
+    cache_id = HEX_CACHE_ID(session_id, session_id_length);
     if (var_smtpd_tls_loglevel >= 3)
        msg_info("looking up session %s in server cache", STR(cache_id));
 
@@ -175,7 +165,6 @@ static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
      * Load the session from cache and decode it.
      */
     if (tls_mgr_lookup(tls_server_cache, STR(cache_id),
-                      OPENSSL_VERSION_NUMBER, TLS_MGR_NO_FLAGS,
                       session_data) == TLS_MGR_STAT_OK) {
        session = tls_session_activate(STR(session_data), LEN(session_data));
        if (session && (var_smtpd_tls_loglevel >= 3))
@@ -191,6 +180,23 @@ static SSL_SESSION *get_server_session_cb(SSL *unused_ssl,
     return (session);
 }
 
+/* uncache_session - remove session from internal & external cache */
+
+static void uncache_session(SSL_CTX *ctx, TLScontext_t *TLScontext)
+{
+    VSTRING *cache_id;
+    SSL_SESSION *session = SSL_get_session(TLScontext->con);
+
+    SSL_CTX_remove_session(ctx, session);
+
+    cache_id = HEX_CACHE_ID(session->session_id, session->session_id_length);
+    if (var_smtpd_tls_loglevel >= 3)
+       msg_info("remove session %s from server cache", STR(cache_id));
+
+    tls_mgr_delete(tls_server_cache, STR(cache_id));
+    vstring_free(cache_id);
+}
+
 /* new_server_session_cb - callback to save session to server cache */
 
 static int new_server_session_cb(SSL *unused_ssl, SSL_SESSION *session)
@@ -198,11 +204,7 @@ static int new_server_session_cb(SSL *unused_ssl, SSL_SESSION *session)
     VSTRING *cache_id;
     VSTRING *session_data;
 
-    /*
-     * Encode the session ID.
-     */
-    cache_id =
-       MAKE_SERVER_CACHE_ID(session->session_id, session->session_id_length);
+    cache_id = HEX_CACHE_ID(session->session_id, session->session_id_length);
     if (var_smtpd_tls_loglevel >= 3)
        msg_info("save session %s to server cache", STR(cache_id));
 
@@ -212,7 +214,6 @@ static int new_server_session_cb(SSL *unused_ssl, SSL_SESSION *session)
     session_data = tls_session_passivate(session);
     if (session_data)
        tls_mgr_update(tls_server_cache, STR(cache_id),
-                      OPENSSL_VERSION_NUMBER, TLS_MGR_NO_FLAGS,
                       STR(session_data), LEN(session_data));
 
     /*
@@ -381,16 +382,25 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
      */
     if (askcert)
        verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
-    SSL_CTX_set_verify(server_ctx, verify_flags, server_verify_callback);
+    SSL_CTX_set_verify(server_ctx, verify_flags,
+                      tls_verify_certificate_callback);
     if (*var_smtpd_tls_CAfile)
        SSL_CTX_set_client_CA_list(server_ctx,
                             SSL_load_client_CA_file(var_smtpd_tls_CAfile));
 
     /*
-     * Initialize the session cache. In order to share cached sessions among
-     * multiple SMTP server processes, we use an external cache and set the
-     * internal cache size to a minimum value of 1. Access to the external
-     * cache is handled by the appropriate callback functions.
+     * Initialize the session cache.
+     * 
+     * With a large number of concurrent smtpd(8) processes, it is not a good
+     * idea to cache multiple large session objects in each process. We set
+     * the internal cache size to 1, and don't register a "remove_cb" so as
+     * to avoid deleting good sessions from the external cache prematurely
+     * (when the internal cache is full, OpenSSL removes sessions from the
+     * external cache also)!
+     * 
+     * This makes SSL_CTX_remove_session() not useful for flushing broken
+     * sessions from the external cache, so we must delete them directly (not
+     * via a callback).
      * 
      * Set a session id context to identify to what type of server process
      * created a session. In our case, the context is simply the name of the
@@ -415,7 +425,8 @@ SSL_CTX *tls_server_init(int unused_verifydepth, int askcert)
     if (tls_mgr_policy(&cache_types) == TLS_MGR_STAT_OK
        && (tls_server_cache = (cache_types & TLS_MGR_SCACHE_SERVER)) != 0) {
        SSL_CTX_set_session_cache_mode(server_ctx,
-                     SSL_SESS_CACHE_SERVER | SSL_SESS_CACHE_NO_AUTO_CLEAR);
+                                      SSL_SESS_CACHE_SERVER |
+                                      SSL_SESS_CACHE_NO_AUTO_CLEAR);
        SSL_CTX_sess_set_get_cb(server_ctx, get_server_session_cb);
        SSL_CTX_sess_set_new_cb(server_ctx, new_server_session_cb);
     }
@@ -448,7 +459,6 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
     int     verify_flags;
     unsigned int n;
     TLScontext_t *TLScontext;
-    SSL_SESSION *session;
     SSL_CIPHER *cipher;
     X509   *peer;
 
@@ -459,29 +469,19 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
      * Allocate a new TLScontext for the new connection and get an SSL
      * structure. Add the location of TLScontext to the SSL to later retrieve
      * the information inside the tls_verify_certificate_callback().
-     * 
-     * XXX Need a dedicated procedure for consistent initialization of all the
-     * fields in this structure.
      */
-#define PEERNAME_SIZE sizeof(TLScontext->peername_save)
-
-    NEW_TLS_CONTEXT(TLScontext);
-    TLScontext->log_level = var_smtpd_tls_loglevel;
-    strncpy(TLScontext->peername_save, peername, PEERNAME_SIZE - 1);
-    TLScontext->peername_save[PEERNAME_SIZE - 1] = 0;
-    (void) lowercase(TLScontext->peername_save);
+    TLScontext = tls_alloc_context(var_smtpd_tls_loglevel, peername);
 
     if ((TLScontext->con = (SSL *) SSL_new(server_ctx)) == NULL) {
        msg_info("Could not allocate 'TLScontext->con' with SSL_new()");
        tls_print_errors();
-       FREE_TLS_CONTEXT(TLScontext);
+       tls_free_context(TLScontext);
        return (0);
     }
     if (!SSL_set_ex_data(TLScontext->con, TLScontext_index, TLScontext)) {
        msg_info("Could not set application data for 'TLScontext->con'");
        tls_print_errors();
-       SSL_free(TLScontext->con);
-       FREE_TLS_CONTEXT(TLScontext);
+       tls_free_context(TLScontext);
        return (0);
     }
 
@@ -493,7 +493,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
        verify_flags = SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE;
        verify_flags |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT;
        TLScontext->enforce_verify_errors = 1;
-       SSL_set_verify(TLScontext->con, verify_flags, server_verify_callback);
+       SSL_set_verify(TLScontext->con, verify_flags,
+                      tls_verify_certificate_callback);
     } else {
        TLScontext->enforce_verify_errors = 0;
     }
@@ -511,8 +512,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
                          &TLScontext->network_bio, TLS_BIO_BUFSIZE)) {
        msg_info("Could not obtain BIO_pair");
        tls_print_errors();
-       SSL_free(TLScontext->con);
-       FREE_TLS_CONTEXT(TLScontext);
+       tls_free_context(TLScontext);
        return (0);
     }
 
@@ -559,9 +559,7 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
     if (sts <= 0) {
        msg_info("SSL_accept error from %s[%s]: %d", peername, peeraddr, sts);
        tls_print_errors();
-       SSL_free(TLScontext->con);
-       BIO_free(TLScontext->network_bio);      /* 200411 */
-       FREE_TLS_CONTEXT(TLScontext);
+       tls_free_context(TLScontext);
        return (0);
     }
     /* Only loglevel==4 dumps everything */
@@ -651,11 +649,8 @@ TLScontext_t *tls_server_start(SSL_CTX *server_ctx, VSTREAM *stream,
     if (requirecert) {
        if (!tls_info->peer_verified || !tls_info->peer_CN) {
            msg_info("Re-used session without peer certificate removed");
-           session = SSL_get_session(TLScontext->con);
-           SSL_CTX_remove_session(server_ctx, session);
-           SSL_free(TLScontext->con);
-           BIO_free(TLScontext->network_bio);  /* 200411 */
-           FREE_TLS_CONTEXT(TLScontext);
+           uncache_session(server_ctx, TLScontext);
+           tls_free_context(TLScontext);
            return (0);
        }
     }
index 8ffe56923e5d661ee1068023a561ad60f6cf5a3f..8caf341f0b32e038a6f0e7ced5a982d5a2d3ffab 100644 (file)
@@ -101,22 +101,8 @@ void    tls_session_stop(SSL_CTX *ctx, VSTREAM *stream, int timeout,
        if (retval == 0)
            tls_bio_shutdown(vstream_fileno(stream), timeout, TLScontext);
     }
-
-    /*
-     * Free the SSL structure and the BIOs. Warning: the internal_bio is
-     * connected to the SSL structure and is automatically freed with it. Do
-     * not free it again (core dump)!! Only free the network_bio.
-     * 
-     * XXX SSL_CTX_flush_sessions() searches memory for expired sessions and
-     * removes them from memory and external cache.
-     */
-    SSL_free(TLScontext->con);
-
-    BIO_free(TLScontext->network_bio);
-    FREE_TLS_CONTEXT(TLScontext);
+    tls_free_context(TLScontext);
     tls_stream_stop(stream);
-    SSL_CTX_flush_sessions(ctx, time(NULL));
-
     *tls_info = tls_info_zero;
 }
 
index 2d4fb092e158743776f656a711cadf3534233328..508ba7f3cd75444960995742980a57003f17ba3e 100644 (file)
@@ -7,10 +7,9 @@
 /*     #define TLS_INTERNAL
 /*     #include <tls.h>
 /*
-/*     int     tls_verify_certificate_callback(ok, ctx, int flags)
+/*     int     tls_verify_certificate_callback(ok, ctx)
 /*     int     ok;
 /*     X509_STORE_CTX *ctx;
-/*     int     flags;
 /* DESCRIPTION
 /*     tls_verify_callback() is called several times (directly or
 /*     indirectly) from crypto/x509/x509_vfy.c. It is called as
 /* .IP ctx
 /*     TLS client or server context. This also specifies the
 /*     TLScontext with enforcement options.
-/* .IP flags
-/* .RS
-/* .IP TLS_VERIFY_PEERNAME
-/*     Verify the peer hostname against the names listed
-/*     in the peer certificate. The peer hostname is specified
-/*     via the ctx argument.
-/* .RE
 /* LICENSE
 /* .ad
 /* .fi
 #define TLS_INTERNAL
 #include <tls.h>
 
-/* match_hostname -  match hostname against pattern */
-
-static int match_hostname(const char *pattern, const char *hostname)
-{
-    char   *peername_left;
-
-    return (strcasecmp(hostname, pattern) == 0
-           || (pattern[0] == '*' && pattern[1] == '.' && pattern[2] != 0
-               && (peername_left = strchr(hostname, '.')) != 0
-               && strcasecmp(peername_left + 1, pattern + 2) == 0));
-}
-
 /* tls_verify_certificate_callback - verify peer certificate info */
 
-int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx, int flags)
+int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx)
 {
     char    buf[1024];
     X509   *err_cert;
@@ -170,78 +150,15 @@ int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx, int flags)
     }
     if (!ok) {
        msg_info("certificate verification failed for %s: num=%d:%s",
-                TLScontext->peername_save, err,
+                TLScontext->peername, err,
                 X509_verify_cert_error_string(err));
     }
 
     /*
-     * Match the peer hostname against the names listed in the peer
-     * certificate.
+     * We delay peername verification until the SSL handshake completes. The
+     * peername verification previously done here is now called directly from
+     * tls_client_start(). This substantially simplifies the cache interface.
      */
-    if (ok && (depth == 0) && (flags & TLS_VERIFY_PEERNAME)) {
-       int     i,
-               r;
-       int     hostname_matched;
-       int     dNSName_found;
-
-       STACK_OF(GENERAL_NAME) * gens;
-
-       /*
-        * Verify the name(s) in the peer certificate against the peer
-        * hostname. Log peer hostname/certificate mis-matches. If a match is
-        * required but fails, bail out with a verification error.
-        */
-       hostname_matched = dNSName_found = 0;
-
-       gens = X509_get_ext_d2i(err_cert, NID_subject_alt_name, 0, 0);
-       if (gens) {
-           for (i = 0, r = sk_GENERAL_NAME_num(gens); i < r; ++i) {
-               const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, i);
-
-               if (gn->type == GEN_DNS) {
-                   dNSName_found++;
-                   if ((hostname_matched =
-                        match_hostname((char *) gn->d.ia5->data,
-                                       TLScontext->peername_save)))
-                       break;
-               }
-           }
-           sk_GENERAL_NAME_free(gens);
-       }
-       if (dNSName_found) {
-           if (!hostname_matched)
-               msg_info("certificate peer name verification failed for %s: "
-                      "%d dNSNames in certificate found, but none matches",
-                        TLScontext->peername_save, dNSName_found);
-       } else {
-           buf[0] = '\0';
-           if (!X509_NAME_get_text_by_NID(X509_get_subject_name(err_cert),
-                                       NID_commonName, buf, sizeof(buf))) {
-               msg_info("certificate peer name verification failed for %s:"
-                        "cannot parse subject CommonName",
-                        TLScontext->peername_save);
-               tls_print_errors();
-           } else {
-               hostname_matched = match_hostname(buf,
-                                                 TLScontext->peername_save);
-               if (!hostname_matched)
-                   msg_info("certificate peer name verification failed for %s:"
-                            " CommonName mis-match: %s",
-                            TLScontext->peername_save, buf);
-           }
-       }
-
-       if (!hostname_matched) {
-           if (TLScontext->enforce_verify_errors && TLScontext->enforce_CN) {
-               err = X509_V_ERR_CERT_REJECTED;
-               X509_STORE_CTX_set_error(ctx, err);
-               msg_info("certificate peer name verification failed for %s:"
-                        " hostname mismatch", TLScontext->peername_save);
-               ok = 0;
-           }
-       } else
-           TLScontext->hostname_matched = 1;
-    }
 
     /*
      * Other causes for verification failure.
@@ -252,19 +169,19 @@ int     tls_verify_certificate_callback(int ok, X509_STORE_CTX *ctx, int flags)
                          buf, sizeof(buf));
        msg_info("certificate verification failed for %s:"
                 "issuer %s certificate unavailable",
-                TLScontext->peername_save, buf);
+                TLScontext->peername, buf);
        break;
     case X509_V_ERR_CERT_NOT_YET_VALID:
     case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD:
        msg_info("certificate verification failed for %s:"
                 "certificate not yet valid",
-                TLScontext->peername_save);
+                TLScontext->peername);
        break;
     case X509_V_ERR_CERT_HAS_EXPIRED:
     case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD:
        msg_info("certificate verification failed for %s:"
                 "certificate has expired",
-                TLScontext->peername_save);
+                TLScontext->peername);
        break;
     }
     if (TLScontext->log_level >= 2)
index cd2b1d92773114ead151e39495908fd9af3b9db6..5651523977761dad273dc0c4c01ab797c29f2db7 100644 (file)
@@ -521,8 +521,6 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
     static VSTRING *buffer = 0;
     int     cache_type;
     int     len;
-    long    openssl_vsn;
-    int     flags;
     static char wakeup[] = {           /* master wakeup request */
        TRIGGER_REQ_WAKEUP,
        0,
@@ -560,20 +558,13 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
            if (attr_scan(client_stream, ATTR_FLAG_STRICT,
                        ATTR_TYPE_NUM, TLS_MGR_ATTR_CACHE_TYPE, &cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
-                         ATTR_TYPE_LONG, TLS_MGR_ATTR_VERSION, &openssl_vsn,
-                         ATTR_TYPE_NUM, TLS_MGR_ATTR_FLAGS, &flags,
-                         ATTR_TYPE_END) == 4) {
+                         ATTR_TYPE_END) == 2) {
                if ((cache = WHICH_CACHE_INFO(cache_type)) == 0) {
                    msg_warn("bogus cache type \"%d\" in \"%s\" request",
                             cache_type, TLS_MGR_REQ_LOOKUP);
                    VSTRING_RESET(buffer);
                } else {
-                   status =
-                       tls_scache_lookup(cache, STR(cache_id), openssl_vsn,
-                                         flags,
-                                         TLS_SCACHE_DONT_NEED_OPENSSL_VSN,
-                                         TLS_SCACHE_DONT_NEED_FLAGS,
-                                         buffer) ?
+                   status = tls_scache_lookup(cache, STR(cache_id), buffer) ?
                        TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
                }
            }
@@ -591,17 +582,15 @@ static void tlsmgr_service(VSTREAM *client_stream, char *unused_service,
            if (attr_scan(client_stream, ATTR_FLAG_STRICT,
                        ATTR_TYPE_NUM, TLS_MGR_ATTR_CACHE_TYPE, &cache_type,
                          ATTR_TYPE_STR, TLS_MGR_ATTR_CACHE_ID, cache_id,
-                         ATTR_TYPE_LONG, TLS_MGR_ATTR_VERSION, &openssl_vsn,
-                         ATTR_TYPE_NUM, TLS_MGR_ATTR_FLAGS, &flags,
                          ATTR_TYPE_DATA, TLS_MGR_ATTR_SESSION, buffer,
-                         ATTR_TYPE_END) == 5) {
+                         ATTR_TYPE_END) == 3) {
                if ((cache = WHICH_CACHE_INFO(cache_type)) == 0) {
                    msg_warn("bogus cache type \"%d\" in \"%s\" request",
                             cache_type, TLS_MGR_REQ_UPDATE);
                } else {
                    status =
-                       tls_scache_update(cache, STR(cache_id), openssl_vsn,
-                                         flags, STR(buffer), LEN(buffer)) ?
+                       tls_scache_update(cache, STR(cache_id),
+                                         STR(buffer), LEN(buffer)) ?
                        TLS_MGR_STAT_OK : TLS_MGR_STAT_ERR;
                }
            }
index 7f5b38a54f48856fbd685714deeff8709ba00d34..858cba06622c7fda4f48d9ff9a54ab61a56670e1 100644 (file)
@@ -61,6 +61,7 @@
 #include <sent.h>
 #include <mail_params.h>
 #include <mail_addr_find.h>
+#include <dsn_util.h>
 
 /* Application-specific. */
 
@@ -139,9 +140,10 @@ static int deliver_mailbox_file(LOCAL_STATE state, USER_ATTR usr_attr)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+                         defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, why->dsn),
+            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
             "mailbox %s: %s", usr_attr.mailbox, vstring_str(why->vstring));
     } else {
        deliver_status = sent(BOUNCE_FLAGS(state.request),
index 497c8b15e90213d2f132fc6bc843145d8f5b8cfc..9f9d6b4320eab1da99447d49e369f6df00d2fc59 100644 (file)
@@ -61,6 +61,7 @@
 #include <sent.h>
 #include <mail_params.h>
 #include <mbox_open.h>
+#include <dsn_util.h>
 
 /* Application-specific. */
 
@@ -220,9 +221,10 @@ int     deliver_maildir(LOCAL_STATE state, USER_ATTR usr_attr)
     if (mail_copy_status & MAIL_COPY_STAT_CORRUPT) {
        deliver_status = DEL_STAT_DEFER;
     } else if (mail_copy_status != 0) {
-       deliver_status = (why->dsn[0] == '4' ? defer_append : bounce_append)
+       deliver_status = (DSN_CLASS(why->dsn) == '4' ?
+                         defer_append : bounce_append)
            (BOUNCE_FLAGS(state.request),
-            BOUNCE_ATTR(state.msg_attr, why->dsn),
+            BOUNCE_ATTR(state.msg_attr, DSN_CODE(why->dsn)),
             "maildir delivery failed: %s", vstring_str(why->vstring));
        if (errno == EACCES) {
            msg_warn("maildir access problem for UID/GID=%lu/%lu: %s",