Selaa lähdekoodia

Merge branch 'FiSH'

* FiSH:
  * Use tokens to be more compatible with other FiSH implementations
  * Prevent keyx on channels
  * Use bd::base64(Encode|Decode) with helper functions to match FiSH base64 encoding
  * Only initialize the prime and generator once
  * Add DH/BN symbols from libcrypto
  * Add cmd_keyx to initiate DH1080 key exchange
  * Store public/private keys as well as current timestamp when sending key-exchange response
  * Add inbound FiSH key-exchange
  * Add DH1080 helper functions from various FiSH source code (mostly ZNC)
  * Process FiSH messages inbound using user's SECPASS as the key
  * Update bdlib to bring in base64 fixes
  * Change privmsg()/notice() to use bd::String
Bryan Drewery 15 vuotta sitten
vanhempi
commit
f92ecc07ff

+ 2 - 0
doc/UPDATES

@@ -21,6 +21,8 @@
   * Added TCL support. This is *only* a .tcl command currently, no scripts are loadable yet.
   * 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.
 
 1.3.1 - http://wraith.botpack.net/milestone/1.3.1
   * Fix crash related to slowpart

+ 3 - 0
doc/help.txt

@@ -1191,6 +1191,9 @@ See also: console, invite
    Jumping servers ALWAYS makes the bot lose ops! be careful!
  
 See also: botjump, servers, botserver
+:leaf:keyx
+### $bkeyx$b <nick>
+    Initiaite DH1080 key-exchange with nick for FiSH protocol.
 :leaf:kick
 ###  $bkick$b [channel|*] <nickname> [reason]
    Will kick a user off your current console channel (or specified

+ 1 - 1
lib/bdlib

@@ -1 +1 @@
-Subproject commit d32981dd6d0a440ed1c5061b310a4fb23c1cd825
+Subproject commit 5cba0c6ba8cde4185cd9d141fca7925635f10f84

+ 13 - 8
src/chanprog.c

@@ -954,32 +954,37 @@ samechans(const char *nick, const char *delim)
   return ret;
 }
 
-struct chanset_t* find_common_opped_chan(const char* nick) {
+struct chanset_t* find_common_opped_chan(bd::String nick) {
   for (struct chanset_t* chan = chanset; chan; chan = chan->next) {
     if (channel_active(chan) && (me_op(chan) || me_voice(chan))) {
-      if (ismember(chan, nick))
+      if (ismember(chan, nick.c_str()))
         return chan;
     }
   }
   return NULL;
 }
 
-void privmsg(const char* target, const char* msg, int idx) {
+void privmsg(bd::String target, bd::String msg, int idx) {
   struct chanset_t* chan = NULL;
   if (have_cprivmsg && !strchr(CHANMETA, target[0]))
     chan = find_common_opped_chan(target);
+
+  // Encrypt with FiSH?
+  if (!strchr(CHANMETA, target[0]) && FishKeys.contains(target) && FishKeys[target]->sharedKey.length()) {
+    msg = "+OK " + egg_bf_encrypt(msg, FishKeys[target]->sharedKey);
+  }
   if (chan)
-    dprintf(idx, "CPRIVMSG %s %s :%s\n", target, chan->name, msg);
+    dprintf(idx, "CPRIVMSG %s %s :%s\n", target.c_str(), chan->name, msg.c_str());
   else
-    dprintf(idx, "PRIVMSG %s :%s\n", target, msg);
+    dprintf(idx, "PRIVMSG %s :%s\n", target.c_str(), msg.c_str());
 }
 
-void notice(const char* target, const char* 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]))
     chan = find_common_opped_chan(target);
   if (chan)
-    dprintf(idx, "CNOTICE %s %s :%s\n", target, chan->name, msg);
+    dprintf(idx, "CNOTICE %s %s :%s\n", target.c_str(), chan->name, msg.c_str());
   else
-    dprintf(idx, "NOTICE %s :%s\n", target, msg);
+    dprintf(idx, "NOTICE %s :%s\n", target.c_str(), msg.c_str());
 }

+ 2 - 2
src/chanprog.h

@@ -32,8 +32,8 @@ void add_child_bots();
 bool is_hub(const char*);
 void load_internal_users();
 void setup_HQ(int);
-void privmsg(const char* target, const char* msg, int idx);
-void notice(const char* target, const char* msg, int idx);
+void privmsg(bd::String target, bd::String msg, int idx);
+void notice(bd::String target, bd::String msg, int idx);
 
 extern struct chanset_t		*chanset, *chanset_default;
 extern char			admin[], origbotnick[HANDLEN + 1], origbotname[NICKLEN], jupenick[NICKLEN], botname[NICKLEN], *def_chanset;

+ 1 - 1
src/cmds.c

@@ -4549,7 +4549,7 @@ void cmd_tcl(int idx, char *par)
 
   bd::String result(tcl_eval(par));
   if (dcc[idx].irc && strcmp(dcc[idx].u.chat->con_chan, "*")) {
-      privmsg(dcc[idx].u.chat->con_chan, tcl_eval(par).c_str(), DP_SERVER);
+      privmsg(dcc[idx].u.chat->con_chan, tcl_eval(par), DP_SERVER);
   } else
     dprintf(idx, result.c_str(), DP_SERVER);
 }

+ 2 - 1
src/crypto/Makefile.in

