view src/context.h @ 465:79e944269c0b

SA needs original rfc5321 envelope from to do proper spf checking. Remove some debug code.
author Carl Byington <carl@five-ten-sg.com>
date Thu, 25 Apr 2019 09:35:53 -0700
parents 428de28b34b7
children 5209e92b4885
line wrap: on
line source

/*

Copyright (c) 2007 Carl Byington - 510 Software Group, released under
the GPL version 3 or any later version at your choice available at
http://www.gnu.org/licenses/gpl-3.0.txt

*/

#ifndef context_include
#define context_include

#ifdef NS_PACKETSZ
    #define maxdnslength (NS_PACKETSZ*8)
#else
    #define maxdnslength 1000
#endif

enum status {oksofar,       // not rejected yet
             whitesofar,    // probably whitelisted but require_signed or unsigned_black dkim requirements might change that
             white,         // whitelisted
             black,         // blacklisted
             reject};       // rejected by a dns list

class DKIM;
class DNSBL;
class DNSWL;
class CONTEXT;
class VERIFY;
class SMTP;
class WHITELISTER;
class DELAYWHITE;
class recorder;
class mlfiPriv;

typedef map<const char *, const char *, ltstr>      string_map;
typedef set<int>                                    int_set;
typedef set<int32_t>                                int32_t_set;
typedef int32_t_set *                               int32_t_set_p;
typedef set<uint32_t>                               uint32_t_set;
typedef uint32_t_set *                              uint32_t_set_p;
typedef list<SMTP *>                                smtp_list;
typedef DKIM *                                      DKIMP;
typedef DNSBL *                                     DNSBLP;
typedef DNSWL *                                     DNSWLP;
typedef VERIFY *                                    VERIFYP;
typedef WHITELISTER *                               WHITELISTERP;
typedef DELAYWHITE *                                DELAYWHITEP;
typedef map<const char *, DKIMP, ltstr>             dkimp_map;
typedef list<DNSBLP>                                dnsblp_list;
typedef map<const char *, DNSBLP, ltstr>            dnsblp_map;
typedef list<DNSWLP>                                dnswlp_list;
typedef map<const char *, DNSWLP, ltstr>            dnswlp_map;
typedef CONTEXT *                                   CONTEXTP;
typedef list<CONTEXTP>                              context_list;
typedef map<const char *, CONTEXTP, ltstr>          context_map;
typedef map<const char *, uint32_t, ltstr>          ns_mapper;  // name to ipv4 address
typedef map<const char *, int, ltstr>               rates;
typedef map<const char *, uint32_t_set_p, ltstr>    auth_addresses;
typedef map<const char *, time_t,  ltstr>           autowhite_sent;
typedef map<const char *, VERIFYP, ltstr>           verify_map;
typedef map<const char *, WHITELISTERP, ltstr>      whitelister_map;
typedef list<DELAYWHITEP>                           delay_whitelist;

class SMTP {
    static const int maxlen = 1000;
    static const int qlen = 20;
    int     fd;
    bool    error;
    time_t  stamp;
    char    efrom[maxlen];  // last envelope from sent on this socket
    int     pending;        // unread bytes in buffer, not including the null terminator
    char    buffer[maxlen];
    char    queueid[qlen];  // last queueid for logging
public:
    SMTP(int f)             {fd = f; error = false; now(); efrom[0] = '\0'; queueid[0] = '\0'; init();};
    ~SMTP()                 {if (!error) quit(); closefd();};
    void    init()          {pending = 0; buffer[0] = '\0';};
    void    append(const char *c)   {strncat(buffer, c, max(0, maxlen-1-(int)strlen(c)));};
    bool    err()           {return error;};
    void    now()           {stamp = time(NULL);};
    time_t  get_stamp()     {return stamp;};
    int     get_fd()        {return fd;};
    void    set_id(const char *id)  {strncpy(queueid, id, qlen); queueid[qlen-1] = '\0';};
    int     writer();
    int     reader();
    int     read_line();
    int     read_response();
    void    flush_line(int r);
    int     cmd(const char *c);
    int     helo();
    int     rset();
    int     from(const char *f);
    int     rcpt(const char *t);
    int     quit();
    void    closefd();
    void    log(const char *m, int v);
    void    log(const char *m, const char *v);
};

class VERIFY {
    const char      *host;      // host to be used to verify recipient addresses
    time_t          last_err;   // time of last socket error
    pthread_mutex_t mutex;      // protect the lists of sockets and timestamps
    smtp_list       connections;// open sockets, ready to be used
public:
    VERIFY(const char *h);
    void    log(const char *m, const char *q, const char *v);
    void    closer();           // if the oldest socket is ancient, close it
    SMTP    *get_connection(const char *queueid);
    void    put_connection(SMTP *conn);
    bool    ok(const char *queueid, const char *from, const char *to);
};

class WHITELISTER {
    const char      *fn;        // file to use
    int             days;       // how long do we keep entries
    pthread_mutex_t mutex;      // protect the flag and map
    time_t          loaded;     // when we loaded this file
    bool            need;       // force writing on new entries
    autowhite_sent  rcpts;      // recipient map to remember when we sent them mail
public:
    WHITELISTER(const char *f, int d);
    void    merge();
    void    writer();           // dump any changes back to the file
    void    sent(const char *to);
    bool    is_white(const char *from); // should we white list this sender (did we send them anything recently)
    int     get_days()      {return days;};
    void    set_days(int d) {days = d;};
};

class DELAYWHITE {
    const char     *loto;
    WHITELISTERP    w;
    CONTEXTP        con;
public:
    DELAYWHITE(const char *loto_, WHITELISTERP w_, CONTEXTP con_);
    const char     *get_loto() {return loto;};
    WHITELISTERP    get_w()    {return w;};
    CONTEXTP        get_con()  {return con;};
};

struct DKIM {
    const char *action;
    const char *signer;
    const char *extraspf;
public:
    DKIM(const char *action_, const char *signer_, const char*extraspf_);
};

struct DNSBL {
    const char    *name;    // nickname for this dns based list
    const char    *suffix;  // blacklist suffix like blackholes.five-ten-sg.com
    const char    *message; // error message with one or two %s operators for the ip address replacement
    DNSBL(const char *n, const char *s, const char *m);
    bool operator==(const DNSBL &rhs);
};

struct DNSWL {
    const char    *name;    // nickname for this dns based list
    const char    *suffix;  // whitelist suffix like list.dnswl.org
    int            level;   // matches 127.0.x.y where y >= level
    DNSWL(const char *n, const char *s, const int l);
    bool operator==(const DNSWL &rhs);
};

