/* * Encrypt a message and store in bf_dest (using key for target) * @param server * @param msg_ptr * @param target * @param bf_dest * @return 1 if everything ok 0 if not */ int FiSH_encrypt(const SERVER_REC * serverRec, const char *msgPtr, const char *target, char *bf_dest) { char theKey[KEYBUF_SIZE] = ""; char contactName[CONTACT_SIZE] = ""; if (IsNULLorEmpty(msgPtr) || bf_dest == NULL || IsNULLorEmpty(target)) return 0; if (settings_get_bool("process_outgoing") == 0) return 0; if (getIniSectionForContact(serverRec, target, contactName) == FALSE) return 0; if (getContactKey(contactName, theKey) == FALSE) return 0; strcpy(bf_dest, "+OK "); encrypt_string(theKey, msgPtr, bf_dest + 4, strlen(msgPtr)); ZeroMemory(theKey, KEYBUF_SIZE); return 1; }
void encrypt_msg(SERVER_REC * server, char *target, char *msg, char *orig_target) { char bf_dest[800] = "", *plainMsg; char contactName[CONTACT_SIZE] = ""; if (IsNULLorEmpty(msg) || IsNULLorEmpty(target)) return; if (getIniSectionForContact(server, target, contactName) == FALSE) return; if (getContactKey(contactName, NULL) == FALSE) return; plainMsg = isPlainPrefix(msg); if (plainMsg) { signal_continue(4, server, target, plainMsg, orig_target); return; } // generally cut a message to a size of 512 byte, as everything above will never arrive complete anyway if (strlen(msg) > 512) msg[512] = '\0'; if (FiSH_encrypt(server, msg, target, bf_dest) == 1) { // message was encrypted bf_dest[512] = '\0'; signal_continue(4, server, target, bf_dest, orig_target); } }
/* * format outgoing (encrypted) messages (add crypt-mark or remove plain-prefix) */ void format_msg(SERVER_REC * server, char *msg, char *target, char *orig_target) { char contactName[CONTACT_SIZE] = "", myMark[20] = "", formattedMsg[800] = ""; int i, markPos; char *plainMsg; if (IsNULLorEmpty(msg) || IsNULLorEmpty(target)) return; if (settings_get_bool("process_outgoing") == 0) return; if (getIniSectionForContact(server, target, contactName) == FALSE) return; if (getContactKey(contactName, NULL) == FALSE) return; plainMsg = isPlainPrefix(msg); if (plainMsg) { signal_continue(4, server, plainMsg, target, orig_target); return; } // generally cut a message to a size of 512 byte, as everything above will never arrive complete anyway if (strlen(msg) > 512) msg[512] = '\0'; // append crypt-mark? strncpy(myMark, settings_get_str("mark_encrypted"), sizeof(myMark)); if (*myMark != '\0') { strcpy(formattedMsg, msg); markPos = settings_get_int("mark_position"); if (markPos == 0) strcat(formattedMsg, myMark); //append mark at the end else { // prefix mark i = strlen(myMark); memmove(formattedMsg + i, formattedMsg, strlen(formattedMsg) + 1); strncpy(formattedMsg, myMark, i); } signal_continue(4, server, formattedMsg, target, orig_target); ZeroMemory(formattedMsg, sizeof(formattedMsg)); } return; }
/* * format outgoing (encrypted) messages (add crypt-mark or remove plain-prefix) */ void format_msg(SERVER_REC *server, char *msg, char *target, char *orig_target) { char contactName[CONTACT_SIZE]="", myMark[20]="", markPos[20]="", formattedMsg[800]=""; int i; char *plainMsg; if (IsNULLorEmpty(msg) || IsNULLorEmpty(target)) return; if (GetBlowIniSwitch("FiSH", "process_outgoing", "1") == 0) return; if (GetIniSectionForContact(server, target, contactName)==FALSE) return; if (LoadKeyForContact(contactName, NULL)==FALSE) return; plainMsg = IsPlainPrefix(msg); if (plainMsg) { signal_continue(4, server, plainMsg, target, orig_target); return; } // generally cut a message to a size of 512 byte, as everything above will never arrive complete anyway if (strlen(msg) > 512) msg[512]='\0'; // append crypt-mark? if (GetBlowIniSwitch(contactName, "mark_encrypted", "1") != 0) { GetPrivateProfileString("FiSH", "mark_encrypted", "", myMark, sizeof(myMark), iniPath); // global setting if (*myMark != '\0') { strcpy(formattedMsg, msg); GetPrivateProfileString("FiSH", "mark_position", "0", markPos, sizeof(markPos), iniPath); if (*markPos=='0') strcat(formattedMsg, myMark); //append mark at the end else { // prefix mark i=strlen(myMark); memmove(formattedMsg+i, formattedMsg, strlen(formattedMsg)+1); strncpy(formattedMsg, myMark, i); } signal_continue(4, server, formattedMsg, target, orig_target); ZeroMemory(formattedMsg, sizeof(formattedMsg)); } } return; }
void cmd_keyx(const char *target, SERVER_REC * server, WI_ITEM_REC * item) { if (IsNULLorEmpty(target)) { if (item != NULL) target = window_item_get_target(item); else { printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Please define nick/#channel. Usage: /keyx <nick/#channel>"); return; } } if (ischannel(*target)) { printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 KeyXchange does not work for channels!"); return; } DH1080_gen(g_myPrivKey, g_myPubKey); irc_send_cmdv((IRC_SERVER_REC *) server, "NOTICE %s :%s %s", target, "DH1080_INIT", g_myPubKey); printtext(server, item != NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Sent my DH1080 public key to %s, waiting for reply ...", target); }
/** * Sets the key for a nick / channel in a server * @param data command * @param server irssi server record * @param item irssi window/item */ void cmd_setkey(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { GHashTable *optlist; char contactName[CONTACT_SIZE]="", encryptedKey[150]=""; const char *target, *key; void *free_arg; if (IsNULLorEmpty(data)) { printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 No parameters. Usage: /setkey [-<server tag>] [<nick | #channel>] <key>"); return; } if (!cmd_get_params(data, &free_arg, 2 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, "setkey", &optlist, &target, &key)) return; if (*target=='\0') { printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 No parameters. Usage: /setkey [-<server tag>] [<nick | #channel>] <key>"); cmd_params_free(free_arg); return; } server = cmd_options_get_server("setkey", optlist, server); if (server == NULL || !server->connected) cmd_param_error(CMDERR_NOT_CONNECTED); if (*key=='\0') { // one paramter given - it's the key key = target; if (item != NULL) target = window_item_get_target(item); else { printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Please define nick/#channel. Usage: /setkey [-<server tag>] [<nick | #channel>] <key>"); cmd_params_free(free_arg); return; } } encrypt_key((char *)key, encryptedKey); if (GetIniSectionForContact(server, target, contactName)==FALSE) return; if (WritePrivateProfileString(contactName, "key", encryptedKey, iniPath) == -1) { ZeroMemory(encryptedKey, sizeof(encryptedKey)); printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied."); cmd_params_free(free_arg); return; } ZeroMemory(encryptedKey, sizeof(encryptedKey)); printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s@%s successfully set!", target, server->tag); cmd_params_free(free_arg); }
void raw_handler(SERVER_REC *server, char *data) { char channel[CONTACT_SIZE], *ptr, *ptr_bak; int len; if (IsNULLorEmpty(data)) return; ptr=strchr(data, ' '); // point to command if (ptr==NULL) return; ptr++; if (strncmp(ptr, "332 ", 4)!=0) return; // 332 = TOPIC ptr_bak=ptr; ptr=strchr(ptr, '#'); // point to #channel if (ptr==NULL) { ptr=strchr(ptr_bak, '&'); // point to &channel if (ptr==NULL) { ptr=strchr(ptr_bak, '!'); // point to !channel if (ptr==NULL) return; } } len=strchr(ptr, ' ')-ptr; if (len >= CONTACT_SIZE-2) return; // channel string too long, something went wrong strncpy(channel, ptr, len); channel[len]='\0'; ptr=strchr(ptr, ':'); // point to topic msg start if (ptr==NULL) return; ptr++; FiSH_decrypt(server, ptr, ptr, channel); }
void cmd_key(const char *data, SERVER_REC * server, WI_ITEM_REC * item) { GHashTable *optlist; char *target; char contactName[CONTACT_SIZE] = "", theKey[KEYBUF_SIZE] = ""; void *free_arg; if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, "key", &optlist, &target)) return; if (item != NULL && IsNULLorEmpty(target)) target = (char *)window_item_get_target(item); if (IsNULLorEmpty(target)) { printtext(server, item != NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Please define nick/#channel. Usage: /key [-<server tag>] [<nick | #channel>]"); return; } server = cmd_options_get_server("key", optlist, server); if (server == NULL || !server->connected) cmd_param_error(CMDERR_NOT_CONNECTED); target = (char *)g_strchomp(target); if (getIniSectionForContact(server, target, contactName) == FALSE) return; if (getContactKey(contactName, theKey) == FALSE) { ZeroMemory(theKey, KEYBUF_SIZE); printtext(server, item != NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s@%s not found or invalid!", target, server->tag); return; } printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s@%s: %s", target, server->tag, theKey); ZeroMemory(theKey, KEYBUF_SIZE); }
/* * New command: /notice+ <nick/#channel> <notice message> */ void cmd_crypt_notice(const char *data, SERVER_REC * server, WI_ITEM_REC * item) { char bf_dest[1000] = "", *msg; const char *target; void *free_arg = NULL; if (data == NULL || (strlen(data) < 3)) goto notice_error; if (!cmd_get_params(data, &free_arg, 1, &target)) goto notice_error; msg = strchr(data, ' '); if (IsNULLorEmpty(target) || IsNULLorEmpty(msg)) goto notice_error; msg++; // point to the notice message // generally refuse a notice size of more than 512 byte, as everything above will never arrive complete anyway if (strlen(msg) >= 512) { printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /notice+ \002error\002: message argument exceeds buffer size!"); return; } if (FiSH_encrypt(server, msg, target, bf_dest) == 0) { printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /notice+ \002error\002: Encryption disabled or no key found for %s.", target); return; } bf_dest[512] = '\0'; irc_send_cmdv((IRC_SERVER_REC *) server, "NOTICE %s :%s\n", target, bf_dest); signal_emit("message irc own_notice", 3, server, msg, target); cmd_params_free(free_arg); return; notice_error: if (free_arg) cmd_params_free(free_arg); printtext(server, item != NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Usage: /notice+ <nick/#channel> <notice message>"); }
void cmd_delkey(const char *data, SERVER_REC * server, WI_ITEM_REC * item) { GHashTable *optlist; char *target; char contactName[CONTACT_SIZE] = ""; void *free_arg; if (!cmd_get_params(data, &free_arg, 1 | PARAM_FLAG_OPTIONS | PARAM_FLAG_UNKNOWN_OPTIONS | PARAM_FLAG_GETREST, "delkey", &optlist, &target)) return; if (item != NULL && IsNULLorEmpty(target)) target = (char *)window_item_get_target(item); if (IsNULLorEmpty(target)) { printtext(server, item != NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 No parameters. Usage: /delkey [-<server tag>] [<nick | #channel>]"); return; } server = cmd_options_get_server("delkey", optlist, server); if (server == NULL || !server->connected) cmd_param_error(CMDERR_NOT_CONNECTED); target = (char *)g_strchomp(target); if (getIniSectionForContact(server, target, contactName) == FALSE) return; if (deleteIniValue(contactName, "key", iniPath) == 1) { printtext(server, item != NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s@%s successfully removed!", target, server->tag); } else { printtext(server, item != NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 No key found for %s@%s", target, server->tag); } }
/* * Encrypt a message and store in bf_dest (using key for target) * @param server * @param msg_ptr * @param target * @param bf_dest * @return 1 if everything ok 0 if not */ int FiSH_encrypt(const SERVER_REC *server, const char *msg_ptr, const char *target, char *bf_dest) { char theKey[KEYBUF_SIZE]=""; char contactName[CONTACT_SIZE]=""; if (IsNULLorEmpty(msg_ptr) || bf_dest==NULL || IsNULLorEmpty(target)) return 0; if (GetBlowIniSwitch("FiSH", "process_outgoing", "1") == 0) return 0; if (GetIniSectionForContact(server, target, contactName)==FALSE) return 0; if (LoadKeyForContact(contactName, theKey)==FALSE) return 0; strcpy(bf_dest, "+OK "); encrypt_string(theKey, msg_ptr, bf_dest+4, strlen(msg_ptr)); ZeroMemory(theKey, KEYBUF_SIZE); return 1; }
void raw_handler(SERVER_REC * server, char *data) { GString *decrypted; char channel[CONTACT_SIZE], *ptr, *ptr_bak; int len; if (IsNULLorEmpty(data)) return; ptr = strchr(data, ' '); // point to command if (ptr == NULL) return; ptr++; if (strncmp(ptr, "332 ", 4) != 0) return; // 332 = TOPIC ptr_bak = ptr; ptr = strchr(ptr, '#'); // point to #channel if (ptr == NULL) { ptr = strchr(ptr_bak, '&'); // point to &channel if (ptr == NULL) { ptr = strchr(ptr_bak, '!'); // point to !channel if (ptr == NULL) return; } } len = strchr(ptr, ' ') - ptr; if (len >= CONTACT_SIZE - 2) return; // channel string too long, something went wrong strncpy(channel, ptr, len); channel[len] = '\0'; ptr = strchr(ptr, ':'); // point to topic msg start if (ptr == NULL) return; ptr++; decrypted = g_string_new(""); if (FiSH_decrypt(server, ptr, channel, decrypted)) { g_string_prepend_len(decrypted, data, strlen(data) - strlen(ptr)); signal_continue(2, server, decrypted->str); ZeroMemory(decrypted->str, decrypted->len); } g_string_free(decrypted, TRUE); }
static void sig_complete_topic_plus(GList **list, WINDOW_REC *window, const char *word, const char *line, int *want_space) { char *p; char *topic; int topic_len; const char *mark; int mark_len; g_return_if_fail(list != NULL); g_return_if_fail(word != NULL); if (*word == '\0' && IS_CHANNEL(window->active)) { topic = g_strdup(CHANNEL(window->active)->topic); if (topic != NULL) { mark = settings_get_str("mark_encrypted"); if (!IsNULLorEmpty(mark)) { topic_len = strlen(topic); mark_len = strlen(mark); if (settings_get_int("mark_position") == 0) { // suffix p = topic + (topic_len - mark_len); if (strncmp(p, mark, mark_len) == 0) { *p = '\0'; // Remove mark } } else { // prefix if (strncmp(topic, mark, mark_len) == 0) { g_memmove(topic, topic + mark_len, topic_len - mark_len); } } } *list = g_list_append(NULL, topic); signal_stop(); } } }
/* * Decrypt a base64 cipher text (using key for target) */ int FiSH_decrypt(const SERVER_REC * serverRec, char *msg_ptr, const char *target, GString* decrypted_msg) { char contactName[CONTACT_SIZE] = ""; char theKey[KEYBUF_SIZE] = ""; char bf_dest[1000] = ""; char myMark[20] = ""; char *recoded; int msg_len, i, mark_broken_block = 0, action_found = 0, markPos; if (IsNULLorEmpty(msg_ptr) || decrypted_msg == NULL || IsNULLorEmpty(target)) return 0; if (settings_get_bool("process_incoming") == 0) return 0; if (strncmp(msg_ptr, "+OK ", 4) == 0) msg_ptr += 4; // TODO: Maybe remove this? else if (strncmp(msg_ptr, "mcps ", 5) == 0) msg_ptr += 5; else return 0; // don't process, blowcrypt-prefix not found // Verify base64 string msg_len = strlen(msg_ptr); if ((strspn(msg_ptr, B64) != (size_t) msg_len) || (msg_len < 12)) return 0; if (getIniSectionForContact(serverRec, target, contactName) == FALSE) return 0; if (getContactKey(contactName, theKey) == FALSE) return 0; // usually a received message does not exceed 512 chars, but we want to prevent evil buffer overflow if (msg_len >= (int)(sizeof(bf_dest) * 1.5)) msg_ptr[(int)(sizeof(bf_dest) * 1.5) - 20] = '\0'; // block-align blowcrypt strings if truncated by IRC server (each block is 12 chars long) // such a truncated block is destroyed and not needed anymore if (msg_len != (msg_len / 12) * 12) { msg_len = (msg_len / 12) * 12; msg_ptr[msg_len] = '\0'; strncpy(myMark, settings_get_str("mark_broken_block"), sizeof(myMark)); if (*myMark == '\0' || isNoChar(*myMark)) mark_broken_block = 0; else mark_broken_block = 1; } decrypt_string(theKey, msg_ptr, bf_dest, msg_len); ZeroMemory(theKey, KEYBUF_SIZE); if (*bf_dest == '\0') return 0; // don't process, decrypted msg is bad // recode message again, last time it was the encrypted message... if (settings_get_bool("recode") && serverRec != NULL) { recoded = recode_in(serverRec, bf_dest, target); if (recoded) { strncpy(bf_dest, recoded, sizeof(bf_dest)); ZeroMemory(recoded, strlen(recoded)); g_free(recoded); } } i = 0; while (bf_dest[i] != 0x0A && bf_dest[i] != 0x0D && bf_dest[i] != '\0') i++; bf_dest[i] = '\0'; // in case of wrong key, decrypted message might have control characters -> cut message if (strncmp(bf_dest, "\001ACTION ", 8) == 0) { // ACTION message found if (bf_dest[i - 1] == '\001') bf_dest[i - 1] = '\0'; // remove 0x01 control character action_found = 1; } // append broken-block-mark? if (mark_broken_block) strcat(bf_dest, myMark); // append crypt-mark? strncpy(myMark, settings_get_str("mark_encrypted"), sizeof(myMark)); if (*myMark != '\0') { markPos = settings_get_int("mark_position"); if (markPos == 0 || action_found) strcat(bf_dest, myMark); // append mark at the end (default for ACTION messages) else { // prefix mark i = strlen(myMark); memmove(bf_dest + i, bf_dest, strlen(bf_dest) + 1); strncpy(bf_dest, myMark, i); } } g_string_assign(decrypted_msg, bf_dest); ZeroMemory(bf_dest, sizeof(bf_dest)); return 1; }
/* * Decrypt a base64 cipher text (using key for target) */ int FiSH_decrypt(const SERVER_REC *server, char *msg_ptr, char *msg_bak, const char *target) { char contactName[CONTACT_SIZE]="", theKey[KEYBUF_SIZE]="", bf_dest[1000]=""; char myMark[20]="", markPos[20]="", *recoded; int msg_len, i, mark_broken_block=0, action_found=0; if (IsNULLorEmpty(msg_ptr) || msg_bak==NULL || IsNULLorEmpty(target)) return 0; if (GetBlowIniSwitch("FiSH", "process_incoming", "1") == 0) return 0; if (strncmp(msg_ptr, "+OK ", 4)==0) msg_ptr += 4; else if (strncmp(msg_ptr, "mcps ", 5)==0) msg_ptr += 5; else return 0; // don't process, blowcrypt-prefix not found // Verify base64 string msg_len=strlen(msg_ptr); if ((strspn(msg_ptr, B64) != (size_t)msg_len) || (msg_len < 12)) return 0; if (GetIniSectionForContact(server, target, contactName)==FALSE) return 0; if (LoadKeyForContact(contactName, theKey)==FALSE) return 0; // usually a received message does not exceed 512 chars, but we want to prevent evil buffer overflow if (msg_len >= (int)(sizeof(bf_dest)*1.5)) msg_ptr[(int)(sizeof(bf_dest)*1.5)-20]='\0'; // block-align blowcrypt strings if truncated by IRC server (each block is 12 chars long) // such a truncated block is destroyed and not needed anymore if (msg_len != (msg_len/12)*12) { msg_len=(msg_len/12)*12; msg_ptr[msg_len]='\0'; GetPrivateProfileString("FiSH", "mark_broken_block", " \002&\002", myMark, sizeof(myMark), iniPath); if (*myMark=='\0' || isNoChar(*myMark)) mark_broken_block=0; else mark_broken_block=1; } decrypt_string(theKey, msg_ptr, bf_dest, msg_len); ZeroMemory(theKey, KEYBUF_SIZE); if (*bf_dest=='\0') return 0; // don't process, decrypted msg is bad #ifdef FiSH_USE_IRSSI_RECODE // recode message again, last time it was the encrypted message... if (settings_get_bool("recode") && server!=NULL) { recoded = recode_in(server, bf_dest, target); if (recoded) { strncpy(bf_dest, recoded, sizeof(bf_dest)); ZeroMemory(recoded, strlen(recoded)); g_free(recoded); } } #endif i=0; while (bf_dest[i] != 0x0A && bf_dest[i] != 0x0D && bf_dest[i] != '\0') i++; bf_dest[i]='\0'; // in case of wrong key, decrypted message might have control characters -> cut message if (strncmp(bf_dest, "\001ACTION ", 8)==0) { // ACTION message found if (bf_dest[i-1] == '\001') bf_dest[i-1] = '\0'; // remove 0x01 control character action_found = 1; } // append broken-block-mark? if (mark_broken_block) strcat(bf_dest, myMark); // append crypt-mark? if (GetBlowIniSwitch(contactName, "mark_encrypted", "1") != 0) { GetPrivateProfileString("FiSH", "mark_encrypted", "", myMark, sizeof(myMark), iniPath); // global setting if (*myMark != '\0') { GetPrivateProfileString("FiSH", "mark_position", "0", markPos, sizeof(markPos), iniPath); if (*markPos=='0' || action_found) strcat(bf_dest, myMark); // append mark at the end (default for ACTION messages) else { // prefix mark i=strlen(myMark); memmove(bf_dest+i, bf_dest, strlen(bf_dest)+1); strncpy(bf_dest, myMark, i); } } } strcpy(msg_bak, bf_dest); // copy decrypted message back (overwriting the base64 cipher text) ZeroMemory(bf_dest, sizeof(bf_dest)); return 1; }