diff --git a/smf-spf-tests-refuse.conf b/smf-spf-tests-refuse.conf index 5c19304..611bd67 100644 --- a/smf-spf-tests-refuse.conf +++ b/smf-spf-tests-refuse.conf @@ -1,5 +1,6 @@ WhitelistIP 127.0.0.0/8 RefuseFail on # (on|off) +RefuseSPFnone on # (on|off) SoftFail off # (on|off) AcceptTempError off TagSubject of # (on|off) diff --git a/smf-spf.c b/smf-spf.c index 83255a6..20575eb 100644 --- a/smf-spf.c +++ b/smf-spf.c @@ -51,6 +51,7 @@ #define SYSLOG_FACILITY LOG_MAIL #define SPF_TTL 3600 #define REFUSE_FAIL 1 +#define REFUSE_NONE 0 #define SOFT_FAIL 0 #define ACCEPT_PERMERR 1 #define TAG_SUBJECT 1 @@ -133,6 +134,7 @@ typedef struct config { STR *froms; STR *tos; int refuse_fail; + int refuse_none; int soft_fail; int accept_temperror; int tag_subject; @@ -372,6 +374,7 @@ static int load_config(void) { conf.sendmail_socket = strdup(OCONN); conf.syslog_facility = SYSLOG_FACILITY; conf.refuse_fail = REFUSE_FAIL; + conf.refuse_none = REFUSE_NONE; conf.soft_fail = SOFT_FAIL; conf.accept_temperror = ACCEPT_PERMERR; conf.tag_subject = TAG_SUBJECT; @@ -469,6 +472,10 @@ static int load_config(void) { conf.soft_fail = 1; continue; } + if (!strcasecmp(key, "refusespfnone") && !strcasecmp(val, "on")) { + conf.refuse_none = 1; + continue; + } if (!strcasecmp(key, "refusefail") && !strcasecmp(val, "off")) { conf.refuse_fail = 0; continue; @@ -774,6 +781,15 @@ static sfsistat smf_envfrom(SMFICTX *ctx, char **args) { SPF_request_set_env_from(spf_request, context->sender); if (SPF_request_query_mailfrom(spf_request, &spf_response)) { syslog(LOG_INFO, "SPF none: ip=%s, fqdn=%s, helo=%s, from=%s", context->addr, context->fqdn, context->helo, context->from); + if (status == SPF_RESULT_NONE && conf.refuse_none && !strstr(context->from, "<>")) { + char reject[2 * MAXLINE]; + snprintf(reject, sizeof(reject), "Sorry, we only accept mail from SPF enabled domains", context->sender); + if (spf_response) SPF_response_free(spf_response); + if (spf_request) SPF_request_free(spf_request); + if (spf_server) SPF_server_free(spf_server); + smfi_setreply(ctx, "550" , "5.7.1", reject); + return SMFIS_REJECT; + } if (cache && conf.spf_ttl) { mutex_lock(&cache_mutex); cache_put(context->key, conf.spf_ttl, SPF_RESULT_NONE); diff --git a/smf-spf.conf b/smf-spf.conf index d956e3e..e339ba2 100644 --- a/smf-spf.conf +++ b/smf-spf.conf @@ -36,6 +36,13 @@ WhitelistIP 192.168.0.0/16 #WhitelistTo @yourspamloverdomain.tld #WhitelistTo spamlover@yourdomain.tld +# Refuse e-Mail messages at SPF None results +# On empty senders this restriction will not be applied +# +# Default: off +# +#RefuseSPFNone off # (on|off) + # Refuse e-Mail messages at SPF Fail results (RFC-4408) # # Default: on diff --git a/tests/04-fulltest-refusenone.lua b/tests/04-fulltest-refusenone.lua new file mode 100644 index 0000000..d53b43c --- /dev/null +++ b/tests/04-fulltest-refusenone.lua @@ -0,0 +1,36 @@ +-- Copyright (c) 2009-2013, The Trusted Domain Project. All rights reserved. +mt.echo("SPF pass test") + +-- try to start the filter +mt.startfilter("./smf-spf", "-f", "-c","./smf-spf-tests-refuse.conf") + +-- try to connect to it +conn = mt.connect("inet:2424@127.0.0.1", 40, 0.25) +if conn == nil then + error("mt.connect() failed") +end + +-- send connection information +-- mt.negotiate() is called implicitly +mt.macro(conn, SMFIC_CONNECT, "j", "mta.name.local") +if mt.conninfo(conn, "localhost", "195.22.26.194") ~= nil then + error("mt.conninfo() failed") +end +if mt.getreply(conn) ~= SMFIR_CONTINUE then + error("mt.conninfo() unexpected reply") +end + +-- send envelope macros and sender data +-- mt.helo() is called implicitly +mt.macro(conn, SMFIC_MAIL, "i", "t-verify-spfnone") +if mt.mailfrom(conn, "") ~= nil then + error("mt.mailfrom() failed") +end + +if mt.getreply(conn) ~= SMFIR_REPLYCODE then + error("mt.mailfrom() unexpected reply") +end + +print ("received SMFIR_REJECT ") + +mt.disconnect(conn)