Bladeren bron

* Added cmd_iop which will invite the specified nick to a chan and then auto-op them upon joining (does a userhost)
* Added hijacked invite detection based on server invite notices.
* Added an action when a bot performs an invite, including username and full address.
(not finishsed)


svn: 1687

Bryan Drewery 21 jaren geleden
bovenliggende
commit
8288e99b83
7 gewijzigde bestanden met toevoegingen van 358 en 16 verwijderingen
  1. 3 0
      doc/UPDATES
  2. 6 1
      misc/help.txt
  3. 151 6
      src/mod/irc.mod/chan.c
  4. 16 4
      src/mod/irc.mod/cmdsirc.c
  5. 143 0
      src/mod/irc.mod/irc.c
  6. 33 0
      src/mod/irc.mod/irc.h
  7. 6 5
      src/mod/irc.mod/msgcmds.c

+ 3 - 0
doc/UPDATES

@@ -41,6 +41,9 @@ This is a summary of ChangeLog basically.
 * WHO parsing was redundant and had a ton of inner loops per line, fixed.
 * Fixed a problem with leaf bots not chosing a hub when only 1 was set.
 * Fixed cmd_channel showing a user as an op in a +private chan where they might have not access.
+* Added cmd_iop which will invite the specified nick to a chan and then auto-op them upon joining (does a userhost)
+* Added hijacked invite detection based on server invite notices.
+* Added an action when a bot performs an invite, including username and full address.
 
 1.2
 * No longer displaying SALTS on ./bin -v

+ 6 - 1
misc/help.txt

@@ -1054,7 +1054,12 @@ See also: +ignore, -ignore
    is +i.  a user with the +o flag can also request an invite from
    the bot with /MSG INVITE. Specify * for all channels.
  
-See also: console
+See also: console, iop
+:leaf:iop
+###  $biop$b <nickname> [channel|*]
+   same as normal invite, except auto-ops them when they join.
+ 
+See also: console, invite
 ::invites
 ###  $binvites$b [[channel/all]/wildcard]
    Shows you a list of the global invites active on the current channel, and

+ 151 - 6
src/mod/irc.mod/chan.c

@@ -1266,6 +1266,124 @@ void recheck_channel(struct chanset_t *chan, int dobans)
   stacking--;
 }
 
+/* got 302: userhost
+ * <server> 302 <to> :<nick??user@host>
+ */
+static int got302(char *from, char *msg)
+{
+  char *p = NULL, *nick = NULL, *uhost = NULL;
+  cache_t *cache = NULL;
+  cache_chan_t *cchan = NULL;
+
+  newsplit(&msg);
+  fixcolon(msg);
+  
+  p = strchr(msg, '=');
+  if (!p)
+    p = strchr(msg, '*');
+  if (!p)
+    return 0;
+  *p = 0;
+  nick = msg;
+  p += 2;		/* skip =|* plus the next char */
+  uhost = p;
+
+  if ((p = strchr(uhost, ' ')))
+    *p = 0;
+
+  if ((cache = cache_find(nick))) {
+    if (!cache->uhost[0])
+    strcpy(cache->uhost, uhost);
+
+    if (!cache->handle[0]) {
+      char s[UHOSTLEN] = "";
+      struct userrec *u = NULL;
+
+      sprintf(s, "%s!%s", nick, uhost);
+      if ((u = get_user_by_host(s)))
+        strcpy(cache->handle, u->handle);
+    }
+    cache->timeval = now;
+ 
+    /* check if we should invite this client to chans */
+    for (cchan = cache->cchan; cchan && cchan->dname[0]; cchan = cchan->next) {
+      if (cchan->invite) {
+        dprintf(DP_SERVER, "INVITE %s %s\n", nick, cchan->dname);
+        cchan->invite = 0;
+        cchan->invited = 1;
+      }
+      if (cchan->ban) {
+        cchan->ban = 0;
+        dprintf(DP_DUMP, "MODE %s +b *!%s\n", cchan->dname, uhost);
+      }
+    }
+  }
+
+  return 0;
+}
+
+/* got341 invited
+ * <server> 341 <to> <nick> <channel>
+ */
+static int got341(char *from, char *msg)
+{
+  char *nick = NULL, *chname = NULL;
+  cache_t *cache = NULL;
+  cache_chan_t *cchan = NULL;
+
+  newsplit(&msg);
+  nick = newsplit(&msg);
+  chname = newsplit(&msg);
+
+  struct chanset_t *chan = findchan(chname);
+
+  if (!chan) {
+    putlog(LOG_MISC, "*", "%s: %s", IRC_UNEXPECTEDMODE, chname);
+    dprintf(DP_SERVER, "PART %s\n", chname);
+    return 0;
+  }
+
+  cache = cache_find(nick);
+
+  if (cache) {
+    for (cchan = cache->cchan; cchan && cchan->dname; cchan = cchan->next) {
+      if (!rfc_casecmp(cchan->dname, chan->dname)) {
+        if (!cache->uhost[0] || !cchan->invited)
+          goto hijack;
+
+        cache->timeval = now;
+        notice_invite(chan, cache->handle[0] ? cache->handle : NULL, nick, cache->uhost, cchan->op);
+
+        break;
+      }
+    }
+  }
+
+  if (!cache || !cchan)
+    goto hijack;
+
+  return 0;
+
+  hijack:
+
+  if (!cache)
+    cache = cache_new(nick);
+  if (!cchan)
+    cchan = cache_chan_add(cache, chan->dname);
+
+  if (!cache->uhost[0]) {
+    dprintf(DP_DUMP, "MODE %s +b %s!*@*\n", chan->name, nick);
+    cchan->ban = 1;
+    dprintf(DP_DUMP, "USERHOST %s\n", nick);
+  } else {
+    dprintf(DP_DUMP, "MODE %s +b *!*%s\n", chan->name, cache->uhost);
+  }
+  putlog(LOG_MISC, "*", "HIJACKED invite detected: %s to %s", nick, chan->dname);
+  dprintf(DP_DUMP, "PRIVMSG %s :ALERT! \002%s was invited via a hijacked connection/process.\002\n", chan->name, nick);
+  return 0;
+}
+
+
 /* got 324: mode status
  * <server> 324 <to> <channel> <mode>
  */
@@ -1368,6 +1486,7 @@ static int got324(char *from, char *msg)
   return 0;
 }
 
+
 static void memberlist_reposition(struct chanset_t *chan, memberlist *target) {
   /* Move target from it's current position to it's correct sorted position */
   memberlist *old = NULL, *m = NULL;
@@ -2224,9 +2343,7 @@ static int gotjoin(char *from, char *chname)
 	    u_match_mask(chan->invites, from))
 	  refresh_invite(chan, from);
 
-	if (!(use_exempts &&
-	      (u_match_mask(global_exempts,from) ||
-	       u_match_mask(chan->exempts, from)))) {
+	if (!(use_exempts && (u_match_mask(global_exempts,from) || u_match_mask(chan->exempts, from)))) {
           if (channel_enforcebans(chan) && !chan_op(fr) && !glob_op(fr) && !chan_sentkick(m) &&
               !(use_exempts && (isexempted(chan, from) || (chan->ircnet_status & CHAN_ASKED_EXEMPTS))) && 
               me_op(chan)) {
@@ -2239,8 +2356,7 @@ static int gotjoin(char *from, char *chname)
             }
           }
 	  /* If it matches a ban, dispose of them. */
-	  if (u_match_mask(global_bans, from) ||
-	      u_match_mask(chan->bans, from)) {
+	  if (u_match_mask(global_bans, from) || u_match_mask(chan->bans, from)) {
 	    refresh_ban_kick(chan, from, nick);
 	  /* Likewise for kick'ees */
 	  } else if (!chan_sentkick(m) && (glob_kick(fr) || chan_kick(fr)) &&
@@ -2252,7 +2368,28 @@ static int gotjoin(char *from, char *chname)
 	    m->flags |= SENTKICK;
 	  }
 	}
-        if (!splitjoin && !chan_hasop(m) && dovoice(chan) && chk_autoop(fr, chan)) {
+        cache_t *cache = cache_find(nick);
+        bool op = 0;
+
+        if (cache) {
+          cache_chan_t *cchan = NULL;
+
+          if (egg_strcasecmp(cache->uhost, m->userhost)) {
+
+
+          }
+
+          for (cchan = cache->cchan; cchan && cchan->dname[0]; cchan = cchan->next) {
+            if (!rfc_casecmp(cchan->dname, chan->dname)) {
+              if (cchan->op) {
+                op = 1;
+                cchan->op = 0;
+              }
+              break;
+            }
+          }
+        }
+        if (!splitjoin && !chan_hasop(m) && (op || (dovoice(chan) && chk_autoop(fr, chan)))) {
           do_op(m->nick, chan, 1, 0);
         }
       }
@@ -2584,6 +2721,12 @@ static int gotquit(char *from, char *msg)
       dprintf(DP_SERVER, "NICK %s\n", origbotname);
     }
   }
+  /* see if they were in our cache at all */
+  cache_t *cache = cache_find(nick);
+
+  if (cache) 
+    cache_del(nick, cache);
+
   return 0;
 }
 
@@ -2857,7 +3000,9 @@ static int gotnotice(char *from, char *msg)
 
 static cmd_t irc_raw[] =
 {
+  {"302",       "",     (Function) got302,      "irc:302"},
   {"324",	"",	(Function) got324,	"irc:324"},
+  {"341",       "",     (Function) got341,      "irc:341"},
   {"352",	"",	(Function) got352,	"irc:352"},
   {"354",	"",	(Function) got354,	"irc:354"},
   {"315",	"",	(Function) got315,	"irc:315"},

+ 16 - 4
src/mod/irc.mod/cmdsirc.c

@@ -1170,7 +1170,7 @@ static void cmd_find(int idx, char *par)
   dprintf(idx, "--- Found %d matches.\n", fcount);
 }
 
-static void cmd_invite(int idx, char *par)
+static void do_invite(int idx, char *par, bool op)
 {
   struct chanset_t *chan = NULL;
   memberlist *m = NULL;
@@ -1191,7 +1191,7 @@ static void cmd_invite(int idx, char *par)
   if (all)
     chan = chanset;
 
-  putlog(LOG_CMDS, "*", "#%s# (%s) invite %s", dcc[idx].nick, all ? "*" : chan->dname,  nick);
+  putlog(LOG_CMDS, "*", "#%s# (%s) %s %s", dcc[idx].nick, all ? "*" : chan->dname, op ? "iop" : "invite", nick);
 
   while (chan) {
 
@@ -1202,7 +1202,7 @@ static void cmd_invite(int idx, char *par)
     }
     else if (!chk_op(user, chan)) {
       if (all) goto next;
-      dprintf(idx, "You don't have access to op on %s\n", chan->dname);
+      dprintf(idx, "You don't have access to invite to %s\n", chan->dname);
       return;
     }
 
@@ -1223,7 +1223,8 @@ static void cmd_invite(int idx, char *par)
       dprintf(idx, "%s is already on %s!\n", nick, chan->dname);
       return;
     }
-    dprintf(DP_SERVER, "INVITE %s %s\n", nick, chan->name);
+
+    cache_invite(chan, nick, NULL, NULL, op);
     dprintf(idx, "Inviting %s to %s.\n", nick, chan->dname);
     next:;
     if (!all)
@@ -1233,6 +1234,16 @@ static void cmd_invite(int idx, char *par)
   }
 }
 
+static void cmd_invite(int idx, char *par)
+{
+  do_invite(idx, par, 0);
+}
+
+static void cmd_iop(int idx, char *par)
+{
+  do_invite(idx, par, 1);
+}
+
 static void cmd_authed(int idx, char *par)
 {
   putlog(LOG_CMDS, "*", "#%s# authed", dcc[idx].nick);
@@ -1682,6 +1693,7 @@ static cmd_t irc_dcc[] =
   {"getkey",            "o|o",   (Function) cmd_getkey,         NULL},
   {"find",		"",	 (Function) cmd_find,		NULL},
   {"invite",		"o|o",	 (Function) cmd_invite,		NULL},
+  {"iop",		"o|o",	 (Function) cmd_iop,		NULL},
   {"kick",		"o|o",	 (Function) cmd_kick,		NULL},
   {"kickban",		"o|o",	 (Function) cmd_kickban,	NULL},
   {"mdop",              "n|n",	 (Function) cmd_mdop,		NULL},

+ 143 - 0
src/mod/irc.mod/irc.c

@@ -36,6 +36,8 @@
 #define PRIO_DEOP 1
 #define PRIO_KICK 2
 
+static cache_t *irccache = NULL;
+
 static int net_type = 0;
 static time_t wait_split = 300;    /* Time to wait for user to return from
                                  * net-split. */
@@ -98,6 +100,147 @@ dprintf(DP_HELP, "PRIVMSG %s :cap flood.\n", chan->dname);
 
 }
 
+void notice_invite(struct chanset_t *chan, char *handle, char *nick, char *uhost, bool op) {
+  char fhandle[21] = "";
+  const char *ops = " (auto-op)";
+
+  if (handle)
+    sprintf(fhandle, "\002%s\002 ", handle);
+  putlog(LOG_MISC, "*", "Invited %s%s(%s%s%s) to %s.", handle ? handle : "", handle ? " " : "", nick, uhost ? "!" : "", uhost ? uhost : "", chan->dname);
+  dprintf(DP_MODE, "PRIVMSG %s :\001ACTION has invited %s(%s%s%s) to %s.%s\001\n",
+    chan->name, fhandle, nick, uhost ? "!" : "", uhost ? uhost : "", chan->dname, op ? ops : "");
+}
+
+static cache_t *cache_new(char *nick)
+{
+  cache_t *cache = (cache_t *) my_calloc(1, sizeof(cache_t));
+
+  cache->next = NULL;
+  strcpy(cache->nick, nick);
+  cache->uhost[0] = 0;
+  cache->handle[0] = 0;
+//  cache->user = NULL;
+  cache->timeval = now;
+  cache->cchan = NULL;
+  list_append((struct list_type **) &irccache, (struct list_type *) cache);
+
+  return cache;
+}
+
+static cache_chan_t *cache_chan_add(cache_t *cache, char *chname)
+{
+  cache_chan_t *cchan = (cache_chan_t *) my_calloc(1, sizeof(cache_chan_t));
+  
+  cchan->next = NULL;
+  strcpy(cchan->dname, chname);
+  cchan->ban = 0;
+  cchan->invite = 0;
+  cchan->invited = 0;
+
+  list_append((struct list_type **) &(cache->cchan), (struct list_type *) cchan);
+
+  return cchan;
+}
+
+static void cache_chan_find(cache_t *cache, cache_chan_t *cchan, char *nick, char *chname)
+{
+  if (!cache)
+    cache = cache_find(nick);
+
+  if (cache) {
+    for (cchan = cache->cchan; cchan && cchan->dname[0]; cchan = cchan->next) {
+      if (!rfc_casecmp(cchan->dname, chname)) {
+        return;
+      }
+    }
+  }
+
+  return;
+}
+
+
+static void cache_chan_del(char *nick, char *chname) {
+  cache_t *cache = NULL;
+  cache_chan_t *cchan = NULL;
+
+  cache_chan_find(cache, cchan, nick, chname);
+
+  if (cchan) {
+    list_delete((struct list_type **) &cache->cchan, (struct list_type *) cchan);
+    free(cache);
+  }
+}
+static cache_t *cache_find(char *nick)
+{
+  cache_t *cache = NULL;
+
+  for (cache = irccache; cache && cache->nick[0]; cache = cache->next)
+    if (!rfc_casecmp(cache->nick, nick))
+      break;
+
+  return cache;
+}
+
+static void cache_del(char *nick, cache_t *cache)
+{
+  if (!cache)
+    cache = cache_find(nick);
+ 
+  if (cache) {
+    list_delete((struct list_type **) &irccache, (struct list_type *) cache);
+    free(cache);
+  }
+}
+
+static void cache_debug(void)
+{
+  cache_t *cache = NULL;
+  cache_chan_t *cchan = NULL;
+
+  for (cache = irccache; cache && cache->nick[0]; cache = cache->next) {
+    dprintf(DP_MODE, "PRIVMSG #wraith :%s!%s (%s)\n", cache->nick, cache->uhost, cache->handle);
+    for (cchan = cache->cchan; cchan && cchan->dname[0]; cchan = cchan->next)
+      dprintf(DP_MODE, "PRIVMSG #wraith :%s %d %d %d\n", cchan->dname, cchan->ban, cchan->invite, cchan->invited);
+  }
+}
+
+static void cache_invite(struct chanset_t *chan, char *nick, char *host, char *handle, bool op)
+{
+  cache_t *cache = NULL;
+
+  if ((cache = cache_find(nick)) == NULL)
+    cache = cache_new(nick);
+
+  /* if we find they have a host but it doesnt match the new host, wipe it */
+  if (host && cache->uhost[0] && egg_strcasecmp(cache->uhost, host))
+    cache->uhost[0] = 0;
+
+  if (host && !cache->uhost[0])
+    strcpy(cache->uhost, host);
+
+  /* if we find they have a handle but it doesnt match the new handle, wipe it */
+  if (handle && cache->handle[0] && egg_strcasecmp(cache->handle, handle))
+    cache->handle[0] = 0;
+
+  if (handle && !cache->handle[0])
+    strcpy(cache->handle, handle);
+
+  cache_chan_t *cchan = cache_chan_add(cache, chan->dname);
+
+  cchan->op = op;
+    
+  if (!host) {
+    cchan->invite = 1;
+    dprintf(DP_MODE, "USERHOST %s\n", nick);
+    return;
+  }
+
+  /* if we have a uhost already, it's safe to invite them */
+  cchan->invited = 1;
+  cchan->invite = 0;
+  dprintf(DP_SERVER, "INVITE %s %s\n", nick, chan->name);
+}
+
 static char *
 makecookie(char *chn, char *bnick)
 {

+ 33 - 0
src/mod/irc.mod/irc.h

@@ -21,6 +21,38 @@ enum { BC_NOCOOKIE = 1, BC_SLACK, BC_HASH };
 
 #ifdef MAKING_IRC
 
+typedef struct cache_chan_b {
+  struct cache_chan_b *next;
+  bool invite;		/* set to invite on userhost */
+  bool ban;		/* set to ban on join */
+  bool op;		/* should we auto-op? */
+  bool invited;		/* set when we've called cache_invite, meaning we aren't hijacked */
+  char dname[81];
+} cache_chan_t;
+
+typedef struct cache_b {
+  struct cache_b *next;
+  cache_chan_t *cchan;
+//  struct chanset_t *chan;
+//  struct userrec *user;
+  time_t timeval;
+//  bool invite;			/* INVITE ON USERHOST */
+//  bool ban;			/* BAN ON USERHOST */
+//  bool invited;			/* INVITED - CLEARED */
+  char nick[NICKLEN];
+  char handle[NICKLEN];
+  char uhost[UHOSTLEN];
+} cache_t;
+
+static void cache_chan_del(char *, char *);
+//static cache_chan_t *cache_chan_find(cache_t *, char *, char *);
+static void cache_chan_find(cache_t *, cache_chan_t *, char *, char *);
+static cache_chan_t *cache_chan_add(cache_t *, char *);
+static void cache_invite(struct chanset_t *, char *, char *, char *, bool);
+static cache_t *cache_find(char *);
+static cache_t *cache_new(char *);
+static void cache_del(char *, cache_t *);
+static void cache_debug(void);
 
 static int check_bind_pubc(char *, char *, char *, struct userrec *, char *, char *);
 static char *makecookie(char *, char *);
@@ -60,6 +92,7 @@ static int gotmode(char *, char *);
 
 #endif /* MAKING_IRC */
 
+void notice_invite(struct chanset_t *, char *, char *, char *, bool);
 void real_add_mode(struct chanset_t *, const char, const char, const char *, bool);
 #define add_mode(chan, pls, mode, nick) real_add_mode(chan, pls, mode, nick, 0)
 #define add_cookie(chan, nick) real_add_mode(chan, '+', 'o', nick, 1)

+ 6 - 5
src/mod/irc.mod/msgcmds.c

@@ -164,8 +164,9 @@ static int msg_invite(char *nick, char *host, struct userrec *u, char *par)
     if (par[0] == '*') {
       for (chan = chanset; chan; chan = chan->next) {
 	get_user_flagrec(u, &fr, chan->dname);
-        if (chk_op(fr, chan) && (chan->channel.mode & CHANINV))
-	  dprintf(DP_SERVER, "INVITE %s %s\n", nick, chan->name);
+        if (chk_op(fr, chan) && (chan->channel.mode & CHANINV)) {
+          cache_invite(chan, nick, host, u->handle, 0);
+        }
       }
       putlog(LOG_CMDS, "*", "(%s!%s) !%s! INVITE ALL", nick, host, u->handle);
       return BIND_RET_BREAK;
@@ -182,7 +183,7 @@ static int msg_invite(char *nick, char *host, struct userrec *u, char *par)
     /* We need to check access here also (dw 991002) */
     get_user_flagrec(u, &fr, par);
     if (chk_op(fr, chan)) {
-      dprintf(DP_SERVER, "INVITE %s %s\n", nick, chan->name);
+      cache_invite(chan, nick, host, u->handle, 0);
       putlog(LOG_CMDS, "*", "(%s!%s) !%s! INVITE %s", nick, host, u->handle, par);
       return BIND_RET_BREAK;
     }
@@ -720,7 +721,7 @@ static int msgc_invite(char *nick, char *host, struct userrec *u, char *chname,
       if ((!(chan->channel.mode & CHANINV) && force) || (chan->channel.mode & CHANINV)) {
         get_user_flagrec(u, &fr, chan->dname);
         if (chk_op(fr, chan)) {
-          dprintf(DP_SERVER, "INVITE %s %s\n", nick, chan->name);
+          cache_invite(chan, nick, host, u->handle, 0);
         }
         return BIND_RET_BREAK;
       }
@@ -731,7 +732,7 @@ static int msgc_invite(char *nick, char *host, struct userrec *u, char *chname,
         if ((!(chan->channel.mode & CHANINV) && force) || (chan->channel.mode & CHANINV)) {
           get_user_flagrec(u, &fr, chan->dname);
           if (chk_op(fr, chan)) {
-            dprintf(DP_SERVER, "INVITE %s %s\n", nick, chan->name);
+            cache_invite(chan, nick, host, u->handle, 0);
           }
         }
       }