@@ -14,7 +14,8 @@ depcomp = /bin/sh $(top_srcdir)/autotools/depcomp
 
 OBJS = \
        aes_util.o \
-       bf_util.o
+       bf_util.o \
+       dh_util.o
 
 doofus:
 	@echo ""

+ 2 - 2
src/crypto/bf_util.c

@@ -51,8 +51,8 @@ bd::String egg_bf_encrypt(bd::String in, const bd::String& key)
   /* No key, no encryption */
   if (!key.length()) return in;
 
-  bd::String out(size_t(in.length() * 1.5));
   size_t datalen = in.length();
+  bd::String out(static_cast<size_t>(datalen * 1.5));
   if (datalen % 8 != 0) {
     datalen += 8 - (datalen % 8);
     in.resize(datalen, 0);
@@ -94,7 +94,7 @@ bd::String egg_bf_decrypt(bd::String in, const bd::String& key)
   // Skip over '+OK '
   if (in(0, 4) == "+OK ")
     in += static_cast<size_t>(4);
-  bd::String out(size_t(in.length() * .9));
+  bd::String out(static_cast<size_t>(in.length() * .9));
   // Too small to process
   if (in.size() < 12) return out;
 

+ 149 - 0
src/crypto/dh_util.c

@@ -0,0 +1,149 @@
+/* dh_util.c
+ *
+ * Adapted from ZNC-fish
+ */
+
+#include "src/libcrypto.h"
+#include "src/compat/compat.h"
+#include <bdlib/src/String.h>
+#include <bdlib/src/base64.h>
+#include "dh_util.h"
+
+static BIGNUM* b_prime = NULL;
+static BIGNUM* b_generator = NULL;
+
+void DH1080_init() {
+  // ### new sophie-germain 1080bit prime number ###
+  //const char *prime1080 = "++ECLiPSE+is+proud+to+present+latest+FiSH+release+featuring+even+more+security+for+you+++shouts+go+out+to+TMG+for+helping+to+generate+this+cool+sophie+germain+prime+number++++/C32L";
+  // Base16: FBE1022E23D213E8ACFA9AE8B9DFADA3EA6B7AC7A7B7E95AB5EB2DF858921FEADE95E6AC7BE7DE6ADBAB8A783E7AF7A7FA6A2B7BEB1E72EAE2B72F9FA2BFB2A2EFBEFAC868BADB3E828FA8BADFADA3E4CC1BE7E8AFE85E9698A783EB68FA07A77AB6AD7BEB618ACF9CA2897EB28A6189EFA07AB99A8A7FA9AE299EFA7BA66DEAFEFBEFBF0B7D8B
+  // Base10: 12745216229761186769575009943944198619149164746831579719941140425076456621824834322853258804883232842877311723249782818608677050956745409379781245497526069657222703636504651898833151008222772087491045206203033063108075098874712912417029101508315117935752962862335062591404043092163187352352197487303798807791605274487594646923
+  const char *prime1080 = "FBE1022E23D213E8ACFA9AE8B9DFADA3EA6B7AC7A7B7E95AB5EB2DF858921FEADE95E6AC7BE7DE6ADBAB8A783E7AF7A7FA6A2B7BEB1E72EAE2B72F9FA2BFB2A2EFBEFAC868BADB3E828FA8BADFADA3E4CC1BE7E8AFE85E9698A783EB68FA07A77AB6AD7BEB618ACF9CA2897EB28A6189EFA07AB99A8A7FA9AE299EFA7BA66DEAFEFBEFBF0B7D8B";
+
+  if (!BN_hex2bn(&b_prime, prime1080)) {
+    sdprintf("BAD PRIME");
+    return;
+  }
+
+  if (!BN_dec2bn(&b_generator, "2")) {
+    sdprintf("BAD GENERATOR");
+    return;
+  }
+}
+
+/**
+ * @brief Encode a string using FiSH's base64 algorithm (from FiSH/mIRC)
+ * @note Any = padding is removed, and an 'A' is added if no padding was needed
+ * @param bd::String str The string to encode
+ * @returns Encoded string
+ * @note Adapated from FiSH code
+ */
+bd::String fishBase64Encode(const bd::String& str) {
+  bd::String result(bd::base64Encode(str));
+
+  // No padding, add an A on the end (base64-encoded NULL-terminator)
+  if (result.rfind('=') == result.npos) {
+    result += 'A';
+  } else {
+    // Remove padding
+    while (result.rfind('=') != result.npos) {
+      --result;
+    }
+  }
+  return result;
+}
+
+/**
+ * @brief Decode a string using FiSH's base64 algorithm (from FiSH/mIRC)
+ * @param bd::String str The string to decode
+ * @returns Decoded data
+ * @note Adapated from FiSH code
+ */
+bd::String fishBase64Decode(const bd::String& str) {
+  bd::String temp(str);
+
+  // Remove the 'A' NULL-terminator if present
+  if (temp.length() % 4 == 1 && temp(-1, 1) == 'A') {
+    --temp;
+  }
+
+  while (temp.length() % 4) {
+    temp += '=';
+  }
+
+  return bd::base64Decode(temp);
+}
+
+
+void DH1080_gen(bd::String& privateKey, bd::String& publicKeyB64) {
+  DH *dh = NULL;
+
+  dh = DH_new();
+  dh->p = BN_dup(b_prime);
+  dh->g = BN_dup(b_generator);
+
+  if (!DH_generate_key(dh)) {
+    DH_free(dh);
+    return;
+  }
+
+  // Get private key
+  privateKey.resize(BN_num_bytes(dh->priv_key), 0);
+  BN_bn2bin(dh->priv_key, reinterpret_cast<unsigned char*>(privateKey.mdata()));
+
+  // Get public key
+  bd::String publicKey;
+  // Resize as the mdata() modification won't update the internal length, but resize() will
+  publicKey.resize(static_cast<size_t>(BN_num_bytes(dh->pub_key)));
+  BN_bn2bin(dh->pub_key, reinterpret_cast<unsigned char*>(publicKey.mdata()));;
+
+  // base64 encode
+  publicKeyB64 = fishBase64Encode(publicKey);
+
+  DH_free(dh);
+}
+
+bool DH1080_comp(const bd::String privateKey, const bd::String theirPublicKeyB64, bd::String& sharedKey) {
+  BIGNUM *b_myPrivkey = NULL, *b_HisPubkey = NULL;
+  DH *dh = NULL;
+
+
+  dh = DH_new();
+  dh->p = BN_dup(b_prime);
+  dh->g = BN_dup(b_generator);
+
+  // Setup my private key
+  b_myPrivkey = BN_bin2bn(reinterpret_cast<const unsigned char*>(privateKey.data()), privateKey.length(), NULL);
+  dh->priv_key = b_myPrivkey;
+
+  // Prep their public key
+  bd::String theirPublicKey(fishBase64Decode(theirPublicKeyB64));
+  b_HisPubkey = BN_bin2bn(reinterpret_cast<const unsigned char*>(theirPublicKey.data()), theirPublicKey.length(), NULL);
+
+  // Compute the Shared key
+  char *key = (char *)my_calloc(1, DH_size(dh));
+  size_t len = DH_compute_key((unsigned char *)key, b_HisPubkey, dh);
+  DH_free(dh);
+  BN_clear_free(b_HisPubkey);
+  if (len == static_cast<size_t>(-1)) {
+    // Bad pub key
+    unsigned long err = ERR_get_error();
+    sdprintf("** DH Error: %s", ERR_error_string(err, NULL));
+    free(key);
+
+    sharedKey = ERR_error_string(err, NULL);
+    return false;
+  }
+
+  SHA256_CTX c;
+  bd::String SHA256Digest(static_cast<size_t>(SHA256_DIGEST_LENGTH));
+  SHA256Digest.resize(SHA256_DIGEST_LENGTH);
+
+  SHA256_Init(&c);
+  SHA256_Update(&c, key, len);
+  SHA256_Final(reinterpret_cast<unsigned char*>(SHA256Digest.mdata()), &c);
+  sharedKey = fishBase64Encode(SHA256Digest);
+
+  free(key);
+
+  return true;
+}

+ 21 - 0
src/crypto/dh_util.h

@@ -0,0 +1,21 @@
+/* dh_util.h
+ *
+ */
+
+#ifndef _DH_UTIL_H
+#define _DH_UTIL_H 1
+
+#include <sys/types.h>
+#include <openssl/dh.h>
+
+namespace bd {
+  class String;
+}
+
+// Adapated from znc-fish
+bd::String fishBase64Encode(const bd::String& str);
+bd::String fishBase64Decode(const bd::String& str);
+void DH1080_gen(bd::String& privateKey, bd::String& publicKeyB64);
+bool DH1080_comp(const bd::String privateKey, const bd::String theirPublicKeyB64, bd::String& sharedKey);
+void DH1080_init();
+#endif

+ 15 - 0
src/libcrypto.c

@@ -65,6 +65,21 @@ static int load_symbols(void *handle) {
   DLSYM_GLOBAL(handle, SHA256_Init);
   DLSYM_GLOBAL(handle, SHA256_Update);
 
+  DLSYM_GLOBAL(handle, BN_bin2bn);
+  DLSYM_GLOBAL(handle, BN_bn2bin);
+  DLSYM_GLOBAL(handle, BN_clear_free);
+  DLSYM_GLOBAL(handle, BN_dec2bn);
+  DLSYM_GLOBAL(handle, BN_dup);
+  DLSYM_GLOBAL(handle, BN_hex2bn);
+  DLSYM_GLOBAL(handle, BN_num_bits);
+
+  DLSYM_GLOBAL(handle, DH_compute_key);
+  DLSYM_GLOBAL(handle, DH_free);
+  DLSYM_GLOBAL(handle, DH_generate_key);
+  DLSYM_GLOBAL(handle, DH_new);
+  DLSYM_GLOBAL(handle, DH_size);
+
+
   return 0;
 }
 

+ 16 - 0
src/libcrypto.h

@@ -8,6 +8,7 @@
 #include <openssl/sha.h>
 #include "src/crypto/aes_util.h"
 #include "src/crypto/bf_util.h"
+#include "src/crypto/dh_util.h"
 
 #include "common.h"
 #include "dl.h"
@@ -44,6 +45,21 @@ typedef int (*SHA256_Final_t)(unsigned char*, SHA256_CTX *);
 typedef int (*SHA256_Init_t)(SHA256_CTX*);
 typedef int (*SHA256_Update_t)(SHA256_CTX*, const void*, size_t);
 
+typedef BIGNUM* (*BN_bin2bn_t)(const unsigned char*, int, BIGNUM*);
+typedef BIGNUM* (*BN_dup_t)(const BIGNUM*);
+typedef int (*BN_bn2bin_t)(BIGNUM*, unsigned char*);
+typedef int (*BN_dec2bn_t)(BIGNUM**, const char*);
+typedef int (*BN_hex2bn_t)(BIGNUM**, const char*);
+typedef int (*BN_num_bits_t)(const BIGNUM*);
+typedef void (*BN_clear_free_t)(BIGNUM*);
+
+typedef int (*DH_compute_key_t)(unsigned char*, const BIGNUM*, DH*);
+typedef void (*DH_free_t)(DH*);
+typedef int (*DH_generate_key_t)(DH*);
+typedef DH* (*DH_new_t)(void);
+typedef int (*DH_size_t)(const DH*);
+
+
 #include ".defs/libcrypto_defs.h"
 
 int load_libcrypto();

+ 1 - 1
src/libtcl.c

@@ -106,7 +106,7 @@ static int cmd_privmsg STDVAR {
   bd::String str = argv[2];
   for (int i = 3; i < argc; ++i)
     str += " " + bd::String(argv[i]);
-  privmsg(argv[1], str.c_str(), DP_SERVER);
+  privmsg(argv[1], str, DP_SERVER);
 
   return TCL_OK;
 }

+ 1 - 1
src/log.c

@@ -349,7 +349,7 @@ irc_log(struct chanset_t *chan, const char *format, ...)
   if ((chan && strcasecmp(chan->dname, relay_chan)) || !chan) {
     bd::String msg;
     msg = bd::String::printf("[%s] %s", chan ? chan->dname : "*" , va_out);
-    privmsg(relay_chan, msg.c_str(), DP_HELP);
+    privmsg(relay_chan, msg, DP_HELP);
   }
 /*
   chanout_but(-1, 1, "[%s] %s\n", chan->dname, va_out);

+ 1 - 0
src/main.c

@@ -694,6 +694,7 @@ int main(int argc, char **argv)
   if (load_libcrypto()) {
     fatal("Unable to load libcrypto.", 0);
   }
+  DH1080_init();
 
   /* Initialize variables and stuff */
   timer_update_now(&egg_timeval_now);

+ 14 - 14
src/mod/ctcp.mod/ctcp.c

@@ -407,7 +407,7 @@ static int ctcp_FINGER(char *nick, char *uhost, struct userrec *u, char *object,
   bd::String msg;
   msg = bd::String::printf("\001%s %s (%s) Idle %d second%s\001", keyword, "",
                    botuserhost, (int) idletime, idletime == 1 ? "" : "s");
-  notice(nick, msg.c_str(), DP_HELP);
+  notice(nick, msg, DP_HELP);
   return BIND_RET_BREAK;
 }
 
@@ -418,7 +418,7 @@ static int ctcp_ECHO(char *nick, char *uhost, struct userrec *u, char *object, c
   strlcpy(reply, text, sizeof(reply));
   bd::String msg;
   msg = bd::String::printf("\001%s %s\001", keyword, reply);
-  notice(nick, msg.c_str(), DP_HELP);
+  notice(nick, msg, DP_HELP);
   return BIND_RET_BREAK;
 }
 static int ctcp_PING(char *nick, char *uhost, struct userrec *u, char *object, char *keyword, char *text)
@@ -426,7 +426,7 @@ static int ctcp_PING(char *nick, char *uhost, struct userrec *u, char *object, c
   if (strlen(text) <= 80) {       /* bitchx ignores > 80 */
     bd::String msg;
     msg = bd::String::printf("\001%s %s\001", keyword, text);
-    notice(nick, msg.c_str(), DP_HELP);
+    notice(nick, msg, DP_HELP);
   }
   return BIND_RET_BREAK;
 }
@@ -476,11 +476,11 @@ static int ctcp_VERSION(char *nick, char *uhost, struct userrec *u, char *object
 
   bd::String msg;
   msg = bd::String::printf("\001%s %s%s\001", keyword, ctcpversion, s);
-  notice(nick, msg.c_str(), queue);
+  notice(nick, msg, queue);
 
   if (ctcpversion2[0]) {
     msg = bd::String::printf("\001%s %s\001", keyword, ctcpversion2);
-    notice(nick, msg.c_str(), DP_HELP);
+    notice(nick, msg, DP_HELP);
   }
   return BIND_RET_BREAK;
 }
@@ -490,7 +490,7 @@ static int ctcp_WHOAMI(char *nick, char *uhost, struct userrec *u, char *object,
   if (cloak_script > 0 && cloak_script < 9) {
     bd::String msg;
     msg = bd::String::printf("\002BitchX\002: Access Denied");
-    notice(nick, msg.c_str(), DP_HELP);
+    notice(nick, msg, DP_HELP);
   }
   return BIND_RET_BREAK;
 }
@@ -506,7 +506,7 @@ static int ctcp_OP(char *nick, char *uhost, struct userrec *u, char *object, cha
       *p = 0;
     bd::String msg;
     msg = bd::String::printf("\002BitchX\002: I'm not on %s or I'm not opped", chan);
-    notice(nick, msg.c_str(), DP_HELP);
+    notice(nick, msg, DP_HELP);
   }
   return BIND_RET_BREAK;
 }
