Forráskód Böngészése

Merge branch '402-cookies'

* 402-cookies: (24 commits)
  * Use bdlib base64
  * Add comment to UPDATES noting that bad cookies from lag shouldn't occur anymore
  * Clear bot counter using handle, not userrec, in case it couldn't be looked up temporarily
  * Use bd::HashTable for bot counters
  * Consolidate the key generation into 1 function
  * Stringize the hash format
  * Convert salt lookups to new format
  * Fix segfalt in flush_cookies
  * Disable dual-queue for cookies
  * Don't check counter for my own ops as it may have incremented in the queue before seeing the last last counter
  * Clear a bot's count when it links in
  * Only validate the opper on the first cookie
  * Ensure that the 'me' memberlist for 'opper' to makecookie has a resolved user
  * Cookies no longer fail if the opping bot changes nick after queueing the mode.
  * Fix algorithm so bots can check their own counters when seen
  * Fix rand not properly being added to key when checking cookie
  * Dont need to hash the partial chan anymore as the entire channel is in the key now
  * Remove some old comments
  * Fix key being truncated
  * Move the opper's uhost into the key
  ...
Bryan Drewery 16 éve
szülő
commit
ab8c760a5a
9 módosított fájl, 140 hozzáadás és 42 törlés
  1. 1 0
      doc/CREDITS
  2. 5 1
      doc/UPDATES
  3. 1 1
      src/base64.c
  4. 1 1
      src/base64.h
  5. 4 0
      src/botnet.c
  6. 1 0
      src/cmds.c
  7. 111 36
      src/mod/irc.mod/irc.c
  8. 2 1
      src/mod/irc.mod/irc.h
  9. 14 2
      src/mod/irc.mod/mode.c

+ 1 - 0
doc/CREDITS

@@ -12,6 +12,7 @@ Credits and thanks to the following:
 * pgpkeys for finding bugs, and providing input.
 * Excelsior for celdrop which inspired many features.
 * syt for giving me inspiration to code a more secure bot.
+* mulder for helping with the cookie op algorithm
 * Blackjac for helping with the bx auth script with his Sentinel script.
 * Mystikal for various bugs.
 * Many others.

+ 5 - 1
doc/UPDATES

@@ -7,6 +7,11 @@
   * Optimized how some shell operations are executed.
   * Uname checking (and associated emails) have been removed.
   * Fix crash on OpenBSD due to mmap(2) bug.
+* Cookie op changes
+  * Cookie op algorithm changed again, they can no longer be easily reused (fixes #402) Thank you mulder.
+  * Cookies no longer fail if the opping bot changes nick after queueing the mode.
+  * Cookie time window increased, false-positives from lag/netsplits should be completely eliminated now.
+  * Auto disable cookies on ircu(undernet) and Unreal servers as they do not support unbanning nonexistant bans.
 * DNS changes
   * More DNS errors are now handled which fixes some misc DNS issues.
   * DNS now works on servers which tend to only return partial results (fixes #424)
@@ -53,7 +58,6 @@
   * Fix segfault with /KNOCK on csircd.
   * Fix restart causing bot to change nick (this was a regression in jupenick)
   * Fix cmd_slowjoin not working on backup bots correctly.
-  * Auto disable cookies on ircu(undernet) and Unreal servers as they do not allow unbanning nonexistant bans.
   * Fix a crash from long hubnicks.
 * Misc changes
   * EFnet server list updates (To update yours: .set -YES servers - | .set -YES servers6 -)

+ 1 - 1
src/base64.c

@@ -73,7 +73,7 @@ static char base64to[256] =
 };
 
 
-int base64_to_int(char *buf)
+int base64_to_int(const char *buf)
 {
   int i = 0;
 

+ 1 - 1
src/base64.h

@@ -7,7 +7,7 @@ namespace bd {
 };
 
 char *int_to_base64(unsigned int);
-int base64_to_int(char *);
+int base64_to_int(const char *);
 
 bd::String broken_base64Encode(const bd::String&);
 char *b64enc(const unsigned char *data, size_t len);

+ 4 - 0
src/botnet.c

@@ -72,6 +72,8 @@ tand_t *findbot(const char *who)
   return NULL;
 }
 
+extern void counter_clear(const char* botnick);
+
 /* Add a tandem bot to our chain list
  */
 void addbot(char *who, char *from, char *next, char flag, int vlocalhub, time_t vbuildts, char *vcommit, char *vversion)
@@ -105,6 +107,8 @@ void addbot(char *who, char *from, char *next, char flag, int vlocalhub, time_t
   else
     ptr2->uplink = findbot(next);
   tands++;
+
+  counter_clear(who);
 }
 
 #ifdef G_BACKUP

+ 1 - 0
src/cmds.c

@@ -408,6 +408,7 @@ static void cmd_about(int idx, char *par)
   dprintf(idx, STR(" * $bpgpkeys$b for finding bugs, and providing input.\n"));
   dprintf(idx, STR(" * $bExcelsior$b for celdrop which inspired many features.\n"));
   dprintf(idx, STR(" * $bsyt$b for giving me inspiration to code a more secure bot.\n"));
+  dprintf(idx, STR(" * $bmulder$b for helping with the cookie op algorithm.\n"));
   dprintf(idx, STR(" * $bBlackjac$b for helping with the bx auth script with his Sentinel script.\n"));
   dprintf(idx, STR(" * $bMystikal$b for various bugs.\n"));
   dprintf(idx, STR(" * $bMany$b others.\n"));

+ 111 - 36
src/mod/irc.mod/irc.c

@@ -31,6 +31,7 @@
 #include "src/adns.h"
 #include "src/match.h"
 #include "src/settings.h"
+#include "src/base64.h"
 #include "src/tandem.h"
 #include "src/net.h"
 #include "src/botnet.h"
@@ -53,6 +54,9 @@
 #include "src/mod/server.mod/server.h"
 #include "src/mod/channels.mod/channels.h"
 #include "src/mod/ctcp.mod/ctcp.h"
+#include <bdlib/src/String.h>
+#include <bdlib/src/HashTable.h>
+#include <bdlib/src/base64.h>
 
 #include <stdarg.h>
 
@@ -85,6 +89,8 @@ static bool ban_fun = 1;
 static bool prevent_mixing = 1;  /* To prevent mixing old/new modes */
 static bool include_lk = 1;      /* For correct calculation
                                  * in real_add_mode. */
+bd::HashTable<bd::String, unsigned long> bot_counters;
+static unsigned long my_counter = 0;
 
 static int
 voice_ok(memberlist *m, struct chanset_t *chan)
@@ -327,25 +333,20 @@ static void cache_invite(struct chanset_t *chan, char *nick, char *host, char *h
   dprintf(DP_SERVER, "INVITE %s %s\n", nick, chan->name);
 }
 
-const char * cookie_hash(const char* chname, const memberlist* opper, const memberlist* opped, const char* ts, const char* salt) {
-  char tohash[101] = "";
+static inline const char * cookie_hash(const char* chname, const memberlist* opper, const memberlist* opped, const char* ts, const char* randstring, const char* key) {
+  char tohash[201] = "";
   const char salt2[] = SALT2;
 
-  /* Only use first 3 chars of chan */
-  simple_snprintf(tohash, sizeof(tohash), STR("%c%c%c%c%s%c%c%c%c%c%s%s%s%s"),
+  simple_snprintf(tohash, sizeof(tohash), STR("%c%s%c%c%c\n%c%c%s%s%s"),
                                      salt2[0],
-                                     toupper(chname[0]),
-                                     toupper(chname[1]),
-                                     toupper(chname[2]),  
-                                     &ts[4],
-                                     salt[0], salt[1], salt[2], salt[3],
+                                     ts,
+                                     randstring[0], randstring[1], randstring[2], randstring[3],
                                      salt2[15],
-                                     opper->nick,
                                      opped->nick,
                                      opped->userhost,
-                                     opper->userhost);
+                                     key);
 #ifdef DEBUG
-sdprintf("chname: %s ts: %s salt: %c%c%c%c", chname, ts, salt[0], salt[1], salt[2], salt[3]);
+sdprintf("chname: %s ts: %s randstring: %c%c%c%c", chname, ts, randstring[0], randstring[1], randstring[2], randstring[3]);
 sdprintf("tohash: %s", tohash);
 #endif
   const char* md5 = MD5(tohash);
@@ -353,6 +354,32 @@ sdprintf("tohash: %s", tohash);
   return md5;
 }
 
+static inline void cookie_key(char *key, size_t key_len, const char* randstring, const memberlist *opper, const char *chname) {
+  const char salt1[] = SALT1;
+  const char salt2[] = SALT2;
+  simple_snprintf2(key, key_len, STR("%c%c%c%s%c%c%c%c%c%c%^s%c%c%c%c%c%c%c%s"),
+                                        randstring[0],
+                                        salt1[5],
+                                        randstring[3],
+                                        opper->user->handle,
+                                        randstring[2],
+                                        salt1[4],
+                                        salt1[0],
+                                        salt1[1],
+                                        salt1[3],
+                                        salt1[6],
+                                        chname,
+                                        salt1[10],
+                                        randstring[1],
+                                        salt2[15],
+                                        salt2[13],
+                                        salt1[10],
+                                        salt2[3],
+                                        salt2[1],
+                                        opper->userhost
+                                        );
+}
+
 #define HASH_INDEX1(_x) (8 + (_x))
 #define HASH_INDEX2(_x) (16 + (_x))
 #define HASH_INDEX3(_x) (18 + (_x))
@@ -363,22 +390,29 @@ void makecookie(char *out, size_t len, const char *chname, const memberlist* opp
   make_rand_str(randstring, 4);
   /* &ts[4] is now last 6 digits of time */
   simple_snprintf(ts, sizeof(ts), "%010li", (long) (now + timesync));
-  
-  const char* hash1 = cookie_hash(chname, opper, m1, ts, randstring);
-  const char* hash2 = m2 ? cookie_hash(chname, opper, m2, ts, randstring) : NULL;
-  const char* hash3 = m3 ? cookie_hash(chname, opper, m3, ts, randstring) : NULL;
 
+  char cookie_clear[101] = "";
+
+  //Increase my counter
+  ++my_counter;
+  simple_snprintf2(cookie_clear, sizeof(cookie_clear), STR("%s%s%D"), randstring, &ts[3], my_counter);
+
+  char key[150] = "";
+  cookie_key(key, sizeof(key), randstring, opper, chname);
+
+  const char* hash1 = cookie_hash(chname, opper, m1, &ts[4], randstring, key);
+  const char* hash2 = m2 ? cookie_hash(chname, opper, m2, &ts[4], randstring, key) : NULL;
+  const char* hash3 = m3 ? cookie_hash(chname, opper, m3, &ts[4], randstring, key) : NULL;
+  bd::String cookie = encrypt_string(MD5(key), bd::String(cookie_clear));
+  cookie = bd::base64Encode(cookie);
 #ifdef DEBUG
+sdprintf("key: %s", key);
+sdprintf("cookie_clear: %s", cookie_clear);
 sdprintf("hash1: %s", hash1);
-if (hash2)
-sdprintf("hash2: %s", hash2);
-if (hash3)
-sdprintf("hash3: %s", hash3);
+if (hash2) sdprintf("hash2: %s", hash2);
+if (hash3) sdprintf("hash3: %s", hash3);
 #endif
 
-//  register size_t len = m3 ? 25 : (m2 ? 22 : 19);
-//  register char* buf = (char*) my_calloc(1, len + 1);
-
   if (m3)
     simple_snprintf(out, len + 1, STR("%c%c%c%c%c%c%c%c%c!%s@%s"), 
                          hash1[HASH_INDEX1(0)], 
@@ -391,7 +425,7 @@ sdprintf("hash3: %s", hash3);
                          hash3[HASH_INDEX2(2)], 
                          hash3[HASH_INDEX3(2)], 
                          randstring, 
-                         ts);
+                         cookie.c_str());
   else if (m2)
     simple_snprintf(out, len + 1, STR("%c%c%c%c%c%c!%s@%s"), 
                          hash1[HASH_INDEX1(0)], 
@@ -401,38 +435,77 @@ sdprintf("hash3: %s", hash3);
                          hash2[HASH_INDEX2(1)], 
                          hash2[HASH_INDEX3(1)], 
                          randstring, 
-                         ts);
+                         cookie.c_str());
   else
     simple_snprintf(out, len + 1, STR("%c%c%c!%s@%s"), 
                          hash1[HASH_INDEX1(0)], 
                          hash1[HASH_INDEX2(0)], 
                          hash1[HASH_INDEX3(0)], 
                          randstring, 
-                         ts);
+                         cookie.c_str());
 #ifdef DEBUG
 sdprintf("cookie: %s", out);
 #endif
-//  return buf;
 }
 
-/* 111222333!salt@timestamp. */
-static int checkcookie(const char *chname, const memberlist* opper, const memberlist* opped, const char *cookie, int indexHint) {
-#define TS(_x) (6 + (_x) + ((hashes << 1) + hashes)) /* x + (hashes * 3) */
+// Clear counter for bot
+void counter_clear(const char* botnick) {
+  bot_counters[botnick] = 0;
+}
+
+static inline int checkcookie(const char *chname, const memberlist* opper, const memberlist* opped, const char *cookie, int indexHint) {
+#define HOST(_x) (6 + (_x) + ((hashes << 1) + hashes)) /* x + (hashes * 3) */
 #define SALT(_x) (1 + (_x) + ((hashes << 1) + hashes)) /* x + (hashes * 3) */
   /* How many hashes are in the cookie? */
   const size_t hashes = cookie[3] == '!' ? 1 : (cookie[6] == '!' ? 2 : 3);
 
+  char key[150] = "";
+  const char *randstring = &cookie[SALT(0)];
+
+  cookie_key(key, sizeof(key), randstring, opper, chname);
+
+  bd::String ciphertext = bd::base64Decode((char*) &cookie[HOST(0)]);
+  bd::String cleartext = decrypt_string(MD5(key), ciphertext);
+  char ts[8] = "";
+  strlcpy(ts, cleartext.c_str() + 4, sizeof(ts));
+  unsigned long counter = base64_to_int(cleartext.c_str() + 4 + 7);
+
+  //Lookup counter for the opper
+  unsigned long last_counter = 0;
+  // Don't check counter for my own ops as it may have incremented in the queue before seeing the last last counter
+  bd::String handle;
+  if (conf.bot->u != opper->user) {
+    handle = opper->user->handle;
+    if (bot_counters.contains(handle))
+      last_counter = bot_counters[handle];
+    else
+      last_counter = 0;
+  }
+
 #ifdef DEBUG
-sdprintf("ts from cookie: %s", &cookie[TS(0)]);
+sdprintf("key: %s", key);
+sdprintf("plaintext from cookie: %s", cleartext.c_str());
+sdprintf("ts from cookie: %s", ts);
+if (indexHint == 0) {
+  sdprintf("last counter from %s: %lu", opper->user->handle, last_counter);
+  sdprintf("counter from cookie: %lu", counter);
+}
 #endif
-  /* The timestamp is already null-terminated :) */
-  const char *ts = &cookie[TS(0)];
+
   const time_t optime = atol(ts);
-  if (((now + timesync) - optime) > 1800)
+  if ((((now + timesync) % 10000000) - optime) > 3900)
     return BC_SLACK;
 
-  const char *salt = &cookie[SALT(0)];
-  const char *hash = cookie_hash(chname, opper, opped, ts, salt);
+  //Only check on the first cookie
+  if (indexHint == 0 && conf.bot->u != opper->user) {
+    if (counter <= last_counter)
+      return BC_COUNTER;
+
+    //Update counter for the opper
+    bot_counters[handle] = counter;
+  }
+
+  const char *hash = cookie_hash(chname, opper, opped, &ts[1], randstring, key);
 #ifdef DEBUG
 sdprintf("hash: %s", hash);
 #endif
@@ -453,6 +526,8 @@ sdprintf("hash: %s", hash);
 
   /* None matched -> failure */
   return BC_HASH;
+#undef HOST
+#undef SALT
 }
 
 /*

+ 2 - 1
src/mod/irc.mod/irc.h

@@ -8,7 +8,7 @@
 
 #include "src/auth.h"
 
-enum { BC_NOCOOKIE = 1, BC_SLACK, BC_HASH };
+enum { BC_NOCOOKIE = 1, BC_SLACK, BC_HASH, BC_COUNTER };
 
 //#define REVENGE_KICK 1		/* Kicked victim	*/
 //#define REVENGE_DEOP 2		/* Took op		*/
@@ -65,6 +65,7 @@ static void cache_invite(struct chanset_t *, char *, char *, char *, bool, bool)
 //static char *makecookie(const char *, const memberlist*, const memberlist*, const memberlist* = NULL, const memberlist* = NULL);
 void makecookie(char*, size_t, const char *, const memberlist*, const memberlist*, const memberlist* = NULL, const memberlist* = NULL);
 static int checkcookie(const char*, const memberlist*, const memberlist*, const char*, int);
+extern void counter_clear(const char*);
 static bool me_voice(struct chanset_t *);
 static bool any_ops(struct chanset_t *);
 static char *getchanmode(struct chanset_t *);

+ 14 - 2
src/mod/irc.mod/mode.c

@@ -150,8 +150,16 @@ flush_cookies(struct chanset_t *chan, int pri)
 
   if (post[0]) {
     memberlist* me = ismember(chan, botname);
+
+    if (me && !me->user && !me->tried_getuser) {
+      char uhost[UHOSTLEN] = "";
+      simple_snprintf(uhost, sizeof(uhost), "%s!%s", me->nick, me->userhost);
+      me->user = get_user_by_host(uhost);
+      me->tried_getuser = 1;
+    }
+
     /* Am I even on the channel? */
-    if (!me)
+    if (!me || !me->user)
       return;
 
     /* remove the trailing space... */
@@ -170,7 +178,8 @@ flush_cookies(struct chanset_t *chan, int pri)
     makecookie(&out[myindex], sizeof(out) - myindex, chan->dname, me, nicks[0], nicks[1], nicks[2]);
   }
   if (out[0]) {
-    if (pri == QUICK) {
+    // Enabling this creates a queued cookie and a dumped cookie, resulting in out-of-order counters.
+    if (pri == QUICK && 0) {
       char outbuf[201] = "";
 
       const size_t len = simple_snprintf(outbuf, sizeof(outbuf), "MODE %s %s\r\n", chan->name, out);
@@ -1179,6 +1188,9 @@ gotmode(char *from, char *msg)
               else if (isbadop == BC_SLACK)
                 putlog(LOG_WARN, "*", "Invalid cookie (bad time): %s!%s MODE %s %s", 
                     m->nick, m->userhost, chan->dname, modes[modecnt - 1]);
+              else if (isbadop == BC_COUNTER)
+                putlog(LOG_WARN, "*", "Invalid cookie (bad count): %s!%s MODE %s %s",
+                    m->nick, m->userhost, chan->dname, modes[modecnt - 1]);
             } 
 #ifdef DEBUG
             else