]> jfr.im git - solanum.git/blobdiff - authd/providers/blacklist.c
authd: misc fixes
[solanum.git] / authd / providers / blacklist.c
index ae52bbdcff856049359d3f3825aad863abe418c3..922d4b2ca7764b1e1f9c72089785b6082d11e04a 100644 (file)
@@ -39,6 +39,7 @@
 
 #include "authd.h"
 #include "provider.h"
+#include "notice.h"
 #include "stdinc.h"
 #include "dns.h"
 
@@ -57,11 +58,12 @@ struct blacklist
 {
        char host[IRCD_RES_HOSTLEN + 1];
        char reason[BUFSIZE];           /* Reason template (ircd fills in the blanks) */
-       unsigned char iptype;           /* IP types supported */
+       uint8_t iptype;                 /* IP types supported */
        rb_dlink_list filters;          /* Filters for queries */
 
        bool delete;                    /* If true delete when no clients */
        int refcount;                   /* When 0 and delete is set, remove this blacklist */
+       unsigned int hits;
 
        time_t lastwarning;             /* Last warning about garbage replies sent */
 };
@@ -72,7 +74,7 @@ struct blacklist_lookup
        struct blacklist *bl;           /* Blacklist we're checking */
        struct auth_client *auth;       /* Client */
        struct dns_query *query;        /* DNS query pointer */
-       
+
        rb_dlink_node node;
 };
 
@@ -80,7 +82,7 @@ struct blacklist_lookup
 struct blacklist_filter
 {
        filter_t type;                  /* Type of filter */
-       char filterstr[HOSTIPLEN];      /* The filter itself */
+       char filter[HOSTIPLEN];         /* The filter itself */
 
        rb_dlink_node node;
 };
@@ -101,8 +103,8 @@ static void blacklists_cancel(struct auth_client *);
 
 /* private interfaces */
 static void unref_blacklist(struct blacklist *);
-static struct blacklist *new_blacklist(char *, char *, bool, bool, rb_dlink_list *);
-static struct blacklist *find_blacklist(char *);
+static struct blacklist *new_blacklist(const char *, const char *, uint8_t, rb_dlink_list *);
+static struct blacklist *find_blacklist(const char *);
 static bool blacklist_check_reply(struct blacklist_lookup *, const char *);
 static void blacklist_dns_callback(const char *, bool, query_type, void *);
 static void initiate_blacklist_dnsquery(struct blacklist *, struct auth_client *);
@@ -111,7 +113,6 @@ static void timeout_blacklist_queries_event(void *);
 /* Variables */
 static rb_dlink_list blacklist_list = { NULL, NULL, 0 };
 static struct ev_entry *timeout_ev;
-static EVH timeout_blacklists;
 static int blacklist_timeout = 15;
 
 /* private interfaces */
@@ -136,11 +137,11 @@ unref_blacklist(struct blacklist *bl)
 }
 
 static struct blacklist *