@@ -526,14 +526,14 @@ static int ctcp_INVITE_UNBAN(char *nick, char *uhost, struct userrec *u, char *o
       if (chan->ircnet_status & CHAN_ACTIVE) {
         if (!strcasecmp(chan->name, chname)) {
           msg = bd::String::printf("\002BitchX\002: Access Denied");
-          notice(nick, msg.c_str(), DP_HELP);
+          notice(nick, msg, DP_HELP);
           return BIND_RET_LOG;
         }
       }
       chan = chan->next;
     }
     msg = bd::String::printf("\002BitchX\002: I'm not on that channel");
-    notice(nick, msg.c_str(), DP_HELP);
+    notice(nick, msg, DP_HELP);
   }
   return BIND_RET_BREAK;
 }
@@ -548,7 +548,7 @@ static int ctcp_USERINFO(char *nick, char *uhost, struct userrec *u, char *objec
   }
   bd::String msg;
   msg = bd::String::printf("\001%s %s\001", keyword, ctcpuserinfo);
-  notice(nick, msg.c_str(), DP_HELP);
+  notice(nick, msg, DP_HELP);
   return BIND_RET_BREAK;
 }
 
@@ -610,11 +610,11 @@ static int ctcp_CLIENTINFO(char *nick, char *uhost, struct userrec *u, char *obj
     strlcpy(buf, "UPTIME my uptime", sizeof(buf));
   else {
     msg = bd::String::printf("\001ERRMSG %s is not a valid function\001", text);
-    notice(nick, msg.c_str(), DP_HELP);
+    notice(nick, msg, DP_HELP);
     return BIND_RET_LOG;
   }
   msg = bd::String::printf("\001%s %s\001", keyword, buf);
-  notice(nick, msg.c_str(), DP_HELP);
+  notice(nick, msg, DP_HELP);
   return BIND_RET_BREAK;
 }
 
