diff options
| -rw-r--r-- | common/smtppass.c | 90 | ||||
| -rw-r--r-- | common/smtppass.h | 1 | ||||
| -rw-r--r-- | common/spio.c | 17 | ||||
| -rw-r--r-- | common/sppriv.h | 1 | 
4 files changed, 105 insertions, 4 deletions
| 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 */ | 
