summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stef@memberwebs.com>2004-11-26 23:03:27 +0000
committerStef Walter <stef@memberwebs.com>2004-11-26 23:03:27 +0000
commitfb796d7ae45604c7c5842d6111f3a5204eeb53e5 (patch)
treea11f47743d9482a682dc10fef6e12aecc39f5853
parent1c8ac60c3855a955dd9199d65d2f4d657b8ae806 (diff)
- Add XCLIENT support
- Ignore XCLIENT/XFORWARD commands coming from clients.
-rw-r--r--ChangeLog3
-rw-r--r--common/smtppass.c90
-rw-r--r--common/smtppass.h1
-rw-r--r--common/spio.c17
-rw-r--r--common/sppriv.h1
-rw-r--r--doc/clamsmtpd.conf3
-rw-r--r--doc/clamsmtpd.conf.54
7 files changed, 114 insertions, 5 deletions
diff --git a/ChangeLog b/ChangeLog
index 73febc4..b7d0e09 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,6 @@
1.2 ????
-
+ - XCLIENT support
+ - Drop XCLIENT and XFORWARD commands coming in from clients for security.
- Added the sample virus_action.sh script to the distribution
- Documentation fixes [Olivier Beyssac]
diff --git a/common/smtppass.c b/common/smtppass.c
index 5d25719..59a4bf4 100644
--- a/common/smtppass.c
+++ b/common/smtppass.c
@@ -89,11 +89,13 @@ spthread_t;
#define SMTP_DATAINTERMED "354 Start mail input; end with <CRLF>.<CRLF>" CRLF
#define SMTP_FAILED "451 Local Error" CRLF
#define SMTP_NOTSUPP "502 Command not implemented" CRLF
+#define SMTP_NOTAUTH "554 Insufficient authorization" CRLF
#define SMTP_OK "250 Ok" CRLF
#define SMTP_REJPREFIX "550 Content Rejected; "
#define SMTP_DATA "DATA" CRLF
#define SMTP_NOOP "NOOP" CRLF
+#define SMTP_XCLIENT "XCLIENT ADDR=%s" CRLF
#define SMTP_BANNER "220 smtp.passthru" CRLF
#define SMTP_HELO_RSP "250 smtp.passthru" CRLF
#define SMTP_EHLO_RSP "250-smtp.passthru" CRLF
@@ -106,6 +108,8 @@ spthread_t;
#define ESMTP_CHUNK "CHUNKING"
#define ESMTP_BINARY "BINARYMIME"
#define ESMTP_CHECK "CHECKPOINT"
+#define ESMTP_XFORWARD "XFORWARD"
+#define ESMTP_XCLIENT "XCLIENT"
#define HELO_CMD "HELO"
#define EHLO_CMD "EHLO"
@@ -115,6 +119,8 @@ spthread_t;
#define RSET_CMD "RSET"
#define STARTTLS_CMD "STARTTLS"
#define BDAT_CMD "BDAT"
+#define XFORWARD_CMD "XFORWARD"
+#define XCLIENT_CMD "XCLIENT"
#define DATA_END_SIG "." CRLF
@@ -146,6 +152,7 @@ spthread_t;
#define CFG_KEEPALIVES "KeepAlives"
#define CFG_USER "User"
#define CFG_PIDFILE "PidFile"
+#define CFG_XCLIENT "XClient"
/* -----------------------------------------------------------------------
* DEFAULT SETTINGS
@@ -794,6 +801,10 @@ static int smtp_passthru(spctx_t* ctx)
int first_rsp = 1; /* The first 220 response from server to be filtered */
int filter_host = 0; /* Next response is 250 hostname, which we change */
+ /* XCLIENT is for use in access control */
+ int xclient_sup = 0; /* Is XCLIENT supported? */
+ int xclient_sent = 0; /* Have we sent an XCLIENT command? */
+
ASSERT(spio_valid(&(ctx->client)) &&
spio_valid(&(ctx->server)));
@@ -832,6 +843,26 @@ static int smtp_passthru(spctx_t* ctx)
/* Only valid after EHLO or HELO commands */
filter_host = 0;
+ /*
+ * At this point we may want to send our XCLIENT. This is a per
+ * connection command, lasts until a RSET.
+ */
+ if(xclient_sup && !xclient_sent && g_state.xclient)
+ {
+ sp_messagex(ctx, LOG_DEBUG, "sending XCLIENT");
+
+ if(spio_write_dataf(ctx, &(ctx->server), SMTP_XCLIENT, ctx->client.peername) == -1)
+ RETURN(-1);
+
+ if(read_server_response(ctx) == -1)
+ RETURN(-1);
+
+ if(!get_successful_rsp(S_LINE, NULL))
+ sp_messagex(ctx, LOG_WARNING, "server didn't accept XCLIENT");
+
+ xclient_sent = 1;
+ }
+
/* Handle the DATA section via our AV checker */
if(is_first_word(C_LINE, DATA_CMD, KL(DATA_CMD)))
{
@@ -858,17 +889,30 @@ static int smtp_passthru(spctx_t* ctx)
}
/*
- * We need our response to HELO to be modified in order
+ * We need our response to HELO and EHLO to be modified in order
* to prevent complaints about mail loops
*/
- else if(is_first_word(C_LINE, EHLO_CMD, KL(EHLO_CMD)) ||
- is_first_word(C_LINE, HELO_CMD, KL(HELO_CMD)))
+ else if(is_first_word(C_LINE, EHLO_CMD, KL(EHLO_CMD)))
{
/* EHLO can have multline responses so we set a flag */
filter_host = 1;
}
/*
+ * We always support XFORWARD on a HELO type connection. We do this
+ * for security reasons, so that a client can't get around filtering
+ * by backing up one on the protocol.
+ */
+ else if(is_first_word(C_LINE, HELO_CMD, KL(HELO_CMD)))
+ {
+ sp_messagex(ctx, LOG_DEBUG, "XCLIENT support assumed");
+ xclient_sup = 1;
+
+ /* Filter host as with EHLO above */
+ filter_host = 1;
+ }
+
+ /*
* We don't like these commands. Filter them out. We should have
* filtered out their service extensions earlier in the EHLO response.
* This is just for errant clients.
@@ -885,6 +929,25 @@ static int smtp_passthru(spctx_t* ctx)
continue;
}
+ /*
+ * For security reasons we're not about to forward any XCLIENTs
+ * or XFORWARDs from our client through. This could lead to a
+ * client using our privileged IP address to change an audit
+ * trail or relay etc...
+ */
+ else if(is_first_word(C_LINE, XCLIENT_CMD, KL(XCLIENT_CMD)) ||
+ is_first_word(C_LINE, XFORWARD_CMD, KL(XFORWARD_CMD)))
+ {
+ trim_end(C_LINE);
+ sp_messagex(ctx, LOG_WARNING, "client attempted use of privileged feature: %s", C_LINE);
+
+ if(spio_write_data(ctx, &(ctx->client), SMTP_NOTAUTH) == -1)
+ RETURN(-1);
+
+ /* Command handled */
+ continue;
+ }
+
/* All other commands just get passed through to server */
if(spio_write_data(ctx, &(ctx->server), C_LINE) == -1)
RETURN(-1);
@@ -963,11 +1026,23 @@ static int smtp_passthru(spctx_t* ctx)
*/
if(is_first_word(C_LINE, EHLO_CMD, KL(EHLO_CMD)))
{
+ /*
+ * On ESMTP connections we let the server tell us whether it
+ * wants XFORWARDs or not. (In contrast to old SMTP above).
+ */
+ if(is_first_word(p, ESMTP_XCLIENT, KL(ESMTP_XCLIENT)))
+ {
+ sp_messagex(ctx, LOG_DEBUG, "XCLIENT supported");
+ xclient_sup = 1;
+ }
+
if(is_first_word(p, ESMTP_PIPELINE, KL(ESMTP_PIPELINE)) ||
is_first_word(p, ESMTP_TLS, KL(ESMTP_TLS)) ||
is_first_word(p, ESMTP_CHUNK, KL(ESMTP_CHUNK)) ||
is_first_word(p, ESMTP_BINARY, KL(ESMTP_BINARY)) ||
- is_first_word(p, ESMTP_CHECK, KL(ESMTP_CHECK)))
+ is_first_word(p, ESMTP_CHECK, KL(ESMTP_CHECK)) ||
+ is_first_word(p, ESMTP_XCLIENT, KL(ESMTP_XCLIENT)) ||
+ is_first_word(p, ESMTP_XFORWARD, KL(ESMTP_XFORWARD)))
{
sp_messagex(ctx, LOG_DEBUG, "filtered ESMTP feature: %s", trim_space((char*)p));
@@ -1598,6 +1673,13 @@ int sp_parse_option(const char* name, const char* value)
ret = 1;
}
+ else if(strcasecmp(CFG_XCLIENT, name) == 0)
+ {
+ if((g_state.xclient = strtob(value)) == -1)
+ errx(2, "invalid value for " CFG_XCLIENT);
+ ret = 1;
+ }
+
else if(strcasecmp(CFG_OUTADDR, name) == 0)
{
if(sock_any_pton(value, &(g_state.outaddr), SANY_OPT_DEFPORT(25)) == -1)
diff --git a/common/smtppass.h b/common/smtppass.h
index 418686e..5522ca9 100644
--- a/common/smtppass.h
+++ b/common/smtppass.h
@@ -99,6 +99,7 @@ int spio_read_line(struct spctx* ctx, spio_t* io, int opts);
/* Write data to socket (must supply line endings if needed).
* Guaranteed to accept all data or fail. */
int spio_write_data(struct spctx* ctx, spio_t* io, const char* data);
+int spio_write_dataf(struct spctx* ctx, spio_t* io, const char* fmt, ...);
int spio_write_data_raw(struct spctx* ctx, spio_t* io, unsigned char* buf, int len);
/* Empty the given socket */
diff --git a/common/spio.c b/common/spio.c
index e8425bf..f43988a 100644
--- a/common/spio.c
+++ b/common/spio.c
@@ -510,6 +510,23 @@ int spio_write_data(spctx_t* ctx, spio_t* io, const char* data)
return spio_write_data_raw(ctx, io, (unsigned char*)data, len);
}
+int spio_write_dataf(struct spctx* ctx, spio_t* io, const char* fmt, ...)
+{
+ char buf[SP_LINE_LENGTH];
+ va_list ap;
+ ASSERT(ctx && io && fmt);
+
+ buf[0] = 0;
+
+ va_start(ap, fmt);
+ vsnprintf(buf, SP_LINE_LENGTH, fmt, ap);
+ va_end(ap);
+
+ buf[SP_LINE_LENGTH - 1] = 0;
+
+ return spio_write_data(ctx, io, buf);
+}
+
int spio_write_data_raw(spctx_t* ctx, spio_t* io, unsigned char* buf, int len)
{
int r;
diff --git a/common/sppriv.h b/common/sppriv.h
index e978107..2ecf249 100644
--- a/common/sppriv.h
+++ b/common/sppriv.h
@@ -49,6 +49,7 @@ typedef struct spstate
struct timeval timeout; /* Timeout for communication */
int keepalives; /* Send server keep alives at this interval */
int transparent; /* Transparent proxying */
+ int xclient; /* Send XFORWARD info */
const char* directory; /* The temp directory */
const char* user; /* User to run as */
const char* pidfile; /* The pid file for daemon */
diff --git a/doc/clamsmtpd.conf b/doc/clamsmtpd.conf
index 2033171..8ef4820 100644
--- a/doc/clamsmtpd.conf
+++ b/doc/clamsmtpd.conf
@@ -22,6 +22,9 @@ OutAddress: 10026
# Keep Alives (ie: NOOP's to server)
#KeepAlives: 0
+# Send XCLIENT commands to receiving server
+#XClient: off
+
# Address to listen on (defaults to all local addresses on port 10025)
#Listen: 0.0.0.0:10025
diff --git a/doc/clamsmtpd.conf.5 b/doc/clamsmtpd.conf.5
index e9cac98..49cd582 100644
--- a/doc/clamsmtpd.conf.5
+++ b/doc/clamsmtpd.conf.5
@@ -149,6 +149,10 @@ in
.Xr clamsmtpd 8
for a discussion of this option.
[ Default: off ]
+.It Ar XClient
+Send an XCLIENT command to the receiving server. This is useful for forwarding
+client addresses and connection info to servers that support this feature.
+[ Default: off ]
.El
.Sh ADDRESSES
Addresses can be specified in multiple formats: