/* * Copyright (c) 2005 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: ssreplacemsg.c,v 1.13 2007/08/18 15:58:24 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/memops.h" #include "sm/reccom.h" #include "sm/rcbst.h" #include "smtps-str.h" #include "s2q.h" #include "smtps.h" #if MTA_USE_PMILTER #include "s2m.h" #include "sm/pmilter.h" #include "log.h" /* ** SS_REPLACEMSG -- read message chunk and write it to cdb ** ** Parameters: ** ss_sess -- session context ** l -- length of buffer to read from rcb ** ** Returns: ** usual sm_error code ** ** Called by: sm_w4q2s_reply() */ sm_ret_T ss_replacemsg(ss_sess_P ss_sess, uint32_t l) { sm_ret_T ret; int c; uint u; ssize_t byteswritten; ss_ta_P ss_ta; cdb_ctx_P cdb_ctx; const uchar *buf; static SM_DECL_EOT; SM_IS_SS_SESS(ss_sess); ss_ta = ss_sess->ssse_ta; cdb_ctx = ss_sess->ssse_sctx->ssc_cdb_ctx; sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_INFO, 12, "sev=INFO, func=ss_replacemsg, ss_sess=%s, ss_ta=%s, status=%d" , ss_sess->ssse_id, ss_ta->ssta_id, ss_ta->ssta_msg_st); if (ss_ta->ssta_msg_st != SSTA_ST_MR_CDB_OK) { ret = sm_rcb_skip(ss_sess->ssse_rcb, l); return SM_SUCCESS; } ret = sm_rcb_getdata(ss_sess->ssse_rcb, &buf, l); if (sm_is_err(ret)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_replacemsg, ss_sess=%s, ss_ta=%s, sm_rcb_getdata=%m, l=%d" , ss_sess->ssse_id, ss_ta->ssta_id, ret, l); ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL; return ret; } for (u = 0; u < l; u++) { c = buf[u]; if (c == eot[ss_ta->ssta_eot_state]) { if (++ss_ta->ssta_eot_state >= SM_EOT_LEN) break; } else { ss_ta->ssta_eot_state = 0; if (c == eot[ss_ta->ssta_eot_state]) ++ss_ta->ssta_eot_state; } } /* ** Increment u as it is the index in buf[] not the length ** of the buffer to write. */ if (ss_ta->ssta_eot_state >= SM_EOT_LEN && ++u < l) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_replacemsg, found_eot_at=%d, buf_len=%d" , u, l); l = u; ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL; } ret = cdb_write(cdb_ctx, ss_ta->ssta_msg_fp, buf, l, &byteswritten); if (sm_is_err(ret) || byteswritten < 0 || l != (uint32_t) byteswritten) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_replacemsg, ss_sess=%s, ss_ta=%s, cdb_write=%m, len=%u, byteswritten=%d" , ss_sess->ssse_id, ss_ta->ssta_id, ret, l , (int) byteswritten); ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL; } else ss_ta->ssta_rmsg_sz_b += byteswritten; return SM_SUCCESS; } /* ** SS_CLOSEMSG -- close (replacement) cdb ** ** Parameters: ** ss_sess -- session context ** ** Returns: ** usual sm_error code ** ** Side Effects: removes original cdb if everything goes well here */ static sm_ret_T ss_closemsg(ss_sess_P ss_sess) { sm_ret_T ret; ss_ta_P ss_ta; cdb_ctx_P cdb_ctx; ss_ctx_P ss_ctx; SM_IS_SS_SESS(ss_sess); ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); ss_ctx = ss_sess->ssse_sctx; ret = SM_SUCCESS; sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_INFO, 12, "sev=INFO, func=ss_closemsg, ss_sess=%s, ss_ta=%s, status=%d" , ss_sess->ssse_id, ss_ta->ssta_id, ss_ta->ssta_msg_st); /* no message has been written? */ if (SSTA_ST_MR_NONE == ss_ta->ssta_msg_st) return SM_SUCCESS; cdb_ctx = ss_sess->ssse_sctx->ssc_cdb_ctx; if (NULL == ss_ta->ssta_cdb_id) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 2, "sev=ERR, func=ss_closemsg, ss_sess=%s, ss_ta=%s, status=missing cdb id" , ss_sess->ssse_id, ss_ta->ssta_id); ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL; ret = sm_error_perm(SM_EM_PMILTER, SM_E_UNEXPECTED); goto error; } if (SM_GOT_CRLF == ss_ta->ssta_eot_state) { const char buf[] = ".\r\n"; size_t l; ssize_t byteswritten; l = sizeof(buf) - 1; ret = cdb_write(cdb_ctx, ss_ta->ssta_msg_fp, (const uchar *) buf, l, &byteswritten); if (sm_is_err(ret) || byteswritten < 0 || l != (size_t) byteswritten) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 2, "sev=ERR, func=ss_closemsg, ss_sess=%s, ss_ta=%s, cdb_write=%m, len=%y, byteswritten=%d, status=fail" , ss_sess->ssse_id, ss_ta->ssta_id , ret, l, byteswritten); ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL; } else ss_ta->ssta_rmsg_sz_b += byteswritten; } else if (ss_ta->ssta_eot_state < SM_EOT_LEN) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 2, "sev=ERR, func=ss_closemsg, ss_sess=%s, ss_ta=%s, eot_state=%d, status=invalid end of message" , ss_sess->ssse_id, ss_ta->ssta_id , ss_ta->ssta_eot_state); ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL; ret = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); } if (SSTA_ST_MR_CDB_OK != ss_ta->ssta_msg_st) goto error; ret = cdb_close(cdb_ctx, ss_ta->ssta_msg_fp, SM_IO_CF_SYNC); if (sm_is_err(ret)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 8, "sev=ERR, func=ss_closemsg, ss_sess=%s, ss_ta=%s, cdb_close=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); goto error; } ss_ta->ssta_msg_fp = NULL; ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_CLSD; ret = sm_s2m_msg_rplc_stat(ss_sess, ss_ctx->ssc_s2m_ctx, ss_sess->ssse_id, SM_SUCCESS); if (sm_is_err(ret)) sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_WARN, 9, "sev=WARN, func=ss_closemsg, ss_sess=%s, ss_ta=%s, sm_s2m_msg_rplc_stat=%#x" , ss_sess->ssse_id, ss_ta->ssta_id, ret); /* remove original file, new file is safely stored */ (void) cdb_abort(cdb_ctx, ss_ta->ssta_dfp); ss_ta->ssta_dfp = NULL; SSTA_CLR_FLAG(ss_ta, SSTA_FL_CDB_EXISTS); ss_ta->ssta_msg_sz_b = ss_ta->ssta_rmsg_sz_b; sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_INFO, 9, "sev=INFO, func=ss_closemsg, ss_sess=%s, ss_ta=%s, status=message_replaced, size=%lu" , ss_sess->ssse_id, ss_ta->ssta_id , (ulong) ss_ta->ssta_msg_sz_b); return SM_SUCCESS; error: if (cdb_ctx != NULL) { if (ss_ta->ssta_msg_fp != NULL) { (void) cdb_abort(cdb_ctx, ss_ta->ssta_msg_fp); ss_ta->ssta_msg_fp = NULL; /* abort also removes the file */ SM_CSTR_FREE(ss_ta->ssta_cdb_id); } if (ss_ta->ssta_cdb_id != NULL) { (void) cdb_unlink(cdb_ctx, (const char *) sm_cstr_data(ss_ta->ssta_cdb_id)); SM_CSTR_FREE(ss_ta->ssta_cdb_id); } } if (!sm_is_err(ret)) ret = sm_err_perm(EIO); (void) sm_s2m_msg_rplc_stat(ss_sess, ss_ctx->ssc_s2m_ctx, ss_sess->ssse_id, ret); return ret; } /* ** SS_RPLCMSG -- read message chunks and write them to cdb ** ** Parameters: ** ss_sess -- session context ** ** Returns: ** usual sm_error code */ sm_ret_T ss_rplcmsg(ss_sess_P ss_sess) { sm_ret_T ret; ss_ta_P ss_ta; ss_ctx_P ss_ctx; cdb_ctx_P cdb_ctx; ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); ss_ctx = ss_sess->ssse_sctx; cdb_ctx = ss_sess->ssse_sctx->ssc_cdb_ctx; ret = cdb_open_write(cdb_ctx, ss_ta->ssta_id, ss_ta->ssta_msg_fp, SM_IO_WREXCL, 1, &ss_ta->ssta_cdb_id); if (sm_is_err(ret)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_rplcmsg, ss_sess=%s, ss_ta=%s, cdb_open=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_NONE; } else ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_OK; ss_ta->ssta_eot_state = SM_GOT_CRLF; do { ret = ss_add_rq(ss_sess, ss_sess->ssse_id, ss_ctx->ssc_s2m_ctx, NULL); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 4, "sev=ERR, func=ss_rplcmsg, ss_sess=%s, ss_ta=%s, ss_add_rq=%m" , ss_sess->ssse_id, ss_ta->ssta_id , ret); ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL; break; } ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4m2s, ss_ctx->ssc_s2m_ctx); if (sm_is_err(ret)) { /* don't use milter anymore in this session */ SSSE_CLR_FLAG(ss_sess, SSSE_FL_PM_USE); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERR, 4, "sev=ERR, func=ss_rplcmsg, ss_sess=%s, ss_ta=%s, sm_w4q2s_reply=%m" , ss_sess->ssse_id, ss_ta->ssta_id , ret); ss_ta->ssta_msg_st = SSTA_ST_MR_CDB_FAIL; break; } } while (SMTP_R_CONT == ret); ret = ss_closemsg(ss_sess); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_PMILTER, SS_LMOD_PMILTER, SM_LOG_ERROR, 6, "sev=ERROR, func=ss_rplcmsg, ss_sess=%s, ss_ta=%s, ss_closemsg=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); if (SSC_IS_MFLAG(ss_ctx, SSC_MFL_PM_421)) ret = SMTP_R_SSD; else /* ignore error */ ret = SM_SUCCESS; } return ret; } #endif /* MTA_USE_PMILTER */