X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_auth.c;h=924bd50ab13dcc36df46936301348155a327312d;hb=894ccd3b8d5acb0067562d26bf72228d3e617203;hp=bb6f30a5e5db65c21a1c6a0a4843a1e087e94308;hpb=6156ee4dda1a87f89e25c67a6a85bb98da16c88e;p=ircu2.10.12-pk.git diff --git a/ircd/s_auth.c b/ircd/s_auth.c index bb6f30a..924bd50 100644 --- a/ircd/s_auth.c +++ b/ircd/s_auth.c @@ -52,6 +52,7 @@ #include "list.h" #include "msg.h" /* for MAXPARA */ #include "numeric.h" +#include "numnicks.h" #include "querycmds.h" #include "random.h" #include "res.h" @@ -83,6 +84,7 @@ enum AuthRequestFlag { AR_IAUTH_HURRY, /**< we told iauth to hurry up */ AR_IAUTH_USERNAME, /**< iauth sent a username (preferred or forced) */ AR_IAUTH_FUSERNAME, /**< iauth sent a forced username */ + AR_IAUTH_SOFT_DONE, /**< iauth has no objection to client */ AR_PASSWORD_CHECKED, /**< client password already checked */ AR_NUM_FLAGS }; @@ -117,6 +119,7 @@ static struct { MSG("NOTICE AUTH :*** Checking Ident\r\n"), MSG("NOTICE AUTH :*** Got ident response\r\n"), MSG("NOTICE AUTH :*** No ident response\r\n"), + MSG("NOTICE AUTH :*** \r\n"), MSG("NOTICE AUTH :*** Your forward and reverse DNS do not match, " "ignoring hostname.\r\n"), MSG("NOTICE AUTH :*** Invalid hostname\r\n") @@ -131,6 +134,7 @@ typedef enum { REPORT_DO_ID, REPORT_FIN_ID, REPORT_FAIL_ID, + REPORT_FAIL_IAUTH, REPORT_IP_MISMATCH, REPORT_INVAL_DNS } ReportType; @@ -198,7 +202,9 @@ struct IAuth { #define i_debug(iauth) ((iauth)->i_debug) /** Active instance of IAuth. */ -struct IAuth *iauth; +static struct IAuth *iauth; +/** Freelist of AuthRequest structures. */ +static struct AuthRequest *auth_freelist; static void iauth_sock_callback(struct Event *ev); static void iauth_stderr_callback(struct Event *ev); @@ -381,12 +387,14 @@ static int check_auth_finished(struct AuthRequest *auth) * as possible so that iauth's challenge/response (which uses PASS * for responses) is not confused with the client's password. */ - if (!FlagHas(&auth->flags, AR_PASSWORD_CHECKED)) + if (IsUserPort(auth->client) + && !FlagHas(&auth->flags, AR_PASSWORD_CHECKED)) { struct ConfItem *aconf; aconf = cli_confs(auth->client)->value.aconf; - if (!EmptyString(aconf->passwd) + if (aconf + && !EmptyString(aconf->passwd) && strcmp(cli_passwd(auth->client), aconf->passwd)) { ServerStats->is_ref++; @@ -421,13 +429,17 @@ static int check_auth_finished(struct AuthRequest *auth) else FlagSet(&auth->flags, AR_IAUTH_HURRY); - destroy_auth_request(auth); - if (!IsUserPort(auth->client)) - return 0; - memset(cli_passwd(auth->client), 0, sizeof(cli_passwd(auth->client))); - res = auth_set_username(auth); - if (res == 0) + if (IsUserPort(auth->client)) + { + memset(cli_passwd(auth->client), 0, sizeof(cli_passwd(auth->client))); + res = auth_set_username(auth); + if (res == 0) res = register_user(auth->client, auth->client); + } + else + res = 0; + if (res == 0) + destroy_auth_request(auth); return res; } @@ -451,6 +463,26 @@ auth_verify_hostname(const char *host, int maxlen) return 1; /* it's a valid hostname */ } +/** Check whether a client already has a CONF_CLIENT configuration + * item. + * + * @return A pointer to the client's first CONF_CLIENT, or NULL if + * there are none. + */ +static struct ConfItem *find_conf_client(struct Client *cptr) +{ + struct SLink *list; + + for (list = cli_confs(cptr); list != NULL; list = list->next) { + struct ConfItem *aconf; + aconf = list->value.aconf; + if (aconf->status & CONF_CLIENT) + return aconf; + } + + return NULL; +} + /** Assign a client to a connection class. * @param[in] cptr Client to assign to a class. * @return Zero if client is kept, CPTR_KILLED if rejected. @@ -463,6 +495,10 @@ static int preregister_user(struct Client *cptr) ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN); ircd_strncpy(cli_user(cptr)->realhost, cli_sockhost(cptr), HOSTLEN); + if (find_conf_client(cptr)) { + return 0; + } + switch (conf_check_client(cptr)) { case ACR_OK: @@ -493,7 +529,7 @@ static int preregister_user(struct Client *cptr) /* Can this ever happen? */ case ACR_BAD_SOCKET: ++ServerStats->is_ref; - IPcheck_connect_fail(cptr); + IPcheck_connect_fail(cptr, 0); return exit_client(cptr, cptr, &me, "Unknown error -- Try again"); } return 0; @@ -715,7 +751,59 @@ void destroy_auth_request(struct AuthRequest* auth) if (t_active(&auth->timeout)) timer_del(&auth->timeout); + cli_auth(auth->client) = NULL; + auth->next = auth_freelist; + auth_freelist = auth; +} + +/** Handle a 'ping' (authorization) timeout for a client. + * @param[in] cptr The client whose session authorization has timed out. + * @return Zero if client is kept, CPTR_KILLED if client rejected. + */ +int auth_ping_timeout(struct Client *cptr) +{ + struct AuthRequest *auth; + enum AuthRequestFlag flag; + + auth = cli_auth(cptr); + + /* Check whether the auth request is gone (more likely, it never + * existed, as in an outbound server connection). */ + if (!auth) + return exit_client_msg(cptr, cptr, &me, "Registration Timeout"); + + /* Check for a user-controlled timeout. */ + for (flag = 0; flag <= AR_LAST_SCAN; ++flag) { + if (FlagHas(&auth->flags, flag)) { + /* Display message if they have sent a NICK and a USER but no + * nospoof PONG. + */ + if (*(cli_name(cptr)) && cli_user(cptr) && *(cli_user(cptr))->username) { + send_reply(cptr, SND_EXPLICIT | ERR_BADPING, + ":Your client may not be compatible with this server."); + send_reply(cptr, SND_EXPLICIT | ERR_BADPING, + ":Compatible clients are available at %s", + feature_str(FEAT_URL_CLIENTS)); + } + return exit_client_msg(cptr, cptr, &me, "Registration Timeout"); + } + } + + /* Check for iauth timeout. */ + if (FlagHas(&auth->flags, AR_IAUTH_PENDING)) { + if (IAuthHas(iauth, IAUTH_REQUIRED) + && !FlagHas(&auth->flags, AR_IAUTH_SOFT_DONE)) { + sendheader(cptr, REPORT_FAIL_IAUTH); + return exit_client_msg(cptr, cptr, &me, "Authorization Timeout"); + } + sendto_iauth(cptr, "T"); + FlagClr(&auth->flags, AR_IAUTH_PENDING); + return check_auth_finished(auth); + } + + assert(0 && "Unexpectedly reached end of auth_ping_timeout()"); + return 0; } /** Timeout a given auth request. @@ -736,14 +824,6 @@ static void auth_timeout_callback(struct Event* ev) log_write(LS_RESOLVER, L_INFO, 0, "Registration timeout %s", get_client_name(auth->client, HIDE_IP)); - /* Tell iauth if we will let the client on. */ - if (FlagHas(&auth->flags, AR_IAUTH_PENDING) - && !IAuthHas(iauth, IAUTH_REQUIRED)) - { - sendto_iauth(auth->client, "T"); - FlagClr(&auth->flags , AR_IAUTH_PENDING); - } - /* Notify client if ident lookup failed. */ if (FlagHas(&auth->flags, AR_AUTH_PENDING)) { FlagClr(&auth->flags, AR_AUTH_PENDING); @@ -753,6 +833,7 @@ static void auth_timeout_callback(struct Event* ev) /* Likewise if dns lookup failed. */ if (FlagHas(&auth->flags, AR_DNS_PENDING)) { + FlagClr(&auth->flags, AR_DNS_PENDING); delete_resolver_queries(auth); if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FAIL_DNS); @@ -781,21 +862,18 @@ static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FAIL_DNS); sendto_iauth(auth->client, "d"); - } else if (irc_in_addr_cmp(addr, &cli_ip(auth->client)) - && irc_in_addr_cmp(addr, &auth->original)) { + } else if (!irc_in_addr_valid(addr) + || (irc_in_addr_cmp(&cli_ip(auth->client), addr) + && irc_in_addr_cmp(&auth->original, addr))) { /* IP for hostname did not match client's IP. */ sendto_opmask_butone(0, SNO_IPMISMATCH, "IP# Mismatch: %s != %s[%s]", cli_sock_ip(auth->client), h_name, ircd_ntoa(addr)); if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_IP_MISMATCH); - /* Clear DNS pending flag so free_client doesn't ask the resolver - * to delete the query that just finished. - */ if (feature_bool(FEAT_KILL_IPMISMATCH)) { - IPcheck_disconnect(auth->client); - Count_unknowndisconnects(UserStats); - free_client(auth->client); + exit_client(auth->client, auth->client, &me, "IP mismatch"); + return; } } else if (!auth_verify_hostname(h_name, HOSTLEN)) { /* Hostname did not look valid. */ @@ -920,8 +998,13 @@ void start_auth(struct Client* client) socket_events(&(cli_socket(client)), SOCK_ACTION_SET | SOCK_EVENT_READABLE); /* Allocate the AuthRequest. */ - auth = MyCalloc(1, sizeof(*auth)); + auth = auth_freelist; + if (auth) + auth_freelist = auth->next; + else + auth = MyMalloc(sizeof(*auth)); assert(0 != auth); + memset(auth, 0, sizeof(*auth)); auth->client = client; cli_auth(client) = auth; s_fd(&auth->socket) = -1; @@ -934,19 +1017,11 @@ void start_auth(struct Client* client) ++ServerStats->is_abad; if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FAIL_ID); - IPcheck_disconnect(auth->client); - Count_unknowndisconnects(UserStats); - free_client(auth->client); + exit_client(auth->client, auth->client, &me, "Socket local/peer lookup failed"); return; } auth->port = remote.port; - /* Try to start DNS lookup. */ - start_dns_query(auth); - - /* Try to start ident lookup. */ - start_auth_query(auth); - /* Set required client inputs for users. */ if (IsUserPort(client)) { cli_user(client) = make_user(client); @@ -958,6 +1033,12 @@ void start_auth(struct Client* client) start_iauth_query(auth); } + /* Try to start DNS lookup. */ + start_dns_query(auth); + + /* Try to start ident lookup. */ + start_auth_query(auth); + /* Add client to GlobalClientList. */ add_client_to_list(client); @@ -981,6 +1062,7 @@ int auth_set_pong(struct AuthRequest *auth, unsigned int cookie) ":To connect, type /QUOTE PONG %u", auth->cookie); return 0; } + cli_lasttime(auth->client) = CurrentTime; FlagClr(&auth->flags, AR_NEEDS_PONG); return check_auth_finished(auth); } @@ -988,10 +1070,14 @@ int auth_set_pong(struct AuthRequest *auth, unsigned int cookie) /** Record a user's claimed username and userinfo. * @param[in] auth Authorization request for client. * @param[in] username Client's asserted username. + * @param[in] hostname Third argument of USER command (client's + * hostname, per RFC 1459). + * @param[in] servername Fourth argument of USER command (server's + * name, per RFC 1459). * @param[in] userinfo Client's asserted self-description. * @return Zero if client should be kept, CPTR_KILLED if rejected. */ -int auth_set_user(struct AuthRequest *auth, const char *username, const char *userinfo) +int auth_set_user(struct AuthRequest *auth, const char *username, const char *hostname, const char *servername, const char *userinfo) { struct Client *cptr; @@ -1004,7 +1090,7 @@ int auth_set_user(struct AuthRequest *auth, const char *username, const char *us ircd_strncpy(cli_user(cptr)->username, username, USERLEN); ircd_strncpy(cli_user(cptr)->host, cli_sockhost(cptr), HOSTLEN); if (IAuthHas(iauth, IAUTH_UNDERNET)) - sendto_iauth(cptr, "U %s :%s", username, userinfo); + sendto_iauth(cptr, "U %s %s %s :%s", username, hostname, servername, userinfo); else if (IAuthHas(iauth, IAUTH_ADDLINFO)) sendto_iauth(cptr, "U %s", username); return check_auth_finished(auth); @@ -1057,6 +1143,17 @@ void auth_send_exit(struct Client *cptr) sendto_iauth(cptr, "D"); } +/** Forward an XREPLY on to iauth. + * @param[in] sptr Source of the XREPLY. + * @param[in] routing Routing information for the original XQUERY. + * @param[in] reply Contents of the reply. + */ +void auth_send_xreply(struct Client *sptr, const char *routing, + const char *reply) +{ + sendto_iauth(NULL, "X %#C %s :%s", sptr, routing, reply); +} + /** Mark that a user has started capabilities negotiation. * This blocks authorization until auth_cap_done() is called. * @param[in] auth Authorization request for client. @@ -1105,13 +1202,17 @@ int iauth_do_spawn(struct IAuth *iauth, int automatic) /* Attempt to allocate a pair of sockets. */ res = os_socketpair(s_io); - if (res) - return errno; + if (res) { + res = errno; + Debug((DEBUG_INFO, "Unable to create IAuth socketpair: %s", strerror(res))); + return res; + } /* Mark the parent's side of the pair (element 0) as non-blocking. */ res = os_set_nonblocking(s_io[0]); if (!res) { res = errno; + Debug((DEBUG_INFO, "Unable to make IAuth socket non-blocking: %s", strerror(res))); close(s_io[1]); close(s_io[0]); return res; @@ -1122,6 +1223,7 @@ int iauth_do_spawn(struct IAuth *iauth, int automatic) SS_CONNECTED, SOCK_EVENT_READABLE, s_io[0]); if (!res) { res = errno; + Debug((DEBUG_INFO, "Unable to register IAuth socket: %s", strerror(res))); close(s_io[1]); close(s_io[0]); return res; @@ -1131,6 +1233,7 @@ int iauth_do_spawn(struct IAuth *iauth, int automatic) res = os_socketpair(s_err); if (res) { res = errno; + Debug((DEBUG_INFO, "Unable to create IAuth stderr: %s", strerror(res))); socket_del(i_socket(iauth)); close(s_io[1]); close(s_io[0]); @@ -1141,6 +1244,7 @@ int iauth_do_spawn(struct IAuth *iauth, int automatic) res = os_set_nonblocking(s_err[0]); if (!res) { res = errno; + Debug((DEBUG_INFO, "Unable to make IAuth stderr non-blocking: %s", strerror(res))); close(s_err[1]); close(s_err[0]); socket_del(i_socket(iauth)); @@ -1154,6 +1258,7 @@ int iauth_do_spawn(struct IAuth *iauth, int automatic) SS_CONNECTED, SOCK_EVENT_READABLE, s_err[0]); if (!res) { res = errno; + Debug((DEBUG_INFO, "Unable to register IAuth stderr: %s", strerror(res))); close(s_err[1]); close(s_err[0]); socket_del(i_socket(iauth)); @@ -1167,6 +1272,7 @@ int iauth_do_spawn(struct IAuth *iauth, int automatic) if (cpid < 0) { /* Error forking the child, still in parent. */ res = errno; + Debug((DEBUG_INFO, "Unable to fork IAuth child: %s", strerror(res))); socket_del(i_stderr(iauth)); close(s_err[1]); close(s_err[0]); @@ -1226,17 +1332,15 @@ int auth_spawn(int argc, char *argv[]) same = 0; } /* Check that we have no more pre-existing arguments. */ - if (iauth->i_argv[ii]) + if (same && iauth->i_argv[ii]) same = 0; - /* If they are the same and still connected, clear the "closing" flag and exit.*/ + /* If they are the same and still connected, clear the "closing" flag and exit. */ if (same && i_GetConnected(iauth)) { + Debug((DEBUG_INFO, "Reusing existing IAuth process")); IAuthClr(iauth, IAUTH_CLOSING); return 2; } - /* Deallocate old argv elements. */ - for (ii = 0; iauth->i_argv[ii]; ++ii) - MyFree(iauth->i_argv[ii]); - MyFree(iauth->i_argv); + auth_close_unused(); } /* Need to initialize a new connection. */ @@ -1266,18 +1370,22 @@ void auth_mark_closing(void) */ static void iauth_disconnect(struct IAuth *iauth) { - if (!i_GetConnected(iauth)) + if (iauth == NULL) return; /* Close main socket. */ - close(s_fd(i_socket(iauth))); - socket_del(i_socket(iauth)); - s_fd(i_socket(iauth)) = -1; + if (s_fd(i_socket(iauth)) != -1) { + close(s_fd(i_socket(iauth))); + socket_del(i_socket(iauth)); + s_fd(i_socket(iauth)) = -1; + } /* Close error socket. */ - close(s_fd(i_stderr(iauth))); - socket_del(i_stderr(iauth)); - s_fd(i_stderr(iauth)) = -1; + if (s_fd(i_stderr(iauth)) != -1) { + close(s_fd(i_stderr(iauth))); + socket_del(i_stderr(iauth)); + s_fd(i_stderr(iauth)) = -1; + } } /** Close all %IAuth connections marked as closing. */ @@ -1358,6 +1466,7 @@ static int sendto_iauth(struct Client *cptr, const char *format, ...) /* Tack it onto the iauth sendq and try to write it. */ ++iauth->i_sendM; msgq_add(i_sendQ(iauth), mb, 0); + msgq_clean(mb); iauth_write(iauth); return 1; } @@ -1672,6 +1781,14 @@ static int iauth_cmd_hostname(struct IAuth *iauth, struct Client *cli, } /* Set hostname from params. */ ircd_strncpy(cli_sockhost(cli), params[0], HOSTLEN); + /* If we have gotten here, the user is in a "hurry" state and has + * been pre-registered. Their hostname was set during that, and + * needs to be overwritten now. + */ + if (FlagHas(&auth->flags, AR_IAUTH_HURRY)) { + ircd_strncpy(cli_user(cli)->host, cli_sockhost(cli), HOSTLEN); + ircd_strncpy(cli_user(cli)->realhost, cli_sockhost(cli), HOSTLEN); + } return 1; } @@ -1711,7 +1828,7 @@ static int iauth_cmd_ip_address(struct IAuth *iauth, struct Client *cli, memcpy(&auth->original, &cli_ip(cli), sizeof(auth->original)); /* Undo original IP connection in IPcheck. */ - IPcheck_connect_fail(cli); + IPcheck_connect_fail(cli, 1); ClearIPChecked(cli); /* Update the IP and charge them as a remote connect. */ @@ -1733,7 +1850,7 @@ static struct ConfItem *auth_find_class_conf(const char *class_name) /* Make sure the configuration class is valid. */ class = find_class(class_name); - if (!class) + if (!class || !class->valid) return NULL; /* Look for an existing ConfItem for the class. */ @@ -1750,6 +1867,13 @@ static struct ConfItem *auth_find_class_conf(const char *class_name) ConClass(class)); return NULL; } + /* make_conf() "helpfully" links the conf into GlobalConfList, + * which we do not want, so undo that. (Ugh.) + */ + if (aconf == GlobalConfList) { + GlobalConfList = aconf->next; + } + /* Back to business as usual. */ aconf->conn_class = class; aconf->next = aconf_list; aconf_list = aconf; @@ -1758,12 +1882,28 @@ static struct ConfItem *auth_find_class_conf(const char *class_name) return aconf; } +/** Tentatively accept a client in IAuth. + * @param[in] iauth Active IAuth session. + * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters. + * @param[in] params Optional class name for client. + * @return Negative (CPTR_KILLED) if the connection is refused, one otherwise. + */ +static int iauth_cmd_soft_done(struct IAuth *iauth, struct Client *cli, + int parc, char **params) +{ + /* Clear iauth pending flag. */ + assert(cli_auth(cli) != NULL); + FlagSet(&cli_auth(cli)->flags, AR_IAUTH_SOFT_DONE); + return 1; +} + /** Accept a client in IAuth. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. * @param[in] parc Number of parameters. * @param[in] params Optional class name for client. - * @return One. + * @return Negative (CPTR_KILLED) if the connection is refused, one otherwise. */ static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, int parc, char **params) @@ -1779,9 +1919,24 @@ static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, struct ConfItem *aconf; aconf = auth_find_class_conf(params[0]); - if (aconf) - attach_conf(cli, aconf); - else + if (aconf) { + enum AuthorizationCheckResult acr; + + acr = attach_conf(cli, aconf); + switch (acr) { + case ACR_OK: + /* There should maybe be some way to set FLAG_DOID here.. */ + break; + case ACR_TOO_MANY_IN_CLASS: + ++ServerStats->is_ref; + return exit_client(cli, cli, &me, + "Sorry, your connection class is full - try " + "again later or try another server"); + default: + log_write(LS_IAUTH, L_ERROR, 0, "IAuth: Unexpected AuthorizationCheckResult %d from attach_conf()", acr); + break; + } + } else sendto_opmask_butone_ratelimited(NULL, SNO_AUTH, &warn_time, "iauth tried to use undefined class [%s]", params[0]); @@ -1795,7 +1950,8 @@ static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, * @param[in] cli Client referenced by command. * @param[in] parc Number of parameters. * @param[in] params Account name and optional class name for client. - * @return Non-zero if \a cli authorization should be checked for completion. + * @return Negative if the connection is refused, otherwise non-zero + * if \a cli authorization should be checked for completion. */ static int iauth_cmd_done_account(struct IAuth *iauth, struct Client *cli, int parc, char **params) @@ -1815,8 +1971,10 @@ static int iauth_cmd_done_account(struct IAuth *iauth, struct Client *cli, } /* If account has a creation timestamp, use it. */ assert(cli_user(cli) != NULL); - if (params[0][len] == ':') + if (params[0][len] == ':') { cli_user(cli)->acc_create = strtoul(params[0] + len + 1, NULL, 10); + params[0][len] = '\0'; + } /* Copy account name to User structure. */ ircd_strncpy(cli_user(cli)->account, params[0], ACCOUNTLEN); @@ -1844,6 +2002,25 @@ static int iauth_cmd_kill(struct IAuth *iauth, struct Client *cli, return 0; } +/** Change a client's usermode. + * @param[in] iauth Active IAuth session. + * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (at least one). + * @param[in] params Usermode arguments for client (with the first + * starting with '+'). + * @return Zero. + */ +static int iauth_cmd_usermode(struct IAuth *iauth, struct Client *cli, + int parc, char **params) +{ + if (params[0][0] == '+') + { + set_user_mode(cli, cli, parc + 2, params - 2, ALLOWMODES_ANY); + } + return 0; +} + + /** Send a challenge string to the client. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. @@ -1859,6 +2036,55 @@ static int iauth_cmd_challenge(struct IAuth *iauth, struct Client *cli, return 0; } +/** Send an extension query to a specified remote server. + * @param[in] iauth Active IAuth session. + * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (3). + * @param[in] params Remote server, routing information, and query. + * @return Zero. + */ +static int iauth_cmd_xquery(struct IAuth *iauth, struct Client *cli, + int parc, char **params) +{ + char *serv; + const char *routing; + const char *query; + struct Client *acptr; + + /* Process parameters */ + if (EmptyString(params[0])) { + sendto_iauth(cli, "E Missing :Missing server parameter"); + return 0; + } else + serv = params[0]; + + if (EmptyString(params[1])) { + sendto_iauth(cli, "E Missing :Missing routing parameter"); + return 0; + } else + routing = params[1]; + + if (EmptyString(params[2])) { + sendto_iauth(cli, "E Missing :Missing query parameter"); + return 0; + } else + query = params[2]; + + /* Try to find the specified server */ + if (!(acptr = find_match_server(serv))) { + sendto_iauth(cli, "x %s %s :Server not online", serv, routing); + return 0; + } + + /* If it's to us, do nothing; otherwise, forward the query */ + if (!IsMe(acptr)) + /* The "iauth:" prefix helps ircu route the reply to iauth */ + sendcmdto_one(&me, CMD_XQUERY, acptr, "%C iauth:%s :%s", acptr, routing, + query); + + return 0; +} + /** Parse a \a message from \a iauth. * @param[in] iauth Active IAuth session. * @param[in] message Message to be parsed. @@ -1883,12 +2109,15 @@ static void iauth_parse(struct IAuth *iauth, char *message) case 'A': handler = iauth_cmd_config; has_cli = 0; break; case 's': handler = iauth_cmd_newstats; has_cli = 0; break; case 'S': handler = iauth_cmd_stats; has_cli = 0; break; + case 'X': handler = iauth_cmd_xquery; has_cli = 0; break; case 'o': handler = iauth_cmd_username_forced; has_cli = 1; break; case 'U': handler = iauth_cmd_username_good; has_cli = 1; break; case 'u': handler = iauth_cmd_username_bad; has_cli = 1; break; case 'N': handler = iauth_cmd_hostname; has_cli = 1; break; case 'I': handler = iauth_cmd_ip_address; has_cli = 1; break; + case 'M': handler = iauth_cmd_usermode; has_cli = 1; break; case 'C': handler = iauth_cmd_challenge; has_cli = 1; break; + case 'd': handler = iauth_cmd_soft_done; has_cli = 1; break; case 'D': handler = iauth_cmd_done_client; has_cli = 1; break; case 'R': handler = iauth_cmd_done_account; has_cli = 1; break; case 'k': /* The 'k' command indicates the user should be booted @@ -1930,7 +2159,9 @@ static void iauth_parse(struct IAuth *iauth, char *message) } else { /* Try to find the client associated with the request. */ id = strtol(params[0], NULL, 10); - if (id < 0 || id > HighestFd || !(cli = LocalClientArray[id])) + if (parc < 3) + sendto_iauth(NULL, "E Missing :Need "); + else if (id < 0 || id > HighestFd || !(cli = LocalClientArray[id])) /* Client no longer exists (or never existed). */ sendto_iauth(NULL, "E Gone :[%s %s %s]", params[0], params[1], params[2]); @@ -1954,7 +2185,7 @@ static void iauth_parse(struct IAuth *iauth, char *message) /* Report mismatch to iauth. */ sendto_iauth(cli, "E Mismatch :[%s] != [%s]", params[1], ircd_ntoa(&cli_ip(cli))); - else if (handler(iauth, cli, parc - 3, params + 3)) + else if (handler(iauth, cli, parc - 3, params + 3) > 0) /* Handler indicated a possible state change. */ check_auth_finished(auth); } @@ -2090,14 +2321,17 @@ static void iauth_stderr_callback(struct Event *ev) assert(0 != iauth); switch (ev_type(ev)) { + case ET_DESTROY: + /* We do not restart iauth here: the stdout handler does that for us. */ + break; case ET_READ: iauth_read_stderr(iauth); break; case ET_ERROR: log_write(LS_IAUTH, L_ERROR, 0, "IAuth stderr error: %s", strerror(ev_data(ev))); - /* and fall through to the ET_EOF/ET_DESTROY case */ - case ET_DESTROY: + /* and fall through to the ET_EOF case */ case ET_EOF: + iauth_disconnect(iauth); break; default: assert(0 && "Unrecognized event type"); @@ -2119,7 +2353,6 @@ void report_iauth_conf(struct Client *cptr, const struct StatDesc *sd, char *par send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s", link->value.cp); } - send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth configuration."); } /** Report active iauth's statistics to \a cptr. @@ -2136,5 +2369,4 @@ void report_iauth_conf(struct Client *cptr, const struct StatDesc *sd, char *par send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s", link->value.cp); } - send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth statistics."); }