diff options
author | Berke Viktor <berkeviktor@aol.com> | 2011-11-24 23:17:31 +0100 |
---|---|---|
committer | Berke Viktor <berkeviktor@aol.com> | 2011-11-24 23:17:31 +0100 |
commit | 1a96ca3edd2b3b503a00180a8d1bb225cac2059b (patch) | |
tree | 3d06014fb0c47873b2260eef2c4a44bde25d2b8d /plugins/fishlim | |
parent | 895e3b6b40c8a50231e775e3ccca1e4437946eb8 (diff) |
add fishlim plugin
Diffstat (limited to 'plugins/fishlim')
-rw-r--r-- | plugins/fishlim/INSTALL | 16 | ||||
-rw-r--r-- | plugins/fishlim/LICENSE | 21 | ||||
-rw-r--r-- | plugins/fishlim/Makefile | 40 | ||||
-rw-r--r-- | plugins/fishlim/README | 44 | ||||
-rw-r--r-- | plugins/fishlim/bool.h | 5 | ||||
-rw-r--r-- | plugins/fishlim/fish.c | 192 | ||||
-rw-r--r-- | plugins/fishlim/fish.h | 42 | ||||
-rw-r--r-- | plugins/fishlim/irc.c | 112 | ||||
-rw-r--r-- | plugins/fishlim/irc.h | 43 | ||||
-rw-r--r-- | plugins/fishlim/keystore.c | 217 | ||||
-rw-r--r-- | plugins/fishlim/keystore.h | 42 | ||||
-rw-r--r-- | plugins/fishlim/makefile.mak | 30 | ||||
-rw-r--r-- | plugins/fishlim/misc.c | 54 | ||||
-rw-r--r-- | plugins/fishlim/misc.h | 36 | ||||
-rw-r--r-- | plugins/fishlim/test.c | 83 | ||||
-rw-r--r-- | plugins/fishlim/xchat_plugin.c | 278 |
16 files changed, 1255 insertions, 0 deletions
diff --git a/plugins/fishlim/INSTALL b/plugins/fishlim/INSTALL new file mode 100644 index 00000000..d0d61ff8 --- /dev/null +++ b/plugins/fishlim/INSTALL @@ -0,0 +1,16 @@ + +Install dependencies (on Debian/Ubuntu): + + sudo apt-get install build-essential libglib2.0-dev libssl-dev + + +Build the plugin with: + + make + + +Install with: + + sudo make install + + diff --git a/plugins/fishlim/LICENSE b/plugins/fishlim/LICENSE new file mode 100644 index 00000000..427f2631 --- /dev/null +++ b/plugins/fishlim/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) 2010-2011 Samuel Lidén Borell <samuel@slbdata.se> + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/plugins/fishlim/Makefile b/plugins/fishlim/Makefile new file mode 100644 index 00000000..7a5081e4 --- /dev/null +++ b/plugins/fishlim/Makefile @@ -0,0 +1,40 @@ + +OURCFLAGS = -Wall -Wextra -Wno-unused-parameter -std=c99 -pedantic `pkg-config --cflags glib-2.0 libcrypto` -DG_DISABLE_DEPRECATED=1 -fPIC $(CFLAGS) +#OURLINKFLAGS = `pkg-config --libs glib-2.0 libcrypto` -shared -fPIC -Wl,-z,defs $(CFLAGS) $(LDFLAGS) +OURLINKFLAGS = `pkg-config --libs glib-2.0 libcrypto` $(CFLAGS) $(LDFLAGS) + +BASE_OBJECTS = irc.o fish.o keystore.o misc.o +PLUGIN_OBJECTS = $(BASE_OBJECTS) xchat_plugin.o +TEST_OBJECTS = $(BASE_OBJECTS) test.o + +all: fishlim.so test + +fish.o: fish.h keystore.h misc.h +irc.o: irc.h +keystore.o: keystore.h irc.h fish.h misc.h +misc.o: misc.h +test.o: fish.h +xchat_plugin.o: fish.h irc.h keystore.h + +.c.o: + $(CC) $(OURCFLAGS) -c $< -o $@ + +fishlim.so: $(PLUGIN_OBJECTS) + $(CC) -shared $(OURLINKFLAGS) $(PLUGIN_OBJECTS) -o $@ + +test: $(TEST_OBJECTS) + $(CC) $(OURLINKFLAGS) $(TEST_OBJECTS) -o $@ + + +.PHONY: all clean distclean install uninstall +clean: + -$(RM) -f $(PLUGIN_OBJECTS) $(TEST_OBJECTS) fishlim.so test +distclean: clean + +install: fishlim.so + install -d $(DESTDIR)/usr/lib/xchat/plugins/ + install -m 644 fishlim.so $(DESTDIR)/usr/lib/xchat/plugins/ +uninstall: + rm $(DESTDIR)/usr/lib/xchat/plugins/fishlim.so + + diff --git a/plugins/fishlim/README b/plugins/fishlim/README new file mode 100644 index 00000000..9c5cd5fc --- /dev/null +++ b/plugins/fishlim/README @@ -0,0 +1,44 @@ + + + FiSHLiM + + http://fishlim.slbdata.se/ + + +FiSHLiM is an XChat plugin for FiSH IRC encryption. It's my attempt at making +a simple, lightweight and secure plugin for this encryption protocol. + +For installation instructions, see the INSTALL file. + + +Features +-------- + +Working: + * Sending/receiving messages + * Topic decryption + * Using unecrypted keys / keys without a password from blow.ini + * Pure protocol-level filtering (works with highlighting, nick coloring etc) + * Partially encrypted messages (i.e. prefixed with nickname by a bouncer) + +Not working: + * Key exchange + * Password-protected key storage + * Topic encryption + * Remote exploitation (hopefully!) + + +Commands +-------- + +/setkey [nick or #channel] password + + Sets the encryption key for the nick or channel to password. The keys + are stored in the configuration file in ~/.xchat2/blow.ini + + +/delkey nick-or-#channel + + Deletes the given nick or channel from the configuration file. + + diff --git a/plugins/fishlim/bool.h b/plugins/fishlim/bool.h new file mode 100644 index 00000000..2c8ddde4 --- /dev/null +++ b/plugins/fishlim/bool.h @@ -0,0 +1,5 @@ +/* stdbool.h replacement for MSVC */ +#define false 0 +#define true 1 +#define bool _Bool +typedef int _Bool; diff --git a/plugins/fishlim/fish.c b/plugins/fishlim/fish.c new file mode 100644 index 00000000..0d92ecc9 --- /dev/null +++ b/plugins/fishlim/fish.c @@ -0,0 +1,192 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include <stdlib.h> +#include <string.h> +#include <openssl/blowfish.h> + +#include "keystore.h" +#include "fish.h" + +#define IB 64 +static const char fish_base64[64] = "./0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; +static const signed char fish_unbase64[256] = { + IB,IB,IB,IB,IB,IB,IB,IB, IB,IB,IB,IB,IB,IB,IB,IB, + IB,IB,IB,IB,IB,IB,IB,IB, IB,IB,IB,IB,IB,IB,IB,IB, +// ! " # $ % & ' ( ) * + , - . / + IB,IB,IB,IB,IB,IB,IB,IB, IB,IB,IB,IB,IB,IB, 0, 1, +// 0 1 2 3 4 5 6 7 8 9 : ; < = > ? + 2, 3, 4, 5, 6, 7, 8, 9, 10,11,IB,IB,IB,IB,IB,IB, +// @ A B C D E F G H I J K L M N O + IB,38,39,40,41,42,43,44, 45,46,47,48,49,50,51,52, +// P Q R S T U V W X Y Z [ \ ] ^ _ + 53,54,55,56,57,58,59,60, 61,62,63,IB,IB,IB,IB,IB, +// ` a b c d e f g h i j k l m n o + IB,12,13,14,15,16,17,18, 19,20,21,22,23,24,25,26, +// p q r s t u v w x y z { | } ~ <del> + 27,28,29,30,31,32,33,34, 35,36,37,IB,IB,IB,IB,IB, +}; + +#define GET_BYTES(dest, source) do { \ + *((dest)++) = ((source) >> 24) & 0xFF; \ + *((dest)++) = ((source) >> 16) & 0xFF; \ + *((dest)++) = ((source) >> 8) & 0xFF; \ + *((dest)++) = (source) & 0xFF; \ +} while (0); + + +char *fish_encrypt(const char *key, size_t keylen, const char *message) { + BF_KEY bfkey; + size_t messagelen; + size_t i; + int j; + char *encrypted; + char *end; + unsigned char bit; + unsigned char word; + unsigned char d; + BF_set_key(&bfkey, keylen, (const unsigned char*)key); + + messagelen = strlen(message); + if (messagelen == 0) return NULL; + encrypted = malloc(((messagelen-1)/8)*12 + 12 + 1); // each 8-byte block becomes 12 bytes + end = encrypted; + if (!encrypted) return NULL; + + while (*message) { + // Read 8 bytes (a Blowfish block) + BF_LONG binary[2] = { 0, 0 }; + unsigned char c; + for (i = 0; i < 8; i++) { + c = message[i]; + binary[i >> 2] |= c << 8*(3 - (i&3)); + if (c == '\0') break; + } + message += 8; + + // Encrypt block + BF_encrypt(binary, &bfkey); + + // Emit FiSH-BASE64 + bit = 0; + word = 1; + for (j = 0; j < 12; j++) { + d = fish_base64[(binary[word] >> bit) & 63]; + *(end++) = d; + bit += 6; + if (j == 5) { + bit = 0; + word = 0; + } + } + + // Stop if a null terminator was found + if (c == '\0') break; + } + *end = '\0'; + return encrypted; +} + + +char *fish_decrypt(const char *key, size_t keylen, const char *data) { + BF_KEY bfkey; + size_t i; + char *decrypted; + char *end; + unsigned char bit; + unsigned char word; + unsigned char d; + BF_set_key(&bfkey, keylen, (const unsigned char*)key); + + decrypted = malloc(strlen(data)+1); + end = decrypted; + if (!decrypted) return NULL; + + while (*data) { + // Convert from FiSH-BASE64 + BF_LONG binary[2] = { 0, 0 }; + bit = 0; + word = 1; + for (i = 0; i < 12; i++) { + d = fish_unbase64[(const unsigned char)*(data++)]; + if (d == IB) goto decrypt_end; + binary[word] |= d << bit; + bit += 6; + if (i == 5) { + bit = 0; + word = 0; + } + } + + // Decrypt block + BF_decrypt(binary, &bfkey); + + // Copy to buffer + GET_BYTES(end, binary[0]); + GET_BYTES(end, binary[1]); + } + + decrypt_end: + *end = '\0'; + return decrypted; +} + +/** + * Encrypts a message (see fish_decrypt). The key is searched for in the + * key store. + */ +char *fish_encrypt_for_nick(const char *nick, const char *data) { + char *key; + char *encrypted; + + // Look for key + key = keystore_get_key(nick); + if (!key) return NULL; + + // Encrypt + encrypted = fish_encrypt(key, strlen(key), data); + + free(key); + return encrypted; +} + +/** + * Decrypts a message (see fish_decrypt). The key is searched for in the + * key store. + */ +char *fish_decrypt_from_nick(const char *nick, const char *data) { + char *key; + char *decrypted; + // Look for key + key = keystore_get_key(nick); + if (!key) return NULL; + + // Decrypt + decrypted = fish_decrypt(key, strlen(key), data); + + free(key); + return decrypted; +} + + diff --git a/plugins/fishlim/fish.h b/plugins/fishlim/fish.h new file mode 100644 index 00000000..d4057a7b --- /dev/null +++ b/plugins/fishlim/fish.h @@ -0,0 +1,42 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifndef FISH_H +#define FISH_H + +#ifdef _MSC_VER +#include "bool.h" +#else +#include <stdbool.h> +#endif +#include <stddef.h> + +char *fish_encrypt(const char *key, size_t keylen, const char *message); +char *fish_decrypt(const char *key, size_t keylen, const char *data); +char *fish_encrypt_for_nick(const char *nick, const char *data); +char *fish_decrypt_from_nick(const char *nick, const char *data); + +#endif + + diff --git a/plugins/fishlim/irc.c b/plugins/fishlim/irc.c new file mode 100644 index 00000000..a96c4c91 --- /dev/null +++ b/plugins/fishlim/irc.c @@ -0,0 +1,112 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include <stdlib.h> +#include <string.h> +#include "irc.h" + +/** + * Parses an IRC message. The words array should contain the message splitted + * at spaces. The prefix and command is extracted from the message, and + * parameters_offset is set to the index of the first parameter. + */ +bool irc_parse_message(const char *words[], + const char **prefix, const char **command, + size_t *parameters_offset) { + size_t w = 1; + if (prefix) *prefix = NULL; + if (command) *command = NULL; + + // See if the message starts with a prefix (sender user) + if (words[w][0] == ':') { + if (prefix) *prefix = &words[w][1]; + w++; + } + + // Check command + if (words[w][0] == '\0') return false; + if (command) *command = words[w]; + w++; + + *parameters_offset = w; + return true; +} + + +/** + * Finds the nick part of a "IRC prefix", which can have any + * of the following forms: + * + * nick + * nick@host + * nick!ident + * nick!ident@host + */ +char *irc_prefix_get_nick(const char *prefix) { + const char *end; + char *nick; + size_t length; + + if (!prefix) return NULL; + + // Find end of nick + end = prefix; + while (*end != '\0' && *end != '!' && *end != '@') end++; + + // Allocate string + length = end - prefix; + nick = malloc(length+1); + if (!nick) return NULL; + + // Copy to string + memcpy(nick, prefix, length); + nick[length] = '\0'; + return nick; +} + + +/** + * Compares two nick names. Return 0 if equal. Otherwise the return value is + * less than zero if a is less than b or greater than zero if a is greater + * than b. + */ +int irc_nick_cmp(const char *a, const char *b) { + char ac; + char bc; + char diff; + for (;;) { + ac = *(a++); + bc = *(b++); + + // Change into IRC uppercase (see RFC 2812 section 2.2) + if (ac >= 'a' && ac <= '~') ac &= ~0x20; + if (bc >= 'a' && bc <= '~') bc &= ~0x20; + + diff = ac - bc; + if (diff) return diff; + if (!ac) return 0; + } +} + + diff --git a/plugins/fishlim/irc.h b/plugins/fishlim/irc.h new file mode 100644 index 00000000..0c1e7463 --- /dev/null +++ b/plugins/fishlim/irc.h @@ -0,0 +1,43 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifndef IRC_H +#define IRC_H + +#ifdef _MSC_VER +#include "bool.h" +#else +#include <stdbool.h> +#endif +#include <stddef.h> + +bool irc_parse_message(const char *words[], + const char **prefix, const char **command, + size_t *parameters_offset); +char *irc_prefix_get_nick(const char *prefix); +int irc_nick_cmp(const char *a, const char *b); + +#endif + + diff --git a/plugins/fishlim/keystore.c b/plugins/fishlim/keystore.c new file mode 100644 index 00000000..6eccccaf --- /dev/null +++ b/plugins/fishlim/keystore.c @@ -0,0 +1,217 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include <glib.h> +#include <stdlib.h> +#include <string.h> +#include "irc.h" +#include "fish.h" +#include "misc.h" +#include "keystore.h" + + +static char *keystore_password = NULL; + + +/** + * Returns the path to the key store file. + */ +static gchar *get_config_filename() { + // TODO use xchat_get_info(ph, "xchatdirfs") + const gchar *homedir = g_get_home_dir(); + return g_build_filename(homedir, ".xchat2", "blow.ini", NULL); +} + +/** + * Opens the key store file: ~/.xchat2/blow.ini + */ +static GKeyFile *getConfigFile() { + gchar *filename = get_config_filename(); + + GKeyFile *keyfile = g_key_file_new(); + g_key_file_load_from_file(keyfile, filename, + G_KEY_FILE_KEEP_COMMENTS | + G_KEY_FILE_KEEP_TRANSLATIONS, NULL); + + g_free(filename); + return keyfile; +} + + +/** + * Returns the key store password, or the default. + */ +static const char *get_keystore_password() { + return (keystore_password != NULL ? + keystore_password : + // Silly default value... + "blowinikey"); +} + + +/** + * Gets a value for a nick/channel from blow.ini. Unlike + * g_key_file_get_string, this function is case insensitive. + */ +static gchar *get_nick_value(GKeyFile *keyfile, const char *nick, const char *item) { + gchar **group; + gchar **groups = g_key_file_get_groups(keyfile, NULL); + gchar *result = NULL; + + for (group = groups; *group != NULL; group++) { + if (!irc_nick_cmp(*group, nick)) { + result = g_key_file_get_string(keyfile, *group, item, NULL); + break; + } + } + + g_strfreev(groups); + return result; +} + + +/** + * Extracts a key from the key store file. + */ +char *keystore_get_key(const char *nick) { + // Get the key + GKeyFile *keyfile = getConfigFile(); + gchar *value = get_nick_value(keyfile, nick, "key"); + g_key_file_free(keyfile); + if (!value) return NULL; + + if (strncmp(value, "+OK ", 4) != 0) { + // Key is stored in plaintext + return import_glib_string(value); + } else { + // Key is encrypted + const char *encrypted = value+4; + const char *password = get_keystore_password(); + char *decrypted = fish_decrypt(password, strlen(password), encrypted); + g_free(value); + return decrypted; + } +} + +/** + * Deletes a nick and the associated key in the key store file. + */ +static bool delete_nick(GKeyFile *keyfile, const char *nick) { + gchar **group; + gchar **groups = g_key_file_get_groups(keyfile, NULL); + bool ok = false; + + for (group = groups; *group != NULL; group++) { + if (!irc_nick_cmp(*group, nick)) { + ok = g_key_file_remove_group(keyfile, *group, NULL); + break; + } + } + + g_strfreev(groups); + return ok; +} + +/** + * Writes the key store file to disk. + */ +static bool save_keystore(GKeyFile *keyfile) { + char *filename; + bool ok; + // Serialize + gsize file_length; + gchar *file_data = g_key_file_to_data(keyfile, &file_length, NULL); + if (!file_data) return false; + + // Write to file + filename = get_config_filename(); + ok = g_file_set_contents(filename, file_data, file_length, NULL); + g_free(filename); + g_free(file_data); + return ok; +} + +/** + * Sets a key in the key store file. + */ +bool keystore_store_key(const char *nick, const char *key) { + const char *password; + char *encrypted; + char *wrapped; + bool ok = false; + GKeyFile *keyfile = getConfigFile(); + + // Remove old key + delete_nick(keyfile, nick); + + // Add new key + password = get_keystore_password(); + if (password) { + // Encrypt the password + encrypted = fish_encrypt(password, strlen(password), key); + if (!encrypted) goto end; + + // Prepend "OK+ " + wrapped = g_strconcat("OK+ ", encrypted, NULL); + g_free(encrypted); + + // Store encrypted in file + g_key_file_set_string(keyfile, nick, "key", wrapped); + free(wrapped); + } else { + // Store unencrypted in file + g_key_file_set_string(keyfile, nick, "key", key); + } + + // Save key store file + ok = save_keystore(keyfile); + + end: + g_key_file_free(keyfile); + return ok; +} + +/** + * Deletes a nick from the key store. + */ +bool keystore_delete_nick(const char *nick) { + GKeyFile *keyfile = getConfigFile(); + + // Delete entry + bool ok = delete_nick(keyfile, nick); + + // Save + if (ok) save_keystore(keyfile); + + g_key_file_free(keyfile); + return ok; +} + + +void keystore_secure_free(void *ptr, size_t size) { + secure_erase(ptr, size); + free(ptr); +} + + diff --git a/plugins/fishlim/keystore.h b/plugins/fishlim/keystore.h new file mode 100644 index 00000000..e6c527c7 --- /dev/null +++ b/plugins/fishlim/keystore.h @@ -0,0 +1,42 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifndef KEYSTORE_H +#define KEYSTORE_H + +#ifdef _MSC_VER +#include "bool.h" +#else +#include <stdbool.h> +#endif +#include <stddef.h> + +char *keystore_get_key(const char *nick); +int keystore_store_key(const char *nick, const char *key); +bool keystore_delete_nick(const char *nick); + +void keystore_secure_free(void *ptr, size_t size); + +#endif + diff --git a/plugins/fishlim/makefile.mak b/plugins/fishlim/makefile.mak new file mode 100644 index 00000000..decf9bb4 --- /dev/null +++ b/plugins/fishlim/makefile.mak @@ -0,0 +1,30 @@ +include "..\..\src\makeinc.mak" + +TARGET = xcfishlim.dll + +CFLAGS = $(CFLAGS) + +FISHLIM_OBJECTS = \ +fish.obj \ +irc.obj \ +keystore.obj \ +misc.obj \ +xchat_plugin.obj + +all: $(FISHLIM_OBJECTS) fishlim.def + link $(LDFLAGS) $(LIBS) /dll /out:xcfishlim.dll /def:fishlim.def $(FISHLIM_OBJECTS) + +fishlim.def: + echo EXPORTS > fishlim.def + echo xchat_plugin_init >> fishlim.def + echo xchat_plugin_deinit >> fishlim.def + echo xchat_plugin_get_info >> fishlim.def + +.c.obj: + $(CC) $(CFLAGS) $(GLIB) /I.. /c $< + +clean: + del *.obj + del *.dll + del *.exp + del *.lib diff --git a/plugins/fishlim/misc.c b/plugins/fishlim/misc.c new file mode 100644 index 00000000..32fe3595 --- /dev/null +++ b/plugins/fishlim/misc.c @@ -0,0 +1,54 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include <glib.h> +#include <stdlib.h> +#include <string.h> +#include "misc.h" + + +void secure_erase(void *ptr, size_t size) { + // "volatile" prevents this code from being optimized away + volatile char* volptr = ptr; + while (size--) *volptr++ = 0; +} + +/** + * Re-allocates a string with the native allocator. + */ +char *import_glib_string(gchar *gstr) { + size_t size; + char *native; + if (g_mem_is_system_malloc()) return gstr; + + size = strlen(gstr)+1; + native = malloc(size); + memcpy(native, gstr, size); + + secure_erase(gstr, size); + g_free(gstr); + return native; +} + + diff --git a/plugins/fishlim/misc.h b/plugins/fishlim/misc.h new file mode 100644 index 00000000..0adc928c --- /dev/null +++ b/plugins/fishlim/misc.h @@ -0,0 +1,36 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#ifndef MISC_H +#define MISC_H + +void secure_erase(void *ptr, size_t size); + +#ifdef __G_LIB_H__ +char *import_glib_string(gchar *gstr); +#endif + +#endif + + diff --git a/plugins/fishlim/test.c b/plugins/fishlim/test.c new file mode 100644 index 00000000..2032496a --- /dev/null +++ b/plugins/fishlim/test.c @@ -0,0 +1,83 @@ +/* + + Copyright (c) 2010 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include <stdio.h> +#include <string.h> +#include "fish.h" + +static int decrypt(int nick_count, char *nicks[]) { + char encrypted[8192]; + while (fgets(encrypted, sizeof(encrypted), stdin)) { + char *msg; + for (int i = 0; i < nick_count; i++) { + msg = fish_decrypt_from_nick(nicks[i], encrypted); + if (msg) goto success; + } + fprintf(stderr, "None of the recipients were found in the key store!\n"); + return 1; + success: + fprintf(stderr, "Decrypted text >>>%s<<<\n", msg); + } + return 0; +} + +static int encrypt(int nick_count, char *nicks[]) { + char message[8192]; + while (fgets(message, sizeof(message), stdin)) { + // Remove newline character + char *newline = strchr(message, '\n'); + if (newline) *newline = '\0'; + + bool error = false; + for (int i = 0; i < nick_count; i++) { + char *encrypted = fish_encrypt_for_nick(nicks[i], message); + if (encrypted) { + fprintf(stderr, "Encrypted [%s]: >>>%s<<<\n", nicks[i], encrypted); + } else { + error = true; + } + } + + if (error) { + fprintf(stderr, "Some of the recipients were't found in the key store!\n"); + return 1; + } + } + return 0; +} + +int main(int argc, char *argv[]) { + if (argc < 2) { + fprintf(stderr, "usage: %s [-e] nick...\n", argv[0]); + return 2; + } + + if (strcmp(argv[1], "-e") == 0) { + return encrypt(argc-2, &argv[2]); + } else { + return decrypt(argc-1, &argv[1]); + } +} + + diff --git a/plugins/fishlim/xchat_plugin.c b/plugins/fishlim/xchat_plugin.c new file mode 100644 index 00000000..dc67fddb --- /dev/null +++ b/plugins/fishlim/xchat_plugin.c @@ -0,0 +1,278 @@ +/* + + Copyright (c) 2010-2011 Samuel Lidén Borell <samuel@slbdata.se> + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. + +*/ + +#include <stdlib.h> +#include <string.h> + +// #pragma GCC visibility push(default) +#include "xchat-plugin.h" +#define XCHAT_MAX_WORDS 32 +// #pragma GCC visibility pop + +//#define EXPORT __attribute((visibility("default"))) +//#define EXPORT + +#include "fish.h" +#include "keystore.h" +#include "irc.h" + +static const char plugin_name[] = "FiSHLiM"; +static const char plugin_desc[] = "Encryption plugin for the FiSH protocol. Less is More!"; +static const char plugin_version[] = "0.0.13"; + +static const char usage_setkey[] = "Usage: SETKEY [<nick or #channel>] <password>, sets the key for a channel or nick"; +static const char usage_delkey[] = "Usage: DELKEY <nick or #channel>, deletes the key for a channel or nick"; + +static xchat_plugin *ph; + + +/** + * Appends data to a string. Returns true if there was sufficient memory. + * Frees *s and returns false if an error occurs. + */ +static bool append(char **s, size_t *length, const char *data) { + size_t datalen = strlen(data); + char *extended = realloc(*s, *length + datalen + 1); + if (!extended) { + free(*s); + return false; + } + memcpy(extended + *length, data, datalen + 1); + *s = extended; + *length += datalen; + return true; +} + + +/*static int handle_debug(char *word[], char *word_eol[], void *userdata) { + xchat_printf(ph, "debug incoming: "); + for (size_t i = 1; word[i] != NULL && word[i][0] != '\0'; i++) { + xchat_printf(ph, ">%s< ", word[i]); + } + xchat_printf(ph, "\n"); + return XCHAT_EAT_NONE; +}*/ + +/** + * Called when a message is to be sent. + */ +static int handle_outgoing(char *word[], char *word_eol[], void *userdata) { + const char *own_nick; + // Encrypt the message if possible + const char *channel = xchat_get_info(ph, "channel"); + char *encrypted = fish_encrypt_for_nick(channel, word_eol[1]); + if (!encrypted) return XCHAT_EAT_NONE; + + // Display message + own_nick = xchat_get_info(ph, "nick"); + xchat_emit_print(ph, "Your Message", own_nick, word_eol[1], NULL); + + // Send message + xchat_commandf(ph, "PRIVMSG %s :+OK %s", channel, encrypted); + + free(encrypted); + return XCHAT_EAT_XCHAT; +} + +/** + * Called when a channel message or private message is received. + */ +static int handle_incoming(char *word[], char *word_eol[], void *userdata) { + const char *prefix; + const char *command; + const char *recipient; + const char *encrypted; + const char *peice; + char *sender_nick; + char *decrypted; + char *message; + size_t w; + size_t ew; + size_t uw; + size_t length; + + if (!irc_parse_message((const char **)word, &prefix, &command, &w)) + return XCHAT_EAT_NONE; + + // Topic (command 332) has an extra parameter + if (!strcmp(command, "332")) w++; + + // Look for encrypted data + ew; + for (ew = w+1; ew < XCHAT_MAX_WORDS-1; ew++) { + const char *s = (ew == w+1 ? word[ew]+1 : word[ew]); + if (strcmp(s, "+OK") == 0) goto has_encrypted_data; + } + return XCHAT_EAT_NONE; + has_encrypted_data: ; + // Extract sender nick and recipient nick/channel + sender_nick = irc_prefix_get_nick(prefix); + recipient = word[w]; + + // Try to decrypt with these (the keys are searched for in the key store) + encrypted = word[ew+1]; + decrypted = fish_decrypt_from_nick(recipient, encrypted); + if (!decrypted) decrypted = fish_decrypt_from_nick(sender_nick, encrypted); + + // Check for error + if (!decrypted) goto decrypt_error; + + // Build unecrypted message + message = NULL; + length = 0; + if (!append(&message, &length, "RECV")) goto decrypt_error; + + for (uw = 1; uw < XCHAT_MAX_WORDS; uw++) { + if (word[uw][0] != '\0' && !append(&message, &length, " ")) goto decrypt_error; + + if (uw == ew) { + // Add the encrypted data + peice = decrypted; + uw++; // Skip "OK+" + } else { + // Add unencrypted data (for example, a prefix from a bouncer or bot) + peice = (uw == w+1 ? word[uw]+1 : word[uw]); + } + + if (!append(&message, &length, peice)) goto decrypt_error; + } + free(decrypted); + + // Simulate unencrypted message + //xchat_printf(ph, "simulating: %s\n", message); + xchat_command(ph, message); + + free(message); + free(sender_nick); + return XCHAT_EAT_XCHAT; + + decrypt_error: + free(decrypted); + free(sender_nick); + return XCHAT_EAT_NONE; +} + +/** + * Command handler for /setkey + */ +static int handle_setkey(char *word[], char *word_eol[], void *userdata) { + const char *nick; + const char *key; + + // Check syntax + if (*word[2] == '\0') { + xchat_printf(ph, "%s\n", usage_setkey); + return XCHAT_EAT_XCHAT; + } + + if (*word[3] == '\0') { + // /setkey password + nick = xchat_get_info(ph, "channel"); + key = word_eol[2]; + } else { + // /setkey #channel password + nick = word[2]; + key = word_eol[3]; + } + + // Set password + if (keystore_store_key(nick, key)) { + xchat_printf(ph, "Stored key for %s\n", nick); + } else { + xchat_printf(ph, "\00305Failed to store key in blow.ini\n", nick, key); + } + + return XCHAT_EAT_XCHAT; +} + +/** + * Command handler for /delkey + */ +static int handle_delkey(char *word[], char *word_eol[], void *userdata) { + const char *nick; + + // Check syntax + if (*word[2] == '\0' || *word[3] != '\0') { + xchat_printf(ph, "%s\n", usage_delkey); + return XCHAT_EAT_XCHAT; + } + + nick = word_eol[2]; + + // Delete the given nick from the key store + if (keystore_delete_nick(nick)) { + xchat_printf(ph, "Deleted key for %s\n", nick); + } else { + xchat_printf(ph, "\00305Failed to delete key in blow.ini!\n", nick); + } + + return XCHAT_EAT_XCHAT; +} + +/** + * Returns the plugin name version information. + */ +void xchat_plugin_get_info(const char **name, const char **desc, + const char **version, void **reserved) { + *name = plugin_name; + *desc = plugin_desc; + *version = plugin_version; +} + +/** + * Plugin entry point. + */ +int xchat_plugin_init(xchat_plugin *plugin_handle, + const char **name, + const char **desc, + const char **version, + char *arg) { + ph = plugin_handle; + + /* Send our info to XChat */ + *name = plugin_name; + *desc = plugin_desc; + *version = plugin_version; + + /* Register commands */ + xchat_hook_command(ph, "SETKEY", XCHAT_PRI_NORM, handle_setkey, usage_setkey, NULL); + xchat_hook_command(ph, "DELKEY", XCHAT_PRI_NORM, handle_delkey, usage_delkey, NULL); + + /* Add handlers */ + xchat_hook_command(ph, "", XCHAT_PRI_NORM, handle_outgoing, NULL, NULL); + xchat_hook_server(ph, "NOTICE", XCHAT_PRI_NORM, handle_incoming, NULL); + xchat_hook_server(ph, "PRIVMSG", XCHAT_PRI_NORM, handle_incoming, NULL); + //xchat_hook_server(ph, "RAW LINE", XCHAT_PRI_NORM, handle_debug, NULL); + xchat_hook_server(ph, "TOPIC", XCHAT_PRI_NORM, handle_incoming, NULL); + xchat_hook_server(ph, "332", XCHAT_PRI_NORM, handle_incoming, NULL); + + xchat_printf (ph, "%s plugin loaded\n", plugin_name); + /* Return success */ + return 1; +} + +int xchat_plugin_deinit(void) { + xchat_printf (ph, "%s plugin unloaded\n", plugin_name); + return 1; +} |