class CONTEXT {
    CONTEXTP        parent;
    const char *    name;
    context_map     children;               // map child context names to their contexts
    string_set      env_to;                 // this context applies to these envelope recipients
    const char *    verify_host;            // use this smtp host to verify email addresses
    VERIFYP         verifier;               // pointer to the verifier structure
    const char *    generic_regx;           // pointer to generic regular expression
    const char *    generic_message;        // pointer to generic message with one %s
    regex_t         generic_pattern;        // compiled regex pattern
    const char *    white_regx;             // pointer to whitelist regular expression
    regex_t         white_pattern;          // compiled regex pattern
    const char *    autowhite_file;         // file to use for automatic whitelisting
    WHITELISTERP    whitelister;            // pointer to the auto whitelister structure
    string_map      env_from;               // map senders to white/black/unknown
    context_map     env_from_context;       // map senders to a child context
    const char *    env_from_default;       // default value for senders that are not found in the map white/black/unknown/inherit
    bool            content_filtering;      //
    const char *    content_suffix;         // for url body filtering based on ip addresses of hostnames in the body
    const char *    content_message;        // ""
    const char *    uribl_suffix;           // for uribl body filtering based on hostnames in the body
    const char *    uribl_message;          // ""
    string_set      content_host_ignore;    // hosts to ignore for content sbl checking
    string_set      content_tlds;           // names that are tlds
    string_set      content_tldwilds;       // *.names that are tlds
    string_set      content_tldnots;        // names that are not tlds
    string_set      html_tags;              // set of valid html tags
    int             host_limit;             // limit on host names
    const char *    host_limit_message;     // error message for excessive host names
    bool            host_random;            // pick a random selection of host names rather than error for excessive hosts
    int             tag_limit;              // limit on bad html tags
    const char *    tag_limit_message;      // error message for excessive bad html tags
    int             spamassassin_limit;     // max score from spamassassin
    bool            require_match;          // require matching context filtering context
    bool            require_rdns;           // require proper rdns on client ip
    bool            dcc_greylist;           // should we do dcc greylisting?
    int             dcc_bulk_threshold;     // off = 0, many = 1000
    dkimp_map       dkim_from_names;        // map header from domains to dkim constraints
    string_map      dkim_signer_names;      // map dkim signers to actions
    dnsblp_map      dnsbl_names;            // name to dnsbl mapping for lists that are available in this context and children
    dnsblp_list     dnsbl_list;             // list of dnsbls to be used in this context
    bool            dnsbl_list_parsed;      // true iff we have actually parsed a dnsbl_list
    dnswlp_map      dnswl_names;            // name to dnswl mapping for lists that are available in this context and children
    dnswlp_list     dnswl_list;             // list of dnswls to be used in this context
    bool            dnswl_list_parsed;      // true iff we have actually parsed a dnswl_list
    int             default_rate_limit;     // if not specified per user
    int             default_address_limit;  // if not specified per user
    int             daily_rate_multiple;    // daily multiplier applied to hourly rate
    int             daily_address_multiple; // daily multiplier applied to hourly rate
    rates           rcpt_per_hour;          // per user limits on number of recipients per hour
    rates           addresses_per_hour;     // per user limits on number of unique ip address connections per hour


public:
    CONTEXT(CONTEXTP parent_, const char *name_);
    ~CONTEXT();
    CONTEXTP    get_parent()                                {return parent;};
    bool        is_parent(CONTEXTP p);      // is p a parent of this?
    const char* get_full_name(char *buffer, int size);
    void        add_context(CONTEXTP child)                 {children[child->name] = child;};
    bool        allow_env_to(const char *to)                {return (parent) ? parent->cover_env_to(to) : true;};
    bool        cover_env_to(const char *to);

    void        set_verifier(VERIFYP v)                     {verifier    = v;};
    void        set_verify(const char *host)                {verify_host = host;};
    const char* get_verify()                                {return verify_host;};
    VERIFYP     find_verify(const char *to);


    void        set_whitelister(WHITELISTERP v)             {whitelister    = v;};
    void        set_autowhite(const char *fn)               {autowhite_file = fn;};
    const char* get_autowhite()                             {return autowhite_file;};
    WHITELISTERP find_autowhite(const char *from, const char *to);

    void        set_default_rate_limit(int limit)               {default_rate_limit = limit;};
    void        set_default_address_limit(int limit)            {default_address_limit = limit;};
    void        set_daily_rate_multiple(int multiple)           {daily_rate_multiple = multiple;};
    void        set_daily_address_multiple(int multiple)        {daily_address_multiple = multiple;};
    void        add_rate_limit(const char *user, int limit)     {rcpt_per_hour[user] = limit;};
    void        add_address_limit(const char *user, int limit)  {addresses_per_hour[user] = limit;};
    int         find_rate_limit(const char *user);              // recipients per hour/day
    int         find_address_limit(const char *user);           // unique ip address connections per hour/day
    bool        is_unauthenticated_limited(const char *user);
    int         get_daily_rate_multiple()                       {return daily_rate_multiple;};
    int         get_daily_address_multiple()                    {return daily_address_multiple;};

    void        add_to(const char *to)                            {env_to.insert(to);};
    void        add_from(const char *from, const char *status)    {env_from[from] = status;};
    void        add_from_context(const char *from, CONTEXTP con)  {env_from_context[from] = con;};
    void        set_from_default(const char *status)              {env_from_default = status;};
    const char* find_from(const char *from, bool update_white = false, const char *queueid = NULL);
    CONTEXTP    find_context(const char *from);
    CONTEXTP    find_from_context_name(const char *name);

    void        set_content_filtering(bool filter)          {content_filtering = filter;      };
    void        set_content_suffix(const char *suffix)      {content_suffix    = suffix;      };
    void        set_content_message(const char *message)    {content_message   = message;     };
    void        set_uribl_suffix(const char *suffix)        {uribl_suffix      = suffix;      };
    void        set_uribl_message(const char *message)      {uribl_message     = message;     };
    void        add_ignore(const char *host)                {content_host_ignore.insert(host);};
    void        add_tld(const char *tld)                    {content_tlds.insert(tld);        };
    void        add_tldwild(const char *tld)                {content_tldwilds.insert(tld);    };
    void        add_tldnot(const char *tld)                 {content_tldnots.insert(tld);     };

    void        set_host_limit(int limit)                   {host_limit         = limit;  };
    void        set_host_message(const char *message)       {host_limit_message = message;};
    void        set_host_random(bool random)                {host_random        = random; };
    void        set_spamassassin_limit(int limit)           {spamassassin_limit = limit;  };
    void        set_tag_limit(int limit)                    {tag_limit          = limit;  };
    void        set_tag_message(const char *message)        {tag_limit_message  = message;};
    void        add_tag(const char *tag)                    {html_tags.insert(tag);       };

    const char *find_dkim_signer(const char *name);
    void        add_dkim_signer(const char *signer, const char *action)
                                                            {dkim_signer_names[signer] = action;};
    DKIMP       find_dkim_from(const char *name);
    void        add_dkim_from(const char *from, const char *action, const char *signer, const char *extraspf)
                                                            {dkim_from_names[from] = new DKIM(action,signer,extraspf);};

    void        add_dnsbl(const char *name, DNSBLP dns)     {dnsbl_names[name] = dns;  };
    void        add_dnsbl(DNSBLP dns)                       {dnsbl_list.push_back(dns);};
    DNSBLP      find_dnsbl(const char *name);
    void        set_dnsbll_parsed()                         {dnsbl_list_parsed = true;};

    void        add_dnswl(const char *name, DNSWLP dns)     {dnswl_names[name] = dns;  };
    void        add_dnswl(DNSWLP dns)                       {dnswl_list.push_back(dns);};
    DNSWLP      find_dnswl(const char *name);
    void        set_dnswll_parsed()                         {dnswl_list_parsed = true;};

    bool        set_white(const char *regx);
    bool        white_match(const char *from);

    bool        set_generic(const char *regx, const char *msg);
    const char* generic_match(const char *client);

    void        set_require(bool r)                         {require_match      = r; };
    void        set_requirerdns(bool r)                     {require_rdns       = r; };
    void        set_grey(bool g)                            {dcc_greylist       = g; };
    void        set_bulk(int b)                             {dcc_bulk_threshold = b; };

    bool            get_content_filtering()                 {return content_filtering;                      };
    bool            get_require()                           {return content_filtering && require_match;     };
    bool            get_requirerdns()                       {return require_rdns;                           };
    bool            get_grey()                              {return content_filtering && dcc_greylist;      };
    int             get_bulk()                              {return (content_filtering) ? dcc_bulk_threshold : 0;};
    int             get_host_limit()                        {return (content_filtering) ? host_limit         : 0;};
    bool            get_host_random()                       {return (content_filtering) ? host_random        : 0;};
    int             get_spamassassin_limit()                {return (content_filtering) ? spamassassin_limit : 0;};
    const char*     get_content_suffix();
    const char*     get_content_message();
    const char*     get_uribl_suffix();
    const char*     get_uribl_message();
    string_set&     get_content_host_ignore();
    string_set&     get_content_tlds();
    string_set&     get_content_tldwilds();
    string_set&     get_content_tldnots();
    string_set&     get_html_tags();
    dnsblp_list&    get_dnsbl_list();
    dnswlp_list&    get_dnswl_list();

