/* * Copyright (c) 2004-2006 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: pmilter_engine.c,v 1.34 2007/02/13 03:58:18 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/ctype.h" #include "sm/reccom.h" #include "sm/pmilter.h" #include "pmilter.h" #include "sm/pmfdef.h" #include "sm/pmfapi.h" #include "util.h" #if MTA_USE_PMILTER /* ** How to implement session timeouts? ** That is, when the server aborts a connection will pmilter be informed? ** ** Note: currently a reply text is only sent if it ** - has an SMTP reply code ** - the reply code matches the return value of the function ** This means it can't be used for other things right now, e.g., ** changing an address. */ #define CHKREPLYTEXT \ sm_str_getlen(pmse_ctx->pmse_reply_text) > 0 && \ IS_SMTP_REPLY(rcode) && \ SMTP_REPLY_MATCHES_RCODE(pmse_ctx->pmse_reply_text, rcode, 0, rc) /* ** SM_PMILT_NSEID -- New SMTP session ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** rcbe -- RCB for reply ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_nseid(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe) { sm_ret_T ret; int rcode; bool sendreplytext; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; rcode = SMTP_R_OK; sendreplytext = false; if (pmilter->pmfi_connect != NULL && SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_CNNCT)) { int rc; sm_sockaddr_T sm_sockaddr; sm_memzero(&sm_sockaddr, sizeof(sm_sockaddr)); sm_sockaddr.sin.sin_addr.s_addr = pmse_ctx->pmse_cltipv4; sm_sockaddr.sin.sin_family = AF_INET; sm_str_clr(pmse_ctx->pmse_reply_text); pm_clrreplies(pmse_ctx); rcode = (*pmilter->pmfi_connect)(pmse_ctx, (char *) sm_str_getdata(pmse_ctx->pmse_arg1), &sm_sockaddr); sendreplytext = CHKREPLYTEXT; } ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id, SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE, SM_RCBV_INT, RT_M2S_RCODE, rcode, SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND, pmse_ctx->pmse_reply_text, SM_RCBV_END); return ret; } /* ** SM_PMILT_CSEID -- Close SMTP session ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_cseid(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx) { sm_ret_T ret; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; if (pmilter->pmfi_close != NULL) ret = (*pmilter->pmfi_close)(pmse_ctx); return SM_SUCCESS; } /* ** SM_PMILT_HELO -- EHLO/HELO ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** rcbe -- RCB for reply ** ehlo -- was EHLO used? ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_helo(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe, bool ehlo) { sm_ret_T ret; int rcode; bool sendreplytext; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; rcode = SMTP_R_OK; sendreplytext = false; if (pmilter->pmfi_helo != NULL) { int rc; sm_str_clr(pmse_ctx->pmse_reply_text); rcode = (*pmilter->pmfi_helo)(pmse_ctx, (char *) sm_str_getdata(pmse_ctx->pmse_arg1), ehlo); sendreplytext = CHKREPLYTEXT; } ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id, SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE, SM_RCBV_INT, RT_M2S_RCODE, rcode, SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND, pmse_ctx->pmse_reply_text, SM_RCBV_END); return ret; } /* ** SM_PMILT_MAIL -- MAIL ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** rcbe -- RCB for reply ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_mail(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe) { sm_ret_T ret; int rcode; unsigned int nelem; bool sendreplytext; char **argv; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; rcode = SMTP_R_OK; argv = NULL; sendreplytext = false; ret = args2argv(pmse_ctx->pmse_arg2, &nelem, &argv); if (sm_is_err(ret)) goto error; if (pmilter->pmfi_mail != NULL) { int rc; sm_str_clr(pmse_ctx->pmse_reply_text); rcode = (*pmilter->pmfi_mail)(pmse_ctx, (char *) sm_str_getdata(pmse_ctx->pmse_arg1), argv); sendreplytext = CHKREPLYTEXT; } ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id, SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE, SM_RCBV_INT, RT_M2S_RCODE, rcode, SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND, pmse_ctx->pmse_reply_text, SM_RCBV_END); error: if (argv != NULL) sm_free(argv); return ret; } /* ** SM_PMILT_RCPT -- RCPT ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** rcbe -- RCB for reply ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_rcpt(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe) { sm_ret_T ret; int rcode; unsigned int nelem; bool sendreplytext; char **argv; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; rcode = SMTP_R_OK; argv = NULL; sendreplytext = false; ret = args2argv(pmse_ctx->pmse_arg2, &nelem, &argv); if (sm_is_err(ret)) goto error; if (pmilter->pmfi_rcpt != NULL) { int rc; sm_str_clr(pmse_ctx->pmse_reply_text); rcode = (*pmilter->pmfi_rcpt)(pmse_ctx, (char *) sm_str_getdata(pmse_ctx->pmse_arg1), argv); sendreplytext = CHKREPLYTEXT; } ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id, SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE, SM_RCBV_INT, RT_M2S_RCODE, rcode, SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND, pmse_ctx->pmse_reply_text, SM_RCBV_END); error: if (argv != NULL) sm_free(argv); return ret; } /* ** SM_PMILT_DATA -- DATA ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** rcbe -- RCB for reply ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_data(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_rcbe_P rcbe) { sm_ret_T ret; int rcode; bool sendreplytext; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; rcode = SMTP_R_OK; sendreplytext = false; if (pmilter->pmfi_data != NULL) { int rc; sm_str_clr(pmse_ctx->pmse_reply_text); rcode = (*pmilter->pmfi_data)(pmse_ctx); sendreplytext = CHKREPLYTEXT; } ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id, SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE, SM_RCBV_INT, RT_M2S_RCODE, rcode, SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND, pmse_ctx->pmse_reply_text, SM_RCBV_END); return ret; } /* ** SM_PMILT_MSG -- message chunck ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** buf -- message chunck ** len -- size of message chunck ** rcbe -- RCB for reply ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_msg(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, uchar *buf, size_t len, sm_rcbe_P rcbe) { sm_ret_T ret; int rcode; bool sendreplytext; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; rcode = SMTP_R_OK; sendreplytext = false; if (pmilter->pmfi_msg != NULL) { int rc; sm_str_clr(pmse_ctx->pmse_reply_text); rcode = (*pmilter->pmfi_msg)(pmse_ctx, buf, len); sendreplytext = SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC) && CHKREPLYTEXT; } if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) { ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id, SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE, SM_RCBV_INT, RT_M2S_RCODE, rcode, SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND, pmse_ctx->pmse_reply_text, SM_RCBV_END); } return ret; } /* ** SM_PMILT_DOT -- final message chunck ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** buf -- message chunck ** len -- size of message chunck ** rcbe -- RCB for reply ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_dot(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, uchar *buf, size_t len, sm_rcbe_P rcbe) { sm_ret_T ret; int rcode, rc; bool sendreplytext; #if MTA_USE_RSAD bool sendreplies; #endif pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; rcode = SMTP_R_OK; sendreplytext = false; #if MTA_USE_RSAD sendreplies = false; #endif if (pmilter->pmfi_msg != NULL) { sm_str_clr(pmse_ctx->pmse_reply_text); pmse_ctx->pmse_state = PMSE_ST_MSG; rcode = (*pmilter->pmfi_msg)(pmse_ctx, buf, len); if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) { sendreplytext = CHKREPLYTEXT; } else rcode = SMTP_R_OK; } if (!(SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC) && (IS_SMTP_REPLY(rcode) || rcode == SMTP_R_ACCEPT)) && pmilter->pmfi_eom != NULL) { sm_str_clr(pmse_ctx->pmse_reply_text); pm_clrreplies(pmse_ctx); pmse_ctx->pmse_state = PMSE_ST_DOT; rcode = (*pmilter->pmfi_eom)(pmse_ctx); if (SMTP_R_RPLCMSG == rcode && SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSGREPLACE) && pmilter->pmfi_msg_rplc != NULL) { pmse_ctx->pmse_cmd_status = SM_SUCCESS; rcbe->rcbe_wr_cb = sm_pmilt_msg_rplc; rcbe->rcbe_ctx = pmse_ctx; } pmse_ctx->pmse_state = PMSE_ST_NONE; sendreplytext = CHKREPLYTEXT; #if MTA_USE_RSAD sendreplies = (pmse_ctx->pmse_nreplies > 0 && pmse_ctx->pmse_rcodes != NULL); #endif } ret = sm_rcb_putv(&rcbe->rcbe_rcb, RCB_PUTV_FIRST, SM_RCBV_INT, RT_PROT_VER, PROT_VER_RT, SM_RCBV_INT, RT_M2S_ID, pmss_ctx->pmss_id, SM_RCBV_BUF, RT_M2S_SEID, pmse_ctx->pmse_se_id, SMTP_STID_SIZE, SM_RCBV_INT, RT_M2S_RCODE, rcode, SM_RCBV_STR, sendreplytext ? RT_M2S_STATT : RT_NOSEND, pmse_ctx->pmse_reply_text, #if MTA_USE_RSAD SM_RCBV_INTA, sendreplies ? RT_M2S_RCODES : RT_NOSEND, pmse_ctx->pmse_nreplies, pmse_ctx->pmse_rcodes, #endif SM_RCBV_END); if (pmse_ctx->pmse_mail_new != NULL) { if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MAILMOD)) { ret = sm_rcb_putv(&rcbe->rcbe_rcb, 0, SM_RCBV_STR, RT_M2S_MAIL_NEW, pmse_ctx->pmse_mail_new, SM_RCBV_END); } else if (sm_is_success(ret)) ret = sm_error_perm(SM_EM_PMILTER, EINVAL); SM_STR_FREE(pmse_ctx->pmse_mail_new); } /* send recipient mods */ if (!PM_RCPTS_EMPTY(&pmse_ctx->pmse_rcpts_hd)) { if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_RCPTMOD)) { pm_rcpt_P pm_rcpt; for (pm_rcpt = PM_RCPTS_FIRST(&pmse_ctx->pmse_rcpts_hd); pm_rcpt != PM_RCPTS_END(&pmse_ctx->pmse_rcpts_hd) && sm_is_success(ret); pm_rcpt = PM_RCPTS_NEXT(pm_rcpt)) { if (PM_RCPT_ADD == pm_rcpt->pmr_type) ret = sm_rcb_putv(&rcbe->rcbe_rcb, 0, SM_RCBV_STR, RT_M2S_RCPT_ADD, pm_rcpt->pmr_pa, SM_RCBV_END); else ret = sm_rcb_putv(&rcbe->rcbe_rcb, 0, SM_RCBV_STR, RT_M2S_RCPT_DEL, pm_rcpt->pmr_pa, SM_RCBV_INT, RT_M2S_RCPT_IDX, pm_rcpt->pmr_idx, SM_RCBV_END); } } else if (sm_is_success(ret)) ret = sm_error_perm(SM_EM_PMILTER, EINVAL); pm_rcpts_free(pmse_ctx); } /* send header mods */ if (!HDRMODL_EMPTY(pmse_ctx->pmse_hdrmodhd)) { if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_HDRMOD)) { sm_hdrmodl_wr(pmse_ctx->pmse_hdrmodhd, &rcbe->rcbe_rcb, RT_M2S_HM_T_P, RT_M2S_HM_HDR); } else if (sm_is_success(ret)) ret = sm_error_perm(SM_EM_PMILTER, EINVAL); sm_hdrmodl_free(&pmse_ctx->pmse_hdrmodhd); } return ret; } /* ** SM_PMILT_ABORT_TA -- abort current transaction ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_abort_ta(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx) { sm_ret_T ret; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; if (pmilter->pmfi_abort != NULL) ret = (*pmilter->pmfi_abort)(pmse_ctx); return ret; } /* ** SM_PMILT_MSG_RPLC_STAT -- message replacement status ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** pmse_ctx -- pmilter/SMTP server session context ** status -- message replacement status ** ** Returns: ** usual sm_error code */ sm_ret_T sm_pmilt_msg_rplc_stat(pmss_ctx_P pmss_ctx, pmse_ctx_P pmse_ctx, sm_ret_T status) { sm_ret_T ret; pmilter_P pmilter; SM_IS_PMSE_CTX(pmse_ctx); SM_REQUIRE(pmse_ctx->pmse_pmg_ctx != NULL); pmilter = pmse_ctx->pmse_pmg_ctx->pmg_pmilter; SM_REQUIRE(pmilter != NULL); ret = SM_SUCCESS; if (pmilter->pmfi_msg_rplc_stat != NULL) ret = (*pmilter->pmfi_msg_rplc_stat)(pmse_ctx, status); return ret; } #endif /* MTA_USE_PMILTER */