Skip to content

Commit

Permalink
mod_smtp_recipient_monitor: Add recipient-based greylisting for submi…
Browse files Browse the repository at this point in the history
…ssion.

Add a new module which can be leveraged by users to automatically help
prevent them from sending emails from the wrong identities or to the
wrong recipients.
  • Loading branch information
InterLinked1 committed Dec 26, 2024
1 parent 1f16af5 commit e97ce80
Show file tree
Hide file tree
Showing 8 changed files with 320 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Key features and capabilities include:

* Sieve filtering scripts and ManageSieve service
* `MailScript filtering engine <configs/.rules>`_ for flexible, custom, dynamic mail filtering rules (Sieve alternative)
* Intelligent sender/recipient analysis - prevent yourself from ever sending an email to the wrong people by mistake!

* Webmail client backend

Expand Down
21 changes: 20 additions & 1 deletion bbs/stringlist.c
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ void stringlist_empty(struct stringlist *list)
RWLIST_UNLOCK(list);
}

const char *stringlist_next(struct stringlist *list, struct stringitem **i)
const char *stringlist_next(const struct stringlist *list, struct stringitem **i)
{
struct stringitem *inext;
if (!*i) {
Expand Down Expand Up @@ -155,6 +155,25 @@ int stringlist_push(struct stringlist *list, const char *s)
return 0;
}

int stringlist_push_sorted(struct stringlist *list, const char *s)
{
struct stringitem *i;
char *sdup = strdup(s);

if (ALLOC_FAILURE(sdup)) {
return -1;
}

i = calloc(1, sizeof(*i));
if (ALLOC_FAILURE(i)) {
free(sdup);
return -1;
}
i->s = sdup;
RWLIST_INSERT_SORTALPHA(list, i, entry, s);
return 0;
}

int stringlist_push_tail(struct stringlist *list, const char *s)
{
struct stringitem *i;
Expand Down
3 changes: 2 additions & 1 deletion configs/modules.conf
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,9 @@ load = mod_smtp_filter.so
load = mod_smtp_filter_arc.so
load = mod_smtp_filter_dkim.so
load = mod_smtp_filter_dmarc.so
load = mod_smtp_mailing_lists.so
load = mod_spamassassin.so
load = mod_smtp_mailing_lists.so
load = mod_smtp_recipient_monitor.so
load = mod_webmail.so
load = net_imap.so
load = net_nntp.so
Expand Down
39 changes: 37 additions & 2 deletions include/net_smtp.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@
/* Mainly for message submission agents, not encrypted by default, but may use STARTTLS */
#define DEFAULT_SMTP_MSA_PORT 587

#define _smtp_reply(smtp, fmt, ...) \
bbs_debug(6, "%p <= " fmt, smtp, ## __VA_ARGS__); \
bbs_auto_fd_writef(smtp_node(smtp), smtp_node(smtp) ? smtp_node(smtp)->wfd : -1, fmt, ## __VA_ARGS__); \

/*! \brief Final SMTP response with this code */
#define smtp_resp_reply(smtp, code, subcode, reply) _smtp_reply(smtp, "%d %s %s\r\n", code, subcode, reply)
#define smtp_reply(smtp, code, status, fmt, ...) _smtp_reply(smtp, "%d %s " fmt "\r\n", code, #status, ## __VA_ARGS__)
#define smtp_reply_nostatus(smtp, code, fmt, ...) _smtp_reply(smtp, "%d " fmt "\r\n", code, ## __VA_ARGS__)

/*! \brief Non-final SMTP response (subsequent responses with the same code follow) */
#define smtp_reply0_nostatus(smtp, code, fmt, ...) _smtp_reply(smtp, "%d-" fmt "\r\n", code, ## __VA_ARGS__)

struct smtp_session;

void __attribute__ ((format (gnu_printf, 3, 4))) bbs_smtp_log(int level, struct smtp_session *smtp, const char *fmt, ...);
Expand Down Expand Up @@ -72,13 +84,33 @@ enum smtp_direction {
SMTP_DIRECTION_OUT = (1 << 2), /*!< Outgoing mail to another MTA */
};

/*!
* \note There are two different "filtering" APIs available,
* based on the smtp_filter_data (filters) and smtp_msg_process (message processors) structures.
* They are very similar and they probably could theoretically be combined.
* That said, there are a few major differences between them as they exist now,
* that should be considered when deciding which one to use.
*
* - The filtering API can modify/rewrite messages. The message processors do not.
* Thus, filtering here is probably also more akin to "milters" in standard POSIX MTAs.
* This is probably the most important difference. Message processors don't (and can't)
* rewrite messages, so they are probably more efficient to run if you don't need to modify the input.
* - Both can reject messages; message processors have more control over how they are rejected, though filters can quarantine the message.
* - Both can operation on incoming and outgoing messages to varying degrees.
* - In practice, message processors are used for "filtering engines" (ironically) - Sieve and MailScript.
* "Filters" are used for more traditional milter applications, such as SPF, DKIM, etc. - things that prepend headers to the message.
* - Other differences exist. Compare the structs and APIs to see what information is available.
*/

/* == SMTP filter callbacks - these receive a message and potentially modify it == */

struct smtp_filter_data {
struct smtp_session *smtp; /*!< SMTP session */
int inputfd; /*!< File descriptor from which message can be read */
const char *recipient; /*!< Recipient (RCPT TO). Only available for SMTP_DIRECTION_IN and SMTP_DIRECTION_SUBMIT, and if the scope is SMTP_SCOPE_INDIVIDUAL */
size_t size; /*!< Message length */
enum smtp_direction dir; /*!< Direction */
int received; /*!< Time that message was received */
time_t received; /*!< Time that message was received */
/* Duplicated fields: these are simply duplicated from smtp: */
struct bbs_node *node; /*!< Node */
const char *from; /*!< Envelope from */
Expand Down Expand Up @@ -200,16 +232,19 @@ int smtp_message_quarantinable(struct smtp_session *smtp);

struct smtp_msg_process {
/* Inputs */
struct smtp_session *smtp; /*!< SMTP session. Not originally included, so try to avoid using this! */
int fd; /*!< File descriptor of SMTP session */
struct mailbox *mbox; /*!< Mailbox (incoming only) */
struct bbs_user *user; /*!< BBS user (outgoing only) */
struct bbs_node *node; /*!< BBS node */
const char *datafile; /*!< Name of email data file */
FILE *fp; /*!< Email data file (used internally only) */
const char *from; /*!< Envelope from */
const struct stringlist *recipients; /*!< All envelope recipients (RCPT TO). Only available for SMTP_SCOPE_COMBINED/SMTP_DIRECTION_SUBMIT, not SMTP_DIRECTION_IN or SMTP_DIRECTION_OUT */
const char *recipient; /*!< Envelope to - only for INCOMING messages */
int size; /*!< Size of email */
int userid; /*!< User ID (outgoing only) */
unsigned int userid; /*!< User ID (outgoing only) */
enum smtp_direction dir; /*!< Full direction (IN, OUT, or SUBMIT) */
unsigned int direction:1; /*!< 0 = incoming, 1 = outgoing */
/* Outputs */
unsigned int bounce:1; /*!< Whether to send a bounce */
Expand Down
11 changes: 10 additions & 1 deletion include/stringlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ void stringlist_empty(struct stringlist *list);
* \returns Next string, or NULL if end of list reached.
* \note The returned string must not be modified, since it remains in the list.
*/
const char *stringlist_next(struct stringlist *list, struct stringitem **i);
const char *stringlist_next(const struct stringlist *list, struct stringitem **i);

/*!
* \brief Pop the most recently added item to a string list
Expand All @@ -101,6 +101,15 @@ char *stringlist_pop(struct stringlist *list);
*/
int stringlist_push(struct stringlist *list, const char *s);

/*!
* \brief Add an item to a stringlist, such that the stringlist remains sorted alphabetically
* \param list
* \param s String to add
* \note Assumes list is WRLOCKed
* \retval 0 on success, -1 on failure
*/
int stringlist_push_sorted(struct stringlist *list, const char *s);

/*!
* \brief Add an item to the end of a stringlist
* \param list
Expand Down
1 change: 1 addition & 0 deletions modules/mod_smtp_delivery_local.c
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@ static int do_local_delivery(struct smtp_session *smtp, struct smtp_response *re
smtp_mproc_init(smtp, &mproc);
mproc.size = (int) datalen;
mproc.recipient = recip_buf + 1; /* Without <> */
mproc.dir = SMTP_DIRECTION_IN;
mproc.direction = SMTP_MSG_DIRECTION_IN;
mproc.mbox = mbox;
mproc.userid = 0;
Expand Down
Loading

0 comments on commit e97ce80

Please sign in to comment.