-new_blacklist(char *name, char *reason, bool ipv4, bool ipv6, rb_dlink_list *filters)
+new_blacklist(const char *name, const char *reason, uint8_t iptype, rb_dlink_list *filters)
 {
        struct blacklist *bl;
 
-       if (name == NULL || reason == NULL || !(ipv4 || ipv6))
+       if (name == NULL || reason == NULL || iptype == 0)
                return NULL;
 
        if((bl = find_blacklist(name)) == NULL)
@@ -153,10 +154,7 @@ new_blacklist(char *name, char *reason, bool ipv4, bool ipv6, rb_dlink_list *fil
 
        rb_strlcpy(bl->host, name, IRCD_RES_HOSTLEN + 1);
        rb_strlcpy(bl->reason, reason, BUFSIZE);
-       if(ipv4)
-               bl->iptype |= IPTYPE_IPV4;
-       if(ipv6)
-               bl->iptype |= IPTYPE_IPV6;
+       bl->iptype = iptype;
 
        rb_dlinkMoveList(filters, &bl->filters);
 
@@ -166,7 +164,7 @@ new_blacklist(char *name, char *reason, bool ipv4, bool ipv6, rb_dlink_list *fil
 }
 
 static struct blacklist *
-find_blacklist(char *name)
+find_blacklist(const char *name)
 {
        rb_dlink_node *ptr;
 
@@ -212,7 +210,7 @@ blacklist_check_reply(struct blacklist_lookup *bllookup, const char *ipaddr)
                        continue;
                }
 
-               if (strcmp(cmpstr, filter->filterstr) == 0)
+               if (strcmp(cmpstr, filter->filter) == 0)
                        /* Match! */
                        return true;
        }
@@ -233,11 +231,13 @@ blacklist_dns_callback(const char *result, bool status, query_type type, void *d
 {
        struct blacklist_lookup *bllookup = (struct blacklist_lookup *)data;
        struct blacklist_user *bluser;
+       struct blacklist *bl;
        struct auth_client *auth;
 
        if (bllookup == NULL || bllookup->auth == NULL)
                return;
 
+       bl = bllookup->bl;
        auth = bllookup->auth;
        bluser = auth->data[PROVIDER_BLACKLIST];
        if(bluser == NULL)
@@ -246,21 +246,23 @@ blacklist_dns_callback(const char *result, bool status, query_type type, void *d
        if (result != NULL && status && blacklist_check_reply(bllookup, result))
        {
                /* Match found, so proceed no further */
+               bl->hits++;
                blacklists_cancel(auth);
-               reject_client(auth, PROVIDER_BLACKLIST, bllookup->bl->reason);
+               reject_client(auth, PROVIDER_BLACKLIST, bl->host, bl->reason);
                return;
        }
 
-       unref_blacklist(bllookup->bl);
+       unref_blacklist(bl);
+       cancel_query(bllookup->query);  /* Ignore future responses */
        rb_dlinkDelete(&bllookup->node, &bluser->queries);
        rb_free(bllookup);
 
        if(!rb_dlink_list_length(&bluser->queries))
        {
                /* Done here */
-               provider_done(auth, PROVIDER_BLACKLIST);
                rb_free(bluser);
                auth->data[PROVIDER_BLACKLIST] = NULL;
+               provider_done(auth, PROVIDER_BLACKLIST);
        }
 }
 
@@ -325,6 +327,29 @@ lookup_all_blacklists(struct auth_client *auth)
        bluser->timeout = rb_current_time() + blacklist_timeout;
 }
 
+static inline void
+delete_blacklist(struct blacklist *bl)
+{
+       if (bl->refcount > 0)
+               bl->delete = true;
+       else
+       {
+               rb_dlinkFindDestroy(bl, &blacklist_list);
+               rb_free(bl);
+       }
+}
+
+static void
+delete_all_blacklists(void)
+{
+       rb_dlink_node *ptr, *nptr;
+
+       RB_DLINK_FOREACH_SAFE(ptr, nptr, blacklist_list.head)
+       {
+               delete_blacklist(ptr->data);
+       }
+}
+
 /* public interfaces */
 static bool
 blacklists_start(struct auth_client *auth)
@@ -346,16 +371,20 @@ blacklists_start(struct auth_client *auth)
        return true;
 }
 
-/* This is called every time a provider is completed */
+/* This is called every time a provider is completed as long as we are marked not done */
 static void
 blacklists_initiate(struct auth_client *auth, provider_t provider)
 {
        struct blacklist_user *bluser = auth->data[PROVIDER_BLACKLIST];
 
-       if(bluser == NULL || is_provider_done(auth, PROVIDER_BLACKLIST) || rb_dlink_list_length(&bluser->queries))
+       lrb_assert(provider != PROVIDER_BLACKLIST);
+       lrb_assert(!is_provider_done(auth, PROVIDER_BLACKLIST));
+       lrb_assert(rb_dlink_list_length(&blacklist_list) > 0);
+
+       if(bluser == NULL || rb_dlink_list_length(&bluser->queries))
                /* Nothing to do */
                return;
-       else if(!is_provider_done(auth, PROVIDER_RDNS) && !is_provider_done(auth, PROVIDER_IDENT))
+       else if(!(is_provider_done(auth, PROVIDER_RDNS) && is_provider_done(auth, PROVIDER_IDENT)))
                /* Don't start until we've completed these */
                return;
        else
@@ -374,9 +403,11 @@ blacklists_cancel(struct auth_client *auth)
        RB_DLINK_FOREACH_SAFE(ptr, nptr, bluser->queries.head)
        {
                struct blacklist_lookup *bllookup = ptr->data;
-               rb_dlinkDelete(&bllookup->node, &bluser->queries);
-               unref_blacklist(bllookup->bl);
+
                cancel_query(bllookup->query);
+               unref_blacklist(bllookup->bl);
+
+               rb_dlinkDelete(&bllookup->node, &bluser->queries);
                rb_free(bllookup);
        }
 
@@ -404,19 +435,144 @@ blacklists_destroy(void)
                blacklists_cancel(auth);
        }
 
-       RB_DLINK_FOREACH_SAFE(ptr, nptr, blacklist_list.head)
+       delete_all_blacklists();
+       rb_event_delete(timeout_ev);
+}
+
+static void
+add_conf_blacklist(const char *key, int parc, const char **parv)
+{
+       rb_dlink_list filters = { NULL, NULL, 0 };
+       char *tmp, *elemlist = rb_strdup(parv[2]);
+       uint8_t iptype;
+
+       if(*elemlist == '*')
+               goto end;
+
+       for(char *elem = rb_strtok_r(elemlist, ",", &tmp); elem; elem = rb_strtok_r(NULL, ",", &tmp))
        {
-               bl = ptr->data;
-               if (bl->refcount > 0)
-                       bl->delete = true;
-               else
+               struct blacklist_filter *filter = rb_malloc(sizeof(struct blacklist_filter));
+               int dot_c = 0;
+               filter_t type = FILTER_LAST;
+               bool valid = true;
+
+               /* Check blacklist filter type and for validity */
+               for(char *c = elem; *c != '\0'; c++)
+               {
+                       if(*c == '.')
+                       {
+                               if(++dot_c > 3)
+                               {
+                                       warn_opers(L_CRIT, "addr_conf_blacklist got a bad filter (too many octets)");
+                                       valid = false;
+                                       break;
+                               }
+
+                               type = FILTER_ALL;
+                       }
+                       else if(!isdigit(*c))
+                       {
+                               warn_opers(L_CRIT, "addr_conf_blacklist got a bad filter (invalid character in blacklist filter: %c)", *c);
+                               valid = false;
+                               break;
+                       }
+               }
+
+               if(valid && dot_c > 0 && dot_c < 3)
+               {
+                       warn_opers(L_CRIT, "addr_conf_blacklist got a bad filter (insufficient octets)");
+                       valid = false;
+               }
+
+               if(!valid)
+               {
+                       rb_free(filter);
+                       continue;
+               }
+
+               filter->type = type;
+               rb_strlcpy(filter->filter, elem, sizeof(filter->filter));
+               rb_dlinkAdd(filter, &filter->node, &filters);
+       }
+
+end:
+       rb_free(elemlist);
+
+       iptype = atoi(parv[1]) & 0x3;
+       if(new_blacklist(parv[0], parv[3], iptype, &filters) == NULL)
+       {
+               rb_dlink_node *ptr, *nptr;
+
+               warn_opers(L_CRIT, "addr_conf_blacklist got a malformed blacklist");
+
+               RB_DLINK_FOREACH_SAFE(ptr, nptr, filters.head)
                {
                        rb_free(ptr->data);
-                       rb_dlinkDestroy(ptr, &blacklist_list);
+                       rb_dlinkDelete(ptr, &filters);
                }
        }
 }
 
