diff --git a/authd/Makefile.am b/authd/Makefile.am index e1b705e14..fd8299e65 100644 --- a/authd/Makefile.am +++ b/authd/Makefile.am @@ -2,6 +2,13 @@ pkglibexec_PROGRAMS = authd AM_CFLAGS=$(WARNFLAGS) AM_CPPFLAGS = -I../include -I../librb/include +authd_SOURCES = \ + authd.c \ + res.c \ + reslib.c \ + dns.c \ + provider.c \ + providers/rdns.c \ + providers/ident.c -authd_SOURCES = authd.c res.c reslib.c dns.c authd_LDADD = ../librb/src/librb.la diff --git a/authd/dns.c b/authd/dns.c index 52e3191cb..198a4ad32 100644 --- a/authd/dns.c +++ b/authd/dns.c @@ -69,9 +69,10 @@ lookup_ip(const char *host, int aftype, DNSCB callback, void *data) /* See lookup_ip's comment */ struct dns_query * -lookup_hostname(const char *ip, int aftype, DNSCB callback, void *data) +lookup_hostname(const char *ip, DNSCB callback, void *data) { struct dns_query *query = rb_malloc(sizeof(struct dns_query)); + int aftype; if(!rb_inet_pton_sock(ip, (struct sockaddr *)&query->addr)) { @@ -79,6 +80,8 @@ lookup_hostname(const char *ip, int aftype, DNSCB callback, void *data) return NULL; } + aftype = GET_SS_FAMILY(&query->addr); + if(aftype == AF_INET) query->type = QUERY_PTR_A; #ifdef RB_IPV6 @@ -121,10 +124,10 @@ handle_lookup_ip_reply(void *data, struct DNSReply *reply) if(!query) /* Shouldn't happen */ exit(2); - + type = query->type; - if(!reply) + if(reply == NULL) goto end; switch(query->type) @@ -165,13 +168,13 @@ handle_lookup_hostname_reply(void *data, struct DNSReply *reply) char *hostname = NULL; query_type type = QUERY_INVALID; - if(!query) + if(query == NULL) /* Shouldn't happen */ exit(4); type = query->type; - if(!reply) + if(reply == NULL) goto end; if(query->type == QUERY_PTR_A) @@ -180,7 +183,7 @@ handle_lookup_hostname_reply(void *data, struct DNSReply *reply) ip = (struct sockaddr_in *) &query->addr; ip_fwd = (struct sockaddr_in *) &reply->addr; - if(ip->sin_addr.s_addr == ip_fwd->sin_addr.s_addr && strlen(reply->h_name) < 63) + if(ip->sin_addr.s_addr == ip_fwd->sin_addr.s_addr) hostname = reply->h_name; } #ifdef RB_IPV6 @@ -190,7 +193,7 @@ handle_lookup_hostname_reply(void *data, struct DNSReply *reply) ip = (struct sockaddr_in6 *) &query->addr; ip_fwd = (struct sockaddr_in6 *) &reply->addr; - if(memcmp(&ip->sin6_addr, &ip_fwd->sin6_addr, sizeof(struct in6_addr)) == 0 && strlen(reply->h_name) < 63) + if(memcmp(&ip->sin6_addr, &ip_fwd->sin6_addr, sizeof(struct in6_addr)) == 0) hostname = reply->h_name; } #endif @@ -212,7 +215,7 @@ submit_dns_answer(const char *reply, bool status, query_type type, void *data) if(!id || type == QUERY_INVALID) exit(6); - if(!reply || !status) + if(reply == NULL || status == false) { rb_helper_write(authd_helper, "E %s E %c *", id, type); rb_free(id); @@ -243,10 +246,9 @@ resolve_dns(int parc, char *parv[]) break; #ifdef RB_IPV6 case 'S': - aftype = AF_INET6; #endif case 'R': - if(!lookup_hostname(record, aftype, submit_dns_answer, id)) + if(!lookup_hostname(record, submit_dns_answer, id)) submit_dns_answer(NULL, false, qtype, NULL); break; default: @@ -257,7 +259,7 @@ resolve_dns(int parc, char *parv[]) void enumerate_nameservers(const char *rid, const char letter) { - char buf[40 * IRCD_MAXNS]; /* Plenty */ + char buf[(HOSTIPLEN + 1) * IRCD_MAXNS]; char *c = buf; int i; @@ -270,8 +272,7 @@ enumerate_nameservers(const char *rid, const char letter) for(i = 0; i < irc_nscount; i++) { - char addr[40]; - int ret; + char addr[HOSTIPLEN]; rb_inet_ntop_sock((struct sockaddr *)&irc_nsaddr_list[i], addr, sizeof(addr)); @@ -282,8 +283,8 @@ enumerate_nameservers(const char *rid, const char letter) return; } - ret = snprintf(c, 40, "%s ", addr); - c += (size_t)ret; + (void)snprintf(c, HOSTIPLEN + 1, "%s ", addr); + c += strlen(addr) + 1; } *(--c) = '\0'; diff --git a/authd/dns.h b/authd/dns.h index 4a977b280..a21d98585 100644 --- a/authd/dns.h +++ b/authd/dns.h @@ -50,7 +50,7 @@ struct dns_query void *data; }; -extern struct dns_query *lookup_hostname(const char *ip, int aftype, DNSCB callback, void *data); +extern struct dns_query *lookup_hostname(const char *ip, DNSCB callback, void *data); extern struct dns_query *lookup_ip(const char *host, int aftype, DNSCB callback, void *data); extern void cancel_query(struct dns_query *query); diff --git a/authd/provider.c b/authd/provider.c new file mode 100644 index 000000000..fc695bc0f --- /dev/null +++ b/authd/provider.c @@ -0,0 +1,245 @@ +/* authd/provider.c - authentication provider framework + * Copyright (c) 2016 Elizabeth Myers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* So the basic design here is to have "authentication providers" that do + * things like query ident and blacklists and even open proxies. + * + * Providers are registered statically in the struct auth_providers array. You will + * probably want to add an item to the provider_t enum also. + * + * Providers can either return failure immediately, immediate acceptance, or + * do work in the background (calling set_provider to signal this). + * + * It is up to providers to keep their own state on clients if they need to. + * + * All providers must implement at a minimum a perform_provider function. You + * don't have to implement the others if you don't need them. + * + * Providers may kick clients off by rejecting them. Upon rejection, all + * providers are cancelled. They can also unconditionally accept them. + * + * When a provider is done and is neutral on accepting/rejecting a client, it + * should call provider_done. Do NOT call this if you have accepted or rejected + * the client. + * + * --Elizafox, 9 March 2016 + */ + +#include "authd.h" +#include "provider.h" + +rb_dlink_list auth_providers; + +/* Clients waiting */ +struct auth_client auth_clients[MAX_CLIENTS]; + +/* Load a provider */ +void load_provider(struct auth_provider *provider) +{ + provider->init(); + rb_dlinkAdd(provider, &provider->node, &auth_providers); +} + +void unload_provider(struct auth_provider *provider) +{ + provider->destroy(); + rb_dlinkDelete(&provider->node, &auth_providers); +} + +/* Initalise all providers */ +void init_providers(void) +{ + load_provider(&rdns_provider); + load_provider(&ident_provider); +} + +/* Terminate all providers */ +void destroy_providers(void) +{ + rb_dlink_node *ptr; + struct auth_provider *provider; + + /* Cancel outstanding connections */ + for (size_t i = 0; i < MAX_CLIENTS; i++) + { + if(auth_clients[i].cid) + { + /* TBD - is this the right thing? + * (NOTE - this error message is designed for morons) */ + reject_client(&auth_clients[i], 0, true, + "IRC server reloading... try reconnecting in a few seconds"); + } + } + + RB_DLINK_FOREACH(ptr, auth_providers.head) + { + provider = ptr->data; + + if(provider->destroy) + provider->destroy(); + } +} + +/* Cancel outstanding providers for a client */ +void cancel_providers(struct auth_client *auth) +{ + rb_dlink_node *ptr; + struct auth_provider *provider; + + RB_DLINK_FOREACH(ptr, auth_providers.head) + { + provider = ptr->data; + + if(provider->cancel && is_provider(auth, provider->id)) + /* Cancel if required */ + provider->cancel(auth); + } +} + +/* Provider is done */ +void provider_done(struct auth_client *auth, provider_t id) +{ + rb_dlink_node *ptr; + struct auth_provider *provider; + + unset_provider(auth, id); + + if(!auth->providers) + { + /* No more providers, done */ + accept_client(auth, 0); + return; + } + + RB_DLINK_FOREACH(ptr, auth_providers.head) + { + provider = ptr->data; + + if(provider->completed && is_provider(auth, provider->id)) + /* Notify pending clients who asked for it */ + provider->completed(auth, id); + } +} + +/* Reject a client, cancel outstanding providers if any if hard set to true */ +void reject_client(struct auth_client *auth, provider_t id, bool hard, const char *reason) +{ + uint16_t cid = auth->cid; + char reject; + + switch(id) + { + case PROVIDER_RDNS: + reject = 'D'; + break; + case PROVIDER_IDENT: + reject = 'I'; + break; + case PROVIDER_BLACKLIST: + reject = 'B'; + break; + case PROVIDER_NULL: + default: + reject = 'N'; + break; + } + + rb_helper_write(authd_helper, "R %x %c :%s", auth->cid, reject, reason); + + unset_provider(auth, id); + + if(hard && auth->providers) + { + cancel_providers(auth); + memset(&auth_clients[cid], 0, sizeof(struct auth_client)); + } +} + +/* Accept a client, cancel outstanding providers if any */ +void accept_client(struct auth_client *auth, provider_t id) +{ + uint16_t cid = auth->cid; + + rb_helper_write(authd_helper, "A %x %s %s", auth->cid, auth->username, auth->hostname); + + unset_provider(auth, id); + + if(auth->providers) + cancel_providers(auth); + + memset(&auth_clients[cid], 0, sizeof(struct auth_client)); +} + +/* Send a notice to a client */ +void notice_client(struct auth_client *auth, const char *notice) +{ + rb_helper_write(authd_helper, "N %x :%s", auth->cid, notice); +} + +/* Begin authenticating user */ +static void start_auth(const char *cid, const char *l_ip, const char *l_port, const char *c_ip, const char *c_port) +{ + struct auth_provider *provider; + struct auth_client *auth; + long lcid = strtol(cid, NULL, 16); + rb_dlink_node *ptr; + + if(lcid >= MAX_CLIENTS) + return; + + auth = &auth_clients[lcid]; + if(auth->cid != 0) + /* Shouldn't get here */ + return; + + auth->cid = (uint16_t)lcid; + + rb_strlcpy(auth->l_ip, l_ip, sizeof(auth->l_ip)); + auth->l_port = (uint16_t)atoi(l_port); /* should be safe */ + + rb_strlcpy(auth->c_ip, c_ip, sizeof(auth->c_ip)); + auth->c_port = (uint16_t)atoi(c_port); + + RB_DLINK_FOREACH(ptr, auth_providers.head) + { + provider = ptr->data; + + /* Execute providers */ + if(!provider->start(auth)) + { + /* Rejected immediately */ + cancel_providers(auth); + return; + } + } + + /* If no providers are running, accept the client */ + if(!auth->providers) + accept_client(auth, 0); +} + +/* Callback for the initiation */ +void handle_new_connection(int parc, char *parv[]) +{ + if(parc < 7) + return; + + start_auth(parv[1], parv[2], parv[3], parv[4], parv[5]); +} diff --git a/authd/provider.h b/authd/provider.h new file mode 100644 index 000000000..8ce7e53e6 --- /dev/null +++ b/authd/provider.h @@ -0,0 +1,114 @@ +/* authd/provider.h - authentication provider framework + * Copyright (c) 2016 Elizabeth Myers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __CHARYBDIS_AUTHD_PROVIDER_H__ +#define __CHARYBDIS_AUTHD_PROVIDER_H__ + +#include "stdinc.h" + +/* Arbitrary limit */ +#define MAX_CLIENTS 4096 + +/* Registered providers */ +typedef enum +{ + PROVIDER_NULL = 0x0, /* Dummy value */ + PROVIDER_RDNS = 0x1, + PROVIDER_IDENT = 0x2, + PROVIDER_BLACKLIST = 0x4, +} provider_t; + +struct auth_client +{ + uint16_t cid; /* Client ID */ + + char l_ip[HOSTIPLEN + 1]; /* Listener IP address */ + uint16_t l_port; /* Listener port */ + + char c_ip[HOSTIPLEN + 1]; /* Client IP address */ + uint16_t c_port; /* Client port */ + + char hostname[HOSTLEN + 1]; /* Used for DNS lookup */ + char username[USERLEN + 1]; /* Used for ident lookup */ + + unsigned int providers; /* Providers at work, + * none left when set to 0 */ +}; + +typedef bool (*provider_init_t)(void); +typedef bool (*provider_perform_t)(struct auth_client *); +typedef void (*provider_complete_t)(struct auth_client *, provider_t provider); +typedef void (*provider_cancel_t)(struct auth_client *); +typedef void (*provider_destroy_t)(void); + +struct auth_provider +{ + rb_dlink_node node; + + provider_t id; + + provider_init_t init; /* Initalise the provider */ + provider_destroy_t destroy; /* Terminate the provider */ + + provider_perform_t start; /* Perform authentication */ + provider_cancel_t cancel; /* Authentication cancelled */ + provider_complete_t completed; /* Callback for when other performers complete (think dependency chains) */ +}; + +extern rb_dlink_list auth_providers; +extern struct auth_provider rdns_provider; +extern struct auth_provider ident_provider; + +extern struct auth_client auth_clients[MAX_CLIENTS]; + +void load_provider(struct auth_provider *provider); +void unload_provider(struct auth_provider *provider); + +void init_providers(void); +void destroy_providers(void); +void cancel_providers(struct auth_client *auth); + +void provider_done(struct auth_client *auth, provider_t id); +void accept_client(struct auth_client *auth, provider_t id); +void reject_client(struct auth_client *auth, provider_t id, bool hard, const char *reason); + +void notice_client(struct auth_client *auth, const char *notice); + +void handle_new_connection(int parc, char *parv[]); + +/* Provider is operating on this auth_client (set this if you have async work to do) */ +static inline void set_provider(struct auth_client *auth, provider_t provider) +{ + auth->providers |= provider; +} + +/* Provider is no longer operating on this auth client (you should use provider_done) */ +static inline void unset_provider(struct auth_client *auth, provider_t provider) +{ + auth->providers &= ~provider; +} + +/* Check if provider is operating on this auth client */ +static inline bool is_provider(struct auth_client *auth, provider_t provider) +{ + return auth->providers & provider; +} + +#endif /* __CHARYBDIS_AUTHD_PROVIDER_H__ */ diff --git a/authd/providers/ident.c b/authd/providers/ident.c new file mode 100644 index 000000000..d633d1726 --- /dev/null +++ b/authd/providers/ident.c @@ -0,0 +1,392 @@ +/* authd/providers/ident.c - ident lookup provider for authd + * Copyright (c) 2016 Elizabeth Myers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdinc.h" +#include "match.h" +#include "authd.h" +#include "provider.h" +#include "res.h" + +#define IDENT_BUFSIZE 128 + +struct ident_query +{ + rb_dlink_node node; + + struct auth_client *auth; /* Our client */ + time_t timeout; /* Timeout interval */ + rb_fde_t *F; /* Our FD */ +}; + +/* Goinked from old s_auth.c --Elizafox */ +static const char *messages[] = +{ + ":*** Checking Ident", + ":*** Got Ident response", + ":*** No Ident response", +}; + +typedef enum +{ + REPORT_LOOKUP, + REPORT_FOUND, + REPORT_FAIL, +} ident_message; + +static EVH timeout_ident_queries_event; +static CNCB ident_connected; +static PF read_ident_reply; + +static void client_fail(struct ident_query *query, ident_message message); +static void client_success(struct ident_query *query); +static void cleanup_query(struct ident_query *query); +static char * get_valid_ident(char *buf); + +static rb_dlink_list queries; +static struct ev_entry *timeout_ev; +static int ident_timeout = 5; + + +bool ident_init(void) +{ + timeout_ev = rb_event_addish("timeout_ident_queries_event", timeout_ident_queries_event, NULL, 1); + return (timeout_ev != NULL); +} + +void ident_destroy(void) +{ + rb_dlink_node *ptr, *nptr; + + /* Nuke all ident queries */ + RB_DLINK_FOREACH_SAFE(ptr, nptr, queries.head) + { + struct ident_query *query = ptr->data; + + notice_client(query->auth, messages[REPORT_FAIL]); + + rb_close(query->F); + rb_free(query); + rb_dlinkDelete(ptr, &queries); + } +} + +bool ident_start(struct auth_client *auth) +{ + struct ident_query *query = rb_malloc(sizeof(struct ident_query)); + struct rb_sockaddr_storage l_addr, c_addr; + int family; + rb_fde_t *F; + + query->auth = auth; + query->timeout = rb_current_time() + ident_timeout; + + if((F = rb_socket(family, SOCK_STREAM, 0, "ident")) == NULL) + { + client_fail(query, REPORT_FAIL); + return true; /* Not a fatal error */ + } + + query->F = F; + + /* Build sockaddr_storages for rb_connect_tcp below */ + if(!rb_inet_ntop_sock((struct sockaddr *)&l_addr, auth->l_ip, sizeof(l_addr)) || + !rb_inet_ntop_sock((struct sockaddr *)&c_addr, auth->c_ip, sizeof(c_addr))) + { + client_fail(query, REPORT_FAIL); + return true; + } + + /* Set the ports correctly */ +#ifdef RB_IPV6 + if(GET_SS_FAMILY(&l_addr) == AF_INET6) + ((struct sockaddr_in6 *)&l_addr)->sin6_port = 0; + else +#endif + ((struct sockaddr_in *)&l_addr)->sin_port = 0; + +#ifdef RB_IPV6 + if(GET_SS_FAMILY(&c_addr) == AF_INET6) + ((struct sockaddr_in6 *)&c_addr)->sin6_port = htons(113); + else +#endif + ((struct sockaddr_in *)&c_addr)->sin_port = htons(113); + + rb_connect_tcp(F, (struct sockaddr *)&c_addr, + (struct sockaddr *)&l_addr, + GET_SS_LEN(&l_addr), ident_connected, + query, ident_timeout); + + rb_dlinkAdd(query, &query->node, &queries); + + set_provider(auth, PROVIDER_IDENT); + notice_client(auth, messages[REPORT_LOOKUP]); + + return true; +} + +void ident_cancel(struct auth_client *auth) +{ + rb_dlink_node *ptr, *nptr; + + RB_DLINK_FOREACH_SAFE(ptr, nptr, queries.head) + { + struct ident_query *query = ptr->data; + + if(query->auth == auth) + { + client_fail(query, REPORT_FAIL); + + rb_close(query->F); + rb_free(query); + rb_dlinkDelete(ptr, &queries); + + return; + } + } +} + +/* Timeout outstanding queries */ +static void timeout_ident_queries_event(void *notused) +{ + rb_dlink_node *ptr, *nptr; + + RB_DLINK_FOREACH_SAFE(ptr, nptr, queries.head) + { + struct ident_query *query = ptr->data; + + if(query->timeout < rb_current_time()) + { + client_fail(query, REPORT_FAIL); + + rb_close(query->F); + rb_free(query); + rb_dlinkDelete(ptr, &queries); + } + } +} + +/* + * ident_connected() - deal with the result of rb_connect_tcp() + * + * If the connection failed, we simply close the auth fd and report + * a failure. If the connection suceeded send the ident server a query + * giving "theirport , ourport". The write is only attempted *once* so + * it is deemed to be a fail if the entire write doesn't write all the + * data given. This shouldnt be a problem since the socket should have + * a write buffer far greater than this message to store it in should + * problems arise. -avalon + */ +static void ident_connected(rb_fde_t *F, int error, void *data) +{ + struct ident_query *query = data; + struct auth_client *auth = query->auth; + char authbuf[32]; + int authlen; + + /* Check the error */ + if(error != RB_OK) + { + /* We had an error during connection :( */ + client_fail(query, REPORT_FAIL); + cleanup_query(query); + return; + } + + snprintf(authbuf, sizeof(authbuf), "%u , %u\r\n", + auth->c_port, auth->l_port); + authlen = strlen(authbuf); + + if(rb_write(query->F, authbuf, authlen) != authlen) + { + client_fail(query, REPORT_FAIL); + return; + } + + read_ident_reply(query->F, query); +} + +static void +read_ident_reply(rb_fde_t *F, void *data) +{ + struct ident_query *query = data; + struct auth_client *auth = query->auth; + char *s = NULL; + char *t = NULL; + int len; + int count; + char buf[IDENT_BUFSIZE + 1]; /* buffer to read auth reply into */ + + len = rb_read(F, buf, IDENT_BUFSIZE); + if(len < 0 && rb_ignore_errno(errno)) + { + rb_setselect(F, RB_SELECT_READ, read_ident_reply, query); + return; + } + + if(len > 0) + { + buf[len] = '\0'; + + if((s = get_valid_ident(buf))) + { + t = auth->username; + + while (*s == '~' || *s == '^') + s++; + + for (count = USERLEN; *s && count; s++) + { + if(*s == '@') + { + break; + } + if(*s != ' ' && *s != ':' && *s != '[') + { + *t++ = *s; + count--; + } + } + *t = '\0'; + } + } + + if(s == NULL) + client_fail(query, REPORT_FAIL); + else + client_success(query); + + cleanup_query(query); +} + +static void client_fail(struct ident_query *query, ident_message report) +{ + struct auth_client *auth = query->auth; + + if(auth) + { + rb_strlcpy(auth->username, "*", sizeof(auth->username)); + notice_client(auth, messages[report]); + provider_done(auth, PROVIDER_IDENT); + } +} + +static void client_success(struct ident_query *query) +{ + struct auth_client *auth = query->auth; + + if(auth) + { + notice_client(auth, messages[REPORT_FOUND]); + provider_done(auth, PROVIDER_IDENT); + } +} + +static void cleanup_query(struct ident_query *query) +{ + rb_dlink_node *ptr, *nptr; + + RB_DLINK_FOREACH_SAFE(ptr, nptr, queries.head) + { + struct ident_query *query_l = ptr->data; + + if(query_l == query) + { + rb_close(query->F); + rb_free(query); + rb_dlinkDelete(ptr, &queries); + } + } +} + +/* get_valid_ident + * parse ident query reply from identd server + * + * Torn out of old s_auth.c because there was nothing wrong with it + * --Elizafox + * + * Inputs - pointer to ident buf + * Outputs - NULL if no valid ident found, otherwise pointer to name + * Side effects - None + */ +static char * +get_valid_ident(char *buf) +{ + int remp = 0; + int locp = 0; + char *colon1Ptr; + char *colon2Ptr; + char *colon3Ptr; + char *commaPtr; + char *remotePortString; + + /* All this to get rid of a sscanf() fun. */ + remotePortString = buf; + + colon1Ptr = strchr(remotePortString, ':'); + if(!colon1Ptr) + return 0; + + *colon1Ptr = '\0'; + colon1Ptr++; + colon2Ptr = strchr(colon1Ptr, ':'); + if(!colon2Ptr) + return 0; + + *colon2Ptr = '\0'; + colon2Ptr++; + commaPtr = strchr(remotePortString, ','); + + if(!commaPtr) + return 0; + + *commaPtr = '\0'; + commaPtr++; + + remp = atoi(remotePortString); + if(!remp) + return 0; + + locp = atoi(commaPtr); + if(!locp) + return 0; + + /* look for USERID bordered by first pair of colons */ + if(!strstr(colon1Ptr, "USERID")) + return 0; + + colon3Ptr = strchr(colon2Ptr, ':'); + if(!colon3Ptr) + return 0; + + *colon3Ptr = '\0'; + colon3Ptr++; + return (colon3Ptr); +} + + +struct auth_provider ident_provider = +{ + .id = PROVIDER_IDENT, + .init = ident_init, + .destroy = ident_destroy, + .start = ident_start, + .cancel = ident_cancel, + .completed = NULL, +}; diff --git a/authd/providers/rdns.c b/authd/providers/rdns.c new file mode 100644 index 000000000..0a3c2e5e0 --- /dev/null +++ b/authd/providers/rdns.c @@ -0,0 +1,210 @@ +/* authd/providers/rdns.c - rDNS lookup provider for authd + * Copyright (c) 2016 Elizabeth Myers + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice is present in all copies. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "stdinc.h" +#include "rb_commio.h" +#include "authd.h" +#include "provider.h" +#include "res.h" +#include "dns.h" + +struct user_query +{ + rb_dlink_node node; + + struct auth_client *auth; /* Our client */ + struct dns_query *query; /* Pending DNS query */ + time_t timeout; /* When the request times out */ +}; + +/* Goinked from old s_auth.c --Elizabeth */ +static const char *messages[] = +{ + "*** Looking up your hostname...", + "*** Found your hostname", + "*** Couldn't look up your hostname", + "*** Your hostname is too long, ignoring hostname", +}; + +typedef enum +{ + REPORT_LOOKUP, + REPORT_FOUND, + REPORT_FAIL, + REPORT_TOOLONG, +} dns_message; + +static void client_fail(struct user_query *query, dns_message message); +static void client_success(struct user_query *query); +static void get_dns_answer(const char *res, bool status, query_type type, void *data); + +static struct ev_entry *timeout_ev; +static EVH timeout_dns_queries_event; +static int rdns_timeout = 30; +static rb_dlink_list queries; /* Stored here for easy timeout */ + + +bool client_dns_init(void) +{ + timeout_ev = rb_event_addish("timeout_dns_queries_event", timeout_dns_queries_event, NULL, 5); + return (timeout_ev != NULL); +} + +void client_dns_destroy(void) +{ + rb_dlink_node *ptr, *nptr; + struct user_query *query; + + RB_DLINK_FOREACH_SAFE(ptr, nptr, queries.head) + { + client_fail(ptr->data, REPORT_FAIL); + rb_dlinkDelete(ptr, &queries); + rb_free(ptr); + } + + rb_event_delete(timeout_ev); +} + +bool client_dns_start(struct auth_client *auth) +{ + struct user_query *query = rb_malloc(sizeof(struct user_query)); + + query->auth = auth; + query->timeout = rb_current_time() + rdns_timeout; + + query->query = lookup_hostname(auth->c_ip, get_dns_answer, query); + + notice_client(auth, messages[REPORT_LOOKUP]); + set_provider(auth, PROVIDER_RDNS); + return true; +} + +void client_dns_cancel(struct auth_client *auth) +{ + rb_dlink_node *ptr, *nptr; + + RB_DLINK_FOREACH_SAFE(ptr, nptr, queries.head) + { + struct user_query *query = ptr->data; + + if(query->auth == auth) + { + /* Victim found */ + client_fail(query, REPORT_FAIL); + rb_dlinkDelete(ptr, &queries); + rb_free(query); + return; + } + } +} + +static void +get_dns_answer(const char *res, bool status, query_type type, void *data) +{ + struct user_query *query = data; + struct auth_client *auth = query->auth; + bool fail = false; + dns_message response; + rb_dlink_node *ptr, *nptr; + + if(res == NULL || status == false) + { + response = REPORT_FAIL; + fail = true; + goto cleanup; + } + else if(strlen(res) > HOSTLEN) + { + /* Ah well. */ + response = REPORT_TOOLONG; + fail = true; + goto cleanup; + } + + rb_strlcpy(auth->hostname, res, HOSTLEN + 1); + +cleanup: + /* Clean us up off the pending queries list */ + RB_DLINK_FOREACH_SAFE(ptr, nptr, queries.head) + { + struct user_query *query_l = ptr->data; + + if(query == query_l) + { + /* Found */ + if(fail) + client_fail(query, response); + else + client_success(query); + + rb_dlinkDelete(ptr, &queries); + rb_free(query); + return; + } + } +} + +/* Timeout outstanding queries */ +static void timeout_dns_queries_event(void *notused) +{ + rb_dlink_node *ptr, *nptr; + + RB_DLINK_FOREACH_SAFE(ptr, nptr, queries.head) + { + struct user_query *query = ptr->data; + + if(query->auth && query->timeout < rb_current_time()) + { + client_fail(query, REPORT_FAIL); + rb_dlinkDelete(ptr, &queries); + rb_free(query); + return; + } + } +} + +static void client_fail(struct user_query *query, dns_message report) +{ + struct auth_client *auth = query->auth; + + rb_strlcpy(auth->hostname, "*", sizeof(auth->hostname)); + notice_client(auth, messages[report]); + cancel_query(query->query); + provider_done(auth, PROVIDER_RDNS); +} + +static void client_success(struct user_query *query) +{ + struct auth_client *auth = query->auth; + + notice_client(auth, messages[REPORT_FOUND]); + cancel_query(query->query); + provider_done(auth, PROVIDER_RDNS); +} + +struct auth_provider rdns_provider = +{ + .id = PROVIDER_RDNS, + .init = client_dns_init, + .destroy = client_dns_destroy, + .start = client_dns_start, + .cancel = client_dns_cancel, + .completed = NULL, +};