/* * Copyright (c) 2002-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: smtpsh.c,v 1.69 2007/10/16 04:20:36 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/ctype.h" #include "sm/memops.h" #include "sm/io.h" #include "sm/str.h" #include "sm/string.h" #include "sm/time.h" #include "sm/limits.h" #include "sm/types.h" #include "sm/net.h" #include "statethreads/st.h" #define MTA_USE_STATETHREADS 1 #include "sm/stsock.h" #include "sm/tls.h" #include "sm/tlsbio.h" #include "sm/sasl.h" #include "sm/rfc2821.h" #include "sm/misc.h" #include "s2q.h" #include "smtps.h" #include "s2m.h" #include "smtpsrv.h" #include "smtpsh.h" #include "log.h" #if MTA_USE_PMILTER # include "pmilter.h" #endif #include "sm/sm-conf-prt.h" /* ** SSM_MAIL_NEW -- create new mail address ** ** Parameters: ** ss_ta -- SMTP server transaction context ** alloc_pa -- also create sm_str for printable address? ** pmail -- mail (sender) address (output) ** ** Returns: ** usual return code */ sm_ret_T ssm_mail_new(ss_ta_P ss_ta, bool alloc_pa, ss_mail_P *pmail) { ss_mail_P mail; SM_REQUIRE(pmail != NULL); mail = (ss_mail_P) sm_rpool_zalloc(ss_ta->ssta_rpool, sizeof(*mail)); if (NULL == mail) return sm_error_temp(SM_EM_SMTPS, ENOMEM); A2821_INIT_RP(&mail->ssm_a2821, ss_ta->ssta_rpool); if (alloc_pa) { mail->ssm_pa = sm_str_new(ss_ta->ssta_rpool, MAXADDRLEN, MAXADDRLEN); if (NULL == mail->ssm_pa) { ssm_mail_free(ss_ta, mail); return sm_error_temp(SM_EM_SMTPS, ENOMEM); } } *pmail = mail; return SM_SUCCESS; } /* ** SSM_MAIL_FREE -- free mail address ** ** Parameters: ** ss_ta -- SMTP server transaction context ** mail -- mail (sender) address ** ** Returns: ** usual return code */ sm_ret_T ssm_mail_free(ss_ta_P ss_ta, ss_mail_P mail) { if (NULL == mail) return SM_SUCCESS; a2821_free(&mail->ssm_a2821); SM_STR_FREE(mail->ssm_pa); sm_rpool_free(ss_ta->ssta_rpool, mail); return SM_SUCCESS; } /* ** SSR_RCPTS_NEW -- add a new recipient to the recipient list ** ** Parameters: ** ss_ta -- SMTP server transaction context ** rcpts -- list of recipients ** prcpt -- new recipient (output) ** ** Returns: ** usual return code */ sm_ret_T ssr_rcpts_new(ss_ta_P ss_ta, ss_rcpts_P rcpts, ss_rcpt_P *prcpt) { SM_REQUIRE(rcpts != NULL); SM_REQUIRE(prcpt != NULL); *prcpt = (ss_rcpt_P) sm_rpool_zalloc(ss_ta->ssta_rpool, sizeof(**prcpt)); if (*prcpt == NULL) goto error; A2821_INIT_RP(&((*prcpt)->ssr_a2821), ss_ta->ssta_rpool); SS_RCPTS_INSERT_TAIL(rcpts, *prcpt); (*prcpt)->ssr_idx = ss_ta->ssta_rcpts_tot++; return SM_SUCCESS; error: SM_RPOOL_FREE(ss_ta->ssta_rpool, *prcpt); return sm_error_temp(SM_EM_SMTPS, ENOMEM); } /* ** SSR_RCPT_FREE -- free a single recipient address ** ** Parameters: ** ss_ta -- SMTP server transaction context ** rcpt -- recipient to free ** ** Returns: ** usual return code */ sm_ret_T ssr_rcpt_free(ss_ta_P ss_ta, ss_rcpt_P rcpt) { if (NULL == rcpt) return SM_SUCCESS; a2821_free(&rcpt->ssr_a2821); sm_rpool_free(ss_ta->ssta_rpool, rcpt); /* remove adr from list? it's done by SS_RCPTS_REMOVE_FREE() */ /* XXX adjust some counter? */ return SM_SUCCESS; } /* ** SSR_RCPTS_FREE -- free an entire recipient list ** ** Parameters: ** ss_ta -- SMTP server transaction context ** rcpts -- recipient list to free ** ** Returns: ** usual return code */ static sm_ret_T ssr_rcpts_free(ss_ta_P ss_ta, ss_rcpts_P rcpts) { ss_rcpt_P ss_rcpt, ss_rcpt_nxt; if (SS_RCPTS_EMPTY(rcpts)) return SM_SUCCESS; for (ss_rcpt = SS_RCPTS_FIRST(rcpts); ss_rcpt != SS_RCPTS_END(rcpts); ss_rcpt = ss_rcpt_nxt) { ss_rcpt_nxt = SS_RCPTS_NEXT(ss_rcpt); /* remove adr from list? use SS_RCPTS_REMOVE_FREE()? */ ssr_rcpt_free(ss_ta, ss_rcpt); } SS_RCPTS_INIT(rcpts); return SM_SUCCESS; } #if MTA_USE_PMILTER /* ** SSR_RCPT_ADD_MOD -- add recipient modification ** ** Parameters: ** ss_sess -- SMTP server session context ** type -- add/delete ** rcpt_idx -- recipient index ** ** Returns: ** usual return code */ sm_ret_T ssr_rcpt_add_mod(ss_sess_P ss_sess, ushort type, rcpt_idx_T rcpt_idx) { sm_ret_T ret; ss_rcpt_P ss_rcpt; ss_ta_P ss_ta; ss_rcpt = NULL; ss_ta = ss_sess->ssse_ta; if (PM_RCPT_DEL == type) { ss_rcpts_P ss_rcpt_hd; /* outside allowed range? */ if (rcpt_idx >= ss_ta->ssta_rcpts_tot_orig) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_NOTICE, 10, "sev=NOTICE, func=ssr_rcpt_add_mod, delete_rcpt_idx=%u, max_rcpt_idx=%u" , rcpt_idx, ss_ta->ssta_rcpts_tot_orig); return SM_E_NOTFOUND; } ss_rcpt_hd = &ss_ta->ssta_rcpts; for (ss_rcpt = SS_RCPTS_FIRST(ss_rcpt_hd); ss_rcpt != SS_RCPTS_END(ss_rcpt_hd); ss_rcpt = SS_RCPTS_NEXT(ss_rcpt)) { if (ss_rcpt->ssr_idx == rcpt_idx) { ss_rcpt->ssr_type = type; ss_rcpt->ssr_rcode = SMTP_R_DISCARD; --ss_ta->ssta_rcpts_ok; /* ??? */ return SM_SUCCESS; } } sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_NOTICE, 10, "sev=NOTICE, func=ssr_rcpt_add_mod, delete_rcpt_idx=%u, status=not_found" , rcpt_idx); return SM_E_NOTFOUND; } ret = ssr_rcpts_new(ss_ta, &ss_ta->ssta_rcpts, &ss_rcpt); if (sm_is_err(ret)) goto error; (void) sm_postmaster_add_domain(ss_sess->ssse_rd, ss_sess->ssse_sctx->ssc_hostname, 0); ret = t2821_scan((sm_rdstr_P) ss_sess->ssse_rd, &ss_rcpt->ssr_a2821, 0); if (sm_is_err(ret)) goto error; if ((uint)ret < sm_str_getlen(ss_sess->ssse_rd) && !ISSPACE(sm_str_rd_elem(ss_sess->ssse_rd, ret))) goto error; /* check args: none for now, fixme: misleading error if no address */ if ((uint)ret < sm_str_getlen(ss_sess->ssse_rd)) goto error; /* checks should be configurable */ ret = t2821_parse(&ss_rcpt->ssr_a2821, R2821_CORRECT); if (sm_is_err(ret)) goto error; ss_rcpt->ssr_type = type; return ret; error: if (ss_rcpt != NULL) SS_RCPTS_REMOVE_FREE(ss_ta, &ss_ta->ssta_rcpts, ss_rcpt); if (sm_is_success(ret)) ret = sm_err_perm(SM_E_SYNTAX); return ret; } #endif /* MTA_USE_PMILTER */ /* ** SS_TA_CLR -- clear out a transaction context (for reuse) ** ** Parameters: ** ss_ta -- SMTP server transaction context ** ** Returns: ** usual return code */ sm_ret_T ss_ta_clr(ss_ta_P ss_ta) { SM_IS_SS_TA(ss_ta); ssm_mail_free(ss_ta, ss_ta->ssta_mail); ssr_rcpts_free(ss_ta, &ss_ta->ssta_rcpts); SM_STR_FREE(ss_ta->ssta_mail_acc.ssa_reply_text); SM_STR_FREE(ss_ta->ssta_rcpt_acc.ssa_reply_text); #if MTA_USE_PMILTER SM_STR_FREE(ss_ta->ssta_msg_acc.ssa_reply_text); SM_STR_FREE(ss_ta->ssta_mail_new); sm_hdrmodl_free(&ss_ta->ssta_hdrmodhd); SM_CSTR_FREE(ss_ta->ssta_cdb_id); sspm_clrreplies(ss_ta); #endif SM_STR_FREE(ss_ta->ssta_msgid); if (ss_ta->ssta_rpool != NULL) { sm_rpool_delete(ss_ta->ssta_rpool); ss_ta->ssta_rpool = NULL; } /* clear transaction, also resets counters */ sm_memzero(ss_ta, sizeof(*ss_ta)); SS_RCPTS_INIT(&ss_ta->ssta_rcpts); #if SS_TA_CHECK ss_ta->sm_magic = SM_SS_TA_MAGIC; #endif return SM_SUCCESS; } /* ** SS_TA_ABORT -- abort a transaction ** ** Parameters: ** ss_sess -- SMTP server session context ** ss_ta -- SMTP server transaction context ** ** Returns: ** usual return code */ sm_ret_T ss_ta_abort(ss_sess_P ss_sess, ss_ta_P ss_ta) { SM_IS_SS_TA(ss_ta); /* check existing transaction */ if (ss_ta->ssta_state != SSTA_ST_NONE) { #if MTA_USE_PMILTER if (SSTA_IS_FLAG(ss_ta, SSTA_FL_PM_CALLED) && SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE)) { sm_ret_T ret; ss_ctx_P ss_ctx; ss_ctx = ss_sess->ssse_sctx; ret = sm_s2m_abort(ss_sess, ss_ctx->ssc_s2m_ctx, ss_sess->ssse_id); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 6, "sev=ERROR, func=ss_ta_abort, ss_sess=%s, sm_s2m_abort=%m" , ss_sess->ssse_id, ret); } } #endif /* MTA_USE_PMILTER */ /* XXX ... */ /* abort all performed actions */ if (ss_ta->ssta_state >= SSTA_ST_MAIL) { (void) sm_rcb_close_decn(ss_sess->ssse_rcb); } } ss_ta_clr(ss_ta); return SM_SUCCESS; } /* ** SS_TA_INIT -- initialize transaction context ** ss_ta is NOT allocated, it is a local variable in ss_hdl_session() ** ** Parameters: ** ss_sess -- SMTP server session context ** ss_ta -- SMTP server transaction context ** ** Returns: ** usual return code */ sm_ret_T ss_ta_init(ss_sess_P ss_sess, ss_ta_P ss_ta) { sm_rpool_P rp; sm_ret_T ret; rp = NULL; ret = SM_SUCCESS; sm_memzero(ss_ta, sizeof(*ss_ta)); if (Rpools) { rp = sm_rpool_new(NULL); if (NULL == rp) { ret = sm_error_temp(SM_EM_SMTPS, ENOMEM); goto error; } ss_ta->ssta_rpool = rp; } ret = ss_id_next(ss_sess->ssse_sctx->ssc_id, ss_ta->ssta_id); if (sm_is_err(ret)) goto error; SS_RCPTS_INIT(&ss_ta->ssta_rcpts); ss_ta->ssta_state = SSTA_ST_INIT; /* inherit some flags from session; better (efficient) way?? */ if (SSSE_IS_FLAG(ss_sess, SSSE_FL_DISCARD)) SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD); if (SSSE_IS_CFLAG(ss_sess, SSSE_CFL_DELAY_CHKS)) SSTA_SET_FLAG(ss_ta, SSTA_FL_DELAY_CHKS); #if MTA_USE_PMILTER if (SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_USE)) SSTA_SET_FLAG(ss_ta, SSTA_FL_PM_USE); #endif #if SS_TA_CHECK ss_ta->sm_magic = SM_SS_TA_MAGIC; #endif return SM_SUCCESS; error: if (rp != NULL) { sm_rpool_delete(rp); ss_ta->ssta_rpool = NULL; } return ret; } /* ** SS_SESS_FREE -- free a SMTP server session context ** ** Parameters: ** ss_sess -- SMTP server session context ** ** Returns: ** usual return code */ sm_ret_T ss_sess_free(ss_sess_P ss_sess) { ss_ta_P ss_ta; if (NULL == ss_sess) return SM_SUCCESS; /* make sure ss_sess isn't referenced anymore */ if (ss_sess->ssse_sctx != NULL && ss_sess->ssse_sctx->ssc_s2q_ctx != NULL) (void) ss_clr_sess_rq(ss_sess->ssse_sctx->ssc_s2q_ctx, ss_sess); /* should this free the ss_ta inside? */ ss_ta = ss_sess->ssse_ta; if (ss_ta != NULL) (void) ss_ta_abort(ss_sess, ss_ta); if (ss_sess->ssse_fp != NULL) { sm_io_close(ss_sess->ssse_fp, SM_IO_CF_NONE); ss_sess->ssse_fp = NULL; #if MTA_USE_TLS ss_sess->ssse_fptls = NULL; (void) tlsi_free(ss_sess->ssse_tlsi); #endif } #if MTA_USE_SASL if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) { ss_ctx_P ss_ctx; ss_ctx = ss_sess->ssse_sctx; if (ss_ctx != NULL && ss_ctx->ssc_sasl_ctx != NULL && ss_sess->ssse_sasl_conn != NULL) { sasl_dispose(&ss_sess->ssse_sasl_conn); } SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK); } #endif /* MTA_USE_SASL */ SM_STR_FREE(ss_sess->ssse_rd); SM_STR_FREE(ss_sess->ssse_wr); SM_STR_FREE(ss_sess->ssse_str); SM_STR_FREE(ss_sess->ssse_helo); SM_STR_FREE(ss_sess->ssse_acc.ssa_reply_text); SM_CSTR_FREE(ss_sess->ssse_cltname); SM_RCB_FREE(ss_sess->ssse_rcb); #if MTA_USE_PMILTER /* SM_STR_FREE(ss_sess->ssse_ehlo_acc.ssa_reply_text); */ SM_STR_FREE(ss_sess->ssse_macv); # if MTA_USE_SASL SM_STR_FREE(ss_sess->ssse_sasl_mech); SM_STR_FREE(ss_sess->ssse_sasl_authen); SM_STR_FREE(ss_sess->ssse_sasl_author); # endif /* MTA_USE_SASL */ #endif /* MTA_USE_PMILTER */ if (ss_sess->ssse_cond_rd != NULL) st_cond_destroy(ss_sess->ssse_cond_rd); #if MTA_USE_TLS if (ss_sess->ssse_cnf != NULL) { sm_conf_destroy(ss_sess->ssse_cnf); ss_sess->ssse_cnf = NULL; } #endif ss_sess->ssse_sctx = NULL; #if SS_SESS_CHECK ss_sess->sm_magic = SM_MAGIC_NULL; #endif sm_free_size(ss_sess, sizeof(*ss_sess)); return SM_SUCCESS; } /* ** SS_SESS_NEW -- create a new SMTP server session context ** ** Parameters: ** ss_ctx -- SMTP server context ** pss_sess -- (pointer to) SMTP server session context (output) ** ** Returns: ** usual return code */ sm_ret_T ss_sess_new(ss_ctx_P ss_ctx, ss_sess_P *pss_sess) { sm_str_P str; sm_ret_T ret; ss_sess_P ss_sess; uint u; extern sm_cstr_P HostnameNone; SM_REQUIRE(pss_sess != NULL); ss_sess = (ss_sess_P) sm_zalloc(sizeof(*ss_sess)); if (NULL == ss_sess) goto errnomem; str = sm_str_new(NULL, SMTPBUFSIZE, SMTPMAXSIZE); if (NULL == str) goto errnomem; ss_sess->ssse_rd = str; str = sm_str_new(NULL, SMTPBUFSIZE, SMTPMAXSIZE); if (NULL == str) goto errnomem; ss_sess->ssse_wr = str; str = sm_str_new(NULL, SMTPBUFSIZE, SMTPMAXSIZE); if (NULL == str) goto errnomem; ss_sess->ssse_str = str; str = sm_str_new(NULL, 32, SMTPMAXSIZE); if (NULL == str) goto errnomem; ss_sess->ssse_helo = str; #define NO_EHLONAME "Did_not_use_EHLO/HELO" sm_str_scat(ss_sess->ssse_helo, NO_EHLONAME); str = sm_str_new(NULL, 64, SMTPMAXSIZE); if (NULL == str) goto errnomem; ss_sess->ssse_acc.ssa_reply_text = str; ss_sess->ssse_rcb = sm_rcb_new(NULL, S2Q_RCB_SIZE, QSS_RC_MAXSZ); if (NULL == ss_sess->ssse_rcb) goto errnomem; ss_sess->ssse_cond_rd = st_cond_new(); if (NULL == ss_sess->ssse_cond_rd) { ret = sm_error_temp(SM_EM_SMTPS, errno); goto error; } ss_sess->ssse_s2q_idx = SSSE_S2Q_IDX_NONE; #if SS_SESS_CHECK ss_sess->sm_magic = SM_SS_SESS_MAGIC; #endif ss_sess->ssse_cltname = SM_CSTR_DUP(HostnameNone); #if MTA_USE_TLS ss_sess->ssse_maprescnf = sm_err_perm(SM_E_NOTFOUND); ret = tlsi_new(&ss_sess->ssse_tlsi); #endif #if MTA_USE_SASL if (SSC_IS_FLAG(ss_ctx, SSC_FL_SASL_OK)) SSSE_SET_FLAG(ss_sess, SSSE_FL_SASL_OK); if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) { ret = sasl_server_new("smtp", (const char *)sm_str_getdata(ss_ctx->ssc_hostname), NULL, NULL, NULL, NULL, 0, &ss_sess->ssse_sasl_conn); if (ret != SASL_OK) { SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 9, "sev=WARN, func=ss_sess_new, sasl_server_new=%r", ret); } else { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 19, "sev=INFO, func=ss_sess_new, sasl_server_new=%p", ss_sess->ssse_sasl_conn); } } #if 0 /* security layer not yet implemented */ if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) { sasl_security_properties_t ssp; (void) memset(&ssp, '\0', sizeof ssp); ssp.max_ssf = 168; ssp.maxbufsize = MAXOUTLEN; ssp.security_flags = SASLOpts & SASL_SEC_MASK; ret = sasl_setprop(conn, SASL_SEC_PROPS, &ssp); if (ret != SASL_OK) { SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 9, "sev=WARN, func=ss_sess_new, set_sec=%r", ret); } } #endif if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) { /* ** SASL set properties for sasl ** set local/remote IP ** ** XXX where exactly are these used/required? ** Kerberos_v4 */ /* set properties */ sm_memzero(&ss_ctx->ssc_sasl_ctx->sm_sasl_ssp, sizeof(ss_ctx->ssc_sasl_ctx->sm_sasl_ssp)); ss_ctx->ssc_sasl_ctx->sm_sasl_ssp.security_flags = ss_ctx->ssc_sasl_ctx->sm_sasl_sec_flags & SASL_SEC_MAXIMUM; if (sasl_setprop(ss_sess->ssse_sasl_conn, SASL_SEC_PROPS, &ss_ctx->ssc_sasl_ctx->sm_sasl_ssp) != SASL_OK) SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK); if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) { int ext_ssf; /* ** external security strength factor; ** currently we have none so zero */ ext_ssf = 0; if (!((sasl_setprop(ss_sess->ssse_sasl_conn, SASL_SSF_EXTERNAL, &ext_ssf) == SASL_OK) && (sasl_setprop(ss_sess->ssse_sasl_conn, SASL_AUTH_EXTERNAL, NULL) == SASL_OK))) SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK); } if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) { ss_ctx->ssc_sasl_ctx->sm_sasl_n_mechs = sm_saslmechs(ss_ctx->ssc_sasl_ctx, ss_sess->ssse_sasl_conn, &ss_sess->ssse_sasl_mech_list); if (ss_ctx->ssc_sasl_ctx->sm_sasl_n_mechs <= 0) SSSE_CLR_FLAG(ss_sess, SSSE_FL_SASL_OK); } } sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 19, "sev=INFO, func=ss_sess_new, sasl_conn=%p, flags=%x", ss_sess->ssse_sasl_conn, ss_sess->ssse_flags); #endif /* MTA_USE_SASL */ /* inherit some flags from context */ ss_sess->ssse_cnf_flags = ss_ctx->ssc_cnf.ss_cnf_cflags & SSSE_CFL_INHERIT; if (!SM_IS_FLAG(ss_ctx->ssc_cnf.ss_cnf_cflags, SSC_CFL_PMILTER)) ss_sess->ssse_cnf_flags &= ~SSSE_CFL_RSAD; #if MTA_USE_TLS ss_sess->ssse_tlsreq_cnf.tlsreqcnf_viol = ss_ctx->ssc_cnf.ss_cnf_tlsreqfail; #endif for (u = 0; u < SS_MAX_COMM_SRVS; u++) ss_sess->ssse_s2q_id[u] = S2Q_ID_NONE; ss_sess->ssse_sctx = ss_ctx; ret = ss_id_next(ss_ctx->ssc_id, ss_sess->ssse_id); *pss_sess = ss_sess; return ret; errnomem: ret = sm_error_temp(SM_EM_SMTPS, ENOMEM); error: if (ss_sess != NULL) { /* free all allocated data! */ SM_STR_FREE(ss_sess->ssse_rd); SM_STR_FREE(ss_sess->ssse_wr); SM_STR_FREE(ss_sess->ssse_str); SM_STR_FREE(ss_sess->ssse_helo); SM_STR_FREE(ss_sess->ssse_acc.ssa_reply_text); SM_CSTR_FREE(ss_sess->ssse_cltname); SM_RCB_FREE(ss_sess->ssse_rcb); if (ss_sess->ssse_cond_rd != NULL) st_cond_destroy(ss_sess->ssse_cond_rd); #if MTA_USE_TLS if (ss_sess->ssse_cnf != NULL) { sm_conf_destroy(ss_sess->ssse_cnf); ss_sess->ssse_cnf = NULL; } #endif } /* complain */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERR, 1, "sev=ERROR, func=ss_sess_new, status=cannot_create_session_context"); ss_sess_free(ss_sess); *pss_sess = NULL; return ret; } /* ** SS_SESS_FEAT -- set SMTP server session context features ** ** Parameters: ** ss_sess -- SMTP server session context ** ** Returns: ** usual return code ** ** Side Effects: if a session_feature is found that matches the name ** then its options are copied into the session context. */ static sm_ret_T ss_sess_feat(ss_sess_P ss_sess, const char *feat_name, size_t feat_name_len) { uint u; ss_ctx_P ss_ctx; SM_IS_SS_SESS(ss_sess); ss_ctx = ss_sess->ssse_sctx; SM_IS_SS_CTX(ss_ctx); if (NULL == feat_name) return SM_NOMATCH; if (0 == feat_name_len) return SM_NOMATCH; if (NULL == ss_ctx->ssc_cnf.ss_cnf_se_feats.sssfs_feat) return SM_NOMATCH; for (u = 0; u < ss_ctx->ssc_cnf.ss_cnf_se_feats.sssfs_n; u++) { ss_se_feat_T ss_se_feat; ss_se_feat = ss_ctx->ssc_cnf.ss_cnf_se_feats.sssfs_feat[u]; if (ss_se_feat.sssf_name != NULL && strlen(ss_se_feat.sssf_name) == feat_name_len && sm_memeq(ss_se_feat.sssf_name, feat_name, feat_name_len)) { ss_sess->ssse_cnf_flags = ss_se_feat.sssf_flags & SSSE_CFL_INHERIT; return SM_SUCCESS; } } return SM_NOMATCH; } /* ** SS_SESS_CONF -- set SMTP server session context configuration ** ** Parameters: ** ss_sess -- SMTP server session context ** ** Returns: ** usual return code */ static sm_conf_definition_T ssse_tlsreq_defs[] = { TLSREQ_DEFS(tlsreq_cnf_T, tlsreqcnf), /* Sentinel */ { SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL SM_LC_NO_ISSET SM_LC_SET_MAGIC(0)} }; static sm_conf_definition_T ssse_mapcnf_defs[] = { { SM_CONF_DEF_MAGIC, "tls_requirements", sm_conf_type_section, offsetof(ss_sess_T, ssse_tlsreq_cnf), sizeof(tlsreq_cnf_T), NULL, SM_CONF_FLAG_KEEP_DEFAULT, ssse_tlsreq_defs, NULL, NULL, NULL SM_LC_NO_ISSET SM_LC_SET_MAGIC(0) }, { SM_CONF_DEF_MAGIC, "session_feature", sm_conf_type_string, offsetof(ss_sess_T, ssse_feat_name), 0, NULL, 0, NULL, NULL, NULL, "name of session feature" SM_LC_NO_ISSET SM_LC_SET_MAGIC(0) }, /* Sentinel */ { SM_CONF_DEF_MAGIC, NULL, 0, 0, 0, NULL, 0, NULL, NULL, NULL, NULL SM_LC_NO_ISSET SM_LC_SET_MAGIC(0)} }; sm_ret_T ss_sess_conf(ss_sess_P ss_sess) { int err; ss_ctx_P ss_ctx; #define SS_SESS_CONF "ss_session_conf" SM_IS_SS_SESS(ss_sess); ss_ctx = ss_sess->ssse_sctx; SM_IS_SS_CTX(ss_ctx); ss_sess->ssse_cnf = sm_conf_new(SS_SESS_CONF); if (NULL == ss_sess->ssse_cnf) { err = errno; sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERR, 8, "sev=ERROR, func=ss_sess_conf, sm_conf_new=NULL, errno=%d\n", err); return sm_error_temp(SM_EM_SMTPS, ENOMEM); } err = sm_conf_read_data(ss_sess->ssse_cnf, (char *)sm_str_getdata(ss_sess->ssse_str), sm_str_getlen(ss_sess->ssse_str), true); if (err != 0) { sm_prt_conferr(SS_SESS_CONF, ss_sess->ssse_cnf, err, smioerr); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERR, 8, "sev=ERROR, func=ss_sess_conf, sm_conf_read_data=%m\n", err); sm_conf_destroy(ss_sess->ssse_cnf); ss_sess->ssse_cnf = NULL; return err; } err = sm_conf_scan(ss_sess->ssse_cnf, ssse_mapcnf_defs, 0, ss_sess); if (err != 0) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERR, 8, "sev=ERROR, func=ss_sess_conf, sm_conf_scan=%m\n", err); } if (ss_ctx->ssc_cnf.ss_cnf_se_feats.sssfs_n > 0 && ss_sess->ssse_feat_name != NULL) { sm_ret_T r; r = ss_sess_feat(ss_sess, ss_sess->ssse_feat_name, strlen(ss_sess->ssse_feat_name)); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 12, "sev=DBG, func=ss_sess_conf, ss_sess=%s, session_features=%#s, ss_sess_feat=%r" , ss_sess->ssse_id, ss_sess->ssse_feat_name, r); } return err; }