implement IAUTH client code to query a separate server before allowing
authorMichael Poole <mdpoole@troilus.org>
Wed, 30 Jun 2004 22:59:10 +0000 (22:59 +0000)
committerMichael Poole <mdpoole@troilus.org>
Wed, 30 Jun 2004 22:59:10 +0000 (22:59 +0000)
a user onto IRC.

git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1083 c9e4aea6-c8fd-4c43-8297-357d70d61c8c

13 files changed:
ChangeLog
doc/example.conf
doc/readme.iauth [new file with mode: 0644]
include/client.h
include/ircd_auth.h [new file with mode: 0644]
ircd/Makefile.in
ircd/iauth.c [deleted file]
ircd/ircd_auth.c [new file with mode: 0644]
ircd/ircd_lexer.l
ircd/ircd_parser.y
ircd/s_conf.c
ircd/s_misc.c
ircd/s_user.c

index 1e217b94c5441cc98acc2349c6a2503a2fe153fe..dbdb0772387740d651aa168726a03536c2fb0a62 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,24 @@
+2004-06-30  Michael Poole <mdpoole@troilus.org>
+
+       * doc/readme.iauth, include/ircd_auth.h, ircd/ircd_auth.c: New
+       files.
+
+       * doc/example.conf: Illustrate IAUTH configuration.
+
+       * include/client.h: Add fields to record IAUTH status.
+
+       * ircd/Makefile.in: Add ircd_auth.c to Makefile.
+
+       * ircd/ircd_lexer.l, ircd/ircd_parser.y: Add new IAUTH section.
+
+       * ircd/s_conf.c: Notify IAUTH code when reloading a configuration
+       so that an obsolete connection can be abandoned.
+
+       * ircd/s_misc.c: Report client exits via IAUTH.
+
+       * ircd/s_user.c: If IAUTH is active and a connecting user has not
+       been checked against it, interrogate the IAUTH server.
+
 2004-06-25  Michael Poole <mdpoole@troilus.org>
 
        * configure.in: Check for crypt.h as well.
index c55148c51524ad34b026820550095e16be097911..d6132afdc936ab79806883003724deab82d146eb 100644 (file)
@@ -738,6 +738,15 @@ Pseudo "LOGIN" {
  nick = "X@channels.undernet.org";
 };
 
+# You can ask a separate server whether to allow users to connect.
+IAuth {
+ pass = "ircd-iauth";
+ host = "127.0.0.1";
+ port = 7700;
+ connectfreq = 30;
+ timeout = 60;
+};
+
 # [features]
 # IRC servers have a large number of options and features.  Most of these
 # are set at compile time through the use of #define's--see "make config"
diff --git a/doc/readme.iauth b/doc/readme.iauth
new file mode 100644 (file)
index 0000000..455d9da
--- /dev/null
@@ -0,0 +1,82 @@
+OVERVIEW
+========
+
+The IAUTH protocol used here is based on the one in ircd-hybrid 7.0,
+with minor changes to support login-on-connect and true IAUTH-side
+connection classes.  (Several networks use central authorities to vary
+per-netblock connection limits; for example, users from one ISP may
+only be allowed one connection per IP, or one shell provider's
+netblock may be limited to 50 total connections.)  IAUTH-side
+connection classes are controlled by a configuration option; if that
+is enabled, this document will say ICLASS is enabled.
+
+As in IRC, lines sent between the IRC and IAUTH servers are limited to
+512 characters, including the terminating <CR> <LF> sequence.  As in
+IRC, the final argument on a line may be prefixed with :, and must be
+prefixed with : if it contains a space (decimal 32) character.  Tokens
+are separated by single space characters, and each line is a separate
+command.  The first token on a line is a case-insensitive command
+name; unrecognized commands must be ignored.
+
+GREETING
+========
+
+The IRC server connects and sends the Server greeting:
+  Server <servername> [password]
+If ICLASS is enabled, it sends a list of currently connected users:
+  MyUsers <uid>:<username>@<hostname>:<ip> ...
+The IRC server may send several MyUsers lines.  When it has sent all
+MyUsers lines, it sends an EndUsers line:
+  EndUsers
+If ICLASS is disabled, EndUsers is sent immediately after Server.
+
+LOGIN REQUESTS
+==============
+
+When users connect, the IRC server sends a DoAuth request:
+  FullAuth <uid> <nickname> <username> <hostname> <ip> <account> <password> <realname>
+<uid> is a text string up to 20 characters long that identifies the
+client, and is unique a BadAuth response is received or until an
+ExitUser command is sent with the same uid (see below for details on
+those messages).  <uid> may not contain a colon character.  <nickname>
+is the client's initially requested nickname.  <username> is the
+username returned by the ident server (RFC 1413), or a tilde-prefixed
+username supplied by the user.  <hostname> is a text hostname,
+possibly in the form of a dotted quad or IPv6 address, or the
+character '?'.  <ip> is a dotted quad IPv4 address or an IPv6 hex
+address.  <account> and <password> are optional, and are used when the
+client attempts login-on-connect.  <realname> is the realname
+specified by the client's USER message, and may contain spaces.
+
+If the client is accepted, the IAUTH server responds:
+  DoneAuth <uid> <username> <hostname> <class> [account]
+<username> is a replacement username, and <hostname> is a replacement
+hostname.  If the <hostname> from DoAuth was ?, <hostname> is the
+result of a DNS lookup for the client.  <class> is the name of a
+connection class for the client.  <account> is optional and is
+provided if the user's login was successful.
+
+If the client is rejected, the IAUTH server responds:
+  BadAuth <uid> :<reason>
+<reason> may include spaces, and should have a leading ':' sentinel.
+
+DISCONNECTS
+===========
+
+If ICLASS is enabled, the IRC server sends ExitUser when a client
+disconnects:
+  ExitUser <uid>
+
+DIFFERENCES FROM IRCD-HYBRID
+============================
+
+The ircd-hybrid IAUTH code is slightly bitrotted and disabled in 7.0
+(through at least 7.0.1).  This code added the following items:
+  MyUsers, EndUsers and ExitUser commands
+  Server passwords may contain whitespace and be prefixed by :
+  DoneAuth may include an account name
+  FullAuth command replaces DoAuth command and adds account, password,
+    realname parameters
+The Class command is present in ircd-hybrid's code but not used here.
+IP addresses in ircd-hybrid are "in unsigned int format," which is
+limited to IPv4, and so it is not used here.
index cb6779693c511aecf0b974eca5828a3f5bb486a8..478f9bc4b2dc409c871cff6c17366525be63d6e8 100644 (file)
@@ -139,6 +139,7 @@ enum Flag
     FLAG_BURST,                     /* Server is receiving a net.burst */
     FLAG_BURST_ACK,                 /* Server is waiting for eob ack */
     FLAG_IPCHECK,                   /* Added or updated IPregistry data */
