2
0
mirror of https://github.com/vdukhovni/postfix synced 2025-08-30 13:48:06 +00:00

postfix-2.0.16-20031214

This commit is contained in:
Wietse Venema 2003-12-14 00:00:00 -05:00 committed by Viktor Dukhovni
parent 7a646f52bf
commit 0b73da41c8
27 changed files with 630 additions and 390 deletions

View File

@ -8832,6 +8832,29 @@ Apologies for any names omitted.
because the deliver_pass.c module was not updated for the
changed message delivery protocol.
20031211
Safety: in dynamically growing data structures, update the
length info after (instead of before) updating the data
size. Files: util/argv.c, util/inet_addrlist.c, util/intv.c,
util/mvect.c, util/vstring.c, global/recipient_list.c,
*qmgr/qmgr_rcpt_list.c.
20031212
Cleanup: separate extensions XCLIENT (impersonate SMTP
client) and XFORWARD (down-stream logging of up-stream
session information, not necessarily SMTP related). The
protocol is extensible: the server advertises what attributes
XCLIENT or XFORWARD will accept. The requirement for xtext
encoding is dropped as no attribute currently needs it.
All errors are reported by the server and are logged by
the client. See also: XCLIENT_README and XFORWARD_README.
20031214
Feature: XCLIENT and XFORWARD support in the LMTP client.
Open problems:
High: when virtual aliasing is turned off after content

View File

@ -1,182 +1,98 @@
Purpose of the XCLIENT extension to SMTP
========================================
The XCLIENT command targets problems in the following areas:
The XCLIENT command targets the following problems:
1 - Access control tests. SMTP server access rules are difficult
to verify when decisions can be triggered only by remote clients.
In order to facilitate access rule testing, an authorized SMTP
client test program needs the ability to override the SMTP server's
idea of the SMTP client hostname, network address, and other
idea of the SMTP client hostname, network address, and other client
information, for the entire duration of an SMTP session.
2 - Logging after SMTP-based content filter. With the deployment
of Internet->MTA1->filter->MTA2 style content filter applications,
remote client information is lost when MTA1 gives the mail to the
content filter. To simplify the interpretation of MTA2 logging,
it would help if MTA1 could forward client information through the
content filter to MTA2.
2 - Client software that downloads mail from an up-stream mail
server and injects it into a local MTA via SMTP. In order to take
advantage of the local MTA's access rules, the client software
needs the ability to override the SMTP server's idea of the remote
client name, client address and other information. Such information
can typically be extracted from the up-stream mail server's Received:
message header.
3 - Post-filter access control and logging. With Internet->filter->MTA
style content filter applications, the filter can be simplified if
it can delegate decisions concerning mail relay and other access
control to the MTA. As in the first example, this requires that
the filter can override the MTA's idea of the SMTP client hostname,
network address, and other information, for the entire duration of
an SMTP session.
control to the MTA. This is especially useful when the filter acts
as a transparent proxy for SMTP commands. As in the first example,
this requires that the filter can override the MTA's idea of the
SMTP client hostname, network address, and other information.
4 - Fetchmail.
Command syntax
==============
Command overview
================
The EHLO keyword associated with this extension is XCLIENT. In EHLO
replies, XCLIENT is followed by the names of the supported XCLIENT
functions.
The XCLIENT OVERRIDE function updates remote client attributes that
the MTA normally uses for access control, message headers, logging
and so on, for the duration of an entire SMTP session.
The XCLIENT FORWARD function updates temporary remote client
attributes that the MTA uses for transaction logging. These attributes
are valid for only one message delivery attempt. In the absence
of forwarded attributes the MTA must use the normal remote client
attribute values.
In SMTP server EHLO replies, the keyword associated with this
extension is XCLIENT. It is followed by the names of the attributes
that the XCLIENT implementation supports.
The general command syntax is described below. Upper case and
quoted strings specify terminals, lowercase strings specify meta
terminals, SP is whitespace, and descriptive text is enclosed in
{}. Although command and attribute names are shown below in upper
case, they are in fact case insensitive.
terminals, and SP is whitespace. Although command and attribute
names are shown in upper case, they are in fact case insensitive.
xclient-command = XCLIENT SP function SP 1*( attribute )
function = ( OVERRIDE | FORWARD )
xclient-command = XCLIENT 1*( SP attribute )
attribute = name"="value
name = ( NAME|ADDR|CODE|PROTO|HELO )
value = ( { empty } | xtext )
xtext = { attribute value encoded as per RFC 1891 }
name = ( NAME | ADDR | PROTO | HELO )
The XCLIENT command can be sent at any time except in the middle
of a mail delivery transaction (i.e. between MAIL and DOT). The
command may be pipelined after the server EHLO reply announces
ESMTP pipelining support.
command may be pipelined if the server EHLO reply announces ESMTP
pipelining support.
The server reply codes are as follows:
The XCLIENT reply codes are as follows:
Code | Meaning
-----|------------
250 | success
501 | bad command parameter
501 | bad command parameter syntax
503 | mail transaction in progress
421 | unable to proceed
The server must report success in case of an unrecognized attribute
name, although it may log a warning.
The NAME attribute specifies an SMTP client hostname (not an SMTP
client address), [unavailable] when client hostname lookup failed
due to a permanent error, or [temporary] when the lookup error
condition was transient.
Specific usage scenarios
========================
The ADDR attribute specifies an SMTP client numerical IPv4 network
address, an IPv6 address prefixed with IPV6:, or [unavailable]
when the address information is unavailable. Address information
is not enclosed with [].
This section discusses the semantics of XCLIENT requests. Specific
syntax details are given in the next section.
The PROTO attribute specifies either SMTP or ESMTP.
The XCLIENT OVERRIDE request modifies remote client attributes that
the MTA normally uses for access control, message headers, logging,
and for other purposes, for the duraction of the entire SMTP session.
Attributes that are not specified in XCLIENT OVERRIDE requests are
not modified.
The HELO attribute specifies a HELO parameter value, or the value
[unavailable] when the information is unavailable.
The following example overrides only the client hostname and network
address, leaving unchanged all other client attributes such as the
mail protocol or the hostname given in the HELO command:
XCLIENT OVERRIDE NAME=spike.porcupine.org
XCLIENT OVERRIDE ADDR=168.100.189.2
The XCLIENT FORWARD request specifies remote client attributes that
are logged with one message delivery attempt. The attributes are
discarded after the MAIL FROM transaction finishes. In the absence
of any XCLIENT FORWARD attributes, the MTA must use the normal
client attributes.
If only a subset of all possible XCLIENT FORWARD attributes is
specified, the unspecified attributes must be treated as if they
are unknown. The implementation must not replace missing XCLIENT
FORWARD attributes by normal attributes.
The following example updates all forwarded client attributes that
are defined in this document, leaving none at their default unknown
value:
XCLIENT FORWARD NAME=spike.porcupine.org ADDR=168.100.189.2
XCLIENT FORWARD HELO=spike.porcupine.org PROTO=ESMTP
Note 1: attributes specified with successive XCLIENT commands
accumulate.
Note 2: XCLIENT FORWARD attributes take precedence over XCLIENT
OVERRIDE attributes.
Attribute value details
=======================
Attribute values are encoded as RFC 1891 xtext strings. To explicitly
specify that an attribute value is unavailable, the value must be
empty; the client must not send its own internal representation of
unavailable information.
The NAME_CODE attribute specifies NAME hostname lookup status
information. Values are OK (success), TEMP (temporary lookup
failure) or PERM (permanent lookup failure). When CODE is set to
any value other than OK, the NAME attribute is automatically set
to the unknown value.
The NAME attribute specifies a name space (typically, DNS) and
an MTA name within that name space.
and not a numerical address. When a null client name is specified
(i.e. the client name is unknown), the CODE attribute is implicitly
set to PERM. When a valid domain name is specified, CODE is implicitly
set to OK. The server may process a syntactically invalid domain
name as if it were unknown.
The ADDR attribute must specify a numerical network address without
[].
The PROTO attribute should be a string of up to 64 printable
characters, where printable is defined by the ANSI C isascii() and
isprint() predicates.
The HELO attribute should be a syntactically valid HELO
parameter value.
Note 3: syntactically valid NAME and HELO attributes can be up to
Note 1: syntactically valid NAME and HELO attributes can be up to
255 characters long. The client must not send XCLIENT commands that
exceed the 512 character limit of SMTP commands.
exceed the 512 character limit for SMTP commands.
Note 4: attribute values may end up in Received: or other message
headers. The receiving MTA may substitute characters in order to
not violate RFC 822 or RFC 2822.
Note 2: [UNAVAILABLE], [TEMPORARY] and IPV6: may be specified in
upper case, lower case or mixed case.
Security
========
The XCLIENT command changes audit trails and/or client access
permissions. For these reasons, use of these commands must be
restricted to authorized clients only.
permissions. Use of this command must be restricted to authorized
clients.
The examples in this document assume that XCLIENT does not override
its own access control mechanism.
The XCLIENT should not override its own access control mechanism.
SMTP connection caching
=======================
SMTP connection caching makes it possible to deliver multiple
messages within the same SMTP session. Thus, one persistent SMTP
session with a content filter can carry messages from unrelated
clients. The XCLIENT FORWARD attributes are reset after the MAIL
FROM command completes, so there is no risk of information leakage.
messages within the same SMTP session. The XCLIENT attributes are
persistent across deliveries, and need to be reset as appropriate
in order to avoid information leakage.