@@ -625,7 +625,7 @@ static int ctcp_TIME(char *nick, char *uhost, struct userrec *u, char *object, c
   strlcpy(tms, ctime(&now), sizeof(tms));
   bd::String msg;
   msg = bd::String::printf("\001%s %s\001", keyword, tms);
-  notice(nick, msg.c_str(), DP_HELP);
+  notice(nick, msg, DP_HELP);
   return BIND_RET_BREAK;
 }
 
@@ -659,7 +659,7 @@ static int ctcp_CHAT(char *nick, char *uhost, struct userrec *u, char *object, c
        * -poptix 5/1/1997 */
       bd::String msg;
       msg = bd::String::printf("\001DCC CHAT chat %lu %u\001", iptolong(getmyip()), dcc[ix].port);
-      privmsg(nick, msg.c_str(), DP_SERVER);
+      privmsg(nick, msg, DP_SERVER);
     }
     return BIND_RET_BREAK;
 }

+ 1 - 1
src/mod/irc.mod/chan.c

@@ -1668,7 +1668,7 @@ static int got341(char *from, char *msg)
   putlog(LOG_MISC, "*", "HIJACKED invite detected: %s to %s", nick, chan->dname);
   bd::String msg;
   msg = bd::String::printf("ALERT! \002%s was invited via a hijacked connection/process.\002", nick);
