diff options
author | ailin-nemui <ailin-nemui@users.noreply.github.com> | 2018-03-31 13:04:35 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-03-31 13:04:35 +0200 |
commit | 0c1db8f2aee4aa5040a16518adb71f57dc953985 (patch) | |
tree | 4c51838a1b7aa278727b9de7f3b1663120b75e62 /src/otr/otr-ops.c | |
parent | 9fa8c32b9e2f80e308d1ed780490f6d2dfd6bb05 (diff) | |
parent | 0b2f5b0a1e30932b07245c508a673eb0e9b6ab37 (diff) | |
download | irssi-0c1db8f2aee4aa5040a16518adb71f57dc953985.zip |
Merge pull request #854 from irssi/ahf/otr
OTR support, take 2
Diffstat (limited to 'src/otr/otr-ops.c')
-rw-r--r-- | src/otr/otr-ops.c | 362 |
1 files changed, 362 insertions, 0 deletions
diff --git a/src/otr/otr-ops.c b/src/otr/otr-ops.c new file mode 100644 index 00000000..4e8c4e81 --- /dev/null +++ b/src/otr/otr-ops.c @@ -0,0 +1,362 @@ +/* + * Off-the-Record Messaging (OTR) modules for IRC + * Copyright (C) 2008 Uli Meis <a.sporto+bee@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA + */ + +#include "common.h" + +#include "signals.h" +#include "levels.h" +#include "printtext.h" +#include "fe-windows.h" + +#include "key.h" +#include "module.h" +#include "otr-formats.h" +#include "irssi-otr.h" + +static OtrlPolicy OTR_DEFAULT_POLICY = OTRL_POLICY_MANUAL | OTRL_POLICY_WHITESPACE_START_AKE; + +/* + * Return default policy for now. + */ +static OtrlPolicy ops_policy(void *opdata, ConnContext *context) +{ + return OTR_DEFAULT_POLICY; +} + +/* + * Request for key generation. + * + * The lib actually expects us to be finished before the call returns. Since + * this can take more than an hour on some systems there isn't even a point in + * trying... + */ +static void ops_create_privkey(void *opdata, const char *accountname, + const char *protocol) +{ + key_gen_run(user_state_global, accountname); +} + +/* + * Inject OTR message. + */ +static void ops_inject_msg(void *opdata, const char *accountname, + const char *protocol, const char *recipient, const char *message) +{ + SERVER_REC *server = opdata; + + IRSSI_OTR_DEBUG("Inject msg:\n[%s]", message); + otr_send_message(server, recipient, message); +} + +/* + * Gone secure. + */ +static void ops_secure(void *opdata, ConnContext *context) +{ + char ownfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; + char peerfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; + SERVER_REC *server = opdata; + struct otr_peer_context *opc; + + g_return_if_fail(context != NULL); + /* This should *really* not happened */ + g_return_if_fail(context->msgstate == OTRL_MSGSTATE_ENCRYPTED); + + printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_SESSION_SECURE); + otr_status_change(server, context->username, OTR_STATUS_GONE_SECURE); + + opc = context->app_data; + opc->active_fingerprint = context->active_fingerprint; + + if (otrl_context_is_fingerprint_trusted(context->active_fingerprint)) { + /* Secure and trusted */ + return; + } + + /* Not authenticated. Let's print out the fingerprints for comparison. */ + otrl_privkey_hash_to_human(peerfp, context->active_fingerprint->fingerprint); + otrl_privkey_fingerprint(user_state_global->otr_state, ownfp, context->accountname, OTR_PROTOCOL_ID); + + printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_SESSION_UNAUTHENTICATED_WARNING); + printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_FP_INFO, server->nick, ownfp); + printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_FP_INFO, context->username, peerfp); +} + +/* + * Gone insecure. + */ +static void ops_insecure(void *opdata, ConnContext *context) +{ + SERVER_REC *server = opdata; + + printformat(server, context->username, MSGLEVEL_CLIENTCRAP, TXT_OTR_SESSION_INSECURE); + otr_status_change(server, context->username, OTR_STATUS_GONE_INSECURE); +} + +/* + * Really critical with IRC. Unfortunately, we can't tell our peer which size + * to use. + */ +static int ops_max_msg(void *opdata, ConnContext *context) +{ + return OTR_MAX_MSG_SIZE; +} + +static void ops_handle_msg_event(void *opdata, OtrlMessageEvent msg_event, ConnContext *context, const char *message, gcry_error_t err) +{ + SERVER_REC *server = opdata; + char *username = context->username; + + switch (msg_event) { + case OTRL_MSGEVENT_NONE: + break; + case OTRL_MSGEVENT_ENCRYPTION_REQUIRED: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ENCRYPTION_REQUIRED); + break; + case OTRL_MSGEVENT_ENCRYPTION_ERROR: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ENCRYPTION_ERROR); + break; + case OTRL_MSGEVENT_CONNECTION_ENDED: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ENCRYPTION_ENDED, username); + break; + case OTRL_MSGEVENT_SETUP_ERROR: + if (!err) { + err = GPG_ERR_INV_VALUE; + } + switch (err) { + case GPG_ERR_INV_VALUE: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_MALFORMED, username); + break; + default: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ERROR, gcry_strerror(err)); + break; + } + break; + case OTRL_MSGEVENT_MSG_REFLECTED: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_REFLECTED, username); + break; + case OTRL_MSGEVENT_MSG_RESENT: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_RESENT, username, message); + break; + case OTRL_MSGEVENT_RCVDMSG_NOT_IN_PRIVATE: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_NOT_IN_PRIVATE, username); + break; + case OTRL_MSGEVENT_RCVDMSG_UNREADABLE: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_UNREADABLE, username); + break; + case OTRL_MSGEVENT_RCVDMSG_MALFORMED: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_MALFORMED, username); + break; + case OTRL_MSGEVENT_LOG_HEARTBEAT_RCVD: + IRSSI_OTR_DEBUG("Heartbeat received from %s.", username); + break; + case OTRL_MSGEVENT_LOG_HEARTBEAT_SENT: + IRSSI_OTR_DEBUG("Heartbeat sent to %s.", username); + break; + case OTRL_MSGEVENT_RCVDMSG_GENERAL_ERR: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_ERROR, message); + break; + case OTRL_MSGEVENT_RCVDMSG_UNENCRYPTED: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_UNENCRYPTED, username); + + /* + * This is a hack I found to send the message in a private window of + * the username without creating an infinite loop since the 'message + * private' signal is hijacked in this module. If someone is able to + * clean this up with a more elegant solution, by all means PLEASE + * submit a patch or email me a better way. + */ + signal_remove("message private", (SIGNAL_FUNC) sig_message_private); + signal_emit("message private", 5, server, message, username, server->connrec->address, server->nick); + signal_add_first("message private", (SIGNAL_FUNC) sig_message_private); + break; + case OTRL_MSGEVENT_RCVDMSG_UNRECOGNIZED: + printformat(server, username, MSGLEVEL_CLIENTERROR, TXT_OTR_MSG_UNRECOGNIZED, username); + break; + case OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE: + IRSSI_OTR_DEBUG("%s has sent a message for a different instance.", username); + break; + } +} + +/* + * A context changed. + */ +static void ops_up_ctx_list(void *opdata) +{ + otr_status_change(opdata, NULL, OTR_STATUS_CTX_UPDATE); +} + +/* + * Save fingerprint changes. + */ +static void ops_write_fingerprints(void *data) +{ + key_write_fingerprints(user_state_global); +} + +static int ops_is_logged_in(void *opdata, const char *accountname, const char *protocol, const char *recipient) +{ + int ret; + SERVER_REC *server = opdata; + + /* Logged in? */ + ret = server != NULL; + + IRSSI_OTR_DEBUG("User %s %s logged in", accountname, ret ? "" : "not"); + + return ret; +} + +static void ops_create_instag(void *opdata, const char *accountname, + const char *protocol) +{ + otrl_instag_generate(user_state_global->otr_state, "/dev/null", accountname, protocol); + key_write_instags(user_state_global); +} + +static void ops_smp_event(void *opdata, OtrlSMPEvent smp_event, + ConnContext *context, unsigned short progress_percent, char *question) +{ + SERVER_REC *server = opdata; + const char *from = context->username; + struct otr_peer_context *opc = context->app_data; + + /* + * Without a peer context, we can't update the status bar. Code flow error + * if none is found. This context is created automatically by an otrl_* + * call or if non existent when returned from + * otrl_message_sending/receiving. + */ + g_return_if_fail(opc != NULL); + + opc->smp_event = smp_event; + + switch (smp_event) { + case OTRL_SMPEVENT_ASK_FOR_SECRET: + printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_SECRET_QUESTION, from); + opc->ask_secret = 1; + otr_status_change(server, from, OTR_STATUS_SMP_INCOMING); + break; + case OTRL_SMPEVENT_ASK_FOR_ANSWER: + printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_ANSWER_HEADER, from); + printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_ANSWER_QUESTION, question); + printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_ANSWER_FOOTER); + opc->ask_secret = 1; + otr_status_change(server, from, OTR_STATUS_SMP_INCOMING); + break; + case OTRL_SMPEVENT_IN_PROGRESS: + printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_IN_PROGRESS, from); + otr_status_change(server, from, OTR_STATUS_SMP_FINALIZE); + break; + case OTRL_SMPEVENT_SUCCESS: + printformat(server, from, MSGLEVEL_CLIENTCRAP, TXT_OTR_SMP_SUCCESS, from); + otr_status_change(server, from, OTR_STATUS_SMP_SUCCESS); + break; + case OTRL_SMPEVENT_ABORT: + otr_auth_abort(server, context->username); + otr_status_change(server, from, OTR_STATUS_SMP_ABORTED); + break; + case OTRL_SMPEVENT_FAILURE: + case OTRL_SMPEVENT_CHEATED: + case OTRL_SMPEVENT_ERROR: + printformat(server, from, MSGLEVEL_CLIENTERROR, TXT_OTR_SMP_FAILURE, from); + otr_status_change(server, from, OTR_STATUS_SMP_FAILED); + break; + default: + g_warning("Received unknown SMP event: %d", smp_event); + break; + } +} + +/* + * timer_control callback. + */ +static void ops_timer_control(void *opdata, unsigned int interval) +{ + otr_control_timer(interval, opdata); +} + +/* + * Handle otr error message. + */ +static const char *ops_otr_error_message(void *opdata, ConnContext *context, + OtrlErrorCode code) +{ + char *msg = NULL; + + switch (code) { + case OTRL_ERRCODE_NONE: + break; + case OTRL_ERRCODE_ENCRYPTION_ERROR: + msg = strdup("Error occurred encrypting message."); + break; + case OTRL_ERRCODE_MSG_NOT_IN_PRIVATE: + if (context) { + msg = strdup("You sent encrypted data which was unexpected"); + } + break; + case OTRL_ERRCODE_MSG_UNREADABLE: + msg = strdup("You transmitted an unreadable encrypted message"); + break; + case OTRL_ERRCODE_MSG_MALFORMED: + msg = strdup("You transmitted a malformed data message."); + break; + } + + return msg; +} + +/* + * Free otr error message callback. + */ +static void ops_otr_error_message_free(void *opdata, const char *err_msg) +{ + g_free_not_null((char *)err_msg); +} + +/* + * Assign OTR message operations. + */ +OtrlMessageAppOps otr_ops = { + ops_policy, + ops_create_privkey, + ops_is_logged_in, + ops_inject_msg, + ops_up_ctx_list, + NULL, /* new_fingerprint */ + ops_write_fingerprints, + ops_secure, + ops_insecure, + NULL, /* still_secure */ + ops_max_msg, + NULL, /* account_name */ + NULL, /* account_name_free */ + NULL, /* received_symkey */ + ops_otr_error_message, + ops_otr_error_message_free, + NULL, /* resent_msg_prefix */ + NULL, /* resent_msg_prefix_free */ + ops_smp_event, + ops_handle_msg_event, + ops_create_instag, + NULL, /* convert_msg */ + NULL, /* convert_free */ + ops_timer_control, +}; |