Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion doc/iauthd-c.conf.example
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,13 @@ iauth {
// can safely be removed; with earlier versions, that will cause
// Authorization Timeout rejections instead of accepting the
// clients.
//
// This should not be used together with "verify".
timeout 30

// If set, IAuth will kill a connection using +x!.
// Useful when transitioning away from LoC to SASL.
kill_loc "Login-on-Connect is disabled. You may authenticate using SASL. Visit http://undernet.org/sasl for more information."
}

iauth_xquery {
Expand All @@ -62,8 +68,10 @@ iauth_xquery {
// - login-ipr
// - dronecheck
// - combined (almost like dronecheck plus login-ipr)
// - verify
channels.example.org login-ipr
botcheck.example.org dronecheck
verify.example.org verify
}

iauth_class {
Expand All @@ -83,7 +91,7 @@ iauth_class {
username "joe-oper"
address "127.0.0.0/8"
}
"r002" { class trusted; account ircoper }
"r002" { class trusted; account "ircoper" }
"r003" { class clients; xreply_ok "euworld.example.org" }
"r004" { class default_clients }
}
23 changes: 23 additions & 0 deletions modules/iauth.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ unsigned int irc_check_mask(const irc_inaddr *check, const irc_inaddr *mask, uns
/** Maximum length of an iauthd-c standard routing string. */
#define ROUTINGLEN (IRC_NTOP_MAX + 20)

/** Maximum length of a TLS fingerprint. */
#define CERTLEN 64

/** Possible states for a client (with respect to IAuth). */
enum iauth_client_state {
IAUTH_REGISTER,
Expand All @@ -109,6 +112,10 @@ enum iauth_client_state {
enum iauth_flags {
/** Set when we have made a decision for this request. */
IAUTH_RESPONDED,
/** Set when the client has sent a CAP LS. */
IAUTH_GOT_CAP_START,
/** Set when the client has sent CAP END. */
IAUTH_GOT_CAP_END,
/** Set when we have sent a "soft done" for this request. */
IAUTH_SOFT_DONE,
/** Set when we get an 'N' or 'd' message. */
Expand All @@ -121,6 +128,10 @@ enum iauth_flags {
IAUTH_GOT_USER_INFO,
/** Set when we get a 'P' message. */
IAUTH_GOT_PASSWORD,
/** Set when we get a 'Z' message. */
IAUTH_GOT_FINGERPRINT,
/** Set when we get an 'A' message. */
IAUTH_GOT_ACCOUNT,
/** Set when we get a 'H' message. */
IAUTH_GOT_HURRY_UP,
/** Set when we get blank 'u' message, but have not gotten 'U'. */
Expand Down Expand Up @@ -212,6 +223,9 @@ struct iauth_request {
/** Text form of #remote_addr. */
char text_addr[IRC_NTOP_MAX];

/** TLS fingerprint. */
char tls_fingerprint[CERTLEN + 1];

/** Contains submodule-specific data.
*
* No special cleanup of the data is performed. The first element
Expand Down Expand Up @@ -274,6 +288,13 @@ struct iauth_module {
*/
void (*error)(struct iauth_request *req, const char type[], const char info[]);

/** Handler for calculating effective flags that may change dynamically.
* If this callback is provided, it will be called during iauth_check_request()
* to compute the current effective flags for this module, which are OR'd into
* the global effective_flags used for checking request readiness.
*/
void (*calc_effective_flags)(const struct iauth_request *req, struct iauth_flagset *flags_out);

/** Handler for simple field changes.
*
* In particular, \a flag gets #IAUTH_GOT_HOSTNAME for a 'N' or 'd'
Expand Down Expand Up @@ -332,6 +353,8 @@ struct iauth_module {
void iauth_register_module(struct iauth_module *plugin);
void iauth_unregister_module(struct iauth_module *plugin);

const char* iauth_get_kill_loc(void);

/* These functions generate IAuth messages to the server. */
void iauth_accept(struct iauth_request *req);
void iauth_soft_done(struct iauth_request *req);
Expand Down
110 changes: 108 additions & 2 deletions modules/iauth_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ static struct conf_node_object *iauth_conf;
/** Duration of the request timeout. */
static struct conf_node_string *iauth_conf_timeout;

/** Whether to kill connections using +x! when login, login-ipr or combined is not enabled. */
static struct conf_node_string *iauth_conf_kill_loc;

/** Last assigned serial number. */
static unsigned int iauth_serial;

Expand Down Expand Up @@ -157,6 +160,15 @@ void iauth_unregister_module(struct iauth_module *plugin)
calc_iauth_flags();
}

/** Returns the kill_loc configuration value. Empty if unset.
*
* \return The kill_loc configuration value. Empty string if unset.
*/
const char* iauth_get_kill_loc(void)
{
return iauth_conf_kill_loc ? iauth_conf_kill_loc->parsed.p_string : "";
}

/** Looks up the request for \a client_id.
*
* \param[in] client_id ircd-assigned client identifier
Expand Down Expand Up @@ -244,9 +256,23 @@ struct iauth_request *iauth_validate_request(const char routing[])
*/
void iauth_check_request(struct iauth_request *request)
{
struct iauth_module *plugin;
struct set_node *node;
struct iauth_flagset effective_flags = iauth_flags;

/* Allow modules to adjust their effective requirements dynamically */
for (node = set_first(iauth_modules); node; node = set_next(node)) {
plugin = ENCLOSING_STRUCT(node, struct iauth_module, node);
if (plugin->calc_effective_flags != NULL) {
struct iauth_flagset module_flags;
plugin->calc_effective_flags(request, &module_flags);
BITSET_OR(effective_flags, effective_flags, module_flags);
}
}

if (request->holds == 0
&& !BITSET_GET(request->flags, IAUTH_RESPONDED)
&& !BITSET_H_ANDNOT(iauth_flags, request->flags)) {
&& !BITSET_H_ANDNOT(effective_flags, request->flags)) {
if (request->soft_holds == 0)
iauth_accept(request);
else if (!BITSET_GET(request->flags, IAUTH_SOFT_DONE)) {
Expand All @@ -263,7 +289,7 @@ void iauth_check_request(struct iauth_request *request)
request->client);
} else {
log_message(iauth_log, LOG_DEBUG, " -> client %d still waiting: %#x & ~%#x (plus %d soft holds)",
request->client, iauth_flags.bits[0], request->flags.bits[0],
request->client, effective_flags.bits[0], request->flags.bits[0],
request->soft_holds);
}
}
Expand Down Expand Up @@ -408,6 +434,11 @@ static void notify_pre_registered(struct iauth_request *req)

void iauth_accept(struct iauth_request *req)
{
if (BITSET_GET(req->flags, IAUTH_GOT_CAP_START) && !BITSET_GET(req->flags, IAUTH_GOT_CAP_END)) {
log_message(iauth_log, LOG_DEBUG, " -> client %d has CAP pending, delaying registration", req->client);
return;
}

assert(!BITSET_GET(req->flags, IAUTH_RESPONDED));
notify_pre_registered(req);
BITSET_SET(req->flags, IAUTH_RESPONDED);
Expand Down Expand Up @@ -691,6 +722,52 @@ static void parse_nick(struct iauth_request *req, char nick[])
iauth_check_request(req);
}

static void parse_fingerprint(struct iauth_request *req, char fingerprint[])
{
struct iauth_module *plugin;
struct set_node *node;

if (!req) {
iauth_send_opers("ircd sent garbage: -1 Z ...");
return;
}
if (fingerprint) {
strncpy(req->tls_fingerprint, fingerprint, CERTLEN);
req->tls_fingerprint[CERTLEN] = '\0';
BITSET_SET(req->flags, IAUTH_GOT_FINGERPRINT);
}

for (node = set_first(iauth_modules); node; node = set_next(node)) {
plugin = ENCLOSING_STRUCT(node, struct iauth_module, node);
if (plugin->field_change != NULL)
plugin->field_change(req, IAUTH_GOT_FINGERPRINT);
}
iauth_check_request(req);
}

static void parse_account(struct iauth_request *req, char account[])
{
struct iauth_module *plugin;
struct set_node *node;

if (!req) {
iauth_send_opers("ircd sent garbage: -1 A ...");
return;
}
if (account) {
strncpy(req->account, account, ACCOUNTLEN);
req->account[ACCOUNTLEN] = '\0';
BITSET_SET(req->flags, IAUTH_GOT_ACCOUNT);
}

for (node = set_first(iauth_modules); node; node = set_next(node)) {
plugin = ENCLOSING_STRUCT(node, struct iauth_module, node);
if (plugin->field_change != NULL)
plugin->field_change(req, IAUTH_GOT_ACCOUNT);
}
iauth_check_request(req);
}

static void parse_hurry_up(struct iauth_request *req)
{
struct iauth_module *plugin;
Expand Down Expand Up @@ -744,6 +821,22 @@ static void parse_error(struct iauth_request *req, int argc, char *argv[])
}
}

static void parse_cap(struct iauth_request *req, enum iauth_flags flag)
{
struct iauth_module *plugin;
struct set_node *node;

BITSET_SET(req->flags, flag);

/* Notify modules that CAP negotiation has ended */
for (node = set_first(iauth_modules); node; node = set_next(node)) {
plugin = ENCLOSING_STRUCT(node, struct iauth_module, node);
if (plugin->field_change != NULL)
plugin->field_change(req, flag);
}
iauth_check_request(req);
}

static void parse_server_info(int argc, char *argv[])
{
struct iauth_module *plugin;
Expand Down Expand Up @@ -890,12 +983,24 @@ static void iauth_read(evutil_socket_t fd, short events, void *iauth_in_v)
case 'n':
parse_nick(req, argv[1]);
break;
case 'A':
parse_account(req, argv[1]);
break;
case 'Z':
parse_fingerprint(req, argv[1]);
break;
case 'H':
parse_hurry_up(req);
break;
case 'T':
parse_registered(req, 1);
break;
case 'c':
parse_cap(req, IAUTH_GOT_CAP_START);
break;
case 'e':
parse_cap(req, IAUTH_GOT_CAP_END);
break;
case 'E':
parse_error(req, argc, argv);
break;
Expand Down Expand Up @@ -954,6 +1059,7 @@ void module_constructor(UNUSED_ARG(const char name[]))
iauth_modules = set_alloc(set_compare_charp, NULL);
iauth_conf = conf_register_object(NULL, "iauth");
iauth_conf_timeout = conf_register_string(iauth_conf, CONF_STRING_INTERVAL, "timeout", "0");
iauth_conf_kill_loc = conf_register_string(iauth_conf, CONF_STRING_PLAIN, "kill_loc", "");

event_base_once(ev_base, -1, EV_TIMEOUT, iauth_startup, NULL, &tv_zero);
iauth_in = evbuffer_new();
Expand Down
Loading