+static void
+del_conf_blacklist(const char *key, int parc, const char **parv)
+{
+       struct blacklist *bl = find_blacklist(parv[0]);
+       if(bl == NULL)
+       {
+               warn_opers(L_CRIT, "BUG: tried to remove nonexistent blacklist %s", parv[0]);
+               return;
+       }
+
+       delete_blacklist(bl);
+}
+
+static void
+del_conf_blacklist_all(const char *key, int parc, const char **parv)
+{
+       delete_all_blacklists();
+}
+
+static void
+add_conf_blacklist_timeout(const char *key, int parc, const char **parv)
+{
+       int timeout = atoi(parv[0]);
+
+       if(timeout < 0)
+       {
+               warn_opers(L_CRIT, "BUG: blacklist timeout < 0 (value: %d)", timeout);
+               return;
+       }
+
+       blacklist_timeout = timeout;
+}
+
+static void
+blacklist_stats(uint32_t rid, char letter)
+{
+       rb_dlink_node *ptr;
+
+       RB_DLINK_FOREACH(ptr, blacklist_list.head)
+       {
+               struct blacklist *bl = ptr->data;
+
+               if(bl->delete)
+                       continue;
+
+               stats_result(rid, letter, "%s %hhu %u", bl->host, bl->iptype, bl->hits);
+       }
+
+       stats_done(rid, letter);
+}
+
+struct auth_opts_handler blacklist_options[] =
+{
+       { "rbl", 4, add_conf_blacklist },
+       { "rbl_del", 1, del_conf_blacklist },
+       { "rbl_del_all", 0, del_conf_blacklist_all },
+       { "rbl_timeout", 1, add_conf_blacklist_timeout },
+       { NULL, 0, NULL },
+};
+
 struct auth_provider blacklist_provider =
 {
        .id = PROVIDER_BLACKLIST,
@@ -425,4 +581,6 @@ struct auth_provider blacklist_provider =
        .start = blacklists_start,
        .cancel = blacklists_cancel,
        .completed = blacklists_initiate,
+       .opt_handlers = blacklist_options,
+       .stats_handler = { 'B', blacklist_stats },
 };