    void        log(const char *queueid, const char *msg, const char *v);
    const char *extra_spf_data(const char *signers);
    bool        in_signing_set(const char *s, const char *signers);
    void        replace(char *buf, char *p, int nn, const char *what);
    bool        resolve_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *extraspf);
    bool        resolve_one_spf(const char *from, uint32_t ip, mlfiPriv *priv, const char *extraspf, int level = 0);
    const char *acceptable_content(bool local_source, recorder &memory, int score, int bulk, const char *queueid, string_set &signers, const char *from, mlfiPriv *priv, string& msg);
    bool        ignore_host(const char *host);

    void        dump(bool isdefault, bool &spamass, int level = 0);
};


struct CONFIG {
    // the only mutable stuff once it has been loaded from the config file
    int         reference_count;    // protected by the global config_mutex
    // all the rest is constant after loading from the config file
    int             generation;
    time_t          load_time;
    string_set      config_files;
    context_list    contexts;       // owns all the contexts, not just top level contexts
    context_map     env_to;         // map recipient to a filtering context
    CONTEXTP        default_context;// for env_to values that don't have their own specific filtering context
    // the default context is also used for some of the content filtering values

    CONFIG();
    ~CONFIG();
    void        add_context(CONTEXTP con);
    void        add_to(const char *to, CONTEXTP con);
    CONTEXTP    find_context(const char *to);
    void        dump();
};


extern const char *token_asterisk;
extern const char *token_autowhite;
extern const char *token_bang;
extern const char *token_black;
extern const char *token_content;
extern const char *token_context;
extern const char *token_dccbulk;
extern const char *token_dccfrom;
extern const char *token_dccgrey;
extern const char *token_dccto;
extern const char *token_default;
extern const char *token_dnsbl;
extern const char *token_dnsbll;
extern const char *token_dnswl;
extern const char *token_dnswll;
extern const char *token_envfrom;
extern const char *token_envto;
extern const char *token_filter;
extern const char *token_generic;
extern const char *token_host_limit;
extern const char *token_html_limit;
extern const char *token_html_tags;
extern const char *token_ignore;
extern const char *token_include;
extern const char *token_inherit;
extern const char *token_lbrace;
extern const char *token_mailhost;
extern const char *token_many;
extern const char *token_no;
extern const char *token_off;
extern const char *token_ok;
extern const char *token_ok2;
extern const char *token_on;
extern const char *token_period;
extern const char *token_rate;
extern const char *token_rbrace;
extern const char *token_require;
extern const char *token_requirerdns;
extern const char *token_semi;
extern const char *token_soft;
extern const char *token_spamassassin;
extern const char *token_substitute;
extern const char *token_tld;
extern const char *token_unknown;
extern const char *token_uribl;
extern const char *token_verify;
extern const char *token_white;
extern const char *token_white_regex;
extern const char *token_yes;
extern const char *token_dkim_signer;
extern const char *token_dkim_from;
extern const char *token_signed_white;
extern const char *token_signed_black;
extern const char *token_unsigned_black;
extern const char *token_require_signed;
extern const char *token_myhostname;

extern pthread_mutex_t verifier_mutex;     // protect the verifier map
extern pthread_mutex_t whitelister_mutex;  // protect the

void discard(string_set &s);
const char* register_string(string_set &s, const char *name);
const char* register_string(const char *name);
void  clear_strings();
bool  load_conf(CONFIG &dc, const char *fn);
void* verify_closer(void *arg);
void* whitelister_writer(void *arg);
void  token_init();

#endif