-  privmsg(chan->name, msg.c_str(), DP_MODE_NEXT);
+  privmsg(chan->name, msg, DP_MODE_NEXT);
   return 0;
 }
 #endif /* CACHE */

+ 3 - 3
src/mod/irc.mod/cmdsirc.c

@@ -121,7 +121,7 @@ static void cmd_act(int idx, char *par)
 	 chan->dname, par);
   bd::String msg;
   msg = bd::String::printf("\001ACTION %s\001", par);
-  privmsg(chan->name, msg.c_str(), DP_HELP);
+  privmsg(chan->name, msg, DP_HELP);
   dprintf(idx, "Action to %s: %s\n", chan->dname, par);
 }
 
@@ -1768,7 +1768,7 @@ static void cmd_adduser(int idx, char *par)
 
     bd::String msg;
     msg = bd::String::printf("*** You've been add to this botnet as '%s' with the host '%s'. Ask a botnet admin for the msg cmds. Your initial password is: %s", hand, p1, s2);
-    privmsg(nick, msg.c_str(), DP_HELP);
+    privmsg(nick, msg, DP_HELP);
   } else {
     dprintf(idx, "Added hostmask %s to %s.\n", p1, u->handle);
     addhost_by_handle(hand, p1);
@@ -1937,7 +1937,7 @@ static void cmd_play(int idx, char *par)
   while (stream.tell() < stream.length()) {
     str = stream.getline().chomp();
     if (str.length()) {
-      privmsg(chan->name, str.c_str(), DP_PLAY);
+      privmsg(chan->name, str, DP_PLAY);
       ++lines;
     }
   }

+ 1 - 1
src/mod/irc.mod/irc.c

@@ -201,7 +201,7 @@ void notice_invite(struct chanset_t *chan, char *handle, char *nick, char *uhost
   bd::String msg;
   msg = bd::String::printf("\001ACTION has invited %s(%s%s%s) to %s.%s\001",
     fhandle, nick, uhost ? "!" : "", uhost ? uhost : "", chan->dname, op ? ops : "");
-  privmsg(chan->name, msg.c_str(), DP_MODE);
+  privmsg(chan->name, msg, DP_MODE);
 }
 
 #ifdef CACHE

+ 14 - 14
src/mod/irc.mod/msgcmds.c

@@ -44,7 +44,7 @@ static int msg_bewm(char *nick, char *host, struct userrec *u, char *par)
 
   if (!u) {
     msg = bd::String::printf(STR("---- (%s!%s) attempted to gain secure invite, but is not a recognized user."), nick, host);
-    privmsg(chan->name, msg.c_str(), DP_SERVER);
+    privmsg(chan->name, msg, DP_SERVER);
 
     putlog(LOG_CMDS, "*", STR("(%s!%s) !*! BEWM"), nick, host);
     return BIND_RET_BREAK;
@@ -59,12 +59,12 @@ static int msg_bewm(char *nick, char *host, struct userrec *u, char *par)
   if (!chk_op(fr, chan))  {
     putlog(LOG_CMDS, "*", STR("(%s!%s) !%s! !BEWM"), nick, host, u->handle);
     msg = bd::String::printf(STR("---- %s (%s!%s) attempted to gain secure invite, but is missing a flag."), u->handle, nick, host);
-    privmsg(chan->name, msg.c_str(), DP_SERVER);
+    privmsg(chan->name, msg, DP_SERVER);
     return BIND_RET_BREAK;
   }
 
   msg = bd::String::printf("\001ACTION has invited \002%s\002 (%s!%s) to %s.\001", u->handle, nick, host, chan->dname);
-  privmsg(chan->name, msg.c_str(), DP_SERVER);
+  privmsg(chan->name, msg, DP_SERVER);
 
   cache_invite(chan, nick, host, u->handle, 0, 0);
   putlog(LOG_CMDS, "*", STR("(%s!%s) !%s! BEWM"), nick, host, u->handle);
@@ -118,7 +118,7 @@ static int msg_pass(char *nick, char *host, struct userrec *u, char *par)
   set_user(&USERENTRY_PASS, u, mynew);
   bd::String msg;
   msg = bd::String::printf("%s '%s'.", mynew == old ? "Password set to:" : "Password changed to:", mynew);
-  notice(nick, msg.c_str(), DP_HELP);
+  notice(nick, msg, DP_HELP);
   return BIND_RET_BREAK;
 }
 
@@ -145,7 +145,7 @@ static int msg_op(char *nick, char *host, struct userrec *u, char *par)
         msg = bd::String::printf("---- (%s!%s) attempted to OP for %s but is not currently in %s.", nick, host, par, homechan);
       else
         msg = bd::String::printf("---- (%s!%s) attempted to OP but is not currently in %s.", nick, host, homechan);
-      privmsg(homechan, msg.c_str(), DP_SERVER);
+      privmsg(homechan, msg, DP_SERVER);
       return BIND_RET_BREAK;
     }
   }