+    FLAG_IAUTHED,                   /* Got IAUTH response for user */
     FLAG_LOCOP,                     /* Local operator -- SRB */
     FLAG_SERVNOTICE,                /* server notices such as kill */
     FLAG_OPER,                      /* Operator */
@@ -211,6 +212,7 @@ struct Connection
   struct Socket       con_socket; /* socket descriptor for client */
   struct Timer        con_proc; /* process latent messages from client */
   struct AuthRequest* con_auth; /* auth request for client */
+  struct IAuthRequest* con_iauth; /* iauth request for client */
 };
 
 #define CONNECTION_MAGIC 0x12f955f3
@@ -308,6 +310,7 @@ struct Client {
 #define cli_socket(cli)                ((cli)->cli_connect->con_socket)
 #define cli_proc(cli)          ((cli)->cli_connect->con_proc)
 #define cli_auth(cli)          ((cli)->cli_connect->con_auth)
+#define cli_iauth(cli)          ((cli)->cli_connect->con_iauth)
 
 #define con_verify(con)                ((con)->con_magic == CONNECTION_MAGIC)
 #define con_magic(con)         ((con)->con_magic)
@@ -347,6 +350,7 @@ struct Client {
 #define con_socket(con)                ((con)->con_socket)
 #define con_proc(con)          ((con)->con_proc)
 #define con_auth(con)          ((con)->con_auth)
+#define con_iauth(con)          ((con)->con_iauth)
 
 #define STAT_CONNECTING         0x001 /* connecting to another server */
 #define STAT_HANDSHAKE          0x002 /* pass - server sent */
@@ -414,6 +418,7 @@ struct Client {
 #define IsDead(x)               HasFlag(x, FLAG_DEADSOCKET)
 #define IsDeaf(x)               HasFlag(x, FLAG_DEAF)
 #define IsIPChecked(x)          HasFlag(x, FLAG_IPCHECK)
+#define IsIAuthed(x)            HasFlag(x, FLAG_IAUTHED)
 #define IsIdented(x)            HasFlag(x, FLAG_GOTID)
 #define IsInvisible(x)          HasFlag(x, FLAG_INVISIBLE)
 #define IsJunction(x)           HasFlag(x, FLAG_JUNCTION)
@@ -442,6 +447,7 @@ struct Client {
 #define SetDebug(x)             SetFlag(x, FLAG_DEBUG)
 #define SetGotId(x)             SetFlag(x, FLAG_GOTID)
 #define SetIPChecked(x)         SetFlag(x, FLAG_IPCHECK)
+#define SetIAuthed(x)           SetFlag(x, FLAG_IAUTHED)
 #define SetInvisible(x)         SetFlag(x, FLAG_INVISIBLE)
 #define SetJunction(x)          SetFlag(x, FLAG_JUNCTION)
 #define SetLocOp(x)             SetFlag(x, FLAG_LOCOP)
diff --git a/include/ircd_auth.h b/include/ircd_auth.h
new file mode 100644 (file)
index 0000000..61e27f1
--- /dev/null
@@ -0,0 +1,40 @@
+#ifndef INCLUDED_ircd_auth_h
+#define INCLUDED_ircd_auth_h
+
+/*
+ * IRC - Internet Relay Chat, ircd/ircd_auth.h
+ * Copyright 2004 Michael Poole <mdpoole@troilus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#ifndef INCLUDED_config_h
+#include "config.h"
+#endif
+
+struct IAuth;
+extern struct IAuth *iauth_active;
+
+struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout);
+int iauth_start_client(struct IAuth *iauth, struct Client *cptr);
+void iauth_exit_client(struct Client *cptr);
+
+void iauth_mark_closing(void);
+void iauth_close_unused(void);
+
+#endif
index 03d23d479f23a4d7c87e04e0d13da54a74f6fabb..0f02c979e08866c4a0ebc63af6f832b446e04a61 100644 (file)
@@ -107,6 +107,7 @@ IRCD_SRC = \
        hash.c \
        ircd.c \
        ircd_alloc.c \
+       ircd_auth.c \
        ircd_crypt.c \
        ircd_events.c \
        ircd_features.c \
diff --git a/ircd/iauth.c b/ircd/iauth.c
deleted file mode 100644 (file)
index 51cd131..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-/************************************************************************
- *   IRC - Internet Relay Chat, src/listener.c
- *   Copyright (C) 2001 Perry Lorier <isomer@coders.net>
- *
- *   This program is free software; you can redistribute it and/or modify
- *   it under the terms of the GNU General Public License as published by
- *   the Free Software Foundation; either version 1, or (at your option)
- *   any later version.
- *
- *   This program is distributed in the hope that it will be useful,
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- *   GNU General Public License for more details.
- *
- *   You should have received a copy of the GNU General Public License
- *   along with this program; if not, write to the Free Software
- *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
- *  $Id$
- */
-#include "config.h"
-#include "iauth.h"
-#include <sys/types.h>
-#include <sys/socket.h>
-
-struct Iauth_Outstanding {
-       struct iauth_outstanding *next;
-       int client_id;
-}
-
-static struct Iauth_Outstanding *iauth_freelist=NULL;
-struct Iauth IauthPollList = NULL;
-
-void iauth_new(char *service)
-{
-       /* TODO: Check to see if the service is a hostname, and if so
-         *       connect to it like hybrid does.  Hybrid uses a blocking
-        *       gethostbyname here (bletch!!).  On the other hand a
-        *       nonblocking solutions likely to be worse....
-         */    
-       struct Iauth* tmp = (struct Iauth *)malloc(sizeof(struct Iauth));
-       int fd[2];
-       tmp->next = IauthPollList;
-       tmp->fd = 0;
-       tmp->service=strdup(service);
-       tmp->ref_count=0;
-       tmp->active=0;
-       if (socketpair(domain,type,protocol,fd)<0) {
-               free(tmp->service);
-               free(tmp);
-               return;         
-       }
-       tmp->fd=fd[0];
-       if (0 == fork()) {
-                (void)close(pi[0]);
-               (void)dup2(pi[1],0);
-               (void)dup2(pi[1],1);
-                for (i = 2; i < MAXCONNECTIONS; i++)
-                       (void)close(i);
-                if (pi[1] != 0 && pi[1] != 1)
-                      (void)close(pi[1]);
-                (void)execlp(tmp->service, tmp->service, 0);
-                exit(-1);
-       } else {
-               /* TODO: Put the real servername in there */
-               (void)write(tmp->fd,"Server undernet.org 1.1");
-       }
-       IauthPollList=tmp;
-}
diff --git a/ircd/ircd_auth.c b/ircd/ircd_auth.c
new file mode 100644 (file)
index 0000000..e3629d0
--- /dev/null
@@ -0,0 +1,696 @@
+/*
+ * IRC - Internet Relay Chat, ircd/ircd_auth.c
+ * Copyright 2004 Michael Poole <mdpoole@troilus.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * $Id$
+ */
+
+#include "config.h"
+#include "client.h"
+#include "ircd_alloc.h"
+#include "ircd_auth.h"
+#include "ircd_events.h"
+#include "ircd_features.h"
+#include "ircd_log.h"
+#include "ircd_osdep.h"
+#include "ircd_snprintf.h"
+#include "ircd_string.h"
+#include "ircd.h"
+#include "msg.h"
+#include "msgq.h"
+#include "res.h"
+#include "s_bsd.h"
+#include "s_misc.h"
+#include "s_user.h"
+#include "send.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <netdb.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+struct IAuthRequest {
+  struct IAuthRequest *iar_prev;        /* previous request struct */
+  struct IAuthRequest *iar_next;        /* next request struct */
+  struct Client *iar_client;            /* client being authenticated */
+  char iar_timed;                       /* if non-zero, using parent i_request_timer */
+};
+
+enum IAuthFlag
+{
+  IAUTH_BLOCKED,                        /* socket buffer full */
+  IAUTH_CONNECTED,                      /* server greeting/handshake done */
+  IAUTH_ABORT,                          /* abort connection asap */
+  IAUTH_ICLASS,                         /* tell iauth about all local users */
+  IAUTH_CLOSING,                        /* candidate to be disposed */
+  IAUTH_LAST_FLAG
+};
+DECLARE_FLAGSET(IAuthFlags, IAUTH_LAST_FLAG);
+
+struct IAuth {
+  struct IAuthRequest i_list_head;      /* doubly linked list of requests */
+  struct MsgQ i_sendQ;                  /* messages queued to send */
+  struct Socket i_socket;               /* connection to server */
+  struct Timer i_reconn_timer;          /* when to reconnect the connection */
+  struct Timer i_request_timer;         /* when the current request times out */
+  struct IAuthFlags i_flags;            /* connection state/status/flags */
+  struct DNSQuery i_query;              /* DNS lookup for iauth server */
+  unsigned int i_recvM;                 /* messages received */
+  unsigned int i_sendM;                 /* messages sent */
+  unsigned int i_recvK;                 /* kilobytes received */
+  unsigned int i_sendK;                 /* kilobytes sent */
+  unsigned short i_recvB;               /* bytes received modulo 1024 */
+  unsigned short i_sendB;               /* bytes sent modulo 1024 */
+  time_t i_reconnect;                   /* seconds to wait before reconnecting */
+  time_t i_timeout;                     /* seconds to wait for a request */
+  unsigned int i_count;                 /* characters used in i_buffer */
+  char i_buffer[BUFSIZE+1];             /* partial unprocessed line from server */
+  char i_passwd[PASSWDLEN+1];           /* password for connection */
+  char i_host[HOSTLEN+1];               /* iauth server hostname */
+  in_addr_t i_addr;                     /* iauth server ip address */
+  unsigned short i_port;                /* iauth server port */
+  struct IAuth *i_next;                 /* next connection in list */
+};
+
+#define i_flags(iauth) ((iauth)->i_flags)
+#define IAuthGet(iauth, flag) FlagHas(&i_flags(iauth), flag)
+#define IAuthSet(iauth, flag) FlagSet(&i_flags(iauth), flag)
+#define IAuthClr(iauth, flag) FlagClr(&i_flags(iauth), flag)
+#define i_GetBlocked(iauth) IAuthGet(iauth, IAUTH_BLOCKED)
+#define i_SetBlocked(iauth) IAuthSet(iauth, IAUTH_BLOCKED)
+#define i_ClrBlocked(iauth) IAuthClr(iauth, IAUTH_BLOCKED)
+#define i_GetConnected(iauth) IAuthGet(iauth, IAUTH_CONNECTED)
+#define i_SetConnected(iauth) IAuthSet(iauth, IAUTH_CONNECTED)
+#define i_ClrConnected(iauth) IAuthClr(iauth, IAUTH_CONNECTED)
+#define i_GetAbort(iauth) IAuthGet(iauth, IAUTH_ABORT)
+#define i_SetAbort(iauth) IAuthSet(iauth, IAUTH_ABORT)
+#define i_ClrAbort(iauth) IAuthClr(iauth, IAUTH_ABORT)
+#define i_GetIClass(iauth) IAuthGet(iauth, IAUTH_ICLASS)
+#define i_SetIClass(iauth) IAuthSet(iauth, IAUTH_ICLASS)
+#define i_ClrIClass(iauth) IAuthClr(iauth, IAUTH_ICLASS)
+#define i_GetClosing(iauth) IAuthGet(iauth, IAUTH_CLOSING)
+#define i_SetClosing(iauth) IAuthSet(iauth, IAUTH_CLOSING)
+#define i_ClrClosing(iauth) IAuthClr(iauth, IAUTH_CLOSING)
+
+#define i_list_head(iauth) ((iauth)->i_list_head)
+#define i_socket(iauth) ((iauth)->i_socket)
+#define i_reconn_timer(iauth) ((iauth)->i_reconn_timer)
+#define i_request_timer(iauth) ((iauth)->i_request_timer)
+#define i_query(iauth) ((iauth)->i_query)
+#define i_recvB(iauth) ((iauth)->i_recvB)
+#define i_recvK(iauth) ((iauth)->i_recvK)
+#define i_recvM(iauth) ((iauth)->i_recvM)
+#define i_sendB(iauth) ((iauth)->i_sendB)
+#define i_sendK(iauth) ((iauth)->i_sendK)
+#define i_sendM(iauth) ((iauth)->i_sendM)
+#define i_sendQ(iauth) ((iauth)->i_sendQ)
+#define i_reconnect(iauth) ((iauth)->i_reconnect)
+#define i_timeout(iauth) ((iauth)->i_timeout)
+#define i_count(iauth) ((iauth)->i_count)
+#define i_buffer(iauth) ((iauth)->i_buffer)
+#define i_passwd(iauth) ((iauth)->i_passwd)
+#define i_host(iauth) ((iauth)->i_host)
+#define i_addr(iauth) ((iauth)->i_addr)
+#define i_port(iauth) ((iauth)->i_port)
+#define i_next(iauth) ((iauth)->i_next)
+
+struct IAuthCmd {
+  const char *iac_name;
+  void (*iac_func)(struct IAuth *iauth, int, char *[]);
+};
+
+struct IAuth *iauth_active;
+
+static const struct IAuthCmd iauth_cmdtab[];
+
+static void iauth_write(struct IAuth *iauth);
+static void iauth_reconnect(struct IAuth *iauth);
+static void iauth_disconnect(struct IAuth *iauth);
+static void iauth_sock_callback(struct Event *ev);
+static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar);
+static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar);
+
+struct IAuth *iauth_connect(char *host, unsigned short port, char *passwd, time_t reconnect, time_t timeout)
+{
+  struct IAuth *iauth;
+
+  for (iauth = iauth_active; iauth; iauth = i_next(iauth)) {
+    if (!ircd_strncmp(i_host(iauth), host, HOSTLEN)
+        && (i_port(iauth) == port)) {
+      i_ClrClosing(iauth);
+      i_reconnect(iauth) = reconnect;
+      if (t_active(&i_reconn_timer(iauth)) && (t_expire(&i_reconn_timer(iauth)) > CurrentTime + i_reconnect(iauth)))
+        timer_chg(&i_reconn_timer(iauth), TT_RELATIVE, i_reconnect(iauth));
+      break;
+    }
+  }
+  if (NULL == iauth) {
+    if (iauth_active && !i_GetClosing(iauth_active)) {
+      log_write(LS_CONFIG, L_WARNING, 0, "Creating extra active IAuth connection to %s:%d.", host, port);
+    }
+    iauth = MyCalloc(1, sizeof(*iauth));
+    i_list_head(iauth).iar_prev = &i_list_head(iauth);
+    i_list_head(iauth).iar_next = &i_list_head(iauth);
+    msgq_init(&i_sendQ(iauth));
+    ircd_strncpy(i_host(iauth), host, HOSTLEN);
+    i_port(iauth) = port;
+    i_addr(iauth) = INADDR_NONE;
+    i_next(iauth) = iauth_active;
+    iauth_active = iauth;
+    i_reconnect(iauth) = reconnect;
+    iauth_reconnect(iauth);
+  }
+  if (passwd)
+    ircd_strncpy(i_passwd(iauth), passwd, PASSWDLEN);
+  else
+    i_passwd(iauth)[0] = '\0';
+  i_timeout(iauth) = timeout;
+  return iauth;
+}
+
+void iauth_mark_closing(void)
+{
+  struct IAuth *iauth;
+  for (iauth = iauth_active; iauth; iauth = i_next(iauth))
+    i_SetClosing(iauth);
+}
+
+void iauth_close(struct IAuth *iauth)
+{
+  /* Figure out what to do with the closing connection's requests. */
+  if (i_list_head(iauth).iar_next != &i_list_head(iauth)) {
+    struct IAuthRequest *iar;
+    if (iauth_active || i_next(iauth)) {
+      /* If iauth_active != NULL, send requests to it; otherwise if
+       * i_next(iauth) != NULL, we can hope it or some later
+       * connection will be active.
+       */
+      struct IAuth *target = iauth_active ? iauth_active : i_next(iauth);
+
+      /* Append iauth->i_list_head to end of target->i_list_head. */
+      iar = i_list_head(iauth).iar_next;
+      iar->iar_prev = i_list_head(target).iar_prev;
+      i_list_head(target).iar_prev->iar_next = iar;
+      iar = i_list_head(iauth).iar_prev;
+      iar->iar_next = &i_list_head(target);
+      i_list_head(target).iar_prev = iar;
+
+      /* If the target is not closing, send the requests. */
+      for (iar = i_list_head(iauth).iar_next;
+           iar != &i_list_head(target);
+           iar = iar->iar_next) {
+        if (!i_GetClosing(target))
+          iauth_send_request(target, iar);
+      }
+    } else {
+      /* No active connections - approve the requests and drop them. */
+      while ((iar = i_list_head(iauth).iar_next) != &i_list_head(iauth)) {
+        struct Client *client = iar->iar_client;
+        iauth_dispose_request(iauth, iar);
+        register_user(client, client, cli_name(client), cli_username(client));
+      }
+    }
+  }
+  /* Make sure the connection closes with an empty request list. */
+  i_list_head(iauth).iar_prev = &i_list_head(iauth);
+  i_list_head(iauth).iar_next = &i_list_head(iauth);
+  /* Cancel the timer, if it is active. */
+  if (t_active(&i_reconn_timer(iauth)))
+    timer_del(&i_reconn_timer(iauth));
+  if (t_active(&i_request_timer(iauth)))
+    timer_del(&i_request_timer(iauth));
+  /* Disconnect from the server. */
+  if (s_fd(&i_socket(iauth)) != -1)
+    iauth_disconnect(iauth);
+  /* Free memory. */
+  MyFree(iauth);
+}
+
+void iauth_close_unused(void)
+{
+  struct IAuth *prev, *iauth, *next;
+  
+  for (prev = NULL, iauth = iauth_active; iauth; iauth = next) {
+    next = i_next(iauth);
+    if (i_GetClosing(iauth)) {
+      /* Update iauth_active linked list. */
+      if (prev)
+        i_next(prev) = next;
+      else
+        iauth_active = next;
+      /* Close and destroy the connection. */
+      iauth_close(iauth);
+    } else {
+      prev = iauth;
+    }
+  }
+}
+
+static void iauth_send(struct IAuth *iauth, const char *format, ...)
+{
+  va_list vl;
+  struct MsgBuf *mb;
+
+  va_start(vl, format);
+  mb = msgq_vmake(0, format, vl);
+  va_end(vl);
+  msgq_add(&i_sendQ(iauth), mb, 0);
+  msgq_clean(mb);
+}
+
+static void iauth_protocol_violation(struct IAuth *iauth, const char *format, ...)
+{
+  struct VarData vd;
+  assert(iauth != 0);
+  assert(format != 0);
+  vd.vd_format = format;
+  va_start(vd.vd_args, format);
+  sendwallto_group_butone(&me, WALL_DESYNCH, NULL, "IAuth protocol violation: %v", &vd);
+  va_end(vd.vd_args);
+}
+
+static void iauth_on_connect(struct IAuth *iauth)
+{
+  struct IAuthRequest *iar;
+  if (EmptyString(i_passwd(iauth)))
+    iauth_send(iauth, "Server %s", cli_name(&me));
+  else
+    iauth_send(iauth, "Server %s %s", cli_name(&me), i_passwd(iauth));
+  if (i_GetIClass(iauth)) {
+    /* TODO: report local users to iauth */
+    iauth_send(iauth, "EndUsers");
+  }
+  i_SetConnected(iauth);
+  for (iar = i_list_head(iauth).iar_next;
+       iar != &i_list_head(iauth);
+       iar = iar->iar_next)
+    iauth_send_request(iauth, iar);
+  iauth_write(iauth);
+}
+
+static void iauth_disconnect(struct IAuth *iauth)
+{
+  close(s_fd(&i_socket(iauth)));
+  socket_del(&i_socket(iauth));
+  s_fd(&i_socket(iauth)) = -1;
+}
+
+static void iauth_dns_callback(void *vptr, struct hostent *he)
+{
+  struct IAuth *iauth = vptr;
+  if (!he) {
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host lookup failed", i_host(iauth));
+  } else if (he->h_addrtype != AF_INET) {
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: bad host type %d", i_host(iauth), he->h_addrtype);
+  } else {
+    assert(he->h_addrtype == sizeof(i_addr(iauth)));
+    memcpy(&i_addr(iauth), he->h_addr_list[0], sizeof(i_addr(iauth)));
+    if (INADDR_NONE == i_addr(iauth)) {
+      sendto_opmask_butone(0, SNO_OLDSNO, "IAuth connection to %s failed: host came back as INADDR_NONE", i_host(iauth));
+      return;
+    }
+    iauth_reconnect(iauth);
+  }
+}
+
+static void iauth_reconnect_ev(struct Event *ev)
+{
+  if (ev_type(ev) == ET_EXPIRE)
+    iauth_reconnect(t_data(ev_timer(ev)));
+}
+
+static void iauth_schedule_reconnect(struct IAuth *iauth)
+{
+  struct Timer *timer;
+  assert(!t_active(&i_reconn_timer(iauth)));
+  timer = timer_init(&i_reconn_timer(iauth));
+  timer_add(timer, iauth_reconnect_ev, iauth, TT_RELATIVE, i_reconnect(iauth));
+}
+
+static void iauth_reconnect(struct IAuth *iauth)
+{
+  extern struct sockaddr_in VirtualHost;
+  struct sockaddr_in sin;
+  IOResult result;
+  int fd;
+
+  if (INADDR_NONE == i_addr(iauth)) {
+    i_addr(iauth) = inet_addr(i_host(iauth));
+    if (INADDR_NONE == i_addr(iauth)) {
+      i_query(iauth).vptr = iauth;
+      i_query(iauth).callback = iauth_dns_callback;
+      gethost_byname(i_host(iauth), &i_query(iauth));
+      return;
+    }
+  }
+  fd = socket(AF_INET, SOCK_STREAM, 0);
+  if (0 > fd) {
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to allocate socket: %s", strerror(errno));
+    return;
+  }
+  if (feature_bool(FEAT_VIRTUAL_HOST)
+      && bind(fd, (struct sockaddr*)&VirtualHost, sizeof(VirtualHost)) != 0) {
+    close(fd);
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to bind vhost: %s", strerror(errno));
+    return;
+  }
+  if (!os_set_sockbufs(fd, SERVER_TCP_WINDOW, SERVER_TCP_WINDOW)) {
+    close(fd);
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to set socket buffers: %s", strerror(errno));
+    return;
+  }
+  if (!os_set_nonblocking(fd)) {
+    close(fd);
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to make socket non-blocking: %s", strerror(errno));
+    return;
+  }
+  memset(&sin, 0, sizeof(sin));
+  sin.sin_family = AF_INET;
+  sin.sin_addr.s_addr = i_addr(iauth);
+  sin.sin_port = htons(i_port(iauth));
+  result = os_connect_nonb(fd, &sin);
+  if (result == IO_FAILURE) {
+    close(fd);
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to initiate connection: %s", strerror(errno));
+    return;
+  }
+  if (!socket_add(&i_socket(iauth), iauth_sock_callback, iauth,
+                  (result == IO_SUCCESS) ? SS_CONNECTED : SS_CONNECTING,
+                  SOCK_EVENT_READABLE | SOCK_EVENT_WRITABLE, fd)) {
+    close(fd);
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth reconnect unable to add socket: %s", strerror(errno));
+    return;
+  }
+}
+
+static void iauth_read(struct IAuth *iauth)
+{
+  char *src, *endp, *old_buffer, *argv[MAXPARA + 1];
+  unsigned int length, argc, ii;
+  char readbuf[SERVER_TCP_WINDOW];
+
+  length = 0;
+  if (IO_FAILURE == os_recv_nonb(s_fd(&i_socket(iauth)), readbuf, sizeof(readbuf), &length))
+    return;
+  i_recvB(iauth) += length;
+  if (i_recvB(iauth) > 1023) {
+    i_recvK(iauth) += i_recvB(iauth) >> 10;
+    i_recvB(iauth) &= 1023;
+  }
+  old_buffer = i_buffer(iauth);
+  endp = old_buffer + i_count(iauth);
+  for (src = readbuf; length > 0; --length) {
+    *endp = *src++;
+    if (IsEol(*endp)) {
+      /* Skip blank lines. */
+      if (endp == old_buffer)
+        continue;
+      /* NUL-terminate line and split parameters. */
+      *endp = '\0';
+      for (argc = 0, endp = old_buffer; *endp && (argc < MAXPARA); ) {
+        while (*endp == ' ')
+          *endp++ = '\0';
+        if (*endp == '\0')
+          break;
+        if (*endp == ':')
+        {
+          argv[argc++] = endp + 1;
+          break;
+        }
+        argv[argc++] = endp;
+        for (; *endp && *endp != ' '; ++endp) ;
+      }
+      argv[argc] = NULL;
+      /* Count line and reset endp to start of buffer. */
+      i_recvM(iauth)++;
+      endp = old_buffer;
+      /* Look up command and try to dispatch. */
+      if (argc > 0) {
+        for (ii = 0; iauth_cmdtab[ii].iac_name; ++ii) {
+          if (!ircd_strcmp(iauth_cmdtab[ii].iac_name, argv[0])) {
+            iauth_cmdtab[ii].iac_func(iauth, argc, argv);
+            if (i_GetAbort(iauth))
+              iauth_disconnect(iauth);
+            break;
+          }
+        }
+      }
+    }
+    else if (endp < old_buffer + BUFSIZE)
+      endp++;
+  }
+  i_count(iauth) = endp - old_buffer;
+}
+
+static void iauth_write(struct IAuth *iauth)
+{
+  unsigned int bytes_tried, bytes_sent;
+  IOResult iores;
+
+  if (i_GetBlocked(iauth))
+    return;
+  while (MsgQLength(&i_sendQ(iauth)) > 0) {
+    iores = os_sendv_nonb(s_fd(&i_socket(iauth)), &i_sendQ(iauth), &bytes_tried, &bytes_sent);
+    switch (iores) {
+    case IO_SUCCESS:
+      msgq_delete(&i_sendQ(iauth), bytes_sent);
+      i_sendB(iauth) += bytes_sent;
+      if (i_sendB(iauth) > 1023) {
+        i_sendK(iauth) += i_sendB(iauth) >> 10;
+        i_sendB(iauth) &= 1023;
+      }
+      if (bytes_tried == bytes_sent)
+        break;
+      /* If bytes_sent < bytes_tried, fall through to IO_BLOCKED. */
+    case IO_BLOCKED:
+      i_SetBlocked(iauth);
+      socket_events(&i_socket(iauth), SOCK_ACTION_ADD | SOCK_EVENT_WRITABLE);
+      return;
+    case IO_FAILURE:
+      iauth_disconnect(iauth);
+      return;
+    }
+  }
+  /* We were able to flush all events, so remove notification. */
+  socket_events(&i_socket(iauth), SOCK_ACTION_DEL | SOCK_EVENT_WRITABLE);
+}
+
+static void iauth_sock_callback(struct Event *ev)
+{
+  struct IAuth *iauth;
+
+  assert(0 != ev_socket(ev));
+  iauth = (struct IAuth*) s_data(ev_socket(ev));
+  assert(0 != iauth);
+
+  switch (ev_type(ev)) {
+  case ET_CONNECT:
+    socket_state(ev_socket(ev), SS_CONNECTED);
+    iauth_on_connect(iauth);
+    break;
+  case ET_DESTROY:
+    if (!i_GetClosing(iauth))
+      iauth_schedule_reconnect(iauth);
+    break;
+  case ET_READ:
+    iauth_read(iauth);
+    break;
+  case ET_WRITE:
+    i_ClrBlocked(iauth);
+    iauth_write(iauth);
+    break;
+  case ET_EOF:
+    iauth_disconnect(iauth);
+    break;
+  case ET_ERROR:
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth socket error: %s", strerror(ev_data(ev)));
+    log_write(LS_SOCKET, L_ERROR, 0, "IAuth socket error: %s", strerror(ev_data(ev)));
+    iauth_disconnect(iauth);
+    break;
+  default:
+    assert(0 && "Unrecognized event type");
+    break;
+  }
+}
+
+/* Functions related to IAuthRequest structs */
+
+static void iauth_request_ev(struct Event *ev)
+{
+  /* TODO: this could probably be more intelligent */
+  if (ev_type(ev) == ET_EXPIRE) {
+    sendto_opmask_butone(0, SNO_OLDSNO, "IAuth request timed out; reconnecting");
+    iauth_reconnect(t_data(ev_timer(ev)));
+  }
+}
+
+static void iauth_send_request(struct IAuth *iauth, struct IAuthRequest *iar)
+{
+  struct Client *client;
+
+  /* If iauth is not connected, we must defer the request. */
+  if (!i_GetConnected(iauth))
+    return;
+
+  /* If no timed request, set up expiration timer. */
+  if (!t_active(&i_request_timer(iauth))) {
+    struct Timer *timer = timer_init(&i_request_timer(iauth));
+    timer_add(timer, iauth_request_ev, iauth, TT_RELATIVE, i_timeout(iauth));
+    iar->iar_timed = 1;
+  } else
+    iar->iar_timed = 0;
+
+  /* Send the FullAuth request. */
+  client = iar->iar_client;
+  assert(iar->iar_client != NULL);
+  iauth_send(iauth, "FullAuth %x %s %s %s %s %s :%s",
+             client, cli_name(client), cli_username(client),
+             cli_user(client)->host, cli_sock_ip(client),
+             cli_passwd(client), cli_info(client));
+
+  /* Write to the socket if we can. */
+  iauth_write(iauth);
+}
+
+int iauth_start_client(struct IAuth *iauth, struct Client *cptr)
+{
+  struct IAuthRequest *iar;
+
+  /* Allocate and initialize IAuthRequest struct. */
+  if (!(iar = MyCalloc(1, sizeof(*iar))))
+    return exit_client(cptr, cptr, &me, "IAuth memory allocation failed");
+  iar->iar_next = &i_list_head(iauth);
+  iar->iar_prev = i_list_head(iauth).iar_prev;
+  iar->iar_client = cptr;
+  iar->iar_prev->iar_next = iar;
+  iar->iar_next->iar_prev = iar;
+
+  /* Send request. */
+  iauth_send_request(iauth, iar);
+
+  return 0;
+}
+
+void iauth_exit_client(struct Client *cptr)
+{
+  if (cli_iauth(cptr)) {
+    iauth_dispose_request(iauth_active, cli_iauth(cptr));
+    cli_iauth(cptr) = NULL;
+  } else if (IsIAuthed(cptr) && i_GetIClass(iauth_active)) {
+    /* TODO: report quit to iauth */
+  }
+}
+
+static struct IAuthRequest *iauth_find_request(struct IAuth *iauth, char *id)
+{
+  struct IAuthRequest *curr;
+  struct Client *target;
+  target = (struct Client*)strtoul(id, NULL, 16);
+  for (curr = i_list_head(iauth).iar_next;
+       curr != &i_list_head(iauth);
+       curr = curr->iar_next) {
+    assert(curr->iar_client != NULL);
+    if (target == curr->iar_client)
+      return curr;
+  }
+  return NULL;
+}
+
+static void iauth_dispose_request(struct IAuth *iauth, struct IAuthRequest *iar)
+{
+  assert(iar->iar_client != NULL);
+  if (iar->iar_timed)
+    timer_del(&i_request_timer(iauth));
+  cli_iauth(iar->iar_client) = NULL;
+  iar->iar_prev->iar_next = iar->iar_next;
+  iar->iar_next->iar_prev = iar->iar_prev;
+  MyFree(iar);
+}
+
+static void iauth_cmd_doneauth(struct IAuth *iauth, int argc, char *argv[])
+{
+  struct IAuthRequest *iar;
+  struct Client *client;
+  char *id;
+  char *username;
+  char *hostname;
+  char *c_class;
+  char *account;
+
+  if (argc < 5) {
+    iauth_protocol_violation(iauth, "Only %d parameters for DoneAuth (expected >=5)", argc);
+    return;
+  }
+  id = argv[1];
+  username = argv[2];
+  hostname = argv[3];
+  c_class = argv[4];
+  account = (argc > 5) ? argv[5] : 0;
+  iar = iauth_find_request(iauth, id);
+  if (!iar) {
+    iauth_protocol_violation(iauth, "Got unexpected DoneAuth for id %s", id);
+    return;
+  }
+  client = iar->iar_client;
+  ircd_strncpy(cli_username(client), username, USERLEN);
+  ircd_strncpy(cli_user(client)->host, hostname, HOSTLEN);
+  if (account) {
+    ircd_strncpy(cli_user(client)->account, account, ACCOUNTLEN);
+    SetAccount(client);
+  }
+  SetIAuthed(client);
+  iauth_dispose_request(iauth, iar);
+  register_user(client, client, cli_name(client), username);
+}
+
+static void iauth_cmd_badauth(struct IAuth *iauth, int argc, char *argv[])
+{
+  struct IAuthRequest *iar;
+  struct Client *client;
+  char *id;
+  char *reason;
+
+  if (argc < 3) {
+    iauth_protocol_violation(iauth, "Only %d parameters for BadAuth (expected >=3)", argc);
+    return;
+  }
+  id = argv[1];
+  reason = argv[2];
+  if (EmptyString(reason)) {
+    iauth_protocol_violation(iauth, "Empty BadAuth reason for id %s", id);
+    return;
+  }
+  iar = iauth_find_request(iauth, id);
+  if (!iar) {
+    iauth_protocol_violation(iauth, "Got unexpected BadAuth for id %s", id);
+    return;
+  }
+  client = iar->iar_client;
+  iauth_dispose_request(iauth, iar);
+  exit_client(client, client, &me, reason);
+}
+
+static const struct IAuthCmd iauth_cmdtab[] = {
+  { "DoneAuth", iauth_cmd_doneauth },
+  { "BadAuth", iauth_cmd_badauth },
+  { NULL, NULL }
+};
index 91f022fbb96ddb0df9a4d94f1237e32a8051758b..7abff8f26a0aa66d27a30fff5a85372c4a9eea32 100644 (file)
@@ -121,6 +121,8 @@ ip return IP;
 crule return CRULE;
 kill return KILL;
 quarantine return QUARANTINE;
+iauth return IAUTH;
+timeout return TIMEOUT;
 features return FEATURES;
 channel return CHANNEL;
 chan_limit return TPRIV_CHAN_LIMIT;
index cb22a29e50381f7704ff5dc96243b57647051185..7def3f02ee3e0543b25dbb4f4c0ff2e881b642ee 100644 (file)
@@ -32,6 +32,7 @@
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
+#include "ircd_auth.h"
 #include "ircd_chattr.h"
 #include "ircd_log.h"
 #include "ircd_reply.h"
@@ -157,6 +158,8 @@ static void parse_error(char *pattern,...) {
 %token PSEUDO
 %token PREPEND
 %token USERMODE
+%token IAUTH
+%token TIMEOUT
 /* and now a lot of priviledges... */
 %token TPRIV_CHAN_LIMIT TPRIV_MODE_LCHAN TPRIV_DEOP_LCHAN TPRIV_WALK_LCHAN
 %token TPRIV_LOCAL_KILL TPRIV_REHASH TPRIV_RESTART TPRIV_DIE
@@ -183,7 +186,7 @@ blocks: blocks block | block;
 block: adminblock | generalblock | classblock | connectblock |
        serverblock | operblock | portblock | jupeblock | clientblock |
        killblock | cruleblock | motdblock | featuresblock | quarantineblock |
-       pseudoblock | error;
+       pseudoblock | iauthblock | error;
 
 /* The timespec, sizespec and expr was ripped straight from
  * ircd-hybrid-7. */
@@ -997,3 +1000,46 @@ pseudonick: NICK '=' QSTRING ';'
     smap->services = nh;
   }
 };
