/* * 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: smtpsrv.c,v 1.201 2007/11/02 04:54:59 ca Exp $") #include "sm/assert.h" #include "sm/error.h" #include "sm/ctype.h" #include "sm/memops.h" #include "sm/io.h" #include "sm/ioctl.h" #include "sm/filio.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 "sm/cdb.h" #include "statethreads/st.h" #define MTA_USE_STATETHREADS 1 #include "sm/stsock.h" #include "sm/util.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 "sm/smar.h" #include "log.h" #include "sm/resource.h" #include "sm/version.h" #if MTA_USE_PMILTER # include "pmilter.h" #endif #define SMTPS_R_IS_OK(ret) ((ret) == SM_SUCCESS) #define NO_RCPTS(ss_ta) (0 == (ss_ta)->ssta_rcpts_ok) #define SS_DATA_GOT_IT(ss_sess, ss_ta) do { \ sm_str_scopy(ss_sess->ssse_wr, "250 2.0.0 got it id="); \ sm_str_scat(ss_sess->ssse_wr, ss_ta->ssta_id); \ sm_str_scat(ss_sess->ssse_wr, "\r\n"); \ } while (0) #ifndef MTA_GREETING # define MTA_GREETING MTA_VERSION_STR #endif #define SM_DONT_LOG 99 /* value beyond "reasonable" loglevel */ /* ** SS_CONN_LOG -- log connection information ** ** Parameters: ** ss_sess -- SMTP server session context ** level -- log level to use ** ** Returns: ** SM_SUCCESS */ #define SS_CONN_LOG(ss_sess, level, where) \ do { \ if (!SSSE_IS_FLAG(ss_sess, SSSE_FL_CONN_LOGGED)) \ ss_conn_log(ss_sess, level, where); \ } while (0) static sm_ret_T ss_conn_log(ss_sess_P ss_sess, uint level, const char *where) { ss_ctx_P ss_ctx; SM_IS_SS_SESS(ss_sess); if (SSSE_IS_FLAG(ss_sess, SSSE_FL_CONN_LOGGED)) return SM_SUCCESS; ss_ctx = ss_sess->ssse_sctx; SM_IS_SS_CTX(ss_ctx); #if SS_STATS sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, level, "sev=INFO, func=%s, ss_sess=%s, client_ipv4=%A, client_name=%C, connect=%ld" , where, ss_sess->ssse_id, ss_sess->ssse_client.s_addr , ss_sess->ssse_cltname , (long) ss_sess->ssse_connect); #else /* SS_STATS */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, level, "sev=INFO, func=%s, ss_sess=%s, client_ipv4=%A, client_name=%C" , where, ss_sess->ssse_id, ss_sess->ssse_client.s_addr , ss_sess->ssse_cltname); #endif /* SS_STATS */ if (sm_log_wouldlog(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, level)) SSSE_SET_FLAG(ss_sess, SSSE_FL_CONN_LOGGED); return SM_SUCCESS; } /* ** macros for ss_crt_reply() phase */ #define SS_PHASE_OTHER 0 /* no special treatment */ #define SS_PHASE_INIT 1 /* initial 220 greeting */ #if SS_EHLO_ACCESS_CHK #define SS_PHASE_EHLO 2 /* EHLO response */ #endif #define SS_PHASE_TLS 3 /* STARTTLS response */ #define SS_PHASE_AUTH 4 /* AUTH response */ #define SS_PHASE_MAIL 5 /* MAIL response */ #define SS_PHASE_RCPT 6 /* RCPT response */ #define SS_PHASE_DATA 7 /* DATA response */ #define SS_PHASE_DOT 8 /* response to final dot */ #define SS_PHASE_QUIT 9 /* QUIT */ #define SS_PHASE_NOREPLY 10 /* no reply requested */ /* ** SS_CRT_REPLY -- create SMTP reply text based on error code. ** ToDo: add a "phase" parameter, e.g., RCPT, so REJECT can be changed to ** 550 5.1.1 User unknown ** ** Parameters: ** reply -- str for SMTP reply text (output) ** ret -- error code to transform ** phase -- SMTP phase ** new -- clear reply before writing something into it? ** ** Returns: ** usual return code ** ** Note: ** If there's no matching error text for an SMTP reply code, ** check the reply type and return a generic error text. ** This overrides the reply code... hence every function that ** generates an error should either use a defined value or its own ** error text. */ sm_ret_T ss_crt_reply(sm_str_P reply, sm_ret_T ret, int phase, bool new) { SM_REQUIRE(reply != NULL); /* use existing string if it has an SMTP reply code */ if (!new && sm_str_getlen(reply) > 3 && IS_SMTP_REPLY_STR(reply, 0)) return SM_SUCCESS; sm_str_clr(reply); switch (ret) { case SM_SUCCESS: switch (phase) { case SS_PHASE_MAIL: return sm_str_scopy(reply, "250 2.1.0 Sender ok\r\n"); case SS_PHASE_RCPT: return sm_str_scopy(reply, "250 2.1.5 Recipient ok\r\n"); default: return sm_str_scopy(reply, SS_R_OK); } #if 0 case SMTP_R_NULL_SERVER: if (ss_sess->ssse_acc.ssa_reply_text == NULL) return sm_str_scopy(ss_sess->ssse_wr, "550 5.5.0 No\r\n"); else return sm_str_cpy(ss_sess->ssse_wr, ss_sess->ssse_acc.ssa_reply_text); #endif /* 0 */ case SMTP_R_REJECT: switch (phase) { #if SS_EHLO_ACCESS_CHK case SS_PHASE_EHLO: return sm_str_scopy(reply, "550 5.7.1 Not now.\r\n"); #endif case SS_PHASE_MAIL: return sm_str_scopy(reply, "550 5.1.8 Sender rejected.\r\n"); case SS_PHASE_RCPT: return sm_str_scopy(reply, "550 5.1.1 Recipient rejected.\r\n"); default: return sm_str_scopy(reply, SS_R_REJ); } case SMTP_R_SYNTAX: switch (phase) { case SS_PHASE_MAIL: return sm_str_scopy(reply, "550 5.1.7 Syntax error.\r\n"); case SS_PHASE_RCPT: return sm_str_scopy(reply, "550 5.1.3 Syntax error.\r\n"); default: return sm_str_scopy(reply, SS_R_SYN_PAR); } case SMTP_R_TEMP: return sm_str_scopy(reply, SS_R_TMP); case SMTP_R_SSD: return sm_str_scopy(reply, SS_R_SSD); } switch (smtp_reply_type(ret)) { case SMTP_RTYPE_OK: return sm_str_scopy(reply, SS_R_OK); case SMTP_RTYPE_CONT: return sm_str_scopy(reply, SS_R_CONT); case SMTP_RTYPE_TEMP: return sm_str_scopy(reply, SS_R_TMP); case SMTP_RTYPE_PERM: return sm_str_scopy(reply, SS_R_REJ); } return sm_str_scopy(reply, SS_R_OK); } /* ** SS_REPLY -- send an SMTP reply ** ** Parameters: ** ss_sess -- SMTPS session ** str -- text to send ** fp -- file to use ** loglevel -- use for logging ** flushit -- invoke flush()? ** ** Returns: ** usual return code */ /* read/write error */ #define SMTP_RD_ERR sm_error_temp(SM_EM_SMTPS, SM_E_RD) #define SMTP_WR_ERR sm_error_temp(SM_EM_SMTPS, SM_E_WR) static sm_ret_T ss_reply(ss_sess_P ss_sess, sm_str_P str, sm_file_T *fp, int loglevel, bool flushit) { ssize_t r; sm_ret_T ret; if (ss_sess->ssse_sctx->ssc_cnf.ss_cnf_debug > 3) { sm_io_fprintf(smioerr, "send: "); sm_io_write(smioerr, sm_str_getdata(str), sm_str_getlen(str), &r); sm_io_flush(smioerr); } /* softbounce? replace "5xy 5.a.b" -> "4xy 4.a.b" */ if (SSC_IS_CFLAG(ss_sess->ssse_sctx, SSC_CFL_SOFTBOUNCE) && sm_str_getlen(str) > 3 && sm_str_rd_elem(str, 0) == '5') { sm_str_wr_elem(str, 0, (uchar) '4'); if (sm_str_getlen(str) > 10 && sm_str_rd_elem(str, 4) == '5') sm_str_wr_elem(str, 4, (uchar) '4'); } ret = sm_io_write(fp, sm_str_getdata(str), sm_str_getlen(str), &r); if (r != sm_str_getlen(str)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, (loglevel >= 0) ? loglevel : 5, "sev=WARN, func=ss_reply, ss_sess=%s, write=error, n=%d, r=%d, ret=%m" , ss_sess->ssse_id, sm_str_getlen(str), (int) r, ret); if (sm_is_err(ret)) return ret; return SMTP_WR_ERR; } if (flushit) { ret = sm_io_flush(fp); if (sm_is_err(ret)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, (loglevel >= 0) ? loglevel : 5, "sev=WARN, func=ss_reply, ss_sess=%s, flush=%m" , ss_sess->ssse_id, ret); return ret; } } return SMTP_OK; } /* ** SS_READ_CMD -- read an SMTP command (into ssse_rd, \r\n not in ssse_rd) ** ** Parameters: ** ss_sess -- SMTPS session ** ** Returns: ** usual return code */ static sm_ret_T ss_read_cmd(ss_sess_P ss_sess) { ssize_t r; sm_ret_T ret; sm_str_clr(ss_sess->ssse_rd); #if 0 /* Switch to read mode? Not really necessary */ sm_iotord(ss_sess->ssse_fp); #endif errno = 0; ret = sm_fgetline0(ss_sess->ssse_fp, ss_sess->ssse_rd); if (ss_sess->ssse_sctx->ssc_cnf.ss_cnf_debug > 3) { sm_io_fprintf(smioerr, "rcvd [len=%d, ret=%r]: " , sm_str_getlen(ss_sess->ssse_rd), ret); sm_io_write(smioerr, sm_str_getdata(ss_sess->ssse_rd), sm_str_getlen(ss_sess->ssse_rd), &r); sm_io_flush(smioerr); } if (SM_IO_EOF == ret) return ret; if (sm_is_err(ret)) { if (ss_sess->ssse_sctx->ssc_cnf.ss_cnf_debug > 1) sm_io_fprintf(smioerr, "sev=DEBUG, func=ss_read_cmd, read=error, ret=%r\n", ret); return ret; } return SMTP_OK; } /* ** SS_NOPCMD -- Some "nop" cmd was entered: check whether counters are exceeded ** ** Parameters: ** ss_sess -- SMTPS session ** checkonly -- do not increase counters, only check them ** reply -- default reply ** ** Returns: ** usual return code */ static sm_ret_T ss_nopcmd(ss_sess_P ss_sess, bool checkonly, const char *reply) { ss_ta_P ss_ta; ss_ctx_P ss_ctx; bool toomany; SM_IS_SS_SESS(ss_sess); ss_ctx = ss_sess->ssse_sctx; SM_IS_SS_CTX(ss_ctx); ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); if (!checkonly) ++ss_ta->ssta_nopcmds; toomany = ss_ta->ssta_nopcmds > ss_ctx->ssc_cnf.ss_cnf_ta_max_nopcmds; if (!toomany && !SSTA_IS_ACTIVE(ss_ta)) { if (!checkonly) ++ss_sess->ssse_nopcmds; toomany = ss_sess->ssse_nopcmds > ss_ctx->ssc_cnf.ss_cnf_sess_max_nopcmds; } if (toomany) { sm_str_scopy(ss_sess->ssse_wr, "421 4.7.0 Too many useless commands.\r\n"); ss_sess->ssse_state = SSSE_ST_QUIT; return sm_error_temp(SM_EM_SMTPS, EPERM); } sm_str_scopy(ss_sess->ssse_wr, reply); return SM_SUCCESS; } /* ** SS_BADCMD -- Some "bad" cmd was entered: check whether counters are exceeded ** ** Parameters: ** ss_sess -- SMTPS session ** checkonly -- do not increase counters, only check them ** reply -- default reply ** ** Returns: ** usual return code */ static sm_ret_T ss_badcmd(ss_sess_P ss_sess, bool checkonly, const char *reply) { ss_ta_P ss_ta; ss_ctx_P ss_ctx; bool toomany; SM_IS_SS_SESS(ss_sess); ss_ctx = ss_sess->ssse_sctx; SM_IS_SS_CTX(ss_ctx); ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); if (SSTA_IS_ACTIVE(ss_ta)) { if (!checkonly) ++ss_ta->ssta_badcmds; toomany = ss_ta->ssta_badcmds > ss_ctx->ssc_cnf.ss_cnf_ta_max_badcmds; } else { if (!checkonly) ++ss_sess->ssse_badcmds; toomany = ss_sess->ssse_badcmds > ss_ctx->ssc_cnf.ss_cnf_sess_max_badcmds; } if (toomany) { sm_str_scopy(ss_sess->ssse_wr, "421 4.7.0 Too many bad commands.\r\n"); ss_sess->ssse_state = SSSE_ST_QUIT; return sm_error_temp(SM_EM_SMTPS, EPERM); } if (reply != NULL) sm_str_scopy(ss_sess->ssse_wr, reply); return SM_SUCCESS; } /* ** SS_INVALIDADDR -- invalid address: check whether counters are exceeded ** ** Parameters: ** ss_sess -- SMTPS session ** ** Returns: ** SUCCESS or SSD */ static sm_ret_T ss_invalidaddr(ss_sess_P ss_sess) { ss_ta_P ss_ta; ss_ctx_P ss_ctx; bool toomany; SM_IS_SS_SESS(ss_sess); ss_ctx = ss_sess->ssse_sctx; SM_IS_SS_CTX(ss_ctx); ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); toomany = false; if (SSTA_IS_ACTIVE(ss_ta)) { ++ss_ta->ssta_invldaddr; toomany = ss_ta->ssta_invldaddr > ss_ctx->ssc_cnf.ss_cnf_ta_max_invldaddr; } ++ss_sess->ssse_invldaddr; if (!toomany) toomany = ss_sess->ssse_invldaddr > ss_ctx->ssc_cnf.ss_cnf_sess_max_invldaddr; if (!toomany) return SM_SUCCESS; sm_str_scopy(ss_sess->ssse_wr, "421 4.7.0 Too many invalid addresses.\r\n"); ss_sess->ssse_state = SSSE_ST_QUIT; return SMTP_R_SSD; } /* ** SS_RSET -- RSET command ** ** Parameters: ** ss_sess -- SMTPS session ** ** Returns: ** usual return code */ static sm_ret_T ss_rset(ss_sess_P ss_sess) { sm_ret_T ret; ss_ta_P ss_ta; SM_IS_SS_SESS(ss_sess); ss_ta = ss_sess->ssse_ta; if (!SSTA_IS_ACTIVE(ss_ta)) { ret = ss_nopcmd(ss_sess, false, SS_R_OK); if (sm_is_err(ret)) return ret; } ret = ss_ta_abort(ss_sess, ss_ta); ss_ta->ssta_state = SSTA_ST_NONE; if (sm_is_err(ret)) return ret; /* error message alread in ssse_wr */ return sm_str_scopy(ss_sess->ssse_wr, SS_R_OK); } /* ** SS_EHLO -- EHLO/HELO command ** ** Parameters: ** ss_sess -- SMTPS session ** ehlo -- client sent ehlo? ** ** Returns: ** usual return code */ static sm_ret_T ss_ehlo(ss_sess_P ss_sess, bool ehlo) { size_t l, i; ss_ta_P ss_ta; sm_ret_T ret; ulong max_sz_b; #if SS_EHLO_ACCESS_CHK ss_ctx_P ss_ctx; #endif SM_IS_SS_SESS(ss_sess); ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); ret = ss_ta_abort(ss_sess, ss_ta); if (sm_is_err(ret)) return ret; /* error message already in ssse_wr */ #if SS_EHLO_ACCESS_CHK ss_ctx = ss_sess->ssse_sctx; #endif if (!ehlo) SSSE_SET_FLAG(ss_sess, SSSE_FL_HELO); else SSSE_CLR_FLAG(ss_sess, SSSE_FL_HELO); /* need to copy HELO parameter here */ sm_str_clr(ss_sess->ssse_helo); l = sm_str_getlen(ss_sess->ssse_rd); if (l < 5 || !ISSPACE(sm_str_rd_elem(ss_sess->ssse_rd, 4))) goto syntaxerror; i = 5; /* RFC 2821: only one space... */ while (i < l && ISSPACE(sm_str_rd_elem(ss_sess->ssse_rd, i))) ++i; if (i >= l) goto syntaxerror; /* RFC 2821: parameter must be Domain, see syntax in sm/rfc2821.h */ while (i < l && sm_str_rd_elem(ss_sess->ssse_rd, i) != '\0' && !ISSPACE(sm_str_rd_elem(ss_sess->ssse_rd, i)) && ISPRINT(sm_str_rd_elem(ss_sess->ssse_rd, i))) { /* further syntax check? valid char? */ if (sm_is_err(sm_str_put(ss_sess->ssse_helo, sm_str_rd_elem(ss_sess->ssse_rd, i)))) goto error; ++i; } if (i > l || (i < l && sm_str_rd_elem(ss_sess->ssse_rd, i) != '\0')) goto syntaxerror; if (sm_is_err(sm_str_term(ss_sess->ssse_helo))) goto error; if (SSSE_IS_CFLAG(ss_sess, SSSE_CFL_EHLO_SYNTAX)) { if (!validdomain(ss_sess->ssse_helo, R2821_NO_TRLDOT)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_NOTICE, 10, "sev=NOTICE, func=ss_ehlo, ss_sess=%s, %s=%@S, stat=%d, status=Invalid_domain" , ss_sess->ssse_id, ehlo ? "ehlo" : "helo", ss_sess->ssse_helo, 501); goto syntaxerror; } } if (sm_io_getinfo(ss_sess->ssse_fp, SM_IO_IS_READABLE, NULL)) { sm_str_scopy(ss_sess->ssse_wr, "421 Illegal Pipelining detected\r\n"); return sm_error_temp(SM_EM_SMTPS, SM_E_ILL_PIPE); } #if SS_EHLO_ACCESS_CHK if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_ACCESS_DB) && SSSE_IS_CFLAG(ss_sess, SSSE_CFL_CHK_EHLO) && !SSSE_ARE_FLAGS(ss_sess, SSSE_FL_QUICK|SSSE_FL_CLIENT_RELAY) && !SSSE_ARE_FLAGS(ss_sess, SSSE_FL_QUICK|SSSE_FL_CLIENT_OK) ) { ret = sm_s2a_str(ss_sess, ss_ctx->ssc_s2a_ctx, ss_sess->ssse_id, ss_sess->ssse_helo, RT_S2A_EHLO, SMARA_LT_EHLO_ACC, SMARA_LFL_STR); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 7, "sev=ERROR, func=ss_ehlo, ss_sess=%s, sm_s2a_str=%r" , ss_sess->ssse_id, ret); goto error; } else /* not really required because of goto above */ ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4a2s, ss_ctx->ssc_s2a_ctx); 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_ehlo, ss_sess=%s, func=ss_ehlo, where=smar_check, sm_w4q2s_reply=%r" , ss_sess->ssse_id, ret); goto error; } else if (SMAR_RISQUICK(ret)) { /* SSTA_SET_FLAG(ss_ta, SSTA_FL_EHLO_QCK); */ SMAR_RCLRQUICK(ret); } else if (SSTA_IS_FLAG(ss_ta, SSTA_FL_DELAY_CHKS) && IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) { sm_str_P str; sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 12, "sev=DEBUG, ss_sess=%s, func=ss_ehlo, where=smar_check, sm_w4q2s_reply=%r, reply=%r, text=%@T" , ss_sess->ssse_id, ret , ss_ta->ssta_ehlo_acc.ssa_reply_code , ss_sess->ssse_wr); /* delay rejection */ str = sm_str_dup(NULL, ss_sess->ssse_wr); if (str != NULL) { ss_ta->ssta_ehlo_acc.ssa_reply_text = str; sm_str_clr(ss_sess->ssse_wr); ret = SMTP_R_OK; } else { /* can't accept transaction: ENOMEM */ ret = SMTP_R_SSD; (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_EHLO, true); sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 4, "sev=ERROR, ss_sess=%s, ss_ta=%s, func=ss_ehlo, sm_str_dup=ENOMEM" , ss_sess->ssse_id, ss_ta->ssta_id); } } if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) { int rc; sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 10, "sev=INFO, ss_sess=%s, ehlo=%@N, stat=%r, text=%@T" , ss_sess->ssse_id, ss_sess->ssse_helo, ret, ss_sess->ssse_wr); if (!SMTP_REPLY_MATCHES_RCODE(ss_sess->ssse_wr, ret, 0, rc)) (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_MAIL, true); return SM_SUCCESS; /* to avoid abort in session loop */ } else { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 13, "sev=INFO, ss_sess=%s, ss_ta=%s, func=ss_ehlo, where=smar_check, sm_w4q2s_reply=%r" , ss_sess->ssse_id , ss_ta->ssta_id, ret); if (SMTP_R_DISCARD == ret) { SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD); ret = SM_SUCCESS; /* "normalize" */ } } } #endif /* SS_EHLO_ACCESS_CHK */ /* refuse hostname as EHLO parameter unless localhost */ if (ss_sess->ssse_client.s_addr != ntohl(INADDR_LOOPBACK) && !SSSE_ARE_FLAGS(ss_sess, SSSE_FL_QUICK|SSSE_FL_CLIENT_RELAY) && !SSSE_ARE_FLAGS(ss_sess, SSSE_FL_QUICK|SSSE_FL_CLIENT_OK) && sm_str_getlen(ss_sess->ssse_helo) == sm_str_getlen(ss_sess->ssse_sctx->ssc_hostname) && strncasecmp((char *) sm_str_data(ss_sess->ssse_helo), (char *) sm_str_data(ss_sess->ssse_sctx->ssc_hostname), sm_str_getlen(ss_sess->ssse_helo)) == 0) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_NOTICE, 9, "sev=NOTICE, func=ss_ehlo, ss_sess=%s, %s=%@S, stat=%d, status=Identity_Theft" , ss_sess->ssse_id, ehlo ? "ehlo" : "helo", ss_sess->ssse_helo, 550); sm_str_scopy(ss_sess->ssse_wr, "550 Identity Theft!\r\n"); return SM_SUCCESS; /* to avoid abort in session loop */ } #if MTA_USE_PMILTER ret = sspm_helo(ss_sess, ehlo); if (sm_is_err(ret)) goto error; if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) return ret; /* goto some-error?? */ /* currently no handling of delay_checks here! */ #endif if (SSSE_IS_FLAG(ss_sess, SSSE_FL_NULL) || !ehlo) { if (sm_is_err(sm_str_scopy(ss_sess->ssse_wr, "250 ")) || sm_is_err(sm_str_cat(ss_sess->ssse_wr, ss_sess->ssse_sctx->ssc_hostname)) || sm_is_err(sm_str_scat(ss_sess->ssse_wr, " Hi there\r\n"))) goto error; } else { if (sm_is_err(sm_str_scopy(ss_sess->ssse_wr, "250-")) || sm_is_err(sm_str_cat(ss_sess->ssse_wr, ss_sess->ssse_sctx->ssc_hostname)) || sm_is_err(sm_str_scat(ss_sess->ssse_wr, " ESMTP Hi there\r\n250-PIPELINING\r\n"))) goto error; if (SSC_IS_FLAG(ss_sess->ssse_sctx, SSC_FL_TLS_OK) && SSSE_IS_CFLAG(ss_sess, SSSE_CFL_STARTTLS) && !SSSE_IS_FLAG(ss_sess, SSSE_FL_STARTTLS) && sm_is_err(sm_str_scat(ss_sess->ssse_wr, "250-STARTTLS\r\n"))) goto error; #if MTA_USE_SASL if (SSC_IS_FLAG(ss_sess->ssse_sctx, SSC_FL_SASL_OK) && SSSE_IS_CFLAG(ss_sess, SSSE_CFL_AUTH) && !SSSE_IS_FLAG(ss_sess, SSSE_FL_AUTH) && ss_sess->ssse_sasl_mech_list != NULL && sm_is_err(sm_strprintf(ss_sess->ssse_wr, "250-AUTH %s\r\n", ss_sess->ssse_sasl_mech_list))) goto error; #endif /* MTA_USE_SASL */ max_sz_b = ss_sess->ssse_sctx->ssc_cnf.ss_cnf_max_msg_sz_kb * ONEKB; if (max_sz_b > 0 && sm_strprintf(ss_sess->ssse_wr, "250-SIZE %lu\r\n", max_sz_b) <= 8) goto error; if (SSC_IS_CFLAG(ss_sess->ssse_sctx, SSC_CFL_8BITMIME) && sm_is_err(sm_str_scat(ss_sess->ssse_wr, "250-8BITMIME\r\n"))) goto error; if (sm_is_err(sm_str_scat(ss_sess->ssse_wr, "250-ENHANCEDSTATUSCODES\r\n"))) goto error; if (SSC_IS_CFLAG(ss_sess->ssse_sctx, SSC_CFL_XVERP) && sm_is_err(sm_str_scat(ss_sess->ssse_wr, "250-XVERP\r\n"))) goto error; #if MTA_USE_PMILTER && MTA_USE_RSAD if (SSSE_IS_CFLAG(ss_sess, SSSE_CFL_RSAD) && sm_is_err(sm_str_scat(ss_sess->ssse_wr, "250-PRDR\r\n"))) goto error; #endif if (sm_is_err(sm_str_scat(ss_sess->ssse_wr, "250 HELP\r\n"))) goto error; } ss_sess->ssse_state = ehlo ? SSSE_ST_EHLO : SSSE_ST_HELO; return SM_SUCCESS; error: sm_str_scopy(ss_sess->ssse_wr, SS_R_SSD); return sm_error_temp(SM_EM_SMTPS, ENOMEM); syntaxerror: ret = ss_badcmd(ss_sess, false, "501 Syntax error.\r\n"); return ret; /* to avoid abort in session loop */ } /* check for null server and return proper error code */ /* ss_sess->ssse_acc.ssa_reply_text shouldn't be NULL */ #define SMTPS_NULL_SERVER \ do { \ if (SSSE_IS_FLAG(ss_sess, SSSE_FL_NULL)) \ return (ss_sess->ssse_acc.ssa_reply_text == NULL || \ sm_str_getlen(ss_sess->ssse_acc.ssa_reply_text)\ <= 4) \ ? sm_str_scopy(ss_sess->ssse_wr, \ "550 5.7.1 Access denied\r\n") \ : sm_str_cpy(ss_sess->ssse_wr, \ ss_sess->ssse_acc.ssa_reply_text); \ } while (0) /* hack: caller should send no reply; FIX THIS: return value semantics! */ #define SS_NO_REPLY 1 #if MTA_USE_TLS /* ** SS_TLSREQ -- Check STARTTLS requirements ** ** Parameters: ** ss_sess -- SMTPS session ** rcode -- reply code to use in case of an error ** ** Returns: ** usual return code */ static sm_ret_T ss_tlsreq(ss_sess_P ss_sess, sm_ret_T rcode) { sm_ret_T ret; uint u; const char *cstr; sm_str_P str; SM_IS_SS_SESS(ss_sess); ret = SM_SUCCESS; if (SM_IS_FLAG(ss_sess->ssse_tlsreq_cnf.tlsreqcnf_flags, TLSREQ_FL_VRFD) && TLS_VRFY_OK != ss_sess->ssse_tlsi->tlsi_vrfy) return rcode; u = ss_sess->ssse_tlsreq_cnf.tlsreqcnf_min_cipher_bits; if (SM_IS_FLAG(ss_sess->ssse_tlsreq_cnf.tlsreqcnf_flags, TLSREQ_FL_ENCR) && 0 == u) return rcode; if (u > 0 && u > ss_sess->ssse_tlsi->tlsi_cipher_bits) return rcode; if ((cstr = ss_sess->ssse_tlsreq_cnf.tlsreqcnf_cert_subject) != NULL && ((str = ss_sess->ssse_tlsi->tlsi_cert_subject) == NULL || strcasecmp(cstr, sm_str_getdata(str)) != 0)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 9, "sev=INFO, func=ss_tlsreq, cert_subject=%#S, required=%s" , str, cstr); return rcode; } if ((cstr = ss_sess->ssse_tlsreq_cnf.tlsreqcnf_cert_issuer) != NULL && ((str = ss_sess->ssse_tlsi->tlsi_cert_issuer) == NULL || strcasecmp(cstr, sm_str_getdata(str)) != 0)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 9, "sev=INFO, func=ss_tlsreq, cert_issuer=%#S, required=%s" , str, cstr); return rcode; } if ((cstr = ss_sess->ssse_tlsreq_cnf.tlsreqcnf_common_name) != NULL && ((str = ss_sess->ssse_tlsi->tlsi_cn_subject) == NULL || strcasecmp(cstr, sm_str_getdata(str)) != 0)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 9, "sev=INFO, func=ss_tlsreq, common_name=%#S, required=%s" , str, cstr); return rcode; } return ret; } /* ** SS_TLS -- STARTTLS command ** ** Parameters: ** ss_sess -- SMTPS session ** ** Returns: ** usual return code */ static sm_ret_T ss_tls(ss_sess_P ss_sess) { sm_ret_T ret; ssize_t written; int r; ss_ta_P ss_ta; ss_ctx_P ss_ctx; /* check handling of error cases; see comments below */ SM_IS_SS_SESS(ss_sess); SMTPS_NULL_SERVER; ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); /* is this check ok? should we just reset the transaction? */ if (ss_ta->ssta_state != SSTA_ST_NONE) return sm_str_scopy(ss_sess->ssse_wr, SS_R_NOTNOW); ss_ctx = ss_sess->ssse_sctx; if (!SSC_IS_FLAG(ss_ctx, SSC_FL_TLS_OK) || SSSE_IS_FLAG(ss_sess, SSSE_FL_STARTTLS) || !SSSE_IS_CFLAG(ss_sess, SSSE_CFL_STARTTLS)) return sm_str_scopy(ss_sess->ssse_wr, SS_R_NOTNOW); /* ** Set verification globally (per SSL_CTX) ** Last parameter: request a client cert -- should be an option ** (globally, per client). */ SSSE_SET_FLAG(ss_sess, SSSE_FL_STARTTLS); ss_sess->ssse_con = SSL_new(ss_ctx->ssc_ssl_ctx); if (NULL == ss_sess->ssse_con) goto notls; tls_set_verify(ss_ctx->ssc_ssl_ctx, ss_sess->ssse_con, true); ret = sm_str_scopy(ss_sess->ssse_wr, "220 2.0.0 try it\r\n"); if (sm_is_err(ret)) goto fail; ret = ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, false); sm_str_clr(ss_sess->ssse_wr); if (ret != SMTP_OK) { SS_CONN_LOG(ss_sess, 2, "ss_tls"); sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_tls, ss_sess=%s, where=try_starttls, send=%m" , ss_sess->ssse_id, ret); goto fail; } /* maybe do this before replying in case this fails?? */ ret = tls_open(ss_sess->ssse_fp, ss_sess->ssse_con, ss_sess->ssse_sctx->ssc_tlsl_ctx, &ss_sess->ssse_fptls); if (sm_is_err(ret)) { SS_CONN_LOG(ss_sess, 8, "ss_tls"); sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_tls, ss_sess=%s, where=connection, tls_open=%m" , ss_sess->ssse_id, ret); goto fail; } SSL_set_accept_state(ss_sess->ssse_con); ret = do_tls_operation(ss_sess->ssse_fptls, SSL_accept, NULL, NULL, NULL, 0, &written); if (sm_is_err(ret)) { SS_CONN_LOG(ss_sess, 8, "ss_tls"); sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_tls, ss_sess=%s, where=connection, starttls=%m" , ss_sess->ssse_id, ret); goto closetls; } r = 1; ret = sm_io_setinfo(ss_sess->ssse_fptls, SM_IO_DOUBLE, &r); if (sm_is_err(ret)) goto closetls; (void) tls_get_info(ss_ctx->ssc_tlsl_ctx, ss_sess->ssse_con, TLS_F_SRV|TLS_F_CERT_REQ, inet_ntoa(ss_sess->ssse_client), ss_sess->ssse_tlsi); SS_CONN_LOG(ss_sess, 9, "ss_tls"); sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 9, "sev=INFO, func=ss_tls, ss_sess=%s, where=connection, starttls=successful, cipher=%S, bits=%d/%d, verify=%s" , ss_sess->ssse_id , ss_sess->ssse_tlsi->tlsi_cipher , ss_sess->ssse_tlsi->tlsi_cipher_bits , ss_sess->ssse_tlsi->tlsi_algs_bits , tls_vrfy2txt(ss_sess->ssse_tlsi->tlsi_vrfy) ); if (TLS_VRFY_OK == ss_sess->ssse_tlsi->tlsi_vrfy && SSC_IS_CFLAG(ss_ctx, SSC_CFL_TLS_REL_VRFY)) SSSE_SET_FLAG(ss_sess, SSSE_FL_CLIENT_RELAY); else if (TLS_VRFY_OK == ss_sess->ssse_tlsi->tlsi_vrfy && SSC_IS_CFLAG(ss_ctx, SSC_CFL_ACCESS_DB) && SSC_IS_CFLAG(ss_ctx, SSC_CFL_TLS_REL_ACC)) { /* check access map... */ ret = sm_s2a_tls(ss_sess, ss_ctx->ssc_s2a_ctx); if (sm_is_err(ret)) { SS_CONN_LOG(ss_sess, 7, "ss_tls"); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 7, "sev=ERROR, func=ss_tls, ss_sess=%s, sm_s2a_tls=%m" , ss_sess->ssse_id, ret); goto closetls; } else { /* not really required because of goto above */ ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4a2s, ss_ctx->ssc_s2a_ctx); } if (sm_is_err(ret)) { SS_CONN_LOG(ss_sess, 6, "ss_tls"); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 6, "sev=ERROR, func=ss_tls, ss_sess=%s, sm_w4q2s_reply=%m" , ss_sess->ssse_id, ret); goto closetls; } /* common code: see smtps.c */ else if (SMAR_RISQUICK(ret)) { SSSE_SET_FLAG(ss_sess, SSSE_FL_QUICK); SMAR_RCLRQUICK(ret); } if (SMTP_R_RELAY == ret) { SSSE_SET_FLAG(ss_sess, SSSE_FL_CLIENT_RELAY); ret = SM_SUCCESS; /* "normalize" */ } else if (SMTP_R_DISCARD == ret) { SSSE_SET_FLAG(ss_sess, SSSE_FL_DISCARD); ret = SM_SUCCESS; /* "normalize" */ } else if (SMTP_R_OK == ret) { SSSE_SET_FLAG(ss_sess, SSSE_FL_CLIENT_OK); ret = SM_SUCCESS; /* "normalize" */ } else if (SMTP_R_CONT == ret) { ret = SM_SUCCESS; /* "normalize" */ } /* end common code */ /* reject/accept TLS??? there is no further reply after the handshake */ if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) { SS_CONN_LOG(ss_sess, 12, "ss_tls"); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 12, "sev=DBG, func=ss_tls, ss_sess=%s, sm_w4q2s_reply=%m, reply=%r, text=%@T" , ss_sess->ssse_id, ret , ss_sess->ssse_acc.ssa_reply_code , ss_sess->ssse_wr); #if 0 /* copy error text (if valid) */ if (sm_str_getlen(ss_sess->ssse_wr) > 4 && sm_str_cpy(ss_sess->ssse_acc.ssa_reply_text, ss_sess->ssse_wr) == SM_SUCCESS) { /* delay rejection? */ if (SSSE_IS_CFLAG(ss_sess, SSSE_CFL_DELAY_CHKS)) { sm_str_clr(ss_sess->ssse_wr); ret = SMTP_R_OK; } } else { /* ** Can't accept session; complain?? ** Decrease concurrency? */ ret = SMTP_R_SSD; sm_str_clr(ss_sess->ssse_wr); sm_str_clr(ss_sess->ssse_acc.ssa_reply_text); } #endif /* 0 */ } } /* HACK */ ss_sess->ssse_fp = ss_sess->ssse_fptls; SSSE_SET_FLAG(ss_sess, SSSE_FL_STARTTLS); #if MTA_USE_SASL if (SSSE_IS_FLAG(ss_sess, SSSE_FL_SASL_OK)) { /* r = cipher_bits; see above! */ ss_sess->ssse_sasl_ext_ssf = ss_sess->ssse_tlsi->tlsi_cipher_bits; ret = sasl_setprop(ss_sess->ssse_sasl_conn, SASL_SSF_EXTERNAL, &r); SS_CONN_LOG(ss_sess, 9, "ss_tls"); sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 9, "sev=INFO, func=ss_tls, ss_sess=%s, set_ssf=%d" , ss_sess->ssse_id, ret); 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); } #endif /* MTA_USE_SASL */ return SS_NO_REPLY; notls: ret = sm_str_scopy(ss_sess->ssse_wr, "454 4.3.3 Nope\r\n"); return SM_SUCCESS; closetls: /* shut down */ if (ss_sess->ssse_fptls != NULL) { sm_io_close(ss_sess->ssse_fptls, SM_IO_CF_NONE); ss_sess->ssse_fptls = NULL; ss_sess->ssse_fp = NULL; } fail: sm_str_clr(ss_sess->ssse_wr); return ret; } #endif /* MTA_USE_TLS */ #if MTA_USE_SASL /* ** RESET_SASLCONN -- reset SASL connection data ** ** Parameters: ** sasl_conn -- SASL connection context ** hostname -- host name ** various connection data ** ** Returns: ** SASL result */ static int reset_saslconn(sasl_conn_t **sasl_conn, char *hostname, char *remoteip, char *localip, char *auth_id, sasl_ssf_t *ext_ssf) { int ret; sasl_dispose(sasl_conn); ret = sasl_server_new("smtp", hostname, NULL, NULL, NULL, NULL, 0, sasl_conn); if (ret != SASL_OK) return ret; # if NETINET || NETINET6 if (remoteip != NULL) { ret = sasl_setprop(*sasl_conn, SASL_IPREMOTEPORT, remoteip); if (ret != SASL_OK) return ret; } if (localip != NULL) { ret = sasl_setprop(*sasl_conn, SASL_IPLOCALPORT, localip); if (ret != SASL_OK) return ret; } # endif /* NETINET || NETINET6 */ ret = sasl_setprop(*sasl_conn, SASL_SSF_EXTERNAL, ext_ssf); if (ret != SASL_OK) return ret; ret = sasl_setprop(*sasl_conn, SASL_AUTH_EXTERNAL, auth_id); if (ret != SASL_OK) return ret; return SASL_OK; } /* ** SS_AUTH -- AUTH command ** ** Parameters: ** ss_sess -- SMTPS session ** ** Returns: ** usual return code */ #define RESET_SASLCONN do { \ int r; \ ss_sess->ssse_sasl_state = SASL_NOT_AUTH; \ if ((r = reset_saslconn(&ss_sess->ssse_sasl_conn, \ (char *)sm_str_getdata(ss_ctx->ssc_hostname), NULL, NULL, NULL, \ (sasl_ssf_t *)&ss_sess->ssse_sasl_ext_ssf)) != 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_auth, reset_saslconn=%d", \ r); \ } \ } while (0) static sm_ret_T ss_auth(ss_sess_P ss_sess) { sm_ret_T ret, args; uint i, cltinlen, more; uint argv[SS_MAX_ARGS]; uint challengelen, encodelen; ss_ta_P ss_ta; ss_ctx_P ss_ctx; char *cltin, *str; const char *challenge, *mech; SM_IS_SS_SESS(ss_sess); SMTPS_NULL_SERVER; ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); if (ss_ta->ssta_state != SSTA_ST_NONE) return sm_str_scopy(ss_sess->ssse_wr, SS_R_NOTNOW); ss_ctx = ss_sess->ssse_sctx; if (!SSC_IS_FLAG(ss_ctx, SSC_FL_SASL_OK) || !SSSE_IS_CFLAG(ss_sess, SSSE_CFL_AUTH) || SSSE_IS_FLAG(ss_sess, SSSE_FL_AUTH)) return sm_str_scopy(ss_sess->ssse_wr, SS_R_NOTNOW); /* debugging .... remove/change later on */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 13, "auth=%S, tries=%u", ss_sess->ssse_rd, ss_sess->ssse_sasl_tries); /* hardcoded limit (CONF) */ if (++ss_sess->ssse_sasl_tries > 3) { (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_SSD, SS_PHASE_AUTH, true); return sm_error_temp(SM_EM_SMTPS, EPERM); /* error code?? */ } /* 5: "auth " */ args = sm_str2argv(ss_sess->ssse_rd, 5, sizeof(argv)/sizeof(argv[0]), argv); /* debugging .... remove/change later on */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 13, "auth=%S, ret=%#x",ss_sess->ssse_rd, args); if (sm_is_err(args) || args < 1) return sm_str_scopy(ss_sess->ssse_wr, "501 5.5.2 AUTH mechanism must be specified\r\n"); more = 0; cltinlen = 0; cltin = NULL; ret = sm_str_cpy(ss_sess->ssse_str, ss_sess->ssse_rd); if (sm_is_err(ret)) return sm_str_scopy(ss_sess->ssse_wr, SS_R_TMP); /* ** Note: mech only points to storage provided by ssse_str! ** That is, ssse_str must NOT be changed! */ mech = (const char *)sm_str_getdata(ss_sess->ssse_str) + argv[0]; /* ** check whether mechanism is available; ** would sasl_server_start do that itself? */ #if 0 if (iteminlist(mech, ss_ctx->ssse_sasl_mech_list, " ") == NULL) { return sm_strprintf(ss_sess->ssse_wr, "501 5.5.2 AUTH mechanism %.32s not available\r\n", mech); } #endif /* 0 */ /* ** check whether argument is available ** start server (with/without argument) ** while (CONTINUE) { ** send response ** get more data ** feed it into sasl_server_step() ** } ** if (OK) done ** else fail */ if (args >= 2) { cltin = (char *)sm_str_getdata(ss_sess->ssse_str) + argv[1]; i = strlen(cltin); /* ** RFC 2554 ** 4. The AUTH command [[...]] ** Unlike a zero-length client answer to a 334 reply, a zero- ** length initial response is sent as a single equals sign ("="). */ if ('=' == cltin[0] && '\0' == cltin[1]) { cltin = ""; cltinlen = 0; } else { str = (char *)sm_str_data(ss_sess->ssse_wr); ret = sasl_decode64(cltin, i, str, sm_str_getsize(ss_sess->ssse_wr), &cltinlen); if (ret != SASL_OK) { return sm_strprintf(ss_sess->ssse_wr, "501 5.5.4 cannot decode '%s' %d\r\n", cltin, ret); } SM_STR_SETLEN(ss_sess->ssse_wr, cltinlen); cltin = (char *)sm_str_getdata(ss_sess->ssse_wr); } } else { cltin = NULL; cltinlen = 0; } /* see if that auth type exists; challenge is allocated by sasl */ ret = sasl_server_start(ss_sess->ssse_sasl_conn, mech, cltin, cltinlen, &challenge, &challengelen); ss_sess->ssse_sasl_state = SASL_PROC_AUTH; while (SASL_CONTINUE == ret) { if (NULL == challenge) { /* ??? something seems to be wrong... */ ret = SASL_FAIL; break; } /* encode data from challenge/challengelen into ssse_rd */ sm_str_clr(ss_sess->ssse_rd); str = (char *)sm_str_data(ss_sess->ssse_rd); ret = sasl_encode64(challenge, challengelen, str, sm_str_getsize(ss_sess->ssse_rd), &encodelen); /* ** according to Cyrus SASL doc challenge is free()d by the ** library: it's stored in the context */ if (ret != SASL_OK) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 5, "sev=WARN, func=ss_auth, ss_sess=%s, sasl_encode=%d" , ss_sess->ssse_id, ret); /* start over? */ RESET_SASLCONN; return sm_str_scopy(ss_sess->ssse_wr, "454 4.5.4 Temporary authentication failure\r\n"); } SM_STR_SETLEN(ss_sess->ssse_rd, encodelen); /* create a reply in ssse_wr */ sm_str_clr(ss_sess->ssse_wr); sm_str_scat(ss_sess->ssse_wr, "334 "); sm_str_cat(ss_sess->ssse_wr, ss_sess->ssse_rd); sm_str_scat(ss_sess->ssse_wr, "\r\n"); ret = ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, false); sm_str_clr(ss_sess->ssse_wr); if (ret != SMTP_OK) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_auth, ss_sess=%s, client_ipv4=%A, where=auth_continue, send=%m" , ss_sess->ssse_id , ss_sess->ssse_client.s_addr, ret); goto fail; } ret = ss_read_cmd(ss_sess); if (sm_is_err(ret)) { RESET_SASLCONN; return ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_AUTH, false); break; } /* remove "\r\n" */ sm_str_rm_trail(ss_sess->ssse_rd, "\r\n"); if (sm_str_getlen(ss_sess->ssse_rd) == 1 && sm_str_rd_elem(ss_sess->ssse_rd, 0) == '*') { /* rfc 2254 4. */ RESET_SASLCONN; return sm_str_scopy(ss_sess->ssse_wr, "501 5.7.0 AUTH aborted\r\n"); break; } /* decode data from _rd into _wr */ str = (char *)sm_str_data(ss_sess->ssse_wr); ret = sasl_decode64((const char *)sm_str_getdata(ss_sess->ssse_rd), sm_str_getlen(ss_sess->ssse_rd), str, sm_str_getsize(ss_sess->ssse_wr), &cltinlen); if (ret != SASL_OK) { /* rfc 2254 4. */ RESET_SASLCONN; return sm_str_scopy(ss_sess->ssse_wr, "501 5.5.4 cannot decode AUTH parameter\r\n"); break; } SM_STR_SETLEN(ss_sess->ssse_wr, cltinlen); /* ** use decoded data in _wr for step, ** new data in challenge/challengelen */ str = (char *)sm_str_getdata(ss_sess->ssse_wr); ret = sasl_server_step(ss_sess->ssse_sasl_conn, str, sm_str_getlen(ss_sess->ssse_wr), &challenge, &challengelen); } if (SASL_OK == ret) { bool istrustedmech; SSSE_SET_FLAG(ss_sess, SSSE_FL_AUTH); istrustedmech = iteminlist(mech, ss_ctx->ssc_cnf.ss_cnf_trusted_mechs, " ") != NULL; if (istrustedmech) SSSE_SET_FLAG(ss_sess, SSSE_FL_CLIENT_RELAY); ss_sess->ssse_sasl_state = SASL_IS_AUTH; sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 9, "sev=INFO, func=ss_auth, ss_sess=%s, auth=%s, trusted_mech=%s" , ss_sess->ssse_id, mech , istrustedmech ? "yes" : "no"); # if MTA_USE_PMILTER cltinlen = strlen(mech); ss_sess->ssse_sasl_mech = sm_str_scpy0(NULL, mech, cltinlen + 2); /* need to set: ss_sess->ssse_sasl_authen ss_sess->ssse_sasl_author */ # endif /* MTA_USE_PMILTER */ return sm_str_scopy(ss_sess->ssse_wr, "235 2.0.0 OK Authenticated\r\n"); } else { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_auth, ss_sess=%s, where=auth, ret=%d, errstring=%s, errdetail=%s" , ss_sess->ssse_id, ret , sasl_errstring(ret, NULL, NULL) , sasl_errdetail(ss_sess->ssse_sasl_conn)); RESET_SASLCONN; return sm_str_scopy(ss_sess->ssse_wr, "535 5.7.0 authentication failed\r\n"); } return ret; fail: sm_str_clr(ss_sess->ssse_wr); return ret; } #endif /* MTA_USE_SASL */ /* ** SS_MAIL_ARGS -- Check arguments for MAIL command ** ** Parameters: ** ss_sess -- SMTPS session ** offset -- offset in ssse_rd where arguments begin ** ** Returns: ** usual return code ** ** Side Effects: ** ss_sess->ssse_wr contains error text in case of an error */ static sm_ret_T ss_mail_args(ss_sess_P ss_sess, size_t offset) { int i, args; sm_ret_T ret; char *argname, *argvalue; uint argnames[SS_MAX_ARGS], argvalues[SS_MAX_ARGS]; /* ssse_rd is '\0' terminated; make a copy as sm_str2args() "damages" it. */ ret = sm_str_cpy(ss_sess->ssse_str, ss_sess->ssse_rd); if (sm_is_err(ret)) { sm_str_scopy(ss_sess->ssse_wr, SS_R_SSD); return ret; } args = sm_str2args(ss_sess->ssse_str, offset, sizeof(argnames)/sizeof(argnames[0]), argnames, argvalues); if (sm_is_err(args)) { sm_str_scopy(ss_sess->ssse_wr, SS_R_SYN_ARG); return sm_error_perm(SM_EM_SMTPS, EINVAL); } for (i = 0; i < args; i++) { argname = (char *) sm_str_getdata(ss_sess->ssse_str) + argnames[i]; if (argvalues[i] > 0) argvalue = (char *)sm_str_getdata(ss_sess->ssse_str) + argvalues[i]; else argvalue = NULL; if (sm_strcaseeq(argname, "body") && SSC_IS_CFLAG(ss_sess->ssse_sctx, SSC_CFL_8BITMIME)) { if (!sm_strcaseeq(argvalue, "8bitmime") && !sm_strcaseeq(argvalue, "7bit")) { sm_str_scopy(ss_sess->ssse_wr, "501 5.5.4 unknown body= value\r\n"); return sm_error_perm(SM_EM_SMTPS, EINVAL); } } else if (sm_strcaseeq(argname, "size")) { ulong msg_size, max_msg_sz_kb; char *endptr; errno = 0; msg_size = strtoul(argvalue, &endptr, 10); if ((ULONG_MAX == msg_size && errno == ERANGE) || !ISDIGIT(argvalue[0])) { sm_str_scopy(ss_sess->ssse_wr, "501 5.5.4 invalid size= value\r\n"); return sm_error_perm(SM_EM_SMTPS, EINVAL); } max_msg_sz_kb = ss_sess->ssse_sctx->ssc_cnf.ss_cnf_max_msg_sz_kb * ONEKB; if (max_msg_sz_kb > 0 && msg_size > max_msg_sz_kb) { sm_strprintf(ss_sess->ssse_wr, "552 5.3.4 Size %lu exceeds maximum value %lu\r\n" , msg_size, max_msg_sz_kb); return sm_error_perm(SM_EM_SMTPS, EINVAL); } } else if (sm_strcaseeq(argname, "xverp") && SSC_IS_CFLAG(ss_sess->ssse_sctx, SSC_CFL_XVERP)) { SSTA_SET_FLAG(ss_sess->ssse_ta, SSTA_FL_XVERP); /* ignore argvalue for now */ } #if MTA_USE_PMILTER && MTA_USE_RSAD else if (sm_strcaseeq(argname, "prdr") && SSC_IS_CFLAG(ss_sess->ssse_sctx, SSC_CFL_RSAD) && NULL == argvalue) { SSTA_SET_FLAG(ss_sess->ssse_ta, SSTA_FL_RSAD); } #endif #if MTA_USE_SASL else if (sm_strcaseeq(argname, "auth")) { /* just accept it... log? */ /* needed for pmilter macro? */ } #endif /* MTA_USE_SASL */ else { sm_str_scopy(ss_sess->ssse_wr, "501 5.5.0 Unknown argument\r\n"); return sm_error_perm(SM_EM_SMTPS, EINVAL); } } return SM_SUCCESS; } /* ** SS_MAIL -- MAIL command ** ** Parameters: ** ss_sess -- SMTPS session ** ** Returns: ** usual return code */ static sm_ret_T ss_mail(ss_sess_P ss_sess) { sm_ret_T ret; uint flags, ltype, argoffset; int log; ss_ta_P ss_ta; ss_mail_P mail; ss_ctx_P ss_ctx; #define MAILLEN 10 /* length of "MAIL FROM:" */ SM_IS_SS_SESS(ss_sess); SMTPS_NULL_SERVER; ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); ss_ctx = ss_sess->ssse_sctx; mail = NULL; log = SM_DONT_LOG; argoffset = 0; sm_str_clr(ss_sess->ssse_wr); if (SSSE_IS_CFLAG(ss_sess, SSSE_CFL_EHLO_REQ) && !(SSSE_ST_EHLO == ss_sess->ssse_state || SSSE_ST_HELO == ss_sess->ssse_state)) { log = 14; sm_str_scopy(ss_sess->ssse_wr, SS_R_EHLO_REQ); goto cleanup; } if (SSTA_ST_NONE == ss_ta->ssta_state) { ret = ss_ta_init(ss_sess, ss_ta); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 13, "sev=INFO, func=ss_mail, ss_sess=%s, ss_ta=%s, ss_ta_init=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); if (sm_is_err(ret)) goto ssd; } else { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 15, "sev=DBG, func=ss_mail, ss_sess=%s, tss_a=%s, status=ta_reuse" , ss_sess->ssse_id, ss_ta->ssta_id); } if (ss_ta->ssta_state != SSTA_ST_INIT) { sm_str_scopy(ss_sess->ssse_wr, SS_R_SEQ); goto cleanup; } if (strncasecmp((char *) sm_str_getdata(ss_sess->ssse_rd), "mail from:", MAILLEN) != 0) { sm_str_scopy(ss_sess->ssse_wr, SS_R_SYNE); goto cleanup; } if (ss_sess->ssse_ta_tot >= ss_ctx->ssc_cnf.ss_cnf_t_tot_lim) { log = 10; sm_str_scopy(ss_sess->ssse_wr, "452 4.3.0 Too many transactions.\r\n"); goto cleanup; } ++ss_sess->ssse_ta_tot; #if MTA_USE_TLS if (ss_sess->ssse_cnf != NULL) { ret = ss_tlsreq(ss_sess, SMTP_R_SSD); if (sm_is_err(ret)) goto ssd; if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) { log = 8; /* better/selectable error code? */ if (SM_IS_FLAG(ss_sess->ssse_tlsreq_cnf.tlsreqcnf_viol, TLSREQ_VIOL_PERM)) sm_str_scopy(ss_sess->ssse_wr, "503 5.7.0 security requirements not met.\r\n"); else if (SM_IS_FLAG(ss_sess->ssse_tlsreq_cnf.tlsreqcnf_viol, TLSREQ_VIOL_TEMP)) sm_str_scopy(ss_sess->ssse_wr, "403 4.7.0 security requirements not met.\r\n"); else { /* default behavior */ sm_str_scopy(ss_sess->ssse_wr, "421 4.7.0 security requirements not met.\r\n"); goto ssd; } goto cleanup; } } #endif ret = ssm_mail_new(ss_ta, true, &mail); if (sm_is_err(ret)) goto ssd; ret = t2821_scan((sm_rdstr_P) ss_sess->ssse_rd, &mail->ssm_a2821, MAILLEN); if (sm_is_err(ret)) { log = 14; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYN_S_A); goto cleanup; } if ((uint)ret < sm_str_getlen(ss_sess->ssse_rd) && !ISSPACE(sm_str_rd_elem(ss_sess->ssse_rd, ret))) { log = 18; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYN_PAR); goto cleanup; } if (ret > MAILLEN && (uint)ret < sm_str_getlen(ss_sess->ssse_rd)) { argoffset = ret; ret = ss_mail_args(ss_sess, ret); if (sm_is_err(ret)) { log = 12; goto cleanup; } } flags = R2821_CORRECT|R2821_EMPTY; ret = t2821_parse(&mail->ssm_a2821, flags); if (sm_is_err(ret)) { log = 13; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYN_S_A); goto cleanup; } sm_str_clr(ss_sess->ssse_str); ret = t2821_str(&mail->ssm_a2821, ss_sess->ssse_str, 0); if (sm_is_err(ret)) goto ssd; sm_str_clr(ss_sess->ssse_wr); /* clear reply string */ if (sm_str_getlen(ss_sess->ssse_str) == 2 && sm_memeq(sm_str_data(ss_sess->ssse_str), "<>", 2)) SSTA_SET_FLAG(ss_ta, SSTA_FL_DSN); sm_str_clr(mail->ssm_pa); ret = sm_str_cpy(mail->ssm_pa, ss_sess->ssse_str); if (sm_is_err(ret)) goto ssd; /* ** Need to perform checks if ** sender is not "<>" ** and neither QUICK:RELAY nor QUICK:OK for session are set */ if (!SSTA_IS_FLAG(ss_ta, SSTA_FL_DSN) && !SSSE_ARE_FLAGS(ss_sess, SSSE_FL_QUICK|SSSE_FL_CLIENT_RELAY) && !SSSE_ARE_FLAGS(ss_sess, SSSE_FL_QUICK|SSSE_FL_CLIENT_OK) ) { ltype = SMARA_LT_MAIL_ROUTE|SMARA_LT_MAIL_LOCAL; flags = SMARA_LFL_2821; if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_ACCESS_DB)) { ltype |= SMARA_LT_MAIL_ACC; flags |= SMARA_LFL_MXACC; if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_ACCIMPLDET)) flags |= SMARA_LFL_ACCIMPLDET; } ret = sm_s2a_addr(ss_sess, ss_ctx->ssc_s2a_ctx, ss_ta->ssta_id, ss_sess->ssse_str, RT_S2A_MAIL, ltype, flags); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 7, "sev=ERROR, func=ss_mail, ss_sess=%s, ss_ta=%s, sm_s2a_addr=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); goto ssd; } else /* not really required because of goto above */ ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4a2s, ss_ctx->ssc_s2a_ctx); if (SMAR_R_VAL(ret) == SMTP_R_CONT) ret = SMAR_R_FLAGS(ret)|SM_SUCCESS; /* "normalize" */ 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_mail, ss_sess=%s, ss_ta=%s, where=smar_check, sm_w4q2s_reply=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); goto ssd; } else if (SMAR_RISQUICK(ret)) { /* SSTA_SET_FLAG(ss_ta, SSTA_FL_MAIL_QCK); */ SMAR_RCLRQUICK(ret); } else if (SSTA_IS_FLAG(ss_ta, SSTA_FL_DELAY_CHKS) && IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) { sm_str_P str; sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 12, "sev=DBG, func=ss_mail, ss_sess=%s, ss_ta=%s, where=smar_check, sm_w4q2s_reply=%m, reply=%r, text=%@T" , ss_sess->ssse_id, ss_ta->ssta_id, ret , ss_ta->ssta_mail_acc.ssa_reply_code , ss_sess->ssse_wr); /* delay rejection */ str = sm_str_dup(NULL, ss_sess->ssse_wr); if (str != NULL) { ss_ta->ssta_mail_acc.ssa_reply_text = str; sm_str_clr(ss_sess->ssse_wr); ret = SMTP_R_OK; } else { /* can't accept transaction: ENOMEM */ ret = SMTP_R_SSD; (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_MAIL, true); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 4, "sev=ERROR, func=ss_mail, ss_sess=%s, ss_ta=%s, sm_str_dup=ENOMEM" , ss_sess->ssse_id, ss_ta->ssta_id); } } if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) { int rc; sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 10, "sev=INFO, func=ss_mail, ss_sess=%s, ss_ta=%s, mail=%@N, stat=%d, text=%@T" , ss_sess->ssse_id, ss_ta->ssta_id , ss_sess->ssse_str , ret, ss_sess->ssse_wr); if (!SMTP_REPLY_MATCHES_RCODE(ss_sess->ssse_wr, ret, 0, rc)) (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_MAIL, true); goto error; } else { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 13, "sev=INFO, func=ss_mail, ss_sess=%s, ss_ta=%s, where=smar_check, sm_w4q2s_reply=%m" , ss_sess->ssse_id , ss_ta->ssta_id, ret); if (SMTP_R_DISCARD == ret) { SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD); ret = SM_SUCCESS; /* "normalize" */ } } } #if MTA_USE_PMILTER ret = sspm_mail(ss_sess, ret, argoffset); if (sm_is_err(ret)) goto error; if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) goto error; #endif ss_ta->ssta_state = SSTA_ST_MAIL; ss_ta->ssta_mail = mail; SS_CONN_LOG(ss_sess, 2, "ss_mail"); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 2, "sev=INFO, ss_sess=%s, ss_ta=%s, mail=%@N, stat=%d" , ss_sess->ssse_id, ss_ta->ssta_id, ss_sess->ssse_str , ret | (SSTA_IS_FLAG(ss_ta, SSTA_FL_DISCARD) ? SMTP_R_DISCARD : 0)); return ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_MAIL, false); cleanup: ret = SM_SUCCESS; ssd: /* only called for sm_is_err(); hence fallthrough from cleanup is ok */ if (sm_is_err(ret)) { sm_str_scopy(ss_sess->ssse_wr, SS_R_SSD); /* fail on which errors? */ if (SM_E_FULL == sm_error_value(ret) || SM_E_CONN_CLSD == sm_error_value(ret) || S2s_IS_IOERR(ss_ctx)) { Max_cur_threads = 0; /* force shutdown */ SSC_SET_FLAG(ss_ctx, SSC_FL_TERMINATING|SSC_FL_SHUTDOWN); if (0 == ss_ctx->ssc_shutdown) ss_ctx->ssc_shutdown = st_time(); #if 0 /* * ask for restart of server process? * should not be really necessary as mcp should do that anyway */ if (SM_E_CONN_CLSD == sm_error_value(ret)) SSC_SET_FLAG(ss_ctx, SSC_FL_RESTARTDEP); #endif sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 1, "sev=ERROR, func=ss_mail, pid=%d, status=forcing_shutdown" , (int) My_pid); } } error: if (log < SM_DONT_LOG && sm_str_getlen(ss_sess->ssse_wr) > 0) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, log, "sev=INFO, func=ss_mail, ss_sess=%s, ss_ta=%s, ret=%m, status=%@T, input=%@T" , ss_sess->ssse_id, ss_ta->ssta_id, ret , ss_sess->ssse_wr, ss_sess->ssse_rd); } ssm_mail_free(ss_ta, mail); return ret; } /* macro for ss_rcpt */ #define SS_RELAYING_DENIED(rcpt_str) do { \ sm_log_write(ss_ctx->ssc_lctx, \ SS_LCAT_SERVER, SS_LMOD_SERVER, \ SM_LOG_NOTICE, 3, \ "sev=NOTICE, ss_sess=%s, ss_ta=%s, rcpt=%@N, stat=%d, status=%s Relaying denied" \ , ss_sess->ssse_id, ss_ta->ssta_id, rcpt_str \ , SSSE_IS_FLAG(ss_sess, SSSE_FL_CLT_RLY_TMP) \ ? 450 : 550 \ , SSSE_IS_FLAG(ss_sess, SSSE_FL_CLT_RLY_TMP) \ ? "450 4.4.0" : "550 5.7.1"); \ if (SSSE_IS_FLAG(ss_sess, SSSE_FL_CLT_RLY_TMP)) \ sm_str_scopy(ss_sess->ssse_wr, \ "450 4.4.0 Relaying temporarily denied.\r\n"); \ else \ sm_str_scopy(ss_sess->ssse_wr, \ "550 5.7.1 Relaying denied.\r\n"); \ } while (0) /* ** SS_RCPT -- RCPT command ** ** Parameters: ** ss_sess -- SMTPS session ** ** Returns: ** usual return code */ static sm_ret_T ss_rcpt(ss_sess_P ss_sess) { uint flags, ltype; uint fct_flags; int log; sm_ret_T ret, res; ss_ta_P ss_ta; ss_rcpt_P ss_rcpt; ss_ctx_P ss_ctx; #define RCPTLEN 8 /* length of "RCPT TO:" */ #define SSRF_FL_RELAY 0x0001 /* relaying allowed */ #define SSRF_FL_OK2CPM 0x0002 /* ok to call pmilter */ #define SSRF_FL_PMC 0x0004 /* pmilter has been called */ #define SSRF_FL_BAD 0x0008 /* treat as "bad command" */ SM_IS_SS_SESS(ss_sess); ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); ss_rcpt = NULL; fct_flags = SSSE_IS_FLAG(ss_sess, SSSE_FL_CLIENT_RELAY) ? SSRF_FL_RELAY : 0; ss_ctx = ss_sess->ssse_sctx; ret = SM_SUCCESS; log = SM_DONT_LOG; sm_str_clr(ss_sess->ssse_wr); sm_str_clr(ss_sess->ssse_str); if (ss_ta->ssta_state != SSTA_ST_MAIL && ss_ta->ssta_state != SSTA_ST_RCPT) { log = 20; sm_str_scopy(ss_sess->ssse_wr, SS_R_SEQ); goto cleanup; } if (strncasecmp((char *) sm_str_getdata(ss_sess->ssse_rd), "rcpt to:", RCPTLEN) != 0) { log = 14; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYNE); SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } if (ss_sess->ssse_rcpts_tot >= ss_ctx->ssc_cnf.ss_cnf_r_tot_lim || ss_ta->ssta_rcpts_tot >= ss_ctx->ssc_cnf.ss_cnf_r_ta_lim || ss_ta->ssta_rcpts_tot >= ss_ctx->ssc_cnf.ss_cnf_r_tot_lim ) { log = 14; sm_str_scopy(ss_sess->ssse_wr, "452 4.5.3 Too many recipients.\r\n"); SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } if (ss_ta->ssta_rcpts_len >= QSS_RC_MAXSZ / 2) { log = 14; sm_str_scopy(ss_sess->ssse_wr, "452 4.5.3 Too many recipients.\r\n"); SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } ret = ssr_rcpts_new(ss_ta, &ss_ta->ssta_rcpts, &ss_rcpt); if (sm_is_err(ret)) { log = 12; sm_str_scopy(ss_sess->ssse_wr, SS_R_SSD); goto error; } ss_sess->ssse_rcpts_tot++; /* ** HACK: convert "" to "" ** Notes: ** 1. This checks the ENTIRE string! It will NOT work if arguments ** exist! (doesn't matter right now as there are none allowed). ** 2. it unconditionally use ss_sess->ssse_sctx->ssc_hostname; ** maybe this should be configurable. ** 3. Many other functions in smX "expect" an address of the ** form "", so this is the easiest way ** to satisfy that expectation. */ (void) sm_postmaster_add_domain(ss_sess->ssse_rd, ss_sess->ssse_sctx->ssc_hostname, RCPTLEN); ret = t2821_scan((sm_rdstr_P) ss_sess->ssse_rd, &ss_rcpt->ssr_a2821, RCPTLEN); if (sm_is_err(ret)) { log = 12; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYN_R_A); SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } if ((uint)ret < sm_str_getlen(ss_sess->ssse_rd) && !ISSPACE(sm_str_rd_elem(ss_sess->ssse_rd, ret))) { log = 12; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYNE); SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } /* check args: none for now, fixme: misleading error if no address */ if ((uint)ret < sm_str_getlen(ss_sess->ssse_rd)) { log = 14; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYNE); /* "555 5.5.5 Parameter unsupported.\r\n") */ SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } /* * keep track of total length of all rcpts to avoid overflowing RCB * (entire string: it's too much but that doesn't matter much as we just * try to establish some upper bound). */ ss_ta->ssta_rcpts_len += sm_str_getlen(ss_sess->ssse_rd); /* further checks should be somewhere else, configurable */ ret = t2821_parse(&ss_rcpt->ssr_a2821, R2821_CORRECT); if (sm_is_err(ret)) { log = 12; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYN_R_A); SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } SM_SET_FLAG(fct_flags, SSRF_FL_OK2CPM); /* Simple anti-relay test... */ if (!SM_IS_FLAG(fct_flags, SSRF_FL_RELAY)) { int r; ret = t2821_str(&ss_rcpt->ssr_a2821, ss_sess->ssse_wr, 0); if (sm_is_err(ret)) { log = 14; sm_str_scopy(ss_sess->ssse_wr, SS_R_SYN_R_A); goto cleanup; } r = regexec(&ss_ctx->ssc_relayto, (const char *) sm_str_getdata(ss_sess->ssse_wr), 0, NULL, 0); if (0 == r) SM_SET_FLAG(fct_flags, SSRF_FL_RELAY); if (r != 0 && !SSC_IS_CFLAG(ss_ctx, SSC_CFL_ACCESS_DB) && SSC_IS_CFLAG(ss_ctx, SSC_CFL_LMTPNOTRELAY)) { SS_RELAYING_DENIED(ss_sess->ssse_wr); SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } sm_str_clr(ss_sess->ssse_wr); } /* ** Check whether RCPT is acceptable ... Where/Who should do it? ** For example: local domain: check user list. ** Should SMTPS do this or SMAR? ** There is a comment in qmgr (sm_q_rcptid()) to do it there; ** however, it might be simpler to call an "anti-spam" SMAR ** routine from here than going via QMGR. */ /* send the recipient information to SMAR then QMGR */ sm_str_clr(ss_sess->ssse_str); ret = t2821_str(&ss_rcpt->ssr_a2821, ss_sess->ssse_str, 0); if (sm_is_err(ret)) { log = 12; sm_str_scopy(ss_sess->ssse_wr, SS_R_SSD); goto error; } ltype = SMARA_LT_RCPT_LOCAL; flags = SMARA_LFL_2821; if (!SSC_IS_CFLAG(ss_ctx, SSC_CFL_LMTPNOTRELAY)) ltype |= SMARA_LT_RCPT_LCL_R; if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_PROTBYMAIL|SSC_CFL_PROTBYCLTADDR)) ltype |= SMARA_LT_RCPT_PROT; if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_PROTMAP)) flags |= SMARA_LFL_PROTMAP; if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_PROTIMPLDET)) flags |= SMARA_LFL_PROTIMPLDET; if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_ACCIMPLDET)) flags |= SMARA_LFL_ACCIMPLDET; /* ** Need to check access map if ** access feature is set ** but not QUICK:RELAY for session ** and not QUICK:OK for session unless !relay (i.e., it might be ** necessary to allow relaying) */ if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_ACCESS_DB) && !(SSSE_ARE_FLAGS(ss_sess, SSSE_FL_QUICK|SSSE_FL_CLIENT_RELAY)) && !(SSSE_ARE_FLAGS(ss_sess, SSSE_FL_QUICK|SSSE_FL_CLIENT_OK) && SM_IS_FLAG(fct_flags, SSRF_FL_RELAY)) ) { ltype |= SMARA_LT_RCPT_ACC; } /* ** greylisting: if requested ** and not done before in this session ** and not client:ok ** and not client relaying allowed */ if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_GREY) && !SSSE_IS_FLAG(ss_sess, SSSE_FL_GREYCHKD) && !SSSE_IS_FLAG(ss_sess, SSSE_FL_CLIENT_OK) && !SSSE_IS_FLAG(ss_sess, SSSE_FL_CLIENT_RELAY)) { ltype |= SMARA_LT_GREY; } ret = sm_s2a_rcptid(ss_sess, ss_ctx->ssc_s2a_ctx, ss_ta->ssta_id, ss_rcpt->ssr_idx, ltype, flags, ss_sess->ssse_str); 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_rcpt, ss_sess=%s, ss_ta=%s, sm_s2a_rcptid=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); goto error; } ret = sm_w4q2s_reply(ss_sess, ss_ctx->ssc_cnf.ss_cnf_w4a2s, ss_ctx->ssc_s2a_ctx); 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_rcpt, ss_sess=%s, ss_ta=%s, sm_w4q2s_reply=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); goto error; } else if (SMAR_RISQUICK(ret)) { /* SSTA_SET_FLAG(ss_ta, SSTA_FL_RCPT_QCK); */ SMAR_RCLRQUICK(ret); /* quick:discard -> discard entire transaction */ if (SMTP_R_DISCARD == ret) SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD); } if (SMAR_RISGREYCHKD(ret)) { SSSE_SET_FLAG(ss_sess, SSSE_FL_GREYCHKD); SMAR_RCLRGREYCHKD(ret); } if (SMAR_RISGREY(ret)) { SSSE_SET_FLAG(ss_sess, SSSE_FL_GREYLSTD); SMAR_RCLRGREY(ret); if (SSC_IS_CFLAG(ss_ctx, SSC_CFL_GREY_DATA)) { ret = SMAR_R_FLAGS(ret)|SM_SUCCESS; sm_str_clr(ss_sess->ssse_wr); } } if (!SM_IS_FLAG(fct_flags, SSRF_FL_RELAY) && SMAR_R_IS(ret, SMAR_R_LOC_RCPT) && !SSC_IS_CFLAG(ss_ctx, SSC_CFL_LMTPNOTRELAY)) SM_SET_FLAG(fct_flags, SSRF_FL_RELAY); SMAR_CLRFLAGS(ret); if (!SM_IS_FLAG(fct_flags, SSRF_FL_RELAY) && ret != SMTP_R_RELAY && !SMTP_IS_REPLY_ERROR(ret)) { SS_RELAYING_DENIED(ss_sess->ssse_str); SM_SET_FLAG(fct_flags, SSRF_FL_BAD); goto cleanup; } if (SMTP_R_RELAY == ret) ret = SM_SUCCESS; /* "normalize" */ else if (SMTP_R_DISCARD == ret) /* check before relaying denied?? */ goto discard; else if (SMTP_R_CONT == ret) ret = SM_SUCCESS; /* "normalize" */ /* ** Note: it might be useful to process several RCPT commands ** concurrently (if PIPELINING is active). This can "hide" ** latencies in the address processing, esp. DNS lookups. ** Question: how much would this complicate the code? ** Currently probably quite a lot, because the code has ** to check whether there is data from the client ** and whether there is a response from the QMGR. ** ** Notes: ** If a pmilter should allow relaying, then this code needs ** to be moved up (before SS_RELAYING_DENIED()) or that ** information must be maintained somehow (e.g., check "relay" ** after pmilter call). ** ** This is "flow through" code: ret may contain an error ** which must not be overridden (unless "increased"). */ #if MTA_USE_PMILTER res = sspm_rcpt(ss_sess, ss_rcpt, ret); # if MTA_USE_RSAD ss_rcpt->ssr_rcode = res; # endif SM_SET_FLAG(fct_flags, SSRF_FL_PMC); if (sm_is_err(res)) { /* check whether res is more "severe" than ret? */ ret = res; goto error; } if (SMTP_R_DISCARD == res) { ret = res; goto discard; } /* HACK: relies on order of SMTP reply code: 5 more "severe" than 4 */ if (smtp_reply_type(res) > smtp_reply_type(ret)) ret = res; #endif /* XXX how does this interact with RSAD??? */ /* everything is fine so far? check previous rejections */ if (SMTPS_R_IS_OK(ret) && SSTA_IS_FLAG(ss_ta, SSTA_FL_DELAY_CHKS)) { ss_acc_P ss_acc; ss_acc = NULL; sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 12, "sev=DBG, func=ss_rcpt, ss_sess=%s, ss_ta=%s, ta_result=%d, se_result=%d, map2_res=%d, reply2=%d" , ss_sess->ssse_id, ss_ta->ssta_id , ss_ta->ssta_mail_acc.ssa_map_result , ss_sess->ssse_acc.ssa_map_result , ss_ta->ssta_rcpt_acc2.ssa_map_result , ss_ta->ssta_rcpt_acc2.ssa_reply_code ); #if 0 /* todo: should check for SpamFriend... */ if (ss_ta->ssta_rcpt_acc2.ssa_map_result == SM_ACC_FOUND && ss_ta->ssta_rcpt_acc2.ssa_reply_code == SMTP_R_OK) { ret = SM_SUCCESS; } #else /* 0 */ if (ss_ta->ssta_rcpt_acc.ssa_map_result == SM_ACC_FOUND && SMAR_RISQUICK(ss_ta->ssta_rcpt_acc.ssa_reply_code) && (SMAR_RWOFLAGS(ss_ta->ssta_rcpt_acc.ssa_reply_code) == SMTP_R_OK || SMAR_RWOFLAGS(ss_ta->ssta_rcpt_acc.ssa_reply_code) == SMTP_R_RELAY) ) { ret = SM_SUCCESS; } #endif /* 0 */ else if (ss_ta->ssta_mail_acc.ssa_map_result == SM_ACC_FOUND && IS_SMTP_REPLY(ss_ta->ssta_mail_acc.ssa_reply_code)) ss_acc = &ss_ta->ssta_mail_acc; else if (ss_sess->ssse_acc.ssa_map_result == SM_ACC_FOUND && IS_SMTP_REPLY(ss_sess->ssse_acc.ssa_reply_code)) ss_acc = &ss_sess->ssse_acc; if (ss_acc != NULL) { ret = ss_acc->ssa_reply_code; if (ss_acc->ssa_reply_text != NULL) sm_str_cpy(ss_sess->ssse_wr, ss_acc->ssa_reply_text); else sm_str_clr(ss_sess->ssse_wr); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 12, "sev=DBG, func=ss_rcpt, ss_sess=%s, ss_ta=%s, reply=%m, text=%@T" , ss_sess->ssse_id, ss_ta->ssta_id , ret, ss_sess->ssse_wr); } } res = ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_RCPT, false); if (SMTPS_R_IS_OK(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 2, "sev=INFO, func=ss_rcpt, ss_sess=%s, ss_ta=%s, rcpt=%@T, idx=%u, stat=%d" , ss_sess->ssse_id, ss_ta->ssta_id, ss_sess->ssse_str , ss_rcpt->ssr_idx, ret); } else { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 2, "sev=INFO, func=ss_rcpt, ss_sess=%s, ss_ta=%s, rcpt=%@T, idx=%u, stat=%d, status=%@T" , ss_sess->ssse_id, ss_ta->ssta_id, ss_sess->ssse_str , ss_rcpt->ssr_idx, ret, ss_sess->ssse_wr); } /* everything is fine? */ if (SM_SUCCESS == ret) { ss_ta->ssta_state = SSTA_ST_RCPT; ss_ta->ssta_rcpts_ok++; } else { SS_RCPTS_REMOVE_FREE(ss_ta, &ss_ta->ssta_rcpts, ss_rcpt); if (IS_SMTP_REPLY(ret) && smtp_is_reply_fail(ret) && ss_invalidaddr(ss_sess) == SMTP_R_SSD) ret = SMTP_R_SSD; } return (SMTP_R_SSD == ret) ? ret : res; discard: ss_ta->ssta_state = SSTA_ST_RCPT; SSTA_SET_FLAG(ss_ta, SSTA_FL_DSCRD_RCPT); if (sm_str_getlen(ss_sess->ssse_wr) == 0) sm_str_scopy(ss_sess->ssse_wr, SS_R_OK); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 9, "sev=INFO, func=ss_rcpt, ss_sess=%s, ss_ta=%s, rcpt=%@T, stat=%d" , ss_sess->ssse_id, ss_ta->ssta_id, ss_sess->ssse_str , SMTP_R_DISCARD); log = SM_DONT_LOG; cleanup: /* ** Note: ssse_str might be empty if an error occurred before rcpt was ** parse properly. Should we log the input instead? */ if (SM_SUCCESS == ret && IS_SMTP_REPLY_STR(ss_sess->ssse_wr, 0)) ret = SMTP_REPLY_STR2VAL(ss_sess->ssse_wr, 0); if (log < SM_DONT_LOG && sm_str_getlen(ss_sess->ssse_wr) > 0) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, log, "sev=INFO, func=ss_rcpt, ss_sess=%s, ss_ta=%s, rcpt=%@T, error=%m, status=%@T" , ss_sess->ssse_id, ss_ta->ssta_id, ss_sess->ssse_str , ret, ss_sess->ssse_wr); } #if MTA_USE_PMILTER if (!SM_IS_FLAG(fct_flags, SSRF_FL_PMC) && SM_IS_FLAG(fct_flags, SSRF_FL_OK2CPM)) { res = sspm_rcpt(ss_sess, ss_rcpt, ret); SM_SET_FLAG(fct_flags, SSRF_FL_PMC); } #endif ret = SM_SUCCESS; error: if (sm_str_getlen(ss_sess->ssse_wr) == 0) sm_str_scopy(ss_sess->ssse_wr, SS_R_SSD); if (ret != SM_SUCCESS && log < SM_DONT_LOG && sm_str_getlen(ss_sess->ssse_wr) > 0) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, log, "sev=INFO, func=ss_rcpt, ss_sess=%s, ss_ta=%s, rcpt=%@T, error=%m, status=%@T" , ss_sess->ssse_id, ss_ta->ssta_id, ss_sess->ssse_str , ret, ss_sess->ssse_wr); } /* remove rcpt from list again and free it */ if (ss_rcpt != NULL) SS_RCPTS_REMOVE_FREE(ss_ta, &ss_ta->ssta_rcpts, ss_rcpt); if (SM_IS_FLAG(fct_flags, SSRF_FL_BAD) && IS_SMTP_REPLY(ret) && smtp_is_reply_fail(ret) && ss_invalidaddr(ss_sess) == SMTP_R_SSD) ret = SMTP_R_SSD; return ret; } /* ** SS_DATA_EOB -- found EOB in data stream: write block to CDB etc ** ** Parameters: ** ss_sess -- SMTPS session ** bufp -- pointer to (begin of) file buffer ** pskip -- (pointer to) skip rest of input? (output) ** ** Returns: ** usual return code */ static sm_ret_T ss_data_eob(ss_sess_P ss_sess, uchar *bufp, bool *pskip) { sm_ret_T ret; size_t bytes2write, max_sz_kb; ssize_t byteswritten; ss_ta_P ss_ta; SM_IS_SS_SESS(ss_sess); ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); SM_REQUIRE(pskip != NULL); /* write the current buffer */ bytes2write = f_p(*ss_sess->ssse_fp) - bufp; if (0 == bytes2write) return SM_SUCCESS; ret = cdb_write(cdb_ctx, ss_ta->ssta_dfp, bufp, bytes2write, &byteswritten); if (sm_is_err(ret)) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_data_eob, ss_sess=%s, ss_ta=%s, write_cdb=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_TEMP, SS_PHASE_DOT, true); *pskip = true; return ret; } ss_ta->ssta_msg_sz_b += byteswritten; max_sz_kb = ss_sess->ssse_sctx->ssc_cnf.ss_cnf_max_msg_sz_kb; if (max_sz_kb > 0 && max_sz_kb < ss_ta->ssta_msg_sz_b / 1024) { sm_log_write(ss_sess->ssse_sctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_NOTICE, 8, "sev=NOTICE, func=ss_data_eob, ss_sess=%s, ss_ta=%s, max_message_size=%lu, status=exceeded" , ss_sess->ssse_id, ss_ta->ssta_id , (ulong) max_sz_kb); sm_str_scopy(ss_sess->ssse_wr, "552 5.3.4 Maximum message size exceeded.\r\n"); *pskip = true; } #if MTA_USE_PMILTER ret = sspm_msg(ss_sess, bufp, bytes2write, false, pskip); #endif return ret; } #if MTA_USE_PMILTER && MTA_USE_RSAD /* ** SS_RSAD_SET_REPLIES -- Set RCPT replies after end of mail data ** ** Parameters: ** ss_ctx -- SMTPS context ** ss_sess -- SMTPS session ** ss_ta -- SMTP server transaction context ** ** Returns: ** usual return code */ static sm_ret_T ss_rsad_set_replies(ss_ctx_P ss_ctx, ss_sess_P ss_sess, ss_ta_P ss_ta) { sm_ret_T ret, rcode; uint u; ss_rcpt_P ss_rcpt; SM_IS_SS_SESS(ss_sess); SM_IS_SS_TA(ss_ta); if (ss_ta->ssta_nreplies != ss_ta->ssta_rcpts_ok_orig) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_NOTICE, 2, "sev=NOTICE, func=ss_rsad_set_replies, ss_sess=%s, ss_ta=%s, replies=%u, ok=%u, status=inconsistent" , ss_sess->ssse_id, ss_ta->ssta_id , ss_ta->ssta_nreplies, ss_ta->ssta_rcpts_ok_orig); return SMTP_R_SSD; } #define SMTP_R_IS_OK(rcode) (SMTP_OK == rcode || smtp_reply_type(rcode) == SMTP_RTYPE_OK) /* set replies */ if (SS_RCPTS_EMPTY(&ss_ta->ssta_rcpts)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 2, "sev=ERROR, func=ss_rsad_set_replies, ss_sess=%s, ss_ta=%s, replies=%u, ok=%u, status=no_rcpts" , ss_sess->ssse_id, ss_ta->ssta_id , ss_ta->ssta_nreplies, ss_ta->ssta_rcpts_ok); return SMTP_R_SSD; } ss_rcpt = SS_RCPTS_FIRST(&ss_ta->ssta_rcpts); rcode = ss_ta->ssta_rcodes[0]; for (u = 0; u < ss_ta->ssta_nreplies; u++) { while (!SMTP_R_IS_OK(ss_rcpt->ssr_rcode) && ss_rcpt->ssr_type != PM_RCPT_DEL) { ss_rcpt = SS_RCPTS_NEXT(ss_rcpt); if (ss_rcpt == SS_RCPTS_END(&ss_ta->ssta_rcpts)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_rsad_set_replies, ss_sess=%s, ss_ta=%s, status=no_valid_rcpt_found" , ss_sess->ssse_id, ss_ta->ssta_id); return SMTP_R_SSD; } } ret = ss_ta->ssta_rcodes[u]; if (ret < rcode) rcode = ret; if (!SMTP_R_IS_OK(ret)) { ss_rcpt->ssr_type = PM_RCPT_RSAD; ss_rcpt->ssr_rcode = ret; --ss_ta->ssta_rcpts_ok; /* ??? */ } ss_rcpt = SS_RCPTS_NEXT(ss_rcpt); } /* "normalize" rcode */ if (rcode != SMTP_OK && smtp_reply_type(rcode) == SMTP_RTYPE_OK) rcode = SMTP_OK; return rcode; } /* ** SS_RSAD_SEND_REPLIES -- Send deferred RCPT replies after end of mail data ** ** Parameters: ** ss_ctx -- SMTPS context ** ss_sess -- SMTPS session ** ss_ta -- SMTP server transaction context ** ** Returns: ** usual return code */ static sm_ret_T ss_rsad_send_replies(ss_ctx_P ss_ctx, ss_sess_P ss_sess, ss_ta_P ss_ta) { sm_ret_T ret, rcode; uint ui; ss_rcpt_P ss_rcpt; SM_IS_SS_SESS(ss_sess); SM_IS_SS_TA(ss_ta); rcode = 599; #if 0 /* optimization: check for common RCPT status */ for (ss_rcpt = SS_RCPTS_FIRST(&ss_rcpts_hd); ss_rcpt != SS_RCPTS_END(&ss_rcpts_hd); ss_rcpt = SS_RCPTS_NEXT(ss_rcpt)) { n = atoi(ss_rcpt->ssr_reply); if (CRS_INIT == common_rcpt_status) common_rcpt_status = n; else if (n != common_rcpt_status) { common_rcpt_status = CRS_NONE; break; } } if (IS_SMTP_REPLY(common_rcpt_status)) sm_snprintf(resp, sizeof(resp), "%d first\r\n", common_rcpt_status); else strlcpy(resp, "353 RCPT status follows\r\n", sizeof resp); #endif # if MTA_USE_RSAD == 2 /* send "first reply" */ sm_str_scopy(ss_sess->ssse_wr, "353 RCPT replies follow.\r\n"); ret = ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, false); sm_str_clr(ss_sess->ssse_wr); if (ret != SMTP_OK) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_rsad_send_replies, ss_sess=%s, ss_ta=%s, status=write_error_first_reply, ret=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); goto error; } # endif /* MTA_USE_RSAD == 2 */ /* send all replies back to client */ for (ss_rcpt = SS_RCPTS_FIRST(&ss_ta->ssta_rcpts), ui = 0; ss_rcpt != SS_RCPTS_END(&ss_ta->ssta_rcpts) && ui < ss_ta->ssta_rcpts_ok_orig; ss_rcpt = SS_RCPTS_NEXT(ss_rcpt), ui++) { if (PM_RCPT_ADD == ss_rcpt->ssr_type) { /* shouldn't happen, ssta_rcpts_ok_orig should have stopped loop! */ break; } ret = ss_rcpt->ssr_rcode; if (ret < rcode) rcode = ret; (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_RCPT, true); ret = ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, false); sm_str_clr(ss_sess->ssse_wr); if (ret != SMTP_OK) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_rsad_send_replies, ss_sess=%s, ss_ta=%s, idx=%u, status=write_error_rsad, ret=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ss_rcpt->ssr_idx, ret); goto error; } else sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 10, "sev=INFO, func=ss_rsad_send_replies, ss_sess=%s, ss_ta=%s, idx=%u, ret=%d" , ss_sess->ssse_id, ss_ta->ssta_id, ss_rcpt->ssr_idx, ss_rcpt->ssr_rcode); #if 0 if we do this, then the loop must be a bit more clever, i.e., ss_rcpt_nxt = SS_RCPTS_NEXT(ss_rcpt); if (ss_rcpt->ssr_rcode != SMTP_OK) SS_RCPTS_REMOVE_FREE(ss_ta, &ss_ta->ssta_rcpts, ss_rcpt); and in the loop as "next" part: ss_rcpt = ss_rcpt_nxt; #endif } /* "normalize" rcode */ if (rcode != SMTP_OK && smtp_reply_type(rcode) == SMTP_RTYPE_OK) rcode = SMTP_OK; return rcode; error: return ret; } #endif /* MTA_USE_PMILTER && MTA_USE_RSAD */ /* ** SS_DATA -- DATA command ** ** Parameters: ** ss_sess -- SMTPS session ** ** Returns: ** usual return code ** ** Side Effects: writes cdb (unless DISCARD is set or errors occur) ** ** Note: SMTP requires that the input is read completely before ** returning a reply, hence error handling is done by ** remembering the error and setting "skip" instead of ** just jumping to the error handling label. The only ** exception is a read error in which case the connection ** is dropped. */ #ifndef SS_DATA_DEBUG # define SS_DATA_DEBUG 0 #endif /* RFC 2822: message-id = "Message-ID:" msg-id CRLF in-reply-to = "In-Reply-To:" 1*msg-id CRLF references = "References:" 1*msg-id CRLF msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] id-left = dot-atom-text / no-fold-quote / obs-id-left id-right = dot-atom-text / no-fold-literal / obs-id-right no-fold-quote = DQUOTE *(qtext / quoted-pair) DQUOTE no-fold-literal = "[" *(dtext / quoted-pair) "]" The state machine below does not implement this, it just skips leading whitespace. This should be "good enough" because currently the message-id is only used for logging. */ #define MSGID_ST_NONE 0 #define MSGID_ST_NEW (-1) /* found message-id: header */ #define MSGID_ST_FIRST (-2) /* found message-id: header, str allocated */ #define MSGID_ST_SKIP (-3) /* skipping over initial white space */ #define MSGID_ST_RDING (-4) /* reading message id */ #define MSGID_ST_GOT (-5) /* got message id */ #define MSGID_ST_NOMEM (-9) /* can't allocate memory for message id */ static sm_ret_T ss_data(ss_sess_P ss_sess) { int c; uint eot_state, eoh_state, rcvd_state, hops; #if SS_CHECK_LINE_LEN uint eol_state, line_len; #endif int msgid_state, res_msgid_state; size_t bufs, bytes2write, max_sz_kb; ssize_t byteswritten; bool skip; #if SS_DATA_DEBUG char sbuf[512]; ssize_t b; #endif sm_ret_T ret, res; ss_ta_P ss_ta; uchar *bufp; time_t currt; cdb_ctx_P cdb_ctx; ss_ctx_P ss_ctx; static SM_DECL_EOT; static SM_DECL_EOH; #if SS_CHECK_LINE_LEN static const char eol[] = "\r\n"; #define SM_EOL_LEN (sizeof(eol) - 1) #endif static const char rcvd[] = "\r\nreceived:"; static const char msgid[] = "\r\nmessage-id:"; static const char res_msgid[] = "\r\nresent-message-id:"; #if SS_TEST extern int Unsafe; #endif SM_IS_SS_SESS(ss_sess); ss_ta = ss_sess->ssse_ta; SM_IS_SS_TA(ss_ta); ss_ctx = ss_sess->ssse_sctx; /* beware of pipelining and discarded recipients */ if (ss_ta->ssta_state != SSTA_ST_RCPT || (NO_RCPTS(ss_ta) && !SSTA_IS_FLAG(ss_ta, SSTA_FL_DSCRD_RCPT))) return sm_str_scopy(ss_sess->ssse_wr, SS_R_SEQ); if (SSSE_IS_FLAG(ss_sess, SSSE_FL_GREYLSTD)) { (void) sm_str_scopy(ss_sess->ssse_wr, SS_R_GREY); ret = SMTP_R_SSD; sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 9, "sev=INFO, func=ss_data, ss_sess=%s, ss_ta=%s, stat=%d, status=%@T" , ss_sess->ssse_id, ss_ta->ssta_id , ret, ss_sess->ssse_wr); return ret; } /* discard entire transaction if all recipients have been discarded */ if (ss_ta->ssta_rcpts_ok == 0 && SSTA_IS_FLAG(ss_ta, SSTA_FL_DSCRD_RCPT)) SSTA_SET_FLAG(ss_ta, SSTA_FL_DISCARD); skip = SSTA_IS_FLAG(ss_ta, SSTA_FL_DISCARD); ss_ta->ssta_msg_sz_b = 0; byteswritten = 0; #if SS_CHECK_LINE_LEN eol_state = 0; line_len = 0; SSTA_CLR_FLAG(ss_ta, SSTA_FL_LL_EXC); #endif max_sz_kb = ss_ctx->ssc_cnf.ss_cnf_max_msg_sz_kb; ss_ta->ssta_dfp = NULL; currt = st_time(); cdb_ctx = ss_ctx->ssc_cdb_ctx; /* Check for "illegal" pipelining: DATA is synchronization point */ if (sm_io_getinfo(ss_sess->ssse_fp, SM_IO_IS_READABLE, NULL)) { sm_str_scopy(ss_sess->ssse_wr, "421 4.5.0 Illegal Pipelining detected (DATA)\r\n"); ret = sm_error_temp(SM_EM_SMTPS, SM_E_ILL_PIPE); goto error; } #if MTA_USE_PMILTER ret = sspm_data(ss_sess, &skip); if (sm_is_err(ret)) goto error; if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) goto error; #endif if (!skip) { /* open cdb entry */ ret = cdb_open_write(cdb_ctx, ss_ta->ssta_id, ss_ta->ssta_dfp, SM_IO_WREXCL, 0, NULL); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_data, ss_sess=%s, ss_ta=%s, cdb_open=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_TEMP, SS_PHASE_DATA, true); goto error; } SSTA_SET_FLAG(ss_ta, SSTA_FL_CDB_EXISTS); /* set group id? It might be inherited from directory */ if (ss_ctx->ssc_cnf.ss_cnf_cdb_gid > 0 && (c = fchown(sm_io_getinfo(ss_ta->ssta_dfp, SM_IO_WHAT_FD, NULL), (uid_t) -1, ss_ctx->ssc_cnf.ss_cnf_cdb_gid)) < 0) { #if SS_DEBUG_CHOWN_CDB struct stat sb; #endif ret = sm_error_temp(SM_EM_SMTPS, errno); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_data, ss_sess=%s, ss_ta=%s, chgrp_cdb=%d" , ss_sess->ssse_id, ss_ta->ssta_id, errno); #if SS_DEBUG_CHOWN_CDB c = fstat(f_fd(*ss_ta->ssta_dfp), &sb); if (0 == c) sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_data, ss_sess=%s, ss_ta=%s, uid=%d, gid=%d, cdb_gid=%d" , ss_sess->ssse_id, ss_ta->ssta_id , (int) sb.st_uid, (int) sb.st_gid , (int) ss_ctx->ssc_cnf.ss_cnf_cdb_gid); else sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_data, ss_sess=%s, ss_ta=%s, fstat=%d" , ss_sess->ssse_id, ss_ta->ssta_id, c); #endif /* SS_DEBUG_CHOWN_CDB */ (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_TEMP, SS_PHASE_DATA, true); goto error; } } ss_ta->ssta_state = SSTA_ST_DATA; /* initial state: begin of line, hence \r\n are "skipped" */ eot_state = rcvd_state = SM_GOT_CRLF; msgid_state = res_msgid_state = SM_GOT_CRLF; eoh_state = hops = 0; sm_str_scopy(ss_sess->ssse_wr, "354 Go\r\n"); if ((ret = ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, true)) != SMTP_OK) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_data, ss_sess=%s, ss_ta=%s, write_354_response=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); if (!sm_is_err(ret)) ret = SMTP_WR_ERR; goto error; } /* ** Write Received: header; do NOT use %S for helo: it ends in '\0'. ** More data?? */ sm_str_clr(ss_sess->ssse_str); #if MTA_USE_TLS if (!skip && SSSE_IS_FLAG(ss_sess, SSSE_FL_STARTTLS)) { sm_str_scat(ss_sess->ssse_str, "(TLS"); if (ss_sess->ssse_tlsi->tlsi_version != NULL) { sm_str_scat(ss_sess->ssse_str, "="); sm_str_cat(ss_sess->ssse_str, ss_sess->ssse_tlsi->tlsi_version); } if (ss_sess->ssse_tlsi->tlsi_cipher != NULL) { sm_str_scat(ss_sess->ssse_str, ", cipher="); sm_str_cat(ss_sess->ssse_str, ss_sess->ssse_tlsi->tlsi_cipher); } if (ss_sess->ssse_tlsi->tlsi_cipher_bits != 0) { sm_str_scat(ss_sess->ssse_str, ", bits="); sm_strprintf(ss_sess->ssse_str, "%d", ss_sess->ssse_tlsi->tlsi_cipher_bits); } sm_str_scat(ss_sess->ssse_str, ", verify="); sm_str_scat(ss_sess->ssse_str, tls_vrfy2txt(ss_sess->ssse_tlsi->tlsi_vrfy)); if (sm_str_getlen(ss_sess->ssse_str) > 40) sm_str_scat(ss_sess->ssse_str, ")\r\n\t"); else sm_str_scat(ss_sess->ssse_str, ") "); } #endif sm_str_clr(ss_sess->ssse_wr); if (!skip) { sm_strprintf(ss_sess->ssse_wr, "Received: from %.128N (%.128C [%A])\r\n\tby %S (%s) with %sSMTP%s%s\r\n\t%Sid %s; " , ss_sess->ssse_helo , ss_sess->ssse_cltname , ss_sess->ssse_client.s_addr , ss_ctx->ssc_hostname , MTA_VERSION_STR , SSSE_IS_FLAG(ss_sess, SSSE_FL_HELO) ? "" : "E" , SSSE_IS_FLAG(ss_sess, SSSE_FL_STARTTLS) ? "S" : "" , SSSE_IS_FLAG(ss_sess, SSSE_FL_AUTH) ? "A" : "" , ss_sess->ssse_str , ss_ta->ssta_id ); ret = arpadate(&currt, ss_sess->ssse_wr); ret = sm_str_scat(ss_sess->ssse_wr, "\r\n"); ret = cdb_write(cdb_ctx, ss_ta->ssta_dfp, sm_str_data(ss_sess->ssse_wr), sm_str_getlen(ss_sess->ssse_wr), &byteswritten); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_data, ss_sess=%s, ss_ta=%s, write_cdb=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_TEMP, SS_PHASE_DOT, true); skip = true; } else { #if MTA_USE_PMILTER ret = sspm_msg(ss_sess, sm_str_data(ss_sess->ssse_wr), sm_str_getlen(ss_sess->ssse_wr), true, &skip); #endif sm_str_clr(ss_sess->ssse_wr); } ss_ta->ssta_msg_sz_b += byteswritten; } /* ** Notes: need to check whether the first line contains ** a header: if it doesn't: print a blank line. ** Also need to check the number of Received: headers, ** this means we need to recognize header/body boundary ** and Received: headers. This is done below in a simple way. */ /* ** See the smX docs: libraries.func.tex Required I/O Functionality */ bufs = 0; /* Switch to read mode before accessing the buffer */ sm_iotord(ss_sess->ssse_fp); bufp = f_p(*ss_sess->ssse_fp); for (;;) { c = sm_getb(ss_sess->ssse_fp); do { if (SM_IO_EOF == c) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_data, ss_sess=%s, ss_ta=%s, read_data=EOF, bufs=%lu" , ss_sess->ssse_id, ss_ta->ssta_id , (ulong) bufs); ret = sm_error_perm(SM_EM_SMTPS, SM_E_EOF); goto error; } if (c != SM_IO_EOB) { SM_ASSERT(c >= 0); break; } if (!skip) { (void) ss_data_eob(ss_sess, bufp, &skip); /* ignore return code, skip is important */ } /* get new buffer */ c = sm_rget(ss_sess->ssse_fp); if (sm_is_err(c)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_data, ss_sess=%s, ss_ta=%s, read_error=%m, bufs=%lu" , ss_sess->ssse_id, ss_ta->ssta_id, c , (ulong) bufs); ret = c; /* SMTP_RD_ERR; */ goto error; } ++bufs; bufp = f_p(*ss_sess->ssse_fp) - 1; if (bufp < f_bfbase(*ss_sess->ssse_fp)) bufp = f_bfbase(*ss_sess->ssse_fp); #if SS_DATA_DEBUG if (ss_ctx->ssc_cnf.ss_cnf_debug > 1) { int e = errno; sm_snprintf(sbuf, sizeof(sbuf), "data: read buf=%d, c=%3d, f_r=%x, f_p=%lx, bufp=%lx, *bufp=%d, f_bfbase=%lx, f_bfsize=%x, e=%d\r\n" , bufs, c, f_r(*ss_sess->ssse_fp), (long) f_p(*ss_sess->ssse_fp), (long) bufp, (int) *bufp, (long) f_bfbase(*ss_sess->ssse_fp), f_bfsize(*ss_sess->ssse_fp), e); sm_io_write(smioerr, (uchar *)sbuf, strlen(sbuf), &b); sm_io_flush(smioerr); errno = e; } #endif /* SS_DATA_DEBUG */ } while (c < 0); /* HACK!! */ #if SS_CHECK_LINE_LEN if (ss_sess->ssse_max_line_len > 0 && !skip) { if (++line_len > ss_sess->ssse_max_line_len) { skip = true; SSTA_SET_FLAG(ss_ta, SSTA_FL_LL_EXC); } if (c == eol[eol_state]) { if (++eol_state >= SM_EOL_LEN) { line_len = 0; eol_state = 0; } } else { eol_state = 0; if (c == eol[eol_state]) ++eol_state; } } #endif /* SS_CHECK_LINE_LEN */ if (eoh_state < SM_EOH_LEN) { if (c == eoh[eoh_state]) ++eoh_state; else { eoh_state = 0; if (c == eoh[eoh_state]) ++eoh_state; } if (TOLOWER(c) == rcvd[rcvd_state]) { if (++rcvd_state >= strlen(rcvd)) { if (++hops > ss_ctx->ssc_cnf.ss_cnf_maxhops) skip = true; rcvd_state = 0; #if SS_DATA_DEBUG if (ss_ctx->ssc_cnf.ss_cnf_debug > 2) { sm_snprintf(sbuf, sizeof(sbuf), "data: hops=%d\r\n" , hops); sm_io_write(smioerr, (uchar *)sbuf, strlen(sbuf), &b); sm_io_flush(smioerr); } #endif /* SS_DATA_DEBUG */ } } else { rcvd_state = 0; if (TOLOWER(c) == rcvd[rcvd_state]) ++rcvd_state; } /* put this into some function so it can be reused?? */ if (NULL == ss_ta->ssta_msgid && msgid_state >= 0) { if (TOLOWER(c) == msgid[msgid_state]) { if (++msgid_state >= (int)strlen(msgid)) msgid_state = MSGID_ST_NEW; } else { msgid_state = 0; if (TOLOWER(c) == msgid[msgid_state]) ++msgid_state; } } if (res_msgid_state >= 0) { if (TOLOWER(c) == res_msgid[res_msgid_state]) { if (++res_msgid_state >= (int)strlen(res_msgid)) msgid_state = MSGID_ST_NEW; } else { res_msgid_state = 0; if (TOLOWER(c) == res_msgid[res_msgid_state]) ++res_msgid_state; } } if (MSGID_ST_NEW == msgid_state) { if (NULL == ss_ta->ssta_msgid) ss_ta->ssta_msgid = sm_str_new(NULL, SMTPMSGIDSIZE, SMTPMAXSIZE); else sm_str_clr(ss_ta->ssta_msgid); if (ss_ta->ssta_msgid != NULL) msgid_state = MSGID_ST_FIRST; else msgid_state = MSGID_ST_NOMEM; } if (MSGID_ST_FIRST == msgid_state) msgid_state = MSGID_ST_SKIP; else if (MSGID_ST_SKIP == msgid_state && ISSPACE(c)) ; else if (MSGID_ST_SKIP == msgid_state && ISPRINT(c)) msgid_state = MSGID_ST_RDING; if (MSGID_ST_RDING == msgid_state) { if (ISSPACE(c)) { msgid_state = MSGID_ST_GOT; sm_str_term(ss_ta->ssta_msgid); } else if (ISPRINT(c)) SM_STR_PUT(ss_ta->ssta_msgid, c); } } if (c == eot[eot_state]) { if (++eot_state >= SM_EOT_LEN) break; } else { eot_state = 0; if (c == eot[eot_state]) ++eot_state; } #if 0 if (ss_ctx->ssc_cnf.ss_cnf_debug > 1 && r > 0) { sm_snprintf(sbuf, sizeof(sbuf), "eot_state: %d, c=%c\n" , eot_state, isprint(c) ? c : '_'); sm_io_write(smioerr, (uchar *)sbuf, strlen(sbuf), &b); } #endif /* 0 */ } SM_ASSERT(eot_state >= SM_EOT_LEN); /* really?? */ /* should we complain if skip is set?? */ if (hops > ss_ctx->ssc_cnf.ss_cnf_maxhops) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_data, ss_sess=%s, ss_ta=%s, too_many_hops=%u, max=%d" , ss_sess->ssse_id, ss_ta->ssta_id , hops , ss_ctx->ssc_cnf.ss_cnf_maxhops); sm_str_scopy(ss_sess->ssse_wr, "554 5.4.6 Too many hops\r\n"); ret = 554; goto error; } #if SS_CHECK_LINE_LEN /* should we complain if skip is set?? */ if (SSTA_IS_FLAG(ss_ta, SSTA_FL_LL_EXC)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_data, ss_sess=%s, ss_ta=%s, line_length_exceeded=%u, max=%u" , ss_sess->ssse_id, ss_ta->ssta_id , line_len, ss_sess->ssse_max_line_len); sm_str_scopy(ss_sess->ssse_wr, "554 5.5.0 Some line in mail too long\r\n"); ret = 554; goto error; } #endif /* SS_CHECK_LINE_LEN */ if (skip) { /* pmilter will not be contacted! */ if (!SSTA_IS_FLAG(ss_ta, SSTA_FL_DISCARD)) ret = 552; goto error; } /* write the current buffer */ bytes2write = f_p(*ss_sess->ssse_fp) - bufp; ret = cdb_write(cdb_ctx, ss_ta->ssta_dfp, bufp, bytes2write, &byteswritten); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_data, ss_sess=%s, ss_ta=%s, write_cdb=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_TEMP, SS_PHASE_DOT, true); goto error; } /* "else" not needed because of "goto error" above */ ss_ta->ssta_msg_sz_b += byteswritten; if (max_sz_kb > 0 && max_sz_kb < ss_ta->ssta_msg_sz_b / 1024) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_data, ss_sess=%s, ss_ta=%s, max_message_size=%lu, status=exceeded" , ss_sess->ssse_id, ss_ta->ssta_id , (ulong) max_sz_kb); sm_str_scopy(ss_sess->ssse_wr, "552 5.3.4 Maximum message size exceeded.\r\n"); ret = 552; skip = true; goto error; } #if MTA_USE_PMILTER ss_ta->ssta_rcpts_ok_orig = ss_ta->ssta_rcpts_ok; ss_ta->ssta_rcpts_tot_orig = ss_ta->ssta_rcpts_tot; ss_ta->ssta_hops = hops; ret = sspm_eob(ss_sess, bufp, bytes2write, &skip); if (sm_is_err(ret)) goto error; if (IS_SMTP_REPLY(ret) && SMTP_IS_REPLY_ERROR(ret)) goto error; # if MTA_USE_RSAD if (ret != SMTP_R_D_EOM) SSTA_CLR_FLAG(ss_sess->ssse_ta, SSTA_FL_RSAD); # endif if (SSC_IS_PMCAP(ss_ctx, SM_SCAP_PM_MSG_RC) && ss_ta->ssta_msg_acc.ssa_map_result == SM_ACC_FOUND && IS_SMTP_REPLY(ss_ta->ssta_msg_acc.ssa_reply_code) && SMTP_IS_REPLY_ERROR(ss_ta->ssta_msg_acc.ssa_reply_code)) { if (ss_ta->ssta_msg_acc.ssa_reply_text != NULL) { sm_str_clr(ss_sess->ssse_wr); sm_str_cat(ss_sess->ssse_wr, ss_ta->ssta_msg_acc.ssa_reply_text); } ret = ss_ta->ssta_msg_acc.ssa_reply_code; goto error; } if (skip) { if (!SSTA_IS_FLAG(ss_ta, SSTA_FL_DISCARD)) ret = 550; goto error; } # if SM_TEST_HDRMOD { sm_hdrmod_P sm_hdrmod; extern sm_cstr_P Hdr_pre, Hdr_app; /* HACK FOR TESTING */ sm_hdrmod = NULL; if (Hdr_pre != NULL || Hdr_app != NULL) ret = sm_hdrmod_new(&ss_ta->ssta_hdrmodhd, true, &sm_hdrmod); if (Hdr_pre != NULL && sm_hdrmod != NULL) { sm_hdrmod->sm_hm_hdr = Hdr_pre; sm_hdrmod->sm_hm_type = SM_HM_TYPE_PREPEND; Hdr_pre = NULL; if (Hdr_app != NULL) ret = sm_hdrmod_new(&ss_ta->ssta_hdrmodhd, true, &sm_hdrmod); } if (Hdr_app != NULL && sm_hdrmod != NULL) { sm_hdrmod->sm_hm_hdr = Hdr_app; sm_hdrmod->sm_hm_type = SM_HM_TYPE_APPEND; Hdr_app = NULL; } /* END HACK FOR TESTING */ } # endif /* SM_TEST_HDRMOD */ #endif /* MTA_USE_PMILTER */ /* data MUST now be safely stored.... */ if (ss_ta->ssta_dfp != NULL) { int flags; flags = SM_IO_CF_SYNC; #if SS_TEST if (Unsafe > 0) flags = SM_IO_CF_NONE; #endif #if SS_TEST if (Unsafe <= 1) { #endif ret = cdb_close(cdb_ctx, ss_ta->ssta_dfp, flags); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_data, ss_sess=%s, ss_ta=%s, close_cdb=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_TEMP, SS_PHASE_DOT, true); goto error; } ss_ta->ssta_dfp = NULL; #if SS_TEST } #endif } #if MTA_USE_PMILTER && MTA_USE_RSAD if (SSTA_IS_FLAG(ss_sess->ssse_ta, SSTA_FL_RSAD)) { ret = ss_rsad_set_replies(ss_ctx, ss_sess, ss_ta); if (SMTP_R_SSD == ret) goto errorreply; /* overall status: error -> don't accept */ if (ret != SMTP_OK) { (void) ss_rsad_send_replies(ss_ctx, ss_sess, ss_ta); goto errorreply; } } #endif /* MTA_USE_PMILTER && MTA_USE_RSAD */ ret = sm_s2q_1taid(ss_sess, ss_ctx->ssc_s2q_ctx); if (sm_is_err(ret)) { /* change error */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_data, ss_sess=%s, ss_ta=%s, sm_s2q_1taid=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); ret = SMTP_R_SSD; (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_DOT, true); goto error; } ret = sm_w4q2s_reply(ss_sess, TMO_W4Q2S, ss_ctx->ssc_s2q_ctx); if (sm_is_err(ret)) { /* change error */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 8, "sev=ERROR, func=ss_data, ss_sess=%s, ss_ta=%s, where=ss_data, sm_w4q2s_reply=%m" , ss_sess->ssse_id, ss_ta->ssta_id, ret); ret = SMTP_R_SSD; (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_DOT, true); goto error; } #if MTA_USE_PMILTER && MTA_USE_RSAD if (SSTA_IS_FLAG(ss_sess->ssse_ta, SSTA_FL_RSAD)) ret = ss_rsad_send_replies(ss_ctx, ss_sess, ss_ta); #endif if (SM_SUCCESS == ret) { SS_DATA_GOT_IT(ss_sess, ss_ta); } else { (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_DOT, false); goto error; } ss_ta->ssta_state = SSTA_ST_DOT; /* NONE ? */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 3, "sev=INFO, ss_sess=%s, ss_ta=%s, msgid=%#N, size=%lu, stat=0" , ss_sess->ssse_id, ss_ta->ssta_id , ss_ta->ssta_msgid, (ulong) ss_ta->ssta_msg_sz_b); ret = SMTP_OK; /* clear transaction data */ ss_ta_clr(ss_ta); ss_sess->ssse_nopcmds = 0; return ret; errorreply: (void) ss_crt_reply(ss_sess->ssse_wr, ret, SS_PHASE_DOT, true); error: /* Remove message */ if (ss_ta->ssta_dfp != NULL) { res = cdb_abort(cdb_ctx, ss_ta->ssta_dfp); ss_ta->ssta_dfp = NULL; SSTA_CLR_FLAG(ss_ta, SSTA_FL_CDB_EXISTS); } else if (SSTA_IS_FLAG(ss_ta, SSTA_FL_CDB_EXISTS)) res = cdb_unlink(cdb_ctx, ss_ta->ssta_id); else res = SM_SUCCESS; if (sm_is_err(res)) sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERR, 4, "sev=ERROR, func=ss_data, ss_sess=%s, ss_ta=%s, cdb_%s=%m" , ss_sess->ssse_id, ss_ta->ssta_id , SSTA_IS_FLAG(ss_ta, SSTA_FL_CDB_EXISTS) ? "unlink" : "abort" , res); if (SM_SUCCESS == ret) { /* pretend we got it: this works only for DISCARD !*/ SM_ASSERT(SSTA_IS_FLAG(ss_ta, SSTA_FL_DISCARD)); SS_DATA_GOT_IT(ss_sess, ss_ta); } (void) ss_ta_abort(ss_sess, ss_ta); /* returning an error will cause a session abort! */ return ret; } /* ** SS_TA_ZERO -- initialize transaction context (first time) ** ss_ta is NOT allocated, it is a local variable in ss_hdl_session() ** ** Parameters: ** ss_ta -- SMTP server transaction context ** ** Returns: ** SM_SUCCESS */ static sm_ret_T ss_ta_zero(ss_ta_P ss_ta) { sm_memzero(ss_ta, sizeof(*ss_ta)); return SM_SUCCESS; } /* ** SS_HDL_SESSION -- Session handling function stub. ** ** Parameters: ** ss_sess -- SMTP server session context ** must be freed after use. ** sessok -- is it ok to accept the session? ** ** Returns: ** usual return code */ sm_ret_T ss_hdl_session(ss_sess_P ss_sess, sm_ret_T sessok) { int r; sm_ret_T ret; ssize_t b; bool dontlog; char *buf; ss_ta_T ss_ta; /* should be malloc()ated? */ ss_ctx_P ss_ctx; SM_IS_SS_SESS(ss_sess); dontlog = false; ss_ctx = ss_sess->ssse_sctx; SM_IS_SS_CTX(ss_ctx); ss_ta_zero(&ss_ta); /* don't accept at all? */ if (SMTP_R_SSD == sessok) { (void) ss_crt_reply(ss_sess->ssse_wr, sessok, SS_PHASE_INIT, false); (void) ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, true); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 11, "sev=INFO, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, client_name=%C, stat=%d, text=%@T" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr , ss_sess->ssse_cltname , sessok, ss_sess->ssse_wr); if (sm_log_wouldlog(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, 11)) SSSE_SET_FLAG(ss_sess, SSSE_FL_CONN_LOGGED); goto err_cseid; } sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 13, "sev=DBG, func=ss_hdl_session, pid=%d, idx=%d, ss_sess=%s, hdl_session=%d" , (int) My_pid, ss_ctx->ssc_id, ss_sess->ssse_id, sessok); ret = ss_ta_clr(&ss_ta); if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_ERROR, 2, "sev=ERROR, func=ss_hdl_session, ss_sess=%s, ss_ta_clr=%m" , ss_sess->ssse_id, ret); (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_SSD, SS_PHASE_INIT, false); (void) ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, true); goto err_no_ta; } ss_sess->ssse_state = SSSE_ST_CONNECTED; ss_sess->ssse_ta = &ss_ta; /* Check for "illegal" pipelining. NOT for smtp over SSL! */ if (sm_io_getinfo(ss_sess->ssse_fp, SM_IO_IS_READABLE, NULL)) { bool iseof; /* ** RFC 2821 says only "SHOULD", not must wait for greeting. ** Hence this must be optional... (override per IP address?) */ iseof = false; #ifdef FIONREAD /* alternative: try to read one byte, session terminates */ r = 1; (void) ioctl(f_fd(*(ss_sess->ssse_fp)), FIONREAD, &r); iseof = (0 == r); #endif sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 3, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, status=%s_before_greeting" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr , iseof ? "EOF" : "data"); if (sm_log_wouldlog(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, 3)) SSSE_SET_FLAG(ss_sess, SSSE_FL_CONN_LOGGED); ret = sm_error_temp(SM_EM_SMTPS, SM_E_ILL_PIPE); dontlog = true; sm_str_clr(ss_sess->ssse_wr); if (!iseof && !SSSE_IS_CFLAG(ss_sess, SSSE_CFL_DATA_BEFORE_GREET)) { sm_str_scopy(ss_sess->ssse_wr, "421 client SHOULD wait for greeting\r\n"); goto errreply; } goto error; } /* create greeting */ sm_str_scopy(ss_sess->ssse_wr, "220 "); sm_str_cat(ss_sess->ssse_wr, ss_ctx->ssc_hostname); sm_str_scat(ss_sess->ssse_wr, " ESMTP " MTA_GREETING "\r\n"); ret = ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, true); if (ret != SMTP_OK) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 9, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, write_greeting=%m" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr, ret); if (sm_log_wouldlog(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, 9)) SSSE_SET_FLAG(ss_sess, SSSE_FL_CONN_LOGGED); goto error; } ss_sess->ssse_state = SSSE_ST_GREETED; if (!SSSE_IS_FLAG(ss_sess, SSSE_FL_CLIENT_RELAY)) { r = regexec(&ss_ctx->ssc_relayfrom, inet_ntoa(ss_sess->ssse_client), (size_t) 0, (regmatch_t *) NULL, 0); if (0 == r) SSSE_SET_FLAG(ss_sess, SSSE_FL_CLIENT_RELAY); } (void) ss_conn_log(ss_sess, 9, "ss_hdl_session"); /* ** Note: the ss_*() commands must return SM_SUCCESS almost always; ** a (fatal) error must only be returned if the session should ** be terminated. Maybe the functions can return a warning. */ while ((ret = ss_read_cmd(ss_sess)) == SMTP_OK) { /* ** RFC 2821: 4.1.1 Command Semantics and Syntax ** SMTP commands are character strings terminated by ** . The commands themselves are alphabetic characters ** terminated by if parameters follow and ** otherwise. (In the interest of improved interoperability, ** SMTP receivers are encouraged to tolerate trailing white ** space before the terminating .) */ r = sm_str_getlen(ss_sess->ssse_rd); if (0 == r) { if (sm_eof(ss_sess->ssse_fp)) { /* can't happen?? see ss_read_cmd() */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 6, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, status=EOF_without_QUIT" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr); goto error; } switch (errno) { case 0: break; case EINTR: continue; case ETIMEDOUT: case EAGAIN: default: sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, status=read_error, error=%m" , ss_sess->ssse_id , ss_sess->ssse_client.s_addr , sm_err_temp(errno)); if (sm_log_wouldlog(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, 3)) SSSE_SET_FLAG(ss_sess, SSSE_FL_CONN_LOGGED); goto error; } } (void) sm_str_rm_trail_sp(ss_sess->ssse_rd); buf = (char *)sm_str_getdata(ss_sess->ssse_rd); if (NULL == buf) { (void) ss_crt_reply(ss_sess->ssse_wr, SMTP_R_SSD, SS_PHASE_OTHER, false); (void) ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, true); goto errreply; } if (ss_ctx->ssc_cnf.ss_cnf_debug > 5) sm_io_write(smioerr, (uchar *)buf, r, &b); /* ** Try to find command and invoke corresponding function. ** Expected return codes: ** sm_is_err(): return an error to client and abort! ** SS_NO_REPLY: don't send a reply (only some commands) ** default: send reply stored in ss_sess->ssse_wr */ if (strcasecmp(buf, "quit") == 0) { sm_str_scopy(ss_sess->ssse_wr, "221 2.0.0 Bye\r\n"); ss_sess->ssse_state = SSSE_ST_QUIT; (void) ss_ta_abort(ss_sess, &ss_ta); } else if (strncasecmp(buf, "helo", 4) == 0) { ret = ss_ehlo(ss_sess, false); if (sm_is_err(ret)) goto errreply; } else if (strncasecmp(buf, "ehlo", 4) == 0) { ret = ss_ehlo(ss_sess, true); if (sm_is_err(ret)) goto errreply; } #if MTA_USE_TLS else if (strncasecmp(buf, "starttls", 8) == 0) { ret = ss_tls(ss_sess); if (sm_is_err(ret)) goto errreply; if (SS_NO_REPLY == ret) continue; } #endif /* MTA_USE_TLS */ #if MTA_USE_SASL else if (strncasecmp(buf, "auth ", 5) == 0) { ret = ss_auth(ss_sess); SS_CONN_LOG(ss_sess, 15, "ss_hdl_session"); sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 15, "sev=INFO, func=ss_hdl_session, ss_sess=%s, auth=%#x" , ss_sess->ssse_id, ret); if (sm_is_err(ret)) goto errreply; if (SS_NO_REPLY == ret) continue; } #endif /* MTA_USE_SASL */ else if (strncasecmp(buf, "mail", 4) == 0) { ret = ss_mail(ss_sess); if (sm_is_err(ret)) goto errreply; } else if (strncasecmp(buf, "rcpt", 4) == 0) { ret = ss_rcpt(ss_sess); if (sm_is_err(ret)) goto errreply; } else if (strcasecmp(buf, "data") == 0) { ret = ss_data(ss_sess); if (sm_is_err(ret)) goto errreply; TA_COUNT(ss_sess->ssse_idx)++; } else if (strcasecmp(buf, "rset") == 0) { ret = ss_rset(ss_sess); if (sm_is_err(ret)) goto errreply; } else if (strncasecmp(buf, "expn", 4) == 0 || strncasecmp(buf, "vrfy", 4) == 0) { ret = ss_nopcmd(ss_sess, false, "502 5.7.0 Nope\r\n"); if (sm_is_err(ret)) goto errreply; } else if (strncasecmp(buf, "noop", 4) == 0) { ret = ss_nopcmd(ss_sess, false, SS_R_OK); if (sm_is_err(ret)) goto errreply; } else if (strcasecmp(buf, "help") == 0) { sm_str_scopy(ss_sess->ssse_wr, "250 2.0.0 http://www.ietf.org/rfc/rfc2821.txt\r\n"); } else if (strncasecmp(buf, "connect", 7) == 0 || strncasecmp(buf, "post", 4) == 0 || strncasecmp(buf, "user", 4) == 0 || strncasecmp(buf, "get", 3) == 0) { /* todo: more proxy commands? */ sm_str_scopy(ss_sess->ssse_wr, "421 4.7.0 No HTTP\r\n"); ss_sess->ssse_state = SSSE_ST_QUIT; } else { /* increment error counter */ ret = ss_badcmd(ss_sess, false, "500 5.5.1 What?\r\n"); if (sm_is_err(ret)) goto errreply; /* maybe sleep... */ st_sleep(1); } #if 0 sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 2, "sev=INFO, func=ss_hdl_session, ss_sess=%s, reply=%S", ss_sess->ssse_id, ss_sess->ssse_wr); #endif if (SMTP_R_SSD == ret) ss_sess->ssse_state = SSSE_ST_QUIT; if (ret != SMTP_NO_REPLY) ret = ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, -1, false); else ret = SMTP_OK; sm_str_clr(ss_sess->ssse_wr); if (ret != SMTP_OK) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, status=write_error, ret=%m" , ss_sess->ssse_id , ss_sess->ssse_client.s_addr, ret); if (sm_log_wouldlog(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, 2)) SSSE_SET_FLAG(ss_sess, SSSE_FL_CONN_LOGGED); goto error; } if (SSSE_ST_QUIT == ss_sess->ssse_state) { /* close transaction and session */ RQST_COUNT(ss_sess->ssse_idx)++; goto done; } } if (SM_IO_EOF == ret) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_INFO, 11, "sev=INFO, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, status=EOF_without_QUIT, se_state=0x%x, ta_state=0x%x" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr , ss_sess->ssse_state, ss_ta.ssta_state); sm_str_clr(ss_sess->ssse_wr); goto error; } if (sm_is_err(ret)) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 2, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, status=read_error, ret=%m" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr, ret); sm_str_clr(ss_sess->ssse_wr); goto error; } done: /* same cleanup for now as in error */ errreply: if (sm_str_getlen(ss_sess->ssse_wr) > 0) (void) ss_reply(ss_sess, ss_sess->ssse_wr, ss_sess->ssse_fp, dontlog ? 14 : -1, true); error: if (sm_is_err(ret) && (r = sm_str_getlen(ss_sess->ssse_wr)) > 0 && !dontlog) { sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_WARN, 8, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, status=%@T, ret=%m" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr , ss_sess->ssse_wr, ret); } (void) ss_ta_abort(ss_sess, &ss_ta); err_no_ta: (void) sm_rcb_close_decn(ss_sess->ssse_rcb); err_cseid: ret = sm_s2q_cseid(ss_sess, ss_ctx->ssc_s2q_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_WARN, 2, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, sm_s2q_cseid=%m" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr, ret); } #if MTA_USE_PMILTER if (SSSE_IS_FLAG(ss_sess, SSSE_FL_PM_CALLED)) { ret = sm_s2m_cseid(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_WARN, 5, "sev=WARN, func=ss_hdl_session, ss_sess=%s, client_ipv4=%A, sm_s2m_cseid=%m" , ss_sess->ssse_id, ss_sess->ssse_client.s_addr, ret); } } #endif /* MTA_USE_PMILTER */ sm_log_write(ss_ctx->ssc_lctx, SS_LCAT_SERVER, SS_LMOD_SERVER, SM_LOG_DEBUG, 13, "sev=DBG, func=ss_hdl_session, ss_sess=%s, status=closing" , ss_sess->ssse_id); ss_sess_free(ss_sess); /* always success... */ return SM_SUCCESS; }