*
* 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
static rb_dictionary *cid_clients;
static struct ev_entry *timeout_ev;
-rb_dictionary *bl_stats;
+rb_dictionary *dnsbl_stats;
+
+rb_dlink_list opm_list;
+struct OPMListener opm_listeners[LISTEN_LAST];
static struct authd_cb authd_cmd_tab[256] =
{
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;
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", strcasecmp);
-
if(timeout_ev == NULL)
timeout_ev = rb_event_addish("timeout_dead_authd_clients", timeout_dead_authd_clients, NULL, 1);
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;
}
}
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 */
}
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
{
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]);
static void
cmd_oper_warn(int parc, char **parv)
{
- switch(*parv[2])
+ switch(*parv[1])
{
case 'D': /* Debug */
- sendto_realops_snomask(SNO_DEBUG, L_ALL, "authd debug: %s", parv[3]);
- idebug("authd: %s", parv[3]);
+ 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[3]);
- inotice("authd: %s", parv[3]);
+ 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[3]);
- iwarn("authd: %s", parv[3]);
+ 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[3]);
- ierror("authd: %s", parv[3]);
+ 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[2], parv[3]);
- ilog(L_MAIN, "authd unknown oper notice type (%s): %s", parv[2], parv[3]);
+ 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;
}
}
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)
void
configure_authd(void)
{
- /* These will do for now */
+ /* Timeouts */
set_authd_timeout("ident_timeout", GlobalSetOptions.ident_timeout);
set_authd_timeout("rdns_timeout", ConfigFileEntry.connect_timeout);
set_authd_timeout("rbl_timeout", ConfigFileEntry.connect_timeout);
+
ident_check_enable(!ConfigFileEntry.disable_auth);
+
+ /* Configure OPM */
+ if(rb_dlink_list_length(&opm_list) > 0 &&
+ (opm_listeners[LISTEN_IPV4].ipaddr[0] != '\0' ||
+ opm_listeners[LISTEN_IPV6].ipaddr[0] != '\0'))
+ {
+ rb_dlink_node *ptr;
+
+ if(opm_listeners[LISTEN_IPV4].ipaddr[0] != '\0')
+ rb_helper_write(authd_helper, "O opm_listener %s %hu",
+ opm_listeners[LISTEN_IPV4].ipaddr, opm_listeners[LISTEN_IPV4].port);
+
+ 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);
+
+ RB_DLINK_FOREACH(ptr, opm_list.head)
+ {
+ struct OPMScanner *scanner = ptr->data;
+ rb_helper_write(authd_helper, "O opm_scanner %s %hu",
+ scanner->type, scanner->port);
+ }
+
+ opm_check_enable(true);
+ }
+ else
+ opm_check_enable(false);
+
+ /* Configure DNSBLs */
+ 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);
+
+ 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);
+}
- iwarn("authd: restart_authd_cb called, authd died?");
- sendto_realops_snomask(SNO_GENERAL, L_ALL, "authd: restart_authd_cb called, authd died?");
+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)
{
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();
- rehash(false); /* FIXME - needed to reload authd configuration */
+ configure_authd();
}
void
restart_authd(void)
{
+ ierror("authd restarting...");
restart_authd_cb(authd_helper);
}
rehash_authd(void)
{
rb_helper_write(authd_helper, "R");
- configure_authd();
}
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];
uint16_t client_port, listen_port;
uint32_t authd_cid;
- if(client_p->preClient == NULL || client_p->preClient->authd_cid != 0)
+ if(client_p->preClient == NULL || client_p->preClient->auth.cid != 0)
return;
- authd_cid = client_p->preClient->authd_cid = generate_cid();
+ authd_cid = client_p->preClient->auth.cid = generate_cid();
/* Collisions are extremely unlikely, so disregard the possibility */
rb_dictionary_add(cid_clients, RB_UINT_TO_POINTER(authd_cid), 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->authd_timeout = rb_current_time() + ConfigFileEntry.connect_timeout + 10;
+ 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)
{
- if(client_p->preClient == NULL || client_p->preClient->authd_cid == 0)
+ if(client_p->preClient == NULL || client_p->preClient->auth.cid == 0)
return;
if(*ident != '*')
{
rb_strlcpy(client_p->username, ident, sizeof(client_p->username));
+ SetGotId(client_p);
ServerStats.is_asuc++;
}
else
if(*host != '*')
rb_strlcpy(client_p->host, host, sizeof(client_p->host));
- rb_dictionary_delete(cid_clients, RB_UINT_TO_POINTER(client_p->preClient->authd_cid));
+ rb_dictionary_delete(cid_clients, RB_UINT_TO_POINTER(client_p->preClient->auth.cid));
- client_p->preClient->authd_accepted = accept;
- client_p->preClient->authd_cause = cause;
- client_p->preClient->authd_data = (data == NULL ? NULL : rb_strdup(data));
- client_p->preClient->authd_reason = (reason == NULL ? NULL : rb_strdup(reason));
- client_p->preClient->authd_cid = 0;
+ client_p->preClient->auth.accepted = accept;
+ client_p->preClient->auth.cause = cause;
+ client_p->preClient->auth.data = (data == NULL ? NULL : rb_strdup(data));
+ 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 */
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->authd_cid == 0)
- return;
-
- rb_dictionary_delete(cid_clients, RB_UINT_TO_POINTER(client_p->preClient->authd_cid));
-
- if(authd_helper != NULL)
- rb_helper_write(authd_helper, "E %x", client_p->preClient->authd_cid);
-
- client_p->preClient->authd_accepted = true;
- client_p->preClient->authd_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->authd_timeout < rb_current_time())
- authd_abort_client(client_p);
+ if(client_p->preClient->auth.timeout < rb_current_time())
+ {
+ 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 blacklist_stats *stats = rb_malloc(sizeof(struct blacklist_stats));
+ 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.
*/
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)
{
- rb_dictionary_delete(bl_stats, host);
+ struct DNSBLEntry *entry = rb_dictionary_retrieve(dnsbl_stats, host);
+
+ if(entry != NULL)
+ {
+ 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 blacklist_stats *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");
}
rb_helper_write(authd_helper, "O ident_enabled %d", enabled ? 1 : 0);
}
-/* Create an OPM listener */
+/* Create an OPM listener
+ * XXX - This is a big nasty hack, but it avoids resending duplicate data when
+ * configure_authd() is called.
+ */
+void
+conf_create_opm_listener(const char *ip, uint16_t port)
+{
+ char ipbuf[HOSTIPLEN];
+ struct OPMListener *listener;
+
+ rb_strlcpy(ipbuf, ip, sizeof(ipbuf));
+ if(ipbuf[0] == ':')
+ {
+ memmove(ipbuf + 1, ipbuf, sizeof(ipbuf) - 1);
+ ipbuf[0] = '0';
+ }
+
+ /* I am much too lazy to use rb_inet_pton and GET_SS_FAMILY for now --Elizafox */
+ listener = &opm_listeners[(strchr(ipbuf, ':') != NULL ? LISTEN_IPV6 : LISTEN_IPV4)];
+ rb_strlcpy(listener->ipaddr, ipbuf, sizeof(listener->ipaddr));
+ listener->port = port;
+}
+
void
create_opm_listener(const char *ip, uint16_t port)
{
char ipbuf[HOSTIPLEN];
+
+ /* XXX duplicated in conf_create_opm_listener */
rb_strlcpy(ipbuf, ip, sizeof(ipbuf));
if(ipbuf[0] == ':')
{
ipbuf[0] = '0';
}
+ conf_create_opm_listener(ip, port);
rb_helper_write(authd_helper, "O opm_listener %s %hu", ipbuf, port);
}
+void
+delete_opm_listener_all(void)
+{
+ memset(&opm_listeners, 0, sizeof(opm_listeners));
+ rb_helper_write(authd_helper, "O opm_listener_del_all");
+}
+
/* Disable all OPM scans */
void
opm_check_enable(bool enabled)
rb_helper_write(authd_helper, "O opm_enabled %d", enabled ? 1 : 0);
}
-/* Create an OPM proxy scanner */
+/* Create an OPM proxy scanner
+ * XXX - This is a big nasty hack, but it avoids resending duplicate data when
+ * configure_authd() is called.
+ */
+void
+conf_create_opm_proxy_scanner(const char *type, uint16_t port)
+{
+ struct OPMScanner *scanner = rb_malloc(sizeof(struct OPMScanner));
+
+ rb_strlcpy(scanner->type, type, sizeof(scanner->type));
+ scanner->port = port;
+ rb_dlinkAdd(scanner, &scanner->node, &opm_list);
+}
+
void
create_opm_proxy_scanner(const char *type, uint16_t port)
{
+ conf_create_opm_proxy_scanner(type, port);
rb_helper_write(authd_helper, "O opm_scanner %s %hu", type, port);
}
void
delete_opm_proxy_scanner(const char *type, uint16_t port)
{
+ rb_dlink_node *ptr, *nptr;
+
+ RB_DLINK_FOREACH_SAFE(ptr, nptr, opm_list.head)
+ {
+ struct OPMScanner *scanner = ptr->data;
+
+ if(rb_strncasecmp(scanner->type, type, sizeof(scanner->type)) == 0 &&
+ scanner->port == port)
+ {
+ rb_dlinkDelete(ptr, &opm_list);
+ rb_free(scanner);
+ break;
+ }
+ }
+
rb_helper_write(authd_helper, "O opm_scanner_del %s %hu", type, port);
}
void
delete_opm_proxy_scanner_all(void)
{
+ rb_dlink_node *ptr, *nptr;
+
+ RB_DLINK_FOREACH_SAFE(ptr, nptr, opm_list.head)
+ {
+ struct OPMScanner *scanner = ptr->data;
+
+ rb_dlinkDelete(ptr, &opm_list);
+ rb_free(scanner);
+ }
+
rb_helper_write(authd_helper, "O opm_scanner_del_all");
}