source: otr.c @ 5d62040

Last change on this file since 5d62040 was 5d62040, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-10T21:12:13Z

remove cleartext fallbacks during keygen

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