source: otr.c @ a13855a

Last change on this file since a13855a was a13855a, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-06T16:42:23Z

use peernicks and try to guess max message size

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