+
+iauthblock: IAUTH '{'
+{
+  pass = host = NULL;
+  port = 0;
+  tconn = 60;
+  tping = 60;
+} iauthitems '}' ';'
+{
+  if (!name || !host || !port) {
+    log_write(LS_CONFIG, L_ERROR, 0, "IAuth block needs a server name and port.");
+    return 0;
+  }
+  iauth_connect(host, port, pass, tconn, tping);
+  MyFree(pass);
+  MyFree(host);
+  pass = host = NULL;
+}
+
+iauthitems: iauthitem iauthitems | iauthitem;
+iauthitem: iauthpass | iauthhost | iauthport | iauthconnfreq | iauthtimeout | error;
+iauthpass: PASS '=' QSTRING ';'
+{
+  MyFree(pass);
+  DupString(pass, yylval.text);
+};
+iauthhost: HOST '=' QSTRING ';'
+{
+  MyFree(host);
+  DupString(host, yylval.text);
+};
+iauthport: PORT '=' NUMBER ';'
+{
+  port = yylval.num;
+};
+iauthconnfreq: CONNECTFREQ '=' timespec ';'
+{
+  tconn = yylval.num;
+};
+iauthtimeout: TIMEOUT '=' timespec ';'
+{
+  tping = yylval.num;
+};
index 63e5bfe26876cf093e632a8c1a599da88555f3d1..f386b9806fa0fc77eeff1167d329d88ee31d7164 100644 (file)
@@ -32,6 +32,7 @@
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
+#include "ircd_auth.h"
 #include "ircd_chattr.h"
 #include "ircd_log.h"
 #include "ircd_reply.h"
