source: otr.c @ 8521b02

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