@@ -162,7 +162,7 @@ static int msg_op(char *nick, char *host, struct userrec *u, char *par)
               putlog(LOG_CMDS, "*", "(%s!%s) !%s! OP %s", nick, host, u->handle, par);
               if (manop_warn && chan->manop) {
                 msg = bd::String::printf("%s is currently set to punish for manual op.", chan->dname);
-                notice(nick, msg.c_str(), DP_HELP);
+                notice(nick, msg, DP_HELP);
               }
             }
           }
@@ -177,7 +177,7 @@ static int msg_op(char *nick, char *host, struct userrec *u, char *par)
               stats++;
               if (manop_warn && chan->manop) {
                 msg = bd::String::printf("%s is currently set to punish for manual op.", chan->dname);
-                notice(nick, msg.c_str(), DP_HELP);
+                notice(nick, msg, DP_HELP);
               }
             }
           }
@@ -221,14 +221,14 @@ static int msg_ident(char *nick, char *host, struct userrec *u, char *par)
       return BIND_RET_BREAK;
     } else if (u) {
       msg = bd::String::printf("You're not %s, you're %s.", who, u->handle);
-      notice(nick, msg.c_str(), DP_HELP);
+      notice(nick, msg, DP_HELP);
       return BIND_RET_BREAK;
     } else {
       putlog(LOG_CMDS, "*", "(%s!%s) !*! IDENT %s", nick, host, who);
       simple_snprintf(s, sizeof s, "%s!%s", nick, host);
       maskaddr(s, s1, 0); /* *!user@host */
       msg = bd::String::printf("Added hostmask: %s", s1);
-      notice(nick, msg.c_str(), DP_HELP);
+      notice(nick, msg, DP_HELP);
       addhost_by_handle(who, s1);
       check_this_user(who, 0, NULL);
       return BIND_RET_BREAK;
@@ -262,12 +262,12 @@ static int msg_invite(char *nick, char *host, struct userrec *u, char *par)
     bd::String msg;
     if (!(chan = findchan_by_dname(par))) {
       msg = bd::String::printf("Usage: /MSG %s %s <pass> <channel>", botname, msginvite);
-      notice(nick, msg.c_str(), DP_HELP);
+      notice(nick, msg, DP_HELP);
       return BIND_RET_BREAK;
     }
     if (!channel_active(chan)) {
       msg = bd::String::printf("%s: Not on that channel right now.", par);
-      notice(nick, msg.c_str(), DP_HELP);
+      notice(nick, msg, DP_HELP);
       return BIND_RET_BREAK;
     }
     /* We need to check access here also (dw 991002) */
@@ -321,7 +321,7 @@ static int msg_authstart(char *nick, char *host, struct userrec *u, char *par)
   auth->Status(AUTH_PASS);
   bd::String msg;
   msg = bd::String::printf(STR("auth%s %s"), u ? "." : "!", conf.bot->nick);
-  privmsg(nick, msg.c_str(), DP_HELP);
+  privmsg(nick, msg, DP_HELP);
 
   return BIND_RET_BREAK;
 }
@@ -333,7 +333,7 @@ AuthFinish(Auth *auth)
   auth->Done();
   bd::String msg;
   msg = bd::String::printf(STR("You are now authorized for cmds, see %chelp"), auth_prefix[0]);
-  notice(auth->nick, msg.c_str(), DP_HELP);
+  notice(auth->nick, msg, DP_HELP);
 }
 
 static int msg_auth(char *nick, char *host, struct userrec *u, char *par)
@@ -360,7 +360,7 @@ static int msg_auth(char *nick, char *host, struct userrec *u, char *par)
       auth->MakeHash();
       bd::String msg;
       msg = bd::String::printf(STR("-Auth %s %s"), auth->rand, conf.bot->nick);
-      privmsg(nick, msg.c_str(), DP_HELP);
+      privmsg(nick, msg, DP_HELP);
     } else {
       /* no auth_key and/or no SECPASS for the user, don't require a hash auth */
       AuthFinish(auth);

+ 34 - 0
src/mod/server.mod/cmdsserv.c

@@ -105,6 +105,39 @@ static void cmd_jump(int idx, char *par)
   cycle_time = 0;
 }
 
+static void cmd_keyx(int idx, char *par) {
+  putlog(LOG_CMDS, "*", "#%s# keyx %s", dcc[idx].nick, par);
+
+  if (!par[0]) {
+    dprintf(idx, "Usage: keyx <nick>\n");
+    return;
+  }
+
+  if (strchr(CHANMETA, par[0])) {
+    dprintf(idx, "Error: Cannot key-exchange with a channel.\n");
+    return;
+  }
+
+  if (!server_online) {
+    dprintf(idx, "Error: Not online.\n");
+    return;
+  }
+
+  char *nick = newsplit(&par);
+  bd::String myPublicKeyB64, myPrivateKey, sharedKey;
+
+  DH1080_gen(myPrivateKey, myPublicKeyB64);
+
+  putlog(LOG_MSGS, "*", "[FiSH] Initiating DH1080 key-exchange with %s - sending my public key", nick);
+  notice(nick, "DH1080_INIT " + myPublicKeyB64, DP_HELP);
+  fish_data_t* fishData = new fish_data_t;
+  fishData->myPublicKeyB64 = myPublicKeyB64;
+  fishData->myPrivateKey = myPrivateKey;
+  fishData->timestamp = now;
+  FishKeys[nick] = fishData;
+  return;
+}
+
 static void cmd_clearqueue(int idx, char *par)
 {
   int msgs;
@@ -167,6 +200,7 @@ static cmd_t C_dcc_serv[] =
   {"clearqueue",	"m",	(Function) cmd_clearqueue,	NULL, LEAF|AUTH},
   {"dump",		"a",	(Function) cmd_dump,		NULL, LEAF},
   {"jump",		"m",	(Function) cmd_jump,		NULL, LEAF},
+  {"keyx",		"o",	(Function) cmd_keyx,		NULL, LEAF},
   {"servers",		"m",	(Function) cmd_servers,		NULL, LEAF},
   {"umode",		"m",	(Function) cmd_umode,		NULL, LEAF},
   {NULL,		NULL,	NULL,				NULL, 0}

+ 2 - 0
src/mod/server.mod/server.c

@@ -112,6 +112,8 @@ bool in_callerid = 0;
 bool have_cprivmsg = 0;
 bool have_cnotice = 0;
 
+bd::HashTable<bd::String, fish_data_t*> FishKeys;
+
 static bool double_warned = 0;
 
 static void empty_msgq(void);

+ 10 - 0
src/mod/server.mod/server.h

@@ -9,6 +9,8 @@
 #include "src/binds.h"
 #include "src/dcc.h"
 #include "src/set.h"
+#include <bdlib/src/String.h>
+#include <bdlib/src/HashTable.h>
 
 #define DO_LOST 1
 #define NO_LOST 0
@@ -45,6 +47,13 @@ enum {
 	NETT_HYBRID_EFNET	= 4	/* new +e/+I Efnet hybrid.	  */
 };
 
+typedef struct {
+  bd::String sharedKey;
+  bd::String myPrivateKey;
+  bd::String myPublicKeyB64;
+  time_t timestamp;
+} fish_data_t;
+
 extern bind_table_t	*BT_ctcp, *BT_ctcr;
 extern size_t		nick_len;
 extern bool		trigger_on_ignore, floodless, keepnick, in_deaf, in_callerid, have_cprivmsg, have_cnotice;
@@ -58,6 +67,7 @@ extern char		cursrvname[], botrealname[121], botuserhost[], ctcp_reply[1024],
 extern struct server_list *serverlist;
 extern struct dcc_table SERVER_SOCKET;
 extern rate_t		flood_msg, flood_ctcp, flood_callerid;
+extern bd::HashTable<bd::String, fish_data_t*> FishKeys;
 
 int check_bind_ctcpr(char *, char *, struct userrec *, char *, char *, char *, bind_table_t *);
 void nicks_available(char* buf, char delim = 0, bool buf_contains_available = 1);

+ 84 - 2
src/mod/server.mod/servmsg.c

@@ -155,7 +155,35 @@ static int check_bind_raw(char *from, char *code, char *msg)
   int ret = 0;
 
   myfrom = p1 = strdup(from);
-  mymsg = p2 = strdup(msg);
+
+  // Decrypt FiSH before processing
+  if (!strcmp(code, "PRIVMSG")) {
+    char* colon = strchr(msg, ':');
+    ++colon;
+    if (colon) {
+      if (!strncmp(colon, "+OK ", 4)) {
+        char *p = strchr(from, '!');
+        bd::String ciphertext(colon), sharedKey, nick(from, p - from);
+
+        if (FishKeys.contains(nick)) {
+          sharedKey = FishKeys[nick]->sharedKey;
+        } else {
+          struct userrec *u = get_user_by_host(from);
+          if (u) {
+            sharedKey = static_cast<char*>(get_user(&USERENTRY_SECPASS, u));
+          }
+        }
+
+        if (sharedKey.length()) {
+          // Decrypt the message before passing along to the binds
+          bd::String cleartext(bd::String(msg, colon - msg) + egg_bf_decrypt(ciphertext, sharedKey));
+          mymsg = p2 = strdup(cleartext.c_str());
+        }
+      }
+    }
+  }
+  if (!p2)
+    mymsg = p2 = strdup(msg);
 
   ret = check_bind(BT_raw, code, NULL, myfrom, mymsg);
   free(p1);
@@ -739,6 +767,48 @@ static int gotmsg(char *from, char *msg)
   return 0;
 }
 
