]> jfr.im git - solanum.git/blobdiff - ircd/authproc.c
support RSFNC indicating type of FNC (e.g. FORCE vs REGAIN) (#406)
[solanum.git] / ircd / authproc.c
index a07db671eb32aa4b1e064108f136baef7d728512..a8a5d0db81a4b2974450749a804944fbd69de8ee 100644 (file)
@@ -4,7 +4,7 @@
  *
  *  Copyright (C) 2005 Aaron Sethman <androsyn@ratbox.org>
  *  Copyright (C) 2005-2012 ircd-ratbox development team
- *  Copyright (C) 2016 William Pitcock <nenolod@dereferenced.org>
+ *  Copyright (C) 2016 Ariadne Conill <ariadne@dereferenced.org>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -67,7 +67,7 @@ uint32_t cid;
 static rb_dictionary *cid_clients;
 static struct ev_entry *timeout_ev;
 
-rb_dictionary *bl_stats;
+rb_dictionary *dnsbl_stats = NULL;
 
 rb_dlink_list opm_list;
 struct OPMListener opm_listeners[LISTEN_LAST];
@@ -88,24 +88,19 @@ static int
 start_authd(void)
 {
        char fullpath[PATH_MAX + 1];
-#ifdef _WIN32
-       const char *suffix = ".exe";
-#else
-       const char *suffix = "";
-#endif
+
        if(authd_path == NULL)
        {
-               snprintf(fullpath, sizeof(fullpath), "%s%cauthd%s", ircd_paths[IRCD_PATH_LIBEXEC], RB_PATH_SEPARATOR, suffix);
+               snprintf(fullpath, sizeof(fullpath), "%s/authd", ircd_paths[IRCD_PATH_LIBEXEC]);
 
                if(access(fullpath, X_OK) == -1)
                {
-                       snprintf(fullpath, sizeof(fullpath), "%s%cbin%cauthd%s",
-                                ConfigFileEntry.dpath, RB_PATH_SEPARATOR, RB_PATH_SEPARATOR, suffix);
+                       snprintf(fullpath, sizeof(fullpath), "%s/bin/authd", ConfigFileEntry.dpath);
                        if(access(fullpath, X_OK) == -1)
                        {
                                ierror("Unable to execute authd in %s or %s/bin",
                                        ircd_paths[IRCD_PATH_LIBEXEC], ConfigFileEntry.dpath);
-                               sendto_realops_snomask(SNO_GENERAL, L_ALL,
+                               sendto_realops_snomask(SNO_GENERAL, L_NETWIDE,
                                                       "Unable to execute authd in %s or %s/bin",
                                                       ircd_paths[IRCD_PATH_LIBEXEC], ConfigFileEntry.dpath);
                                return 1;
@@ -119,9 +114,6 @@ start_authd(void)
        if(cid_clients == NULL)
                cid_clients = rb_dictionary_create("authd cid to uid mapping", rb_uint32cmp);
 
-       if(bl_stats == NULL)
-               bl_stats = rb_dictionary_create("blacklist statistics", rb_strcasecmp);
-
        if(timeout_ev == NULL)
                timeout_ev = rb_event_addish("timeout_dead_authd_clients", timeout_dead_authd_clients, NULL, 1);
 
@@ -130,11 +122,12 @@ start_authd(void)
        if(authd_helper == NULL)
        {
                ierror("Unable to start authd helper: %s", strerror(errno));
-               sendto_realops_snomask(SNO_GENERAL, L_ALL, "Unable to start authd helper: %s", strerror(errno));
+               sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "Unable to start authd helper: %s", strerror(errno));
                return 1;
        }
+
        ilog(L_MAIN, "authd helper started");
-       sendto_realops_snomask(SNO_GENERAL, L_ALL, "authd helper started");
+       sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "authd helper started");
        rb_helper_run(authd_helper);
        return 0;
 }
@@ -155,14 +148,14 @@ str_to_cid(const char *str)
 }
 
 static inline struct Client *
-cid_to_client(uint32_t cid, bool delete)
+cid_to_client(uint32_t ncid, bool del)
 {
        struct Client *client_p;
 
-       if(delete)
-               client_p = rb_dictionary_delete(cid_clients, RB_UINT_TO_POINTER(cid));
+       if(del)
+               client_p = rb_dictionary_delete(cid_clients, RB_UINT_TO_POINTER(ncid));
        else
-               client_p = rb_dictionary_retrieve(cid_clients, RB_UINT_TO_POINTER(cid));
+               client_p = rb_dictionary_retrieve(cid_clients, RB_UINT_TO_POINTER(ncid));
 
        /* If the client's not found, that's okay, it may have already gone away.
         * --Elizafox */
@@ -171,14 +164,14 @@ cid_to_client(uint32_t cid, bool delete)
 }
 
 static inline struct Client *
-str_cid_to_client(const char *str, bool delete)
+str_cid_to_client(const char *str, bool del)
 {
-       uint32_t cid = str_to_cid(str);
+       uint32_t ncid = str_to_cid(str);
 
-       if(cid == 0)
+       if(ncid == 0)
                return NULL;
 
-       return cid_to_client(cid, delete);
+       return cid_to_client(ncid, del);
 }
 
 static void
@@ -204,7 +197,10 @@ cmd_notice_client(int parc, char **parv)
 {
        struct Client *client_p;
 
-       if((client_p = str_cid_to_client(parv[1], false)) == NULL)
+       if ((client_p = str_cid_to_client(parv[1], false)) == NULL)
+               return;
+
+       if (IsAnyDead(client_p))
                return;
 
        sendto_one_notice(client_p, ":%s", parv[2]);
@@ -228,23 +224,23 @@ cmd_oper_warn(int parc, char **parv)
        switch(*parv[1])
        {
        case 'D':       /* Debug */
-               sendto_realops_snomask(SNO_DEBUG, L_ALL, "authd debug: %s", parv[2]);
+               sendto_realops_snomask(SNO_DEBUG, L_NETWIDE, "authd debug: %s", parv[2]);
                idebug("authd: %s", parv[2]);
                break;
        case 'I':       /* Info */
-               sendto_realops_snomask(SNO_GENERAL, L_ALL, "authd info: %s", parv[2]);
+               sendto_realops_snomask(SNO_DEBUG, L_NETWIDE, "authd info: %s", parv[2]);
                inotice("authd: %s", parv[2]);
                break;
        case 'W':       /* Warning */
-               sendto_realops_snomask(SNO_GENERAL, L_ALL, "authd WARNING: %s", parv[2]);
+               sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "authd WARNING: %s", parv[2]);
                iwarn("authd: %s", parv[2]);
                break;
        case 'C':       /* Critical (error) */
-               sendto_realops_snomask(SNO_GENERAL, L_ALL, "authd CRITICAL: %s", parv[2]);
+               sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "authd CRITICAL: %s", parv[2]);
                ierror("authd: %s", parv[2]);
                break;
        default:        /* idk */
-               sendto_realops_snomask(SNO_GENERAL, L_ALL, "authd sent us an unknown oper notice type (%s): %s", parv[1], parv[2]);
+               sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "authd sent us an unknown oper notice type (%s): %s", parv[1], parv[2]);
                ilog(L_MAIN, "authd unknown oper notice type (%s): %s", parv[1], parv[2]);
                break;
        }