@@ -1060,11 +1061,13 @@ int rehash(struct Client *cptr, int sig)
 
   class_mark_delete();
   mark_listeners_closing();
+  iauth_mark_closing();
 
   read_configuration_file();
 
   log_reopen(); /* reopen log files */
 
+  iauth_close_unused();
   close_listeners();
   class_delete_marked();         /* unless it fails */
 
index bf37eb95eb42a68aee0c542c3bf17593c75fa08e..aa9030bfdb5c5b22aaac751d57690a1e803cc2ed 100644 (file)
@@ -31,6 +31,7 @@
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
+#include "ircd_auth.h"
 #include "ircd_features.h"
 #include "ircd_log.h"
 #include "ircd_reply.h"
@@ -296,6 +297,8 @@ static void exit_one_client(struct Client* bcptr, const char* comment)
     assert(!IsServer(bcptr));
     /* bcptr->user->server->serv->client_list[IndexYXX(bcptr)] = NULL; */
     RemoveYXXClient(cli_user(bcptr)->server, cli_yxx(bcptr));
+    if (IsIAuthed(bcptr) || cli_iauth(bcptr))
+        iauth_exit_client(bcptr);
   }
 
   /* Remove bcptr from the client list */
index 4082ea39c49ac11b31e6627f2b310b61c3cf1464..2aefc68517d0aa7fd112a98ffe30df147434e5b6 100644 (file)
@@ -32,6 +32,7 @@
 #include "hash.h"
 #include "ircd.h"
 #include "ircd_alloc.h"
+#include "ircd_auth.h"
 #include "ircd_chattr.h"
 #include "ircd_features.h"
 #include "ircd_log.h"
@@ -375,6 +376,12 @@ int register_user(struct Client *cptr, struct Client *sptr,
     static time_t last_too_many2;
 
     assert(cptr == sptr);
+    if (!IsIAuthed(sptr)) {
+      if (iauth_active)
+        return iauth_start_client(iauth_active, sptr);
+      else
+        SetIAuthed(sptr);
+    }
     switch (conf_check_client(sptr))
     {
       case ACR_OK: