Bläddra i källkod

Merge branch 'ducch-fish-for-chans'

* ducch-fish-for-chans:
  * Update docs
  * Trim key when setting it
  * Use a bd::String for key
  * Cleanup fish-key to be consistent with other Chanchars
  * Fix wrong chanchar name for 'fish-key' when writing userfile
  * Fix bot-initiated key exchange due to FiSH support in notice()
  * Don't allow setting FiSH key in channel via cmd_setkey
  * Make the size of the key a const size_t
  * Only remove key if it exists
  * Update docs
  * Support FiSH for channel targets in privmsg/notice
  * FiSH key in .chanset instead of .setkey
  * DRY set_fish_key(target, key). empty to remove key.
  * Random FiSH key support in .setkey
Bryan Drewery 14 år sedan
förälder
incheckning
a50307d79f

+ 4 - 3
doc/UPDATES

@@ -10,9 +10,10 @@
   * Fix blowfish not working correctly on 64bit
   * Suicide will now remove all bots related to the binary being removed (fixes #435)
   * FiSH message support added.
-  * FiSH support for DH1080 key-exchange. 'keyx' command added to start from bot, and responds to key-exchanges.
-  * Auto FiSH key-exchange when accepting users via callerid (controllable with set 'fish-auto-keyx')
-  * Automatic re-key exchange after every message to avoid replay attacks (controllable with set 'fish-paranoid')
+    * FiSH support for DH1080 key-exchange. 'keyx' command added to start from bot, and responds to key-exchanges.
+    * Auto FiSH key-exchange when accepting users via callerid (controllable with set 'fish-auto-keyx')
+    * Automatic re-key exchange after every message to avoid replay attacks (controllable with set 'fish-paranoid')
+    * Set FiSH key via cmd_setkey and 'chanset fish-key'
   * When 'mdop' protection is on, re-op all previously opped clients automatically.
   * When 'mop' protection is on, deop all previously regular clients automatically.
   * Add './wraith -V' which will display the packconfig that the bot is using.

+ 4 - 1
doc/help.txt

@@ -411,6 +411,8 @@ See also: link%{+a}, newhub%{-}
  
         $bflood-lock-time$b   How long in seconds to keep the channel locked
                          during drone floods.
+        $bfish-key$b          The key to use for FiSH encryption. Use '{ key }'
+                          to set and '{}' to unset.
  
         $binvite-time$b       Set here how long temporary invites will last (in
                           minutes). If you set this setting to 0, the bot will
@@ -1741,10 +1743,11 @@ See also: reload, backup
  
 See also: botset
 :leaf:setkey
-### $bsetkey$b <nick|channel> [key]
+### $bsetkey$b <nick> [key|rand]
  
     Sets the FiSH key for the given target.
     Use no key to clear the currently set key.
+    Use 'rand' to generate a random key.
  
 See also: keyx
 ::sha1

+ 1 - 0
src/chan.h

@@ -214,6 +214,7 @@ struct chanset_t {
   char mns[21];			/* negative mode changes		*/
   char key_prot[121];		/* desired password			*/
   bd::Array<bd::String> *groups;/* groups that should join */
+  char fish_key[50];
 /* Chanchar template
  *char temp[121];
  */

+ 48 - 5
src/chanprog.c

@@ -890,17 +890,20 @@ struct chanset_t* find_common_opped_chan(bd::String nick) {
 
 void privmsg(bd::String target, bd::String msg, int idx) {
   struct chanset_t* chan = NULL;
-  if (have_cprivmsg && !strchr(CHANMETA, target[0]))
+  bool talking_to_chan = strchr(CHANMETA, target[0]);
+  if (have_cprivmsg && !talking_to_chan)
     chan = find_common_opped_chan(target);
   bool cleartextPrefix = (msg(0, 3) == "+p ");
 
   // Encrypt with FiSH?
-  if (!strchr(CHANMETA, target[0]) && !cleartextPrefix && FishKeys.contains(target) && FishKeys[target]->sharedKey.length()) {
+  if (!cleartextPrefix && FishKeys.contains(target) && FishKeys[target]->sharedKey.length()) {
     msg = "+OK " + egg_bf_encrypt(msg, FishKeys[target]->sharedKey);
   }
+
   if (cleartextPrefix) {
     msg += static_cast<size_t>(3);
   }
+
   if (chan)
     dprintf(idx, "CPRIVMSG %s %s :%s\n", target.c_str(), chan->name, msg.c_str());
   else
@@ -909,8 +912,19 @@ void privmsg(bd::String target, bd::String msg, int idx) {
 
 void notice(bd::String target, bd::String msg, int idx) {
   struct chanset_t* chan = NULL;
-  if (have_cnotice && !strchr(CHANMETA, target[0]))
+  bool talking_to_chan = strchr(CHANMETA, target[0]);
+  if (have_cnotice && !talking_to_chan)
     chan = find_common_opped_chan(target);
+  bool cleartextPrefix = (msg(0, 3) == "+p ");
+
+  if (!cleartextPrefix && FishKeys.contains(target) && FishKeys[target]->sharedKey.length()) {
+    msg = "+OK " + egg_bf_encrypt(msg, FishKeys[target]->sharedKey);
+  }
+
+  if (cleartextPrefix) {
+    msg += static_cast<size_t>(3);
+  }
+
   if (chan)
     dprintf(idx, "CNOTICE %s %s :%s\n", target.c_str(), chan->name, msg.c_str());
   else
@@ -923,12 +937,41 @@ void keyx(const bd::String &target) {
 
   DH1080_gen(myPrivateKey, myPublicKeyB64);
 
-  putlog(LOG_MSGS, "*", "[FiSH] Initiating DH1080 key-exchange with %s - sending my public key", target.c_str());
-  notice(target, "DH1080_INIT " + myPublicKeyB64, DP_HELP);
   fish_data_t* fishData = FishKeys.contains(target) ? FishKeys[target] : new fish_data_t;
   fishData->sharedKey.clear();
+  putlog(LOG_MSGS, "*", "[FiSH] Initiating DH1080 key-exchange with %s - sending my public key", target.c_str());
+  notice(target, "DH1080_INIT " + myPublicKeyB64, DP_HELP);
   fishData->myPublicKeyB64 = myPublicKeyB64;
   fishData->myPrivateKey = myPrivateKey;
   fishData->timestamp = now;
   FishKeys[target] = fishData;
 }
+
+void set_fish_key(char *target, bd::String key)
+{
+  fish_data_t* fishData = FishKeys.contains(target) ? FishKeys[target] : NULL;
+
+  if (!key.length()) { //remove key
+    if (fishData) {
+      FishKeys.remove(target);
+      delete fishData;
+    }
+  } else { //set key
+    fishData = new fish_data_t;
+
+    if (key == "rand") {
+      // Set a RANDOM key
+      const size_t randomKeyLength = 32;
+      char *rand_key = (char*)my_calloc(1, randomKeyLength+1);
+      make_rand_str(rand_key, randomKeyLength);
+      fishData->sharedKey = rand_key;
+      free(rand_key);
+    } else {
+      fishData->sharedKey = key;
+    }
+
+    // Set the key
+    fishData->timestamp = now;
+    FishKeys[target] = fishData;
+  }
+}

+ 1 - 0
src/chanprog.h

@@ -34,6 +34,7 @@ void setup_HQ(int);
 void privmsg(bd::String target, bd::String msg, int idx);
 void notice(bd::String target, bd::String msg, int idx);
 void keyx(const bd::String& target);
+void set_fish_key(char *, bd::String);
 
 extern struct chanset_t		*chanset, *chanset_default;
 extern char			admin[], origbotnick[HANDLEN + 1], origbotname[NICKLEN], jupenick[NICKLEN], botname[NICKLEN], *def_chanset;

+ 13 - 3
src/mod/channels.mod/chanmisc.c

@@ -387,6 +387,17 @@ int channel_modify(char *result, struct chanset_t *chan, int items, char **item,
       }
       chan->limitraise = atoi(item[i]);
       chan->limit_prot = 0;
+    } else if (!strcmp(item[i], "fish-key")) {
+      i++;
+      if (i >= items) {
+        if (result)
+          strlcpy(result, "channel fish-key needs argument", RESULT_LEN);
+        return ERROR;
+      }
+      bd::String key(item[i]);
+      key.trim();
+      set_fish_key(chan->dname, key);
+      strlcpy(chan->fish_key, key.c_str(), sizeof(chan->fish_key));
 /*
     } else if (!strcmp(item[i], "revenge-mode")) {
       i++;
@@ -565,7 +576,7 @@ int channel_modify(char *result, struct chanset_t *chan, int items, char **item,
         return ERROR;
       }
       chan->protect_backup = atoi(item[i]);
-     
+    }
 
 /* Chanint template
  *  } else if (!strcmp(item[i], "temp")) {
@@ -577,8 +588,6 @@ int channel_modify(char *result, struct chanset_t *chan, int items, char **item,
  *    }
  *    chan->temp = atoi(item[i]);
  */
-    }
-
 
     else if (!strcmp(item[i], "+enforcebans"))
       chan->status |= CHAN_ENFORCEBANS;
@@ -1009,6 +1018,7 @@ int channel_add(char *result, const char *newname, char *options, bool isdefault
     chan->groups = new bd::Array<bd::String>;
     *(chan->groups) << "main";
     chan->protect_backup = 1;
+    chan->fish_key[0] = 0;
     chan->knock_flags = 0;
     chan->flood_lock_time = 120;
     chan->flood_exempt_mode = 0;

+ 1 - 0
src/mod/channels.mod/cmdschan.c

@@ -1185,6 +1185,7 @@ static void cmd_chaninfo(int idx, char *par)
     get_mode_protect(chan, work, sizeof(work));
     dprintf(idx, "Protect modes (chanmode): %s\n", work[0] ? work : "None");
     dprintf(idx, "Groups: %s\n", chan->groups && chan->groups->length() ? static_cast<bd::String>(chan->groups->join(" ")).c_str() : "None");
+    dprintf(idx, "FiSH Key: %s\n", chan->fish_key[0] ? chan->fish_key : "not set");
 //    dprintf(idx, "Protect topic (topic)   : %s\n", chan->topic[0] ? chan->topic : "");
 /* Chanchar template
  *  dprintf(idx, "String temp: %s\n", chan->temp[0] ? chan->temp : "NULL");

+ 2 - 1
src/mod/channels.mod/userchan.c

@@ -719,7 +719,7 @@ flood-chan %d:%d flood-ctcp %d:%d flood-join %d:%d \
 flood-kick %d:%d flood-deop %d:%d flood-nick %d:%d flood-mjoin %d:%d \
 closed-ban %d closed-invite %d closed-private %d ban-time %d \
 exempt-time %d invite-time %d voice-non-ident %d auto-delay %d \
-flood-exempt %d flood-lock-time %d knock %d \
+flood-exempt %d flood-lock-time %d knock %d fish-key { %s } \
 %cmeankicks %cenforcebans %cdynamicbans %cuserbans %cbitch \
 %cprivate %ccycle %cinactive %cdynamicexempts %cuserexempts \
 %cdynamicinvites %cuserinvites %cnodesynch %cclosed %cvoice \
@@ -758,6 +758,7 @@ flood-exempt %d flood-lock-time %d knock %d \
         chan->flood_exempt_mode,
         chan->flood_lock_time,
         chan->knock_flags,
+        chan->fish_key,
  	PLSMNS(channel_meankicks(chan)),
  	PLSMNS(channel_enforcebans(chan)),
 	PLSMNS(channel_dynamicbans(chan)),

+ 14 - 14
src/mod/server.mod/cmdsserv.c

@@ -131,33 +131,33 @@ static void cmd_keyx(int idx, char *par) {
 static void cmd_setkey(int idx, char *par) {
   putlog(LOG_CMDS, "*", "#%s# setkey %s", dcc[idx].nick, par);
 
-  if (!par[0]) {
-    dprintf(idx, "Usage: setkey <nick|channel> [key]\n");
+  const bool target_is_chan = par[0] && strchr(CHANMETA, par[0]);
+
+  if (!par[0] || target_is_chan) {
+    dprintf(idx, "Usage: setkey <nick> [key|rand]\n");
+    if (target_is_chan) {
+      dprintf(idx, "Use 'chanset fish-key' to set a key for a channel.\n");
+    }
     return;
   }
 
   char *target = newsplit(&par);
-  char *key = newsplit(&par);
+  char *newkey = newsplit(&par);
   bool have_key = FishKeys.contains(target);
-  fish_data_t* fishData = NULL;
 
-  if (!key[0]) {
+  if (!newkey[0]) {
     // Clear the key
     if (have_key) {
-      fishData = FishKeys[target];
-      FishKeys.remove(target);
-      delete fishData;
+      set_fish_key(target, "");
       dprintf(idx, "Key cleared for '%s'\n", target);
     } else {
       dprintf(idx, "No key found for '%s'\n", target);
     }
   } else {
-    // Set the key
-    fishData = new fish_data_t;
-    fishData->sharedKey = key;
-    fishData->timestamp = now;
-    FishKeys[target] = fishData;
-    dprintf(idx, "Set key for '%s' to: %s", target, key);
+    // Set a new key
+    set_fish_key(target, newkey);
+    fish_data_t* fishData = FishKeys[target];
+    dprintf(idx, "Set key for '%s' to: %s", target, fishData->sharedKey.c_str());
   }
   return;
 }