@@ -277,14 +273,14 @@ parse_authd_reply(rb_helper * helper)
        ssize_t len;
        int parc;
        char buf[READBUF_SIZE];
-       char *parv[MAXPARA + 1];
+       char *parv[MAXPARA];
 
        while((len = rb_helper_read(helper, buf, sizeof(buf))) > 0)
        {
                struct authd_cb *cmd;
 
-               parc = rb_string_to_array(buf, parv, MAXPARA+1);
-               cmd = &authd_cmd_tab[*parv[0]];
+               parc = rb_string_to_array(buf, parv, sizeof(parv));
+               cmd = &authd_cmd_tab[(unsigned char)*parv[0]];
                if(cmd->fn != NULL)
                {
                        if(cmd->min_parc > parc)
@@ -337,11 +333,9 @@ configure_authd(void)
                        rb_helper_write(authd_helper, "O opm_listener %s %hu",
                                opm_listeners[LISTEN_IPV4].ipaddr, opm_listeners[LISTEN_IPV4].port);
 
-#ifdef RB_IPV6
                if(opm_listeners[LISTEN_IPV6].ipaddr[0] != '\0')
                        rb_helper_write(authd_helper, "O opm_listener %s %hu",
                                opm_listeners[LISTEN_IPV6].ipaddr, opm_listeners[LISTEN_IPV6].port);
-#endif
 
                RB_DLINK_FOREACH(ptr, opm_list.head)
                {
@@ -354,16 +348,55 @@ configure_authd(void)
        }
        else
                opm_check_enable(false);
+
+       /* Configure DNSBLs */
+       if (dnsbl_stats != NULL)
+       {
+               rb_dictionary_iter iter;
+               struct DNSBLEntry *entry;
+               RB_DICTIONARY_FOREACH(entry, &iter, dnsbl_stats)
+               {
+                       rb_helper_write(authd_helper, "O rbl %s %hhu %s :%s", entry->host,
+                                       entry->iptype, entry->filters, entry->reason);
+               }
+       }
 }
 
 static void
-restart_authd_cb(rb_helper * helper)
+authd_free_client(struct Client *client_p)
 {
-       rb_dictionary_iter iter;
-       struct Client *client_p;
+       if(client_p == NULL || client_p->preClient == NULL)
+               return;
+
+       if(client_p->preClient->auth.cid == 0)
+               return;
+
+       if(authd_helper != NULL)
+               rb_helper_write(authd_helper, "E %x", client_p->preClient->auth.cid);
 
-       iwarn("authd: restart_authd_cb called, authd died?");
-       sendto_realops_snomask(SNO_GENERAL, L_ALL, "authd: restart_authd_cb called, authd died?");
+       client_p->preClient->auth.accepted = true;
+       client_p->preClient->auth.cid = 0;
+}
+
+static void
+authd_free_client_cb(rb_dictionary_element *delem, void *unused)
+{
+       struct Client *client_p = delem->data;
+       authd_free_client(client_p);
+}
+
+void
+authd_abort_client(struct Client *client_p)
+{
+       rb_dictionary_delete(cid_clients, RB_UINT_TO_POINTER(client_p->preClient->auth.cid));
+       authd_free_client(client_p);
+}
+
+static void
+restart_authd_cb(rb_helper * helper)
+{
+       iwarn("authd helper died - attempting to restart");
+       sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, "authdd helper died - attempting to restart");
 
        if(helper != NULL)
        {
@@ -371,11 +404,8 @@ restart_authd_cb(rb_helper * helper)
                authd_helper = NULL;
        }
 
-       RB_DICTIONARY_FOREACH(client_p, &iter, cid_clients)
-       {
-               /* Abort any existing clients */
-               authd_abort_client(client_p);
-       }
+       rb_dictionary_destroy(cid_clients, authd_free_client_cb, NULL);
+       cid_clients = NULL;
 
        start_authd();
        configure_authd();
@@ -417,9 +447,15 @@ generate_cid(void)
  * gonna accept the client and ignore authd's suggestion.
  *
  * --Elizafox
+ *
+ * If this is an SSL connection we must defer handing off the client for
+ * reading until it is open and we have the certificate fingerprint, otherwise
+ * it's possible for the client to immediately send data before authd completes
+ * and before the status of the connection is communicated via ssld. This data
+ * could then be processed too early by read_packet().
  */
 void
-authd_initiate_client(struct Client *client_p)
+authd_initiate_client(struct Client *client_p, bool defer)
 {
        char client_ipaddr[HOSTIPLEN+1];
        char listen_ipaddr[HOSTIPLEN+1];
@@ -442,15 +478,40 @@ authd_initiate_client(struct Client *client_p)
        listen_port = ntohs(GET_SS_PORT(&client_p->preClient->lip));
        client_port = ntohs(GET_SS_PORT(&client_p->localClient->ip));
 
+       if(defer)
+               client_p->preClient->auth.flags |= AUTHC_F_DEFERRED;
+
        /* Add a bit of a fudge factor... */
        client_p->preClient->auth.timeout = rb_current_time() + ConfigFileEntry.connect_timeout + 10;
 
-       rb_helper_write(authd_helper, "C %x %s %hu %s %hu", authd_cid, listen_ipaddr, listen_port, client_ipaddr, client_port);
+       rb_helper_write(authd_helper, "C %x %s %hu %s %hu %x", authd_cid, listen_ipaddr, listen_port, client_ipaddr, client_port,
+#ifdef HAVE_LIBSCTP
+               IsSCTP(client_p) ? IPPROTO_SCTP : IPPROTO_TCP);
+#else
+               IPPROTO_TCP);
+#endif
+}
+
+static inline void
+authd_read_client(struct Client *client_p)
+{
+       /*
+        * When a client has auth'ed, we want to start reading what it sends
+        * us. This is what read_packet() does.
+        *     -- adrian
+        *
+        * Above comment was originally in s_auth.c, but moved here with below code.
+        * --Elizafox
+        */
+       rb_dlinkAddTail(client_p, &client_p->node, &global_client_list);
+       read_packet(client_p->localClient->F, client_p);
 }
 
 /* When this is called we have a decision on client acceptance.
  *
- * After this point authd no longer "owns" the client.
+ * After this point authd no longer "owns" the client, but if
+ * it's flagged as deferred then we're still waiting for a call
+ * to authd_deferred_client().
  */
 static inline void
 authd_decide_client(struct Client *client_p, const char *ident, const char *host, bool accept, char cause, const char *data, const char *reason)