+// Adapated from ZNC
+void handle_DH1080_init(const char* nick, const char* uhost, const char* from, struct userrec* u, const bd::String theirPublicKeyB64) {
+  bd::String myPublicKeyB64, myPrivateKey, sharedKey;
+
+  DH1080_gen(myPrivateKey, myPublicKeyB64);
+  if (!DH1080_comp(myPrivateKey, theirPublicKeyB64, sharedKey)) {
+    sdprintf("Error computing DH1080 for %s: %s", nick, sharedKey.c_str());
+    return;
+  }
+
+  putlog(LOG_MSGS, "*", "[FiSH] Received DH1080 public key from (%s!%s) - sending mine", nick, uhost);
+  notice(nick, "DH1080_FINISH " + myPublicKeyB64, DP_HELP);
+  fish_data_t* fishData = new fish_data_t;
+  fishData->myPublicKeyB64 = myPublicKeyB64;
+  fishData->myPrivateKey = myPrivateKey;
+  fishData->sharedKey = sharedKey;
+  fishData->timestamp = now;
+  FishKeys[nick] = fishData;
+  sdprintf("Set key for %s: %s", nick, sharedKey.c_str());
+  return;
+}
+
+void handle_DH1080_finish(const char* nick, const char* uhost, const char* from, struct userrec* u, const bd::String theirPublicKeyB64) {
+  if (!FishKeys.contains(nick)) {
+    putlog(LOG_MSGS, "*", "[FiSH] Unexpected DH1080_FINISH from (%s!%s) - ignoring", nick, uhost);
+    return;
+  }
+
+  fish_data_t* fishData = FishKeys[nick];
+  bd::String sharedKey;
+
+  if (!DH1080_comp(fishData->myPrivateKey, theirPublicKeyB64, sharedKey)) {
+    sdprintf("Error computing DH1080 for %s: %s", nick, sharedKey.c_str());
+    return;
+  }
+
+  putlog(LOG_MSGS, "*", "[FiSH] Key successfully set for (%s!%s)", nick, uhost);
+  fishData->sharedKey = sharedKey;
+  sdprintf("Set key for %s: %s", nick, sharedKey.c_str());
+  return;
+}
+
 /* Got a private notice.
  */
 static int gotnotice(char *from, char *msg)
@@ -817,7 +887,19 @@ static int gotnotice(char *from, char *msg)
       } else if (!ignoring) {
         detect_flood(nick, uhost, from, FLOOD_NOTICE);
         u = get_user_by_host(from);
-        putlog(LOG_MSGS, "*", "-%s (%s)- %s", nick, uhost, msg);
+
+        bd::String smsg(msg);
+        bd::String which = newsplit(smsg);
+
+        if (which == "DH1080_INIT") {
+          bd::String theirPublicKeyB64(newsplit(smsg));
+          handle_DH1080_init(nick, uhost, from, u, theirPublicKeyB64);
+        } else if (which == "DH1080_FINISH") {
+          bd::String theirPublicKeyB64(newsplit(smsg));
+          handle_DH1080_finish(nick, uhost, from, u, theirPublicKeyB64);
+        } else {
+          putlog(LOG_MSGS, "*", "-%s (%s)- %s", nick, uhost, msg);
+        }
       }
     }
   }

+ 1 - 1
src/mod/transfer.mod/transfer.c

@@ -752,7 +752,7 @@ static void dcc_get_pending(int idx, char *buf, int len)
   if (dcc[idx].sock == -1) {
     bd::String msg;
     msg = bd::String::printf("Bad connection (%s)", strerror(errno));
-    notice(dcc[idx].nick, msg.c_str(), DP_HELP);
+    notice(dcc[idx].nick, msg, DP_HELP);
     putlog(LOG_FILES, "*", "DCC bad connection: GET %s (%s!%s)",
 	   dcc[idx].u.xfer->origname, dcc[idx].nick, dcc[idx].host);
     fclose(dcc[idx].u.xfer->f);