source: otr.c @ 3c80a9d

Last change on this file since 3c80a9d was 3c80a9d, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-03T22:28:13Z

otr: check some error conditions

  • Property mode set to 100644
File size: 26.9 KB
Line 
1#include "bitlbee.h"
2#ifdef WITH_OTR
3#include "irc.h"
4#include "otr.h"
5#include <sys/types.h>
6#include <unistd.h>
7
8/**
9files used to store OTR data:
10  $configdir/$nick.otr_keys
11  $configdir/$nick.otr_fprints
12 **/
13
14
15/** OTR interface routines for the OtrlMessageAppOps struct: **/
16
17OtrlPolicy op_policy(void *opdata, ConnContext *context);
18
19void op_create_privkey(void *opdata, const char *accountname, const char *protocol);
20
21int op_is_logged_in(void *opdata, const char *accountname, const char *protocol,
22        const char *recipient);
23
24void op_inject_message(void *opdata, const char *accountname, const char *protocol,
25        const char *recipient, const char *message);
26
27int op_display_otr_message(void *opdata, const char *accountname, const char *protocol,
28        const char *username, const char *msg);
29
30void op_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
31        const char *protocol, const char *username, unsigned char fingerprint[20]);
32
33void op_write_fingerprints(void *opdata);
34
35void op_gone_secure(void *opdata, ConnContext *context);
36
37void op_gone_secure(void *opdata, ConnContext *context);
38
39void op_gone_insecure(void *opdata, ConnContext *context);
40
41void op_still_secure(void *opdata, ConnContext *context, int is_reply);
42
43void op_log_message(void *opdata, const char *message);
44
45/* TODO: int op_max_message_size(void *opdata, ConnContext *context); */
46
47/* TODO: const char *op_account_name(void *opdata, const char *account,
48        const char *protocol); */
49
50
51/** otr sub-command handlers: **/
52
53/* TODO: void cmd_otr_keygen(irc_t *irc, char **args); */
54void cmd_otr_abort(irc_t *irc, char **args); /* TODO: does this cmd even make sense? */
55void cmd_otr_request(irc_t *irc, char **args); /* TODO: do we even need this? */
56void cmd_otr_auth(irc_t *irc, char **args);
57/* TODO: void cmd_otr_affirm(irc_t *irc, char **args); */
58void cmd_otr_fprints(irc_t *irc, char **args);
59void cmd_otr_info(irc_t *irc, char **args);
60void cmd_otr_policy(irc_t *irc, char **args);
61
62const command_t otr_commands[] = {
63        { "abort",    1, &cmd_otr_abort,    0 },
64        { "request",  1, &cmd_otr_request,  0 },
65        { "auth",     2, &cmd_otr_auth,     0 },
66        { "fprints",  0, &cmd_otr_fprints,  0 },
67        { "info",     1, &cmd_otr_info,     0 },
68        { "policy",   0, &cmd_otr_policy,   0 },
69        { NULL }
70};
71
72
73/** misc. helpers/subroutines: **/
74
75/* start background thread to generate a (new) key for a given account */
76void otr_keygen(irc_t *irc, const char *handle, const char *protocol);
77/* keygen thread main func */
78gpointer otr_keygen_thread_func(gpointer data);
79/* mainloop handler for when keygen thread finishes */
80gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond);
81/* data to be passed to otr_keygen_thread_func */
82struct kgdata {
83        irc_t *irc;            /* access to OTR userstate */
84        char *keyfile;         /* free me! */
85        const char *handle;      /* don't free! */
86        const char *protocol;    /* don't free! */
87        GMutex *mutex;         /* lock for the 'done' flag, free me! */
88        int done;              /* is the thread done? */
89        gcry_error_t result;   /* return value of otrl_privkey_generate */
90};
91
92/* yes/no handlers for "generate key now?" */
93void yes_keygen(gpointer w, void *data);
94void no_keygen(gpointer w, void *data);
95
96/* helper to make sure accountname and protocol match the incoming "opdata" */
97struct im_connection *check_imc(void *opdata, const char *accountname,
98        const char *protocol);
99
100/* determine the nick for a given handle/protocol pair */
101const char *peernick(irc_t *irc, const char *handle, const char *protocol);
102
103/* handle SMP TLVs from a received message */
104void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs);
105
106/* show the list of fingerprints associated with a given context */
107void show_fingerprints(irc_t *irc, ConnContext *ctx);
108
109
110
111/*** routines declared in otr.h: ***/
112
113void otr_init(void)
114{
115        if(!g_thread_supported()) g_thread_init(NULL);
116        OTRL_INIT;
117       
118        /* fill global OtrlMessageAppOps */
119        global.otr_ops.policy = &op_policy;
120        global.otr_ops.create_privkey = &op_create_privkey;
121        global.otr_ops.is_logged_in = &op_is_logged_in;
122        global.otr_ops.inject_message = &op_inject_message;
123        global.otr_ops.notify = NULL;
124        global.otr_ops.display_otr_message = &op_display_otr_message;
125        global.otr_ops.update_context_list = NULL;
126        global.otr_ops.protocol_name = NULL;
127        global.otr_ops.protocol_name_free = NULL;
128        global.otr_ops.new_fingerprint = &op_new_fingerprint;
129        global.otr_ops.write_fingerprints = &op_write_fingerprints;
130        global.otr_ops.gone_secure = &op_gone_secure;
131        global.otr_ops.gone_insecure = &op_gone_insecure;
132        global.otr_ops.still_secure = &op_still_secure;
133        global.otr_ops.log_message = &op_log_message;
134        global.otr_ops.max_message_size = NULL;
135        global.otr_ops.account_name = NULL;
136        global.otr_ops.account_name_free = NULL;
137}
138
139/* Notice on the otr_mutex:
140
141   The incoming/outgoing message handlers try to lock the otr_mutex. If they succeed,
142   this will prevent a concurrent keygen (possibly spawned by that very command)
143   from messing up the userstate. If the lock fails, that means there already is
144   a keygen in progress. Instead of blocking for an unknown time, they
145   will bail out gracefully, informing the user of this temporary "coma".
146   TODO: Hold back incoming/outgoing messages and process them when keygen completes?
147
148   The other routines do not lock the otr_mutex themselves, it is done as a
149   catch-all in the root command handler. Rationale:
150     a) it's easy to code
151     b) it makes it obvious that no command can get its userstate corrupted
152     c) the "irc" struct is readily available there for feedback to the user
153 */
154
155void otr_load(irc_t *irc)
156{
157        char s[512];
158        account_t *a;
159        gcry_error_t e;
160
161        log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick);
162
163        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick);
164        e = otrl_privkey_read(irc->otr_us, s);
165        if(e && e!=ENOENT) {
166                log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e));
167        }
168        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick);
169        e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL);
170        if(e && e!=ENOENT) {
171                log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e));
172        }
173       
174        /* check for otr keys on all accounts */
175        for(a=irc->accounts; a; a=a->next) {
176                otr_check_for_key(a);
177        }
178}
179
180void otr_save(irc_t *irc)
181{
182        char s[512];
183        gcry_error_t e;
184
185        log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick);
186
187        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick);
188        e = otrl_privkey_write_fingerprints(irc->otr_us, s);
189        if(e) {
190                log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e));
191        }
192}
193
194void otr_remove(const char *nick)
195{
196        char s[512];
197       
198        log_message(LOGLVL_DEBUG, "otr_remove '%s'", nick);
199
200        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick);
201        unlink(s);
202        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick);
203        unlink(s);
204}
205
206void otr_rename(const char *onick, const char *nnick)
207{
208        char s[512], t[512];
209       
210        log_message(LOGLVL_DEBUG, "otr_rename '%s' -> '%s'", onick, nnick);
211
212        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick);
213        g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick);
214        rename(s,t);
215        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick);
216        g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick);
217        rename(s,t);
218}
219
220void otr_check_for_key(account_t *a)
221{
222        irc_t *irc = a->irc;
223        char buf[45];
224        char *fp;
225       
226        fp = otrl_privkey_fingerprint(irc->otr_us, buf, a->user, a->prpl->name);
227        if(fp) {
228                irc_usermsg(irc, "otr: %s/%s ready with f'print %s",
229                        a->user, a->prpl->name, fp);
230        } else {
231                otr_keygen(irc, a->user, a->prpl->name);
232        }
233}
234
235char *otr_handle_message(struct im_connection *ic, const char *handle, const char *msg)
236{
237        int ignore_msg;
238        char *newmsg = NULL;
239        OtrlTLV *tlvs = NULL;
240        char *colormsg;
241       
242    if(!g_mutex_trylock(ic->irc->otr_mutex)) {
243        /* TODO: queue msgs received during keygen for later */
244                irc_usermsg(ic->irc, "msg from %s/%s during keygen - dropped",
245                        handle, ic->acc->prpl->name);
246                return NULL;
247        }
248
249        ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic,
250                ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg,
251                &tlvs, NULL, NULL);
252
253        otr_handle_smp(ic, handle, tlvs);
254       
255        if(ignore_msg) {
256                /* this was an internal OTR protocol message */
257                g_mutex_unlock(ic->irc->otr_mutex);
258                return NULL;
259        } else if(!newmsg) {
260                /* this was a non-OTR message */
261                g_mutex_unlock(ic->irc->otr_mutex);
262                return g_strdup(msg);
263        } else {
264                /* OTR has processed this message */
265                ConnContext *context = otrl_context_find(ic->irc->otr_us, handle,
266                        ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL);
267                if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
268                        /* color according to f'print trust */
269                        char color;
270                        const char *trust = context->active_fingerprint->trust;
271                        if(trust && trust[0] != '\0')
272                                color='3';   /* green */
273                        else
274                                color='5';   /* red */
275                        colormsg = g_strdup_printf("\x03%c%s\x0F", color, newmsg);
276                } else {
277                        colormsg = g_strdup(newmsg);
278                }
279                otrl_message_free(newmsg);
280                g_mutex_unlock(ic->irc->otr_mutex);
281                return colormsg;
282        }
283}
284
285int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags)
286{       
287    int st;
288    char *otrmsg = NULL;
289    ConnContext *ctx = NULL;
290   
291    if(!g_mutex_trylock(ic->irc->otr_mutex)) {
292        irc_usermsg(ic->irc, "msg to %s/%s during keygen - not sent",
293                handle, ic->acc->prpl->name);
294        return 1;
295    }
296   
297        st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic,
298                ic->acc->user, ic->acc->prpl->name, handle,
299                msg, NULL, &otrmsg, NULL, NULL);
300        if(st) {
301                g_mutex_unlock(ic->irc->otr_mutex);
302                return st;
303        }
304
305        ctx = otrl_context_find(ic->irc->otr_us,
306                        handle, ic->acc->user, ic->acc->prpl->name,
307                        1, NULL, NULL, NULL);
308
309        if(otrmsg) {
310                if(!ctx) {
311                        otrl_message_free(otrmsg);
312                        g_mutex_unlock(ic->irc->otr_mutex);
313                        return 1;
314                }
315                st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx,
316                        otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
317                otrl_message_free(otrmsg);
318        } else {
319                /* yeah, well, some const casts as usual... ;-) */
320                st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags );
321        }
322       
323        g_mutex_unlock(ic->irc->otr_mutex);
324        return st;
325}
326
327void cmd_otr(irc_t *irc, char **args)
328{
329        const command_t *cmd;
330       
331        if(!args[0])
332                return;
333       
334        if(!args[1])
335                return;
336       
337        for(cmd=otr_commands; cmd->command; cmd++) {
338                if(strcmp(cmd->command, args[1]) == 0)
339                        break;
340        }
341       
342        if(!cmd->command) {
343                irc_usermsg(irc, "%s %s: unknown subcommand, see \x02help otr\x02",
344                        args[0], args[1]);
345                return;
346        }
347       
348        if(!args[cmd->required_parameters+1]) {
349                irc_usermsg(irc, "%s %s: not enough arguments (%d req.)",
350                        args[0], args[1], cmd->required_parameters);
351                return;
352        }
353       
354        cmd->execute(irc, args+1);
355}
356
357
358/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/
359
360OtrlPolicy op_policy(void *opdata, ConnContext *context)
361{
362        /* TODO: OTR policy configurable */
363        return OTRL_POLICY_OPPORTUNISTIC;
364}
365
366void op_create_privkey(void *opdata, const char *accountname,
367        const char *protocol)
368{
369        struct im_connection *ic = check_imc(opdata, accountname, protocol);
370        char *s;
371       
372        log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol);
373
374        s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?",
375                accountname, protocol);
376        query_add(ic->irc, ic, s, yes_keygen, no_keygen, ic->acc);
377}
378
379int op_is_logged_in(void *opdata, const char *accountname,
380        const char *protocol, const char *recipient)
381{
382        struct im_connection *ic = check_imc(opdata, accountname, protocol);
383        user_t *u;
384
385        log_message(LOGLVL_DEBUG, "op_is_logged_in '%s' '%s' '%s'", accountname, protocol, recipient);
386       
387        /* lookup the user_t for the given recipient */
388        u = user_findhandle(ic, recipient);
389        if(u) {
390                if(u->online)
391                        return 1;
392                else
393                        return 0;
394        } else {
395                return -1;
396        }
397}
398
399void op_inject_message(void *opdata, const char *accountname,
400        const char *protocol, const char *recipient, const char *message)
401{
402        struct im_connection *ic = check_imc(opdata, accountname, protocol);
403
404        log_message(LOGLVL_DEBUG, "op_inject_message '%s' '%s' '%s' '%s'", accountname, protocol, recipient, message);
405
406        if (strcmp(accountname, recipient) == 0) {
407                /* huh? injecting messages to myself? */
408                irc_usermsg(ic->irc, "note to self: %s", message);
409        } else {
410                /* need to drop some consts here :-( */
411                /* TODO: get flags into op_inject_message?! */
412                ic->acc->prpl->buddy_msg(ic, (char *)recipient, (char *)message, 0);
413                /* ignoring return value :-/ */
414        }
415}
416
417int op_display_otr_message(void *opdata, const char *accountname,
418        const char *protocol, const char *username, const char *msg)
419{
420        struct im_connection *ic = check_imc(opdata, accountname, protocol);
421
422        log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, msg);
423
424        irc_usermsg(ic->irc, "%s", msg);
425
426        return 0;
427}
428
429void op_new_fingerprint(void *opdata, OtrlUserState us,
430        const char *accountname, const char *protocol,
431        const char *username, unsigned char fingerprint[20])
432{
433        struct im_connection *ic = check_imc(opdata, accountname, protocol);
434        char hunam[45];         /* anybody looking? ;-) */
435       
436        otrl_privkey_hash_to_human(hunam, fingerprint);
437        log_message(LOGLVL_DEBUG, "op_new_fingerprint '%s' '%s' '%s' '%s'", accountname, protocol, username, hunam);
438
439        irc_usermsg(ic->irc, "new fingerprint for %s: %s",
440                peernick(ic->irc, username, protocol), hunam);
441}
442
443void op_write_fingerprints(void *opdata)
444{
445        struct im_connection *ic = (struct im_connection *)opdata;
446
447        log_message(LOGLVL_DEBUG, "op_write_fingerprints");
448
449        otr_save(ic->irc);
450}
451
452void op_gone_secure(void *opdata, ConnContext *context)
453{
454        struct im_connection *ic =
455                check_imc(opdata, context->accountname, context->protocol);
456
457        log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
458
459        irc_usermsg(ic->irc, "conversation with %s is now off the record",
460                peernick(ic->irc, context->username, context->protocol));
461}
462
463void op_gone_insecure(void *opdata, ConnContext *context)
464{
465        struct im_connection *ic =
466                check_imc(opdata, context->accountname, context->protocol);
467
468        log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
469
470        irc_usermsg(ic->irc, "conversation with %s is now in the clear",
471                peernick(ic->irc, context->username, context->protocol));
472}
473
474void op_still_secure(void *opdata, ConnContext *context, int is_reply)
475{
476        struct im_connection *ic =
477                check_imc(opdata, context->accountname, context->protocol);
478
479        log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d",
480                context->accountname, context->protocol, context->username, is_reply);
481
482        irc_usermsg(ic->irc, "otr connection with %s has been refreshed",
483                peernick(ic->irc, context->username, context->protocol));
484}
485
486void op_log_message(void *opdata, const char *message)
487{
488        log_message(LOGLVL_INFO, "%s", message);
489}
490
491
492/*** OTR sub-command handlers ***/
493
494void cmd_otr_abort(irc_t *irc, char **args)
495{
496        user_t *u;
497
498        u = user_find(irc, args[1]);
499        if(!u || !u->ic) {
500                irc_usermsg(irc, "%s: unknown user", args[1]);
501                return;
502        }
503       
504        otrl_message_disconnect(irc->otr_us, &global.otr_ops,
505                u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle);
506}
507
508void cmd_otr_request(irc_t *irc, char **args)
509{
510        user_t *u;
511
512        u = user_find(irc, args[1]);
513        if(!u || !u->ic) {
514                irc_usermsg(irc, "%s: unknown user", args[1]);
515                return;
516        }
517        if(!u->online) {
518                irc_usermsg(irc, "%s is offline", args[1]);
519                return;
520        }
521       
522        imc_buddy_msg(u->ic, u->handle, "?OTR?", 0);
523}
524
525void cmd_otr_auth(irc_t *irc, char **args)
526{
527        user_t *u;
528        ConnContext *ctx;
529       
530        u = user_find(irc, args[1]);
531        if(!u || !u->ic) {
532                irc_usermsg(irc, "%s: unknown user", args[1]);
533                return;
534        }
535        if(!u->online) {
536                irc_usermsg(irc, "%s is offline", args[1]);
537                return;
538        }
539       
540        ctx = otrl_context_find(irc->otr_us, u->handle,
541                u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL);
542        if(!ctx) {
543                /* huh? out of memory or what? */
544                return;
545        }
546
547        if(ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) {
548                log_message(LOGLVL_INFO,
549                        "SMP already in phase %d, sending abort before reinitiating",
550                        ctx->smstate->nextExpected+1);
551                otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx);
552                otrl_sm_state_free(ctx->smstate);
553        }
554       
555        /* warning: the following assumes that smstates are cleared whenever an SMP
556           is completed or aborted! */ 
557        if(ctx->smstate->secret == NULL) {
558                irc_usermsg(irc, "smp: initiating with %s...", u->nick);
559                otrl_message_initiate_smp(irc->otr_us, &global.otr_ops,
560                        u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
561                /* smp is now in EXPECT2 */
562        } else {
563                /* if we're still in EXPECT1 but smstate is initialized, we must have
564                   received the SMP1, so let's issue a response */
565                irc_usermsg(irc, "smp: responding to %s...", u->nick);
566                otrl_message_respond_smp(irc->otr_us, &global.otr_ops,
567                        u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
568                /* smp is now in EXPECT3 */
569        }
570}
571
572void cmd_otr_fprints(irc_t *irc, char **args)
573{
574        if(args[1]) {
575                /* list given buddy's fingerprints */
576                user_t *u;
577                ConnContext *ctx;
578       
579                u = user_find(irc, args[1]);
580                if(!u || !u->ic) {
581                        irc_usermsg(irc, "%s: unknown user", args[1]);
582                        return;
583                }
584       
585                ctx = otrl_context_find(irc->otr_us, u->handle,
586                        u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
587                if(!ctx) {
588                        irc_usermsg(irc, "no fingerprints");
589                } else {
590                        show_fingerprints(irc, ctx);
591                }
592        } else {
593                /* list all known fingerprints */
594                ConnContext *ctx;
595                for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {
596                        irc_usermsg(irc, "[%s]", peernick(irc, ctx->username, ctx->protocol));
597                        show_fingerprints(irc, ctx);
598                }
599                if(!irc->otr_us->context_root) {
600                        irc_usermsg(irc, "no fingerprints");
601                }
602        }
603}
604
605void cmd_otr_info(irc_t *irc, char **args)
606{
607        user_t *u;
608        ConnContext *ctx;
609        Fingerprint *fp;
610        char human[45];
611        const char *offer_status;
612        const char *message_state;
613        const char *trust;
614
615        if(!args) {
616                irc_usermsg(irc, "no args?!");
617                return;
618        }
619        if(!args[1]) {
620                irc_usermsg(irc, "no args[1]?!");
621                return;
622        }
623        u = user_find(irc, args[1]);
624        if(!u || !u->ic) {
625                irc_usermsg(irc, "%s: unknown user", args[1]);
626                return;
627        }
628       
629        ctx = otrl_context_find(irc->otr_us, u->handle,
630                u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
631        if(!ctx) {
632                irc_usermsg(irc, "no otr info on %s", args[1]);
633                return;
634        }
635
636        switch(ctx->otr_offer) {
637        case OFFER_NOT:       offer_status="none sent";          break;
638        case OFFER_SENT:      offer_status="awaiting reply";     break;
639        case OFFER_ACCEPTED:  offer_status="accepted our offer"; break;
640        case OFFER_REJECTED:  offer_status="ignored our offer";  break;
641        default:              offer_status="?";
642        }
643
644        switch(ctx->msgstate) {
645        case OTRL_MSGSTATE_PLAINTEXT: message_state="cleartext"; break;
646        case OTRL_MSGSTATE_ENCRYPTED: message_state="encrypted"; break;
647        case OTRL_MSGSTATE_FINISHED:  message_state="shut down"; break;
648        default:                      message_state="?";
649        }
650
651        irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1],
652                ctx->username, ctx->protocol, ctx->accountname, ctx->protocol);
653        irc_usermsg(irc, "  otr offer status: %s", offer_status);
654        irc_usermsg(irc, "  connection state: %s", message_state);
655        irc_usermsg(irc, "  protocol version: %d", ctx->protocol_version);
656        fp = ctx->active_fingerprint;
657        if(!fp) {
658                irc_usermsg(irc, "  active f'print:   none");
659        } else {
660                otrl_privkey_hash_to_human(human, fp->fingerprint);
661                if(!fp->trust || fp->trust[0] == '\0') {
662                        trust="untrusted";
663                } else {
664                        trust=fp->trust;
665                }
666                irc_usermsg(irc, "  active f'print:   %s (%s)", human, trust);
667        }
668}
669
670void cmd_otr_policy(irc_t *irc, char **args)
671{
672        irc_usermsg(irc, "n/a: not implemented");
673}
674
675
676/*** local helpers / subroutines: ***/
677
678/* Socialist Millionaires' Protocol */
679void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs)
680{
681        irc_t *irc = ic->irc;
682        OtrlUserState us = irc->otr_us;
683        OtrlMessageAppOps *ops = &global.otr_ops;
684        OtrlTLV *tlv = NULL;
685        ConnContext *context;
686        NextExpectedSMP nextMsg;
687        user_t *u;
688
689        u = user_findhandle(ic, handle);
690        if(!u) return;
691        context = otrl_context_find(us, handle,
692                ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL);
693        if(!context) {
694                /* huh? out of memory or what? */
695                return;
696        }
697        nextMsg = context->smstate->nextExpected;
698
699        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
700        if (tlv) {
701                if (nextMsg != OTRL_SMP_EXPECT1) {
702                        irc_usermsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick);
703                        otrl_message_abort_smp(us, ops, u->ic, context);
704                        otrl_sm_state_free(context->smstate);
705                } else {
706                        irc_usermsg(irc, "smp: initiated by %s"
707                                " - respond with \x02otr smp %s <secret>\x02",
708                                u->nick, u->nick);
709                        /* smp stays in EXPECT1 until user responds */
710                }
711        }
712        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
713        if (tlv) {
714                if (nextMsg != OTRL_SMP_EXPECT2) {
715                        irc_usermsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick);
716                        otrl_message_abort_smp(us, ops, u->ic, context);
717                        otrl_sm_state_free(context->smstate);
718                } else {
719                        /* SMP2 received, otrl_message_receiving will have sent SMP3 */
720                        context->smstate->nextExpected = OTRL_SMP_EXPECT4;
721                }
722        }
723        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
724        if (tlv) {
725                if (nextMsg != OTRL_SMP_EXPECT3) {
726                        irc_usermsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick);
727                        otrl_message_abort_smp(us, ops, u->ic, context);
728                        otrl_sm_state_free(context->smstate);
729                } else {
730                        /* SMP3 received, otrl_message_receiving will have sent SMP4 and set fp trust */
731                        const char *trust = context->active_fingerprint->trust;
732                        if(!trust || trust[0]=='\0') {
733                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
734                                        u->nick);
735                        } else {
736                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
737                                        u->nick);
738                        }
739                        otrl_sm_state_free(context->smstate);
740                        /* smp is in back in EXPECT1 */
741                }
742        }
743        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
744        if (tlv) {
745                if (nextMsg != OTRL_SMP_EXPECT4) {
746                        irc_usermsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick);
747                        otrl_message_abort_smp(us, ops, u->ic, context);
748                        otrl_sm_state_free(context->smstate);
749                } else {
750                        /* SMP4 received, otrl_message_receiving will have set fp trust */
751                        const char *trust = context->active_fingerprint->trust;
752                        if(!trust || trust[0]=='\0') {
753                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
754                                        u->nick);
755                        } else {
756                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
757                                        u->nick);
758                        }
759                        otrl_sm_state_free(context->smstate);
760                        /* smp is in back in EXPECT1 */
761                }
762        }
763        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
764        if (tlv) {
765                irc_usermsg(irc, "smp: received abort from %s", u->nick);
766                otrl_sm_state_free(context->smstate);
767                /* smp is in back in EXPECT1 */
768        }
769}
770
771/* helper to assert that account and protocol names given to ops below always
772   match the im_connection passed through as opdata */
773struct im_connection *check_imc(void *opdata, const char *accountname,
774        const char *protocol)
775{
776        struct im_connection *ic = (struct im_connection *)opdata;
777
778        if (strcmp(accountname, ic->acc->user) != 0) {
779                log_message(LOGLVL_WARNING,
780                        "otr: internal account name mismatch: '%s' vs '%s'",
781                        accountname, ic->acc->user);
782        }
783        if (strcmp(protocol, ic->acc->prpl->name) != 0) {
784                log_message(LOGLVL_WARNING,
785                        "otr: internal protocol name mismatch: '%s' vs '%s'",
786                        protocol, ic->acc->prpl->name);
787        }
788       
789        return ic;
790}
791
792const char *peernick(irc_t *irc, const char *handle, const char *protocol)
793{
794        user_t *u;
795        static char fallback[512];
796       
797        g_snprintf(fallback, 511, "%s/%s", handle, protocol);
798        for(u=irc->users; u; u=u->next) {
799                struct prpl *prpl;
800                if(!u->ic || !u->handle)
801                        break;
802                prpl = u->ic->acc->prpl;
803                if(strcmp(prpl->name, protocol) == 0
804                        && prpl->handle_cmp(u->handle, handle) == 0) {
805                        return u->nick;
806                }
807        }
808       
809        return fallback;
810}
811
812void show_fingerprints(irc_t *irc, ConnContext *ctx)
813{
814        char human[45];
815        Fingerprint *fp;
816        const char *trust;
817        int count=0;
818       
819        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
820                if(!fp->fingerprint)
821                        continue;
822                count++;
823                otrl_privkey_hash_to_human(human, fp->fingerprint);
824                if(!fp->trust || fp->trust[0] == '\0') {
825                        trust="untrusted";
826                } else {
827                        trust=fp->trust;
828                }
829                if(fp == ctx->active_fingerprint) {
830                        irc_usermsg(irc, "\x02%s (%s)\x02", human, trust);
831                } else {
832                        irc_usermsg(irc, "%s (%s)", human, trust);
833                }
834        }
835        if(count==0)
836                irc_usermsg(irc, "no fingerprints");
837}
838
839void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
840{
841        GError *err;
842        GThread *thr;
843        struct kgdata *kg;
844        gint ev;
845       
846        irc_usermsg(irc, "generating new otr privkey for %s/%s...",
847                handle, protocol);
848       
849        kg = g_new0(struct kgdata, 1);
850        if(!kg) {
851                irc_usermsg(irc, "otr keygen failed: out of memory");
852                return;
853        }
854
855        /* Assemble the job description to be passed to thread and handler */
856        kg->irc = irc;
857        kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick);
858        if(!kg->keyfile) {
859                irc_usermsg(irc, "otr keygen failed: out of memory");
860                g_free(kg);
861                return;
862        }
863        kg->handle = handle;
864        kg->protocol = protocol;
865        kg->mutex = g_mutex_new();
866        if(!kg->mutex) {
867                irc_usermsg(irc, "otr keygen failed: couldn't create mutex");
868                g_free(kg->keyfile);
869                g_free(kg);
870                return;
871        }
872        kg->done = FALSE;
873
874        /* Poll for completion of the thread periodically. I would have preferred
875           to just wait on a pipe but this way it's portable to Windows. *sigh*
876        */
877        ev = b_timeout_add(1000, &keygen_finish_handler, kg);
878        if(!ev) {
879                irc_usermsg(irc, "otr keygen failed: couldn't register timeout");
880                g_free(kg->keyfile);
881                g_mutex_free(kg->mutex);
882                g_free(kg);
883                return;
884        }
885
886        thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err);
887        if(!thr) {
888                irc_usermsg(irc, "otr keygen failed: %s", err->message);
889                g_free(kg->keyfile);
890                g_mutex_free(kg->mutex);
891                g_free(kg);
892                b_event_remove(ev);
893        }
894}
895
896gpointer otr_keygen_thread_func(gpointer data)
897{
898        struct kgdata *kg = (struct kgdata *)data;
899       
900        /* lock OTR subsystem and do the work */
901        g_mutex_lock(kg->irc->otr_mutex);
902        kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle,
903                kg->protocol);
904        g_mutex_unlock(kg->irc->otr_mutex);
905        /* OTR enabled again */
906       
907        /* notify mainloop */
908        g_mutex_lock(kg->mutex);
909        kg->done = TRUE;
910        g_mutex_unlock(kg->mutex);
911       
912        return NULL;
913}
914
915gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
916{
917        struct kgdata *kg = (struct kgdata *)data;
918        int done;
919       
920        g_mutex_lock(kg->mutex);
921        done = kg->done;
922        g_mutex_unlock(kg->mutex);
923        if(kg->done) {
924                if(kg->result) {
925                        irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result));
926                } else {
927                        irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol);
928                }
929                g_free(kg->keyfile);
930                g_mutex_free(kg->mutex);
931                g_free(kg);
932                return FALSE; /* unregister timeout */
933        }
934
935        return TRUE;  /* still working, continue checking */
936}
937
938void yes_keygen(gpointer w, void *data)
939{
940        account_t *acc = (account_t *)data;
941       
942        otr_keygen(acc->irc, acc->user, acc->prpl->name);
943}
944
945void no_keygen(gpointer w, void *data)
946{
947        account_t *acc = (account_t *)data;
948       
949        irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s",
950                acc->user, acc->prpl->name);
951}
952
953
954#else /* WITH_OTR undefined */
955
956void cmd_otr(irc_t *irc, char **args)
957{
958        irc_usermsg(irc, "otr: n/a, compiled without OTR support");
959}
960
961#endif
Note: See TracBrowser for help on using the repository browser.