From 764c7d1009feda7db971ea7ac6bcb2a4acef0efc Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 3 Feb 2008 22:30:03 +0100 Subject: OTR support, first checkin --- otr.c | 953 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 953 insertions(+) create mode 100644 otr.c (limited to 'otr.c') diff --git a/otr.c b/otr.c new file mode 100644 index 00000000..2b920f4b --- /dev/null +++ b/otr.c @@ -0,0 +1,953 @@ +#include "bitlbee.h" +#ifdef WITH_OTR +#include "irc.h" +#include "otr.h" +#include +#include + +/** +files used to store OTR data: + $configdir/$nick.otr_keys + $configdir/$nick.otr_fprints + **/ + + +/** OTR interface routines for the OtrlMessageAppOps struct: **/ + +OtrlPolicy op_policy(void *opdata, ConnContext *context); + +void op_create_privkey(void *opdata, const char *accountname, const char *protocol); + +int op_is_logged_in(void *opdata, const char *accountname, const char *protocol, + const char *recipient); + +void op_inject_message(void *opdata, const char *accountname, const char *protocol, + const char *recipient, const char *message); + +int op_display_otr_message(void *opdata, const char *accountname, const char *protocol, + const char *username, const char *msg); + +void op_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname, + const char *protocol, const char *username, unsigned char fingerprint[20]); + +void op_write_fingerprints(void *opdata); + +void op_gone_secure(void *opdata, ConnContext *context); + +void op_gone_secure(void *opdata, ConnContext *context); + +void op_gone_insecure(void *opdata, ConnContext *context); + +void op_still_secure(void *opdata, ConnContext *context, int is_reply); + +void op_log_message(void *opdata, const char *message); + +/* TODO: int op_max_message_size(void *opdata, ConnContext *context); */ + +/* TODO: const char *op_account_name(void *opdata, const char *account, + const char *protocol); */ + + +/** otr sub-command handlers: **/ + +/* TODO: void cmd_otr_keygen(irc_t *irc, char **args); */ +void cmd_otr_abort(irc_t *irc, char **args); /* TODO: does this cmd even make sense? */ +void cmd_otr_request(irc_t *irc, char **args); /* TODO: do we even need this? */ +void cmd_otr_auth(irc_t *irc, char **args); +/* TODO: void cmd_otr_affirm(irc_t *irc, char **args); */ +void cmd_otr_fprints(irc_t *irc, char **args); +void cmd_otr_info(irc_t *irc, char **args); +void cmd_otr_policy(irc_t *irc, char **args); + +const command_t otr_commands[] = { + { "abort", 1, &cmd_otr_abort, 0 }, + { "request", 1, &cmd_otr_request, 0 }, + { "auth", 2, &cmd_otr_auth, 0 }, + { "fprints", 0, &cmd_otr_fprints, 0 }, + { "info", 1, &cmd_otr_info, 0 }, + { "policy", 0, &cmd_otr_policy, 0 }, + { NULL } +}; + + +/** misc. helpers/subroutines: **/ + +/* start background thread to generate a (new) key for a given account */ +void otr_keygen(irc_t *irc, const char *handle, const char *protocol); +/* keygen thread main func */ +gpointer otr_keygen_thread_func(gpointer data); +/* mainloop handler for when keygen thread finishes */ +gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); +/* data to be passed to otr_keygen_thread_func */ +struct kgdata { + irc_t *irc; /* access to OTR userstate */ + char *keyfile; /* free me! */ + const char *handle; /* don't free! */ + const char *protocol; /* don't free! */ + GMutex *mutex; /* lock for the 'done' flag, free me! */ + int done; /* is the thread done? */ + gcry_error_t result; /* return value of otrl_privkey_generate */ +}; + +/* yes/no handlers for "generate key now?" */ +void yes_keygen(gpointer w, void *data); +void no_keygen(gpointer w, void *data); + +/* helper to make sure accountname and protocol match the incoming "opdata" */ +struct im_connection *check_imc(void *opdata, const char *accountname, + const char *protocol); + +/* determine the nick for a given handle/protocol pair */ +const char *peernick(irc_t *irc, const char *handle, const char *protocol); + +/* handle SMP TLVs from a received message */ +void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs); + +/* show the list of fingerprints associated with a given context */ +void show_fingerprints(irc_t *irc, ConnContext *ctx); + + + +/*** routines declared in otr.h: ***/ + +void otr_init(void) +{ + if(!g_thread_supported()) g_thread_init(NULL); + OTRL_INIT; + + /* fill global OtrlMessageAppOps */ + global.otr_ops.policy = &op_policy; + global.otr_ops.create_privkey = &op_create_privkey; + global.otr_ops.is_logged_in = &op_is_logged_in; + global.otr_ops.inject_message = &op_inject_message; + global.otr_ops.notify = NULL; + global.otr_ops.display_otr_message = &op_display_otr_message; + global.otr_ops.update_context_list = NULL; + global.otr_ops.protocol_name = NULL; + global.otr_ops.protocol_name_free = NULL; + global.otr_ops.new_fingerprint = &op_new_fingerprint; + global.otr_ops.write_fingerprints = &op_write_fingerprints; + global.otr_ops.gone_secure = &op_gone_secure; + global.otr_ops.gone_insecure = &op_gone_insecure; + global.otr_ops.still_secure = &op_still_secure; + global.otr_ops.log_message = &op_log_message; + global.otr_ops.max_message_size = NULL; + global.otr_ops.account_name = NULL; + global.otr_ops.account_name_free = NULL; +} + +/* Notice on the otr_mutex: + + The incoming/outgoing message handlers try to lock the otr_mutex. If they succeed, + this will prevent a concurrent keygen (possibly spawned by that very command) + from messing up the userstate. If the lock fails, that means there already is + a keygen in progress. Instead of blocking for an unknown time, they + will bail out gracefully, informing the user of this temporary "coma". + TODO: Hold back incoming/outgoing messages and process them when keygen completes? + + The other routines do not lock the otr_mutex themselves, it is done as a + catch-all in the root command handler. Rationale: + a) it's easy to code + b) it makes it obvious that no command can get its userstate corrupted + c) the "irc" struct is readily available there for feedback to the user + */ + +void otr_load(irc_t *irc) +{ + char s[512]; + account_t *a; + gcry_error_t e; + + log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); + + g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); + e = otrl_privkey_read(irc->otr_us, s); + if(e && e!=ENOENT) { + log_message(LOGLVL_ERROR, "%s: %s", s, strerror(e)); + } + g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); + e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); + if(e && e!=ENOENT) { + log_message(LOGLVL_ERROR, "%s: %s", s, strerror(e)); + } + + /* check for otr keys on all accounts */ + for(a=irc->accounts; a; a=a->next) { + otr_check_for_key(a); + } +} + +void otr_save(irc_t *irc) +{ + char s[512]; + + log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick); + + g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); + otrl_privkey_write_fingerprints(irc->otr_us, s); +} + +void otr_remove(const char *nick) +{ + char s[512]; + + log_message(LOGLVL_DEBUG, "otr_remove '%s'", nick); + + g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick); + unlink(s); + g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick); + unlink(s); +} + +void otr_rename(const char *onick, const char *nnick) +{ + char s[512], t[512]; + + log_message(LOGLVL_DEBUG, "otr_rename '%s' -> '%s'", onick, nnick); + + g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick); + g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick); + rename(s,t); + g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick); + g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick); + rename(s,t); +} + +void otr_check_for_key(account_t *a) +{ + irc_t *irc = a->irc; + char buf[45]; + char *fp; + + fp = otrl_privkey_fingerprint(irc->otr_us, buf, a->user, a->prpl->name); + if(fp) { + irc_usermsg(irc, "otr: %s/%s ready with f'print %s", + a->user, a->prpl->name, fp); + } else { + otr_keygen(irc, a->user, a->prpl->name); + } +} + +char *otr_handle_message(struct im_connection *ic, const char *handle, const char *msg) +{ + int ignore_msg; + char *newmsg = NULL; + OtrlTLV *tlvs = NULL; + char *colormsg; + + if(!g_mutex_trylock(ic->irc->otr_mutex)) { + /* TODO: queue msgs received during keygen for later */ + irc_usermsg(ic->irc, "msg from %s/%s during keygen - dropped", + handle, ic->acc->prpl->name); + return NULL; + } + + ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic, + ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, + &tlvs, NULL, NULL); + + otr_handle_smp(ic, handle, tlvs); + + if(ignore_msg) { + /* this was an internal OTR protocol message */ + g_mutex_unlock(ic->irc->otr_mutex); + return NULL; + } else if(!newmsg) { + /* this was a non-OTR message */ + g_mutex_unlock(ic->irc->otr_mutex); + return g_strdup(msg); + } else { + /* OTR has processed this message */ + ConnContext *context = otrl_context_find(ic->irc->otr_us, handle, + ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + /* color according to f'print trust */ + char color; + const char *trust = context->active_fingerprint->trust; + if(trust && trust[0] != '\0') + color='3'; /* green */ + else + color='5'; /* red */ + colormsg = g_strdup_printf("\x03%c%s\x0F", color, newmsg); + } else { + colormsg = g_strdup(newmsg); + } + otrl_message_free(newmsg); + g_mutex_unlock(ic->irc->otr_mutex); + return colormsg; + } +} + +int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags) +{ + int st; + char *otrmsg = NULL; + ConnContext *ctx = NULL; + + if(!g_mutex_trylock(ic->irc->otr_mutex)) { + irc_usermsg(ic->irc, "msg to %s/%s during keygen - not sent", + handle, ic->acc->prpl->name); + return 1; + } + + st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, + ic->acc->user, ic->acc->prpl->name, handle, + msg, NULL, &otrmsg, NULL, NULL); + if(st) { + g_mutex_unlock(ic->irc->otr_mutex); + return st; + } + + ctx = otrl_context_find(ic->irc->otr_us, + handle, ic->acc->user, ic->acc->prpl->name, + 1, NULL, NULL, NULL); + + if(otrmsg) { + if(!ctx) { + otrl_message_free(otrmsg); + g_mutex_unlock(ic->irc->otr_mutex); + return 1; + } + st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx, + otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL); + otrl_message_free(otrmsg); + } else { + /* yeah, well, some const casts as usual... ;-) */ + st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); + } + + g_mutex_unlock(ic->irc->otr_mutex); + return st; +} + +void cmd_otr(irc_t *irc, char **args) +{ + const command_t *cmd; + + if(!args[0]) + return; + + if(!args[1]) + return; + + for(cmd=otr_commands; cmd->command; cmd++) { + if(strcmp(cmd->command, args[1]) == 0) + break; + } + + if(!cmd->command) { + irc_usermsg(irc, "%s %s: unknown subcommand, see \x02help otr\x02", + args[0], args[1]); + return; + } + + if(!args[cmd->required_parameters+1]) { + irc_usermsg(irc, "%s %s: not enough arguments (%d req.)", + args[0], args[1], cmd->required_parameters); + return; + } + + cmd->execute(irc, args+1); +} + + +/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/ + +OtrlPolicy op_policy(void *opdata, ConnContext *context) +{ + /* TODO: OTR policy configurable */ + return OTRL_POLICY_OPPORTUNISTIC; +} + +void op_create_privkey(void *opdata, const char *accountname, + const char *protocol) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + char *s; + + log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol); + + s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?", + accountname, protocol); + query_add(ic->irc, ic, s, yes_keygen, no_keygen, ic->acc); +} + +int op_is_logged_in(void *opdata, const char *accountname, + const char *protocol, const char *recipient) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + user_t *u; + + log_message(LOGLVL_DEBUG, "op_is_logged_in '%s' '%s' '%s'", accountname, protocol, recipient); + + /* lookup the user_t for the given recipient */ + u = user_findhandle(ic, recipient); + if(u) { + if(u->online) + return 1; + else + return 0; + } else { + return -1; + } +} + +void op_inject_message(void *opdata, const char *accountname, + const char *protocol, const char *recipient, const char *message) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + + log_message(LOGLVL_DEBUG, "op_inject_message '%s' '%s' '%s' '%s'", accountname, protocol, recipient, message); + + if (strcmp(accountname, recipient) == 0) { + /* huh? injecting messages to myself? */ + irc_usermsg(ic->irc, "note to self: %s", message); + } else { + /* need to drop some consts here :-( */ + /* TODO: get flags into op_inject_message?! */ + ic->acc->prpl->buddy_msg(ic, (char *)recipient, (char *)message, 0); + /* ignoring return value :-/ */ + } +} + +int op_display_otr_message(void *opdata, const char *accountname, + const char *protocol, const char *username, const char *msg) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + + log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, msg); + + irc_usermsg(ic->irc, "%s", msg); + + return 0; +} + +void op_new_fingerprint(void *opdata, OtrlUserState us, + const char *accountname, const char *protocol, + const char *username, unsigned char fingerprint[20]) +{ + struct im_connection *ic = check_imc(opdata, accountname, protocol); + char hunam[45]; /* anybody looking? ;-) */ + + otrl_privkey_hash_to_human(hunam, fingerprint); + log_message(LOGLVL_DEBUG, "op_new_fingerprint '%s' '%s' '%s' '%s'", accountname, protocol, username, hunam); + + irc_usermsg(ic->irc, "new fingerprint for %s: %s", + peernick(ic->irc, username, protocol), hunam); +} + +void op_write_fingerprints(void *opdata) +{ + struct im_connection *ic = (struct im_connection *)opdata; + + log_message(LOGLVL_DEBUG, "op_write_fingerprints"); + + otr_save(ic->irc); +} + +void op_gone_secure(void *opdata, ConnContext *context) +{ + struct im_connection *ic = + check_imc(opdata, context->accountname, context->protocol); + + log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); + + irc_usermsg(ic->irc, "conversation with %s is now off the record", + peernick(ic->irc, context->username, context->protocol)); +} + +void op_gone_insecure(void *opdata, ConnContext *context) +{ + struct im_connection *ic = + check_imc(opdata, context->accountname, context->protocol); + + log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); + + irc_usermsg(ic->irc, "conversation with %s is now in the clear", + peernick(ic->irc, context->username, context->protocol)); +} + +void op_still_secure(void *opdata, ConnContext *context, int is_reply) +{ + struct im_connection *ic = + check_imc(opdata, context->accountname, context->protocol); + + log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d", + context->accountname, context->protocol, context->username, is_reply); + + irc_usermsg(ic->irc, "otr connection with %s has been refreshed", + peernick(ic->irc, context->username, context->protocol)); +} + +void op_log_message(void *opdata, const char *message) +{ + log_message(LOGLVL_INFO, "%s", message); +} + + +/*** OTR sub-command handlers ***/ + +void cmd_otr_abort(irc_t *irc, char **args) +{ + user_t *u; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + + otrl_message_disconnect(irc->otr_us, &global.otr_ops, + u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle); +} + +void cmd_otr_request(irc_t *irc, char **args) +{ + user_t *u; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + if(!u->online) { + irc_usermsg(irc, "%s is offline", args[1]); + return; + } + + imc_buddy_msg(u->ic, u->handle, "?OTR?", 0); +} + +void cmd_otr_auth(irc_t *irc, char **args) +{ + user_t *u; + ConnContext *ctx; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + if(!u->online) { + irc_usermsg(irc, "%s is offline", args[1]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, + u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL); + if(!ctx) { + return; + } + + if(ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) { + log_message(LOGLVL_INFO, + "SMP already in phase %d, sending abort before reinitiating", + ctx->smstate->nextExpected+1); + otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx); + otrl_sm_state_free(ctx->smstate); + } + + /* warning: the following assumes that smstates are cleared whenever an SMP + is completed or aborted! */ + if(ctx->smstate->secret == NULL) { + irc_usermsg(irc, "smp: initiating with %s...", u->nick); + otrl_message_initiate_smp(irc->otr_us, &global.otr_ops, + u->ic, ctx, (unsigned char *)args[2], strlen(args[2])); + /* smp is now in EXPECT2 */ + } else { + /* if we're still in EXPECT1 but smstate is initialized, we must have + received the SMP1, so let's issue a response */ + irc_usermsg(irc, "smp: responding to %s...", u->nick); + otrl_message_respond_smp(irc->otr_us, &global.otr_ops, + u->ic, ctx, (unsigned char *)args[2], strlen(args[2])); + /* smp is now in EXPECT3 */ + } +} + +void cmd_otr_fprints(irc_t *irc, char **args) +{ + if(args[1]) { + /* list given buddy's fingerprints */ + user_t *u; + ConnContext *ctx; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, + u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no fingerprints"); + } else { + show_fingerprints(irc, ctx); + } + } else { + /* list all known fingerprints */ + ConnContext *ctx; + for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) { + irc_usermsg(irc, "[%s]", peernick(irc, ctx->username, ctx->protocol)); + show_fingerprints(irc, ctx); + } + if(!irc->otr_us->context_root) { + irc_usermsg(irc, "no fingerprints"); + } + } +} + +void cmd_otr_info(irc_t *irc, char **args) +{ + user_t *u; + ConnContext *ctx; + Fingerprint *fp; + char human[45]; + const char *offer_status; + const char *message_state; + const char *trust; + + if(!args) { + irc_usermsg(irc, "no args?!"); + return; + } + if(!args[1]) { + irc_usermsg(irc, "no args[1]?!"); + return; + } + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, + u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no otr info on %s", args[1]); + return; + } + + switch(ctx->otr_offer) { + case OFFER_NOT: offer_status="none sent"; break; + case OFFER_SENT: offer_status="awaiting reply"; break; + case OFFER_ACCEPTED: offer_status="accepted our offer"; break; + case OFFER_REJECTED: offer_status="ignored our offer"; break; + default: offer_status="?"; + } + + switch(ctx->msgstate) { + case OTRL_MSGSTATE_PLAINTEXT: message_state="cleartext"; break; + case OTRL_MSGSTATE_ENCRYPTED: message_state="encrypted"; break; + case OTRL_MSGSTATE_FINISHED: message_state="shut down"; break; + default: message_state="?"; + } + + irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1], + ctx->username, ctx->protocol, ctx->accountname, ctx->protocol); + irc_usermsg(irc, " otr offer status: %s", offer_status); + irc_usermsg(irc, " connection state: %s", message_state); + irc_usermsg(irc, " protocol version: %d", ctx->protocol_version); + fp = ctx->active_fingerprint; + if(!fp) { + irc_usermsg(irc, " active f'print: none"); + } else { + otrl_privkey_hash_to_human(human, fp->fingerprint); + if(!fp->trust || fp->trust[0] == '\0') { + trust="untrusted"; + } else { + trust=fp->trust; + } + irc_usermsg(irc, " active f'print: %s (%s)", human, trust); + } +} + +void cmd_otr_policy(irc_t *irc, char **args) +{ + irc_usermsg(irc, "n/a: not implemented"); +} + + +/*** local helpers / subroutines: ***/ + +/* Socialist Millionaires' Protocol */ +void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs) +{ + irc_t *irc = ic->irc; + OtrlUserState us = irc->otr_us; + OtrlMessageAppOps *ops = &global.otr_ops; + OtrlTLV *tlv = NULL; + ConnContext *context; + NextExpectedSMP nextMsg; + user_t *u; + + u = user_findhandle(ic, handle); + if(!u) return; + context = otrl_context_find(us, handle, + ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL); + if(!context) return; + nextMsg = context->smstate->nextExpected; + + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT1) { + irc_usermsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick); + otrl_message_abort_smp(us, ops, u->ic, context); + otrl_sm_state_free(context->smstate); + } else { + irc_usermsg(irc, "smp: initiated by %s" + " - respond with \x02otr smp %s \x02", + u->nick, u->nick); + /* smp stays in EXPECT1 until user responds */ + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT2) { + irc_usermsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick); + otrl_message_abort_smp(us, ops, u->ic, context); + otrl_sm_state_free(context->smstate); + } else { + /* SMP2 received, otrl_message_receiving will have sent SMP3 */ + context->smstate->nextExpected = OTRL_SMP_EXPECT4; + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT3) { + irc_usermsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick); + otrl_message_abort_smp(us, ops, u->ic, context); + otrl_sm_state_free(context->smstate); + } else { + /* SMP3 received, otrl_message_receiving will have sent SMP4 and set fp trust */ + const char *trust = context->active_fingerprint->trust; + if(!trust || trust[0]=='\0') { + irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted", + u->nick); + } else { + irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted", + u->nick); + } + otrl_sm_state_free(context->smstate); + /* smp is in back in EXPECT1 */ + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4); + if (tlv) { + if (nextMsg != OTRL_SMP_EXPECT4) { + irc_usermsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick); + otrl_message_abort_smp(us, ops, u->ic, context); + otrl_sm_state_free(context->smstate); + } else { + /* SMP4 received, otrl_message_receiving will have set fp trust */ + const char *trust = context->active_fingerprint->trust; + if(!trust || trust[0]=='\0') { + irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted", + u->nick); + } else { + irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted", + u->nick); + } + otrl_sm_state_free(context->smstate); + /* smp is in back in EXPECT1 */ + } + } + tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT); + if (tlv) { + irc_usermsg(irc, "smp: received abort from %s", u->nick); + otrl_sm_state_free(context->smstate); + /* smp is in back in EXPECT1 */ + } +} + +/* helper to assert that account and protocol names given to ops below always + match the im_connection passed through as opdata */ +struct im_connection *check_imc(void *opdata, const char *accountname, + const char *protocol) +{ + struct im_connection *ic = (struct im_connection *)opdata; + + if (strcmp(accountname, ic->acc->user) != 0) { + log_message(LOGLVL_WARNING, + "otr: internal account name mismatch: '%s' vs '%s'", + accountname, ic->acc->user); + } + if (strcmp(protocol, ic->acc->prpl->name) != 0) { + log_message(LOGLVL_WARNING, + "otr: internal protocol name mismatch: '%s' vs '%s'", + protocol, ic->acc->prpl->name); + } + + return ic; +} + +const char *peernick(irc_t *irc, const char *handle, const char *protocol) +{ + user_t *u; + static char fallback[512]; + + g_snprintf(fallback, 511, "%s/%s", handle, protocol); + for(u=irc->users; u; u=u->next) { + struct prpl *prpl; + if(!u->ic || !u->handle) + break; + prpl = u->ic->acc->prpl; + if(strcmp(prpl->name, protocol) == 0 + && prpl->handle_cmp(u->handle, handle) == 0) { + return u->nick; + } + } + + return fallback; +} + +void show_fingerprints(irc_t *irc, ConnContext *ctx) +{ + char human[45]; + Fingerprint *fp; + const char *trust; + int count=0; + + for(fp=&ctx->fingerprint_root; fp; fp=fp->next) { + if(!fp->fingerprint) + continue; + count++; + otrl_privkey_hash_to_human(human, fp->fingerprint); + if(!fp->trust || fp->trust[0] == '\0') { + trust="untrusted"; + } else { + trust=fp->trust; + } + if(fp == ctx->active_fingerprint) { + irc_usermsg(irc, "\x02%s (%s)\x02", human, trust); + } else { + irc_usermsg(irc, "%s (%s)", human, trust); + } + } + if(count==0) + irc_usermsg(irc, "no fingerprints"); +} + +void otr_keygen(irc_t *irc, const char *handle, const char *protocol) +{ + GError *err; + GThread *thr; + struct kgdata *kg; + gint ev; + + irc_usermsg(irc, "generating new otr privkey for %s/%s...", + handle, protocol); + + kg = g_new0(struct kgdata, 1); + if(!kg) { + irc_usermsg(irc, "otr keygen failed: out of memory"); + return; + } + + /* Assemble the job description to be passed to thread and handler */ + kg->irc = irc; + kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick); + if(!kg->keyfile) { + irc_usermsg(irc, "otr keygen failed: out of memory"); + g_free(kg); + return; + } + kg->handle = handle; + kg->protocol = protocol; + kg->mutex = g_mutex_new(); + if(!kg->mutex) { + irc_usermsg(irc, "otr keygen failed: couldn't create mutex"); + g_free(kg->keyfile); + g_free(kg); + return; + } + kg->done = FALSE; + + /* Poll for completion of the thread periodically. I would have preferred + to just wait on a pipe but this way it's portable to Windows. *sigh* + */ + ev = b_timeout_add(1000, &keygen_finish_handler, kg); + if(!ev) { + irc_usermsg(irc, "otr keygen failed: couldn't register timeout"); + g_free(kg->keyfile); + g_mutex_free(kg->mutex); + g_free(kg); + return; + } + + thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); + if(!thr) { + irc_usermsg(irc, "otr keygen failed: %s", err->message); + g_free(kg->keyfile); + g_mutex_free(kg->mutex); + g_free(kg); + b_event_remove(ev); + } +} + +gpointer otr_keygen_thread_func(gpointer data) +{ + struct kgdata *kg = (struct kgdata *)data; + + /* lock OTR subsystem and do the work */ + g_mutex_lock(kg->irc->otr_mutex); + kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, + kg->protocol); + g_mutex_unlock(kg->irc->otr_mutex); + /* OTR enabled again */ + + /* notify mainloop */ + g_mutex_lock(kg->mutex); + kg->done = TRUE; + g_mutex_unlock(kg->mutex); + + return NULL; +} + +gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) +{ + struct kgdata *kg = (struct kgdata *)data; + int done; + + g_mutex_lock(kg->mutex); + done = kg->done; + g_mutex_unlock(kg->mutex); + if(kg->done) { + if(kg->result) { + irc_usermsg(kg->irc, "otr keygen failed: libgcrypt error"); /* TODO: diagnostics */ + } else { + irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); + } + g_free(kg->keyfile); + g_mutex_free(kg->mutex); + g_free(kg); + return FALSE; /* unregister timeout */ + } + + return TRUE; /* still working, continue checking */ +} + +void yes_keygen(gpointer w, void *data) +{ + account_t *acc = (account_t *)data; + + otr_keygen(acc->irc, acc->user, acc->prpl->name); +} + +void no_keygen(gpointer w, void *data) +{ + account_t *acc = (account_t *)data; + + irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s", + acc->user, acc->prpl->name); +} + + +#else /* WITH_OTR undefined */ + +void cmd_otr(irc_t *irc, char **args) +{ + irc_usermsg(irc, "otr: n/a, compiled without OTR support"); +} + +#endif -- cgit v1.2.3 From 3c80a9de52771f14e8aa947688fbf1dfc6ef5895 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 3 Feb 2008 23:28:13 +0100 Subject: otr: check some error conditions --- otr.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 2b920f4b..2ac40e60 100644 --- a/otr.c +++ b/otr.c @@ -163,12 +163,12 @@ void otr_load(irc_t *irc) g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr_us, s); if(e && e!=ENOENT) { - log_message(LOGLVL_ERROR, "%s: %s", s, strerror(e)); + log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); if(e && e!=ENOENT) { - log_message(LOGLVL_ERROR, "%s: %s", s, strerror(e)); + log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); } /* check for otr keys on all accounts */ @@ -180,11 +180,15 @@ void otr_load(irc_t *irc) void otr_save(irc_t *irc) { char s[512]; + gcry_error_t e; log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); - otrl_privkey_write_fingerprints(irc->otr_us, s); + e = otrl_privkey_write_fingerprints(irc->otr_us, s); + if(e) { + log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e)); + } } void otr_remove(const char *nick) @@ -536,6 +540,7 @@ void cmd_otr_auth(irc_t *irc, char **args) ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL); if(!ctx) { + /* huh? out of memory or what? */ return; } @@ -685,7 +690,10 @@ void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs) if(!u) return; context = otrl_context_find(us, handle, ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL); - if(!context) return; + if(!context) { + /* huh? out of memory or what? */ + return; + } nextMsg = context->smstate->nextExpected; tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1); @@ -914,7 +922,7 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) g_mutex_unlock(kg->mutex); if(kg->done) { if(kg->result) { - irc_usermsg(kg->irc, "otr keygen failed: libgcrypt error"); /* TODO: diagnostics */ + irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result)); } else { irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); } -- cgit v1.2.3 From a13855a57daae6eba05c9600b69f640c2949e944 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Wed, 6 Feb 2008 17:42:23 +0100 Subject: use peernicks and try to guess max message size --- otr.c | 50 +++++++++++++++++++++++++++++++++++--------------- 1 file changed, 35 insertions(+), 15 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 2ac40e60..78952699 100644 --- a/otr.c +++ b/otr.c @@ -42,10 +42,9 @@ void op_still_secure(void *opdata, ConnContext *context, int is_reply); void op_log_message(void *opdata, const char *message); -/* TODO: int op_max_message_size(void *opdata, ConnContext *context); */ +int op_max_message_size(void *opdata, ConnContext *context); -/* TODO: const char *op_account_name(void *opdata, const char *account, - const char *protocol); */ +const char *op_account_name(void *opdata, const char *account, const char *protocol); /** otr sub-command handlers: **/ @@ -131,8 +130,8 @@ void otr_init(void) global.otr_ops.gone_insecure = &op_gone_insecure; global.otr_ops.still_secure = &op_still_secure; global.otr_ops.log_message = &op_log_message; - global.otr_ops.max_message_size = NULL; - global.otr_ops.account_name = NULL; + global.otr_ops.max_message_size = &op_max_message_size; + global.otr_ops.account_name = &op_account_name; global.otr_ops.account_name_free = NULL; } @@ -220,13 +219,12 @@ void otr_rename(const char *onick, const char *nnick) void otr_check_for_key(account_t *a) { irc_t *irc = a->irc; - char buf[45]; - char *fp; + OtrlPrivKey *k; - fp = otrl_privkey_fingerprint(irc->otr_us, buf, a->user, a->prpl->name); - if(fp) { - irc_usermsg(irc, "otr: %s/%s ready with f'print %s", - a->user, a->prpl->name, fp); + k = otrl_privkey_find(irc->otr_us, a->user, a->prpl->name); + if(k) { + irc_usermsg(irc, "otr: %s/%s ready", + a->user, a->prpl->name); } else { otr_keygen(irc, a->user, a->prpl->name); } @@ -241,8 +239,8 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(!g_mutex_trylock(ic->irc->otr_mutex)) { /* TODO: queue msgs received during keygen for later */ - irc_usermsg(ic->irc, "msg from %s/%s during keygen - dropped", - handle, ic->acc->prpl->name); + irc_usermsg(ic->irc, "msg from %s during keygen - dropped", + peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; } @@ -289,8 +287,8 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m ConnContext *ctx = NULL; if(!g_mutex_trylock(ic->irc->otr_mutex)) { - irc_usermsg(ic->irc, "msg to %s/%s during keygen - not sent", - handle, ic->acc->prpl->name); + irc_usermsg(ic->irc, "msg to %s during keygen - not sent", + peernick(ic->irc, handle, ic->acc->prpl->name)); return 1; } @@ -488,6 +486,27 @@ void op_log_message(void *opdata, const char *message) log_message(LOGLVL_INFO, "%s", message); } +int op_max_message_size(void *opdata, ConnContext *context) +{ + /* TODO: make max_message_size a property of the prpl. + the values here are taken from the libotr UPGRADING file */ + if(!strcmp(context->protocol, "msn")) + return 1409; + if(!strcmp(context->protocol, "yahoo")) + return 832; + if(!strcmp(context->protocol, "oscar")) + return 2343; +} + +const char *op_account_name(void *opdata, const char *account, const char *protocol) +{ + struct im_connection *ic = (struct im_connection *)opdata; + + log_message(LOGLVL_DEBUG, "op_account_name '%s' '%s'", account, protocol); + + return peernick(ic->irc, account, protocol); +} + /*** OTR sub-command handlers ***/ @@ -946,6 +965,7 @@ void no_keygen(gpointer w, void *data) { account_t *acc = (account_t *)data; + /* TODO: remember that we didn't want a key? */ irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s", acc->user, acc->prpl->name); } -- cgit v1.2.3 From 5a71d9c5b14aa749b532666b71b25ce2afcdc5bb Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 9 Feb 2008 18:58:13 +0100 Subject: - add support for setting ops/voice according to OTR msgstate - add 'otr trust' user command - support non-otr messages during keygen - run otr messages through strip_html - interpret and tags in html messages - record max message size in prpl - add 'encrypted' flag to user_t - cosmetics --- otr.c | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 236 insertions(+), 50 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 78952699..60581ee6 100644 --- a/otr.c +++ b/otr.c @@ -34,8 +34,6 @@ void op_write_fingerprints(void *opdata); void op_gone_secure(void *opdata, ConnContext *context); -void op_gone_secure(void *opdata, ConnContext *context); - void op_gone_insecure(void *opdata, ConnContext *context); void op_still_secure(void *opdata, ConnContext *context, int is_reply); @@ -52,7 +50,8 @@ const char *op_account_name(void *opdata, const char *account, const char *proto /* TODO: void cmd_otr_keygen(irc_t *irc, char **args); */ void cmd_otr_abort(irc_t *irc, char **args); /* TODO: does this cmd even make sense? */ void cmd_otr_request(irc_t *irc, char **args); /* TODO: do we even need this? */ -void cmd_otr_auth(irc_t *irc, char **args); +void cmd_otr_smp(irc_t *irc, char **args); +void cmd_otr_trust(irc_t *irc, char **args); /* TODO: void cmd_otr_affirm(irc_t *irc, char **args); */ void cmd_otr_fprints(irc_t *irc, char **args); void cmd_otr_info(irc_t *irc, char **args); @@ -61,7 +60,8 @@ void cmd_otr_policy(irc_t *irc, char **args); const command_t otr_commands[] = { { "abort", 1, &cmd_otr_abort, 0 }, { "request", 1, &cmd_otr_request, 0 }, - { "auth", 2, &cmd_otr_auth, 0 }, + { "smp", 2, &cmd_otr_smp, 0 }, + { "trust", 6, &cmd_otr_trust, 0 }, { "fprints", 0, &cmd_otr_fprints, 0 }, { "info", 1, &cmd_otr_info, 0 }, { "policy", 0, &cmd_otr_policy, 0 }, @@ -96,12 +96,22 @@ void no_keygen(gpointer w, void *data); struct im_connection *check_imc(void *opdata, const char *accountname, const char *protocol); -/* determine the nick for a given handle/protocol pair */ +/* determine the nick for a given handle/protocol pair + returns "handle/protocol" if not found */ const char *peernick(irc_t *irc, const char *handle, const char *protocol); +/* determine the user_t for a given handle/protocol pair + returns NULL if not found */ +user_t *peeruser(irc_t *irc, const char *handle, const char *protocol); + /* handle SMP TLVs from a received message */ void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs); +/* update op/voice flag of given user according to encryption state and settings + returns 0 if neither op_buddies nor voice_buddies is set to "encrypted", + i.e. msgstate should be announced seperately */ +int otr_update_modeflags(irc_t *irc, user_t *u); + /* show the list of fingerprints associated with a given context */ void show_fingerprints(irc_t *irc, ConnContext *ctx); @@ -238,8 +248,15 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha char *colormsg; if(!g_mutex_trylock(ic->irc->otr_mutex)) { - /* TODO: queue msgs received during keygen for later */ - irc_usermsg(ic->irc, "msg from %s during keygen - dropped", + user_t *u = user_findhandle(ic, handle); + + /* fallback for non-otr clients */ + if(u && !u->encrypted) { + return g_strdup(msg); + } + + /* TODO: queue msgs received during keygen for later? */ + irc_usermsg(ic->irc, "otr msg from %s during keygen - dropped", peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; } @@ -282,14 +299,24 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags) { - int st; - char *otrmsg = NULL; - ConnContext *ctx = NULL; - + int st; + char *otrmsg = NULL; + ConnContext *ctx = NULL; + if(!g_mutex_trylock(ic->irc->otr_mutex)) { - irc_usermsg(ic->irc, "msg to %s during keygen - not sent", - peernick(ic->irc, handle, ic->acc->prpl->name)); - return 1; + user_t *u = user_findhandle(ic, handle); + + /* Fallback for non-otr clients. + Yes, we must be very sure this doesn't send stuff in the clear where it + shouldn't... */ + if(u && !u->encrypted) { + return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); + } + + /* otherwise refuse to send */ + irc_usermsg(ic->irc, "otr msg to %s not sent during keygen", + peernick(ic->irc, handle, ic->acc->prpl->name)); + return 1; } st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, @@ -413,14 +440,17 @@ void op_inject_message(void *opdata, const char *accountname, } int op_display_otr_message(void *opdata, const char *accountname, - const char *protocol, const char *username, const char *msg) + const char *protocol, const char *username, const char *message) { struct im_connection *ic = check_imc(opdata, accountname, protocol); + char *msg = g_strdup(message); - log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, msg); + log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, message); - irc_usermsg(ic->irc, "%s", msg); + strip_html(msg); + irc_usermsg(ic->irc, "otr: %s", msg); + g_free(msg); return 0; } @@ -451,51 +481,84 @@ void op_gone_secure(void *opdata, ConnContext *context) { struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); + user_t *u; log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); - irc_usermsg(ic->irc, "conversation with %s is now off the record", - peernick(ic->irc, context->username, context->protocol)); + u = peeruser(ic->irc, context->username, context->protocol); + if(!u) { + log_message(LOGLVL_ERROR, + "BUG: otr.c: op_gone_secure: user_t for %s/%s not found!", + context->username, context->protocol); + return; + } + if(context->active_fingerprint->trust[0]) + u->encrypted = 2; + else + u->encrypted = 1; + if(!otr_update_modeflags(ic->irc, u)) + irc_usermsg(ic->irc, "conversation with %s is now off the record", u->nick); } void op_gone_insecure(void *opdata, ConnContext *context) { struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); + user_t *u; log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); - irc_usermsg(ic->irc, "conversation with %s is now in the clear", - peernick(ic->irc, context->username, context->protocol)); + u = peeruser(ic->irc, context->username, context->protocol); + if(!u) { + log_message(LOGLVL_ERROR, + "BUG: otr.c: op_gone_insecure: user_t for %s/%s not found!", + context->username, context->protocol); + return; + } + u->encrypted = 0; + if(!otr_update_modeflags(ic->irc, u)) + irc_usermsg(ic->irc, "conversation with %s is now in the clear", u->nick); } void op_still_secure(void *opdata, ConnContext *context, int is_reply) { struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); + user_t *u; log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d", context->accountname, context->protocol, context->username, is_reply); - irc_usermsg(ic->irc, "otr connection with %s has been refreshed", - peernick(ic->irc, context->username, context->protocol)); + u = peeruser(ic->irc, context->username, context->protocol); + if(!u) { + log_message(LOGLVL_ERROR, + "BUG: otr.c: op_still_secure: user_t for %s/%s not found!", + context->username, context->protocol); + return; + } + if(context->active_fingerprint->trust[0]) + u->encrypted = 2; + else + u->encrypted = 1; + if(!otr_update_modeflags(ic->irc, u)) + irc_usermsg(ic->irc, "otr connection with %s has been refreshed", u->nick); } void op_log_message(void *opdata, const char *message) { - log_message(LOGLVL_INFO, "%s", message); + char *msg = g_strdup(message); + + strip_html(msg); + log_message(LOGLVL_INFO, "otr: %s", msg); + g_free(msg); } int op_max_message_size(void *opdata, ConnContext *context) { - /* TODO: make max_message_size a property of the prpl. - the values here are taken from the libotr UPGRADING file */ - if(!strcmp(context->protocol, "msn")) - return 1409; - if(!strcmp(context->protocol, "yahoo")) - return 832; - if(!strcmp(context->protocol, "oscar")) - return 2343; + struct im_connection *ic = + check_imc(opdata, context->accountname, context->protocol); + + return ic->acc->prpl->mms; } const char *op_account_name(void *opdata, const char *account, const char *protocol) @@ -541,7 +604,7 @@ void cmd_otr_request(irc_t *irc, char **args) imc_buddy_msg(u->ic, u->handle, "?OTR?", 0); } -void cmd_otr_auth(irc_t *irc, char **args) +void cmd_otr_smp(irc_t *irc, char **args) { user_t *u; ConnContext *ctx; @@ -588,6 +651,80 @@ void cmd_otr_auth(irc_t *irc, char **args) } } +int hexval(char a) +{ + int x=tolower(a); + + if(x>='a' && x<='f') + x = x - 'a' + 10; + else if(x>='0' && x<='9') + x = x - '0'; + else + return -1; + + return x; +} + +void cmd_otr_trust(irc_t *irc, char **args) +{ + user_t *u; + ConnContext *ctx; + unsigned char raw[20]; + Fingerprint *fp; + int i,j; + + u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, + u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "%s: no otr context with user", args[1]); + return; + } + + /* convert given fingerprint to raw representation */ + for(i=0; i<5; i++) { + for(j=0; j<4; j++) { + char *p = args[2+i]+(2*j); + char *q = p+1; + int x, y; + + if(!*p || !*q) { + irc_usermsg(irc, "failed: truncated fingerprint block %d", i+1); + return; + } + + x = hexval(*p); + y = hexval(*q); + if(x<0) { + irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+1, i+1); + return; + } + if(y<0) { + irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+2, i+1); + return; + } + + raw[i*4+j] = x*16 + y; + } + } + fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL); + if(!fp) { + irc_usermsg(irc, "failed: no such fingerprint for %s", args[1]); + } else { + char *trust = args[7] ? args[7] : "affirmed"; + otrl_context_set_trust(fp, trust); + irc_usermsg(irc, "fingerprint match, trust set to \"%s\"", trust); + if(u->encrypted) + u->encrypted = 2; + otr_update_modeflags(irc, u); + } +} + void cmd_otr_fprints(irc_t *irc, char **args) { if(args[1]) { @@ -648,7 +785,7 @@ void cmd_otr_info(irc_t *irc, char **args) ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { - irc_usermsg(irc, "no otr info on %s", args[1]); + irc_usermsg(irc, "no otr context with %s", args[1]); return; } @@ -671,18 +808,21 @@ void cmd_otr_info(irc_t *irc, char **args) ctx->username, ctx->protocol, ctx->accountname, ctx->protocol); irc_usermsg(irc, " otr offer status: %s", offer_status); irc_usermsg(irc, " connection state: %s", message_state); - irc_usermsg(irc, " protocol version: %d", ctx->protocol_version); - fp = ctx->active_fingerprint; - if(!fp) { - irc_usermsg(irc, " active f'print: none"); - } else { - otrl_privkey_hash_to_human(human, fp->fingerprint); - if(!fp->trust || fp->trust[0] == '\0') { - trust="untrusted"; + + if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + irc_usermsg(irc, " protocol version: %d", ctx->protocol_version); + fp = ctx->active_fingerprint; + if(!fp) { + irc_usermsg(irc, " active f'print: none?"); } else { - trust=fp->trust; + otrl_privkey_hash_to_human(human, fp->fingerprint); + if(!fp->trust || fp->trust[0] == '\0') { + trust="untrusted"; + } else { + trust=fp->trust; + } + irc_usermsg(irc, " active f'print: %s (%s)", human, trust); } - irc_usermsg(irc, " active f'print: %s (%s)", human, trust); } } @@ -808,12 +948,10 @@ struct im_connection *check_imc(void *opdata, const char *accountname, return ic; } -const char *peernick(irc_t *irc, const char *handle, const char *protocol) +user_t *peeruser(irc_t *irc, const char *handle, const char *protocol) { user_t *u; - static char fallback[512]; - g_snprintf(fallback, 511, "%s/%s", handle, protocol); for(u=irc->users; u; u=u->next) { struct prpl *prpl; if(!u->ic || !u->handle) @@ -821,11 +959,55 @@ const char *peernick(irc_t *irc, const char *handle, const char *protocol) prpl = u->ic->acc->prpl; if(strcmp(prpl->name, protocol) == 0 && prpl->handle_cmp(u->handle, handle) == 0) { - return u->nick; + return u; } } - return fallback; + return NULL; +} + +const char *peernick(irc_t *irc, const char *handle, const char *protocol) +{ + static char fallback[512]; + + user_t *u = peeruser(irc, handle, protocol); + if(u) { + return u->nick; + } else { + g_snprintf(fallback, 511, "%s/%s", handle, protocol); + return fallback; + } +} + +int otr_update_modeflags(irc_t *irc, user_t *u) +{ + char *vo = set_getstr(&irc->set, "voice_buddies"); + char *oo = set_getstr(&irc->set, "op_buddies"); + char eflag=0, tflag=0; + int e = u->encrypted; + int t = (u->encrypted > 1); + + if(!strcmp(vo, "encrypted")) + eflag='v'; + else if(!strcmp(oo, "encrypted")) + eflag='o'; + if(!strcmp(vo, "trusted")) + tflag='v'; + else if(!strcmp(oo, "trusted")) + tflag='o'; + + if(!eflag) + return 0; + + if(tflag) { + irc_write( irc, ":%s!%s@%s MODE %s %c%c%c%c %s %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, e?'+':'-', eflag, t?'+':'-', tflag, u->nick, u->nick ); + } else { + irc_write( irc, ":%s!%s@%s MODE %s %c%c %s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, e?'+':'-', eflag, u->nick ); + } + + return 1; } void show_fingerprints(irc_t *irc, ConnContext *ctx) @@ -965,9 +1147,13 @@ void no_keygen(gpointer w, void *data) { account_t *acc = (account_t *)data; - /* TODO: remember that we didn't want a key? */ irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s", acc->user, acc->prpl->name); + /* TODO: + irc_usermsg(acc->irc, "setting otr policy for %s/%s to \"never\"", + acc->user, acc->prpl->name); + set_setstr(acc->set, "otr_policy", "never"); + */ } -- cgit v1.2.3 From 52e6e17d3b3610afd6d6b997257b72e798d95c47 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 14:36:43 +0100 Subject: Support halfops for 'notaway' status etc. --- otr.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 46 insertions(+), 21 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 60581ee6..ccb65887 100644 --- a/otr.c +++ b/otr.c @@ -981,31 +981,56 @@ const char *peernick(irc_t *irc, const char *handle, const char *protocol) int otr_update_modeflags(irc_t *irc, user_t *u) { - char *vo = set_getstr(&irc->set, "voice_buddies"); - char *oo = set_getstr(&irc->set, "op_buddies"); - char eflag=0, tflag=0; - int e = u->encrypted; - int t = (u->encrypted > 1); + char *vb = set_getstr(&irc->set, "voice_buddies"); + char *hb = set_getstr(&irc->set, "halfop_buddies"); + char *ob = set_getstr(&irc->set, "op_buddies"); + int encrypted = u->encrypted; + int trusted = u->encrypted > 1; + char flags[7]; + int nflags; + char *p = flags; + int i; - if(!strcmp(vo, "encrypted")) - eflag='v'; - else if(!strcmp(oo, "encrypted")) - eflag='o'; - if(!strcmp(vo, "trusted")) - tflag='v'; - else if(!strcmp(oo, "trusted")) - tflag='o'; + if(!strcmp(vb, "encrypted")) { + *(p++) = encrypted ? '+' : '-'; + *(p++) = 'v'; + nflags++; + } else if(!strcmp(vb, "trusted")) { + *(p++) = trusted ? '+' : '-'; + *(p++) = 'v'; + nflags++; + } + if(!strcmp(hb, "encrypted")) { + *(p++) = encrypted ? '+' : '-'; + *(p++) = 'h'; + nflags++; + } else if(!strcmp(hb, "trusted")) { + *(p++) = trusted ? '+' : '-'; + *(p++) = 'h'; + nflags++; + } + if(!strcmp(ob, "encrypted")) { + *(p++) = encrypted ? '+' : '-'; + *(p++) = 'o'; + nflags++; + } else if(!strcmp(ob, "trusted")) { + *(p++) = trusted ? '+' : '-'; + *(p++) = 'o'; + nflags++; + } + *p = '\0'; - if(!eflag) + p = g_malloc(nflags * (strlen(u->nick)+1) + 1); + *p = '\0'; + if(!p) return 0; - - if(tflag) { - irc_write( irc, ":%s!%s@%s MODE %s %c%c%c%c %s %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, e?'+':'-', eflag, t?'+':'-', tflag, u->nick, u->nick ); - } else { - irc_write( irc, ":%s!%s@%s MODE %s %c%c %s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, e?'+':'-', eflag, u->nick ); + for(i=0; inick); } + irc_write( irc, ":%s!%s@%s MODE %s %s%s", irc->mynick, irc->mynick, irc->myhost, + irc->channel, flags, p ); + g_free(p); return 1; } -- cgit v1.2.3 From 8521b02a157c92b9f6cddca043d30f7659ec2c25 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 17:46:10 +0100 Subject: - rename request/abort commands to connect/disconnect - support 'otr info' without argument for general info - some cosmetics --- otr.c | 319 ++++++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 183 insertions(+), 136 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index ccb65887..aaab8def 100644 --- a/otr.c +++ b/otr.c @@ -47,24 +47,22 @@ const char *op_account_name(void *opdata, const char *account, const char *proto /** otr sub-command handlers: **/ -/* TODO: void cmd_otr_keygen(irc_t *irc, char **args); */ -void cmd_otr_abort(irc_t *irc, char **args); /* TODO: does this cmd even make sense? */ -void cmd_otr_request(irc_t *irc, char **args); /* TODO: do we even need this? */ +void cmd_otr_connect(irc_t *irc, char **args); +void cmd_otr_disconnect(irc_t *irc, char **args); void cmd_otr_smp(irc_t *irc, char **args); void cmd_otr_trust(irc_t *irc, char **args); -/* TODO: void cmd_otr_affirm(irc_t *irc, char **args); */ -void cmd_otr_fprints(irc_t *irc, char **args); void cmd_otr_info(irc_t *irc, char **args); -void cmd_otr_policy(irc_t *irc, char **args); +/* void cmd_otr_forget(irc_t *irc, char **args); */ const command_t otr_commands[] = { - { "abort", 1, &cmd_otr_abort, 0 }, - { "request", 1, &cmd_otr_request, 0 }, - { "smp", 2, &cmd_otr_smp, 0 }, - { "trust", 6, &cmd_otr_trust, 0 }, - { "fprints", 0, &cmd_otr_fprints, 0 }, - { "info", 1, &cmd_otr_info, 0 }, - { "policy", 0, &cmd_otr_policy, 0 }, + { "connect", 1, &cmd_otr_connect, 0 }, + { "disconnect", 1, &cmd_otr_disconnect, 0 }, + { "smp", 2, &cmd_otr_smp, 0 }, + { "trust", 6, &cmd_otr_trust, 0 }, + { "info", 0, &cmd_otr_info, 0 }, + /* + { "forget", 1, &cmd_otr_forget, 0 }, + */ { NULL } }; @@ -100,6 +98,9 @@ struct im_connection *check_imc(void *opdata, const char *accountname, returns "handle/protocol" if not found */ const char *peernick(irc_t *irc, const char *handle, const char *protocol); +/* turn a hexadecimal digit into its numerical value */ +int hexval(char a); + /* determine the user_t for a given handle/protocol pair returns NULL if not found */ user_t *peeruser(irc_t *irc, const char *handle, const char *protocol); @@ -112,11 +113,16 @@ void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs) i.e. msgstate should be announced seperately */ int otr_update_modeflags(irc_t *irc, user_t *u); +/* show general info about the OTR subsystem; called by 'otr info' */ +void show_general_otr_info(irc_t *irc); + +/* show info about a given OTR context */ +void show_otr_context_info(irc_t *irc, ConnContext *ctx); + /* show the list of fingerprints associated with a given context */ void show_fingerprints(irc_t *irc, ConnContext *ctx); - /*** routines declared in otr.h: ***/ void otr_init(void) @@ -488,8 +494,8 @@ void op_gone_secure(void *opdata, ConnContext *context) u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, - "BUG: otr.c: op_gone_secure: user_t for %s/%s not found!", - context->username, context->protocol); + "BUG: otr.c: op_gone_secure: user_t for %s/%s/%s not found!", + context->username, context->protocol, context->accountname); return; } if(context->active_fingerprint->trust[0]) @@ -511,8 +517,8 @@ void op_gone_insecure(void *opdata, ConnContext *context) u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, - "BUG: otr.c: op_gone_insecure: user_t for %s/%s not found!", - context->username, context->protocol); + "BUG: otr.c: op_gone_insecure: user_t for %s/%s/%s not found!", + context->username, context->protocol, context->accountname); return; } u->encrypted = 0; @@ -532,8 +538,8 @@ void op_still_secure(void *opdata, ConnContext *context, int is_reply) u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, - "BUG: otr.c: op_still_secure: user_t for %s/%s not found!", - context->username, context->protocol); + "BUG: otr.c: op_still_secure: user_t for %s/%s/%s not found!", + context->username, context->protocol, context->accountname); return; } if(context->active_fingerprint->trust[0]) @@ -573,7 +579,7 @@ const char *op_account_name(void *opdata, const char *account, const char *proto /*** OTR sub-command handlers ***/ -void cmd_otr_abort(irc_t *irc, char **args) +void cmd_otr_disconnect(irc_t *irc, char **args) { user_t *u; @@ -587,7 +593,7 @@ void cmd_otr_abort(irc_t *irc, char **args) u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle); } -void cmd_otr_request(irc_t *irc, char **args) +void cmd_otr_connect(irc_t *irc, char **args) { user_t *u; @@ -651,20 +657,6 @@ void cmd_otr_smp(irc_t *irc, char **args) } } -int hexval(char a) -{ - int x=tolower(a); - - if(x>='a' && x<='f') - x = x - 'a' + 10; - else if(x>='0' && x<='9') - x = x - '0'; - else - return -1; - - return x; -} - void cmd_otr_trust(irc_t *irc, char **args) { user_t *u; @@ -725,112 +717,59 @@ void cmd_otr_trust(irc_t *irc, char **args) } } -void cmd_otr_fprints(irc_t *irc, char **args) +void cmd_otr_info(irc_t *irc, char **args) { - if(args[1]) { - /* list given buddy's fingerprints */ - user_t *u; - ConnContext *ctx; - - u = user_find(irc, args[1]); - if(!u || !u->ic) { - irc_usermsg(irc, "%s: unknown user", args[1]); - return; - } - - ctx = otrl_context_find(irc->otr_us, u->handle, - u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); - if(!ctx) { - irc_usermsg(irc, "no fingerprints"); - } else { - show_fingerprints(irc, ctx); - } + if(!args[1]) { + show_general_otr_info(irc); } else { - /* list all known fingerprints */ + char *arg = g_strdup(args[1]); + char *myhandle, *handle, *protocol; ConnContext *ctx; - for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) { - irc_usermsg(irc, "[%s]", peernick(irc, ctx->username, ctx->protocol)); - show_fingerprints(irc, ctx); - } - if(!irc->otr_us->context_root) { - irc_usermsg(irc, "no fingerprints"); + + /* interpret arg as 'user/protocol/account' if possible */ + protocol = strchr(arg, '/'); + if(protocol) { + *(protocol++) = '\0'; + myhandle = strchr(protocol, '/'); + if(!myhandle) { + /* TODO: try to find a unique account for this context */ + } } - } -} - -void cmd_otr_info(irc_t *irc, char **args) -{ - user_t *u; - ConnContext *ctx; - Fingerprint *fp; - char human[45]; - const char *offer_status; - const char *message_state; - const char *trust; - - if(!args) { - irc_usermsg(irc, "no args?!"); - return; - } - if(!args[1]) { - irc_usermsg(irc, "no args[1]?!"); - return; - } - u = user_find(irc, args[1]); - if(!u || !u->ic) { - irc_usermsg(irc, "%s: unknown user", args[1]); - return; - } - - ctx = otrl_context_find(irc->otr_us, u->handle, - u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); - if(!ctx) { - irc_usermsg(irc, "no otr context with %s", args[1]); - return; - } - - switch(ctx->otr_offer) { - case OFFER_NOT: offer_status="none sent"; break; - case OFFER_SENT: offer_status="awaiting reply"; break; - case OFFER_ACCEPTED: offer_status="accepted our offer"; break; - case OFFER_REJECTED: offer_status="ignored our offer"; break; - default: offer_status="?"; - } - - switch(ctx->msgstate) { - case OTRL_MSGSTATE_PLAINTEXT: message_state="cleartext"; break; - case OTRL_MSGSTATE_ENCRYPTED: message_state="encrypted"; break; - case OTRL_MSGSTATE_FINISHED: message_state="shut down"; break; - default: message_state="?"; - } - - irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1], - ctx->username, ctx->protocol, ctx->accountname, ctx->protocol); - irc_usermsg(irc, " otr offer status: %s", offer_status); - irc_usermsg(irc, " connection state: %s", message_state); - - if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { - irc_usermsg(irc, " protocol version: %d", ctx->protocol_version); - fp = ctx->active_fingerprint; - if(!fp) { - irc_usermsg(irc, " active f'print: none?"); + if(protocol && myhandle) { + *(myhandle++) = '\0'; + handle = arg; + ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no such context (%s %s %s)", handle, protocol, myhandle); + g_free(arg); + return; + } } else { - otrl_privkey_hash_to_human(human, fp->fingerprint); - if(!fp->trust || fp->trust[0] == '\0') { - trust="untrusted"; - } else { - trust=fp->trust; + user_t *u = user_find(irc, args[1]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[1]); + g_free(arg); + return; + } + ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no otr context with %s", args[1]); + g_free(arg); + return; } - irc_usermsg(irc, " active f'print: %s (%s)", human, trust); } + + /* show how we resolved the (nick) argument, if we did */ + if(handle!=arg) { + irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1], + ctx->username, ctx->protocol, ctx->accountname, ctx->protocol); + } + show_otr_context_info(irc, ctx); + g_free(arg); } } -void cmd_otr_policy(irc_t *irc, char **args) -{ - irc_usermsg(irc, "n/a: not implemented"); -} - /*** local helpers / subroutines: ***/ @@ -952,10 +891,12 @@ user_t *peeruser(irc_t *irc, const char *handle, const char *protocol) { user_t *u; + log_message(LOGLVL_DEBUG, "peeruser '%s' '%s'", handle, protocol); + for(u=irc->users; u; u=u->next) { struct prpl *prpl; if(!u->ic || !u->handle) - break; + continue; prpl = u->ic->acc->prpl; if(strcmp(prpl->name, protocol) == 0 && prpl->handle_cmp(u->handle, handle) == 0) { @@ -966,6 +907,20 @@ user_t *peeruser(irc_t *irc, const char *handle, const char *protocol) return NULL; } +int hexval(char a) +{ + int x=tolower(a); + + if(x>='a' && x<='f') + x = x - 'a' + 10; + else if(x>='0' && x<='9') + x = x - '0'; + else + return -1; + + return x; +} + const char *peernick(irc_t *irc, const char *handle, const char *protocol) { static char fallback[512]; @@ -1053,13 +1008,105 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx) trust=fp->trust; } if(fp == ctx->active_fingerprint) { - irc_usermsg(irc, "\x02%s (%s)\x02", human, trust); + irc_usermsg(irc, " \x02%s (%s)\x02", human, trust); } else { - irc_usermsg(irc, "%s (%s)", human, trust); + irc_usermsg(irc, " %s (%s)", human, trust); } } if(count==0) - irc_usermsg(irc, "no fingerprints"); + irc_usermsg(irc, " no fingerprints"); +} + +void show_general_otr_info(irc_t *irc) +{ + ConnContext *ctx; + OtrlPrivKey *key; + char human[45]; + + /* list all privkeys */ + irc_usermsg(irc, "\x1fprivate keys:\x1f"); + for(key=irc->otr_us->privkey_root; key; key=key->next) { + const char *hash; + + switch(key->pubkey_type) { + case OTRL_PUBKEY_TYPE_DSA: + irc_usermsg(irc, " %s/%s - DSA", key->accountname, key->protocol); + break; + default: + irc_usermsg(irc, " %s/%s - type %d", key->accountname, key->protocol, + key->pubkey_type); + } + + /* No, it doesn't make much sense to search for the privkey again by + account/protocol, but libotr currently doesn't provide a direct routine + for hashing a given 'OtrlPrivKey'... */ + hash = otrl_privkey_fingerprint(irc->otr_us, human, key->accountname, key->protocol); + if(hash) /* should always succeed */ + irc_usermsg(irc, " %s", human); + } + + /* list all contexts */ + irc_usermsg(irc, "%s", ""); + irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)"); + for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {\ + user_t *u; + char *userstring; + + u = peeruser(irc, ctx->username, ctx->protocol); + if(u) + userstring = g_strdup_printf("%s/%s/%s (%s)", + ctx->username, ctx->protocol, ctx->accountname, u->nick); + else + userstring = g_strdup_printf("%s/%s/%s", + ctx->username, ctx->protocol, ctx->accountname); + + if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + otrl_privkey_hash_to_human(human, ctx->active_fingerprint->fingerprint); + irc_usermsg(irc, " \x02%s\x02", userstring); + irc_usermsg(irc, " %s", human); + } else { + irc_usermsg(irc, " %s", userstring); + } + + g_free(userstring); + } +} + +void show_otr_context_info(irc_t *irc, ConnContext *ctx) +{ + switch(ctx->otr_offer) { + case OFFER_NOT: + irc_usermsg(irc, " otr offer status: none sent"); + break; + case OFFER_SENT: + irc_usermsg(irc, " otr offer status: awaiting reply"); + break; + case OFFER_ACCEPTED: + irc_usermsg(irc, " otr offer status: accepted our offer"); + break; + case OFFER_REJECTED: + irc_usermsg(irc, " otr offer status: ignored our offer"); + break; + default: + irc_usermsg(irc, " otr offer status: %d", ctx->otr_offer); + } + + switch(ctx->msgstate) { + case OTRL_MSGSTATE_PLAINTEXT: + irc_usermsg(irc, " connection state: cleartext"); + break; + case OTRL_MSGSTATE_ENCRYPTED: + irc_usermsg(irc, " connection state: encrypted (v%d)", ctx->protocol_version); + break; + case OTRL_MSGSTATE_FINISHED: + irc_usermsg(irc, " connection state: shut down"); + break; + default: + irc_usermsg(irc, " connection state: %d", ctx->msgstate); + } + + irc_usermsg(irc, " known fingerprints: (bold=active)"); + show_fingerprints(irc, ctx); } void otr_keygen(irc_t *irc, const char *handle, const char *protocol) -- cgit v1.2.3 From 94e7eb3aaefc753ea81ea8c9e38570407d4c9790 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 18:56:59 +0100 Subject: add 'otr keygen' command --- otr.c | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index aaab8def..6320b3fc 100644 --- a/otr.c +++ b/otr.c @@ -52,6 +52,7 @@ void cmd_otr_disconnect(irc_t *irc, char **args); void cmd_otr_smp(irc_t *irc, char **args); void cmd_otr_trust(irc_t *irc, char **args); void cmd_otr_info(irc_t *irc, char **args); +void cmd_otr_keygen(irc_t *irc, char **args); /* void cmd_otr_forget(irc_t *irc, char **args); */ const command_t otr_commands[] = { @@ -60,6 +61,7 @@ const command_t otr_commands[] = { { "smp", 2, &cmd_otr_smp, 0 }, { "trust", 6, &cmd_otr_trust, 0 }, { "info", 0, &cmd_otr_info, 0 }, + { "keygen", 1, &cmd_otr_keygen, 0 }, /* { "forget", 1, &cmd_otr_forget, 0 }, */ @@ -770,6 +772,32 @@ void cmd_otr_info(irc_t *irc, char **args) } } +void cmd_otr_keygen(irc_t *irc, char **args) +{ + int i, n; + account_t *a; + + n = atoi(args[1]); + if(n<0 || (!n && strcmp(args[1], "0"))) { + irc_usermsg(irc, "%s: invalid account number", args[1]); + return; + } + + a = irc->accounts; + for(i=0; inext); + if(!a) { + irc_usermsg(irc, "%s: no such account", args[1]); + return; + } + + if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) { + char *s = g_strdup_printf("account %d already has a key, replace it?", n); + query_add(irc, a->ic, s, yes_keygen, no_keygen, a); + } else { + otr_keygen(irc, a->user, a->prpl->name); + } +} + /*** local helpers / subroutines: ***/ @@ -1219,13 +1247,8 @@ void no_keygen(gpointer w, void *data) { account_t *acc = (account_t *)data; - irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s", + irc_usermsg(acc->irc, "keygen cancelled for %s/%s", acc->user, acc->prpl->name); - /* TODO: - irc_usermsg(acc->irc, "setting otr policy for %s/%s to \"never\"", - acc->user, acc->prpl->name); - set_setstr(acc->set, "otr_policy", "never"); - */ } -- cgit v1.2.3 From 5d6204020ac3439388666df54600ad65a384046b Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 22:12:13 +0100 Subject: remove cleartext fallbacks during keygen --- otr.c | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 6320b3fc..0f441e3d 100644 --- a/otr.c +++ b/otr.c @@ -256,15 +256,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha char *colormsg; if(!g_mutex_trylock(ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* fallback for non-otr clients */ - if(u && !u->encrypted) { - return g_strdup(msg); - } - - /* TODO: queue msgs received during keygen for later? */ - irc_usermsg(ic->irc, "otr msg from %s during keygen - dropped", + irc_usermsg(ic->irc, "otr keygen in progress - msg from %s dropped", peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; } @@ -312,17 +304,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m ConnContext *ctx = NULL; if(!g_mutex_trylock(ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* Fallback for non-otr clients. - Yes, we must be very sure this doesn't send stuff in the clear where it - shouldn't... */ - if(u && !u->encrypted) { - return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); - } - - /* otherwise refuse to send */ - irc_usermsg(ic->irc, "otr msg to %s not sent during keygen", + irc_usermsg(ic->irc, "otr keygen in progress - msg to %s not sent", peernick(ic->irc, handle, ic->acc->prpl->name)); return 1; } -- cgit v1.2.3 From 5bf5edfde81640c1d918a2a1a1bd8bcd450805e1 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 10 Feb 2008 22:54:28 +0100 Subject: log out all accounts when going into keygen --- otr.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 0f441e3d..66cdb645 100644 --- a/otr.c +++ b/otr.c @@ -124,6 +124,9 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx); /* show the list of fingerprints associated with a given context */ void show_fingerprints(irc_t *irc, ConnContext *ctx); +/* to log out accounts during keygen */ +extern void cmd_account(irc_t *irc, char **cmd); + /*** routines declared in otr.h: ***/ @@ -1121,14 +1124,12 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { + char *account_off[] = {"account", "off", NULL}; GError *err; GThread *thr; struct kgdata *kg; gint ev; - irc_usermsg(irc, "generating new otr privkey for %s/%s...", - handle, protocol); - kg = g_new0(struct kgdata, 1); if(!kg) { irc_usermsg(irc, "otr keygen failed: out of memory"); @@ -1166,6 +1167,13 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) return; } + /* tell the user what's happening, go comatose, and start the keygen */ + irc_usermsg(irc, "going comatose for otr key generation, this will take a moment"); + irc_usermsg(irc, "all accounts logging out, user commands disabled"); + cmd_account(irc, account_off); + irc_usermsg(irc, "generating new otr privkey for %s/%s...", + handle, protocol); + thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); if(!thr) { irc_usermsg(irc, "otr keygen failed: %s", err->message); -- cgit v1.2.3 From 8c2b1c32386b9cbb06e1e6f03b3f2926e7761461 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 15:36:19 +0100 Subject: honor simulate_netsplit for encrypted/trusted mode changes --- otr.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 66cdb645..eeef55bb 100644 --- a/otr.c +++ b/otr.c @@ -957,6 +957,7 @@ int otr_update_modeflags(irc_t *irc, user_t *u) char flags[7]; int nflags; char *p = flags; + char *from; int i; if(!strcmp(vb, "encrypted")) { @@ -996,8 +997,12 @@ int otr_update_modeflags(irc_t *irc, user_t *u) strcat(p, " "); strcat(p, u->nick); } - irc_write( irc, ":%s!%s@%s MODE %s %s%s", irc->mynick, irc->mynick, irc->myhost, - irc->channel, flags, p ); + if(set_getbool(&irc->set, "simulate_netsplit")) + from = g_strdup(irc->myhost); + else + from = g_strdup_printf("%s!%s@%s", irc->mynick, irc->mynick, irc->myhost); + irc_write(irc, ":%s MODE %s %s%s", from, irc->channel, flags, p); + g_free(from); g_free(p); return 1; -- cgit v1.2.3 From c59530844d25ae814141cf56f0fa810968e8df55 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 11 Feb 2008 22:20:35 +0100 Subject: - use a recursive otr_mutex - implement 'otr forget fingerprint' and 'otr forget context' commands --- otr.c | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 223 insertions(+), 38 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index eeef55bb..95c5289e 100644 --- a/otr.c +++ b/otr.c @@ -53,7 +53,7 @@ void cmd_otr_smp(irc_t *irc, char **args); void cmd_otr_trust(irc_t *irc, char **args); void cmd_otr_info(irc_t *irc, char **args); void cmd_otr_keygen(irc_t *irc, char **args); -/* void cmd_otr_forget(irc_t *irc, char **args); */ +void cmd_otr_forget(irc_t *irc, char **args); const command_t otr_commands[] = { { "connect", 1, &cmd_otr_connect, 0 }, @@ -62,9 +62,7 @@ const command_t otr_commands[] = { { "trust", 6, &cmd_otr_trust, 0 }, { "info", 0, &cmd_otr_info, 0 }, { "keygen", 1, &cmd_otr_keygen, 0 }, - /* - { "forget", 1, &cmd_otr_forget, 0 }, - */ + { "forget", 2, &cmd_otr_forget, 0 }, { NULL } }; @@ -73,10 +71,13 @@ const command_t otr_commands[] = { /* start background thread to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); + /* keygen thread main func */ gpointer otr_keygen_thread_func(gpointer data); + /* mainloop handler for when keygen thread finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); + /* data to be passed to otr_keygen_thread_func */ struct kgdata { irc_t *irc; /* access to OTR userstate */ @@ -88,9 +89,10 @@ struct kgdata { gcry_error_t result; /* return value of otrl_privkey_generate */ }; -/* yes/no handlers for "generate key now?" */ +/* some yes/no handlers */ void yes_keygen(gpointer w, void *data); -void no_keygen(gpointer w, void *data); +void yes_forget_fingerprint(gpointer w, void *data); +void yes_forget_context(gpointer w, void *data); /* helper to make sure accountname and protocol match the incoming "opdata" */ struct im_connection *check_imc(void *opdata, const char *accountname, @@ -124,6 +126,9 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx); /* show the list of fingerprints associated with a given context */ void show_fingerprints(irc_t *irc, ConnContext *ctx); +/* find a fingerprint by prefix (given as any number of hex strings) */ +Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args); + /* to log out accounts during keygen */ extern void cmd_account(irc_t *irc, char **cmd); @@ -258,7 +263,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; - if(!g_mutex_trylock(ic->irc->otr_mutex)) { + if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { irc_usermsg(ic->irc, "otr keygen in progress - msg from %s dropped", peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; @@ -272,11 +277,11 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(ignore_msg) { /* this was an internal OTR protocol message */ - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return NULL; } else if(!newmsg) { /* this was a non-OTR message */ - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return g_strdup(msg); } else { /* OTR has processed this message */ @@ -295,7 +300,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha colormsg = g_strdup(newmsg); } otrl_message_free(newmsg); - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return colormsg; } } @@ -306,7 +311,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m char *otrmsg = NULL; ConnContext *ctx = NULL; - if(!g_mutex_trylock(ic->irc->otr_mutex)) { + if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { irc_usermsg(ic->irc, "otr keygen in progress - msg to %s not sent", peernick(ic->irc, handle, ic->acc->prpl->name)); return 1; @@ -316,7 +321,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m ic->acc->user, ic->acc->prpl->name, handle, msg, NULL, &otrmsg, NULL, NULL); if(st) { - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -327,7 +332,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m if(otrmsg) { if(!ctx) { otrl_message_free(otrmsg); - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return 1; } st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx, @@ -338,7 +343,7 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); } - g_mutex_unlock(ic->irc->otr_mutex); + g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -358,7 +363,7 @@ void cmd_otr(irc_t *irc, char **args) } if(!cmd->command) { - irc_usermsg(irc, "%s %s: unknown subcommand, see \x02help otr\x02", + irc_usermsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02", args[0], args[1]); return; } @@ -391,7 +396,7 @@ void op_create_privkey(void *opdata, const char *accountname, s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?", accountname, protocol); - query_add(ic->irc, ic, s, yes_keygen, no_keygen, ic->acc); + query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc); } int op_is_logged_in(void *opdata, const char *accountname, @@ -475,6 +480,7 @@ void op_gone_secure(void *opdata, ConnContext *context) struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); user_t *u; + const char *trust; log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); @@ -485,7 +491,9 @@ void op_gone_secure(void *opdata, ConnContext *context) context->username, context->protocol, context->accountname); return; } - if(context->active_fingerprint->trust[0]) + + trust = context->active_fingerprint->trust; + if(trust && trust[0]) u->encrypted = 2; else u->encrypted = 1; @@ -578,6 +586,17 @@ void cmd_otr_disconnect(irc_t *irc, char **args) otrl_message_disconnect(irc->otr_us, &global.otr_ops, u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle); + + /* for some reason, libotr (3.1.0) doesn't do this itself: */ + if(u->encrypted) { + ConnContext *ctx; + ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(ctx) + op_gone_insecure(u->ic, ctx); + else /* huh? */ + u->encrypted = 0; + } } void cmd_otr_connect(irc_t *irc, char **args) @@ -718,16 +737,13 @@ void cmd_otr_info(irc_t *irc, char **args) if(protocol) { *(protocol++) = '\0'; myhandle = strchr(protocol, '/'); - if(!myhandle) { - /* TODO: try to find a unique account for this context */ - } } if(protocol && myhandle) { *(myhandle++) = '\0'; handle = arg; ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL); if(!ctx) { - irc_usermsg(irc, "no such context (%s %s %s)", handle, protocol, myhandle); + irc_usermsg(irc, "no such context"); g_free(arg); return; } @@ -777,12 +793,125 @@ void cmd_otr_keygen(irc_t *irc, char **args) if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) { char *s = g_strdup_printf("account %d already has a key, replace it?", n); - query_add(irc, a->ic, s, yes_keygen, no_keygen, a); + query_add(irc, a->ic, s, yes_keygen, NULL, a); } else { otr_keygen(irc, a->user, a->prpl->name); } } +void yes_forget_fingerprint(gpointer w, void *data) +{ + struct im_connection *ic = (struct im_connection *)w; + Fingerprint *fp = (Fingerprint *)data; + + if(fp == fp->context->active_fingerprint) { + irc_usermsg(ic->irc, "that fingerprint is active, terminate otr connection first"); + return; + } + + otrl_context_forget_fingerprint(fp, 0); +} + +void yes_forget_context(gpointer w, void *data) +{ + struct im_connection *ic = (struct im_connection *)w; + ConnContext *ctx = (ConnContext *)data; + + if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + irc_usermsg(ic->irc, "active otr connection with %s, terminate it first", + peernick(ic->irc, ctx->username, ctx->protocol)); + return; + } + + if(ctx->msgstate == OTRL_MSGSTATE_FINISHED) + otrl_context_force_plaintext(ctx); + otrl_context_forget(ctx); +} + +void cmd_otr_forget(irc_t *irc, char **args) +{ + if(!strcmp(args[1], "fingerprint")) + { + user_t *u; + ConnContext *ctx; + Fingerprint *fp; + char human[54]; + char *s; + + if(!args[3]) { + irc_usermsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]); + return; + } + + u = user_find(irc, args[2]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[2]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no otr context with %s", args[2]); + return; + } + + fp = match_fingerprint(irc, ctx, ((const char **)args)+3); + if(!fp) { + /* match_fingerprint does error messages */ + return; + } + + if(fp == ctx->active_fingerprint) { + irc_usermsg(irc, "that fingerprint is active, terminate otr connection first"); + return; + } + + otrl_privkey_hash_to_human(human, fp->fingerprint); + s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human); + query_add(irc, u->ic, s, yes_forget_fingerprint, NULL, fp); + } + + else if(!strcmp(args[1], "context")) + { + user_t *u; + ConnContext *ctx; + char *s; + + u = user_find(irc, args[2]); + if(!u || !u->ic) { + irc_usermsg(irc, "%s: unknown user", args[2]); + return; + } + + ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + u->ic->acc->prpl->name, 0, NULL, NULL, NULL); + if(!ctx) { + irc_usermsg(irc, "no otr context with %s", args[2]); + return; + } + + if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + irc_usermsg(irc, "active otr connection with %s, terminate it first", args[2]); + return; + } + + s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]); + query_add(irc, u->ic, s, yes_forget_context, NULL, ctx); + } + + else if(!strcmp(args[1], "key")) + { + irc_usermsg(irc, "n/a: TODO"); + } + + else + { + irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02", + args[0], args[1]); + } +} + /*** local helpers / subroutines: ***/ @@ -955,7 +1084,7 @@ int otr_update_modeflags(irc_t *irc, user_t *u) int encrypted = u->encrypted; int trusted = u->encrypted > 1; char flags[7]; - int nflags; + int nflags=0; char *p = flags; char *from; int i; @@ -1035,6 +1164,72 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx) irc_usermsg(irc, " no fingerprints"); } +Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) +{ + Fingerprint *fp, *fp2; + char human[45]; + char prefix[45], *p; + int n; + int i,j; + + /* assemble the args into a prefix in standard "human" form */ + n=0; + p=prefix; + for(i=0; args[i]; i++) { + for(j=0; args[i][j]; j++) { + char c = toupper(args[i][j]); + + if(n>=40) { + irc_usermsg(irc, "too many fingerprint digits given, expected at most 40"); + return NULL; + } + + if( (c>='A' && c<='F') || (c>='0' && c<='9') ) { + *(p++) = c; + } else { + irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1); + return NULL; + } + + n++; + if(n%8 == 0) + *(p++) = ' '; + } + } + *p = '\0'; + log_message(LOGLVL_DEBUG, "match_fingerprint '%s'", prefix); + log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix)); + + /* find first fingerprint with the given prefix */ + n = strlen(prefix); + for(fp=&ctx->fingerprint_root; fp; fp=fp->next) { + if(!fp->fingerprint) + continue; + otrl_privkey_hash_to_human(human, fp->fingerprint); + if(!strncmp(prefix, human, n)) + break; + } + if(!fp) { + irc_usermsg(irc, "%s: no match", prefix); + return NULL; + } + + /* make sure the match, if any, is unique */ + for(fp2=fp->next; fp2; fp2=fp2->next) { + if(!fp2->fingerprint) + continue; + otrl_privkey_hash_to_human(human, fp2->fingerprint); + if(!strncmp(prefix, human, n)) + break; + } + if(fp2) { + irc_usermsg(irc, "%s: multiple matches", prefix); + return NULL; + } + + return fp; +} + void show_general_otr_info(irc_t *irc) { ConnContext *ctx; @@ -1079,9 +1274,7 @@ void show_general_otr_info(irc_t *irc) ctx->username, ctx->protocol, ctx->accountname); if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { - otrl_privkey_hash_to_human(human, ctx->active_fingerprint->fingerprint); irc_usermsg(irc, " \x02%s\x02", userstring); - irc_usermsg(irc, " %s", human); } else { irc_usermsg(irc, " %s", userstring); } @@ -1123,7 +1316,7 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) irc_usermsg(irc, " connection state: %d", ctx->msgstate); } - irc_usermsg(irc, " known fingerprints: (bold=active)"); + irc_usermsg(irc, " fingerprints: (bold=active)"); show_fingerprints(irc, ctx); } @@ -1158,7 +1351,7 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) g_free(kg); return; } - kg->done = FALSE; + kg->done = 0; /* Poll for completion of the thread periodically. I would have preferred to just wait on a pipe but this way it's portable to Windows. *sigh* @@ -1194,15 +1387,15 @@ gpointer otr_keygen_thread_func(gpointer data) struct kgdata *kg = (struct kgdata *)data; /* lock OTR subsystem and do the work */ - g_mutex_lock(kg->irc->otr_mutex); + g_static_rec_mutex_lock(&kg->irc->otr_mutex); kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, kg->protocol); - g_mutex_unlock(kg->irc->otr_mutex); + g_static_rec_mutex_unlock(&kg->irc->otr_mutex); /* OTR enabled again */ /* notify mainloop */ g_mutex_lock(kg->mutex); - kg->done = TRUE; + kg->done = 1; g_mutex_unlock(kg->mutex); return NULL; @@ -1238,14 +1431,6 @@ void yes_keygen(gpointer w, void *data) otr_keygen(acc->irc, acc->user, acc->prpl->name); } -void no_keygen(gpointer w, void *data) -{ - account_t *acc = (account_t *)data; - - irc_usermsg(acc->irc, "keygen cancelled for %s/%s", - acc->user, acc->prpl->name); -} - #else /* WITH_OTR undefined */ -- cgit v1.2.3 From 5f4eedefd2a444c213f65283a3d942d6287e6ea2 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Tue, 12 Feb 2008 00:16:23 +0100 Subject: - add nonfunctional 'otr forget key' implementation - add 'color_encrypted' setting --- otr.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 12 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 95c5289e..adba7b68 100644 --- a/otr.c +++ b/otr.c @@ -93,6 +93,7 @@ struct kgdata { void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); void yes_forget_context(gpointer w, void *data); +void yes_forget_key(gpointer w, void *data); /* helper to make sure accountname and protocol match the incoming "opdata" */ struct im_connection *check_imc(void *opdata, const char *accountname, @@ -129,6 +130,9 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx); /* find a fingerprint by prefix (given as any number of hex strings) */ Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args); +/* find a private key by fingerprint prefix (given as any number of hex strings) */ +OtrlPrivKey *match_privkey(irc_t *irc, const char **args); + /* to log out accounts during keygen */ extern void cmd_account(irc_t *irc, char **cmd); @@ -287,7 +291,8 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha /* OTR has processed this message */ ConnContext *context = otrl_context_find(ic->irc->otr_us, handle, ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL); - if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) { + if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED && + set_getbool(&ic->irc->set, "color_encrypted")) { /* color according to f'print trust */ char color; const char *trust = context->active_fingerprint->trust; @@ -394,8 +399,8 @@ void op_create_privkey(void *opdata, const char *accountname, log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol); - s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?", - accountname, protocol); + s = g_strdup_printf("oops, no otr privkey for %s - generate one now?", + accountname); query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc); } @@ -793,7 +798,7 @@ void cmd_otr_keygen(irc_t *irc, char **args) if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) { char *s = g_strdup_printf("account %d already has a key, replace it?", n); - query_add(irc, a->ic, s, yes_keygen, NULL, a); + query_add(irc, NULL, s, yes_keygen, NULL, a); } else { otr_keygen(irc, a->user, a->prpl->name); } @@ -801,11 +806,11 @@ void cmd_otr_keygen(irc_t *irc, char **args) void yes_forget_fingerprint(gpointer w, void *data) { - struct im_connection *ic = (struct im_connection *)w; + irc_t *irc = (irc_t *)w; Fingerprint *fp = (Fingerprint *)data; if(fp == fp->context->active_fingerprint) { - irc_usermsg(ic->irc, "that fingerprint is active, terminate otr connection first"); + irc_usermsg(irc, "that fingerprint is active, terminate otr connection first"); return; } @@ -814,12 +819,12 @@ void yes_forget_fingerprint(gpointer w, void *data) void yes_forget_context(gpointer w, void *data) { - struct im_connection *ic = (struct im_connection *)w; + irc_t *irc = (irc_t *)w; ConnContext *ctx = (ConnContext *)data; if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) { - irc_usermsg(ic->irc, "active otr connection with %s, terminate it first", - peernick(ic->irc, ctx->username, ctx->protocol)); + irc_usermsg(irc, "active otr connection with %s, terminate it first", + peernick(irc, ctx->username, ctx->protocol)); return; } @@ -828,6 +833,15 @@ void yes_forget_context(gpointer w, void *data) otrl_context_forget(ctx); } +void yes_forget_key(gpointer w, void *data) +{ + OtrlPrivKey *key = (OtrlPrivKey *)data; + + /* FIXME: For some reason which /completely eludes me/, this call keeps + barfing on the gcry_sexp_release inside (invalid pointer free). */ + otrl_privkey_forget(key); +} + void cmd_otr_forget(irc_t *irc, char **args) { if(!strcmp(args[1], "fingerprint")) @@ -869,7 +883,7 @@ void cmd_otr_forget(irc_t *irc, char **args) otrl_privkey_hash_to_human(human, fp->fingerprint); s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human); - query_add(irc, u->ic, s, yes_forget_fingerprint, NULL, fp); + query_add(irc, NULL, s, yes_forget_fingerprint, NULL, fp); } else if(!strcmp(args[1], "context")) @@ -897,12 +911,28 @@ void cmd_otr_forget(irc_t *irc, char **args) } s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]); - query_add(irc, u->ic, s, yes_forget_context, NULL, ctx); + query_add(irc, NULL, s, yes_forget_context, NULL, ctx); } else if(!strcmp(args[1], "key")) { - irc_usermsg(irc, "n/a: TODO"); + OtrlPrivKey *key; + char *s; + + key = match_privkey(irc, ((const char **)args)+2); + if(!key) { + /* match_privkey does error messages */ + return; + } + + /* TODO: Find out why 'otr forget key' barfs (cf. yes_forget_key) */ + irc_usermsg(irc, "otr %s %s: not implemented, please edit \x02%s%s.otr_keys\x02 manually :-/", + args[0], args[1], global.conf->configdir, irc->nick); + return; + + s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?", + key->accountname, key->protocol); + query_add(irc, NULL, s, yes_forget_key, NULL, key); } else @@ -1230,6 +1260,72 @@ Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) return fp; } +OtrlPrivKey *match_privkey(irc_t *irc, const char **args) +{ + OtrlPrivKey *k, *k2; + char human[45]; + char prefix[45], *p; + int n; + int i,j; + + /* assemble the args into a prefix in standard "human" form */ + n=0; + p=prefix; + for(i=0; args[i]; i++) { + for(j=0; args[i][j]; j++) { + char c = toupper(args[i][j]); + + if(n>=40) { + irc_usermsg(irc, "too many fingerprint digits given, expected at most 40"); + return NULL; + } + + if( (c>='A' && c<='F') || (c>='0' && c<='9') ) { + *(p++) = c; + } else { + irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1); + return NULL; + } + + n++; + if(n%8 == 0) + *(p++) = ' '; + } + } + *p = '\0'; + log_message(LOGLVL_DEBUG, "match_privkey '%s'", prefix); + log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix)); + + /* find first key which matches the given prefix */ + n = strlen(prefix); + for(k=irc->otr_us->privkey_root; k; k=k->next) { + p = otrl_privkey_fingerprint(irc->otr_us, human, k->accountname, k->protocol); + if(!p) /* gah! :-P */ + continue; + if(!strncmp(prefix, human, n)) + break; + } + if(!k) { + irc_usermsg(irc, "%s: no match", prefix); + return NULL; + } + + /* make sure the match, if any, is unique */ + for(k2=k->next; k2; k2=k2->next) { + p = otrl_privkey_fingerprint(irc->otr_us, human, k2->accountname, k2->protocol); + if(!p) /* gah! :-P */ + continue; + if(!strncmp(prefix, human, n)) + break; + } + if(k2) { + irc_usermsg(irc, "%s: multiple matches", prefix); + return NULL; + } + + return k; +} + void show_general_otr_info(irc_t *irc) { ConnContext *ctx; -- cgit v1.2.3 From e2b15bb7ebd347dd044167575068ef87ffd9301c Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Tue, 12 Feb 2008 01:01:35 +0100 Subject: - add global policy setting - add copyright and author notices to otr.h and otr.c --- otr.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 54 insertions(+), 8 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index adba7b68..757caca3 100644 --- a/otr.c +++ b/otr.c @@ -1,3 +1,40 @@ + /********************************************************************\ + * BitlBee -- An IRC to other IM-networks gateway * + * * + * Copyright 2002-2008 Wilmer van der Gaast and others * + \********************************************************************/ + +/* + OTR support (cf. http://www.cypherpunks.ca/otr/) + 2008, Sven Moritz Hallberg + + files used to store OTR data: + /.otr_keys + /.otr_fprints + + top-level todos: (search for TODO for more ;-)) + integrate otr_load/otr_save with existing storage backends + per-account policy settings + per-user policy settings +*/ + +/* + 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 with + the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL; + if not, write to the Free Software Foundation, Inc., 59 Temple Place, + Suite 330, Boston, MA 02111-1307 USA +*/ + #include "bitlbee.h" #ifdef WITH_OTR #include "irc.h" @@ -5,12 +42,6 @@ #include #include -/** -files used to store OTR data: - $configdir/$nick.otr_keys - $configdir/$nick.otr_fprints - **/ - /** OTR interface routines for the OtrlMessageAppOps struct: **/ @@ -344,7 +375,8 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL); otrl_message_free(otrmsg); } else { - /* yeah, well, some const casts as usual... ;-) */ + /* note: otrl_message_sending handles policy, so that if REQUIRE_ENCRYPTION is set, + this case does not occur */ st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); } @@ -387,7 +419,19 @@ void cmd_otr(irc_t *irc, char **args) OtrlPolicy op_policy(void *opdata, ConnContext *context) { - /* TODO: OTR policy configurable */ + struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); + const char *p; + + p = set_getstr(&ic->irc->set, "otr_policy"); + if(!strcmp(p, "never")) + return OTRL_POLICY_NEVER; + if(!strcmp(p, "opportunistic")) + return OTRL_POLICY_OPPORTUNISTIC; + if(!strcmp(p, "manual")) + return OTRL_POLICY_MANUAL; + if(!strcmp(p, "always")) + return OTRL_POLICY_ALWAYS; + return OTRL_POLICY_OPPORTUNISTIC; } @@ -857,6 +901,7 @@ void cmd_otr_forget(irc_t *irc, char **args) return; } + /* TODO: allow context specs ("user/proto/account") in 'otr forget fingerprint'? */ u = user_find(irc, args[2]); if(!u || !u->ic) { irc_usermsg(irc, "%s: unknown user", args[2]); @@ -892,6 +937,7 @@ void cmd_otr_forget(irc_t *irc, char **args) ConnContext *ctx; char *s; + /* TODO: allow context specs ("user/proto/account") in 'otr forget contex'? */ u = user_find(irc, args[2]); if(!u || !u->ic) { irc_usermsg(irc, "%s: unknown user", args[2]); -- cgit v1.2.3 From ef93a2f828b62e74f62638e7fbbe63e47f92194f Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 14 Feb 2008 19:48:34 +0100 Subject: chmod 0600 otr save files --- otr.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 757caca3..943a9c13 100644 --- a/otr.c +++ b/otr.c @@ -249,6 +249,7 @@ void otr_save(irc_t *irc) if(e) { log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e)); } + chmod(s, 0600); } void otr_remove(const char *nick) @@ -1532,6 +1533,7 @@ gpointer otr_keygen_thread_func(gpointer data) g_static_rec_mutex_lock(&kg->irc->otr_mutex); kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, kg->protocol); + chmod(kg->keyfile, 0600); g_static_rec_mutex_unlock(&kg->irc->otr_mutex); /* OTR enabled again */ -- cgit v1.2.3 From f3597a161cd13e076db626fed3d833cac0d62d6c Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 14 Feb 2008 22:01:21 +0100 Subject: revert keygen behaviour to old (lax) behavior --- otr.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 943a9c13..e414bb39 100644 --- a/otr.c +++ b/otr.c @@ -164,9 +164,6 @@ Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args); /* find a private key by fingerprint prefix (given as any number of hex strings) */ OtrlPrivKey *match_privkey(irc_t *irc, const char **args); -/* to log out accounts during keygen */ -extern void cmd_account(irc_t *irc, char **cmd); - /*** routines declared in otr.h: ***/ @@ -300,7 +297,14 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha char *colormsg; if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - irc_usermsg(ic->irc, "otr keygen in progress - msg from %s dropped", + user_t *u = user_findhandle(ic, handle); + + /* fallback for non-otr clients */ + if(u && !u->encrypted) { + return g_strdup(msg); + } + + irc_usermsg(ic->irc, "encrypted msg from %s during keygen - dropped", peernick(ic->irc, handle, ic->acc->prpl->name)); return NULL; } @@ -349,7 +353,15 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m ConnContext *ctx = NULL; if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - irc_usermsg(ic->irc, "otr keygen in progress - msg to %s not sent", + user_t *u = user_findhandle(ic, handle); + + /* Fallback for non-otr clients. + Yes, this better shouldn't send private stuff in the clear... */ + if(u && !u->encrypted) { + return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); + } + + irc_usermsg(ic->irc, "encrypted message to %s during keygen - not sent", peernick(ic->irc, handle, ic->acc->prpl->name)); return 1; } @@ -1465,12 +1477,13 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { - char *account_off[] = {"account", "off", NULL}; GError *err; GThread *thr; struct kgdata *kg; gint ev; + irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); + kg = g_new0(struct kgdata, 1); if(!kg) { irc_usermsg(irc, "otr keygen failed: out of memory"); @@ -1508,13 +1521,6 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) return; } - /* tell the user what's happening, go comatose, and start the keygen */ - irc_usermsg(irc, "going comatose for otr key generation, this will take a moment"); - irc_usermsg(irc, "all accounts logging out, user commands disabled"); - cmd_account(irc, account_off); - irc_usermsg(irc, "generating new otr privkey for %s/%s...", - handle, protocol); - thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); if(!thr) { irc_usermsg(irc, "otr keygen failed: %s", err->message); -- cgit v1.2.3 From 6c91e6e59a1216100d851636d2824ce49ad7ce30 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Fri, 15 Feb 2008 01:45:21 +0100 Subject: otr_load error handling + stonedcoder copyright notice --- otr.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index e414bb39..fcf653c5 100644 --- a/otr.c +++ b/otr.c @@ -6,7 +6,9 @@ /* OTR support (cf. http://www.cypherpunks.ca/otr/) + 2008, Sven Moritz Hallberg + (c) and funded by stonedcoder.org files used to store OTR data: /.otr_keys @@ -214,17 +216,20 @@ void otr_load(irc_t *irc) char s[512]; account_t *a; gcry_error_t e; + int eno; log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr_us, s); - if(e && e!=ENOENT) { + eno = gcry_error_code_to_errno(e); + if(e && eno!=ENOENT) { log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); - if(e && e!=ENOENT) { + eno = gcry_error_code_to_errno(e); + if(e && eno!=ENOENT) { log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); } @@ -1471,7 +1476,7 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) irc_usermsg(irc, " connection state: %d", ctx->msgstate); } - irc_usermsg(irc, " fingerprints: (bold=active)"); + irc_usermsg(irc, " fingerprints: (bold=active)"); show_fingerprints(irc, ctx); } -- cgit v1.2.3 From 522a00f1b1163cedf15a86329c0097601eb7940b Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Fri, 15 Feb 2008 10:27:26 +0100 Subject: remove thread-based keygen replace it with a process-based stub --- otr.c | 190 ++++++++++++------------------------------------------------------ 1 file changed, 33 insertions(+), 157 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index fcf653c5..c4e8a20a 100644 --- a/otr.c +++ b/otr.c @@ -102,26 +102,12 @@ const command_t otr_commands[] = { /** misc. helpers/subroutines: **/ -/* start background thread to generate a (new) key for a given account */ +/* start background process to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); -/* keygen thread main func */ -gpointer otr_keygen_thread_func(gpointer data); - -/* mainloop handler for when keygen thread finishes */ +/* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); -/* data to be passed to otr_keygen_thread_func */ -struct kgdata { - irc_t *irc; /* access to OTR userstate */ - char *keyfile; /* free me! */ - const char *handle; /* don't free! */ - const char *protocol; /* don't free! */ - GMutex *mutex; /* lock for the 'done' flag, free me! */ - int done; /* is the thread done? */ - gcry_error_t result; /* return value of otrl_privkey_generate */ -}; - /* some yes/no handlers */ void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); @@ -171,7 +157,6 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args); void otr_init(void) { - if(!g_thread_supported()) g_thread_init(NULL); OTRL_INIT; /* fill global OtrlMessageAppOps */ @@ -195,42 +180,24 @@ void otr_init(void) global.otr_ops.account_name_free = NULL; } -/* Notice on the otr_mutex: - - The incoming/outgoing message handlers try to lock the otr_mutex. If they succeed, - this will prevent a concurrent keygen (possibly spawned by that very command) - from messing up the userstate. If the lock fails, that means there already is - a keygen in progress. Instead of blocking for an unknown time, they - will bail out gracefully, informing the user of this temporary "coma". - TODO: Hold back incoming/outgoing messages and process them when keygen completes? - - The other routines do not lock the otr_mutex themselves, it is done as a - catch-all in the root command handler. Rationale: - a) it's easy to code - b) it makes it obvious that no command can get its userstate corrupted - c) the "irc" struct is readily available there for feedback to the user - */ - void otr_load(irc_t *irc) { char s[512]; account_t *a; gcry_error_t e; - int eno; + gcry_error_t enoent = gcry_error_from_errno(ENOENT); log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr_us, s); - eno = gcry_error_code_to_errno(e); - if(e && eno!=ENOENT) { - log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); + if(e && e!=enoent) { + irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); - eno = gcry_error_code_to_errno(e); - if(e && eno!=ENOENT) { - log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e)); + if(e && e!=enoent) { + irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } /* check for otr keys on all accounts */ @@ -249,7 +216,7 @@ void otr_save(irc_t *irc) g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_write_fingerprints(irc->otr_us, s); if(e) { - log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e)); + irc_usermsg(irc, "otr save: %s: %s", s, gcry_strerror(e)); } chmod(s, 0600); } @@ -301,19 +268,6 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; - if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* fallback for non-otr clients */ - if(u && !u->encrypted) { - return g_strdup(msg); - } - - irc_usermsg(ic->irc, "encrypted msg from %s during keygen - dropped", - peernick(ic->irc, handle, ic->acc->prpl->name)); - return NULL; - } - ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, &tlvs, NULL, NULL); @@ -322,11 +276,9 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(ignore_msg) { /* this was an internal OTR protocol message */ - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return NULL; } else if(!newmsg) { /* this was a non-OTR message */ - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return g_strdup(msg); } else { /* OTR has processed this message */ @@ -346,7 +298,6 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha colormsg = g_strdup(newmsg); } otrl_message_free(newmsg); - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return colormsg; } } @@ -357,25 +308,10 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m char *otrmsg = NULL; ConnContext *ctx = NULL; - if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) { - user_t *u = user_findhandle(ic, handle); - - /* Fallback for non-otr clients. - Yes, this better shouldn't send private stuff in the clear... */ - if(u && !u->encrypted) { - return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags); - } - - irc_usermsg(ic->irc, "encrypted message to %s during keygen - not sent", - peernick(ic->irc, handle, ic->acc->prpl->name)); - return 1; - } - st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, NULL, &otrmsg, NULL, NULL); if(st) { - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -386,7 +322,6 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m if(otrmsg) { if(!ctx) { otrl_message_free(otrmsg); - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return 1; } st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx, @@ -398,7 +333,6 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags ); } - g_static_rec_mutex_unlock(&ic->irc->otr_mutex); return st; } @@ -1482,101 +1416,43 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { - GError *err; - GThread *thr; - struct kgdata *kg; - gint ev; irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); - - kg = g_new0(struct kgdata, 1); - if(!kg) { - irc_usermsg(irc, "otr keygen failed: out of memory"); - return; - } + irc_usermsg(irc, "n/a: not implemented"); + return; - /* Assemble the job description to be passed to thread and handler */ - kg->irc = irc; - kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick); - if(!kg->keyfile) { - irc_usermsg(irc, "otr keygen failed: out of memory"); - g_free(kg); - return; - } - kg->handle = handle; - kg->protocol = protocol; - kg->mutex = g_mutex_new(); - if(!kg->mutex) { - irc_usermsg(irc, "otr keygen failed: couldn't create mutex"); - g_free(kg->keyfile); - g_free(kg); - return; - } - kg->done = 0; - - /* Poll for completion of the thread periodically. I would have preferred - to just wait on a pipe but this way it's portable to Windows. *sigh* - */ - ev = b_timeout_add(1000, &keygen_finish_handler, kg); - if(!ev) { - irc_usermsg(irc, "otr keygen failed: couldn't register timeout"); - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - return; - } - - thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err); - if(!thr) { - irc_usermsg(irc, "otr keygen failed: %s", err->message); - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - b_event_remove(ev); - } -} - -gpointer otr_keygen_thread_func(gpointer data) -{ - struct kgdata *kg = (struct kgdata *)data; + /* see if we already have a keygen child running. if not, start one and put a + handler on its output. + + b_input_add(fd, GAIM_INPUT_READ, keygen_finish_handler, NULL); - /* lock OTR subsystem and do the work */ - g_static_rec_mutex_lock(&kg->irc->otr_mutex); - kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, - kg->protocol); - chmod(kg->keyfile, 0600); - g_static_rec_mutex_unlock(&kg->irc->otr_mutex); - /* OTR enabled again */ + generate a fresh temp file name for our new key and save our current keys to it. + send the child filename and accountname/protocol for the new key + increment 'ntodo' */ - /* notify mainloop */ - g_mutex_lock(kg->mutex); - kg->done = 1; - g_mutex_unlock(kg->mutex); + /* in the child: + read filename, accountname, protocol from input, and start work: + + result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, + kg->protocol); + + when done, send filename, accountname, and protocol to output. */ - return NULL; } gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) { - struct kgdata *kg = (struct kgdata *)data; - int done; - - g_mutex_lock(kg->mutex); - done = kg->done; - g_mutex_unlock(kg->mutex); - if(kg->done) { - if(kg->result) { - irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result)); - } else { + /* in the handler: + read filename, accountname, and protocol from child output + print a message to the user irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); - } - g_free(kg->keyfile); - g_mutex_free(kg->mutex); - g_free(kg); - return FALSE; /* unregister timeout */ - } + load the file into userstate + call otr_save. + remove the tempfile. + decrement 'ntodo' + if 'ntodo' reaches zero, send SIGTERM to the child, waitpid for it, return FALSE */ - return TRUE; /* still working, continue checking */ + return TRUE; /* still working, keep watching */ } void yes_keygen(gpointer w, void *data) -- cgit v1.2.3 From 27db43361a3fdd3420b12aa5bf151dce4545273f Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Fri, 15 Feb 2008 18:36:18 +0100 Subject: implement background keygen via child process --- otr.c | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 147 insertions(+), 26 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index c4e8a20a..26ce92de 100644 --- a/otr.c +++ b/otr.c @@ -42,6 +42,7 @@ #include "irc.h" #include "otr.h" #include +#include #include @@ -105,9 +106,18 @@ const command_t otr_commands[] = { /* start background process to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); +/* main function for the forked keygen slave */ +void keygen_child_main(OtrlUserState us, int infd, int outfd); + /* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); +/* copy the contents of file a to file b, overwriting it if it exists */ +void copyfile(const char *a, const char *b); + +/* read one line of input from a stream, excluding trailing newline */ +void myfgets(char *s, int size, FILE *stream); + /* some yes/no handlers */ void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); @@ -1416,43 +1426,154 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { - irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); - irc_usermsg(irc, "n/a: not implemented"); - return; /* see if we already have a keygen child running. if not, start one and put a - handler on its output. - - b_input_add(fd, GAIM_INPUT_READ, keygen_finish_handler, NULL); - - generate a fresh temp file name for our new key and save our current keys to it. - send the child filename and accountname/protocol for the new key - increment 'ntodo' */ + handler on its output. */ + if(!irc->otr_keygen || waitpid(irc->otr_keygen, NULL, WNOHANG)) { + pid_t p; + int to[2], from[2]; + FILE *tof, *fromf; + + if(pipe(to) < 0 || pipe(from) < 0) { + irc_usermsg(irc, "otr keygen: couldn't create pipe: %s", strerror(errno)); + return; + } + + tof = fdopen(to[1], "w"); + fromf = fdopen(from[0], "r"); + if(!tof || !fromf) { + irc_usermsg(irc, "otr keygen: couldn't streamify pipe: %s", strerror(errno)); + return; + } + + p = fork(); + if(p<0) { + irc_usermsg(irc, "otr keygen: couldn't fork: %s", strerror(errno)); + return; + } + + if(!p) { + /* child process */ + signal(SIGTERM, exit); + keygen_child_main(irc->otr_us, to[0], from[1]); + exit(0); + } + + irc->otr_keygen = p; + irc->otr_to = tof; + irc->otr_from = fromf; + irc->otr_ntodo = 0; + b_input_add(from[0], GAIM_INPUT_READ, keygen_finish_handler, irc); + } - /* in the child: - read filename, accountname, protocol from input, and start work: + /* send accountname and protocol for the new key to our keygen slave */ + fprintf(irc->otr_to, "%s\n%s\n", handle, protocol); + fflush(irc->otr_to); + irc->otr_ntodo++; +} - result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle, - kg->protocol); +void keygen_child_main(OtrlUserState us, int infd, int outfd) +{ + FILE *input, *output; + char filename[128], accountname[512], protocol[512]; + gcry_error_t e; + int tempfd; + + input = fdopen(infd, "r"); + output = fdopen(outfd, "w"); + + while(!feof(input) && !ferror(input) && !feof(output) && !ferror(output)) { + myfgets(accountname, 512, input); + myfgets(protocol, 512, input); - when done, send filename, accountname, and protocol to output. */ + strncpy(filename, "/tmp/bitlbee-XXXXXX", 128); + tempfd = mkstemp(filename); + close(tempfd); + + e = otrl_privkey_generate(us, filename, accountname, protocol); + if(e) { + fprintf(output, "\n"); /* this means failure */ + fprintf(output, "otr keygen: %s\n", gcry_strerror(e)); + unlink(filename); + } else { + fprintf(output, "%s\n", filename); + fprintf(output, "otr keygen for %s/%s complete\n", accountname, protocol); + } + fflush(output); + } + fclose(input); + fclose(output); } gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) { - /* in the handler: - read filename, accountname, and protocol from child output - print a message to the user - irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol); - load the file into userstate - call otr_save. - remove the tempfile. - decrement 'ntodo' - if 'ntodo' reaches zero, send SIGTERM to the child, waitpid for it, return FALSE */ - - return TRUE; /* still working, keep watching */ + irc_t *irc = (irc_t *)data; + char filename[512], msg[512]; + + log_message(LOGLVL_DEBUG, "keygen_finish_handler cond=%d", cond); + + myfgets(filename, 512, irc->otr_from); + myfgets(msg, 512, irc->otr_from); + + log_message(LOGLVL_DEBUG, "filename='%s'", filename); + log_message(LOGLVL_DEBUG, "msg='%s'", msg); + + irc_usermsg(irc, "%s", msg); + if(filename[0]) { + char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->nick); + char *tmp = g_strdup_printf("%s.new", kf); + copyfile(filename, tmp); + unlink(filename); + rename(tmp,kf); + otrl_privkey_read(irc->otr_us, kf); + g_free(kf); + g_free(tmp); + } + + irc->otr_ntodo--; + if(irc->otr_ntodo < 1) { + /* okay, we think the slave is idle now, so kill him */ + log_message(LOGLVL_DEBUG, "all keys done. die, slave!"); + fclose(irc->otr_from); + fclose(irc->otr_to); + kill(irc->otr_keygen, SIGTERM); + waitpid(irc->otr_keygen, NULL, 0); + irc->otr_keygen = 0; + return FALSE; /* unregister ourselves */ + } else { + return TRUE; /* slave still working, keep watching */ + } +} + +void copyfile(const char *a, const char *b) +{ + int fda, fdb; + int n; + char buf[1024]; + + log_message(LOGLVL_DEBUG, "copyfile '%s' '%s'", a, b); + + fda = open(a, O_RDONLY); + fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + while((n=read(fda, buf, 1024)) > 0) + write(fdb, buf, n); + + close(fda); + close(fdb); +} + +void myfgets(char *s, int size, FILE *stream) +{ + if(!fgets(s, size, stream)) { + s[0] = '\0'; + } else { + int n = strlen(s); + if(n>0 && s[n-1] == '\n') + s[n-1] = '\0'; + } } void yes_keygen(gpointer w, void *data) -- cgit v1.2.3 From dc9797f7ad4177dc72373ce71d375257fb0271a1 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 14:24:44 +0100 Subject: keep track of which keys are queued for generation --- otr.c | 223 +++++++++++++++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 165 insertions(+), 58 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 26ce92de..9535e8db 100644 --- a/otr.c +++ b/otr.c @@ -44,6 +44,7 @@ #include #include #include +#include /** OTR interface routines for the OtrlMessageAppOps struct: **/ @@ -103,6 +104,9 @@ const command_t otr_commands[] = { /** misc. helpers/subroutines: **/ +/* check whether we are already generating a key for a given account */ +int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol); + /* start background process to generate a (new) key for a given account */ void otr_keygen(irc_t *irc, const char *handle, const char *protocol); @@ -190,6 +194,35 @@ void otr_init(void) global.otr_ops.account_name_free = NULL; } +otr_t *otr_new(void) +{ + otr_t *otr = g_new0(otr_t, 1); + + otr->us = otrl_userstate_create(); + + return otr; +} + +void otr_free(otr_t *otr) +{ + otrl_userstate_free(otr->us); + if(otr->keygen) { + kill(otr->keygen, SIGTERM); + waitpid(otr->keygen, NULL, 0); + /* TODO: remove stale keygen tempfiles */ + } + if(otr->to) + fclose(otr->to); + if(otr->from) + fclose(otr->from); + while(otr->todo) { + kg_t *p = otr->todo; + otr->todo = p->next; + g_free(p); + } + g_free(otr); +} + void otr_load(irc_t *irc) { char s[512]; @@ -200,12 +233,12 @@ void otr_load(irc_t *irc) log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); - e = otrl_privkey_read(irc->otr_us, s); + e = otrl_privkey_read(irc->otr->us, s); if(e && e!=enoent) { irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); - e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL); + e = otrl_privkey_read_fingerprints(irc->otr->us, s, NULL, NULL); if(e && e!=enoent) { irc_usermsg(irc, "otr load: %s: %s", s, gcry_strerror(e)); } @@ -224,7 +257,7 @@ void otr_save(irc_t *irc) log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick); g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); - e = otrl_privkey_write_fingerprints(irc->otr_us, s); + e = otrl_privkey_write_fingerprints(irc->otr->us, s); if(e) { irc_usermsg(irc, "otr save: %s: %s", s, gcry_strerror(e)); } @@ -262,10 +295,11 @@ void otr_check_for_key(account_t *a) irc_t *irc = a->irc; OtrlPrivKey *k; - k = otrl_privkey_find(irc->otr_us, a->user, a->prpl->name); + k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name); if(k) { - irc_usermsg(irc, "otr: %s/%s ready", - a->user, a->prpl->name); + irc_usermsg(irc, "otr: %s/%s ready", a->user, a->prpl->name); + } if(keygen_in_progress(irc, a->user, a->prpl->name)) { + irc_usermsg(irc, "otr: keygen for %s/%s in progress", a->user, a->prpl->name); } else { otr_keygen(irc, a->user, a->prpl->name); } @@ -278,7 +312,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; - ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic, + ignore_msg = otrl_message_receiving(ic->irc->otr->us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, &tlvs, NULL, NULL); @@ -292,7 +326,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha return g_strdup(msg); } else { /* OTR has processed this message */ - ConnContext *context = otrl_context_find(ic->irc->otr_us, handle, + ConnContext *context = otrl_context_find(ic->irc->otr->us, handle, ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL); if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED && set_getbool(&ic->irc->set, "color_encrypted")) { @@ -318,14 +352,14 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m char *otrmsg = NULL; ConnContext *ctx = NULL; - st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic, + st = otrl_message_sending(ic->irc->otr->us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, NULL, &otrmsg, NULL, NULL); if(st) { return st; } - ctx = otrl_context_find(ic->irc->otr_us, + ctx = otrl_context_find(ic->irc->otr->us, handle, ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL); @@ -383,6 +417,14 @@ OtrlPolicy op_policy(void *opdata, ConnContext *context) { struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); const char *p; + + log_message(LOGLVL_DEBUG, "op_policy '%s' '%s'", context->accountname, context->protocol); + + /* policy override during keygen: if we're missing the key for context but are currently + generating it, then that's as much as we can do. => temporarily return NEVER. */ + if(keygen_in_progress(ic->irc, context->accountname, context->protocol) && + !otrl_privkey_find(ic->irc->otr->us, context->accountname, context->protocol)) + return OTRL_POLICY_NEVER; p = set_getstr(&ic->irc->set, "otr_policy"); if(!strcmp(p, "never")) @@ -401,13 +443,11 @@ void op_create_privkey(void *opdata, const char *accountname, const char *protocol) { struct im_connection *ic = check_imc(opdata, accountname, protocol); - char *s; log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol); - s = g_strdup_printf("oops, no otr privkey for %s - generate one now?", - accountname); - query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc); + /* will fail silently if keygen already in progress */ + otr_keygen(ic->irc, accountname, protocol); } int op_is_logged_in(void *opdata, const char *accountname, @@ -595,13 +635,13 @@ void cmd_otr_disconnect(irc_t *irc, char **args) return; } - otrl_message_disconnect(irc->otr_us, &global.otr_ops, + otrl_message_disconnect(irc->otr->us, &global.otr_ops, u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle); /* for some reason, libotr (3.1.0) doesn't do this itself: */ if(u->encrypted) { ConnContext *ctx; - ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(ctx) op_gone_insecure(u->ic, ctx); @@ -642,7 +682,7 @@ void cmd_otr_smp(irc_t *irc, char **args) return; } - ctx = otrl_context_find(irc->otr_us, u->handle, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL); if(!ctx) { /* huh? out of memory or what? */ @@ -653,7 +693,7 @@ void cmd_otr_smp(irc_t *irc, char **args) log_message(LOGLVL_INFO, "SMP already in phase %d, sending abort before reinitiating", ctx->smstate->nextExpected+1); - otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx); + otrl_message_abort_smp(irc->otr->us, &global.otr_ops, u->ic, ctx); otrl_sm_state_free(ctx->smstate); } @@ -661,14 +701,14 @@ void cmd_otr_smp(irc_t *irc, char **args) is completed or aborted! */ if(ctx->smstate->secret == NULL) { irc_usermsg(irc, "smp: initiating with %s...", u->nick); - otrl_message_initiate_smp(irc->otr_us, &global.otr_ops, + otrl_message_initiate_smp(irc->otr->us, &global.otr_ops, u->ic, ctx, (unsigned char *)args[2], strlen(args[2])); /* smp is now in EXPECT2 */ } else { /* if we're still in EXPECT1 but smstate is initialized, we must have received the SMP1, so let's issue a response */ irc_usermsg(irc, "smp: responding to %s...", u->nick); - otrl_message_respond_smp(irc->otr_us, &global.otr_ops, + otrl_message_respond_smp(irc->otr->us, &global.otr_ops, u->ic, ctx, (unsigned char *)args[2], strlen(args[2])); /* smp is now in EXPECT3 */ } @@ -688,7 +728,7 @@ void cmd_otr_trust(irc_t *irc, char **args) return; } - ctx = otrl_context_find(irc->otr_us, u->handle, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "%s: no otr context with user", args[1]); @@ -752,7 +792,7 @@ void cmd_otr_info(irc_t *irc, char **args) if(protocol && myhandle) { *(myhandle++) = '\0'; handle = arg; - ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL); + ctx = otrl_context_find(irc->otr->us, handle, myhandle, protocol, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "no such context"); g_free(arg); @@ -765,7 +805,7 @@ void cmd_otr_info(irc_t *irc, char **args) g_free(arg); return; } - ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "no otr context with %s", args[1]); @@ -802,7 +842,12 @@ void cmd_otr_keygen(irc_t *irc, char **args) return; } - if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) { + if(keygen_in_progress(irc, a->user, a->prpl->name)) { + irc_usermsg(irc, "keygen for account %d already in progress", n); + return; + } + + if(otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) { char *s = g_strdup_printf("account %d already has a key, replace it?", n); query_add(irc, NULL, s, yes_keygen, NULL, a); } else { @@ -870,7 +915,7 @@ void cmd_otr_forget(irc_t *irc, char **args) return; } - ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "no otr context with %s", args[2]); @@ -906,7 +951,7 @@ void cmd_otr_forget(irc_t *irc, char **args) return; } - ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user, + ctx = otrl_context_find(irc->otr->us, u->handle, u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL); if(!ctx) { irc_usermsg(irc, "no otr context with %s", args[2]); @@ -957,7 +1002,7 @@ void cmd_otr_forget(irc_t *irc, char **args) void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs) { irc_t *irc = ic->irc; - OtrlUserState us = irc->otr_us; + OtrlUserState us = irc->otr->us; OtrlMessageAppOps *ops = &global.otr_ops; OtrlTLV *tlv = NULL; ConnContext *context; @@ -1306,8 +1351,8 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args) /* find first key which matches the given prefix */ n = strlen(prefix); - for(k=irc->otr_us->privkey_root; k; k=k->next) { - p = otrl_privkey_fingerprint(irc->otr_us, human, k->accountname, k->protocol); + for(k=irc->otr->us->privkey_root; k; k=k->next) { + p = otrl_privkey_fingerprint(irc->otr->us, human, k->accountname, k->protocol); if(!p) /* gah! :-P */ continue; if(!strncmp(prefix, human, n)) @@ -1320,7 +1365,7 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args) /* make sure the match, if any, is unique */ for(k2=k->next; k2; k2=k2->next) { - p = otrl_privkey_fingerprint(irc->otr_us, human, k2->accountname, k2->protocol); + p = otrl_privkey_fingerprint(irc->otr->us, human, k2->accountname, k2->protocol); if(!p) /* gah! :-P */ continue; if(!strncmp(prefix, human, n)) @@ -1342,7 +1387,7 @@ void show_general_otr_info(irc_t *irc) /* list all privkeys */ irc_usermsg(irc, "\x1fprivate keys:\x1f"); - for(key=irc->otr_us->privkey_root; key; key=key->next) { + for(key=irc->otr->us->privkey_root; key; key=key->next) { const char *hash; switch(key->pubkey_type) { @@ -1357,7 +1402,7 @@ void show_general_otr_info(irc_t *irc) /* No, it doesn't make much sense to search for the privkey again by account/protocol, but libotr currently doesn't provide a direct routine for hashing a given 'OtrlPrivKey'... */ - hash = otrl_privkey_fingerprint(irc->otr_us, human, key->accountname, key->protocol); + hash = otrl_privkey_fingerprint(irc->otr->us, human, key->accountname, key->protocol); if(hash) /* should always succeed */ irc_usermsg(irc, " %s", human); } @@ -1365,7 +1410,7 @@ void show_general_otr_info(irc_t *irc) /* list all contexts */ irc_usermsg(irc, "%s", ""); irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)"); - for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {\ + for(ctx=irc->otr->us->context_root; ctx; ctx=ctx->next) {\ user_t *u; char *userstring; @@ -1424,13 +1469,41 @@ void show_otr_context_info(irc_t *irc, ConnContext *ctx) show_fingerprints(irc, ctx); } +int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol) +{ + kg_t *kg; + + log_message(LOGLVL_DEBUG, "keygen_in_progress '%s' '%s'", handle, protocol); + + if(!irc->otr->sent_accountname || !irc->otr->sent_protocol) + return 0; + + /* are we currently working on this key? */ + if(!strcmp(handle, irc->otr->sent_accountname) && + !strcmp(protocol, irc->otr->sent_protocol)) + return 1; + + /* do we have it queued for later? */ + for(kg=irc->otr->todo; kg; kg=kg->next) { + if(!strcmp(handle, kg->accountname) && + !strcmp(protocol, kg->protocol)) + return 1; + } + + return 0; +} + void otr_keygen(irc_t *irc, const char *handle, const char *protocol) { + /* do nothing if a key for the requested account is already being generated */ + if(keygen_in_progress(irc, handle, protocol)) + return; + irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); /* see if we already have a keygen child running. if not, start one and put a handler on its output. */ - if(!irc->otr_keygen || waitpid(irc->otr_keygen, NULL, WNOHANG)) { + if(!irc->otr->keygen || waitpid(irc->otr->keygen, NULL, WNOHANG)) { pid_t p; int to[2], from[2]; FILE *tof, *fromf; @@ -1456,21 +1529,37 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) if(!p) { /* child process */ signal(SIGTERM, exit); - keygen_child_main(irc->otr_us, to[0], from[1]); + keygen_child_main(irc->otr->us, to[0], from[1]); exit(0); } - irc->otr_keygen = p; - irc->otr_to = tof; - irc->otr_from = fromf; - irc->otr_ntodo = 0; + irc->otr->keygen = p; + irc->otr->to = tof; + irc->otr->from = fromf; + irc->otr->sent_accountname = NULL; + irc->otr->sent_protocol = NULL; + irc->otr->todo = NULL; b_input_add(from[0], GAIM_INPUT_READ, keygen_finish_handler, irc); } - /* send accountname and protocol for the new key to our keygen slave */ - fprintf(irc->otr_to, "%s\n%s\n", handle, protocol); - fflush(irc->otr_to); - irc->otr_ntodo++; + /* is the keygen slave currently working? */ + if(irc->otr->sent_accountname) { + /* enqueue our job for later transmission */ + log_message(LOGLVL_DEBUG, "enqueueing keygen for %s/%s", handle, protocol); + kg_t **kg = &irc->otr->todo; + while(*kg) + kg=&((*kg)->next); + *kg = g_new0(kg_t, 1); + (*kg)->accountname = handle; + (*kg)->protocol = protocol; + } else { + /* send our job over and remember it */ + log_message(LOGLVL_DEBUG, "slave: generate for %s/%s!", handle, protocol); + fprintf(irc->otr->to, "%s\n%s\n", handle, protocol); + fflush(irc->otr->to); + irc->otr->sent_accountname = handle; + irc->otr->sent_protocol = protocol; + } } void keygen_child_main(OtrlUserState us, int infd, int outfd) @@ -1514,8 +1603,8 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) log_message(LOGLVL_DEBUG, "keygen_finish_handler cond=%d", cond); - myfgets(filename, 512, irc->otr_from); - myfgets(msg, 512, irc->otr_from); + myfgets(filename, 512, irc->otr->from); + myfgets(msg, 512, irc->otr->from); log_message(LOGLVL_DEBUG, "filename='%s'", filename); log_message(LOGLVL_DEBUG, "msg='%s'", msg); @@ -1527,23 +1616,36 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) copyfile(filename, tmp); unlink(filename); rename(tmp,kf); - otrl_privkey_read(irc->otr_us, kf); + otrl_privkey_read(irc->otr->us, kf); g_free(kf); g_free(tmp); } - - irc->otr_ntodo--; - if(irc->otr_ntodo < 1) { - /* okay, we think the slave is idle now, so kill him */ + + /* forget this job */ + irc->otr->sent_accountname = NULL; + irc->otr->sent_protocol = NULL; + + /* see if there are any more in the queue */ + if(irc->otr->todo) { + kg_t *p = irc->otr->todo; + /* send the next one over */ + log_message(LOGLVL_DEBUG, "slave: keygen for %s/%s!", p->accountname, p->protocol); + fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol); + fflush(irc->otr->to); + irc->otr->sent_accountname = p->accountname; + irc->otr->sent_protocol = p->protocol; + irc->otr->todo = p->next; + g_free(p); + return TRUE; /* keep watching */ + } else { + /* okay, the slave is idle now, so kill him */ log_message(LOGLVL_DEBUG, "all keys done. die, slave!"); - fclose(irc->otr_from); - fclose(irc->otr_to); - kill(irc->otr_keygen, SIGTERM); - waitpid(irc->otr_keygen, NULL, 0); - irc->otr_keygen = 0; + fclose(irc->otr->from); + fclose(irc->otr->to); + kill(irc->otr->keygen, SIGTERM); + waitpid(irc->otr->keygen, NULL, 0); + irc->otr->keygen = 0; return FALSE; /* unregister ourselves */ - } else { - return TRUE; /* slave still working, keep watching */ } } @@ -1580,7 +1682,12 @@ void yes_keygen(gpointer w, void *data) { account_t *acc = (account_t *)data; - otr_keygen(acc->irc, acc->user, acc->prpl->name); + if(keygen_in_progress(acc->irc, acc->user, acc->prpl->name)) { + irc_usermsg(acc->irc, "keygen for %s/%s already in progress", + acc->user, acc->prpl->name); + } else { + otr_keygen(acc->irc, acc->user, acc->prpl->name); + } } -- cgit v1.2.3 From 3064ea42452e7e069bce9fc132ceb8ae4d7d11b4 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 16:20:58 +0100 Subject: rework keygen messages and add some notices --- otr.c | 38 +++++++++++++++++++++++++++++++------- 1 file changed, 31 insertions(+), 7 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 9535e8db..15f4225a 100644 --- a/otr.c +++ b/otr.c @@ -229,6 +229,7 @@ void otr_load(irc_t *irc) account_t *a; gcry_error_t e; gcry_error_t enoent = gcry_error_from_errno(ENOENT); + int kg=0; log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); @@ -245,7 +246,16 @@ void otr_load(irc_t *irc) /* check for otr keys on all accounts */ for(a=irc->accounts; a; a=a->next) { - otr_check_for_key(a); + kg = otr_check_for_key(a) || kg; + } + if(kg) { + irc_usermsg(irc, "Notice: " + "The accounts above do not have OTR encryption keys associated with them, yet. " + "These keys are now being generated in the background. " + "You will be notified as they are completed. " + "It is not necessary to wait; " + "BitlBee can be used normally during key generation. " + "You may safely ignore this message if you don't know what OTR is. ;)"); } } @@ -290,7 +300,7 @@ void otr_rename(const char *onick, const char *nnick) rename(s,t); } -void otr_check_for_key(account_t *a) +int otr_check_for_key(account_t *a) { irc_t *irc = a->irc; OtrlPrivKey *k; @@ -298,10 +308,14 @@ void otr_check_for_key(account_t *a) k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name); if(k) { irc_usermsg(irc, "otr: %s/%s ready", a->user, a->prpl->name); + return 0; } if(keygen_in_progress(irc, a->user, a->prpl->name)) { - irc_usermsg(irc, "otr: keygen for %s/%s in progress", a->user, a->prpl->name); + irc_usermsg(irc, "otr: keygen for %s/%s already in progress", a->user, a->prpl->name); + return 0; } else { + irc_usermsg(irc, "otr: starting background keygen for %s/%s", a->user, a->prpl->name); otr_keygen(irc, a->user, a->prpl->name); + return 1; } } @@ -1244,7 +1258,7 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx) } } if(count==0) - irc_usermsg(irc, " no fingerprints"); + irc_usermsg(irc, " (none)"); } Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) @@ -1384,8 +1398,9 @@ void show_general_otr_info(irc_t *irc) ConnContext *ctx; OtrlPrivKey *key; char human[45]; + kg_t *kg; - /* list all privkeys */ + /* list all privkeys (including ones being generated) */ irc_usermsg(irc, "\x1fprivate keys:\x1f"); for(key=irc->otr->us->privkey_root; key; key=key->next) { const char *hash; @@ -1406,6 +1421,12 @@ void show_general_otr_info(irc_t *irc) if(hash) /* should always succeed */ irc_usermsg(irc, " %s", human); } + for(kg=irc->otr->todo; kg; kg=kg->next) { + irc_usermsg(irc, " %s/%s - DSA", kg->accountname, kg->protocol); + irc_usermsg(irc, " (being generated)"); + } + if(key == irc->otr->us->privkey_root && kg == irc->otr->todo) + irc_usermsg(irc, " (none)"); /* list all contexts */ irc_usermsg(irc, "%s", ""); @@ -1430,6 +1451,8 @@ void show_general_otr_info(irc_t *irc) g_free(userstring); } + if(ctx == irc->otr->us->context_root) + irc_usermsg(irc, " (none)"); } void show_otr_context_info(irc_t *irc, ConnContext *ctx) @@ -1498,8 +1521,6 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) /* do nothing if a key for the requested account is already being generated */ if(keygen_in_progress(irc, handle, protocol)) return; - - irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol); /* see if we already have a keygen child running. if not, start one and put a handler on its output. */ @@ -1686,6 +1707,9 @@ void yes_keygen(gpointer w, void *data) irc_usermsg(acc->irc, "keygen for %s/%s already in progress", acc->user, acc->prpl->name); } else { + irc_usermsg(acc->irc, "starting background keygen for %s/%s", + acc->user, acc->prpl->name); + irc_usermsg(acc->irc, "you will be notified when it completes"); otr_keygen(acc->irc, acc->user, acc->prpl->name); } } -- cgit v1.2.3 From 1221ef093f35d620123cab82aecf4bb7bcc2e86a Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 16:28:08 +0100 Subject: show keys being generated in 'otr info' --- otr.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 15f4225a..460dcda0 100644 --- a/otr.c +++ b/otr.c @@ -1421,11 +1421,18 @@ void show_general_otr_info(irc_t *irc) if(hash) /* should always succeed */ irc_usermsg(irc, " %s", human); } + if(irc->otr->sent_accountname) { + irc_usermsg(irc, " %s/%s - DSA", irc->otr->sent_accountname, + irc->otr->sent_protocol); + irc_usermsg(irc, " (being generated)"); + } for(kg=irc->otr->todo; kg; kg=kg->next) { irc_usermsg(irc, " %s/%s - DSA", kg->accountname, kg->protocol); - irc_usermsg(irc, " (being generated)"); + irc_usermsg(irc, " (queued)"); } - if(key == irc->otr->us->privkey_root && kg == irc->otr->todo) + if(key == irc->otr->us->privkey_root && + !irc->otr->sent_accountname && + kg == irc->otr->todo) irc_usermsg(irc, " (none)"); /* list all contexts */ -- cgit v1.2.3 From 903a2fcc60f82f52fe05c79250e6875dc48f23f0 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 17:24:38 +0100 Subject: remove (broken) 'otr forget key' command again --- otr.c | 31 ------------------------------- 1 file changed, 31 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 460dcda0..78fc688f 100644 --- a/otr.c +++ b/otr.c @@ -126,7 +126,6 @@ void myfgets(char *s, int size, FILE *stream); void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); void yes_forget_context(gpointer w, void *data); -void yes_forget_key(gpointer w, void *data); /* helper to make sure accountname and protocol match the incoming "opdata" */ struct im_connection *check_imc(void *opdata, const char *accountname, @@ -898,15 +897,6 @@ void yes_forget_context(gpointer w, void *data) otrl_context_forget(ctx); } -void yes_forget_key(gpointer w, void *data) -{ - OtrlPrivKey *key = (OtrlPrivKey *)data; - - /* FIXME: For some reason which /completely eludes me/, this call keeps - barfing on the gcry_sexp_release inside (invalid pointer free). */ - otrl_privkey_forget(key); -} - void cmd_otr_forget(irc_t *irc, char **args) { if(!strcmp(args[1], "fingerprint")) @@ -981,27 +971,6 @@ void cmd_otr_forget(irc_t *irc, char **args) query_add(irc, NULL, s, yes_forget_context, NULL, ctx); } - else if(!strcmp(args[1], "key")) - { - OtrlPrivKey *key; - char *s; - - key = match_privkey(irc, ((const char **)args)+2); - if(!key) { - /* match_privkey does error messages */ - return; - } - - /* TODO: Find out why 'otr forget key' barfs (cf. yes_forget_key) */ - irc_usermsg(irc, "otr %s %s: not implemented, please edit \x02%s%s.otr_keys\x02 manually :-/", - args[0], args[1], global.conf->configdir, irc->nick); - return; - - s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?", - key->accountname, key->protocol); - query_add(irc, NULL, s, yes_forget_key, NULL, key); - } - else { irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02", -- cgit v1.2.3 From fd9fa52e0014459079444bd7bfff7a40eef4e27a Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sat, 16 Feb 2008 18:17:29 +0100 Subject: indent fingerprints in 'otr info ' --- otr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 78fc688f..d30c8c93 100644 --- a/otr.c +++ b/otr.c @@ -1221,13 +1221,13 @@ void show_fingerprints(irc_t *irc, ConnContext *ctx) trust=fp->trust; } if(fp == ctx->active_fingerprint) { - irc_usermsg(irc, " \x02%s (%s)\x02", human, trust); + irc_usermsg(irc, " \x02%s (%s)\x02", human, trust); } else { - irc_usermsg(irc, " %s (%s)", human, trust); + irc_usermsg(irc, " %s (%s)", human, trust); } } if(count==0) - irc_usermsg(irc, " (none)"); + irc_usermsg(irc, " (none)"); } Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) -- cgit v1.2.3 From ba5add72f824504a21eb780cae638c3ea2166ba0 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 02:39:39 +0100 Subject: explicitly initialize ssl in order to avoid gnutls and libotr fighting over the global state of libgcrypt --- otr.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index d30c8c93..418da177 100644 --- a/otr.c +++ b/otr.c @@ -111,7 +111,7 @@ int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol); void otr_keygen(irc_t *irc, const char *handle, const char *protocol); /* main function for the forked keygen slave */ -void keygen_child_main(OtrlUserState us, int infd, int outfd); +void keygen_child_main(const char *nick, int infd, int outfd); /* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); @@ -1526,7 +1526,7 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) if(!p) { /* child process */ signal(SIGTERM, exit); - keygen_child_main(irc->otr->us, to[0], from[1]); + keygen_child_main(irc->nick, to[0], from[1]); exit(0); } @@ -1547,25 +1547,32 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) while(*kg) kg=&((*kg)->next); *kg = g_new0(kg_t, 1); - (*kg)->accountname = handle; - (*kg)->protocol = protocol; + (*kg)->accountname = g_strdup(handle); + (*kg)->protocol = g_strdup(protocol); } else { /* send our job over and remember it */ log_message(LOGLVL_DEBUG, "slave: generate for %s/%s!", handle, protocol); fprintf(irc->otr->to, "%s\n%s\n", handle, protocol); fflush(irc->otr->to); - irc->otr->sent_accountname = handle; - irc->otr->sent_protocol = protocol; + irc->otr->sent_accountname = g_strdup(handle); + irc->otr->sent_protocol = g_strdup(protocol); } } -void keygen_child_main(OtrlUserState us, int infd, int outfd) +void keygen_child_main(const char *nick, int infd, int outfd) { + OtrlUserState us; + char *kf; FILE *input, *output; char filename[128], accountname[512], protocol[512]; gcry_error_t e; int tempfd; + us = otrl_userstate_create(); + kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, nick); + otrl_privkey_read(us, kf); + g_free(kf); + input = fdopen(infd, "r"); output = fdopen(outfd, "w"); @@ -1619,6 +1626,8 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) } /* forget this job */ + g_free(irc->otr->sent_accountname); + g_free(irc->otr->sent_protocol); irc->otr->sent_accountname = NULL; irc->otr->sent_protocol = NULL; -- cgit v1.2.3 From 82e8fe8f36b0c0c53389358dca184f6d12184933 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 02:50:23 +0100 Subject: free query strings after query_add --- otr.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 418da177..4d465e71 100644 --- a/otr.c +++ b/otr.c @@ -863,6 +863,7 @@ void cmd_otr_keygen(irc_t *irc, char **args) if(otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) { char *s = g_strdup_printf("account %d already has a key, replace it?", n); query_add(irc, NULL, s, yes_keygen, NULL, a); + g_free(s); } else { otr_keygen(irc, a->user, a->prpl->name); } @@ -940,6 +941,7 @@ void cmd_otr_forget(irc_t *irc, char **args) otrl_privkey_hash_to_human(human, fp->fingerprint); s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human); query_add(irc, NULL, s, yes_forget_fingerprint, NULL, fp); + g_free(s); } else if(!strcmp(args[1], "context")) @@ -969,6 +971,7 @@ void cmd_otr_forget(irc_t *irc, char **args) s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]); query_add(irc, NULL, s, yes_forget_context, NULL, ctx); + g_free(s); } else -- cgit v1.2.3 From d0faf626e98cf8a332afac5ac7d61c80dd8d3064 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 02:58:41 +0100 Subject: put 'otr forget key' back in, which now works --- otr.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 4d465e71..d69dffaf 100644 --- a/otr.c +++ b/otr.c @@ -126,6 +126,7 @@ void myfgets(char *s, int size, FILE *stream); void yes_keygen(gpointer w, void *data); void yes_forget_fingerprint(gpointer w, void *data); void yes_forget_context(gpointer w, void *data); +void yes_forget_key(gpointer w, void *data); /* helper to make sure accountname and protocol match the incoming "opdata" */ struct im_connection *check_imc(void *opdata, const char *accountname, @@ -898,6 +899,13 @@ void yes_forget_context(gpointer w, void *data) otrl_context_forget(ctx); } +void yes_forget_key(gpointer w, void *data) +{ + OtrlPrivKey *key = (OtrlPrivKey *)data; + + otrl_privkey_forget(key); +} + void cmd_otr_forget(irc_t *irc, char **args) { if(!strcmp(args[1], "fingerprint")) @@ -974,6 +982,23 @@ void cmd_otr_forget(irc_t *irc, char **args) g_free(s); } + else if(!strcmp(args[1], "key")) + { + OtrlPrivKey *key; + char *s; + + key = match_privkey(irc, ((const char **)args)+2); + if(!key) { + /* match_privkey does error messages */ + return; + } + + s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?", + key->accountname, key->protocol); + query_add(irc, NULL, s, yes_forget_key, NULL, key); + g_free(s); + } + else { irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02", -- cgit v1.2.3 From 37bff517e244def9f87a711a0154b182c89f07b3 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 03:05:23 +0100 Subject: add TODO note about erasing forgotten keys --- otr.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'otr.c') diff --git a/otr.c b/otr.c index d69dffaf..e675dc25 100644 --- a/otr.c +++ b/otr.c @@ -904,6 +904,9 @@ void yes_forget_key(gpointer w, void *data) OtrlPrivKey *key = (OtrlPrivKey *)data; otrl_privkey_forget(key); + /* Hm, libotr doesn't seem to offer a function for explicitly /writing/ + keyfiles. So the key will be back on the next load... */ + /* TODO: Actually erase forgotten keys from storage? */ } void cmd_otr_forget(irc_t *irc, char **args) -- cgit v1.2.3 From d858d2196a11910382d879527d117c8a181177fc Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 03:12:42 +0100 Subject: remove debugging output --- otr.c | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index e675dc25..8d6f9839 100644 --- a/otr.c +++ b/otr.c @@ -231,8 +231,6 @@ void otr_load(irc_t *irc) gcry_error_t enoent = gcry_error_from_errno(ENOENT); int kg=0; - log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick); - g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick); e = otrl_privkey_read(irc->otr->us, s); if(e && e!=enoent) { @@ -264,8 +262,6 @@ void otr_save(irc_t *irc) char s[512]; gcry_error_t e; - log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick); - g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick); e = otrl_privkey_write_fingerprints(irc->otr->us, s); if(e) { @@ -278,8 +274,6 @@ void otr_remove(const char *nick) { char s[512]; - log_message(LOGLVL_DEBUG, "otr_remove '%s'", nick); - g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick); unlink(s); g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick); @@ -290,8 +284,6 @@ void otr_rename(const char *onick, const char *nnick) { char s[512], t[512]; - log_message(LOGLVL_DEBUG, "otr_rename '%s' -> '%s'", onick, nnick); - g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick); g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick); rename(s,t); @@ -432,8 +424,6 @@ OtrlPolicy op_policy(void *opdata, ConnContext *context) struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol); const char *p; - log_message(LOGLVL_DEBUG, "op_policy '%s' '%s'", context->accountname, context->protocol); - /* policy override during keygen: if we're missing the key for context but are currently generating it, then that's as much as we can do. => temporarily return NEVER. */ if(keygen_in_progress(ic->irc, context->accountname, context->protocol) && @@ -458,8 +448,6 @@ void op_create_privkey(void *opdata, const char *accountname, { struct im_connection *ic = check_imc(opdata, accountname, protocol); - log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol); - /* will fail silently if keygen already in progress */ otr_keygen(ic->irc, accountname, protocol); } @@ -470,8 +458,6 @@ int op_is_logged_in(void *opdata, const char *accountname, struct im_connection *ic = check_imc(opdata, accountname, protocol); user_t *u; - log_message(LOGLVL_DEBUG, "op_is_logged_in '%s' '%s' '%s'", accountname, protocol, recipient); - /* lookup the user_t for the given recipient */ u = user_findhandle(ic, recipient); if(u) { @@ -489,8 +475,6 @@ void op_inject_message(void *opdata, const char *accountname, { struct im_connection *ic = check_imc(opdata, accountname, protocol); - log_message(LOGLVL_DEBUG, "op_inject_message '%s' '%s' '%s' '%s'", accountname, protocol, recipient, message); - if (strcmp(accountname, recipient) == 0) { /* huh? injecting messages to myself? */ irc_usermsg(ic->irc, "note to self: %s", message); @@ -508,8 +492,6 @@ int op_display_otr_message(void *opdata, const char *accountname, struct im_connection *ic = check_imc(opdata, accountname, protocol); char *msg = g_strdup(message); - log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, message); - strip_html(msg); irc_usermsg(ic->irc, "otr: %s", msg); @@ -525,8 +507,6 @@ void op_new_fingerprint(void *opdata, OtrlUserState us, char hunam[45]; /* anybody looking? ;-) */ otrl_privkey_hash_to_human(hunam, fingerprint); - log_message(LOGLVL_DEBUG, "op_new_fingerprint '%s' '%s' '%s' '%s'", accountname, protocol, username, hunam); - irc_usermsg(ic->irc, "new fingerprint for %s: %s", peernick(ic->irc, username, protocol), hunam); } @@ -535,8 +515,6 @@ void op_write_fingerprints(void *opdata) { struct im_connection *ic = (struct im_connection *)opdata; - log_message(LOGLVL_DEBUG, "op_write_fingerprints"); - otr_save(ic->irc); } @@ -547,8 +525,6 @@ void op_gone_secure(void *opdata, ConnContext *context) user_t *u; const char *trust; - log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); - u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, @@ -572,8 +548,6 @@ void op_gone_insecure(void *opdata, ConnContext *context) check_imc(opdata, context->accountname, context->protocol); user_t *u; - log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username); - u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, @@ -592,9 +566,6 @@ void op_still_secure(void *opdata, ConnContext *context, int is_reply) check_imc(opdata, context->accountname, context->protocol); user_t *u; - log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d", - context->accountname, context->protocol, context->username, is_reply); - u = peeruser(ic->irc, context->username, context->protocol); if(!u) { log_message(LOGLVL_ERROR, @@ -631,8 +602,6 @@ const char *op_account_name(void *opdata, const char *account, const char *proto { struct im_connection *ic = (struct im_connection *)opdata; - log_message(LOGLVL_DEBUG, "op_account_name '%s' '%s'", account, protocol); - return peernick(ic->irc, account, protocol); } @@ -1130,8 +1099,6 @@ user_t *peeruser(irc_t *irc, const char *handle, const char *protocol) { user_t *u; - log_message(LOGLVL_DEBUG, "peeruser '%s' '%s'", handle, protocol); - for(u=irc->users; u; u=u->next) { struct prpl *prpl; if(!u->ic || !u->handle) @@ -1294,8 +1261,6 @@ Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args) } } *p = '\0'; - log_message(LOGLVL_DEBUG, "match_fingerprint '%s'", prefix); - log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix)); /* find first fingerprint with the given prefix */ n = strlen(prefix); @@ -1360,8 +1325,6 @@ OtrlPrivKey *match_privkey(irc_t *irc, const char **args) } } *p = '\0'; - log_message(LOGLVL_DEBUG, "match_privkey '%s'", prefix); - log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix)); /* find first key which matches the given prefix */ n = strlen(prefix); @@ -1503,8 +1466,6 @@ int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol) { kg_t *kg; - log_message(LOGLVL_DEBUG, "keygen_in_progress '%s' '%s'", handle, protocol); - if(!irc->otr->sent_accountname || !irc->otr->sent_protocol) return 0; @@ -1573,7 +1534,6 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) /* is the keygen slave currently working? */ if(irc->otr->sent_accountname) { /* enqueue our job for later transmission */ - log_message(LOGLVL_DEBUG, "enqueueing keygen for %s/%s", handle, protocol); kg_t **kg = &irc->otr->todo; while(*kg) kg=&((*kg)->next); @@ -1582,7 +1542,6 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) (*kg)->protocol = g_strdup(protocol); } else { /* send our job over and remember it */ - log_message(LOGLVL_DEBUG, "slave: generate for %s/%s!", handle, protocol); fprintf(irc->otr->to, "%s\n%s\n", handle, protocol); fflush(irc->otr->to); irc->otr->sent_accountname = g_strdup(handle); @@ -1636,14 +1595,9 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) irc_t *irc = (irc_t *)data; char filename[512], msg[512]; - log_message(LOGLVL_DEBUG, "keygen_finish_handler cond=%d", cond); - myfgets(filename, 512, irc->otr->from); myfgets(msg, 512, irc->otr->from); - log_message(LOGLVL_DEBUG, "filename='%s'", filename); - log_message(LOGLVL_DEBUG, "msg='%s'", msg); - irc_usermsg(irc, "%s", msg); if(filename[0]) { char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->nick); @@ -1666,7 +1620,6 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) if(irc->otr->todo) { kg_t *p = irc->otr->todo; /* send the next one over */ - log_message(LOGLVL_DEBUG, "slave: keygen for %s/%s!", p->accountname, p->protocol); fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol); fflush(irc->otr->to); irc->otr->sent_accountname = p->accountname; @@ -1676,7 +1629,6 @@ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond) return TRUE; /* keep watching */ } else { /* okay, the slave is idle now, so kill him */ - log_message(LOGLVL_DEBUG, "all keys done. die, slave!"); fclose(irc->otr->from); fclose(irc->otr->to); kill(irc->otr->keygen, SIGTERM); @@ -1692,8 +1644,6 @@ void copyfile(const char *a, const char *b) int n; char buf[1024]; - log_message(LOGLVL_DEBUG, "copyfile '%s' '%s'", a, b); - fda = open(a, O_RDONLY); fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600); -- cgit v1.2.3 From 9e64011b1758bc9dbeddb517eff5eccbb0733674 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 03:28:05 +0100 Subject: fix an uninitialized value --- otr.c | 1 + 1 file changed, 1 insertion(+) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 8d6f9839..dd3fc13e 100644 --- a/otr.c +++ b/otr.c @@ -768,6 +768,7 @@ void cmd_otr_info(irc_t *irc, char **args) /* interpret arg as 'user/protocol/account' if possible */ protocol = strchr(arg, '/'); + myhandle = NULL; if(protocol) { *(protocol++) = '\0'; myhandle = strchr(protocol, '/'); -- cgit v1.2.3 From 12cc58b34a98ae010cf1718e994b63da35ddd7c2 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 11:52:28 +0100 Subject: don't unnecessarily duplicate otr userstate in keygen slave --- otr.c | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index dd3fc13e..3e953854 100644 --- a/otr.c +++ b/otr.c @@ -111,7 +111,7 @@ int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol); void otr_keygen(irc_t *irc, const char *handle, const char *protocol); /* main function for the forked keygen slave */ -void keygen_child_main(const char *nick, int infd, int outfd); +void keygen_child_main(OtrlUserState us, int infd, int outfd); /* mainloop handler for when a keygen finishes */ gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond); @@ -1519,7 +1519,7 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) if(!p) { /* child process */ signal(SIGTERM, exit); - keygen_child_main(irc->nick, to[0], from[1]); + keygen_child_main(irc->otr->us, to[0], from[1]); exit(0); } @@ -1550,20 +1550,13 @@ void otr_keygen(irc_t *irc, const char *handle, const char *protocol) } } -void keygen_child_main(const char *nick, int infd, int outfd) +void keygen_child_main(OtrlUserState us, int infd, int outfd) { - OtrlUserState us; - char *kf; FILE *input, *output; char filename[128], accountname[512], protocol[512]; gcry_error_t e; int tempfd; - us = otrl_userstate_create(); - kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, nick); - otrl_privkey_read(us, kf); - g_free(kf); - input = fdopen(infd, "r"); output = fdopen(outfd, "w"); -- cgit v1.2.3 From fcfd9c56a81a993d86f18c792b1584ec5c5dca99 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Sun, 17 Feb 2008 14:24:48 +0100 Subject: tabidy some indentation --- otr.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 3e953854..bd76e2ea 100644 --- a/otr.c +++ b/otr.c @@ -249,11 +249,11 @@ void otr_load(irc_t *irc) if(kg) { irc_usermsg(irc, "Notice: " "The accounts above do not have OTR encryption keys associated with them, yet. " - "These keys are now being generated in the background. " - "You will be notified as they are completed. " - "It is not necessary to wait; " - "BitlBee can be used normally during key generation. " - "You may safely ignore this message if you don't know what OTR is. ;)"); + "These keys are now being generated in the background. " + "You will be notified as they are completed. " + "It is not necessary to wait; " + "BitlBee can be used normally during key generation. " + "You may safely ignore this message if you don't know what OTR is. ;)"); } } -- cgit v1.2.3 From 5933da72f1f04810a1b789f9e1e1c1d7e360f30f Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 18 Feb 2008 10:36:23 +0100 Subject: fix "purple message" bug ;) --- otr.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index bd76e2ea..3fdd61b1 100644 --- a/otr.c +++ b/otr.c @@ -337,13 +337,13 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED && set_getbool(&ic->irc->set, "color_encrypted")) { /* color according to f'print trust */ - char color; + int color; const char *trust = context->active_fingerprint->trust; if(trust && trust[0] != '\0') - color='3'; /* green */ + color=3; /* green */ else - color='5'; /* red */ - colormsg = g_strdup_printf("\x03%c%s\x0F", color, newmsg); + color=5; /* red */ + colormsg = g_strdup_printf("\x03%.2d%s\x0F", color, newmsg); } else { colormsg = g_strdup(newmsg); } -- cgit v1.2.3 From b14890de5407689555b1fcc293455fb77d306afd Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Mon, 18 Feb 2008 10:42:25 +0100 Subject: fix "purple background" bug *sigh* :P --- otr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 3fdd61b1..6451568c 100644 --- a/otr.c +++ b/otr.c @@ -343,7 +343,7 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha color=3; /* green */ else color=5; /* red */ - colormsg = g_strdup_printf("\x03%.2d%s\x0F", color, newmsg); + colormsg = g_strdup_printf("\x03%.2d,00%s\x0F", color, newmsg); } else { colormsg = g_strdup(newmsg); } -- cgit v1.2.3 From 9730d7250bb9e938ca00b72efdd8e8b3c03b2753 Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Wed, 16 Jul 2008 23:45:12 +0200 Subject: minor bugfixes --- otr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 6451568c..18e25807 100644 --- a/otr.c +++ b/otr.c @@ -763,7 +763,7 @@ void cmd_otr_info(irc_t *irc, char **args) show_general_otr_info(irc); } else { char *arg = g_strdup(args[1]); - char *myhandle, *handle, *protocol; + char *myhandle, *handle=NULL, *protocol; ConnContext *ctx; /* interpret arg as 'user/protocol/account' if possible */ -- cgit v1.2.3 From fc34fb5d0b717d90edfc7ed78f11166eb23c536e Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Fri, 13 Mar 2009 12:31:47 +0100 Subject: dont specify a background when coloring encrypted messages --- otr.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'otr.c') diff --git a/otr.c b/otr.c index 8d12361b..d69b9a87 100644 --- a/otr.c +++ b/otr.c @@ -349,7 +349,14 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha color=3; /* green */ else color=5; /* red */ - colormsg = g_strdup_printf("\x03%.2d,00%s\x0F", color, newmsg); + + if(newmsg[0] == ',') { + /* could be a problem with the color code */ + /* insert a space between color spec and message */ + colormsg = g_strdup_printf("\x03%.2d %s\x0F", color, newmsg); + } else { + colormsg = g_strdup_printf("\x03%.2d%s\x0F", color, newmsg); + } } else { colormsg = g_strdup(newmsg); } -- cgit v1.2.3 From 1dd34701541fb8142402f593dc256ff9b54121bb Mon Sep 17 00:00:00 2001 From: Sven Moritz Hallberg Date: Thu, 3 Jun 2010 23:47:53 +0200 Subject: add an option to disable otr on twitter (and other unsuitable protocols) --- otr.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'otr.c') diff --git a/otr.c b/otr.c index d69b9a87..c6ba32a4 100644 --- a/otr.c +++ b/otr.c @@ -303,6 +303,11 @@ int otr_check_for_key(account_t *a) irc_t *irc = a->irc; OtrlPrivKey *k; + /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */ + if(a->prpl->options & OPT_NOOTR) { + return 0; + } + k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name); if(k) { irc_usermsg(irc, "otr: %s/%s ready", a->user, a->prpl->name); @@ -324,6 +329,11 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha OtrlTLV *tlvs = NULL; char *colormsg; + /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */ + if(ic->acc->prpl->options & OPT_NOOTR) { + return (g_strdup(msg)); + } + ignore_msg = otrl_message_receiving(ic->irc->otr->us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg, &tlvs, NULL, NULL); @@ -370,6 +380,11 @@ int otr_send_message(struct im_connection *ic, const char *handle, const char *m int st; char *otrmsg = NULL; ConnContext *ctx = NULL; + + /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */ + if(ic->acc->prpl->options & OPT_NOOTR) { + return (ic->acc->prpl->buddy_msg(ic, handle, msg, flags)); + } st = otrl_message_sending(ic->irc->otr->us, &global.otr_ops, ic, ic->acc->user, ic->acc->prpl->name, handle, -- cgit v1.2.3