@@ -461,6 +522,7 @@ authd_decide_client(struct Client *client_p, const char *ident, const char *host
        if(*ident != '*')
        {
                rb_strlcpy(client_p->username, ident, sizeof(client_p->username));
+               SetGotId(client_p);
                ServerStats.is_asuc++;
        }
        else
@@ -477,16 +539,17 @@ authd_decide_client(struct Client *client_p, const char *ident, const char *host
        client_p->preClient->auth.reason = (reason == NULL ? NULL : rb_strdup(reason));
        client_p->preClient->auth.cid = 0;
 
-       /*
-        * When a client has auth'ed, we want to start reading what it sends
-        * us. This is what read_packet() does.
-        *     -- adrian
-        *
-        * Above comment was originally in s_auth.c, but moved here with below code.
-        * --Elizafox
-        */
-       rb_dlinkAddTail(client_p, &client_p->node, &global_client_list);
-       read_packet(client_p->localClient->F, client_p);
+       client_p->preClient->auth.flags |= AUTHC_F_COMPLETE;
+       if((client_p->preClient->auth.flags & AUTHC_F_DEFERRED) == 0)
+               authd_read_client(client_p);
+}
+
+void
+authd_deferred_client(struct Client *client_p)
+{
+       client_p->preClient->auth.flags &= ~AUTHC_F_DEFERRED;
+       if(client_p->preClient->auth.flags & AUTHC_F_COMPLETE)
+               authd_read_client(client_p);
 }
 
 /* Convenience function to accept client */
@@ -503,46 +566,43 @@ authd_reject_client(struct Client *client_p, const char *ident, const char *host
        authd_decide_client(client_p, ident, host, false, cause, data, reason);
 }
 
-void
-authd_abort_client(struct Client *client_p)
-{
-       if(client_p == NULL || client_p->preClient == NULL)
-               return;
-
-       if(client_p->preClient->auth.cid == 0)
-               return;
-
-       rb_dictionary_delete(cid_clients, RB_UINT_TO_POINTER(client_p->preClient->auth.cid));
-
-       if(authd_helper != NULL)
-               rb_helper_write(authd_helper, "E %x", client_p->preClient->auth.cid);
-
-       client_p->preClient->auth.accepted = true;
-       client_p->preClient->auth.cid = 0;
-}
-
 static void
 timeout_dead_authd_clients(void *notused __unused)
 {
        rb_dictionary_iter iter;
        struct Client *client_p;
+       rb_dlink_list freelist = { NULL, NULL, 0 };
+       rb_dlink_node *ptr, *nptr;
 
        RB_DICTIONARY_FOREACH(client_p, &iter, cid_clients)
        {
                if(client_p->preClient->auth.timeout < rb_current_time())
-                       authd_abort_client(client_p);
+               {
+                       rb_dlinkAddAlloc(client_p, &freelist);
+               }
+       }
+
+       /* RB_DICTIONARY_FOREACH is not safe for deletion, so we do this crap */
+       RB_DLINK_FOREACH_SAFE(ptr, nptr, freelist.head)
+       {
+               client_p = ptr->data;
+               authd_abort_client(client_p);
+               rb_dlinkDestroy(ptr, &freelist);
        }
 }
 
-/* Send a new blacklist to authd */
+/* Send a new DNSBL entry to authd */
 void
-add_blacklist(const char *host, const char *reason, uint8_t iptype, rb_dlink_list *filters)
+add_dnsbl_entry(const char *host, const char *reason, uint8_t iptype, rb_dlink_list *filters)
 {
        rb_dlink_node *ptr;
-       struct BlacklistStats *stats = rb_malloc(sizeof(struct BlacklistStats));
+       struct DNSBLEntry *entry = rb_malloc(sizeof(*entry));
        char filterbuf[BUFSIZE] = "*";
        size_t s = 0;
 
+       if(dnsbl_stats == NULL)
+               dnsbl_stats = rb_dictionary_create("dnsbl statistics", rb_strcasecmp);
+
        /* Build a list of comma-separated values for authd.
         * We don't check for validity - do it elsewhere.
         */
@@ -562,39 +622,52 @@ add_blacklist(const char *host, const char *reason, uint8_t iptype, rb_dlink_lis
        if(s)
                filterbuf[s - 1] = '\0';
 
-       stats->iptype = iptype;
-       stats->hits = 0;
-       rb_dictionary_add(bl_stats, host, stats);
+       entry->host = rb_strdup(host);
+       entry->reason = rb_strdup(reason);
+       entry->filters = rb_strdup(filterbuf);
+       entry->iptype = iptype;
+       entry->hits = 0;
 
+       rb_dictionary_add(dnsbl_stats, entry->host, entry);
        rb_helper_write(authd_helper, "O rbl %s %hhu %s :%s", host, iptype, filterbuf, reason);
 }
 
-/* Delete a blacklist */
+/* Delete a DNSBL entry. */
 void
-del_blacklist(const char *host)
+del_dnsbl_entry(const char *host)
 {
-       struct BlacklistStats *stats = rb_dictionary_retrieve(bl_stats, host);
-       if(stats != NULL)
+       struct DNSBLEntry *entry = rb_dictionary_retrieve(dnsbl_stats, host);
+
+       if(entry != NULL)
        {
-               rb_dictionary_delete(bl_stats, host);
-               rb_free(stats);
+               rb_dictionary_delete(dnsbl_stats, entry->host);
+               rb_free(entry->host);
+               rb_free(entry->reason);
+               rb_free(entry->filters);
+               rb_free(entry);
        }
 
        rb_helper_write(authd_helper, "O rbl_del %s", host);
 }
 
-/* Delete all the blacklists */
-void
-del_blacklist_all(void)
+static void
+dnsbl_delete_elem(rb_dictionary_element *delem, void *unused)
 {
-       struct BlacklistStats *stats;
-       rb_dictionary_iter iter;
+       struct DNSBLEntry *entry = delem->data;
 
-       RB_DICTIONARY_FOREACH(stats, &iter, bl_stats)
-       {
-               rb_free(stats);
-               rb_dictionary_delete(bl_stats, iter.cur->key);
-       }
+       rb_free(entry->host);
+       rb_free(entry->reason);
+       rb_free(entry->filters);
+       rb_free(entry);
+}
+
+/* Delete all the DNSBL entries. */
+void
+del_dnsbl_entry_all(void)
+{
+       if(dnsbl_stats != NULL)
+               rb_dictionary_destroy(dnsbl_stats, dnsbl_delete_elem, NULL);
+       dnsbl_stats = NULL;
 
        rb_helper_write(authd_helper, "O rbl_del_all");
 }
@@ -654,7 +727,7 @@ create_opm_listener(const char *ip, uint16_t port)
        }
 
        conf_create_opm_listener(ip, port);
-       rb_helper_write(authd_helper, "O opm_listener %s %hu", ip, port);
+       rb_helper_write(authd_helper, "O opm_listener %s %hu", ipbuf, port);
 }
 
 void