From d0d66ffbb1b8ad300e75c191533bc4627cd77707 Mon Sep 17 00:00:00 2001 From: pk910 Date: Wed, 3 Aug 2011 14:37:57 +0200 Subject: [PATCH] initial commit: cloned OGN iauth --- Makefile | 9 ++ iauth.c | 159 +++++++++++++++++++++ iauth.h | 182 ++++++++++++++++++++++++ iauth.php | 135 ++++++++++++++++++ iauth_cmd.c | 278 ++++++++++++++++++++++++++++++++++++ iauth_io.c | 186 ++++++++++++++++++++++++ iauth_query.c | 384 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1333 insertions(+) create mode 100644 Makefile create mode 100644 iauth.c create mode 100644 iauth.h create mode 100644 iauth.php create mode 100644 iauth_cmd.c create mode 100644 iauth_io.c create mode 100644 iauth_query.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7f61802 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +CFLAGS=-O0 -g -Wall -Wshadow -Werror + +all: + gcc -o iauth -O0 -g -Wall iauth.c iauth_io.c iauth_query.c iauth_cmd.c -Wshadow -Werror + +library: + gcc -o libiauth.so + +binary: diff --git a/iauth.c b/iauth.c new file mode 100644 index 0000000..6ebb8d0 --- /dev/null +++ b/iauth.c @@ -0,0 +1,159 @@ +/* + * Written by David Herrmann. + * Dedicated to the Public Domain. + */ + +#include +#include +#include +#include +#include +#include + +#include "iauth.h" + +static unsigned int running = 1; + +static void iauth_signal(int sig) { + iauth_flog(IAUTH_INFO, "Caught signal %d, ignoring...", sig); +} + +/* Parses one single input line. */ +void iauth_parse(char *line) { + unsigned int i, found; + signed int id; + + /* The line has to look like: + * X + */ + + found = 0; + for(i = 0; line[i]; ++i) { + if(line[i] == ' ') { + found = 1; + break; + } + } + + /* Ignore invalid lines. */ + if(!found) { + iauth_flog(IAUTH_WARNING, "Received invalid line; no subcommand specified."); + return; + } + + /* Get the id. */ + id = atoi(line); + + /* Ignore invalid subcommands. */ + if(!line[++i] || line[i] == ' ' || (line[i + 1] != ' ' && line[i + 1])) { + iauth_flog(IAUTH_WARNING, "Received invalid line; invalid subcommand."); + return; + } + + /* Call the subcommand handler. */ + switch(line[i]) { + case 'C': + /* Client Introduction: C */ + if(id >= 0 && id < iauth_clients_size && line[++i]) iauth_cmd_C(id, &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid C line."); + break; + case 'D': + /* Client Disconnect: D */ + if(id >= 0 && id < iauth_clients_size) { + if(iauth_clients[id].id == id) iauth_cmd_D(&iauth_clients[id]); + else /* Ignore all other disconnects. */ ; + } + else iauth_flog(IAUTH_WARNING, "Received invalid D line."); + break; + case 'L': + /* Login On Connect: L [:][ ] */ + if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_L(&iauth_clients[id], &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid L line."); + break; + case 'H': + /* Hurry Up: H */ + if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_H(&iauth_clients[id], &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid H line."); + break; + case 'M': + /* Server Name and Capacity: M */ + if(id == -1 && line[++i]) iauth_cmd_M(&line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid M line."); + break; + case 'N': + /* Hostname Received: N */ + if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_N(&iauth_clients[id], &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid N line."); + break; + case 'd': + /* Hostname Timeout: d */ + if(id >= 0 && id < iauth_clients_size && iauth_clients[id].id == id) iauth_cmd_d(&iauth_clients[id]); + else iauth_flog(IAUTH_WARNING, "Received invalid d line."); + break; + case 'P': + /* Client Password: P : */ + if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_P(&iauth_clients[id], &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid P line."); + break; + case 'U': + /* Client Username: U : */ + if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_U(&iauth_clients[id], &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid U line."); + break; + case 'u': + /* Client Username: u */ + if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_u(&iauth_clients[id], &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid u line."); + break; + case 'n': + /* Client Nickname: n */ + if(id >= 0 && id < iauth_clients_size && line[++i] && iauth_clients[id].id == id) iauth_cmd_n(&iauth_clients[id], &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid n line."); + break; + case 'T': + /* Client Registered: T */ + if(id >= 0 && id < iauth_clients_size && iauth_clients[id].id == id) iauth_cmd_T(&iauth_clients[id]); + else iauth_flog(IAUTH_WARNING, "Received invalid T line."); + break; + case 'E': + /* Error: E : */ + if(line[++i]) iauth_cmd_E(id, &line[++i]); + else iauth_flog(IAUTH_WARNING, "Received invalid E line."); + break; + default: + /* Invalid subcommand; ignore. */ + iauth_flog(IAUTH_WARNING, "Received invalid subcommand."); + return; + } + + /* Subcommand called, nothing to do; return and await next line. */ + return; +} + +signed int main(signed int argc, char *argv[]) { + char *line; + + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGINT, iauth_signal); + signal(SIGQUIT, iauth_signal); + signal(SIGTERM, iauth_signal); + + if(argc > 1) iauth_logfile = argv[1]; + if(argc > 2) iauth_scriptfile = argv[2]; + if(argc > 3) iauth_debug = 1; + + iauth_flog(IAUTH_INFO, "Starting IAuth"); + + iauth_query_version("OGN iauth"); + iauth_query_policy("ARTU"); + iauth_stats_report(); + + while(running && (line = iauth_read())) { + iauth_parse(line); + } + + iauth_flog(IAUTH_INFO, "Stopping IAuth"); + + return EXIT_SUCCESS; +} diff --git a/iauth.h b/iauth.h new file mode 100644 index 0000000..2e0abf6 --- /dev/null +++ b/iauth.h @@ -0,0 +1,182 @@ +/* + * Written by David Herrmann. + * Dedicated to the Public Domain. + */ + +#include +#include +#include + +/* Global configuration. + * It is not recommended to change these values. They are set to + * an especially high value to support even the weirdest environments. + */ +/* Maximal length of a line. */ +#define IAUTH_LINE 4096 +/* Maximal capacity. */ +#define IAUTH_CAPMAX 1000000 +/* Maximum length of accounts/classes/fakehosts etc.. */ +#define IAUTH_DATALEN 256 + +/* 1 if debug mode is enabled, otherwise 0. */ +extern unsigned int iauth_debug; + +/* Log functions. + * These functions write status data into a log file. + * They all do the same but take either a formatted argument + * or a va_list buffer. + * Error functions log the message and terminate the application, + * log functions only log the message. + */ +#define IAUTH_FATAL 0 +#define IAUTH_WARNING 1 +#define IAUTH_INFO 2 +#define IAUTH_DEBUG 3 +extern const char *iauth_logfile; +extern void iauth_ferror(const char *format, ...); +extern void iauth_verror(const char *format, va_list list); +extern void iauth_flog(unsigned int type, const char *format, ...); +extern void iauth_vlog(unsigned int type, const char *format, va_list list); +extern void iauth_eflog(const char *format, ...); +extern void iauth_evlog(const char *format, va_list list); + +/* IO functions. + * These functions either write a line to the IAuth or read a + * single line from the IAuth. + */ +extern char *iauth_read(); +extern void iauth_fsend(const char *format, ...); +extern void iauth_vsend(const char *format, va_list list); + +/* Allocates/frees memory. + * Aborts with an appropriate message if the allocation fails. + */ +static inline void *iauth_malloc(size_t size) { + void *mem; + if(!(mem = malloc(size))) iauth_eflog("Memory allocation failed."); + memset(mem, 0, size); + return mem; +} +static inline char *iauth_strdup(const char *str) { + char *mem; + if(!(mem = strdup(str))) iauth_eflog("strdup() failed."); + return mem; +} +#define iauth_free(x) ((void)((x)?free(x):0)) + +/* User management. + * Adds or removes a user. + */ +struct iauth_client { + signed int id; + char *ip; + unsigned short port; + char *lo_ip; + unsigned short lo_port; + char *host; + char *c_host; + char *c_serv; + char *nick; + char *username; + char *realname; + char *account; + char *fakehost; + char *cclass; + char *password; + char *ident; + unsigned int state_r : 1; /* Is in "registering" state. */ + unsigned int state_h : 1; /* Is in "hurry" state. */ +}; +#define iauth_set(x, y) (iauth_free(x), (x = y)) +extern struct iauth_client *iauth_clients; +extern unsigned int iauth_clients_size; +extern void iauth_setcap(unsigned int cap); +extern void iauth_addid(signed int id); +extern void iauth_delid(signed int id); + +/* Request handler. + * The real requests are outsourced to a scriptfile. + * The iauth process spawns the scriptfile, passes the + * data as arguments and exspects the process to return + * the result on stdout. + * The scriptfile is spawned for every request. + */ +extern char *iauth_scriptfile; +struct iauth_result { + char cclass[IAUTH_DATALEN + 1]; + char ident[IAUTH_DATALEN + 1]; + char host[IAUTH_DATALEN + 1]; + char ip[IAUTH_DATALEN + 1]; + char modes[IAUTH_DATALEN + 1]; +}; +extern const struct iauth_result *iauth_query(struct iauth_client *cli); +extern char iauth_servname[IAUTH_DATALEN + 1]; + +/* Sends a specific request to the ircd. + * This is a less generic but easier to use interface + * for the iauth_[vf]send() commands. This interface also + * correctly sends statistics. + */ + +/* Operator Notification: > : */ +extern void iauth_query_fnotify(const char *format, ...); +extern void iauth_query_vnotify(const char *format, va_list list); +/* Set Debug Level: G */ +extern void iauth_query_debug(unsigned int debuglevel); +/* Set Policy Options: O */ +extern void iauth_query_policy(const char *policy); +/* iauth Program Version: V : */ +extern void iauth_query_version(const char *version); +/* Start of new configuration: a */ +extern void iauth_query_newconf(); +/* Configuration Information: A : */ +extern void iauth_query_config(const char *hosts, const char *module, const char *value); +/* Start of new statistics: s */ +extern void iauth_query_newstats(); +/* Statistics Information: S : */ +extern void iauth_query_stats(const char *module, const char *value); +/* Forced Username: o */ +extern void iauth_query_set_username(signed int id, const char *username); +/* Trusted Username: U */ +extern void iauth_query_trust_username(signed int id, const char *username); +/* Untrusted Username: u */ +extern void iauth_query_distrust_username(signed int id, const char *username); +/* Client Hostname: N */ +extern void iauth_query_sethost(signed int id, const char *hostname); +/* Client IP Address: I */ +extern void iauth_query_setip(signed int id, const char *ip); +/* Adjust User Mode: M + */ +extern void iauth_query_setmodes(signed int id, const char *modes); +/* Challenge User: C : */ +extern void iauth_query_challenge(signed int id, const char *challenge); +/* Quietly Kill Client: k : */ +extern void iauth_query_reject(signed int id, const char *reason); +/* Kill Client: K : */ +extern void iauth_query_kill(signed int id, const char *reason); +/* Done Checking: D [class] */ +extern void iauth_query_assign(signed int id, const char *cclass); +/* Registered User: R [class] */ +extern void iauth_query_register(signed int id, const char *account, const char *cclass); + +/* Subcommand handlers. */ +extern void iauth_cmd_C(signed int id, char *arg); +extern void iauth_cmd_D(struct iauth_client *client); +extern void iauth_cmd_L(struct iauth_client *client, char *arg); +extern void iauth_cmd_H(struct iauth_client *client, char *arg); +extern void iauth_cmd_M(char *arg); +extern void iauth_cmd_N(struct iauth_client *client, char *arg); +extern void iauth_cmd_d(struct iauth_client *client); +extern void iauth_cmd_P(struct iauth_client *client, char *arg); +extern void iauth_cmd_U(struct iauth_client *client, char *arg); +extern void iauth_cmd_u(struct iauth_client *client, char *arg); +extern void iauth_cmd_n(struct iauth_client *client, char *arg); +extern void iauth_cmd_T(struct iauth_client *client); +extern void iauth_cmd_E(signed int id, char *arg); + +/* Statistics */ +extern void iauth_stats_report(); +extern void iauth_stats_loc_allow(); +extern void iauth_stats_loc_deny(); +extern void iauth_stats_def_allow(); +extern void iauth_stats_def_deny(); + diff --git a/iauth.php b/iauth.php new file mode 100644 index 0000000..267e871 --- /dev/null +++ b/iauth.php @@ -0,0 +1,135 @@ +#!/usr/bin/php + diff --git a/iauth_cmd.c b/iauth_cmd.c new file mode 100644 index 0000000..5480c38 --- /dev/null +++ b/iauth_cmd.c @@ -0,0 +1,278 @@ +/* + * Written by David Herrmann. + * Dedicated to the Public Domain. + */ + +#include +#include +#include +#include + +#include "iauth.h" + +char iauth_servname[IAUTH_DATALEN + 1]; + +static char *iauth_next_data(char **arg) { + char *sep, *ret; + unsigned int len; + + while(**arg == ' ') ++*arg; + if(!**arg) return NULL; + if(**arg == ':' || !(sep = strchr(*arg, ' '))) { + if(**arg == ':') ++*arg; + len = strlen(*arg); + ret = iauth_malloc(len + 1); + strcpy(ret, *arg); + *arg += len; + if(len > IAUTH_DATALEN) ret[IAUTH_DATALEN] = 0; + else ret[len] = 0; + return ret; + } + else { + *sep = 0; + len = strlen(*arg); + ret = iauth_malloc(len + 1); + strcpy(ret, *arg); + if(len > IAUTH_DATALEN) ret[IAUTH_DATALEN] = 0; + else ret[len] = 0; + *sep = ' '; + *arg = sep + 1; + return ret; + } +} + +/* Client Introduction: C */ +void iauth_cmd_C(signed int id, char *arg) { + char *str; + struct iauth_client *cli; + + iauth_delid(id); + iauth_addid(id); + cli = &iauth_clients[id]; + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(cli->ip, str); + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + cli->port = atoi(str); + iauth_free(str); + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(cli->lo_ip, str); + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + cli->lo_port = atoi(str); + iauth_free(str); + + iauth_flog(IAUTH_INFO, "New client (%d) from '%s':%hu to '%s':%hu.", id, cli->ip, cli->port, cli->lo_ip, cli->lo_port); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid C line."); + iauth_delid(id); + return; +} + +/* Client Disconnect: D */ +void iauth_cmd_D(struct iauth_client *client) { + iauth_delid(client->id); +} + +/* Login On Connect: L [:][ ] */ +void iauth_cmd_L(struct iauth_client *client, char *arg) { + char *str; + const struct iauth_result *res; + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->account, str); + + str = iauth_next_data(&arg); + if(str) iauth_set(client->fakehost, str); + + /* Query database for account query. */ + res = iauth_query(client); + if(!res) { + iauth_query_reject(client->id, "Access denied."); + iauth_stats_loc_deny(); + } + else { + if(*res->ident) iauth_query_set_username(client->id, res->ident); + if(*res->host) iauth_query_sethost(client->id, res->host); + if(*res->ip) iauth_query_setip(client->id, res->ip); + if(*res->modes) iauth_query_setmodes(client->id, res->modes); + iauth_query_assign(client->id, (*res->cclass)?res->cclass:NULL); + iauth_stats_loc_allow(); + } + + iauth_delid(client->id); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid L line."); + return; +} + +/* Hurry Up: H */ +void iauth_cmd_H(struct iauth_client *client, char *arg) { + char *str; + const struct iauth_result *res; + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->cclass, str); + client->state_h = 1; + + /* Query database for account query. */ + res = iauth_query(client); + if(!res) { + iauth_query_reject(client->id, "Access denied."); + iauth_stats_def_deny(); + } + else { + if(*res->ident) iauth_query_set_username(client->id, res->ident); + if(*res->host) iauth_query_sethost(client->id, res->host); + if(*res->ip) iauth_query_setip(client->id, res->ip); + if(*res->modes) iauth_query_setmodes(client->id, res->modes); + iauth_query_assign(client->id, (*res->cclass)?res->cclass:NULL); + iauth_stats_def_allow(); + } + iauth_delid(client->id); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid H line."); + return; +} + +/* Server Name and Capacity: M */ +void iauth_cmd_M(char *arg) { + char *server = NULL, *str = NULL; + unsigned int cap; + + server = iauth_next_data(&arg); + if(!server) goto parse_error; + + str = iauth_next_data(&arg); + if(!str || !*str || (cap = atoi(str)) == 0) goto parse_error; + iauth_setcap(cap); + iauth_flog(IAUTH_INFO, "Setting server (%s) capacity to: %u.", server, cap); + strcpy(iauth_servname, server); + iauth_free(str); + iauth_free(server); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid M line."); + iauth_free(server); + iauth_free(str); + return; +} + +/* Hostname Received: N */ +void iauth_cmd_N(struct iauth_client *client, char *arg) { + char *str; + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->host, str); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid N line."); + return; +} + +/* Hostname Timeout: d */ +void iauth_cmd_d(struct iauth_client *client) { + char *str; + + str = iauth_malloc(1); + iauth_set(client->host, str); + + return; +} + +/* Client Password: P : */ +void iauth_cmd_P(struct iauth_client *client, char *arg) { + char *str; + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->password, str); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid P line."); + return; +} + +/* Client Username: U : */ +void iauth_cmd_U(struct iauth_client *client, char *arg) { + char *str; + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->username, str); + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->c_host, str); + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->c_serv, str); + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->realname, str); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid U line."); + return; +} + +/* Client Username: u */ +void iauth_cmd_u(struct iauth_client *client, char *arg) { + char *str; + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->ident, str); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid u line."); + return; +} + +/* Client Nickname: n */ +void iauth_cmd_n(struct iauth_client *client, char *arg) { + char *str; + + str = iauth_next_data(&arg); + if(!str) goto parse_error; + iauth_set(client->nick, str); + + return; + parse_error: + iauth_flog(IAUTH_WARNING, "Parse error: Invalid n line."); + return; +} + +/* Client Registered: T */ +void iauth_cmd_T(struct iauth_client *client) { + iauth_flog(IAUTH_INFO, "Client %d was registered without IAuth answer.", client->id); + iauth_delid(client->id); +} + +/* Error: E : */ +void iauth_cmd_E(signed int id, char *arg) { + iauth_flog(IAUTH_WARNING, "Received IRCd error: %s", arg); +} + diff --git a/iauth_io.c b/iauth_io.c new file mode 100644 index 0000000..995d01b --- /dev/null +++ b/iauth_io.c @@ -0,0 +1,186 @@ +/* + * Written by David Herrmann. + * Dedicated to the Public Domain. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "iauth.h" + +/* /path/to/logfile.log or NULL. */ +const char *iauth_logfile = NULL; +/* Current logfile or NULL. */ +FILE *logfile = NULL; +/* 1 if debug mode is enabled, otherwise 0. */ +unsigned int iauth_debug = 0; + +static void iauth_error(const char *msg, size_t len) { + fwrite(msg, 1, len, stderr); + fwrite("\n", 1, 1, stderr); + /*abort();*/ + exit(1); +} + +void iauth_ferror(const char *format, ...) { + va_list arg; + + va_start(arg, format); + iauth_verror(format, arg); + va_end(arg); +} + +void iauth_verror(const char *format, va_list list) { + char buffer[IAUTH_LINE + 1]; + size_t len; + + memset(buffer, 0, IAUTH_LINE + 1); + len = vsnprintf(buffer, IAUTH_LINE, format, list); + if(!len) len = snprintf(buffer, IAUTH_LINE, ""); + iauth_error(buffer, len); +} + +/* Checks whether we need to reopen the logfile. + * Aborts the application if the logfile cannot be + * opened. + */ +static void iauth_log_reopen() { + if(iauth_logfile) { + if(logfile) fclose(logfile); + logfile = fopen(iauth_logfile, "a"); + if(!logfile) iauth_ferror("Logfile '%s' could not be opened in mode 'a'; errno = '%d'.", iauth_logfile, errno); + iauth_logfile = NULL; + } + else if(!logfile) iauth_ferror("No logfile specified."); +} + +static void iauth_log(unsigned int type, const char *msg, size_t len) { + struct tm *tstamp; + time_t curtime; + char buffer[IAUTH_LINE + 1]; + size_t len2; + + iauth_log_reopen(); + + curtime = time(NULL); + tstamp = localtime(&curtime); + memset(buffer, 0, IAUTH_LINE + 1); + len2 = snprintf(buffer, IAUTH_LINE + 1, "[%02d.%02d.%d %02d:%02d:%02d]", tstamp->tm_mday, tstamp->tm_mon + 1, tstamp->tm_year + 1900, + tstamp->tm_hour, tstamp->tm_min, tstamp->tm_sec); + len2 += snprintf(&buffer[len2], IAUTH_LINE + 1 - len2, " (%s): ", (type == IAUTH_FATAL)?"FATAL":(type == IAUTH_WARNING)?"WARNING": + (type == IAUTH_INFO)?"INFO":(type == IAUTH_DEBUG)?"DEBUG":"UNKNOWN"); + + if(fwrite(buffer, 1, len2, logfile) != len2) iauth_ferror("Write operation on logfile failed; errno = '%d'.", errno); + if(fwrite(msg, 1, len, logfile) != len) iauth_ferror("Write operation on logfile failed; errno = '%d'.", errno); + fwrite("\n", 1, 1, logfile); + fflush(logfile); +} + +void iauth_flog(unsigned int type, const char *format, ...) { + va_list arg; + + va_start(arg, format); + iauth_vlog(type, format, arg); + va_end(arg); +} + +void iauth_vlog(unsigned int type, const char *format, va_list list) { + char buffer[IAUTH_LINE + 1]; + size_t len; + + memset(buffer, 0, IAUTH_LINE + 1); + len = vsnprintf(buffer, IAUTH_LINE, format, list); + if(!len) len = snprintf(buffer, IAUTH_LINE, ""); + iauth_log(type, buffer, len); +} + +void iauth_eflog(const char *format, ...) { + va_list arg; + + va_start(arg, format); + iauth_evlog(format, arg); + va_end(arg); +} + +void iauth_evlog(const char *format, va_list list) { + char buffer[IAUTH_LINE + 1]; + size_t len; + + memset(buffer, 0, IAUTH_LINE + 1); + len = vsnprintf(buffer, IAUTH_LINE, format, list); + if(!len) len = snprintf(buffer, IAUTH_LINE, ""); + iauth_log(IAUTH_FATAL, buffer, len); + iauth_error(buffer, len); +} + +char *iauth_read() { + static char buffer[IAUTH_LINE + 1]; + unsigned int i, ignore = 0; + + next_line: + + if(!fgets(buffer, IAUTH_LINE, stdin)) { + if(feof(stdin)) return NULL; + iauth_eflog("Reading on stdin failed; errno = '%d'.", errno); + } + buffer[IAUTH_LINE] = 0; + + for(i = 0; i < IAUTH_LINE; ++i) { + if(buffer[i] == '\n') { + if(ignore) { + ignore = 0; + goto next_line; + } + if(i == 0) goto next_line; + if(buffer[i - 1] == '\r') { + if(i == 1) goto next_line; + buffer[i - 1] = 0; + } + else buffer[i] = 0; + if(iauth_debug) { + iauth_flog(IAUTH_DEBUG, "IN -> %s", buffer); + } + return buffer; + } + } + + /* Too long message. Discard it! */ + iauth_flog(IAUTH_WARNING, "Message exceeded maximum length of '%d' bytes.", IAUTH_LINE); + ignore = 1; + goto next_line; +} + +void iauth_fsend(const char *format, ...) { + va_list arg; + + va_start(arg, format); + iauth_vsend(format, arg); + va_end(arg); +} + +void iauth_vsend(const char *format, va_list list) { + char buffer[IAUTH_LINE + 1]; + size_t len, len2; + + memset(buffer, 0, IAUTH_LINE + 1); + if(iauth_debug) { + len2 = snprintf(buffer, IAUTH_LINE, "OUT <- "); + len = vsnprintf(&buffer[len2], IAUTH_LINE, format, list); + if(!len) return; + iauth_log(IAUTH_DEBUG, buffer, len + len2); + if(write(0, &buffer[len2], len) != len) iauth_eflog("IAuth write operation failed; errno = '%d'", errno); + write(0, "\r\n", 2); + } + else { + len = vsnprintf(buffer, IAUTH_LINE, format, list); + if(!len) return; + if(write(0, buffer, len) != len) iauth_eflog("IAuth write operation failed; errno = '%d'", errno); + write(0, "\r\n", 2); + } +} diff --git a/iauth_query.c b/iauth_query.c new file mode 100644 index 0000000..7c7c714 --- /dev/null +++ b/iauth_query.c @@ -0,0 +1,384 @@ +/* + * Written by David Herrmann. + * Dedicated to the Public Domain. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "iauth.h" + +/* File which is spawned on a query. */ +char *iauth_scriptfile = NULL; + +const struct iauth_result *iauth_query(struct iauth_client *cli) { + static struct iauth_result res; + static char buffer[IAUTH_DATALEN * 5 + 5]; + pid_t cpid; + signed int fds[2], ret; + char ** parv; + char portbuf[6], portbuf2[6]; + char c = 0; + char *arg, *tread; + + memset(&res, 0, sizeof(res)); + memset(portbuf, 0, 6); + memset(portbuf2, 0, 6); + if(!iauth_scriptfile) return &res; + if(socketpair(AF_UNIX, SOCK_STREAM, 0, fds) != 0) { + iauth_flog(IAUTH_WARNING, "socketpair() failed; errno = '%d'.", errno); + return NULL; + } + + cpid = fork(); + if(cpid < 0) { + close(fds[0]); + close(fds[1]); + iauth_flog(IAUTH_WARNING, "fork() failed; errno = '%d'.", errno); + return NULL; + } + + if(cpid == 0) { + /* This is the child. + * Close the parents socket. Spawn the DB process with the socket + * set as stdout. + * When done, close the socket and exit the child. + * We directly fork() again to prevent zombies. + */ + close(fds[0]); + cpid = fork(); + if(cpid < 0) exit(EXIT_FAILURE); + if(cpid != 0) exit(EXIT_SUCCESS); + + #define ISIZE 18 + parv = iauth_malloc(sizeof(char*) * ISIZE); + parv[0] = iauth_scriptfile; + parv[1] = cli->ip?cli->ip:&c; + snprintf(portbuf, 5, "%hu", cli->port); + parv[2] = portbuf; + parv[3] = cli->lo_ip?cli->lo_ip:&c; + snprintf(portbuf2, 5, "%hu", cli->lo_port); + parv[4] = portbuf2; + parv[5] = cli->host?cli->host:&c; + parv[6] = cli->c_host?cli->c_host:&c; + parv[7] = cli->c_serv?cli->c_serv:&c; + parv[8] = cli->nick?cli->nick:&c; + parv[9] = cli->username?cli->username:&c; + parv[10] = cli->realname?cli->realname:&c; + parv[11] = cli->account?cli->account:&c; + parv[12] = cli->fakehost?cli->fakehost:&c; + parv[13] = cli->cclass?cli->cclass:&c; + parv[14] = cli->password?cli->password:&c; + parv[15] = cli->ident?cli->ident:&c; + parv[16] = iauth_servname; + parv[ISIZE - 1] = NULL; + #undef ISIZE + + if(dup2(fds[1], 1) != -1) { + execvp(iauth_scriptfile, parv); + iauth_flog(IAUTH_WARNING, "execvp() failed; errno = '%d'.", errno); + } + else iauth_flog(IAUTH_WARNING, "dup2() failed; errno = '%d'.", errno); + iauth_free(parv); + close(fds[1]); + exit(EXIT_FAILURE); + } + + /* We are the parent. Read on the file descriptor until it is closed. */ + close(fds[1]); + + /* Wait for client. The client directly forked again, so this should + * not *really* block. + * This whole mechanism prevents zombies. + */ + wait(NULL); + + tread = buffer; + if((ret = read(fds[0], buffer, sizeof(buffer))) < 1) { + close(fds[0]); + return NULL; + } + buffer[sizeof(buffer) - 1] = 0; + + /* Read class. */ + if(!(arg = strchr(tread, ' '))) { + close(fds[0]); + return NULL; + } + *arg++ = 0; + if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.cclass[0] = 0; + else strcpy(res.cclass, tread); + + /* Read ident. */ + tread = arg; + if(!(arg = strchr(tread, ' '))) { + close(fds[0]); + return NULL; + } + *arg++ = 0; + if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.ident[0] = 0; + else strcpy(res.ident, tread); + + /* Read host. */ + tread = arg; + if(!(arg = strchr(tread, ' '))) { + close(fds[0]); + return NULL; + } + *arg++ = 0; + if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.host[0] = 0; + else strcpy(res.host, tread); + + /* Read ip. */ + tread = arg; + if(!(arg = strchr(tread, ' '))) { + close(fds[0]); + return NULL; + } + *arg++ = 0; + if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.ip[0] = 0; + else strcpy(res.ip, tread); + + /* Read modes string. */ + tread = arg; + if(!*tread) { + close(fds[0]); + return NULL; + } + if(strcmp(tread, "$") == 0 || strlen(tread) > IAUTH_DATALEN) res.modes[0] = 0; + else strcpy(res.modes, tread); + + close(fds[0]); + return &res; +} + +/* User management. + * Adds or removes a user. + */ +struct iauth_client *iauth_clients = NULL; +unsigned int iauth_clients_size = 0; + +void iauth_setcap(unsigned int cap) { + if(iauth_clients_size) return; + iauth_clients = iauth_malloc(sizeof(struct iauth_client) * cap); + iauth_clients_size = cap; +} + +void iauth_addid(signed int id) { + if(id < 0) return; + if(!iauth_clients_size || id >= iauth_clients_size) return; + memset(&iauth_clients[id], 0, sizeof(struct iauth_client)); + iauth_clients[id].state_r = 1; + iauth_clients[id].id = id; +} + +void iauth_delid(signed int id) { + if(id < 0) return; + if(!iauth_clients_size || id >= iauth_clients_size) return; + iauth_free(iauth_clients[id].ip); + iauth_free(iauth_clients[id].lo_ip); + iauth_free(iauth_clients[id].host); + iauth_free(iauth_clients[id].c_host); + iauth_free(iauth_clients[id].c_serv); + iauth_free(iauth_clients[id].nick); + iauth_free(iauth_clients[id].username); + iauth_free(iauth_clients[id].realname); + iauth_free(iauth_clients[id].account); + iauth_free(iauth_clients[id].fakehost); + iauth_free(iauth_clients[id].cclass); + iauth_free(iauth_clients[id].password); + iauth_free(iauth_clients[id].ident); + memset(&iauth_clients[id], 0, sizeof(struct iauth_client)); +} + +static unsigned int loc_allow = 0; +static unsigned int loc_deny = 0; +static unsigned int def_allow = 0; +static unsigned int def_deny = 0; + +void iauth_stats_report() { + static unsigned int stats = 0; + static char buffer[512]; + + ++stats; + + if(stats == 1) { + iauth_query_newconf(); + iauth_query_config("*", "loc", "Login-On-Connect handler"); + iauth_query_config("*", "def", "Default handler"); + } + if(stats < 10) goto report; + else if((stats % 10) == 0) goto report; + else return; + + report: + iauth_query_newstats(); + sprintf(buffer, "Count: %u Allowed: %u Denied: %u", loc_allow + loc_deny, loc_allow, loc_deny); + iauth_query_stats("loc", buffer); + sprintf(buffer, "Count: %u Allowed: %u Denied: %u", def_allow + def_deny, def_allow, def_deny); + iauth_query_stats("def", buffer); +} + +void iauth_stats_loc_allow() { + ++loc_allow; + iauth_stats_report(); +} + +void iauth_stats_loc_deny() { + ++loc_deny; + iauth_stats_report(); +} + +void iauth_stats_def_allow() { + ++def_allow; + iauth_stats_report(); +} + +void iauth_stats_def_deny() { + ++def_deny; + iauth_stats_report(); +} + +/* Sends a specific request to the ircd. + * This is a less generic but easier to use interface + * for the iauth_[vf]send() commands. This interface also + * correctly sends statistics. + */ + +/* Operator Notification: > : */ +extern void iauth_query_fnotify(const char *format, ...) { + va_list arg; + + va_start(arg, format); + iauth_query_vnotify(format, arg); + va_end(arg); +} +extern void iauth_query_vnotify(const char *format, va_list list) { + char buffer[IAUTH_LINE + 1]; + + buffer[IAUTH_LINE] = 0; + sprintf(buffer, "> :"); + vsnprintf(buffer, IAUTH_LINE - 4, format, list); + iauth_fsend(buffer); +} + +/* Set Debug Level: G */ +extern void iauth_query_debug(unsigned int debuglevel) { + iauth_fsend("G %u", debuglevel); +} + +/* Set Policy Options: O */ +extern void iauth_query_policy(const char *policy) { + iauth_fsend("O %s", policy); +} + +/* iauth Program Version: V : */ +extern void iauth_query_version(const char *version) { + iauth_fsend("V :%s", version); +} + +/* Start of new configuration: a */ +extern void iauth_query_newconf() { + iauth_fsend("a"); +} + +/* Configuration Information: A : */ +extern void iauth_query_config(const char *hosts, const char *module, const char *value) { + iauth_fsend("A %s %s :%s", hosts, module, value); +} + +/* Start of new statistics: s */ +extern void iauth_query_newstats() { + iauth_fsend("s"); +} + +/* Statistics Information: S : */ +extern void iauth_query_stats(const char *module, const char *value) { + iauth_fsend("S %s :%s", module, value); +} + +/* Forced Username: o */ +extern void iauth_query_set_username(signed int id, const char *username) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("o %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username); +} + +/* Trusted Username: U */ +extern void iauth_query_trust_username(signed int id, const char *username) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("U %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username); +} + +/* Untrusted Username: u */ +extern void iauth_query_distrust_username(signed int id, const char *username) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("u %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, username); +} + +/* Client Hostname: N */ +extern void iauth_query_sethost(signed int id, const char *hostname) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("N %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, hostname); +} + +/* Client IP Address: I */ +extern void iauth_query_setip(signed int id, const char *ip) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("I %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, ip); + iauth_free(iauth_clients[id].ip); + iauth_clients[id].ip = iauth_strdup(ip); +} + +/* Adjust User Mode: M + */ +extern void iauth_query_setmodes(signed int id, const char *modes) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("M %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, modes); +} + +/* Challenge User: C : */ +extern void iauth_query_challenge(signed int id, const char *challenge) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("C %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, challenge); +} + +/* Quietly Kill Client: k : */ +extern void iauth_query_reject(signed int id, const char *reason) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("k %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, reason); + iauth_delid(id); +} + +/* Kill Client: K : */ +extern void iauth_query_kill(signed int id, const char *reason) { + if(id < 0 || id >= iauth_clients_size) return; + iauth_fsend("K %d %s %hu :%s", id, iauth_clients[id].ip, iauth_clients[id].port, reason); + iauth_delid(id); +} + +/* Done Checking: D [class] */ +extern void iauth_query_assign(signed int id, const char *cclass) { + if(id < 0 || id >= iauth_clients_size) return; + if(cclass) + iauth_fsend("D %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, cclass); + else + iauth_fsend("D %d %s %hu", id, iauth_clients[id].ip, iauth_clients[id].port); + iauth_delid(id); +} + +/* Registered User: R [class] */ +extern void iauth_query_register(signed int id, const char *account, const char *cclass) { + if(id < 0 || id >= iauth_clients_size) return; + if(cclass) + iauth_fsend("D %d %s %hu %s %s", id, iauth_clients[id].ip, iauth_clients[id].port, account, cclass); + else + iauth_fsend("D %d %s %hu %s", id, iauth_clients[id].ip, iauth_clients[id].port, account); + iauth_delid(id); +} + -- 2.20.1