#include "list.h"
#include "msg.h" /* for MAXPARA */
#include "numeric.h"
+#include "numnicks.h"
#include "querycmds.h"
#include "random.h"
#include "res.h"
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
};
#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);
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;
}
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.
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:
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.
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)) {
/* Check for iauth timeout. */
if (FlagHas(&auth->flags, AR_IAUTH_PENDING)) {
- sendto_iauth(cptr, "T");
- if (IAuthHas(iauth, IAUTH_REQUIRED)) {
+ 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);
}
/* 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);
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;
}
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);
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);
/** 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;
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);
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.
/* 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;
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;
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]);
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));
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));
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]);
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. */
*/
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. */
/* 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;
}
}
/* 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;
}
/* 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. */
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;
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)
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]);
* @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)
}
/* 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);
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.
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.
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
} 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 <id> <ip> <port>");
+ 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]);
/* 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);
}
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");
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.
send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":%s",
link->value.cp);
}
- send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":End of IAuth statistics.");
}