View File

@ -0,0 +1,95 @@
Purpose of the XFORWARD extension to SMTP
========================================
The XFORWARD command targets the following problem:
- Logging after SMTP-based content filter. With the deployment of
Internet->MTA1->filter->MTA2 style content filter applications,
the logging of client and message identifying information changes
when MTA1 gives the mail to the content filter. To simplify the
interpretation of MTA2 logging, it would help if MTA1 could forward
remote client and/or message identifying information through the
content filter to MTA2, so that the information could be logged as
part of mail handling transactions.
Command syntax
==============
In SMTP server EHLO replies, the keyword associated with this
extension is XFORWARD. The keyword is followed by the names of the
attributes that the XFORWARD implementation supports.
The general command syntax is described below. Upper case and
quoted strings specify terminals, lowercase strings specify meta
terminals, and SP is whitespace. Although command and attribute
names are shown in upper case, they are in fact case insensitive.
xclient-command = XFORWARD 1*( SP attribute )
attribute = name"="value
name = ( NAME | ADDR | PROTO | HELO | IDENT )
The XFORWARD command can be sent at any time except in the middle
of a mail delivery transaction (i.e. between MAIL and DOT). The
command may be pipelined if the server EHLO reply announces ESMTP
pipelining support.
The XFORWARD reply codes are as follows:
Code | Meaning
-----|------------
250 | success
501 | bad command parameter syntax
503 | mail transaction in progress
421 | unable to proceed
The information specified with XFORWARD attribues is not limited
to DNS hostnames, IP addresses or SMTP protocol names. Attribute
values may contain arbitrary text, but must not contain control
characters, non-ASCII characters, whitespace, or other characters
that are special in message headers.
The NAME attribute specifies an up-stream client hostname, or
[UNAVAILABLE] when the information is unavailable. The hostname
may be a non-DNS hostname.
The ADDR attribute specifies an up-stream client network address,
or [UNAVAILABLE] when the information is unavailable. Address
information is not enclosed with []. The address may be a non-IP
address.
The PROTO attribute specifies the mail protocol that was used by
the up-stream client. This may be an SMTP non-SMTP protocol name
of up to 64 characters, or [UNAVAILABLE] when the information is
unavailable.
The HELO attribute specifies the hostname that the up-stream client
host introduced itself with (for example, via the SMTP HELO command),
or [UNAVAILABLE] when the information is unavailable. The hostname
may be a non-DNS hostname.
Note 1: DNS hostnames can be up to 255 characters long. The XFORWARD
client implementation must not send XFORWARD commands that exceed
the 512 character limit for SMTP commands.
Note 2: [UNAVAILABLE] may be specified in upper case, lower case
or mixed case.
Note 3: the XFORWARD server implementation must not mix information
from the current SMTP session with forwarded information from an
up-stream session.
Security
========
The XFORWARD command changes audit trails. Use of this command
must be restricted to authorized clients.
SMTP connection caching
=======================
SMTP connection caching makes it possible to deliver multiple
messages within the same SMTP session. The XFORWARD attributes are
reset after the MAIL FROM command completes, so there is no risk
of information leakage.

View File

@ -217,3 +217,5 @@ $readme_directory/ULTRIX_README:f:root:-:644
$readme_directory/UUCP_README:f:root:-:644
$readme_directory/VERP_README:f:root:-:644
$readme_directory/VIRTUAL_README:f:root:-:644
$readme_directory/XCLIENT_README:f:root:-:644
$readme_directory/XFORWARD_README:f:root:-:644

View File

@ -21,6 +21,17 @@ lmtp_skip_quit_response = yes
#
lmtp_tcp_port = 24
# The lmtp_send_xforward_command parameter controls whether the Postfix
# LMTP client will send an XFORWARD command to the SMTP server, when
# the ELMTP HELO response of the remote host indicates XFORWARD support.
# This allows an "smtp" delivery agent, used for content filter
# message injection, to forward the name, address, protocol and HELO
# name of the original client to the content filter and downstream
# queuing LMTP server. Before you change the value to yes, it is best
# to make sure your content filter supports this command.
#
lmtp_send_xforward_command = no
#
# RESOURCE AND RATE CONTROLS
#
@ -83,6 +94,18 @@ lmtp_connect_timeout = 0s
#
lmtp_lhlo_timeout = 300s
# The lmtp_xforward_timeout parameter specifies the LMTP client timeout
# for sending the LMTP XFORWARD command, and for receiving the server
# response.
#
# In case of problems the client does NOT try the next address on
# the mail exchanger list.
#
# Time units: s (seconds), m (minutes), h (hours), d (days), w (weeks).
# The default time unit is s (seconds).
#
lmtp_xforward_timeout = 300s
# The lmtp_mail_timeout parameter specifies the LMTP client timeout
# for sending the LMTP MAIL FROM command, and for receiving the server
# response.

View File

@ -107,6 +107,13 @@ LMTP(8) LMTP(8)
server. Used as backup if the <b>lmtp</b> service is not
found in <b>services</b>(4).
<b>lmtp_send_xforward_command</b>
If the LMTP server announces XFORWARD support, send
the name, address, protocol and HELO name of the
original client. This can be used to forward client
information through a content filter to a down-
stream queuing LMTP server.
<b>Authentication controls</b>
<b>lmtp_sasl_auth_enable</b>
Enable per-session authentication as per <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a>
@ -206,16 +213,20 @@ LMTP(8) LMTP(8)
Timeout for sending the <b>LHLO</b> command, and for
receiving the server response.
<b>lmtp_xforward_timeout</b>
Timeout for sending the <b>XFORWARD</b> command, and for
receiving the server response.
<b>lmtp_mail_timeout</b>
Timeout for sending the <b>MAIL FROM</b> command, and for
Timeout for sending the <b>MAIL FROM</b> command, and for
receiving the server response.
<b>lmtp_rcpt_timeout</b>
Timeout for sending the <b>RCPT TO</b> command, and for
Timeout for sending the <b>RCPT TO</b> command, and for
receiving the server response.
<b>lmtp_data_init_timeout</b>
Timeout for sending the <b>DATA</b> command, and for
Timeout for sending the <b>DATA</b> command, and for
receiving the server response.
<b>lmtp_data_xfer_timeout</b>
@ -223,16 +234,16 @@ LMTP(8) LMTP(8)
<b>lmtp_data_done_timeout</b>
Timeout for sending the "<b>.</b>" command, and for
receiving the server response. When no response is
received, a warning is logged that the mail may be
receiving the server response. When no response is
received, a warning is logged that the mail may be
delivered multiple times.
<b>lmtp_rset_timeout</b>
Timeout for sending the <b>RSET</b> command, and for
Timeout for sending the <b>RSET</b> command, and for
receiving the server response.
<b>lmtp_quit_timeout</b>
Timeout for sending the <b>QUIT</b> command, and for
Timeout for sending the <b>QUIT</b> command, and for
receiving the server response.
<b>SEE ALSO</b>
@ -245,7 +256,7 @@ LMTP(8) LMTP(8)
syslogd(8) system logging
<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>

View File

@ -135,6 +135,13 @@ SMTP(8) SMTP(8)
Numerical source network address to bind to when
making a connection.
<b>smtp_defer_if_no_mx_address_found</b>
If no, bounce mail when no MX host resolves to an
address (Postfix always ignores MX hosts with equal
or worse preference than the local MTA). If yes,
keep trying until a suitable MX host resolves or
until the mail is too old.
<b>smtp_line_length_limit</b>
Length limit for SMTP message content lines. Zero
means no limit. Some SMTP servers misbehave on
@ -186,6 +193,13 @@ SMTP(8) SMTP(8)
nested deeper, when converting from 8BITMIME format
to 7BIT format.
<b>smtp_send_xforward_command</b>
If the SMTP server announces XFORWARD support, send
the name, address, protocol and HELO name of the
original client. This can be used to forward client
information through a content filter to a down-
stream queuing SMTP server.
<b>Authentication controls</b>
<b>smtp_sasl_auth_enable</b>
Enable per-session authentication as per <a href="http://www.faqs.org/rfcs/rfc2554.html">RFC 2554</a>
@ -274,20 +288,6 @@ SMTP(8) SMTP(8)
received, a warning is logged that the mail may be
delivered multiple times.
<b>smtp_defer_if_no_mx_address_found</b>
If no, bounce mail when no MX host resolves to an
address (Postfix always ignores MX hosts with equal
or worse preference than the local MTA). If yes,
keep trying until a suitable MX host resolves or
until the mail is too old.
<b>smtp_send_xforward_command</b>
If the SMTP server announces XFORWARD support, send
the name, address, protocol and HELO name of the
original client. This can be used to forward client
information through a content filter to a down-
stream queuing SMTP server.
<b>smtp_rset_timeout</b>
Timeout for sending the <b>RSET</b> command.

