source: otr.c @ f55cfe9

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