/* * implements /otr authabort */ void otr_auth_abort(SERVER_REC *irssi, const char *nick) { ConnContext *ctx; assert(irssi); assert(nick); ctx = otr_find_context(irssi, nick, FALSE); if (!ctx) { IRSSI_NOTICE(irssi, nick, "Context for %9%s%9 not found.", nick); goto end; } otrl_message_abort_smp(user_state_global->otr_state, &otr_ops, irssi, ctx); otr_status_change(irssi, nick, OTR_STATUS_SMP_ABORT); if (ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) { IRSSI_NOTICE(irssi, nick, "%rOngoing authentication aborted%n"); } else { IRSSI_NOTICE(irssi, nick, "%rAuthentication aborted%n"); } end: return; }
/* * Gone insecure. */ static void ops_insecure(void *opdata, ConnContext *context) { SERVER_REC *irssi = opdata; IRSSI_NOTICE(irssi, context->username, "Gone %rinsecure%r"); otr_status_change(irssi, context->username, OTR_STATUS_GONE_INSECURE); }
/* * Trust our peer. */ void otr_trust(SERVER_REC *irssi, const char *nick, char *str_fp, struct otr_user_state *ustate) { char peerfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; struct otr_peer_context *opc; ConnContext *ctx; Fingerprint *fp_trust; assert(ustate); if (!irssi && !str_fp) { IRSSI_NOTICE(NULL, nick, "Need a fingerprint!"); goto error; } /* No human string fingerprint given. */ if (!str_fp) { ctx = otr_find_context(irssi, nick, FALSE); if (!ctx) { goto error; } opc = ctx->app_data; /* Always NEED a peer context or else code error. */ assert(opc); fp_trust = ctx->active_fingerprint; } else { fp_trust = otr_find_hash_fingerprint_from_human(str_fp, ustate); } if (fp_trust) { int ret; ret = otrl_context_is_fingerprint_trusted(fp_trust); if (ret) { IRSSI_NOTICE(irssi, nick, "Already trusted!"); goto end; } /* Trust level is manual at this point. */ otrl_context_set_trust(fp_trust, "manual"); key_write_fingerprints(ustate); otr_status_change(irssi, nick, OTR_STATUS_TRUST_MANUAL); otrl_privkey_hash_to_human(peerfp, fp_trust->fingerprint); IRSSI_NOTICE(irssi, nick, "Fingerprint %g%s%n trusted!", peerfp); } else { IRSSI_NOTICE(irssi, nick, "Fingerprint %y%s%n NOT found", (str_fp != NULL) ? str_fp : ""); } end: error: return; }
static void ops_smp_event(void *opdata, OtrlSMPEvent smp_event, ConnContext *context, unsigned short progress_percent, char *question) { SERVER_REC *irssi = 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. */ assert(opc); opc->smp_event = smp_event; switch (smp_event) { case OTRL_SMPEVENT_ASK_FOR_SECRET: IRSSI_NOTICE(irssi, from, "%9%s%9 wants to authenticate. " "Type %9/otr auth <SECRET>%9 to complete.", from); opc->ask_secret = 1; otr_status_change(irssi, from, OTR_STATUS_SMP_INCOMING); break; case OTRL_SMPEVENT_ASK_FOR_ANSWER: IRSSI_NOTICE(irssi, from, "%9%s%9 wants to authenticate and " "asked this question:", from); IRSSI_NOTICE(irssi, from, "%b>%n %y%s%n", question); IRSSI_NOTICE(irssi, from, "Type %9/otr auth <SECRET>%9 to complete."); opc->ask_secret = 1; otr_status_change(irssi, from, OTR_STATUS_SMP_INCOMING); break; case OTRL_SMPEVENT_IN_PROGRESS: IRSSI_NOTICE(irssi, from, "%9%s%9 replied to our auth request", from); otr_status_change(irssi, from, OTR_STATUS_SMP_FINALIZE); break; case OTRL_SMPEVENT_SUCCESS: IRSSI_NOTICE(irssi, from, "%gAuthentication successful.%n"); otr_status_change(irssi, from, OTR_STATUS_SMP_SUCCESS); break; case OTRL_SMPEVENT_ABORT: otr_auth_abort(irssi, context->username); otr_status_change(irssi, from, OTR_STATUS_SMP_ABORTED); break; case OTRL_SMPEVENT_FAILURE: case OTRL_SMPEVENT_CHEATED: case OTRL_SMPEVENT_ERROR: IRSSI_NOTICE(irssi, from, "%RAuthentication failed%n"); otr_status_change(irssi, from, OTR_STATUS_SMP_FAILED); break; default: IRSSI_NOTICE(irssi, from, "Received unknown SMP event. " "Ignoring"); break; } }
/* * Gone secure. */ static void ops_secure(void *opdata, ConnContext *context) { int ret; char ownfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; char peerfp[OTRL_PRIVKEY_FPRINT_HUMAN_LEN]; SERVER_REC *irssi = opdata; struct otr_peer_context *opc; assert(context); /* This should *really* not happened */ assert(context->msgstate == OTRL_MSGSTATE_ENCRYPTED); IRSSI_NOTICE(irssi, context->username, "Gone %9secure%9"); otr_status_change(irssi, context->username, OTR_STATUS_GONE_SECURE); opc = context->app_data; opc->active_fingerprint = context->active_fingerprint; ret = otrl_context_is_fingerprint_trusted(context->active_fingerprint); if (ret) { /* Secure and trusted */ goto end; } /* 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); IRSSI_NOTICE(irssi, context->username, "Your peer is not " "authenticated. To make sure you're talking to the right person you can " "either agree on a secret and use the authentication command " "%9/otr auth%9 or %9/otr authq [QUESTION] SECRET%9. You can also " "use the traditional way and compare fingerprints " "(e.g. telephone or GPG-signed mail) and subsequently enter " "%9/otr trust%9."); IRSSI_NOTICE(irssi, context->username, "Your fingerprint is: %y%s%n", ownfp); IRSSI_NOTICE(irssi, context->username, "%9%s's%9 fingerprint is: %r%s%n", context->username, peerfp); end: return; }
static void ops_smp_event(void *opdata, OtrlSMPEvent smp_event, ConnContext *context, unsigned short progress_percent, char *question) { IRC_CTX *ircctx = (opdata); char *from = context->username; struct co_info *coi = context->app_data; coi->received_smp_init = (smp_event == OTRL_SMPEVENT_ASK_FOR_SECRET) || (smp_event == OTRL_SMPEVENT_ASK_FOR_ANSWER); switch (smp_event) { case OTRL_SMPEVENT_ASK_FOR_SECRET: otr_notice(ircctx, from, TXT_AUTH_PEER, from); otr_status_change(ircctx, from, IO_STC_SMP_INCOMING); break; case OTRL_SMPEVENT_ASK_FOR_ANSWER: otr_notice(ircctx, from, TXT_AUTH_PEER_QA, from, question); otr_status_change(ircctx, from, IO_STC_SMP_INCOMING); break; case OTRL_SMPEVENT_IN_PROGRESS: otr_notice(ircctx, from, TXT_AUTH_PEER_REPLIED, from); otr_status_change(ircctx, from, IO_STC_SMP_FINALIZE); break; case OTRL_SMPEVENT_SUCCESS: otr_notice(ircctx, from, TXT_AUTH_SUCCESSFUL); otr_status_change(ircctx, from, IO_STC_SMP_SUCCESS); break; case OTRL_SMPEVENT_ABORT: otr_abort_auth(context, ircctx, from); otr_status_change(ircctx, from, IO_STC_SMP_ABORTED); break; case OTRL_SMPEVENT_FAILURE: case OTRL_SMPEVENT_CHEATED: case OTRL_SMPEVENT_ERROR: otr_notice(ircctx, from, TXT_AUTH_FAILED); coi->smp_failed = TRUE; otr_status_change(ircctx, from, IO_STC_SMP_FAILED); break; default: otr_logst(MSGLEVEL_CRAP, "Received unknown SMP event"); break; } }
/* * Finish the conversation. */ void otr_finish(SERVER_REC *irssi, const char *nick) { ConnContext *ctx; assert(irssi); assert(nick); ctx = otr_find_context(irssi, nick, FALSE); if (!ctx) { IRSSI_INFO(irssi, nick, "Nothing to do"); goto end; } otrl_message_disconnect(user_state_global->otr_state, &otr_ops, irssi, ctx->accountname, OTR_PROTOCOL_ID, nick, ctx->their_instance); otr_status_change(irssi, nick, OTR_STATUS_FINISHED); IRSSI_INFO(irssi, nick, "Finished conversation with %9%s%9", nick); end: return; }
/* * A context changed. */ static void ops_up_ctx_list(void *opdata) { otr_status_change(opdata, NULL, OTR_STATUS_CTX_UPDATE); }
/* * Hand the given message to OTR. * * Returns 0 if its an OTR protocol message or else negative value. */ int otr_receive(SERVER_REC *irssi, const char *msg, const char *from, char **new_msg) { int ret = -1; char *accname = NULL, *full_msg = NULL; const char *recv_msg = NULL; OtrlTLV *tlvs; ConnContext *ctx; struct otr_peer_context *opc; assert(irssi); accname = create_account_name(irssi); if (!accname) { goto error; } IRSSI_DEBUG("Receiving message..."); ctx = otr_find_context(irssi, from, 1); if (!ctx) { goto error; } /* Add peer context to OTR context if none exists */ if (!ctx->app_data) { add_peer_context_cb(irssi, ctx); } opc = ctx->app_data; assert(opc); ret = enqueue_otr_fragment(msg, opc, &full_msg); switch (ret) { case OTR_MSG_ORIGINAL: recv_msg = msg; break; case OTR_MSG_WAIT_MORE: ret = 1; goto error; case OTR_MSG_USE_QUEUE: recv_msg = full_msg; break; case OTR_MSG_ERROR: ret = -1; goto error; } ret = otrl_message_receiving(user_state_global->otr_state, &otr_ops, irssi, accname, OTR_PROTOCOL_ID, from, recv_msg, new_msg, &tlvs, &ctx, add_peer_context_cb, irssi); if (ret) { IRSSI_DEBUG("Ignoring message of length %d from %s to %s.\n" "%s", strlen(msg), from, accname, msg); } else { if (*new_msg) { IRSSI_DEBUG("Converted received message."); } } /* Check for disconnected message */ OtrlTLV *tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED); if (tlv) { otr_status_change(irssi, from, OTR_STATUS_PEER_FINISHED); IRSSI_NOTICE(irssi, from, "%9%s%9 has finished the OTR " "conversation. If you want to continue talking enter " "%9/otr finish%9 for plaintext or %9/otr init%9 to restart.", from); } otrl_tlv_free(tlvs); IRSSI_DEBUG("Message received."); error: if (full_msg) { free(full_msg); } free(accname); return ret; }
/* * Initiate or respond to SMP authentication. */ void otr_auth(SERVER_REC *irssi, const char *nick, const char *question, const char *secret) { int ret; size_t secret_len = 0; ConnContext *ctx; struct otr_peer_context *opc; assert(irssi); assert(nick); ctx = otr_find_context(irssi, nick, 0); if (!ctx) { IRSSI_NOTICE(irssi, nick, "Context for %9%s%9 not found.", nick); goto end; } opc = ctx->app_data; /* Again, code flow error. */ assert(opc); if (ctx->msgstate != OTRL_MSGSTATE_ENCRYPTED) { IRSSI_INFO(irssi, nick, "You need to establish an OTR session before you " "can authenticate."); goto end; } /* Aborting an ongoing auth */ if (ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) { otr_auth_abort(irssi, nick); } /* reset trust level */ if (ctx->active_fingerprint) { ret = otrl_context_is_fingerprint_trusted(ctx->active_fingerprint); if (!ret) { otrl_context_set_trust(ctx->active_fingerprint, ""); key_write_fingerprints(user_state_global); } } /* Libotr allows empty secret. */ if (secret) { secret_len = strlen(secret); } if (opc->ask_secret) { otrl_message_respond_smp(user_state_global->otr_state, &otr_ops, irssi, ctx, (unsigned char *) secret, secret_len); otr_status_change(irssi, nick, OTR_STATUS_SMP_RESPONDED); IRSSI_NOTICE(irssi, nick, "%yResponding to authentication...%n"); } else { if (question) { otrl_message_initiate_smp_q(user_state_global->otr_state, &otr_ops, irssi, ctx, question, (unsigned char *) secret, secret_len); } else { otrl_message_initiate_smp(user_state_global->otr_state, &otr_ops, irssi, ctx, (unsigned char *) secret, secret_len); } otr_status_change(irssi, nick, OTR_STATUS_SMP_STARTED); IRSSI_NOTICE(irssi, nick, "%yInitiated authentication...%n"); } opc->ask_secret = 0; end: return; }