source: otr.c @ 52e6e17

Last change on this file since 52e6e17 was 52e6e17, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-10T13:36:43Z

Support halfops for 'notaway' status etc.

  • Property mode set to 100644
File size: 32.3 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 *vb = set_getstr(&irc->set, "voice_buddies");
985        char *hb = set_getstr(&irc->set, "halfop_buddies");
986        char *ob = set_getstr(&irc->set, "op_buddies");
987        int encrypted = u->encrypted;
988        int trusted = u->encrypted > 1;
989        char flags[7];
990        int nflags;
991        char *p = flags;
992        int i;
993       
994        if(!strcmp(vb, "encrypted")) {
995                *(p++) = encrypted ? '+' : '-';
996                *(p++) = 'v';
997                nflags++;
998        } else if(!strcmp(vb, "trusted")) {
999                *(p++) = trusted ? '+' : '-';
1000                *(p++) = 'v';
1001                nflags++;
1002        }
1003        if(!strcmp(hb, "encrypted")) {
1004                *(p++) = encrypted ? '+' : '-';
1005                *(p++) = 'h';
1006                nflags++;
1007        } else if(!strcmp(hb, "trusted")) {
1008                *(p++) = trusted ? '+' : '-';
1009                *(p++) = 'h';
1010                nflags++;
1011        }
1012        if(!strcmp(ob, "encrypted")) {
1013                *(p++) = encrypted ? '+' : '-';
1014                *(p++) = 'o';
1015                nflags++;
1016        } else if(!strcmp(ob, "trusted")) {
1017                *(p++) = trusted ? '+' : '-';
1018                *(p++) = 'o';
1019                nflags++;
1020        }
1021        *p = '\0';
1022       
1023        p = g_malloc(nflags * (strlen(u->nick)+1) + 1);
1024        *p = '\0';
1025        if(!p)
1026                return 0;
1027        for(i=0; i<nflags; i++) {
1028                strcat(p, " ");
1029                strcat(p, u->nick);
1030        }
1031        irc_write( irc, ":%s!%s@%s MODE %s %s%s", irc->mynick, irc->mynick, irc->myhost,
1032                irc->channel, flags, p );
1033        g_free(p);
1034               
1035        return 1;
1036}
1037
1038void show_fingerprints(irc_t *irc, ConnContext *ctx)
1039{
1040        char human[45];
1041        Fingerprint *fp;
1042        const char *trust;
1043        int count=0;
1044       
1045        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1046                if(!fp->fingerprint)
1047                        continue;
1048                count++;
1049                otrl_privkey_hash_to_human(human, fp->fingerprint);
1050                if(!fp->trust || fp->trust[0] == '\0') {
1051                        trust="untrusted";
1052                } else {
1053                        trust=fp->trust;
1054                }
1055                if(fp == ctx->active_fingerprint) {
1056                        irc_usermsg(irc, "\x02%s (%s)\x02", human, trust);
1057                } else {
1058                        irc_usermsg(irc, "%s (%s)", human, trust);
1059                }
1060        }
1061        if(count==0)
1062                irc_usermsg(irc, "no fingerprints");
1063}
1064
1065void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
1066{
1067        GError *err;
1068        GThread *thr;
1069        struct kgdata *kg;
1070        gint ev;
1071       
1072        irc_usermsg(irc, "generating new otr privkey for %s/%s...",
1073                handle, protocol);
1074       
1075        kg = g_new0(struct kgdata, 1);
1076        if(!kg) {
1077                irc_usermsg(irc, "otr keygen failed: out of memory");
1078                return;
1079        }
1080
1081        /* Assemble the job description to be passed to thread and handler */
1082        kg->irc = irc;
1083        kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick);
1084        if(!kg->keyfile) {
1085                irc_usermsg(irc, "otr keygen failed: out of memory");
1086                g_free(kg);
1087                return;
1088        }
1089        kg->handle = handle;
1090        kg->protocol = protocol;
1091        kg->mutex = g_mutex_new();
1092        if(!kg->mutex) {
1093                irc_usermsg(irc, "otr keygen failed: couldn't create mutex");
1094                g_free(kg->keyfile);
1095                g_free(kg);
1096                return;
1097        }
1098        kg->done = FALSE;
1099
1100        /* Poll for completion of the thread periodically. I would have preferred
1101           to just wait on a pipe but this way it's portable to Windows. *sigh*
1102        */
1103        ev = b_timeout_add(1000, &keygen_finish_handler, kg);
1104        if(!ev) {
1105                irc_usermsg(irc, "otr keygen failed: couldn't register timeout");
1106                g_free(kg->keyfile);
1107                g_mutex_free(kg->mutex);
1108                g_free(kg);
1109                return;
1110        }
1111
1112        thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err);
1113        if(!thr) {
1114                irc_usermsg(irc, "otr keygen failed: %s", err->message);
1115                g_free(kg->keyfile);
1116                g_mutex_free(kg->mutex);
1117                g_free(kg);
1118                b_event_remove(ev);
1119        }
1120}
1121
1122gpointer otr_keygen_thread_func(gpointer data)
1123{
1124        struct kgdata *kg = (struct kgdata *)data;
1125       
1126        /* lock OTR subsystem and do the work */
1127        g_mutex_lock(kg->irc->otr_mutex);
1128        kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle,
1129                kg->protocol);
1130        g_mutex_unlock(kg->irc->otr_mutex);
1131        /* OTR enabled again */
1132       
1133        /* notify mainloop */
1134        g_mutex_lock(kg->mutex);
1135        kg->done = TRUE;
1136        g_mutex_unlock(kg->mutex);
1137       
1138        return NULL;
1139}
1140
1141gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
1142{
1143        struct kgdata *kg = (struct kgdata *)data;
1144        int done;
1145       
1146        g_mutex_lock(kg->mutex);
1147        done = kg->done;
1148        g_mutex_unlock(kg->mutex);
1149        if(kg->done) {
1150                if(kg->result) {
1151                        irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result));
1152                } else {
1153                        irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol);
1154                }
1155                g_free(kg->keyfile);
1156                g_mutex_free(kg->mutex);
1157                g_free(kg);
1158                return FALSE; /* unregister timeout */
1159        }
1160
1161        return TRUE;  /* still working, continue checking */
1162}
1163
1164void yes_keygen(gpointer w, void *data)
1165{
1166        account_t *acc = (account_t *)data;
1167       
1168        otr_keygen(acc->irc, acc->user, acc->prpl->name);
1169}
1170
1171void no_keygen(gpointer w, void *data)
1172{
1173        account_t *acc = (account_t *)data;
1174       
1175        irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s",
1176                acc->user, acc->prpl->name);
1177        /* TODO:
1178        irc_usermsg(acc->irc, "setting otr policy for %s/%s to \"never\"",
1179                acc->user, acc->prpl->name);
1180        set_setstr(acc->set, "otr_policy", "never");
1181        */
1182}
1183
1184
1185#else /* WITH_OTR undefined */
1186
1187void cmd_otr(irc_t *irc, char **args)
1188{
1189        irc_usermsg(irc, "otr: n/a, compiled without OTR support");
1190}
1191
1192#endif
Note: See TracBrowser for help on using the repository browser.