/* * 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: smtps2pmilter.c,v 1.35 2007/02/03 03:23:04 ca Exp $") #include "sm/error.h" #include "sm/assert.h" #include "sm/io.h" #include "sm/rcb.h" #include "sm/reccom.h" #include "pmilter.h" #include "sm/pmilter.h" #include "sm/pmfdef.h" #include "sm/pmfapi.h" #define PMSS_RC_MINSZ 28 #define PMSS_MAX_REC_LEN (65 * 1024) #if 0 generic reply code: /* prepare reply (async) */ ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errPART; inwaitq = true; /* call milter function (rcbe required for response) */ ret2 = sm_pmilt_PART(pmss_ctx, pmse_ctx, rcbe); if (sm_is_err(ret2)) goto errPART; /* put reply into write queue */ ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); if (sm_is_err(ret)) goto errPART; #endif /* 0 */ /* ** SM_PMILT_SMTPSFIND -- Find SMTPS id ** ** Parameters: ** pmg_ctx -- pmilter (library) context ** pmss_ctx -- task context pmilter/SMTP server ** ** Returns: ** >=0: index ** <0: usual sm_error code ** ** Last code review: */ static sm_ret_T sm_pmilt_smtpsfind(pmg_ctx_P pmg_ctx, pmss_ctx_P pmss_ctx) { int r; uint u; uint32_t j; sm_ret_T ret; pmss_ctx_P pmss2_ctx; r = pthread_mutex_lock(&pmg_ctx->pmg_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG */ PM_LEV_DPRINTF(0, (PM_DEBFP, "sev=ERROR, func=sm_pmilt_smtpsfind, lock=%d\n", r)); return sm_error_temp(SM_EM_PMILTER, r); } ret = sm_error_perm(SM_EM_PMILTER, SM_E_NOTFOUND); /* search for sss ctx */ for (j = 1, u = 0; u < SM_ARRAY_SIZE(pmg_ctx->pmg_sctx); u++, j *= 2) { if ((pmg_ctx->pmg_sused & j) != 0) { pmss2_ctx = pmg_ctx->pmg_sctx[u]; if (pmss2_ctx != pmss_ctx && pmss2_ctx->pmss_id == pmss_ctx->pmss_id) { ret = u; break; } } } r = pthread_mutex_unlock(&pmg_ctx->pmg_mutex); SM_ASSERT(r == 0); if (r != 0 && sm_is_success(ret)) ret = sm_error_perm(SM_EM_PMILTER, r); return ret; } /* ** SM_PMILT_SENDMACLIST -- Send list of macros to SMTPS ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** rcb -- RCB to send to SMTPS ** ** Returns: ** usual sm_error code */ static sm_ret_T sm_pmilt_sendmaclist(pmss_ctx_P pmss_ctx, sm_rcb_P rcb) { sm_ret_T ret; uint i, j; uint32_t macm; bool first; ret = SM_SUCCESS; for (i = 0; !sm_is_err(ret) && i < PM_SMST_MAX; i++) { first = true; for (j = 0; !sm_is_err(ret) && j < PM_MAX_MACROS && (macm = pmss_ctx->pmss_mac_names[i][j]) != PMM_END; j++) { #if PM_SE_TA_ID_LOCAL if (PMM_SEID == macm || PMM_MAIL_TAID == macm) continue; #endif if (first) { ret = sm_rcb_put3uint32(rcb, 4, RT_M2S_MACW, i); first = false; if (sm_is_err(ret)) break; } ret = sm_rcb_put3uint32(rcb, 4, RT_M2S_MACM, macm); } } return ret; } /* ** SM_PMSS_REACT -- Decode data received from SMTPS ** ** Parameters: ** pmss_ctx -- task context pmilter/SMTP server ** tsk -- evthr task ** ** Returns: ** usual sm_error code ** ** Locking: ** XXX who locks pmss_ctx? ** ** Called by: sm_smtps2pmilt */ static sm_ret_T sm_pmss_react(pmss_ctx_P pmss_ctx, sm_evthr_task_P tsk) { uint32_t v, l, rt, tl; sm_ret_T ret, ret2, rv; bool inwaitq; sm_rcb_P rcb; sm_rcbe_P rcbe; pmg_ctx_P pmg_ctx; pmse_ctx_P pmse_ctx; sessta_id_T se_id; SM_IS_PMSS_CTX(pmss_ctx); ret = rv = SM_SUCCESS; inwaitq = false; rcbe = NULL; pmse_ctx = NULL; /* ** XXX Todo: add error codes before each goto err* ** this is necessary to figure out what error happened ** (at least for debugging...) ** it is also necessary in some cases to send a reply ** back to SMTPS, e.g., if PMILTER can't allocate memory ** then SMTPS must be told about it... ** ** In general PMILTER doesn't reply to data it can't decode. ** We assume that the sender is fubared and a reply won't help. ** Let it figure out itself that something went wrong. ** However, we should log an error (logging is completely missing). */ /* Decode rcb */ rcb = pmss_ctx->pmss_com.rcbcom_rdrcb; ret = sm_rcb_open_dec(rcb); if (sm_is_err(ret)) { rv = ret; goto error; } pmg_ctx = pmss_ctx->pmss_pmg_ctx; SM_IS_PMG_CTX(pmg_ctx); /* Total length of record */ ret = sm_rcb_getuint32(rcb, &tl); if (sm_is_err(ret) || tl > PMSS_MAX_REC_LEN || tl > sm_rcb_getlen(rcb)) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_RCB2LONG); goto err2; } /* Decode data, act accordingly... */ /* Protocol header: version */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret)) { rv = ret; goto err2; } if (l != 4 || rt != RT_PROT_VER || v != PROT_VER_RT) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_V_MISM); goto err2; } /* SMTPS (new/existing/close) ID */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); PM_LEV_DPRINTF(3, (PM_DEBFP, "sev=DBG, rt=%X, l=%d, smtpsid=%d\n", rt, l, v)); if (sm_is_err(ret) || l != 4) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } if (RT_S2M_NID == rt) { pmilter_P pmilter; /* New SMTPS */ if (pmss_ctx->pmss_status != PMSS_ST_NONE || pmss_ctx->pmss_id != PMSS_ID_NONE) { /* XXX Internal error? Stop task? SMTPS id exists */ rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); /* XXX use different error! */ goto err2; } pmss_ctx->pmss_id = v; pmss_ctx->pmss_cur_session = 0; ret = sm_pmilt_smtpsfind(pmg_ctx, pmss_ctx); if (ret >= 0) { PM_LEV_DPRINTF(0, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, nid=%d, exists=%d\n", pmss_ctx->pmss_id, ret)); /* XXX Internal error? Stop task? SMTPS id exists */ rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); /* XXX use different error! */ goto err2; } ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_S2M_PROT) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } /* XXX check protocol version */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_S2M_MAXTHRDS) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } pmss_ctx->pmss_max_thrs = v; ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_S2M_CAP) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } pmss_ctx->pmss_cap = v & SM_SCAP_PM_ALL; #if 0 if ((v & SM_SCAP_PM_ALL) != v) sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 4, "sev=INFO, func=sm_pmss_react, server_cap=%#x, libpmilter_cap=%#X" , v, SM_SCAP_PM_ALL); #endif while (!SM_RCB_ISEOB(rcb)) { ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } switch (rt) { case RT_S2M_FCT: pmss_ctx->pmss_fct = v & SM_SFCT_PM_ALL; #if 0 if ((v & SM_SFEAT_PM_ALL) != v) sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 4, "sev=INFO, func=sm_pmss_react, server_fct=%#x, libpmilter_fct=%#X" , v, SM_SFCT_PM_ALL); #endif break; case RT_S2M_FEAT: pmss_ctx->pmss_feat = v & SM_SFEAT_PM_ALL; #if 0 if ((v & SM_SFEAT_PM_ALL) != v) sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 4, "sev=INFO, func=sm_pmss_react, server_feat=%#x, libpmilter_feat=%#X" , v, SM_SFEAT_PM_ALL); #endif break; case RT_S2M_MISC: pmss_ctx->pmss_misc = v & SM_SMISC_PM_ALL; #if 0 if ((v & SM_SMISC_PM_ALL) != v) sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 4, "sev=INFO, func=sm_pmss_react, server_misc=%#x, libpmilter_misc=%#X" , v, SM_SMISC_PM_ALL); #endif break; default: ret = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } } pmss_ctx->pmss_status = PMSS_ST_START; SM_ASSERT(pmss_ctx->pmss_pmg_ctx != NULL); pmilter = pmss_ctx->pmss_pmg_ctx->pmg_pmilter; SM_ASSERT(pmilter != NULL); pmss_ctx->pmss_pmcap = pmilter->pmfi_dfl_cap; pmss_ctx->pmss_pmfct = pmilter->pmfi_dfl_fct; pmss_ctx->pmss_pmfeat = pmilter->pmfi_dfl_feat; pmss_ctx->pmss_pmmisc = pmilter->pmfi_dfl_misc; if (pmilter->pmfi_negotiate != NULL) { /* check protocol, capabilities, requirements, etc */ ret = (*pmilter->pmfi_negotiate)(pmss_ctx, pmss_ctx->pmss_cap, pmss_ctx->pmss_fct, pmss_ctx->pmss_feat, pmss_ctx->pmss_misc, &pmss_ctx->pmss_pmcap, &pmss_ctx->pmss_pmfct, &pmss_ctx->pmss_pmfeat, &pmss_ctx->pmss_pmmisc); #if 0 if (sm_is_err(ret)) /* XXX */ ; #endif } /* ** Send a reply to let client know that the connection ** has been accepted. */ ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto err2; inwaitq = true; /* this doesn't need to be async ... */ 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_INT, RT_M2S_PROT, SM_PMILTER_PROT, SM_RCBV_INT, RT_M2S_CAP, pmss_ctx->pmss_pmcap, SM_RCBV_INT, RT_M2S_FCT, pmss_ctx->pmss_pmfct, SM_RCBV_INT, RT_M2S_FEAT, pmss_ctx->pmss_pmfeat, SM_RCBV_END); if (sm_is_err(ret)) { PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, sm_pcb_putv=%x\n", pmss_ctx->pmss_id, v, ret)); goto err2; } ret = sm_pmilt_sendmaclist(pmss_ctx, &rcbe->rcbe_rcb); if (sm_is_err(ret)) { PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, pm_sendmaclist=%r\n", pmss_ctx->pmss_id, v, ret)); goto err2; } ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); if (sm_is_err(ret)) { PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nid=%d, sm_rcbcom_endrep=%x\n", pmss_ctx->pmss_id, v, ret)); goto err2; } PM_LEV_DPRINTF(5, (PM_DEBFP, "sev=DBG, nid=ok\n")); goto done; } else if (RT_S2M_ID == rt) { /* SMTPS id just for identification */ if (PMSS_ST_NONE == pmss_ctx->pmss_status || pmss_ctx->pmss_id != v) { /* XXX Internal error? Stop task? SMTPS id exists */ rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); /* XXX use different error! */ goto err2; } } else if (RT_S2M_CID == rt) { /* SMTPS shuts down */ if (PMSS_ST_NONE == pmss_ctx->pmss_status || pmss_ctx->pmss_id != v) { /* XXX Internal error? Stop task? SMTPS id exists */ rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); /* XXX use different error! */ goto err2; } /* check for EOB? not really: we shut down anyway */ pmss_ctx->pmss_status = PMSS_ST_SH_DOWN; (void) sm_rcb_close_dec(pmss_ctx->pmss_com.rcbcom_rdrcb); /* ** We assume that all no open sessions/transactions exist, ** i.e., SMTPS properly terminated them before sending this ** message. */ /* Terminate (delete) this task, pmss_ctx is cleaned in caller */ return EVTHR_DEL; } else goto err2; /* RT_S2M_ID == rt is the only case in which we continue here */ /* what's next? */ ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret)) { rv = ret; goto err2; } /* XXX always session id? */ if (l != SMTP_STID_SIZE || (rt != RT_S2M_NSEID && rt != RT_S2M_CSEID && rt != RT_S2M_SEID)) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } /* session id */ ret = sm_rcb_getn(rcb, (uchar *) se_id, l); if (sm_is_err(ret)) { rv = ret; goto errcseid; } if (RT_S2M_CSEID == rt || RT_S2M_SEID == rt) { /* lookup session id to find session context */ ret = sm_pmse_find(pmss_ctx, se_id, &pmse_ctx); if (sm_is_err(ret)) { PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, sess-id=%s, sm_pmse_find=not_found\n", pmss_ctx->pmss_id, se_id)); goto errhelo; } pmse_ctx->pmse_cmd_status = 0; } if (RT_S2M_SEID == rt) { /* get next record */ ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret)) { rv = ret; goto err2; } } if (RT_S2M_NSEID == rt) { ret = sm_pmse_new(pmg_ctx, pmss_ctx, &pmse_ctx); if (sm_is_err(ret)) goto err2; strlcpy(pmse_ctx->pmse_se_id, se_id, sizeof(pmse_ctx->pmse_se_id)); ret = sm_getsmtpsid(pmse_ctx->pmse_se_id); if (ret != pmss_ctx->pmss_id) { /* XXX SMTPS id doesn't match */ rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); /* XXX use different error! */ goto errnseid; } /* XXX client IP address; must match structure later on! */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_S2M_IPV4) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errnseid; } pmse_ctx->pmse_cltipv4 = v; ++pmss_ctx->pmss_cur_session; PM_LEV_DPRINTF(3, (PM_DEBFP, "func=sm_pmss_react, id=%d, nsess-id=%s, IP=%A\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, v)); /* port */ ret = sm_rcb_get3uint32(rcb, &l, &rt, &v); if (sm_is_err(ret) || l != 4 || rt != RT_S2M_PORT) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errnseid; } /* populate SOCKADDR for call to nseid() */ ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret) || rt != RT_S2M_HOST) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errnseid; } SM_STR_FREE(pmse_ctx->pmse_arg1); ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg1, l); if (sm_is_err(ret)) goto errnseid; (void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_CONNECT); ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_CONNECT, rcb); if (sm_is_err(ret)) goto errnseid; ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errnseid; inwaitq = true; ret = sm_pmilt_nseid(pmss_ctx, pmse_ctx, rcbe); SM_STR_FREE(pmse_ctx->pmse_arg1); if (sm_is_err(ret)) { PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nsess-id=%s, sm_pmilt_nseid=%x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, ret)); goto errnseid2; } ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, evthr_en_wr=%p, done=%x\n", &pmss_ctx->pmss_com, ret)); if (sm_is_err(ret)) { PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, nsess-id=%s, sm_rcbcom_endrep=%x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id, ret)); goto errnseid2; } return PMILT_R_ASYNC; errnseid2: /* XXX this needs to undo all operations from sm_pmilt_nseid() */ errnseid: if (pmse_ctx != NULL) { (void) sm_pmse_free(pmss_ctx, pmse_ctx); pmse_ctx = NULL; } #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 4, "sev=ERROR, func=sm_pmss_react, sess_new=%d", ret); #endif /* 0 */ goto err2; } else if (RT_S2M_CSEID == rt) { ret = sm_getsmtpsid(se_id); if (ret != pmss_ctx->pmss_id) { /* XXX SMTPS id doesn't match */ rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); /* XXX use different error! */ goto errcseid; } PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, csess-id=%s\n", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id)); /* check for EOR? */ /* call milter function */ ret2 = sm_pmilt_cseid(pmss_ctx, pmse_ctx); if (sm_is_err(ret2)) goto errcseid; #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 14, "func=sm_pmss_react, id=%d, close_sess=%s", pmss_ctx->pmss_id, pmse_ctx->pmse_se_id); #endif /* 0 */ sm_pmse_free(pmss_ctx, pmse_ctx); --pmss_ctx->pmss_cur_session; /* XXX no reply??? */ goto done; errcseid: /* more cleanup? (pmse_ctx isn't allocated, don't free it!) */ #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 4, "sev=ERROR, func=sm_pmss_react, sess_close=%d", ret); #endif /* 0 */ goto err2; } else if (RT_S2M_HELO == rt || RT_S2M_EHLO == rt) { bool ehlo; ehlo = RT_S2M_EHLO == rt; ret = sm_getsmtpsid(se_id); if (ret != pmss_ctx->pmss_id) { /* XXX SMTPS id doesn't match */ rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); /* XXX use different error! */ goto errhelo; } SM_STR_FREE(pmse_ctx->pmse_arg1); ret = sm_rcb_getn0str(rcb, &pmse_ctx->pmse_arg1, l); if (sm_is_err(ret)) goto errhelo; (void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_EHLO); ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_EHLO, rcb); if (sm_is_err(ret)) goto errnseid; ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errhelo; inwaitq = true; ret2 = sm_pmilt_helo(pmss_ctx, pmse_ctx, rcbe, ehlo); SM_STR_FREE(pmse_ctx->pmse_arg1); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_helo=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2)); /* check for EOR? */ if (sm_is_err(ret2)) goto errhelo; ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); if (sm_is_err(ret)) goto errhelo; return PMILT_R_ASYNC; errhelo: goto err2; } else if (RT_S2M_NTAID == rt) { if (l != SMTP_STID_SIZE) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } /* transaction id */ ret = sm_rcb_getn(rcb, (uchar *) pmse_ctx->pmse_ta_id, l); if (sm_is_err(ret)) { rv = ret; goto errntaid; } PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id)); ret = sm_getsmtpsid(pmse_ctx->pmse_ta_id); if (ret != pmss_ctx->pmss_id) { /* XXX SMTPS id doesn't match */ rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); /* XXX use different error! */ goto errntaid; } ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret) || rt != RT_S2M_MAIL || l >= tl) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errntaid; } SM_STR_FREE(pmse_ctx->pmse_arg1); ret = sm_rcb_getn0str(rcb, &pmse_ctx->pmse_arg1, l); if (sm_is_err(ret)) goto errntaid; PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, mail=%S, l=%d\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_arg1, l)); (void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_MAIL); while (!SM_RCB_ISEOB(rcb)) { ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret)) goto errntaid; switch (rt) { case RT_S2M_ARG: if (l >= tl) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errntaid; } SM_STR_FREE(pmse_ctx->pmse_arg2); ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg2, l); if (sm_is_err(ret)) goto errntaid; PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, mail=%S, args=%S\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_arg1, pmse_ctx->pmse_arg2)); break; case RT_S2M_MACM: ret = sm_pmilt_rdmacro(pmse_ctx, PM_SMST_MAIL, rcb); if (sm_is_err(ret)) goto errntaid; break; default: /* skip over other data */ ret = sm_rcb_skip(rcb, l); if (sm_is_err(ret)) goto errntaid; break; } } ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errntaid2; inwaitq = true; ret2 = sm_pmilt_mail(pmss_ctx, pmse_ctx, rcbe); SM_STR_FREE(pmse_ctx->pmse_arg1); SM_STR_FREE(pmse_ctx->pmse_arg2); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_mail=%r\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2)); if (sm_is_err(ret2)) goto errntaid; ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); if (sm_is_err(ret)) goto errntaid2; #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_mail_pa); #endif /* 0 */ return PMILT_R_ASYNC; errntaid2: errntaid: #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 4, "sev=ERROR, func=sm_pmss_react, ta_new=%d", ret); #endif /* 0 */ SM_STR_FREE(pmse_ctx->pmse_arg1); SM_STR_FREE(pmse_ctx->pmse_arg2); goto err2; } else if (RT_S2M_RCPT_IDX == rt) { SM_STR_FREE(pmse_ctx->pmse_arg1); if (l != 4) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errrcpt; } ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) { rv = ret; goto errrcpt; } pmse_ctx->pmse_rcpt_idx = v; ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret) || rt != RT_S2M_RCPT || l > tl) { rv = sm_is_err(ret) ? ret : sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errrcpt; } ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg1, l); if (sm_is_err(ret)) goto errrcpt; (void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_RCPT); while (!SM_RCB_ISEOB(rcb)) { ret = sm_rcb_get2uint32(rcb, &l, &rt); if (sm_is_err(ret)) goto errrcpt; switch (rt) { case RT_S2M_ARG: if (l >= tl) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errrcpt; } SM_STR_FREE(pmse_ctx->pmse_arg2); ret = sm_rcb_getnstr(rcb, &pmse_ctx->pmse_arg2, l); if (sm_is_err(ret)) goto errrcpt; break; case RT_S2M_MACM: ret = sm_pmilt_rdmacro(pmse_ctx, PM_SMST_RCPT, rcb); if (sm_is_err(ret)) goto errrcpt; break; case RT_S2M_RCPT_ST: if (l != 4) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errrcpt; } ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) { rv = ret; goto errrcpt; } pmse_ctx->pmse_cmd_status = v; break; default: /* skip over other data */ ret = sm_rcb_skip(rcb, l); if (sm_is_err(ret)) goto errrcpt; break; } } ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errrcpt2; inwaitq = true; ret2 = sm_pmilt_rcpt(pmss_ctx, pmse_ctx, rcbe); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_rcpt=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2)); SM_STR_FREE(pmse_ctx->pmse_arg1); SM_STR_FREE(pmse_ctx->pmse_arg2); pmse_ctx->pmse_cmd_status = 0; if (sm_is_err(ret2)) goto errrcpt2; ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); if (sm_is_err(ret)) goto errrcpt2; #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_rcpt_pa); #endif /* 0 */ return PMILT_R_ASYNC; errrcpt2: /* ** More cleanup? See below. */ /* fall through ... */ errrcpt: SM_STR_FREE(pmse_ctx->pmse_arg1); SM_STR_FREE(pmse_ctx->pmse_arg2); #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_ERR, 4, "sev=ERROR, func=sm_pmss_react, in_ss_ta=%x, rv=%x" , ret, rv); #endif /* 0 */ goto err2; } else if (RT_S2M_DATA == rt) { if (l != 4) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errdata; } ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) { rv = ret; goto errdata; } (void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_DATA); ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_DATA, rcb); if (sm_is_err(ret)) goto errdata; ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errdata2; inwaitq = true; ret2 = sm_pmilt_data(pmss_ctx, pmse_ctx, rcbe); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_data=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2)); if (sm_is_err(ret2)) goto errdata2; ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); if (sm_is_err(ret)) goto errdata2; #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_rcpt_pa); #endif /* 0 */ return PMILT_R_ASYNC; errdata2: /* ** More cleanup? See below. */ /* fall through ... */ errdata: goto err2; } else if (RT_S2M_MSG == rt) { uchar *msg; msg = NULL; if (l >= tl) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errmsg; } msg = (uchar *) sm_malloc(l); if (NULL == msg) { rv = sm_error_temp(SM_EM_PMILTER, ENOMEM); goto errmsg; } ret = sm_rcb_getn(rcb, msg, l); if (sm_is_err(ret)) { rv = ret; goto errmsg; } /* ** usually no reply ... however: how to make this "async", ** i.e., put task back into queue? */ if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) { ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errmsg2; inwaitq = true; } ret2 = sm_pmilt_msg(pmss_ctx, pmse_ctx, msg, l, rcbe); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_msg=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2)); /* check for EOR? */ if (sm_is_err(ret2)) goto errmsg2; if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) { ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); if (sm_is_err(ret)) goto errmsg2; } #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_rcpt_pa); #endif /* 0 */ SM_FREE(msg); if (SM_IS_FLAG(pmss_ctx->pmss_pmcap, SM_SCAP_PM_MSG_RC)) return PMILT_R_ASYNC; goto done; errmsg2: /* ** More cleanup? See below. */ /* fall through ... */ errmsg: SM_FREE(msg); goto err2; } else if (RT_S2M_DOT == rt) { uchar *msg; msg = NULL; if (l >= tl) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errdot; } msg = (uchar *) sm_malloc(l); if (NULL == msg) { rv = sm_error_temp(SM_EM_PMILTER, ENOMEM); goto errdot; } ret = sm_rcb_getn(rcb, msg, l); if (sm_is_err(ret)) { rv = ret; goto errdot; } (void) sm_pmilt_freemacros(pmse_ctx, PM_SMST_DOT); ret = sm_pmilt_rdmacros(pmse_ctx, PM_SMST_DOT, rcb); if (sm_is_err(ret)) goto errdot; ret = sm_rcbcom_prerep(&pmss_ctx->pmss_com, tsk, &rcbe); if (sm_is_err(ret)) goto errdot2; inwaitq = true; ret2 = sm_pmilt_dot(pmss_ctx, pmse_ctx, msg, l, rcbe); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_dot=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2)); /* check for EOR? */ if (sm_is_err(ret2)) goto errdot2; ret = sm_rcbcom_endrep(&pmss_ctx->pmss_com, tsk, false, &rcbe); if (sm_is_err(ret)) goto errdot2; #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_rcpt_pa); #endif /* 0 */ SM_FREE(msg); return PMILT_R_ASYNC; errdot2: /* ** More cleanup? See below. */ /* fall through ... */ errdot: SM_FREE(msg); goto err2; } else if (RT_S2M_ABORT_TA == rt) { if (l != 4) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto errabortta; } ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) { rv = ret; goto errabortta; } ret2 = sm_pmilt_abort_ta(pmss_ctx, pmse_ctx); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, nta-id=%s, sm_pmilt_abort_ta=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, ret2)); /* check for EOR? */ if (sm_is_err(ret2)) goto errabortta2; #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_rcpt_pa); #endif /* 0 */ goto done; errabortta2: /* ** More cleanup? See below. */ /* fall through ... */ errabortta: goto err2; } else if (RT_S2M_MSG_RPLC_STAT == rt) { if (l != 4) { rv = sm_error_perm(SM_EM_PMILTER, SM_E_PR_ERR); goto err2; } ret = sm_rcb_getuint32(rcb, &v); if (sm_is_err(ret)) { rv = ret; goto err2; } ret2 = sm_pmilt_msg_rplc_stat(pmss_ctx, pmse_ctx, v); PM_LEV_DPRINTF(4, (PM_DEBFP, "sev=DBG, func=sm_pmss_react, id=%d, ta-id=%s, status=%#x, sm_sm_pmilt_msg_rplc_stat=%#x\n", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, v, ret2)); /* check for EOR? */ if (sm_is_err(ret2)) goto err2; #if 0 sm_log_write(pmss_ctx->pmss_pmg_ctx->pmg_lctx, QM_LCAT_SMTPS, QM_LMOD_FROM_SMTPS, SM_LOG_INFO, 10, "func=sm_pmss_react, smtps_id=%d, ss_ta=%s, from=%S", pmss_ctx->pmss_id, pmse_ctx->pmse_ta_id, pmse_ctx->pmse_rcpt_pa); #endif /* 0 */ goto done; } #if 0 else if (RT_S2M_CTAID == rt) { } #endif /* 0 */ else goto err2; done: if (!inwaitq) { ret = sm_rcb_close_dec(pmss_ctx->pmss_com.rcbcom_rdrcb); (void) sm_rcb_open_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb); } if (SM_SUCCESS == ret && inwaitq) return PMILT_R_ASYNC; return rv; /* preserve original error code! */ err2: /* use rcb functions that don't do check the state */ if (!inwaitq) (void) sm_rcb_close_decn(pmss_ctx->pmss_com.rcbcom_rdrcb); error: /* open rcb for receiving next record */ if (!inwaitq) (void) sm_rcb_open_rcvn(pmss_ctx->pmss_com.rcbcom_rdrcb); #if 0 errret: #endif /* 0 */ PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, id=%d, stat=%d, rt=%x, l=%d, v=%d, ret=%x, rv=%x, inwaitq=%d\n", pmss_ctx->pmss_id, pmss_ctx->pmss_status, rt, l, v, ret, rv, inwaitq)); if (rcbe != NULL) sm_rcbe_free(rcbe); if (inwaitq) { if (sm_is_err(rv)) /* shouldn't happen! */ PM_LEV_DPRINTF(1, (PM_DEBFP, "sev=ERROR, func=sm_pmss_react, rv=%x, inwaitq=%d\n", rv, inwaitq)); return PMILT_R_ASYNC; } return rv; } /* ** SM_SMTPS2PMILT -- PMILT - SMTPS interface ** ** Parameters: ** tsk -- evthr task ** ** Returns: ** usual sm_error code ** ** Called by: sm_pmilt_smtps() for read events */ sm_ret_T sm_smtps2pmilt(sm_evthr_task_P tsk) { int fd, r; #if PMILT_STORE_FDS int i; #endif /* PMILT_STORE_FDS */ sm_ret_T ret; pmg_ctx_P pmg_ctx; pmss_ctx_P pmss_ctx; SM_IS_EVTHR_TSK(tsk); pmss_ctx = (pmss_ctx_P) tsk->evthr_t_actx; pmg_ctx = pmss_ctx->pmss_pmg_ctx; fd = tsk->evthr_t_fd; /* checked in caller */ ret = sm_rcb_rcv(fd, pmss_ctx->pmss_com.rcbcom_rdrcb, PMSS_RC_MINSZ); PM_LEV_DPRINTF(6, (PM_DEBFP, "sev=DBG, func=sm_smtps2pmilt, fd=%d, got ret=%x, buf=%d, len=%d\n", fd, ret, pmss_ctx->pmss_com.rcbcom_rdrcb->sm_rcb_base[0], sm_rcb_getlen(pmss_ctx->pmss_com.rcbcom_rdrcb))); if (ret > 0) return EVTHR_WAITQ; else if (ret == 0) { ret = sm_rcb_close_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb); /* start appropriate function ... */ ret = sm_pmss_react(pmss_ctx, tsk); if (sm_is_err(ret)) goto termit; /* too harsh? */ else if (PMILT_R_WAITQ == ret) return EVTHR_WAITQ; else if (PMILT_R_ASYNC == ret) return EVTHR_OK; else if (EVTHR_DEL == ret) goto termit; /* terminate this SMTPS */ else return ret; } else if (SM_IO_EOF == ret) { ret = sm_rcb_close_rcv(pmss_ctx->pmss_com.rcbcom_rdrcb); termit: close(fd); r = pthread_mutex_lock(&pmg_ctx->pmg_mutex); SM_LOCK_OK(r); if (r != 0) { /* LOG */ goto error; } /* Need to abort all open sessions */ (void) sm_pmss_close(pmss_ctx); /* XXX don't crash while holding lock? */ SM_ASSERT(pmg_ctx->pmg_ssnfd > 0); /* free sss ctx */ pmg_ctx->pmg_ssnfd--; pmg_ctx->pmg_sused &= ~(pmss_ctx->pmss_bit); if (pmss_ctx->pmss_com.rcbcom_rdrcb != NULL) (void) sm_rcb_close_decn(pmss_ctx->pmss_com.rcbcom_rdrcb); r = pthread_mutex_unlock(&pmg_ctx->pmg_mutex); return EVTHR_DEL; } else { /* if (ret < 0) */ PM_LEV_DPRINTF(2, (PM_DEBFP, "sev=ERROR, func=sm_smtps2pmilt, ret=%x, errno=%d\n", ret, errno)); } PM_LEV_DPRINTF(2, (PM_DEBFP, "sev=ERROR, func=sm_smtps2pmilt, fd=%d\n", fd)); error: return EVTHR_DEL; }