View File

@ -104,6 +104,11 @@ Do not wait for the server response after sending QUIT.
.IP \fBlmtp_tcp_port\fR
The TCP port to be used when connecting to a LMTP server. Used as
backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
.IP \fBlmtp_send_xforward_command\fR
If the LMTP server announces XFORWARD support, send the name,
address, protocol and HELO name of the original client. This
can be used to forward client information through a content
filter to a downstream queuing LMTP server.
.SH "Authentication controls"
.IP \fBlmtp_sasl_auth_enable\fR
Enable per-session authentication as per RFC 2554 (SASL).
@ -185,6 +190,9 @@ is deferred.
.IP \fBlmtp_lhlo_timeout\fR
Timeout for sending the \fBLHLO\fR command, and for
receiving the server response.
.IP \fBlmtp_xforward_timeout\fR
Timeout for sending the \fBXFORWARD\fR command, and for
receiving the server response.
.IP \fBlmtp_mail_timeout\fR
Timeout for sending the \fBMAIL FROM\fR command, and for
receiving the server response.

View File

@ -125,6 +125,12 @@ Always send EHLO at the start of a connection.
Never send EHLO at the start of a connection.
.IP \fBsmtp_bind_address\fR
Numerical source network address to bind to when making a connection.
.IP \fBsmtp_defer_if_no_mx_address_found\fR
If no, bounce mail when no MX host resolves to an address
(Postfix always ignores MX hosts with equal or worse preference
than the local MTA).
If yes, keep trying until a suitable MX host resolves or until
the mail is too old.
.IP \fBsmtp_line_length_limit\fR
Length limit for SMTP message content lines. Zero means no limit.
Some SMTP servers misbehave on long lines.
@ -158,6 +164,11 @@ between boundary strings that do not differ in the first
The maximal nesting level of multipart mail that the MIME
processor can handle. Refuse mail that is nested deeper,
when converting from 8BITMIME format to 7BIT format.
.IP \fBsmtp_send_xforward_command\fR
If the SMTP server announces XFORWARD support, send the name,
address, protocol and HELO name of the original client. This
can be used to forward client information through a content
filter to a downstream queuing SMTP server.
.SH "Authentication controls"
.IP \fBsmtp_sasl_auth_enable\fR
Enable per-session authentication as per RFC 2554 (SASL).
@ -228,17 +239,6 @@ Timeout for sending the message content.
Timeout for sending the "\fB.\fR" command, and for
receiving the server response. When no response is received, a
warning is logged that the mail may be delivered multiple times.
.IP \fBsmtp_defer_if_no_mx_address_found\fR
If no, bounce mail when no MX host resolves to an address
(Postfix always ignores MX hosts with equal or worse preference
than the local MTA).
If yes, keep trying until a suitable MX host resolves or until
the mail is too old.
.IP \fBsmtp_send_xforward_command\fR
If the SMTP server announces XFORWARD support, send the name,
address, protocol and HELO name of the original client. This
can be used to forward client information through a content
filter to a downstream queuing SMTP server.
.IP \fBsmtp_rset_timeout\fR
Timeout for sending the \fBRSET\fR command.
.IP \fBsmtp_quit_timeout\fR

View File

@ -753,9 +753,9 @@ extern int var_smtp_conn_tmout;
#define DEF_SMTP_HELO_TMOUT "300s"
extern int var_smtp_helo_tmout;
#define VAR_SMTP_XCLNT_TMOUT "smtp_xclient_timeout"
#define DEF_SMTP_XCLNT_TMOUT "300s"
extern int var_smtp_xclnt_tmout;
#define VAR_SMTP_XFWD_TMOUT "smtp_xforward_timeout"
#define DEF_SMTP_XFWD_TMOUT "300s"
extern int var_smtp_xfwd_tmout;
#define VAR_SMTP_MAIL_TMOUT "smtp_mail_timeout"
#define DEF_SMTP_MAIL_TMOUT "300s"
@ -1032,6 +1032,10 @@ extern int var_lmtp_rset_tmout;
#define DEF_LMTP_LHLO_TMOUT "300s"
extern int var_lmtp_lhlo_tmout;
#define VAR_LMTP_XFWD_TMOUT "lmtp_xforward_timeout"
#define DEF_LMTP_XFWD_TMOUT "300s"
extern int var_lmtp_xfwd_tmout;
#define VAR_LMTP_MAIL_TMOUT "lmtp_mail_timeout"
#define DEF_LMTP_MAIL_TMOUT "300s"
extern int var_lmtp_mail_tmout;
@ -1056,6 +1060,10 @@ extern int var_lmtp_data2_tmout;
#define DEF_LMTP_QUIT_TMOUT "300s"
extern int var_lmtp_quit_tmout;
#define VAR_LMTP_SEND_XFORWARD "lmtp_send_xforward_command"
#define DEF_LMTP_SEND_XFORWARD 0
extern bool var_lmtp_send_xforward;
/*
* Cleanup service. Header info that exceeds $header_size_limit bytes forces
* the start of the message body.

View File

@ -152,15 +152,17 @@ extern char *mail_pathname(const char *, const char *);
#define MAIL_ATTR_ORG_LOCAL "local" /* local submission */
/*
* XCLIENT in SMTP.
* XCLIENT/XFORWARD in SMTP.
*/
#define XCLIENT_CMD "XCLIENT" /* XCLIENT command */
#define XCLIENT_NAME "NAME" /* client name */
#define XCLIENT_ADDR "ADDR" /* client address */
#define XCLIENT_PROTO "PROTO" /* client protocol */
#define XCLIENT_CODE "NAME_CODE" /* client name status */
#define XCLIENT_HELO "HELO" /* client helo */
#define XCLIENT_UNAVAILABLE "[UNAVAILABLE]" /* permanently unavailable */
#define XCLIENT_TEMPORARY "[TEMPUNAVAIL]" /* temporarily unavailable */
#define XFORWARD_CMD "XFORWARD" /* XFORWARD command */
#define XFORWARD_NAME "NAME" /* client name */
#define XFORWARD_ADDR "ADDR" /* client address */
@ -168,6 +170,8 @@ extern char *mail_pathname(const char *, const char *);
#define XFORWARD_HELO "HELO" /* client helo */
#define XFORWARD_IDENT "IDENT" /* message identifier */
#define XFORWARD_UNAVAILABLE "[UNAVAILABLE]" /* attribute unavailable */
/* LICENSE
/* .ad
/* .fi

View File

@ -20,7 +20,7 @@
* Patches change the patchlevel and the release date. Snapshots change the
* release date only, unless they include the same bugfix as a patch release.
*/
#define MAIL_RELEASE_DATE "20031213"
#define MAIL_RELEASE_DATE "20031214"
#define VAR_MAIL_VERSION "mail_version"
#define DEF_MAIL_VERSION "2.0.16-" MAIL_RELEASE_DATE

View File

