source: otr.c @ 764c7d1

Last change on this file since 764c7d1 was 764c7d1, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-03T21:30:03Z

OTR support, first checkin

  • Property mode set to 100644
File size: 26.7 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, "%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, "%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
184        log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick);
185
186        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick);
187        otrl_privkey_write_fingerprints(irc->otr_us, s);
188}
189
190void otr_remove(const char *nick)
191{
192        char s[512];
193       
194        log_message(LOGLVL_DEBUG, "otr_remove '%s'", nick);
195
196        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick);
197        unlink(s);
198        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick);
199        unlink(s);
200}
201
202void otr_rename(const char *onick, const char *nnick)
203{
204        char s[512], t[512];
205       
206        log_message(LOGLVL_DEBUG, "otr_rename '%s' -> '%s'", onick, nnick);
207
208        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick);
209        g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick);
210        rename(s,t);
211        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick);
212        g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick);
213        rename(s,t);
214}
215
216void otr_check_for_key(account_t *a)
217{
218        irc_t *irc = a->irc;
219        char buf[45];
220        char *fp;
221       
222        fp = otrl_privkey_fingerprint(irc->otr_us, buf, a->user, a->prpl->name);
223        if(fp) {
224                irc_usermsg(irc, "otr: %s/%s ready with f'print %s",
225                        a->user, a->prpl->name, fp);
226        } else {
227                otr_keygen(irc, a->user, a->prpl->name);
228        }
229}
230
231char *otr_handle_message(struct im_connection *ic, const char *handle, const char *msg)
232{
233        int ignore_msg;
234        char *newmsg = NULL;
235        OtrlTLV *tlvs = NULL;
236        char *colormsg;
237       
238    if(!g_mutex_trylock(ic->irc->otr_mutex)) {
239        /* TODO: queue msgs received during keygen for later */
240                irc_usermsg(ic->irc, "msg from %s/%s during keygen - dropped",
241                        handle, ic->acc->prpl->name);
242                return NULL;
243        }
244
245        ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic,
246                ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg,
247                &tlvs, NULL, NULL);
248
249        otr_handle_smp(ic, handle, tlvs);
250       
251        if(ignore_msg) {
252                /* this was an internal OTR protocol message */
253                g_mutex_unlock(ic->irc->otr_mutex);
254                return NULL;
255        } else if(!newmsg) {
256                /* this was a non-OTR message */
257                g_mutex_unlock(ic->irc->otr_mutex);
258                return g_strdup(msg);
259        } else {
260                /* OTR has processed this message */
261                ConnContext *context = otrl_context_find(ic->irc->otr_us, handle,
262                        ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL);
263                if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
264                        /* color according to f'print trust */
265                        char color;
266                        const char *trust = context->active_fingerprint->trust;
267                        if(trust && trust[0] != '\0')
268                                color='3';   /* green */
269                        else
270                                color='5';   /* red */
271                        colormsg = g_strdup_printf("\x03%c%s\x0F", color, newmsg);
272                } else {
273                        colormsg = g_strdup(newmsg);
274                }
275                otrl_message_free(newmsg);
276                g_mutex_unlock(ic->irc->otr_mutex);
277                return colormsg;
278        }
279}
280
281int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags)
282{       
283    int st;
284    char *otrmsg = NULL;
285    ConnContext *ctx = NULL;
286   
287    if(!g_mutex_trylock(ic->irc->otr_mutex)) {
288        irc_usermsg(ic->irc, "msg to %s/%s during keygen - not sent",
289                handle, ic->acc->prpl->name);
290        return 1;
291    }
292   
293        st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic,
294                ic->acc->user, ic->acc->prpl->name, handle,
295                msg, NULL, &otrmsg, NULL, NULL);
296        if(st) {
297                g_mutex_unlock(ic->irc->otr_mutex);
298                return st;
299        }
300
301        ctx = otrl_context_find(ic->irc->otr_us,
302                        handle, ic->acc->user, ic->acc->prpl->name,
303                        1, NULL, NULL, NULL);
304
305        if(otrmsg) {
306                if(!ctx) {
307                        otrl_message_free(otrmsg);
308                        g_mutex_unlock(ic->irc->otr_mutex);
309                        return 1;
310                }
311                st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx,
312                        otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
313                otrl_message_free(otrmsg);
314        } else {
315                /* yeah, well, some const casts as usual... ;-) */
316                st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags );
317        }
318       
319        g_mutex_unlock(ic->irc->otr_mutex);
320        return st;
321}
322
323void cmd_otr(irc_t *irc, char **args)
324{
325        const command_t *cmd;
326       
327        if(!args[0])
328                return;
329       
330        if(!args[1])
331                return;
332       
333        for(cmd=otr_commands; cmd->command; cmd++) {
334                if(strcmp(cmd->command, args[1]) == 0)
335                        break;
336        }
337       
338        if(!cmd->command) {
339                irc_usermsg(irc, "%s %s: unknown subcommand, see \x02help otr\x02",
340                        args[0], args[1]);
341                return;
342        }
343       
344        if(!args[cmd->required_parameters+1]) {
345                irc_usermsg(irc, "%s %s: not enough arguments (%d req.)",
346                        args[0], args[1], cmd->required_parameters);
347                return;
348        }
349       
350        cmd->execute(irc, args+1);
351}
352
353
354/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/
355
356OtrlPolicy op_policy(void *opdata, ConnContext *context)
357{
358        /* TODO: OTR policy configurable */
359        return OTRL_POLICY_OPPORTUNISTIC;
360}
361
362void op_create_privkey(void *opdata, const char *accountname,
363        const char *protocol)
364{
365        struct im_connection *ic = check_imc(opdata, accountname, protocol);
366        char *s;
367       
368        log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol);
369
370        s = g_strdup_printf("oops, no otr privkey for %s/%s - generate one now?",
371                accountname, protocol);
372        query_add(ic->irc, ic, s, yes_keygen, no_keygen, ic->acc);
373}
374
375int op_is_logged_in(void *opdata, const char *accountname,
376        const char *protocol, const char *recipient)
377{
378        struct im_connection *ic = check_imc(opdata, accountname, protocol);
379        user_t *u;
380
381        log_message(LOGLVL_DEBUG, "op_is_logged_in '%s' '%s' '%s'", accountname, protocol, recipient);
382       
383        /* lookup the user_t for the given recipient */
384        u = user_findhandle(ic, recipient);
385        if(u) {
386                if(u->online)
387                        return 1;
388                else
389                        return 0;
390        } else {
391                return -1;
392        }
393}
394
395void op_inject_message(void *opdata, const char *accountname,
396        const char *protocol, const char *recipient, const char *message)
397{
398        struct im_connection *ic = check_imc(opdata, accountname, protocol);
399
400        log_message(LOGLVL_DEBUG, "op_inject_message '%s' '%s' '%s' '%s'", accountname, protocol, recipient, message);
401
402        if (strcmp(accountname, recipient) == 0) {
403                /* huh? injecting messages to myself? */
404                irc_usermsg(ic->irc, "note to self: %s", message);
405        } else {
406                /* need to drop some consts here :-( */
407                /* TODO: get flags into op_inject_message?! */
408                ic->acc->prpl->buddy_msg(ic, (char *)recipient, (char *)message, 0);
409                /* ignoring return value :-/ */
410        }
411}
412
413int op_display_otr_message(void *opdata, const char *accountname,
414        const char *protocol, const char *username, const char *msg)
415{
416        struct im_connection *ic = check_imc(opdata, accountname, protocol);
417
418        log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, msg);
419
420        irc_usermsg(ic->irc, "%s", msg);
421
422        return 0;
423}
424
425void op_new_fingerprint(void *opdata, OtrlUserState us,
426        const char *accountname, const char *protocol,
427        const char *username, unsigned char fingerprint[20])
428{
429        struct im_connection *ic = check_imc(opdata, accountname, protocol);
430        char hunam[45];         /* anybody looking? ;-) */
431       
432        otrl_privkey_hash_to_human(hunam, fingerprint);
433        log_message(LOGLVL_DEBUG, "op_new_fingerprint '%s' '%s' '%s' '%s'", accountname, protocol, username, hunam);
434
435        irc_usermsg(ic->irc, "new fingerprint for %s: %s",
436                peernick(ic->irc, username, protocol), hunam);
437}
438
439void op_write_fingerprints(void *opdata)
440{
441        struct im_connection *ic = (struct im_connection *)opdata;
442
443        log_message(LOGLVL_DEBUG, "op_write_fingerprints");
444
445        otr_save(ic->irc);
446}
447
448void op_gone_secure(void *opdata, ConnContext *context)
449{
450        struct im_connection *ic =
451                check_imc(opdata, context->accountname, context->protocol);
452
453        log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
454
455        irc_usermsg(ic->irc, "conversation with %s is now off the record",
456                peernick(ic->irc, context->username, context->protocol));
457}
458
459void op_gone_insecure(void *opdata, ConnContext *context)
460{
461        struct im_connection *ic =
462                check_imc(opdata, context->accountname, context->protocol);
463
464        log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
465
466        irc_usermsg(ic->irc, "conversation with %s is now in the clear",
467                peernick(ic->irc, context->username, context->protocol));
468}
469
470void op_still_secure(void *opdata, ConnContext *context, int is_reply)
471{
472        struct im_connection *ic =
473                check_imc(opdata, context->accountname, context->protocol);
474
475        log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d",
476                context->accountname, context->protocol, context->username, is_reply);
477
478        irc_usermsg(ic->irc, "otr connection with %s has been refreshed",
479                peernick(ic->irc, context->username, context->protocol));
480}
481
482void op_log_message(void *opdata, const char *message)
483{
484        log_message(LOGLVL_INFO, "%s", message);
485}
486
487
488/*** OTR sub-command handlers ***/
489
490void cmd_otr_abort(irc_t *irc, char **args)
491{
492        user_t *u;
493
494        u = user_find(irc, args[1]);
495        if(!u || !u->ic) {
496                irc_usermsg(irc, "%s: unknown user", args[1]);
497                return;
498        }
499       
500        otrl_message_disconnect(irc->otr_us, &global.otr_ops,
501                u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle);
502}
503
504void cmd_otr_request(irc_t *irc, char **args)
505{
506        user_t *u;
507
508        u = user_find(irc, args[1]);
509        if(!u || !u->ic) {
510                irc_usermsg(irc, "%s: unknown user", args[1]);
511                return;
512        }
513        if(!u->online) {
514                irc_usermsg(irc, "%s is offline", args[1]);
515                return;
516        }
517       
518        imc_buddy_msg(u->ic, u->handle, "?OTR?", 0);
519}
520
521void cmd_otr_auth(irc_t *irc, char **args)
522{
523        user_t *u;
524        ConnContext *ctx;
525       
526        u = user_find(irc, args[1]);
527        if(!u || !u->ic) {
528                irc_usermsg(irc, "%s: unknown user", args[1]);
529                return;
530        }
531        if(!u->online) {
532                irc_usermsg(irc, "%s is offline", args[1]);
533                return;
534        }
535       
536        ctx = otrl_context_find(irc->otr_us, u->handle,
537                u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL);
538        if(!ctx) {
539                return;
540        }
541
542        if(ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) {
543                log_message(LOGLVL_INFO,
544                        "SMP already in phase %d, sending abort before reinitiating",
545                        ctx->smstate->nextExpected+1);
546                otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx);
547                otrl_sm_state_free(ctx->smstate);
548        }
549       
550        /* warning: the following assumes that smstates are cleared whenever an SMP
551           is completed or aborted! */ 
552        if(ctx->smstate->secret == NULL) {
553                irc_usermsg(irc, "smp: initiating with %s...", u->nick);
554                otrl_message_initiate_smp(irc->otr_us, &global.otr_ops,
555                        u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
556                /* smp is now in EXPECT2 */
557        } else {
558                /* if we're still in EXPECT1 but smstate is initialized, we must have
559                   received the SMP1, so let's issue a response */
560                irc_usermsg(irc, "smp: responding to %s...", u->nick);
561                otrl_message_respond_smp(irc->otr_us, &global.otr_ops,
562                        u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
563                /* smp is now in EXPECT3 */
564        }
565}
566
567void cmd_otr_fprints(irc_t *irc, char **args)
568{
569        if(args[1]) {
570                /* list given buddy's fingerprints */
571                user_t *u;
572                ConnContext *ctx;
573       
574                u = user_find(irc, args[1]);
575                if(!u || !u->ic) {
576                        irc_usermsg(irc, "%s: unknown user", args[1]);
577                        return;
578                }
579       
580                ctx = otrl_context_find(irc->otr_us, u->handle,
581                        u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
582                if(!ctx) {
583                        irc_usermsg(irc, "no fingerprints");
584                } else {
585                        show_fingerprints(irc, ctx);
586                }
587        } else {
588                /* list all known fingerprints */
589                ConnContext *ctx;
590                for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {
591                        irc_usermsg(irc, "[%s]", peernick(irc, ctx->username, ctx->protocol));
592                        show_fingerprints(irc, ctx);
593                }
594                if(!irc->otr_us->context_root) {
595                        irc_usermsg(irc, "no fingerprints");
596                }
597        }
598}
599
600void cmd_otr_info(irc_t *irc, char **args)
601{
602        user_t *u;
603        ConnContext *ctx;
604        Fingerprint *fp;
605        char human[45];
606        const char *offer_status;
607        const char *message_state;
608        const char *trust;
609
610        if(!args) {
611                irc_usermsg(irc, "no args?!");
612                return;
613        }
614        if(!args[1]) {
615                irc_usermsg(irc, "no args[1]?!");
616                return;
617        }
618        u = user_find(irc, args[1]);
619        if(!u || !u->ic) {
620                irc_usermsg(irc, "%s: unknown user", args[1]);
621                return;
622        }
623       
624        ctx = otrl_context_find(irc->otr_us, u->handle,
625                u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
626        if(!ctx) {
627                irc_usermsg(irc, "no otr info on %s", args[1]);
628                return;
629        }
630
631        switch(ctx->otr_offer) {
632        case OFFER_NOT:       offer_status="none sent";          break;
633        case OFFER_SENT:      offer_status="awaiting reply";     break;
634        case OFFER_ACCEPTED:  offer_status="accepted our offer"; break;
635        case OFFER_REJECTED:  offer_status="ignored our offer";  break;
636        default:              offer_status="?";
637        }
638
639        switch(ctx->msgstate) {
640        case OTRL_MSGSTATE_PLAINTEXT: message_state="cleartext"; break;
641        case OTRL_MSGSTATE_ENCRYPTED: message_state="encrypted"; break;
642        case OTRL_MSGSTATE_FINISHED:  message_state="shut down"; break;
643        default:                      message_state="?";
644        }
645
646        irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1],
647                ctx->username, ctx->protocol, ctx->accountname, ctx->protocol);
648        irc_usermsg(irc, "  otr offer status: %s", offer_status);
649        irc_usermsg(irc, "  connection state: %s", message_state);
650        irc_usermsg(irc, "  protocol version: %d", ctx->protocol_version);
651        fp = ctx->active_fingerprint;
652        if(!fp) {
653                irc_usermsg(irc, "  active f'print:   none");
654        } else {
655                otrl_privkey_hash_to_human(human, fp->fingerprint);
656                if(!fp->trust || fp->trust[0] == '\0') {
657                        trust="untrusted";
658                } else {
659                        trust=fp->trust;
660                }
661                irc_usermsg(irc, "  active f'print:   %s (%s)", human, trust);
662        }
663}
664
665void cmd_otr_policy(irc_t *irc, char **args)
666{
667        irc_usermsg(irc, "n/a: not implemented");
668}
669
670
671/*** local helpers / subroutines: ***/
672
673/* Socialist Millionaires' Protocol */
674void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs)
675{
676        irc_t *irc = ic->irc;
677        OtrlUserState us = irc->otr_us;
678        OtrlMessageAppOps *ops = &global.otr_ops;
679        OtrlTLV *tlv = NULL;
680        ConnContext *context;
681        NextExpectedSMP nextMsg;
682        user_t *u;
683
684        u = user_findhandle(ic, handle);
685        if(!u) return;
686        context = otrl_context_find(us, handle,
687                ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL);
688        if(!context) return;
689        nextMsg = context->smstate->nextExpected;
690
691        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
692        if (tlv) {
693                if (nextMsg != OTRL_SMP_EXPECT1) {
694                        irc_usermsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick);
695                        otrl_message_abort_smp(us, ops, u->ic, context);
696                        otrl_sm_state_free(context->smstate);
697                } else {
698                        irc_usermsg(irc, "smp: initiated by %s"
699                                " - respond with \x02otr smp %s <secret>\x02",
700                                u->nick, u->nick);
701                        /* smp stays in EXPECT1 until user responds */
702                }
703        }
704        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
705        if (tlv) {
706                if (nextMsg != OTRL_SMP_EXPECT2) {
707                        irc_usermsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick);
708                        otrl_message_abort_smp(us, ops, u->ic, context);
709                        otrl_sm_state_free(context->smstate);
710                } else {
711                        /* SMP2 received, otrl_message_receiving will have sent SMP3 */
712                        context->smstate->nextExpected = OTRL_SMP_EXPECT4;
713                }
714        }
715        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
716        if (tlv) {
717                if (nextMsg != OTRL_SMP_EXPECT3) {
718                        irc_usermsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick);
719                        otrl_message_abort_smp(us, ops, u->ic, context);
720                        otrl_sm_state_free(context->smstate);
721                } else {
722                        /* SMP3 received, otrl_message_receiving will have sent SMP4 and set fp trust */
723                        const char *trust = context->active_fingerprint->trust;
724                        if(!trust || trust[0]=='\0') {
725                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
726                                        u->nick);
727                        } else {
728                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
729                                        u->nick);
730                        }
731                        otrl_sm_state_free(context->smstate);
732                        /* smp is in back in EXPECT1 */
733                }
734        }
735        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
736        if (tlv) {
737                if (nextMsg != OTRL_SMP_EXPECT4) {
738                        irc_usermsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick);
739                        otrl_message_abort_smp(us, ops, u->ic, context);
740                        otrl_sm_state_free(context->smstate);
741                } else {
742                        /* SMP4 received, otrl_message_receiving will have set fp trust */
743                        const char *trust = context->active_fingerprint->trust;
744                        if(!trust || trust[0]=='\0') {
745                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
746                                        u->nick);
747                        } else {
748                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
749                                        u->nick);
750                        }
751                        otrl_sm_state_free(context->smstate);
752                        /* smp is in back in EXPECT1 */
753                }
754        }
755        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
756        if (tlv) {
757                irc_usermsg(irc, "smp: received abort from %s", u->nick);
758                otrl_sm_state_free(context->smstate);
759                /* smp is in back in EXPECT1 */
760        }
761}
762
763/* helper to assert that account and protocol names given to ops below always
764   match the im_connection passed through as opdata */
765struct im_connection *check_imc(void *opdata, const char *accountname,
766        const char *protocol)
767{
768        struct im_connection *ic = (struct im_connection *)opdata;
769
770        if (strcmp(accountname, ic->acc->user) != 0) {
771                log_message(LOGLVL_WARNING,
772                        "otr: internal account name mismatch: '%s' vs '%s'",
773                        accountname, ic->acc->user);
774        }
775        if (strcmp(protocol, ic->acc->prpl->name) != 0) {
776                log_message(LOGLVL_WARNING,
777                        "otr: internal protocol name mismatch: '%s' vs '%s'",
778                        protocol, ic->acc->prpl->name);
779        }
780       
781        return ic;
782}
783
784const char *peernick(irc_t *irc, const char *handle, const char *protocol)
785{
786        user_t *u;
787        static char fallback[512];
788       
789        g_snprintf(fallback, 511, "%s/%s", handle, protocol);
790        for(u=irc->users; u; u=u->next) {
791                struct prpl *prpl;
792                if(!u->ic || !u->handle)
793                        break;
794                prpl = u->ic->acc->prpl;
795                if(strcmp(prpl->name, protocol) == 0
796                        && prpl->handle_cmp(u->handle, handle) == 0) {
797                        return u->nick;
798                }
799        }
800       
801        return fallback;
802}
803
804void show_fingerprints(irc_t *irc, ConnContext *ctx)
805{
806        char human[45];
807        Fingerprint *fp;
808        const char *trust;
809        int count=0;
810       
811        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
812                if(!fp->fingerprint)
813                        continue;
814                count++;
815                otrl_privkey_hash_to_human(human, fp->fingerprint);
816                if(!fp->trust || fp->trust[0] == '\0') {
817                        trust="untrusted";
818                } else {
819                        trust=fp->trust;
820                }
821                if(fp == ctx->active_fingerprint) {
822                        irc_usermsg(irc, "\x02%s (%s)\x02", human, trust);
823                } else {
824                        irc_usermsg(irc, "%s (%s)", human, trust);
825                }
826        }
827        if(count==0)
828                irc_usermsg(irc, "no fingerprints");
829}
830
831void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
832{
833        GError *err;
834        GThread *thr;
835        struct kgdata *kg;
836        gint ev;
837       
838        irc_usermsg(irc, "generating new otr privkey for %s/%s...",
839                handle, protocol);
840       
841        kg = g_new0(struct kgdata, 1);
842        if(!kg) {
843                irc_usermsg(irc, "otr keygen failed: out of memory");
844                return;
845        }
846
847        /* Assemble the job description to be passed to thread and handler */
848        kg->irc = irc;
849        kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick);
850        if(!kg->keyfile) {
851                irc_usermsg(irc, "otr keygen failed: out of memory");
852                g_free(kg);
853                return;
854        }
855        kg->handle = handle;
856        kg->protocol = protocol;
857        kg->mutex = g_mutex_new();
858        if(!kg->mutex) {
859                irc_usermsg(irc, "otr keygen failed: couldn't create mutex");
860                g_free(kg->keyfile);
861                g_free(kg);
862                return;
863        }
864        kg->done = FALSE;
865
866        /* Poll for completion of the thread periodically. I would have preferred
867           to just wait on a pipe but this way it's portable to Windows. *sigh*
868        */
869        ev = b_timeout_add(1000, &keygen_finish_handler, kg);
870        if(!ev) {
871                irc_usermsg(irc, "otr keygen failed: couldn't register timeout");
872                g_free(kg->keyfile);
873                g_mutex_free(kg->mutex);
874                g_free(kg);
875                return;
876        }
877
878        thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err);
879        if(!thr) {
880                irc_usermsg(irc, "otr keygen failed: %s", err->message);
881                g_free(kg->keyfile);
882                g_mutex_free(kg->mutex);
883                g_free(kg);
884                b_event_remove(ev);
885        }
886}
887
888gpointer otr_keygen_thread_func(gpointer data)
889{
890        struct kgdata *kg = (struct kgdata *)data;
891       
892        /* lock OTR subsystem and do the work */
893        g_mutex_lock(kg->irc->otr_mutex);
894        kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle,
895                kg->protocol);
896        g_mutex_unlock(kg->irc->otr_mutex);
897        /* OTR enabled again */
898       
899        /* notify mainloop */
900        g_mutex_lock(kg->mutex);
901        kg->done = TRUE;
902        g_mutex_unlock(kg->mutex);
903       
904        return NULL;
905}
906
907gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
908{
909        struct kgdata *kg = (struct kgdata *)data;
910        int done;
911       
912        g_mutex_lock(kg->mutex);
913        done = kg->done;
914        g_mutex_unlock(kg->mutex);
915        if(kg->done) {
916                if(kg->result) {
917                        irc_usermsg(kg->irc, "otr keygen failed: libgcrypt error"); /* TODO: diagnostics */
918                } else {
919                        irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol);
920                }
921                g_free(kg->keyfile);
922                g_mutex_free(kg->mutex);
923                g_free(kg);
924                return FALSE; /* unregister timeout */
925        }
926
927        return TRUE;  /* still working, continue checking */
928}
929
930void yes_keygen(gpointer w, void *data)
931{
932        account_t *acc = (account_t *)data;
933       
934        otr_keygen(acc->irc, acc->user, acc->prpl->name);
935}
936
937void no_keygen(gpointer w, void *data)
938{
939        account_t *acc = (account_t *)data;
940       
941        irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s",
942                acc->user, acc->prpl->name);
943}
944
945
946#else /* WITH_OTR undefined */
947
948void cmd_otr(irc_t *irc, char **args)
949{
950        irc_usermsg(irc, "otr: n/a, compiled without OTR support");
951}
952
953#endif
Note: See TracBrowser for help on using the repository browser.