// FiSH encryption module for irssi, v0.99 #include "FiSH.h" // encrypt a message and store in bf_dest (using key for target) static int FiSH_encrypt(char *msg_ptr, const char *target, char *bf_dest) { unsigned char contactName[100]="", theKey[150]="", ini_outgoing[10]=""; if(msg_ptr==NULL || *msg_ptr=='\0' || bf_dest==NULL || target==NULL || *target=='\0') return 0; GetPrivateProfileString("FiSH", "process_outgoing", "1", ini_outgoing, sizeof(ini_outgoing), iniPath); if(*ini_outgoing=='0' || *ini_outgoing=='n' || *ini_outgoing=='N') return 0; if(strlen(target) >= sizeof(contactName)) return 0; // buffer too small strcpy(contactName, target); FixContactName(contactName); // replace '[' and ']' with '~' in contact name GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath); if(strlen(theKey) < 4) return 0; // don't process, key not found in ini if(strncmp(theKey, "+OK ", 4)==0) { // key is encrypted, lets decrypt decrypt_string((char *)iniKey, theKey+4, theKey, strlen(theKey+4)); if(*theKey=='\0') { // don't process, decrypted key is bad ZeroMemory(theKey, sizeof(theKey)); return 0; } } encrypt_string(theKey, msg_ptr, bf_dest, strlen(msg_ptr)); ZeroMemory(theKey, sizeof(theKey)); return 1; } // decrypt a base64 cipher text (using key for target) static int FiSH_decrypt(char *msg_ptr, char *msg_bak, const char *target) { unsigned char contactName[100], theKey[150]="", bf_dest[1000]=""; unsigned char ini_incoming[10]="", myMark[20]="", mark_pos[20]=""; unsigned int msg_len, i, mark_broken_block=0; if(msg_ptr==NULL || *msg_ptr=='\0' || msg_bak==NULL || target==NULL || *target=='\0') return 0; GetPrivateProfileString("FiSH", "process_incoming", "1", ini_incoming, sizeof(ini_incoming), iniPath); if(*ini_incoming=='0' || *ini_incoming=='n' || *ini_incoming=='N') 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 strcpy(contactName, target); FixContactName(contactName); // replace '[' and ']' with '~' in contact name GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath); if(*theKey=='\0' || strlen(theKey)<4) return 0; // don't process, key not found in ini if(strncmp(theKey, "+OK ", 4)==0) { // key is encrypted, lets decrypt decrypt_string((char *)iniKey, theKey+4, theKey, strlen(theKey+4)); if(*theKey=='\0') { // don't process, decrypted key is bad ZeroMemory(theKey, sizeof(theKey)); return 0; } } // Verify base64 string msg_len=strlen(msg_ptr); if((strspn(msg_ptr, B64) != msg_len) || (msg_len < 12)) 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' || *myMark=='0' || *myMark=='n' || *myMark=='N') mark_broken_block=0; else mark_broken_block=1; } decrypt_string(theKey, msg_ptr, bf_dest, msg_len); ZeroMemory(theKey, sizeof(theKey)); 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(*bf_dest=='\0') return 0; // don't process, decrypted msg is bad // append broken-block-mark? if(mark_broken_block) strcat(bf_dest, myMark); // append crypt-mark? GetPrivateProfileString(contactName, "mark_encrypted", "1", myMark, sizeof(myMark), iniPath); if(*myMark!='0' && *myMark!='n' && *myMark!='N') { GetPrivateProfileString("FiSH", "mark_encrypted", "", myMark, sizeof(myMark), iniPath); // global setting if(*myMark != '\0') { GetPrivateProfileString("FiSH", "mark_position", "0", mark_pos, sizeof(mark_pos), iniPath); if(*mark_pos=='0') strcat(bf_dest, myMark); //append mark at the end 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; } static void decrypt_incoming(SERVER_REC *server, char *msg, const char *nick, const char *addr, const char *target) { unsigned char contactName[100]="", *msg_bak; if(msg==NULL || target==NULL || nick==NULL) return; else if(strncmp(msg, "+OK ", 4)!=0 && strncmp(msg, "mcps ", 5)!=0 && strcmp(nick, "-psyBNC")!=0) return; // don't process, blowcrypt-prefix not found msg_bak=(unsigned char *)msg; // this is needed, because of psyBNC //channel? if(*target=='#' || *target=='&') strcpy(contactName, target); else if(strcmp(nick, "-psyBNC")==0) { msg=strstr(msg, " :(")+3; // points to nick!ident@host in psybnc log if(msg==(char *)3) return; ExtractRnick(contactName, msg); msg=strchr(msg, ' ')+1; if(msg==(char *)1) return; if((strncmp(msg, "+OK ", 4)!=0) && (strncmp(msg, "mcps ", 5)!=0)) return; } else strcpy(contactName, nick); if(FiSH_decrypt(msg, msg, contactName)) { signal_stop(); signal_emit(signal_get_emitted(), 5, server, msg_bak, nick, addr, target); } } static void encrypt_outgoing(SERVER_REC *server, char *msg, char *target, char *orig_target) { unsigned char bf_dest[800]=""; unsigned char contactName[100]="", plain_prefix[20]=""; unsigned char myMark[20]="", mark_pos[20]=""; unsigned int i; // 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'; // plain-prefix in msg found? GetPrivateProfileString("FiSH", "plain_prefix", "+p ", plain_prefix, sizeof(plain_prefix), iniPath); if(*plain_prefix != '\0') { i=strlen(plain_prefix); if(strncasecmp(msg, plain_prefix, i)==0) { irc_send_cmdv(server, "PRIVMSG %s :%s", target, msg+i); return; } } if(FiSH_encrypt(msg, target, bf_dest)==0) { // send plain-text (no key found) irc_send_cmdv(server, "PRIVMSG %s :%s", target, msg); return; } bf_dest[512]='\0'; irc_send_cmdv(server, "PRIVMSG %s :%s %s\n", target, "+OK", bf_dest); strcpy(bf_dest, msg); // append crypt-mark? GetPrivateProfileString(contactName, "mark_encrypted", "1", myMark, sizeof(myMark), iniPath); if(*myMark!='0' && *myMark!='n' && *myMark!='N') { GetPrivateProfileString("FiSH", "mark_encrypted", "", myMark, sizeof(myMark), iniPath); // global setting if(*myMark != '\0') { GetPrivateProfileString("FiSH", "mark_position", "0", mark_pos, sizeof(mark_pos), iniPath); if(*mark_pos=='0') strcat(bf_dest, myMark); //append mark at the end else { // prefix mark i=strlen(myMark); memmove(bf_dest+i, bf_dest, strlen(bf_dest)+1); strncpy(bf_dest, myMark, i); } } } signal_continue(4, server, bf_dest, target, orig_target); ZeroMemory(bf_dest, sizeof(bf_dest)); return; } static void notice_incoming(SERVER_REC *server, char *msg, char *nick, char *address, char *target) { // decrypt NOTICE messages (and forward DH1080 key-exchange) unsigned char *contactPtr; if(strncmp(msg, "DH1024_", 7)==0) { irc_send_cmdv(server, "NOTICE %s :Received \002old DH1024\002 public key from you! Please update to latest version: http://fish.sekure.us\n", nick); printtext(server, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Received \002old DH1024\002 public key from %s! Telling him to update...", nick); return; } if(strncmp(msg, "DH1080_", 7)==0) { DH1080_received(server, msg, nick, address, target); return; } if(*target=='#' || *target=='&') contactPtr=target; else contactPtr=nick; if(FiSH_decrypt(msg, msg, contactPtr)) { signal_stop(); signal_emit(signal_get_emitted(), 5, server, msg, nick, address, target); } } static void decrypt_action(SERVER_REC *server, char *msg, char *nick, char *address, char *target) { if(FiSH_decrypt(msg, msg, target)) { signal_stop(); signal_emit(signal_get_emitted(), 5, server, msg, nick, address, target); } } static void decrypt_topic(SERVER_REC *server, char *channel, char *topic, char *nick, char *address) { if(FiSH_decrypt(topic, topic, channel)) { signal_stop(); signal_emit(signal_get_emitted(), 5, server, channel, topic, nick, address); } } static void topic_changed(CHANNEL_REC *chan_rec) { FiSH_decrypt(chan_rec->topic, chan_rec->topic, chan_rec->name); } static void raw_handler(SERVER_REC *server, char *data) { char channel[100], *ptr, *ptr_bak; int len; if(data==NULL || *data=='\0') 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_bak==NULL) return; } len=strchr(ptr, ' ')-ptr; if(len >= sizeof(channel)-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(ptr, ptr, channel); } static void encrypt_notice(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { // New command: /notice+ unsigned char bf_dest[1000]=""; const char *target; unsigned char *msg; void *free_arg=NULL; if(data==NULL || (strlen(data) < 3)) goto notice_error; if(!cmd_get_params(data, &free_arg, 2, &target, &msg)) goto notice_error; if (target==NULL || *target=='\0' || msg==NULL || *msg=='\0') goto notice_error; // 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+ error: message argument exceeds buffer size!"); goto notice_error; } if(FiSH_encrypt(msg, target, bf_dest)==0) { printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /notice+ error: Encryption disabled or no key found for %s.", target); goto notice_error; } bf_dest[512]='\0'; irc_send_cmdv(server, "NOTICE %s :%s %s\n", target, "+OK", 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+ "); } // set encrypted topic for current channel, irssi syntax: /topic+ static void command_crypt_TOPIC(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { unsigned char bf_dest[1000]=""; const char *target; if(data==0 || *data=='\0') goto topic_error; if(item!=NULL) target=window_item_get_target(item); else goto topic_error; if(*target!='#' && *target!='&') { printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 Please change to the channel window where you want to set the topic!"); goto topic_error; } // generally refuse a topic size of more than 512 byte, as everything above will never arrive complete anyway if(strlen(data) >= 512) { printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /topic+ error: topic length exceeds buffer size!"); goto topic_error; } // encrypt a message (using key for target) if(FiSH_encrypt((char *)data, target, bf_dest)==0) { printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 /topic+ error: Encryption disabled or no key found for %s.", target); goto topic_error; } bf_dest[512]='\0'; irc_send_cmdv(server, "TOPIC %s :%s %s\n", target, "+OK", bf_dest); return; topic_error: printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Usage: /topic+ "); } static void command_helpfish(const char *arg, SERVER_REC *server, WI_ITEM_REC *item) { printtext(NULL, NULL, MSGLEVEL_CRAP, "\n\002FiSH HELP:\002 For more information read FiSH-irssi.txt :)\n\n" " /topic+ \n" " /notice+ \n" " /key []\n" " /setkey [] \n" " /delkey \n" " /keyx [] (DH1080 KeyXchange)\n" " /setinipw \n" " /unsetinipw\n"); } static void command_setinipw(const char *iniPW, SERVER_REC *server, WI_ITEM_REC *item) { unsigned int i=0, pw_len, re_enc=0; unsigned char B64digest[50], SHA256digest[35]; unsigned char bfKey[512], new_iniKey[100], old_iniKey[100], *fptr, *ok_ptr, line_buf[1000], iniPath_new[300]; FILE *h_ini, *h_ini_new; pw_len=strlen(iniPW); if(pw_len < 1 || pw_len > sizeof(new_iniKey)) { printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 No parameters. Usage: /setinipw "); return; } strfcpy(new_iniKey, (char *)iniPW); ZeroMemory(iniPW, pw_len); pw_len=strlen(new_iniKey); if(pw_len < 8) { printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Password too short, at least 8 characters needed! Usage: /setinipw "); return; } SHA256_memory(new_iniKey, pw_len, SHA256digest); ZeroMemory(new_iniKey, sizeof(new_iniKey)); for(i=0;i<40872;i++) SHA256_memory(SHA256digest, 32, SHA256digest); htob64(SHA256digest, B64digest, 32); strcpy(old_iniKey, iniKey); if(unsetiniFlag) strcpy(iniKey, default_iniKey); // unsetinipw else strcpy(iniKey, B64digest); // this is used for encrypting blow.ini for(i=0;i<30752;i++) SHA256_memory(SHA256digest, 32, SHA256digest); htob64(SHA256digest, B64digest, 32); // this is used to verify the entered password ZeroMemory(SHA256digest, sizeof(SHA256digest)); // re-encrypt blow.ini with new password strcpy(iniPath_new, iniPath); strcat(iniPath_new, "_new"); h_ini_new=fopen(iniPath_new, "w"); h_ini=fopen(iniPath,"r"); if(h_ini && h_ini_new) { while (!feof(h_ini)) { fptr=fgets(line_buf, sizeof(line_buf)-2, h_ini); if(fptr) { ok_ptr=strstr(line_buf, "+OK "); if(ok_ptr) { re_enc=1; strtok(ok_ptr+4, " \n\r"); decrypt_string(old_iniKey, ok_ptr+4, bfKey, strlen(ok_ptr+4)); ZeroMemory(ok_ptr+4, strlen(ok_ptr+4)+1); encrypt_string(iniKey, bfKey, ok_ptr+4, strlen(bfKey)); strcat(line_buf, "\n"); } if(fprintf(h_ini_new, "%s", line_buf) < 0) { ZeroMemory(B64digest, sizeof(B64digest)); ZeroMemory(bfKey, sizeof(bfKey)); ZeroMemory(line_buf, sizeof(line_buf)); ZeroMemory(old_iniKey, sizeof(old_iniKey)); fclose(h_ini); fclose(h_ini_new); remove(iniPath_new); printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH ERROR:\002 Unable to write new blow.ini, probably out of disc space."); return; } } } ZeroMemory(bfKey, sizeof(bfKey)); ZeroMemory(line_buf, sizeof(line_buf)); ZeroMemory(old_iniKey, sizeof(old_iniKey)); fclose(h_ini); fclose(h_ini_new); remove(iniPath); rename(iniPath_new, iniPath); } else return; if(WritePrivateProfileString("FiSH", "ini_password_Hash", B64digest, iniPath) == -1) { ZeroMemory(B64digest, sizeof(B64digest)); 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."); return; } ZeroMemory(B64digest, sizeof(B64digest)); if(re_enc) printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH: Re-encrypted blow.ini\002 with new password."); if(!unsetiniFlag) printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 blow.ini password hash saved."); } // Change back to default blow.ini password, irssi syntax: /unsetinipw static void command_unsetinipw(const char *arg, SERVER_REC *server, WI_ITEM_REC *item) { unsetiniFlag=1; command_setinipw("Some_boogie_dummy_key", server, item); unsetiniFlag=0; if(WritePrivateProfileString("FiSH", "ini_password_Hash", "\0", iniPath) == -1) { 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."); return; } printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Changed back to default blow.ini password, you won't have to enter it on start-up anymore!"); } static void command_setkey(const char *data, SERVER_REC *server, WI_ITEM_REC *item) { unsigned char contactName[100]="", encryptedKey[150]=""; const char *target, *key; void *free_arg; if (data==NULL || *data=='\0') { printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 No parameters. Usage: /setkey [] "); return; } if (!cmd_get_params(data, &free_arg, 2, &target, &key)) return; if (*target=='\0') { printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 No parameters. Usage: /setkey [] "); cmd_params_free(free_arg); return; } 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 [] "); cmd_params_free(free_arg); return; } } strcpy(contactName, target); FixContactName(contactName); encrypt_key((char *)key, encryptedKey); 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 successfully set!", target); cmd_params_free(free_arg); } static void command_delkey(const char *target, SERVER_REC *server, WI_ITEM_REC *item) { unsigned char contactName[100]=""; if (target==NULL || *target=='\0') { printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 No parameters. Usage: /delkey "); return; } strfcpy(contactName, (char *)target); FixContactName(contactName); if(WritePrivateProfileString(contactName, "key", "\0", iniPath) == -1) { 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."); return; } printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s successfully removed!", target); } static void command_key(const char *target, SERVER_REC *server, WI_ITEM_REC *item) { unsigned char contactName[100]="", theKey[150]=""; if(target==NULL || *target=='\0') { if (item!=NULL) target=window_item_get_target(item); else { printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Please define nick/#channel. Usage: /key "); return; } } strfcpy(contactName, (char *)target); FixContactName(contactName); GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath); if(*theKey=='\0' || strlen(theKey)<4) { // don't process, key not found in ini printtext(server, item!=NULL ? window_item_get_target(item) : NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s not found!", target); return; } if(strncmp(theKey, "+OK ", 4)==0) { // key is encrypted, lets decrypt decrypt_string((char *)iniKey, theKey+4, theKey, strlen(theKey+4)); if(*theKey=='\0') { // don't process, decrypted key is bad ZeroMemory(theKey, sizeof(theKey)); printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s invalid!", target); return; } } printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s: %s", target, theKey); ZeroMemory(theKey, sizeof(theKey)); } static void command_keyx(const char *target, SERVER_REC *server, WI_ITEM_REC *item) { if(target==NULL || *target=='\0') { if(item!=NULL) target = window_item_get_target(item); else { printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Please define nick/#channel. Usage: /keyx "); return; } } if(*target=='#' || *target=='&') { printtext(server, target, MSGLEVEL_CRAP, "\002FiSH:\002 KeyXchange does not work for channels!"); return; } DH1080_gen(g_myPrivKey, g_myPubKey); irc_send_cmdv(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); } static void DH1080_received(SERVER_REC *server, char *msg, char *nick, char *address, char *target) { unsigned int i; char hisPubKey[300], contactName[100]="", encryptedKey[150]=""; if(*target=='&' || *target=='#' || *nick=='#' || *nick=='&') return; // no KeyXchange for channels... i=strlen(msg); if(i<191 || i>195) return; if(strncmp(msg, "DH1080_INIT ", 12)==0) { strcpy(hisPubKey, msg+12); if(strspn(hisPubKey, B64ABC) != strlen(hisPubKey)) return; if(query_find(server, nick)==NULL) { // query window not found, lets create one keyx_query_created=1; irc_query_create(server->tag, nick, TRUE); keyx_query_created=0; } printtext(server, nick, MSGLEVEL_CRAP, "\002FiSH:\002 Received DH1080 public key from %s, sending mine...", nick); DH1080_gen(g_myPrivKey, g_myPubKey); irc_send_cmdv(server, "NOTICE %s :%s %s", nick, "DH1080_FINISH", g_myPubKey); } else if(strncmp(msg, "DH1080_FINISH ", 14)==0) strcpy(hisPubKey, msg+14); else return; if(DH1080_comp(g_myPrivKey, hisPubKey)==0) return; signal_stop(); strcpy(contactName, nick); FixContactName(contactName); encrypt_key(hisPubKey, encryptedKey); ZeroMemory(hisPubKey, sizeof(hisPubKey)); if(WritePrivateProfileString(contactName, "key", encryptedKey, iniPath) == -1) { ZeroMemory(encryptedKey, sizeof(encryptedKey)); printtext(server, nick, MSGLEVEL_CRAP, "\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied."); return; } ZeroMemory(encryptedKey, sizeof(encryptedKey)); printtext(server, nick, MSGLEVEL_CRAP, "\002FiSH:\002 Key for %s successfully set!", nick); } static void do_auto_keyx(QUERY_REC *query, int automatic) { // perform auto-keyXchange only for known people unsigned char theKey[150]="", ini_auto_keyx[10], contactName[100]=""; if(keyx_query_created) return; // query was created by FiSH GetPrivateProfileString("FiSH", "auto_keyxchange", "1", ini_auto_keyx, sizeof(ini_auto_keyx), iniPath); if(*ini_auto_keyx=='0' || *ini_auto_keyx=='N' || *ini_auto_keyx=='n') return; strcpy(contactName, query->name); FixContactName(contactName); GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath); if(strlen(theKey) > 4) { ZeroMemory(theKey, sizeof(theKey)); command_keyx(query->name, query->server, NULL); } } static void query_nick_changed(QUERY_REC *query, char *orignick) { // copy key for old nick to use with the new one unsigned char theKey[150]="", ini_nicktracker[10], contactName[100]=""; GetPrivateProfileString("FiSH", "nicktracker", "1", ini_nicktracker, sizeof(ini_nicktracker), iniPath); if(*ini_nicktracker=='0' || *ini_nicktracker=='N' || *ini_nicktracker=='n') return; if(orignick==NULL || strcasecmp(orignick, query->name)==0) return; // same nick, different case? strcpy(contactName, orignick); FixContactName(contactName); GetPrivateProfileString(contactName, "key", "", theKey, sizeof(theKey), iniPath); if(strlen(theKey) < 4) return; // see if there is a key for the old nick strcpy(contactName, query->name); FixContactName(contactName); if(WritePrivateProfileString(contactName, "key", theKey, iniPath) == -1) { ZeroMemory(theKey, sizeof(theKey)); printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH ERROR:\002 Unable to write to blow.ini, probably out of space or permission denied."); return; } ZeroMemory(theKey, sizeof(theKey)); } static void void_send(SERVER_REC *server, const char *target, const char *msg, int target_type) { } static void server_register_fish(SERVER_REC *server) { MODULE_SERVER_REC *rec; if(server==NULL) return; rec = g_new0(MODULE_SERVER_REC, 1); MODULE_DATA_SET(server, rec); rec->orig_send_message = server->send_message; server->send_message = void_send; } static void server_unregister_fish(SERVER_REC *server) { MODULE_SERVER_REC *rec; if(server==NULL) return; rec = MODULE_DATA(server); if(rec==NULL) return; server->send_message = rec->orig_send_message; MODULE_DATA_UNSET(server); g_free(rec); } void fish_init(void) { unsigned char iniPasswordHash[50], SHA256digest[35], B64digest[50], *iniPass_ptr; unsigned int i; g_slist_foreach(servers, (GFunc) server_register_fish, NULL); strcpy(iniPath, get_irssi_config()); // path to irssi config file strcpy(tempPath, iniPath); strcpy(strrchr(iniPath, '/'), blow_ini); strcpy(strrchr(tempPath, '/'), "/temp_FiSH.$$$"); GetPrivateProfileString("FiSH", "ini_Password_hash", "0", iniPasswordHash, sizeof(iniPasswordHash), iniPath); if(strlen(iniPasswordHash) == 43) { iniPass_ptr = getpass(" --> Please enter your blow.ini password: "); strcpy(iniKey, iniPass_ptr); ZeroMemory(iniPass_ptr, strlen(iniPass_ptr)); irssi_redraw(); // getpass() screws irssi GUI, lets redraw! SHA256_memory(iniKey, strlen(iniKey), SHA256digest); for(i=0;i<40872;i++) SHA256_memory(SHA256digest, 32, SHA256digest); htob64(SHA256digest, B64digest, 32); strcpy(iniKey, B64digest); // this is used for encrypting blow.ini for(i=0;i<30752;i++) SHA256_memory(SHA256digest, 32, SHA256digest); htob64(SHA256digest, B64digest, 32); // this is used to verify the entered password if(strcmp(B64digest, iniPasswordHash) != 0) { printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Wrong blow.ini password entered, try again..."); printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH module NOT loaded.\002"); return; } printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Correct blow.ini password entered, lets go!"); } else { strcpy(iniKey, default_iniKey); printtext(NULL, NULL, MSGLEVEL_CRAP, "\002FiSH:\002 Using default password to decrypt blow.ini... Try /setinipw to set a custom password."); } signal_add_first("message private", (SIGNAL_FUNC) decrypt_incoming); signal_add_first("message public", (SIGNAL_FUNC) decrypt_incoming); signal_add_first("message irc notice", (SIGNAL_FUNC) notice_incoming); signal_add_first("message irc action", (SIGNAL_FUNC) decrypt_action); signal_add_first("message own_private", (SIGNAL_FUNC) encrypt_outgoing); signal_add_first("message own_public", (SIGNAL_FUNC) encrypt_outgoing); signal_add_first("channel topic changed", (SIGNAL_FUNC) topic_changed); signal_add_first("message topic", (SIGNAL_FUNC) decrypt_topic); signal_add_first("server incoming", (SIGNAL_FUNC) raw_handler); signal_add("server connected", (SIGNAL_FUNC) server_register_fish); signal_add("server disconnected", (SIGNAL_FUNC) server_unregister_fish); signal_add("query created", (SIGNAL_FUNC) do_auto_keyx); signal_add("query nick changed", (SIGNAL_FUNC) query_nick_changed); command_bind("topic+", NULL, (SIGNAL_FUNC) command_crypt_TOPIC); command_bind("notice+", NULL, (SIGNAL_FUNC) encrypt_notice); command_bind("notfish", NULL, (SIGNAL_FUNC) encrypt_notice); command_bind("setkey", NULL, (SIGNAL_FUNC) command_setkey); command_bind("delkey", NULL, (SIGNAL_FUNC) command_delkey); command_bind("key", NULL, (SIGNAL_FUNC) command_key); command_bind("keyx", NULL, (SIGNAL_FUNC) command_keyx); command_bind("setinipw", NULL, (SIGNAL_FUNC) command_setinipw); command_bind("unsetinipw", NULL, (SIGNAL_FUNC) command_unsetinipw); command_bind("fishhelp", NULL, (SIGNAL_FUNC) command_helpfish); command_bind("helpfish", NULL, (SIGNAL_FUNC) command_helpfish); printtext(NULL, NULL, MSGLEVEL_CLIENTNOTICE, "FiSH v0.99 - encryption module for irssi loaded! URL: http://fish.sekure.us\n" "Try /helpfish or /fishhelp for a short command overview"); module_register("fish", "core"); } void fish_deinit(void) { g_slist_foreach(servers, (GFunc) server_unregister_fish, NULL); signal_remove("message private", (SIGNAL_FUNC) decrypt_incoming); signal_remove("message public", (SIGNAL_FUNC) decrypt_incoming); signal_remove("message irc notice", (SIGNAL_FUNC) notice_incoming); signal_remove("message irc action", (SIGNAL_FUNC) decrypt_action); signal_remove("message own_private", (SIGNAL_FUNC) encrypt_outgoing); signal_remove("message own_public", (SIGNAL_FUNC) encrypt_outgoing); signal_remove("channel topic changed", (SIGNAL_FUNC) topic_changed); signal_remove("message topic", (SIGNAL_FUNC) decrypt_topic); signal_remove("server incoming", (SIGNAL_FUNC) raw_handler); signal_remove("server connected", (SIGNAL_FUNC) server_register_fish); signal_remove("server disconnected", (SIGNAL_FUNC) server_unregister_fish); signal_remove("query created", (SIGNAL_FUNC) do_auto_keyx); signal_remove("query nick changed", (SIGNAL_FUNC) query_nick_changed); command_unbind("topic+", (SIGNAL_FUNC) command_crypt_TOPIC); command_unbind("notice+", (SIGNAL_FUNC) encrypt_notice); command_unbind("notfish", (SIGNAL_FUNC) encrypt_notice); command_unbind("setkey", (SIGNAL_FUNC) command_setkey); command_unbind("delkey", (SIGNAL_FUNC) command_delkey); command_unbind("key", (SIGNAL_FUNC) command_key); command_unbind("keyx", (SIGNAL_FUNC) command_keyx); command_unbind("setinipw", (SIGNAL_FUNC) command_setinipw); command_unbind("unsetinipw", (SIGNAL_FUNC) command_unsetinipw); command_unbind("fishhelp", (SIGNAL_FUNC) command_helpfish); command_unbind("helpfish", (SIGNAL_FUNC) command_helpfish); } // :someone!ident@host.net PRIVMSG leetguy :Some Text -> Result: Rnick="someone" int ExtractRnick(char *Rnick, char *incoming_msg) // needs direct pointer to "nick@host" or ":nick@host" { int k=0; if(*incoming_msg == ':') incoming_msg++; while(*incoming_msg!='!' && *incoming_msg!='\0') { Rnick[k]=*incoming_msg; incoming_msg++; k++; } Rnick[k]='\0'; if (*Rnick != '\0') return TRUE; else return FALSE; } // replace '[' and ']' from nick/channel with '~' (else problems with .ini files) void FixContactName(char *contactName) { while(*contactName != '\0') { if((*contactName == '[') || (*contactName == ']')) *contactName='~'; contactName++; } } void memXOR(unsigned char *s1, const unsigned char *s2, int n) { while(n--) *s1++ ^= *s2++; } char *strfcpy(unsigned char *dest, char *buffer) // removes leading and trailing blanks from string { int i=0, k=strlen(buffer); while(buffer[i]==' ') i++; while(buffer[k-1]==' ') k--; buffer[k]=0; strcpy(dest, buffer+i); return dest; }