@ -88,6 +88,11 @@
/* .IP \fBlmtp_tcp_port\fR
/* The TCP port to be used when connecting to a LMTP server. Used as
/* backup if the \fBlmtp\fR service is not found in \fBservices\fR(4).
/* .IP \fBlmtp_send_xforward_command\fR
/* If the LMTP server announces XFORWARD support, send the name,
/* address, protocol and HELO name of the original client. This
/* can be used to forward client information through a content
/* filter to a downstream queuing LMTP server.
/* .SH "Authentication controls"
/* .IP \fBlmtp_sasl_auth_enable\fR
/* Enable per-session authentication as per RFC 2554 (SASL).
@ -169,6 +174,9 @@
/* .IP \fBlmtp_lhlo_timeout\fR
/* Timeout for sending the \fBLHLO\fR command, and for
/* receiving the server response.
/* .IP \fBlmtp_xforward_timeout\fR
/* Timeout for sending the \fBXFORWARD\fR command, and for
/* receiving the server response.
/* .IP \fBlmtp_mail_timeout\fR
/* Timeout for sending the \fBMAIL FROM\fR command, and for
/* receiving the server response.
@ -263,6 +271,7 @@ int var_lmtp_tcp_port;
int var_lmtp_conn_tmout;
int var_lmtp_rset_tmout;
int var_lmtp_lhlo_tmout;
int var_lmtp_xfwd_tmout;
int var_lmtp_mail_tmout;
int var_lmtp_rcpt_tmout;
int var_lmtp_data0_tmout;
@ -276,6 +285,7 @@ char *var_error_rcpt;
char *var_lmtp_sasl_opts;
char *var_lmtp_sasl_passwd;
bool var_lmtp_sasl_enable;
bool var_lmtp_send_xforward;
/*
* Global variables.
@ -538,6 +548,7 @@ int main(int argc, char **argv)
VAR_LMTP_CONN_TMOUT, DEF_LMTP_CONN_TMOUT, &var_lmtp_conn_tmout, 0, 0,
VAR_LMTP_RSET_TMOUT, DEF_LMTP_RSET_TMOUT, &var_lmtp_rset_tmout, 1, 0,
VAR_LMTP_LHLO_TMOUT, DEF_LMTP_LHLO_TMOUT, &var_lmtp_lhlo_tmout, 1, 0,
VAR_LMTP_XFWD_TMOUT, DEF_LMTP_XFWD_TMOUT, &var_lmtp_xfwd_tmout, 1, 0,
VAR_LMTP_MAIL_TMOUT, DEF_LMTP_MAIL_TMOUT, &var_lmtp_mail_tmout, 1, 0,
VAR_LMTP_RCPT_TMOUT, DEF_LMTP_RCPT_TMOUT, &var_lmtp_rcpt_tmout, 1, 0,
VAR_LMTP_DATA0_TMOUT, DEF_LMTP_DATA0_TMOUT, &var_lmtp_data0_tmout, 1, 0,

View File

@ -61,6 +61,16 @@ typedef struct LMTP_STATE {
#define LMTP_FEATURE_PIPELINING (1<<2)
#define LMTP_FEATURE_SIZE (1<<3)
#define LMTP_FEATURE_AUTH (1<<5)
#define LMTP_FEATURE_XFORWARD_NAME (1<<6)
#define LMTP_FEATURE_XFORWARD_ADDR (1<<7)
#define LMTP_FEATURE_XFORWARD_PROTO (1<<8)
#define LMTP_FEATURE_XFORWARD_HELO (1<<9)
#define LMTP_FEATURE_XFORWARD_NAME_ADDR \
(LMTP_FEATURE_XFORWARD_NAME | LMTP_FEATURE_XFORWARD_ADDR)
#define LMTP_FEATURE_XFORWARD_PROTO_HELO \
(LMTP_FEATURE_XFORWARD_PROTO | LMTP_FEATURE_XFORWARD_HELO)
/*
* lmtp.c

View File

@ -100,6 +100,7 @@
#include <vstring_vstream.h>
#include <stringops.h>
#include <mymalloc.h>
#include <name_code.h>
/* Global library. */
@ -141,48 +142,58 @@
* the existing code for exception handling and error reporting.
*
* Client states that are associated with sending mail (up to and including
* SMTP_STATE_DOT) must have smaller numerical values than the non-sending
* states (SMTP_STATE_ABORT .. SMTP_STATE_LAST).
* LMTP_STATE_DOT) must have smaller numerical values than the non-sending
* states (LMTP_STATE_ABORT .. LMTP_STATE_LAST).
*/
#define LMTP_STATE_MAIL 0
#define LMTP_STATE_RCPT 1
#define LMTP_STATE_DATA 2
#define LMTP_STATE_DOT 3
#define LMTP_STATE_ABORT 4
#define LMTP_STATE_RSET 5
#define LMTP_STATE_QUIT 6
#define LMTP_STATE_LAST 7
#define LMTP_STATE_XFORWARD_NAME_ADDR 0
#define LMTP_STATE_XFORWARD_PROTO_HELO 1
#define LMTP_STATE_MAIL 2
#define LMTP_STATE_RCPT 3
#define LMTP_STATE_DATA 4
#define LMTP_STATE_DOT 5
#define LMTP_STATE_ABORT 6
#define LMTP_STATE_RSET 7
#define LMTP_STATE_QUIT 8
#define LMTP_STATE_LAST 9
int *xfer_timeouts[LMTP_STATE_LAST] = {
&var_lmtp_xfwd_tmout, /* name/addr */
&var_lmtp_xfwd_tmout, /* helo/proto */
&var_lmtp_mail_tmout,
&var_lmtp_rcpt_tmout,
&var_lmtp_data0_tmout,
&var_lmtp_data2_tmout,
&var_lmtp_rset_tmout,
&var_lmtp_rset_tmout,
&var_lmtp_rset_tmout, /* abort */
&var_lmtp_rset_tmout, /* rset */
&var_lmtp_quit_tmout,
};
char *xfer_states[LMTP_STATE_LAST] = {
"sending XFORWARD name/address",
"sending XFORWARD protocol/helo_name",
"sending MAIL FROM",
"sending RCPT TO",
"sending DATA command",
"sending end of data -- message may be sent more than once",
"sending RSET",
"sending RSET",
"sending RSET", /* abort */
"sending RSET", /* rset */
"sending QUIT",
};
char *xfer_request[LMTP_STATE_LAST] = {
"XFORWARD name/address command",
"XFORWARD helo/protocol command",
"MAIL FROM command",
"RCPT TO command",
"DATA command",
"end of DATA command",
"RSET command",
"RSET command",
"RSET command", /* abort */
"RSET command", /* rset */
"QUIT command",
};
static int lmtp_send_proto_helo;
/* lmtp_lhlo - perform initial handshake with LMTP server */
int lmtp_lhlo(LMTP_STATE *state)
@ -193,6 +204,13 @@ int lmtp_lhlo(LMTP_STATE *state)
char *lines;
char *words;
char *word;
static NAME_CODE xforward_features[] = {
XFORWARD_NAME, LMTP_FEATURE_XFORWARD_NAME,
XFORWARD_ADDR, LMTP_FEATURE_XFORWARD_ADDR,
XFORWARD_PROTO, LMTP_FEATURE_XFORWARD_PROTO,
XFORWARD_HELO, LMTP_FEATURE_XFORWARD_HELO,
0, 0,
};
/*
* Prepare for disaster.
@ -235,6 +253,10 @@ int lmtp_lhlo(LMTP_STATE *state)
state->features |= LMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
state->features |= LMTP_FEATURE_PIPELINING;
else if (strcasecmp(word, "XFORWARD") == 0)
while ((word = mystrtok(&words, " \t")) != 0)
state->features |= name_code(xforward_features,
NAME_CODE_FLAG_NONE, word);
else if (strcasecmp(word, "SIZE") == 0)
state->features |= LMTP_FEATURE_SIZE;
#ifdef USE_SASL_AUTH
@ -364,6 +386,40 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state)
default:
msg_panic("%s: bad sender state %d", myname, send_state);
/*
* Build the XFORWARD command. With properly sanitized
* information, the command length stays within the 512 byte
* command line length limit.
*/
case LMTP_STATE_XFORWARD_NAME_ADDR:
vstring_strcpy(next_command, XFORWARD_CMD);
if (state->features & LMTP_FEATURE_XFORWARD_NAME)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_NAME, DEL_REQ_ATTR_AVAIL(request->client_name) ?
request->client_name : XFORWARD_UNAVAILABLE);
if (state->features & LMTP_FEATURE_XFORWARD_ADDR)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_ADDR, DEL_REQ_ATTR_AVAIL(request->client_addr) ?
request->client_addr : XFORWARD_UNAVAILABLE);
if (lmtp_send_proto_helo)
next_state = LMTP_STATE_XFORWARD_PROTO_HELO;
else
next_state = LMTP_STATE_MAIL;
break;
case LMTP_STATE_XFORWARD_PROTO_HELO:
vstring_strcpy(next_command, XFORWARD_CMD);
if (state->features & LMTP_FEATURE_XFORWARD_PROTO)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_PROTO, DEL_REQ_ATTR_AVAIL(request->client_proto) ?
request->client_proto : XFORWARD_UNAVAILABLE);
if (state->features & LMTP_FEATURE_XFORWARD_HELO)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_HELO, DEL_REQ_ATTR_AVAIL(request->client_helo) ?
request->client_helo : XFORWARD_UNAVAILABLE);
next_state = LMTP_STATE_MAIL;
break;
/*
* Build the MAIL FROM command.
*/
@ -478,7 +534,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state)
/*
* Sanity check.
*/
if (recv_state < LMTP_STATE_MAIL
if (recv_state < LMTP_STATE_XFORWARD_NAME_ADDR
|| recv_state > LMTP_STATE_QUIT)
msg_panic("%s: bad receiver state %d (sender state %d)",
myname, recv_state, send_state);
@ -499,6 +555,30 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state)
*/
switch (recv_state) {
/*
* Process the XFORWARD response.
*/
case LMTP_STATE_XFORWARD_NAME_ADDR:
if (resp->code / 100 != 2)
msg_warn("host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[LMTP_STATE_XFORWARD_NAME_ADDR]);
if (lmtp_send_proto_helo)
recv_state = LMTP_STATE_XFORWARD_PROTO_HELO;
else
recv_state = LMTP_STATE_MAIL;
break;
case LMTP_STATE_XFORWARD_PROTO_HELO:
if (resp->code / 100 != 2)
msg_warn("host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[LMTP_STATE_XFORWARD_PROTO_HELO]);
recv_state = LMTP_STATE_MAIL;
break;
/*
* Process the MAIL FROM response. When the server
* rejects the sender, set the mail_from_rejected flag so
@ -546,7 +626,7 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state)
&& sent(DEL_REQ_TRACE_FLAGS(request->flags),
request->queue_id, rcpt->orig_addr,
rcpt->address, rcpt->offset,
session->namaddr, request->arrival_time,
session->namaddr, request->arrival_time,
"%s",
translit(resp->str, "\n", " ")) == 0) {
if (request->flags & DEL_REQ_FLAG_SUCCESS)
@ -746,7 +826,31 @@ static int lmtp_loop(LMTP_STATE *state, int send_state, int recv_state)
int lmtp_xfer(LMTP_STATE *state)
{
return (lmtp_loop(state, LMTP_STATE_MAIL, LMTP_STATE_MAIL));
DELIVER_REQUEST *request = state->request;
int start;
int send_name_addr;
/*
* Use the XFORWARD command to forward client attributes only when a
* minimal amount of information is available.
*/
send_name_addr =
(var_lmtp_send_xforward
&& (state->features & LMTP_FEATURE_XFORWARD_NAME_ADDR)
&& (DEL_REQ_ATTR_AVAIL(request->client_name)
|| DEL_REQ_ATTR_AVAIL(request->client_addr)));
lmtp_send_proto_helo =
(var_lmtp_send_xforward
&& (state->features & LMTP_FEATURE_XFORWARD_PROTO_HELO)
&& (DEL_REQ_ATTR_AVAIL(request->client_proto)
|| DEL_REQ_ATTR_AVAIL(request->client_helo)));
if (send_name_addr)
start = LMTP_STATE_XFORWARD_NAME_ADDR;
else if (lmtp_send_proto_helo)
start = LMTP_STATE_XFORWARD_PROTO_HELO;
else
start = LMTP_STATE_MAIL;
return (lmtp_loop(state, start, start));
}
/* lmtp_rset - send a lone RSET command and wait for response */

View File

@ -698,17 +698,19 @@ static void get_service_attr(PIPE_ATTR *attr, char **argv)
* Sanity checks. Verify that every member has an acceptable value.
*/
if (user == 0)
msg_fatal("missing user= attribute");
msg_fatal("missing user= command-line attribute");
if (attr->command == 0)
msg_fatal("missing argv= attribute");
msg_fatal("missing argv= command-line attribute");
if (attr->uid == 0)
msg_fatal("request to deliver as root");
msg_fatal("user= command-line attribute specifies root privileges");
if (attr->uid == var_owner_uid)
msg_fatal("request to deliver as mail system owner");
msg_fatal("user= command-line attribute specifies mail system owner %s",
var_mail_owner);
if (attr->gid == 0)
msg_fatal("request to use privileged group id %ld", (long) attr->gid);
msg_fatal("user= command-line attribute specifies privileged group id 0");
if (attr->gid == var_owner_gid)
msg_fatal("request to use mail system owner group id %ld", (long) attr->gid);
msg_fatal("user= command-line attribute specifies mail system owner %s group id %ld",
var_mail_owner, (long) attr->gid);
/*
* Give the poor tester a clue of what is going on.

View File

@ -154,6 +154,7 @@ smtp_proto.o: ../../include/stringops.h
smtp_proto.o: ../../include/mymalloc.h
smtp_proto.o: ../../include/iostuff.h
smtp_proto.o: ../../include/split_at.h
smtp_proto.o: ../../include/name_code.h
smtp_proto.o: ../../include/mail_params.h
smtp_proto.o: ../../include/smtp_stream.h
smtp_proto.o: ../../include/mail_queue.h
@ -173,7 +174,6 @@ smtp_proto.o: ../../include/mail_proto.h
smtp_proto.o: ../../include/attr.h
smtp_proto.o: ../../include/mime_state.h
smtp_proto.o: ../../include/header_opts.h
smtp_proto.o: ../../include/xtext.h
smtp_proto.o: smtp.h
smtp_proto.o: ../../include/argv.h
smtp_proto.o: smtp_sasl.h

View File

@ -109,6 +109,12 @@
/* Never send EHLO at the start of a connection.
/* .IP \fBsmtp_bind_address\fR
/* Numerical source network address to bind to when making a connection.
/* .IP \fBsmtp_defer_if_no_mx_address_found\fR
/* If no, bounce mail when no MX host resolves to an address
/* (Postfix always ignores MX hosts with equal or worse preference
/* than the local MTA).
/* If yes, keep trying until a suitable MX host resolves or until
/* the mail is too old.
/* .IP \fBsmtp_line_length_limit\fR
/* Length limit for SMTP message content lines. Zero means no limit.
/* Some SMTP servers misbehave on long lines.
@ -142,6 +148,11 @@
/* The maximal nesting level of multipart mail that the MIME
/* processor can handle. Refuse mail that is nested deeper,
/* when converting from 8BITMIME format to 7BIT format.
/* .IP \fBsmtp_send_xforward_command\fR
/* If the SMTP server announces XFORWARD support, send the name,
/* address, protocol and HELO name of the original client. This
/* can be used to forward client information through a content
/* filter to a downstream queuing SMTP server.
/* .SH "Authentication controls"
/* .IP \fBsmtp_sasl_auth_enable\fR
/* Enable per-session authentication as per RFC 2554 (SASL).
@ -212,17 +223,6 @@
/* Timeout for sending the "\fB.\fR" command, and for
/* receiving the server response. When no response is received, a
/* warning is logged that the mail may be delivered multiple times.
/* .IP \fBsmtp_defer_if_no_mx_address_found\fR
/* If no, bounce mail when no MX host resolves to an address
/* (Postfix always ignores MX hosts with equal or worse preference
/* than the local MTA).
/* If yes, keep trying until a suitable MX host resolves or until
/* the mail is too old.
/* .IP \fBsmtp_send_xforward_command\fR
/* If the SMTP server announces XFORWARD support, send the name,
/* address, protocol and HELO name of the original client. This
/* can be used to forward client information through a content
/* filter to a downstream queuing SMTP server.
/* .IP \fBsmtp_rset_timeout\fR
/* Timeout for sending the \fBRSET\fR command.
/* .IP \fBsmtp_quit_timeout\fR
@ -283,7 +283,7 @@
*/
int var_smtp_conn_tmout;
int var_smtp_helo_tmout;
int var_smtp_xclnt_tmout;
int var_smtp_xfwd_tmout;
int var_smtp_mail_tmout;
int var_smtp_rcpt_tmout;
int var_smtp_data0_tmout;
@ -502,7 +502,7 @@ int main(int argc, char **argv)
static CONFIG_TIME_TABLE time_table[] = {
VAR_SMTP_CONN_TMOUT, DEF_SMTP_CONN_TMOUT, &var_smtp_conn_tmout, 0, 0,
VAR_SMTP_HELO_TMOUT, DEF_SMTP_HELO_TMOUT, &var_smtp_helo_tmout, 1, 0,
VAR_SMTP_XCLNT_TMOUT, DEF_SMTP_XCLNT_TMOUT, &var_smtp_xclnt_tmout, 1, 0,
VAR_SMTP_XFWD_TMOUT, DEF_SMTP_XFWD_TMOUT, &var_smtp_xfwd_tmout, 1, 0,
VAR_SMTP_MAIL_TMOUT, DEF_SMTP_MAIL_TMOUT, &var_smtp_mail_tmout, 1, 0,
VAR_SMTP_RCPT_TMOUT, DEF_SMTP_RCPT_TMOUT, &var_smtp_rcpt_tmout, 1, 0,
VAR_SMTP_DATA0_TMOUT, DEF_SMTP_DATA0_TMOUT, &var_smtp_data0_tmout, 1, 0,

View File

@ -69,9 +69,11 @@ typedef struct SMTP_STATE {
#define SMTP_FEATURE_XFORWARD_PROTO (1<<9)
#define SMTP_FEATURE_XFORWARD_HELO (1<<10)
#define SMTP_FEATURE_XFORWARD_MASK \
(SMTP_FEATURE_XFORWARD_NAME | SMTP_FEATURE_XFORWARD_ADDR \
| SMTP_FEATURE_XFORWARD_PROTO | SMTP_FEATURE_XFORWARD_HELO)
#define SMTP_FEATURE_XFORWARD_NAME_ADDR \
(SMTP_FEATURE_XFORWARD_NAME | SMTP_FEATURE_XFORWARD_ADDR)
#define SMTP_FEATURE_XFORWARD_PROTO_HELO \
(SMTP_FEATURE_XFORWARD_PROTO | SMTP_FEATURE_XFORWARD_HELO)
/*
* smtp.c

View File

@ -104,7 +104,6 @@
#include <quote_821_local.h>
#include <mail_proto.h>
#include <mime_state.h>
#include <xtext.h>
/* Application-specific. */
@ -125,8 +124,8 @@
* SMTP_STATE_DOT) must have smaller numerical values than the non-sending
* states (SMTP_STATE_ABORT .. SMTP_STATE_LAST).
*/
#define SMTP_STATE_XFORWARD_ADDR 0
#define SMTP_STATE_XFORWARD_HELO 1
#define SMTP_STATE_XFORWARD_NAME_ADDR 0
#define SMTP_STATE_XFORWARD_PROTO_HELO 1
#define SMTP_STATE_MAIL 2
#define SMTP_STATE_RCPT 3
#define SMTP_STATE_DATA 4
@ -136,8 +135,8 @@
#define SMTP_STATE_LAST 8
int *xfer_timeouts[SMTP_STATE_LAST] = {
&var_smtp_xclnt_tmout,
&var_smtp_xclnt_tmout,
&var_smtp_xfwd_tmout, /* name/addr */
&var_smtp_xfwd_tmout, /* helo/proto */
&var_smtp_mail_tmout,
&var_smtp_rcpt_tmout,
&var_smtp_data0_tmout,
@ -147,8 +146,8 @@ int *xfer_timeouts[SMTP_STATE_LAST] = {
};
char *xfer_states[SMTP_STATE_LAST] = {
"sending XFORWARD address and name",
"sending XFORWARD helo_name and protocol",
"sending XFORWARD name/address",
"sending XFORWARD protocol/helo_name",
"sending MAIL FROM",
"sending RCPT TO",
"sending DATA command",
@ -267,11 +266,11 @@ int smtp_helo(SMTP_STATE *state)
state->features |= SMTP_FEATURE_8BITMIME;
else if (strcasecmp(word, "PIPELINING") == 0)
state->features |= SMTP_FEATURE_PIPELINING;
else if (strcasecmp(word, "XFORWARD") == 0) {
else if (strcasecmp(word, "XFORWARD") == 0)
while ((word = mystrtok(&words, " \t")) != 0)
state->features |= name_code(xforward_features,
NAME_CODE_FLAG_NONE, word);
} else if (strcasecmp(word, "SIZE") == 0) {
else if (strcasecmp(word, "SIZE") == 0) {
state->features |= SMTP_FEATURE_SIZE;
if ((word = mystrtok(&words, " \t")) != 0) {
if (!alldig(word))
@ -395,6 +394,8 @@ int smtp_xfer(SMTP_STATE *state)
int mail_from_rejected;
int downgrading;
int mime_errs;
int send_name_addr;
int send_proto_helo;
/*
* Macros for readability.
@ -498,11 +499,20 @@ int smtp_xfer(SMTP_STATE *state)
* amount of information is available.
*/
nrcpt = 0;
if (var_smtp_send_xforward
&& (state->features & SMTP_FEATURE_XFORWARD_MASK)
&& (DEL_REQ_ATTR_AVAIL(request->client_name)
|| DEL_REQ_ATTR_AVAIL(request->client_addr)))
recv_state = send_state = SMTP_STATE_XFORWARD_ADDR;
send_name_addr =
(var_smtp_send_xforward
&& (state->features & SMTP_FEATURE_XFORWARD_NAME_ADDR)
&& (DEL_REQ_ATTR_AVAIL(request->client_name)
|| DEL_REQ_ATTR_AVAIL(request->client_addr)));
send_proto_helo =
(var_smtp_send_xforward
&& (state->features & SMTP_FEATURE_XFORWARD_PROTO_HELO)
&& (DEL_REQ_ATTR_AVAIL(request->client_proto)
|| DEL_REQ_ATTR_AVAIL(request->client_helo)));
if (send_name_addr)
recv_state = send_state = SMTP_STATE_XFORWARD_NAME_ADDR;
else if (send_proto_helo)
recv_state = send_state = SMTP_STATE_XFORWARD_PROTO_HELO;
else
recv_state = send_state = SMTP_STATE_MAIL;
next_rcpt = send_rcpt = recv_rcpt = 0;
@ -526,33 +536,32 @@ int smtp_xfer(SMTP_STATE *state)
* information, the command length stays within the 512 byte
* command line length limit.
*/
case SMTP_STATE_XFORWARD_ADDR:
case SMTP_STATE_XFORWARD_NAME_ADDR:
vstring_strcpy(next_command, XFORWARD_CMD);
if (state->features & SMTP_FEATURE_XFORWARD_NAME) {
vstring_strcat(next_command, " " XCLIENT_NAME "=");
if (DEL_REQ_ATTR_AVAIL(request->client_name))
xtext_quote_append(next_command, request->client_name, "");
}
if (state->features & SMTP_FEATURE_XFORWARD_ADDR) {
vstring_strcat(next_command, " " XFORWARD_ADDR "=");
if (DEL_REQ_ATTR_AVAIL(request->client_addr))
xtext_quote_append(next_command, request->client_addr, "");
}
next_state = SMTP_STATE_XFORWARD_HELO;
if (state->features & SMTP_FEATURE_XFORWARD_NAME)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_NAME, DEL_REQ_ATTR_AVAIL(request->client_name) ?
request->client_name : XFORWARD_UNAVAILABLE);
if (state->features & SMTP_FEATURE_XFORWARD_ADDR)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_ADDR, DEL_REQ_ATTR_AVAIL(request->client_addr) ?
request->client_addr : XFORWARD_UNAVAILABLE);
if (send_proto_helo)
next_state = SMTP_STATE_XFORWARD_PROTO_HELO;
else
next_state = SMTP_STATE_MAIL;
break;
case SMTP_STATE_XFORWARD_HELO:
case SMTP_STATE_XFORWARD_PROTO_HELO:
vstring_strcpy(next_command, XFORWARD_CMD);
if (state->features & SMTP_FEATURE_XFORWARD_HELO) {
vstring_strcat(next_command, " " XCLIENT_HELO "=");
if (DEL_REQ_ATTR_AVAIL(request->client_helo))
xtext_quote_append(next_command, request->client_helo, "");
}
if (state->features & SMTP_FEATURE_XFORWARD_ADDR) {
vstring_strcat(next_command, " " XFORWARD_PROTO "=");
if (DEL_REQ_ATTR_AVAIL(request->client_proto))
xtext_quote_append(next_command, request->client_proto, "");
}
if (state->features & SMTP_FEATURE_XFORWARD_PROTO)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_PROTO, DEL_REQ_ATTR_AVAIL(request->client_proto) ?
request->client_proto : XFORWARD_UNAVAILABLE);
if (state->features & SMTP_FEATURE_XFORWARD_HELO)
vstring_sprintf_append(next_command, " %s=%s",
XFORWARD_HELO, DEL_REQ_ATTR_AVAIL(request->client_helo) ?
request->client_helo : XFORWARD_UNAVAILABLE);
next_state = SMTP_STATE_MAIL;
break;
@ -665,7 +674,7 @@ int smtp_xfer(SMTP_STATE *state)
/*
* Sanity check.
*/
if (recv_state < SMTP_STATE_XFORWARD_ADDR
if (recv_state < SMTP_STATE_XFORWARD_NAME_ADDR
|| recv_state > SMTP_STATE_QUIT)
msg_panic("%s: bad receiver state %d (sender state %d)",
myname, recv_state, send_state);
@ -689,21 +698,24 @@ int smtp_xfer(SMTP_STATE *state)
/*
* Process the XFORWARD response.
*/
case SMTP_STATE_XFORWARD_ADDR:
case SMTP_STATE_XFORWARD_NAME_ADDR:
if (resp->code / 100 != 2)
msg_warn("host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[SMTP_STATE_MAIL]);
recv_state = SMTP_STATE_XFORWARD_HELO;
xfer_request[SMTP_STATE_XFORWARD_NAME_ADDR]);
if (send_proto_helo)
recv_state = SMTP_STATE_XFORWARD_PROTO_HELO;
else
recv_state = SMTP_STATE_MAIL;
break;
case SMTP_STATE_XFORWARD_HELO:
case SMTP_STATE_XFORWARD_PROTO_HELO:
if (resp->code / 100 != 2)
msg_warn("host %s said: %s (in reply to %s)",
session->namaddr,
translit(resp->str, "\n", " "),
xfer_request[SMTP_STATE_MAIL]);
xfer_request[SMTP_STATE_XFORWARD_PROTO_HELO]);
recv_state = SMTP_STATE_MAIL;
break;

View File

@ -151,7 +151,6 @@ smtpd.o: ../../include/namadr_list.h
smtpd.o: ../../include/input_transp.h
smtpd.o: ../../include/anvil_clnt.h
smtpd.o: ../../include/attr_clnt.h
smtpd.o: ../../include/xtext.h
smtpd.o: ../../include/mail_server.h
smtpd.o: smtpd_token.h
smtpd.o: smtpd.h
@ -261,13 +260,13 @@ smtpd_proxy.o: ../../include/vstring.h
smtpd_proxy.o: ../../include/stringops.h
smtpd_proxy.o: ../../include/connect.h
smtpd_proxy.o: ../../include/iostuff.h
smtpd_proxy.o: ../../include/name_code.h
smtpd_proxy.o: ../../include/mail_error.h
smtpd_proxy.o: ../../include/name_mask.h
smtpd_proxy.o: ../../include/smtp_stream.h
smtpd_proxy.o: ../../include/cleanup_user.h
smtpd_proxy.o: ../../include/mail_params.h
smtpd_proxy.o: ../../include/rec_type.h
smtpd_proxy.o: ../../include/xtext.h
smtpd_proxy.o: ../../include/mail_proto.h
smtpd_proxy.o: ../../include/attr.h
smtpd_proxy.o: smtpd.h

View File

@ -464,7 +464,6 @@
#include <namadr_list.h>
#include <input_transp.h>
#include <anvil_clnt.h>
#include <xtext.h>
/* Single-threaded server skeleton. */
@ -753,8 +752,7 @@ static int ehlo_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
if (xclient_allowed)
smtpd_chat_reply(state, "250-" XCLIENT_CMD
" " XCLIENT_NAME " " XCLIENT_ADDR
" " XCLIENT_CODE " " XCLIENT_PROTO
" " XCLIENT_HELO);
" " XCLIENT_PROTO " " XCLIENT_HELO);
if (xforward_allowed)
smtpd_chat_reply(state, "250-" XFORWARD_CMD
" " XFORWARD_NAME " " XFORWARD_ADDR
@ -1723,22 +1721,21 @@ static int quit_cmd(SMTPD_STATE *state, int unused_argc, SMTPD_TOKEN *unused_arg
return (0);
}
/* xclient_cmd - override client attributes */
/* xclient_cmd - override SMTP client attributes */
static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
int arg_no;
SMTPD_TOKEN *argp;
char *attr_value;
char *attr_name;
int update_namaddr = 0;
int code;
static NAME_CODE xclient_codes[] = {
"OK", SMTPD_PEER_CODE_OK,
"PERM", SMTPD_PEER_CODE_PERM,
"TEMP", SMTPD_PEER_CODE_TEMP,
0, -1,
int peer_code;
static NAME_CODE peer_codes[] = {
XCLIENT_UNAVAILABLE, SMTPD_PEER_CODE_PERM,
XCLIENT_TEMPORARY, SMTPD_PEER_CODE_TEMP,
0, SMTPD_PEER_CODE_OK,
};
static NAME_CODE xclient_proto[] = {
static NAME_CODE proto_names[] = {
MAIL_PROTO_SMTP, 1,
MAIL_PROTO_ESMTP, 2,
0, -1,
@ -1755,7 +1752,7 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: %s name=value...",
smtpd_chat_reply(state, "501 Syntax: %s attribute=value...",
XCLIENT_CMD);
return (-1);
}
@ -1765,122 +1762,99 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
return (-1);
}
#define STREQ(x,y) (strcasecmp((x), (y)) == 0)
#define UPDATE_STR(s, v) do { if (s) myfree(s); s = (v) ? mystrdup(v) : 0; } while(0)
#define UPDATE_STR(s, v) do { \
if (s) myfree(s); \
s = (v) ? mystrdup(v) : 0; \
} while(0)
#define NEUTER_CHARACTERS "<>()\\\";:@"
/*
* Iterate over all NAME=VALUE attributes.
* Iterate over all attribute=value elements.
*/
for (arg_no = 1; arg_no < argc; arg_no++) {
attr_name = argv[arg_no].strval;
for (argp = argv + 1; argp < argv + argc; argp++) {
attr_name = argp->strval;
/*
* For safety's sake mask non-printable characters in the raw and
* decoded values; we don't want to handle unexploded munitions.
* Complain when they send an attribute that we didn't announce.
*
* An implementation must allow clients to send XCLIENT before the
* HELO/EHLO greeting.
*
* The client can send multiple XCLIENT attributes in a single command,
* or multiple XCLIENT commands with fewer attributes.
* For safety's sake mask non-printable characters. We'll do more
* specific censoring later.
*/
if ((attr_value = split_at(attr_name, '=')) == 0) {
if ((attr_value = split_at(attr_name, '=')) == 0 || *attr_value == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: name=value expected");
smtpd_chat_reply(state, "501 Error: attribute=value expected");
return (-1);
}
printable(attr_value, '?');
/*
* NAME=hostname. Also updates the client hostname lookup status
* code. Treat a numerical hostname as an unavailable name.
* NAME=substitute SMTP client hostname. Also updates the client
* hostname lookup status code.
*/
if (STREQ(attr_name, XCLIENT_NAME)) {
if (*attr_value && !valid_hostaddr(attr_value, DONT_GRIPE)) {
if (!valid_hostname(attr_value, DONT_GRIPE)) {
peer_code = name_code(peer_codes, NAME_CODE_FLAG_NONE, attr_value);
if (peer_code != SMTPD_PEER_CODE_OK) {
attr_value = CLIENT_NAME_UNKNOWN;
} else {
if (!valid_hostname(attr_value, DONT_GRIPE)
|| valid_hostaddr(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_NAME, attr_value);
return (-1);
}
UPDATE_STR(state->name, attr_value);
state->peer_code = SMTPD_PEER_CODE_OK;
} else {
UPDATE_STR(state->name, CLIENT_NAME_UNKNOWN);
state->peer_code = SMTPD_PEER_CODE_PERM;
}
state->peer_code = peer_code;
UPDATE_STR(state->name, attr_value);
update_namaddr = 1;
}
/*
* ADDR=client network address.
* ADDR=substitute SMTP client network address.
*/
else if (STREQ(attr_name, XCLIENT_ADDR)) {
if (*attr_value) {
if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
attr_value = CLIENT_ADDR_UNKNOWN;
} else {
if (!valid_hostaddr(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_ADDR, attr_value);
return (-1);
}
UPDATE_STR(state->addr, attr_value);
} else {
UPDATE_STR(state->addr, CLIENT_ADDR_UNKNOWN);
}
UPDATE_STR(state->addr, attr_value);
update_namaddr = 1;
}
/*
* CODE=hostname lookup status. Reset the client hostname if the
* hostname lookup status is not OK.
*/
else if (STREQ(attr_name, XCLIENT_CODE)) {
if (*attr_value) {
if ((code = name_code(xclient_codes, NAME_CODE_FLAG_NONE, attr_value)) < 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s value: %s",
XCLIENT_CODE, attr_value);
return (-1);
}
state->peer_code = code;
if (code != SMTPD_PEER_CODE_OK) {
UPDATE_STR(state->name, CLIENT_NAME_UNKNOWN);
update_namaddr = 1;
}
}
}
/*
* HELO=hostname. Disallow characters that could mess up our own
* Received: message headers but allow [].
* HELO=substitute SMTP client HELO parameter. Censor special
* characters that could mess up message headers.
*/
else if (STREQ(attr_name, XCLIENT_HELO)) {
if (*attr_value) {
if (STREQ(attr_value, XCLIENT_UNAVAILABLE)) {
attr_value = CLIENT_HELO_UNKNOWN;
} else {
if (strlen(attr_value) > VALID_HOSTNAME_LEN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_HELO, attr_value);
return (-1);
}
neuter(attr_value, "<>()\\\";:@", '?');
UPDATE_STR(state->helo_name, attr_value);
} else {
UPDATE_STR(state->helo_name, CLIENT_HELO_UNKNOWN);
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->helo_name, attr_value);
}
/*
* PROTO=protocol name. Disallow characters that could mess up our
* own Received: message headers.
* PROTO=SMTP protocol name.
*/
else if (STREQ(attr_name, XCLIENT_PROTO)) {
if (name_code(xclient_proto, NAME_CODE_FLAG_NONE, attr_value) < 0) {
if (name_code(proto_names, NAME_CODE_FLAG_NONE, attr_value) < 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XCLIENT_PROTO, attr_value);
return (-1);
}
UPDATE_STR(state->protocol, attr_value);
UPDATE_STR(state->protocol, uppercase(attr_value));
}
/*
@ -1911,7 +1885,7 @@ static int xclient_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
{
int arg_no;
SMTPD_TOKEN *argp;
char *attr_value;
char *attr_name;
int updated = 0;
@ -1934,7 +1908,7 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
}
if (argc < 2) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Syntax: %s name=value...",
smtpd_chat_reply(state, "501 Syntax: %s attribute=value...",
XFORWARD_CMD);
return (-1);
}
@ -1951,22 +1925,18 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
smtpd_xforward_preset(state);
/*
* Iterate over all NAME=VALUE attributes.
* Iterate over all attribute=value elements.
*/
for (arg_no = 1; arg_no < argc; arg_no++) {
attr_name = argv[arg_no].strval;
for (argp = argv + 1; argp < argv + argc; argp++) {
attr_name = argp->strval;
/*
* For safety's sake mask non-printable characters in the raw and
* decoded values; we don't want to handle unexploded munitions.
* Complain when they send an attribute that we didn't announce.
*
* The client can send multiple XFORWARD attributes in a single command,
* or multiple XFORWARD commands with fewer attributes.
* For safety's sake mask non-printable characters. We'll do more
* specific censoring later.
*/
if ((attr_value = split_at(attr_name, '=')) == 0) {
if ((attr_value = split_at(attr_name, '=')) == 0 || *attr_value == 0) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Error: name=value expected");
smtpd_chat_reply(state, "501 Error: attribute=value expected");
return (-1);
}
printable(attr_value, '?');
@ -1975,71 +1945,61 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
switch (flag) {
/*
* NAME=hostname. Also updates the client hostname lookup status
* code. Treat a numerical hostname as an unavailable name.
* NAME=host name, not necessarily in the DNS. Censor special
* characters that could mess up message headers.
*/
case SMTPD_XFORWARD_FLAG_NAME:
if (*attr_value && !valid_hostaddr(attr_value, DONT_GRIPE)) {
if (!valid_hostname(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XFORWARD_NAME, attr_value);
return (-1);
}
} else {
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_NAME_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.name, attr_value);
break;
/*
* ADDR=client network address.
* ADDR=host network address, not necessarily on the Internet.
* Censor special characters that could mess up message headers.
*/
case SMTPD_XFORWARD_FLAG_ADDR:
if (*attr_value) {
if (!valid_hostaddr(attr_value, DONT_GRIPE)) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XFORWARD_ADDR, attr_value);
return (-1);
}
} else {
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_ADDR_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.addr, attr_value);
break;
/*
* HELO=hostname. Disallow characters that could mess up our own
* Received: message headers but allow [].
* HELO=hostname that the host introduced itself with (not
* necessarily SMTP HELO). Censor special characters that could
* mess up message headers.
*/
case SMTPD_XFORWARD_FLAG_HELO:
if (*attr_value) {
if (strlen(attr_value) > VALID_HOSTNAME_LEN) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XFORWARD_HELO, attr_value);
return (-1);
}
neuter(attr_value, "<>()\\\";:@", '?');
} else {
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_HELO_UNKNOWN;
} else {
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
UPDATE_STR(state->xforward.helo_name, attr_value);
break;
/*
* PROTO=protocol name. Neutralize characters that could mess up
* our own Received: message headers.
* PROTO=protocol name, not necessarily SMTP or ESMTP. Censor
* special characters that could mess up message headers.
*/
case SMTPD_XFORWARD_FLAG_PROTO:
if (strlen(attr_value) > 64) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XFORWARD_PROTO, attr_value);
return (-1);
if (STREQ(attr_value, XFORWARD_UNAVAILABLE)) {
attr_value = CLIENT_PROTO_UNKNOWN;
} else {
if (strlen(attr_value) > 64) {
state->error_mask |= MAIL_ERROR_PROTOCOL;
smtpd_chat_reply(state, "501 Bad %s syntax: %s",
XFORWARD_PROTO, attr_value);
return (-1);
}
neuter(attr_value, NEUTER_CHARACTERS, '?');
}
neuter(attr_value, "<>()\\\";:@", '?');
UPDATE_STR(state->xforward.protocol, attr_value);
break;
@ -2063,9 +2023,10 @@ static int xforward_cmd(SMTPD_STATE *state, int argc, SMTPD_TOKEN *argv)
if (state->xforward.namaddr)
myfree(state->xforward.namaddr);
state->xforward.namaddr =
IS_AVAIL_CLIENT_ADDR(state->xforward.addr) ?
concatenate(state->xforward.name, "[",
state->xforward.addr, "]",
(char *) 0);
(char *) 0) : mystrdup(state->xforward.name);
}
smtpd_chat_reply(state, "250 Ok");
return (0);

View File

@ -206,9 +206,8 @@ extern void smtpd_peer_reset(SMTPD_STATE *state);
#define SMTPD_PROXY_XFORWARD_IDENT (1<<4) /* message identifier */
/*
* If forwarding client information, don't mix direct client information
* from the current SMTP session with forwarded client information from an
* up-stream session.
* If forwarding client information, don't mix information from the current
* SMTP session with forwarded information from an up-stream session.
*/
#define FORWARD_CLIENT_ATTR(s, a) \
(((s)->xforward.flags & SMTPD_XFORWARD_FLAG_CLIENT_MASK) ? \

View File

@ -156,7 +156,6 @@
#include <cleanup_user.h>
#include <mail_params.h>
#include <rec_type.h>
#include <xtext.h>
#include <mail_proto.h>
/* Application-specific. */
@ -201,10 +200,10 @@ static int smtpd_xforward(SMTPD_STATE *state, VSTRING *buf, const char *name,
* How much space does this attribute need?
*/
if (!value_available)
value = "";
value = XFORWARD_UNAVAILABLE;
new_len = strlen(name) + strlen(value) + 2; /* SPACE name = value */
if (new_len > PAYLOAD_LIMIT)
msg_warn("%s payload %s=%.10s... exceeds SMTP protocol limit",
msg_warn("%s command payload %s=%.10s... exceeds SMTP protocol limit",
XFORWARD_CMD, name, value);
/*

View File

@ -27,7 +27,8 @@ SRCS = alldig.c argv.c argv_split.c attr_print0.c attr_print64.c \
username.c valid_hostname.c vbuf.c vbuf_print.c vstream.c \
vstream_popen.c vstring.c vstring_vstream.c watchdog.c writable.c \
write_buf.c write_wait.c auto_clnt.c attr_clnt.c attr_scan_plain.c \
attr_print_plain.c sane_connect.c neuter.c name_code.c
attr_print_plain.c sane_connect.c neuter.c name_code.c \
uppercase.c
OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
attr_scan0.o attr_scan64.o base64_code.o basename.o binhash.o \
chroot_uid.o clean_env.o close_on_exec.o concatenate.o ctable.o \
@ -56,7 +57,8 @@ OBJS = alldig.o argv.o argv_split.o attr_print0.o attr_print64.o \
username.o valid_hostname.o vbuf.o vbuf_print.o vstream.o \
vstream_popen.o vstring.o vstring_vstream.o watchdog.o writable.o \
write_buf.o write_wait.o auto_clnt.o attr_clnt.o attr_scan_plain.o \
attr_print_plain.o sane_connect.o $(STRCASE) neuter.o name_code.o
attr_print_plain.o sane_connect.o $(STRCASE) neuter.o name_code.o \
uppercase.o
HDRS = argv.h attr.h base64_code.h binhash.h chroot_uid.h clean_env.h \
connect.h ctable.h dict.h dict_db.h dict_dbm.h dict_env.h \
dict_cidr.h dict_ht.h dict_ldap.h dict_mysql.h dict_ni.h dict_nis.h \
@ -1292,6 +1294,11 @@ unix_trigger.o: trigger.h
unsafe.o: unsafe.c
unsafe.o: sys_defs.h
unsafe.o: safe.h
uppercase.o: uppercase.c
uppercase.o: sys_defs.h
uppercase.o: stringops.h
uppercase.o: vstring.h
uppercase.o: vbuf.h
username.o: username.c
username.o: sys_defs.h
username.o: username.h

View File

@ -22,6 +22,7 @@
extern char *printable(char *, int);
extern char *neuter(char *, const char *, int);
extern char *lowercase(char *);
extern char *uppercase(char *);
extern char *skipblanks(const char *);
extern char *trimblanks(char *, int);
extern char *concatenate(const char *,...);

View File

@ -0,0 +1,43 @@
/*++
/* NAME
/* uppercase 3
/* SUMMARY
/* map lowercase characters to uppercase
/* SYNOPSIS
/* #include <stringops.h>
/*
/* char *uppercase(buf)
/* char *buf;
/* DESCRIPTION
/* uppercase() replaces lowercase characters in its null-terminated
/* input by their uppercase equivalent.
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*--*/
/* System library. */
#include "sys_defs.h"
#include <ctype.h>
/* Utility library. */
#include "stringops.h"
char *uppercase(char *string)
{
char *cp;
int ch;
for (cp = string; (ch = *cp) != 0; cp++)
if (ISLOWER(ch))
*cp = TOUPPER(ch);
return (string);
}