X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fs_auth.c;h=83693a24a48125df9150291bdddfcaabc5c59e36;hb=d0a284c68368f5cd58309293e936f82e032f7ae8;hp=a233f9bd7b45a985584aa4a61c364a1a5f3cb9e2;hpb=1594c3690563f6ebaba83b0adff80479ac636364;p=ircu2.10.12-pk.git diff --git a/ircd/s_auth.c b/ircd/s_auth.c index a233f9b..83693a2 100644 --- a/ircd/s_auth.c +++ b/ircd/s_auth.c @@ -50,7 +50,9 @@ #include "ircd_snprintf.h" #include "ircd_string.h" #include "list.h" +#include "msg.h" /* for MAXPARA */ #include "numeric.h" +#include "numnicks.h" #include "querycmds.h" #include "random.h" #include "res.h" @@ -82,6 +84,8 @@ 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 }; @@ -95,7 +99,7 @@ struct AuthRequest { struct irc_sockaddr local; /**< local endpoint address */ struct irc_in_addr original; /**< original client IP address */ struct Socket socket; /**< socket descriptor for auth queries */ - struct Timer timeout; /**< timeout timer for auth queries */ + struct Timer timeout; /**< timeout timer for ident and dns queries */ struct AuthRequestFlags flags; /**< current state of request */ unsigned int cookie; /**< cookie the user must PONG */ unsigned short port; /**< client's remote port number */ @@ -115,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") @@ -129,6 +134,7 @@ typedef enum { REPORT_DO_ID, REPORT_FIN_ID, REPORT_FAIL_ID, + REPORT_FAIL_IAUTH, REPORT_IP_MISMATCH, REPORT_INVAL_DNS } ReportType; @@ -196,13 +202,16 @@ 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); static int sendto_iauth(struct Client *cptr, const char *format, ...); static int preregister_user(struct Client *cptr); -typedef int (*iauth_cmd_handler)(struct IAuth *iauth, struct Client *cli, char *params); +typedef int (*iauth_cmd_handler)(struct IAuth *iauth, struct Client *cli, + int parc, char **params); /** Set username for user associated with \a auth. * @param[in] auth Client authorization request to work on. @@ -352,11 +361,9 @@ badid: * destroy \a auth, clear the password, set the username, and register * the client. * @param[in] auth Authorization request to check. - * @param[in] send_reports Passed to destroy_auth_request() if \a auth - * is complete. * @return Zero if client is kept, CPTR_KILLED if client rejected. */ -static int check_auth_finished(struct AuthRequest *auth, int send_reports) +static int check_auth_finished(struct AuthRequest *auth) { enum AuthRequestFlag flag; int res; @@ -376,34 +383,43 @@ static int check_auth_finished(struct AuthRequest *auth, int send_reports) && preregister_user(auth->client)) return CPTR_KILLED; + /* If we have not done so, check client password. Do this as soon + * as possible so that iauth's challenge/response (which uses PASS + * for responses) is not confused with the client's password. + */ + if (IsUserPort(auth->client) + && !FlagHas(&auth->flags, AR_PASSWORD_CHECKED)) + { + struct ConfItem *aconf; + + aconf = cli_confs(auth->client)->value.aconf; + if (aconf + && !EmptyString(aconf->passwd) + && strcmp(cli_passwd(auth->client), aconf->passwd)) + { + ServerStats->is_ref++; + send_reply(auth->client, ERR_PASSWDMISMATCH); + return exit_client(auth->client, auth->client, &me, "Bad Password"); + } + FlagSet(&auth->flags, AR_PASSWORD_CHECKED); + } + /* Check if iauth is done. */ if (FlagHas(&auth->flags, AR_IAUTH_PENDING)) { /* Switch auth request to hurry-up state. */ if (!FlagHas(&auth->flags, AR_IAUTH_HURRY)) { - struct ConfItem* aconf; - /* Set "hurry" flag in auth request. */ FlagSet(&auth->flags, AR_IAUTH_HURRY); - /* Check password now (to avoid challenge/response conflicts). */ - aconf = cli_confs(auth->client)->value.aconf; - if (!EmptyString(aconf->passwd) - && strcmp(cli_passwd(auth->client), aconf->passwd)) - { - ServerStats->is_ref++; - send_reply(auth->client, ERR_PASSWDMISMATCH); - return exit_client(auth->client, auth->client, &me, "Bad Password"); - } - /* If iauth wants it, send notification. */ if (IAuthHas(iauth, IAUTH_UNDERNET)) - sendto_iauth(auth->client, "H %s", ConfClass(aconf)); + sendto_iauth(auth->client, "H %s", get_client_class(auth->client)); /* If iauth wants it, give client more time. */ if (IAuthHas(iauth, IAUTH_EXTRAWAIT)) - timer_chg(&auth->timeout, TT_RELATIVE, feature_int(FEAT_AUTH_TIMEOUT)); + cli_firsttime(auth->client) = CurrentTime; } Debug((DEBUG_INFO, "Auth %p [%d] still has flag %d", auth, @@ -413,14 +429,17 @@ static int check_auth_finished(struct AuthRequest *auth, int send_reports) else FlagSet(&auth->flags, AR_IAUTH_HURRY); - - destroy_auth_request(auth, send_reports); - 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; } @@ -444,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. @@ -456,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: @@ -486,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; @@ -517,7 +560,7 @@ static void send_auth_query(struct AuthRequest* auth) if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FAIL_ID); FlagClr(&auth->flags, AR_AUTH_PENDING); - check_auth_finished(auth, 0); + check_auth_finished(auth); } } @@ -636,6 +679,8 @@ static void read_auth_reply(struct AuthRequest* auth) if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FAIL_ID); ++ServerStats->is_abad; + if (IAuthHas(iauth, IAUTH_UNDERNET)) + sendto_iauth(auth->client, "u"); } else { if (IsUserPort(auth->client)) sendheader(auth->client, REPORT_FIN_ID); @@ -649,7 +694,7 @@ static void read_auth_reply(struct AuthRequest* auth) } FlagClr(&auth->flags, AR_AUTH_PENDING); - check_auth_finished(auth, 0); + check_auth_finished(auth); } /** Handle socket I/O activity. @@ -691,21 +736,13 @@ static void auth_sock_callback(struct Event* ev) /** Stop an auth request completely. * @param[in] auth The struct AuthRequest to cancel. - * @param[in] send_reports If non-zero, report the failure to the user. */ -void destroy_auth_request(struct AuthRequest* auth, int send_reports) +void destroy_auth_request(struct AuthRequest* auth) { Debug((DEBUG_INFO, "Deleting auth request for %p", auth->client)); - if (FlagHas(&auth->flags, AR_AUTH_PENDING)) { - if (send_reports && IsUserPort(auth->client)) - sendheader(auth->client, REPORT_FAIL_ID); - } - if (FlagHas(&auth->flags, AR_DNS_PENDING)) { delete_resolver_queries(auth); - if (send_reports && IsUserPort(auth->client)) - sendheader(auth->client, REPORT_FAIL_DNS); } if (-1 < s_fd(&auth->socket)) { @@ -714,8 +751,61 @@ void destroy_auth_request(struct AuthRequest* auth, int send_reports) s_fd(&auth->socket) = -1; } - timer_del(&auth->timeout); + 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. @@ -735,18 +825,24 @@ static void auth_timeout_callback(struct Event* ev) /* Report the timeout in the log. */ 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); + if (IsUserPort(auth->client)) + sendheader(auth->client, REPORT_FAIL_ID); + } + + /* 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); } + /* Try to register the client. */ - check_auth_finished(auth, 1); - /* If that failed, kick them off. */ - if (!IsUser(auth->client)) - exit_client(auth->client, auth->client, &me, "Authorization timed out"); + check_auth_finished(auth); } } @@ -768,21 +864,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. */ @@ -796,7 +889,7 @@ static void auth_dns_callback(void* vptr, const struct irc_in_addr *addr, const ircd_strncpy(cli_sockhost(auth->client), h_name, HOSTLEN); sendto_iauth(auth->client, "N %s", h_name); } - check_auth_finished(auth, 0); + check_auth_finished(auth); } /** Flag the client to show an attempt to contact the ident server on @@ -904,12 +997,16 @@ void start_auth(struct Client* client) if (cli_fd(client) > HighestFd) HighestFd = cli_fd(client); LocalClientArray[cli_fd(client)] = client; - add_client_to_list(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; @@ -922,19 +1019,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); @@ -946,8 +1035,17 @@ 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); + /* Check which auth events remain pending. */ - check_auth_finished(auth, 0); + check_auth_finished(auth); } /** Mark that a user has PONGed while unregistered. @@ -966,17 +1064,22 @@ 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, 0); + return check_auth_finished(auth); } /** 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; @@ -989,10 +1092,10 @@ 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, 0); + return check_auth_finished(auth); } /** Handle authorization-related aspects of initial nickname selection. @@ -1018,7 +1121,7 @@ int auth_set_nick(struct AuthRequest *auth, const char *nickname) } if (IAuthHas(iauth, IAUTH_UNDERNET)) sendto_iauth(auth->client, "n %s", nickname); - return check_auth_finished(auth, 0); + return check_auth_finished(auth); } /** Record a user's password. @@ -1030,7 +1133,7 @@ int auth_set_password(struct AuthRequest *auth, const char *password) { assert(auth != NULL); if (IAuthHas(iauth, IAUTH_ADDLINFO)) - sendto_iauth(auth->client, "P %s", password); + sendto_iauth(auth->client, "P :%s", password); return 0; } @@ -1042,6 +1145,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. @@ -1063,7 +1177,7 @@ int auth_cap_done(struct AuthRequest *auth) { assert(auth != NULL); FlagClr(&auth->flags, AR_CAP_PENDING); - return check_auth_finished(auth, 0); + return check_auth_finished(auth); } /** Attempt to spawn the process for an IAuth instance. @@ -1090,13 +1204,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; @@ -1107,6 +1225,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; @@ -1116,6 +1235,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]); @@ -1126,6 +1246,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)); @@ -1139,6 +1260,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)); @@ -1152,6 +1274,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]); @@ -1211,17 +1334,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. */ @@ -1251,18 +1372,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; - /* 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 main socket. */ + 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 all %IAuth connections marked as closing. */ @@ -1343,6 +1468,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; } @@ -1350,26 +1476,30 @@ static int sendto_iauth(struct Client *cptr, const char *format, ...) /** Send text to interested operators (SNO_AUTH server notice). * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params Text to send. * @return Zero. */ -static int iauth_cmd_snotice(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_snotice(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { - sendto_opmask_butone(NULL, SNO_AUTH, "%s", params); + sendto_opmask_butone(NULL, SNO_AUTH, "%s", params[0]); return 0; } /** Set the debug level for the session. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params String starting with an integer. * @return Zero. */ -static int iauth_cmd_debuglevel(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_debuglevel(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { int new_level; - new_level = atoi(params); + new_level = parc > 0 ? atoi(params[0]) : 0; if (i_debug(iauth) > 0 || new_level > 0) { /* The "ia_dbg" name is borrowed from (IRCnet) ircd. */ sendto_opmask_butone(NULL, SNO_AUTH, "ia_dbg = %d", new_level); @@ -1389,10 +1519,12 @@ static int iauth_cmd_debuglevel(struct IAuth *iauth, struct Client *cli, char *p * * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params Zero or more policy options. * @return Zero. */ -static int iauth_cmd_policy(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_policy(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { enum IAuthFlag flag; char *p; @@ -1401,44 +1533,85 @@ static int iauth_cmd_policy(struct IAuth *iauth, struct Client *cli, char *param for (flag = IAUTH_FIRST_OPTION; flag < IAUTH_LAST_FLAG; ++flag) IAuthClr(iauth, flag); - /* Parse new policy set. */ - for (p = params; *p; p++) switch (*p) { - case 'A': IAuthSet(iauth, IAUTH_ADDLINFO); break; - case 'R': IAuthSet(iauth, IAUTH_REQUIRED); break; - case 'T': IAuthSet(iauth, IAUTH_TIMEOUT); break; - case 'W': IAuthSet(iauth, IAUTH_EXTRAWAIT); break; - case 'U': IAuthSet(iauth, IAUTH_UNDERNET); break; - } + if (parc > 0) /* only try to parse if we were given a policy string */ + /* Parse new policy set. */ + for (p = params[0]; *p; p++) switch (*p) { + case 'A': IAuthSet(iauth, IAUTH_ADDLINFO); break; + case 'R': IAuthSet(iauth, IAUTH_REQUIRED); break; + case 'T': IAuthSet(iauth, IAUTH_TIMEOUT); break; + case 'W': IAuthSet(iauth, IAUTH_EXTRAWAIT); break; + case 'U': IAuthSet(iauth, IAUTH_UNDERNET); break; + } /* Optionally notify operators. */ if (i_debug(iauth) > 0) - sendto_opmask_butone(NULL, SNO_AUTH, "iauth options: %s", params); + sendto_opmask_butone(NULL, SNO_AUTH, "iauth options: %s", params[0]); return 0; } /** Set the iauth program version number. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params Version number or name. * @return Zero. */ -static int iauth_cmd_version(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_version(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { MyFree(iauth->i_version); - while (IsSpace(*params)) - ++params; - DupString(iauth->i_version, params); - sendto_opmask_butone(NULL, SNO_AUTH, "iauth version %s running.", iauth->i_version); + DupString(iauth->i_version, parc > 0 ? params[0] : ""); + sendto_opmask_butone(NULL, SNO_AUTH, "iauth version %s running.", + iauth->i_version); return 0; } +/** Paste a parameter list together into a single string. + * @param[in] parc Number of parameters. + * @param[in] params Parameter list to paste together. + * @return Pasted parameter list. + */ +static char *paste_params(int parc, char **params) +{ + char *str, *tmp; + int len = 0, lengths[MAXPARA], i; + + /* Compute the length... */ + for (i = 0; i < parc; i++) + len += lengths[i] = strlen(params[i]); + + /* Allocate memory, accounting for string lengths, spaces (parc - 1), a + * sentinel, and the trailing \0 + */ + str = MyMalloc(len + parc + 1); + + /* Build the pasted string */ + for (tmp = str, i = 0; i < parc; i++) { + if (i) /* add space separator... */ + *(tmp++) = ' '; + if (i == parc - 1) /* add colon sentinel */ + *(tmp++) = ':'; + + /* Copy string component... */ + memcpy(tmp, params[i], lengths[i]); + tmp += lengths[i]; /* move to end of string */ + } + + /* terminate the string... */ + *tmp = '\0'; + + return str; /* return the pasted string */ +} + /** Clear cached iauth configuration information. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (0). * @param[in] params Parameter list (ignored). * @return Zero. */ -static int iauth_cmd_newconfig(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_newconfig(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { struct SLink *head; struct SLink *next; @@ -1457,10 +1630,12 @@ static int iauth_cmd_newconfig(struct IAuth *iauth, struct Client *cli, char *pa /** Append iauth configuration information. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters. * @param[in] params Description of configuration element. * @return Zero. */ -static int iauth_cmd_config(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_config(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { struct SLink *node; @@ -1470,19 +1645,20 @@ static int iauth_cmd_config(struct IAuth *iauth, struct Client *cli, char *param } else { node = iauth->i_config = make_link(); } - while (IsSpace(*params)) - ++params; - DupString(node->value.cp, params); + node->value.cp = paste_params(parc, params); + node->next = 0; /* must be explicitly cleared */ return 0; } /** Clear cached iauth configuration information. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (0). * @param[in] params Parameter list (ignored). * @return Zero. */ -static int iauth_cmd_newstats(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_newstats(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { struct SLink *head; struct SLink *next; @@ -1501,10 +1677,12 @@ static int iauth_cmd_newstats(struct IAuth *iauth, struct Client *cli, char *par /** Append iauth statistics information. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters. * @param[in] params Statistics element. * @return Zero. */ -static int iauth_cmd_stats(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_stats(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { struct SLink *node; if (iauth->i_stats) { @@ -1513,24 +1691,25 @@ static int iauth_cmd_stats(struct IAuth *iauth, struct Client *cli, char *params } else { node = iauth->i_stats = make_link(); } - while (IsSpace(*params)) - ++params; - DupString(node->value.cp, params); + node->value.cp = paste_params(parc, params); + node->next = 0; /* must be explicitly cleared */ return 0; } /** Set client's username to a trusted string even if it breaks the rules. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params Forced username. * @return One. */ -static int iauth_cmd_username_forced(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_username_forced(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { assert(cli_auth(cli) != NULL); FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING); - if (!EmptyString(params)) { - ircd_strncpy(cli_username(cli), params, USERLEN); + if (!EmptyString(params[0])) { + ircd_strncpy(cli_username(cli), params[0], USERLEN); SetGotId(cli); FlagSet(&cli_auth(cli)->flags, AR_IAUTH_USERNAME); FlagSet(&cli_auth(cli)->flags, AR_IAUTH_FUSERNAME); @@ -1541,15 +1720,17 @@ static int iauth_cmd_username_forced(struct IAuth *iauth, struct Client *cli, ch /** Set client's username to a trusted string. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params Trusted username. * @return One. */ -static int iauth_cmd_username_good(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_username_good(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { assert(cli_auth(cli) != NULL); FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING); - if (!EmptyString(params)) { - ircd_strncpy(cli_username(cli), params, USERLEN); + if (!EmptyString(params[0])) { + ircd_strncpy(cli_username(cli), params[0], USERLEN); SetGotId(cli); FlagSet(&cli_auth(cli)->flags, AR_IAUTH_USERNAME); } @@ -1559,29 +1740,33 @@ static int iauth_cmd_username_good(struct IAuth *iauth, struct Client *cli, char /** Set client's username to an untrusted string. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params Untrusted username. * @return One. */ -static int iauth_cmd_username_bad(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_username_bad(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { assert(cli_auth(cli) != NULL); FlagClr(&cli_auth(cli)->flags, AR_AUTH_PENDING); - if (!EmptyString(params)) - ircd_strncpy(cli_user(cli)->username, params, USERLEN); + if (!EmptyString(params[0])) + ircd_strncpy(cli_user(cli)->username, params[0], USERLEN); return 1; } /** Set client's hostname. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params New hostname for client. * @return Non-zero if \a cli authorization should be checked for completion. */ -static int iauth_cmd_hostname(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_hostname(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { struct AuthRequest *auth; - if (EmptyString(params)) { + if (EmptyString(params[0])) { sendto_iauth(cli, "E Missing :Missing hostname parameter"); return 0; } @@ -1597,29 +1782,44 @@ static int iauth_cmd_hostname(struct IAuth *iauth, struct Client *cli, char *par sendheader(cli, REPORT_FIN_DNS); } /* Set hostname from params. */ - ircd_strncpy(cli_sockhost(cli), params, HOSTLEN); + 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; } /** Set client's IP address. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params New IP address for client in dotted quad or * standard IPv6 format. * @return Zero. */ -static int iauth_cmd_ip_address(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_ip_address(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { struct irc_in_addr addr; struct AuthRequest *auth; + if (EmptyString(params[0])) { + sendto_iauth(cli, "E Missing :Missing IP address parameter"); + return 0; + } + /* Get AuthRequest for client. */ auth = cli_auth(cli); assert(auth != NULL); /* Parse the client's new IP address. */ - if (!ircd_aton(&addr, params)) { - sendto_iauth(cli, "E Invalid :Unable to parse IP address [%s]", params); + if (!ircd_aton(&addr, params[0])) { + sendto_iauth(cli, "E Invalid :Unable to parse IP address [%s]", params[0]); return 0; } @@ -1630,7 +1830,7 @@ static int iauth_cmd_ip_address(struct IAuth *iauth, struct Client *cli, char *p 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. */ @@ -1652,7 +1852,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. */ @@ -1669,6 +1869,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; @@ -1677,13 +1884,31 @@ 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, char *params) +static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { static time_t warn_time; @@ -1692,16 +1917,31 @@ static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, char * FlagClr(&cli_auth(cli)->flags, AR_IAUTH_PENDING); /* If a connection class was specified (and usable), assign the client to it. */ - if (!EmptyString(params)) { + if (!EmptyString(params[0])) { struct ConfItem *aconf; - aconf = auth_find_class_conf(params); - if (aconf) - attach_conf(cli, aconf); - else + aconf = auth_find_class_conf(params[0]); + 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); + params[0]); } return 1; @@ -1710,208 +1950,290 @@ static int iauth_cmd_done_client(struct IAuth *iauth, struct Client *cli, char * /** Accept a client in IAuth and assign them to an account. * @param[in] iauth Active IAuth session. * @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, char *params) +static int iauth_cmd_done_account(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { - char *end; size_t len; /* Sanity check. */ - if (EmptyString(params)) { + if (EmptyString(params[0])) { sendto_iauth(cli, "E Missing :Missing account parameter"); return 0; } /* Check length of account name. */ - len = strcspn(params, ": "); + len = strcspn(params[0], ": "); if (len > ACCOUNTLEN) { sendto_iauth(cli, "E Invalid :Account parameter too long"); return 0; } /* If account has a creation timestamp, use it. */ assert(cli_user(cli) != NULL); - if (params[len] == ':') - cli_user(cli)->acc_create = strtoul(params + len + 1, &end, 10); - else - end = params + 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, ACCOUNTLEN); + ircd_strncpy(cli_user(cli)->account, params[0], ACCOUNTLEN); SetAccount(cli); - /* Skip whitespace before next argument. */ - while (IsSpace(*end)) - ++end; + /* Fall through to the normal "done" handler. */ - return iauth_cmd_done_client(iauth, cli, end); + return iauth_cmd_done_client(iauth, cli, parc - 1, params + 1); } /** Reject a client's connection. * @param[in] iauth Active IAuth session. * @param[in] cli Client referenced by command. + * @param[in] parc Number of parameters (1). * @param[in] params Optional kill message. * @return Zero. */ -static int iauth_cmd_kill(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_kill(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { if (cli_auth(cli)) FlagClr(&cli_auth(cli)->flags, AR_IAUTH_PENDING); - if (EmptyString(params)) - params = "Access denied"; - exit_client(cli, cli, &me, params); + if (EmptyString(params[0])) + params[0] = "Access denied"; + exit_client(cli, cli, &me, params[0]); + 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. + * @param[in] parc Number of parameters (1). * @param[in] params Challenge message for client. * @return Zero. */ -static int iauth_cmd_challenge(struct IAuth *iauth, struct Client *cli, char *params) +static int iauth_cmd_challenge(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { - sendrawto_one(cli, "NOTICE AUTH :*** %s", params); + if (!EmptyString(params[0])) + sendrawto_one(cli, "NOTICE AUTH :*** %s", params[0]); return 0; } -/** Read input from \a iauth. - * Reads up to SERVER_TCP_WINDOW bytes per pass. - * @param[in] iauth Readable connection. +/** 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 void iauth_read(struct IAuth *iauth) +static int iauth_cmd_xquery(struct IAuth *iauth, struct Client *cli, + int parc, char **params) { - static char readbuf[SERVER_TCP_WINDOW]; + 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. + */ +static void iauth_parse(struct IAuth *iauth, char *message) +{ + char *params[MAXPARA + 1]; /* leave space for NULL */ + int parc = 0; iauth_cmd_handler handler; struct AuthRequest *auth; struct Client *cli; - char *old_buffer; - char *params; - char *endp; - char *src; - unsigned int length; int has_cli; - int res; int id; - switch (os_recv_nonb(s_fd(i_socket(iauth)), readbuf, sizeof(readbuf), &length)) - { - case IO_SUCCESS: break; - case IO_FAILURE: iauth_disconnect(iauth); - case IO_BLOCKED: return; - } - iauth->i_recvB += length; - old_buffer = iauth->i_buffer; - endp = old_buffer + iauth->i_count; - for (src = readbuf; length > 0; --length) { - *endp = *src++; - if (IsEol(*endp)) { - /* Terminate line, reset buffer and update statistics. */ - *endp = '\0'; - endp = old_buffer; - ++iauth->i_recvM; - - /* If spammy debug, send the message to opers. */ - if (i_debug(iauth) > 1) - sendto_opmask_butone(NULL, SNO_AUTH, "%s", endp); - - /* Find command handler. A lot of the handlers would be simpler - * with an argument splitter like in parse.c, but some commands - * (notably '>') do not use delimiters that way. - */ - switch (*(endp = old_buffer)) { - case '>': handler = iauth_cmd_snotice; has_cli = 0; break; - case 'G': handler = iauth_cmd_debuglevel; has_cli = 0; break; - case 'O': handler = iauth_cmd_policy; has_cli = 0; break; - case 'V': handler = iauth_cmd_version; has_cli = 0; break; - case 'a': handler = iauth_cmd_newconfig; has_cli = 0; break; - 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 '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 'C': handler = iauth_cmd_challenge; 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 - * off without telling opers. There is no way to - * signal that to exit_client(), so we fall through to - * the case that we do implement. - */ - case 'K': handler = iauth_cmd_kill; has_cli = 2; break; - case 'r': /* we handle termination directly */ continue; - default: sendto_iauth(NULL, "E Garbage :[%s]", endp); continue; - } + /* Find command handler... */ + switch (*(message++)) { + case '>': handler = iauth_cmd_snotice; has_cli = 0; break; + case 'G': handler = iauth_cmd_debuglevel; has_cli = 0; break; + case 'O': handler = iauth_cmd_policy; has_cli = 0; break; + case 'V': handler = iauth_cmd_version; has_cli = 0; break; + case 'a': handler = iauth_cmd_newconfig; has_cli = 0; break; + 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 + * off without telling opers. There is no way to + * signal that to exit_client(), so we fall through to + * the case that we do implement. + */ + case 'K': handler = iauth_cmd_kill; has_cli = 2; break; + case 'r': /* we handle termination directly */ return; + default: sendto_iauth(NULL, "E Garbage :[%s]", message); return; + } - /* Skip whitespace at start of arguments. */ - while (IsSpace(*++endp)) ; + while (parc < MAXPARA) { + while (IsSpace(*message)) /* skip leading whitespace */ + message++; - /* At this point, has_cli has one of three values: - * 0 - no client is identified - * 1 - a client is identified and must still be registering - * 2 - a client is identified but may be fully registered - */ + if (!*message) /* hit the end of the string, break out */ + break; - /* Figure out how to handle the command. */ - if (!has_cli) { - /* Handler does not need a client. */ - handler(iauth, NULL, endp); - } else { - /* Try to find the client associated with the request. */ - id = strtol(endp, ¶ms, 10); - while (IsSpace(*params)) - ++params; - if (id < 0 || id > HighestFd || !(cli = LocalClientArray[id])) { - /* Client no longer exists (or never existed). */ - sendto_iauth(NULL, "E Gone :[%s]", params); - } else if ((!(auth = cli_auth(cli)) - || !FlagHas(&auth->flags, AR_IAUTH_PENDING)) - && (has_cli == 1)) { - /* Client is done with IAuth checks. */ - sendto_iauth(cli, "E Done :[%s]", params); - } else { - struct irc_sockaddr addr; - char *orig_ip; - - /* Skip whitespace before IP address. */ - while (IsSpace(*params)) - ++params; - /* Record start of IP address, then null terminate it. */ - orig_ip = params; - while (!IsSpace(*params) && *params != '\0') - ++params; - if (IsSpace(*params)) - *params++ = '\0'; - /* Parse out client IP address and port number. */ - res = ipmask_parse(orig_ip, &addr.addr, NULL); - addr.port = strtol(params, ¶ms, 10); - /* Skip whitespace and optional sentinel after port number. */ - while (IsSpace(*params)) - ++params; - if (*params == ':') - ++params; - /* Check IP address and port number against expected. */ - if (0 == res - || irc_in_addr_cmp(&addr.addr, &cli_ip(cli)) - || (auth && addr.port != auth->port)) { - /* Report mismatch to iauth. */ - sendto_iauth(cli, "E Mismatch :[%s] != [%s]", - orig_ip, ircd_ntoa(&cli_ip(cli))); - } else if (handler(iauth, cli, params)) { - /* Handler indicated a possible state change. */ - check_auth_finished(auth, 0); - } - } - } + if (*message == ':') { /* found sentinel... */ + params[parc++] = message + 1; + break; /* it's the last parameter anyway */ + } + + params[parc++] = message; /* save the parameter */ + while (*message && !IsSpace(*message)) + message++; /* find the end of the parameter */ - /* Reset buffer pointer to read next line. */ - endp = old_buffer; + if (*message) /* terminate the parameter */ + *(message++) = '\0'; + } + + params[parc] = NULL; /* terminate the parameter list */ + + /* Check to see if the command specifies a client... */ + if (!has_cli) { + /* Handler does not need a client. */ + handler(iauth, NULL, parc, params); + } else { + /* Try to find the client associated with the request. */ + id = strtol(params[0], NULL, 10); + 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]); + else if ((!(auth = cli_auth(cli)) || + !FlagHas(&auth->flags, AR_IAUTH_PENDING)) && + has_cli == 1) + /* Client is done with IAuth checks. */ + sendto_iauth(cli, "E Done :[%s %s %s]", params[0], params[1], params[2]); + else { + struct irc_sockaddr addr; + int res; + + /* Parse IP address and port number from parameters */ + res = ipmask_parse(params[1], &addr.addr, NULL); + addr.port = strtol(params[2], NULL, 10); + + /* Check IP address and port number against expected. */ + if (0 == res || + irc_in_addr_cmp(&addr.addr, &cli_ip(cli)) || + (auth && addr.port != auth->port)) + /* 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) > 0) + /* Handler indicated a possible state change. */ + check_auth_finished(auth); } - else if (endp < old_buffer + BUFSIZE) - ++endp; } - iauth->i_count = endp - old_buffer; +} + +/** Read input from \a iauth. + * Reads up to SERVER_TCP_WINDOW bytes per pass. + * @param[in] iauth Readable connection. + */ +static void iauth_read(struct IAuth *iauth) +{ + static char readbuf[SERVER_TCP_WINDOW]; + unsigned int length, count; + char *sol; + char *eol; + + /* Copy partial data to readbuf, append new data. */ + length = iauth->i_count; + memcpy(readbuf, iauth->i_buffer, length); + if (IO_SUCCESS != os_recv_nonb(s_fd(i_socket(iauth)), + readbuf + length, + sizeof(readbuf) - length - 1, + &count)) + return; + readbuf[length += count] = '\0'; + + /* Parse each complete line. */ + for (sol = readbuf; (eol = strchr(sol, '\n')) != NULL; sol = eol + 1) { + *eol = '\0'; + if (*(eol - 1) == '\r') /* take out carriage returns, too... */ + *(eol - 1) = '\0'; + + /* If spammy debug, send the message to opers. */ + if (i_debug(iauth) > 1) + sendto_opmask_butone(NULL, SNO_AUTH, "Parsing: \"%s\"", sol); + + /* Parse the line... */ + iauth_parse(iauth, sol); + } + + /* Put unused data back into connection's buffer. */ + iauth->i_count = strlen(sol); + if (iauth->i_count > BUFSIZE) + iauth->i_count = BUFSIZE; + memcpy(iauth->i_buffer, sol, iauth->i_count); } /** Handle socket activity for an %IAuth connection. @@ -1928,8 +2250,7 @@ static void iauth_sock_callback(struct Event *ev) switch (ev_type(ev)) { case ET_DESTROY: - /* Hm, what happened here? */ - if (!IAuthHas(iauth, IAUTH_CLOSING)) + if (!IAuthHas(iauth, IAUTH_CLOSING) && !s_active(i_stderr(iauth))) iauth_do_spawn(iauth, 1); break; case ET_READ: @@ -1957,7 +2278,7 @@ static void iauth_sock_callback(struct Event *ev) static void iauth_read_stderr(struct IAuth *iauth) { static char readbuf[SERVER_TCP_WINDOW]; - unsigned int length; + unsigned int length, count; char *sol; char *eol; @@ -1967,13 +2288,15 @@ static void iauth_read_stderr(struct IAuth *iauth) if (IO_SUCCESS != os_recv_nonb(s_fd(i_stderr(iauth)), readbuf + length, sizeof(readbuf) - length - 1, - &length)) + &count)) return; - readbuf[length] = '\0'; + readbuf[length += count] = '\0'; /* Send each complete line to SNO_AUTH. */ for (sol = readbuf; (eol = strchr(sol, '\n')) != NULL; sol = eol + 1) { *eol = '\0'; + if (*(eol - 1) == '\r') /* take out carriage returns, too... */ + *(eol - 1) = '\0'; Debug((DEBUG_ERROR, "IAuth error: %s", sol)); log_write(LS_IAUTH, L_ERROR, 0, "IAuth error: %s", sol); sendto_opmask_butone(NULL, SNO_AUTH, "%s", sol); @@ -1999,14 +2322,18 @@ static void iauth_stderr_callback(struct Event *ev) assert(0 != iauth); switch (ev_type(ev)) { + case ET_DESTROY: + if (!IAuthHas(iauth, IAUTH_CLOSING) && !s_active(i_socket(iauth))) + iauth_do_spawn(iauth, 1); + 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"); @@ -2028,7 +2355,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. @@ -2045,5 +2371,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."); }