source: otr.c @ 6c91e6e

Last change on this file since 6c91e6e was 6c91e6e, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-15T00:45:21Z

otr_load error handling + stonedcoder copyright notice

  • Property mode set to 100644
File size: 43.2 KB
Line 
1  /********************************************************************\
2  * BitlBee -- An IRC to other IM-networks gateway                     *
3  *                                                                    *
4  * Copyright 2002-2008 Wilmer van der Gaast and others                *
5  \********************************************************************/
6
7/*
8  OTR support (cf. http://www.cypherpunks.ca/otr/)
9 
10  2008, Sven Moritz Hallberg <pesco@khjk.org>
11  (c) and funded by stonedcoder.org
12   
13  files used to store OTR data:
14    <configdir>/<nick>.otr_keys
15    <configdir>/<nick>.otr_fprints
16   
17  top-level todos: (search for TODO for more ;-))
18    integrate otr_load/otr_save with existing storage backends
19    per-account policy settings
20    per-user policy settings
21*/
22
23/*
24  This program is free software; you can redistribute it and/or modify
25  it under the terms of the GNU General Public License as published by
26  the Free Software Foundation; either version 2 of the License, or
27  (at your option) any later version.
28
29  This program is distributed in the hope that it will be useful,
30  but WITHOUT ANY WARRANTY; without even the implied warranty of
31  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
32  GNU General Public License for more details.
33
34  You should have received a copy of the GNU General Public License with
35  the Debian GNU/Linux distribution in /usr/share/common-licenses/GPL;
36  if not, write to the Free Software Foundation, Inc., 59 Temple Place,
37  Suite 330, Boston, MA  02111-1307  USA
38*/
39
40#include "bitlbee.h"
41#ifdef WITH_OTR
42#include "irc.h"
43#include "otr.h"
44#include <sys/types.h>
45#include <unistd.h>
46
47
48/** OTR interface routines for the OtrlMessageAppOps struct: **/
49
50OtrlPolicy op_policy(void *opdata, ConnContext *context);
51
52void op_create_privkey(void *opdata, const char *accountname, const char *protocol);
53
54int op_is_logged_in(void *opdata, const char *accountname, const char *protocol,
55        const char *recipient);
56
57void op_inject_message(void *opdata, const char *accountname, const char *protocol,
58        const char *recipient, const char *message);
59
60int op_display_otr_message(void *opdata, const char *accountname, const char *protocol,
61        const char *username, const char *msg);
62
63void op_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
64        const char *protocol, const char *username, unsigned char fingerprint[20]);
65
66void op_write_fingerprints(void *opdata);
67
68void op_gone_secure(void *opdata, ConnContext *context);
69
70void op_gone_insecure(void *opdata, ConnContext *context);
71
72void op_still_secure(void *opdata, ConnContext *context, int is_reply);
73
74void op_log_message(void *opdata, const char *message);
75
76int op_max_message_size(void *opdata, ConnContext *context);
77
78const char *op_account_name(void *opdata, const char *account, const char *protocol);
79
80
81/** otr sub-command handlers: **/
82
83void cmd_otr_connect(irc_t *irc, char **args);
84void cmd_otr_disconnect(irc_t *irc, char **args);
85void cmd_otr_smp(irc_t *irc, char **args);
86void cmd_otr_trust(irc_t *irc, char **args);
87void cmd_otr_info(irc_t *irc, char **args);
88void cmd_otr_keygen(irc_t *irc, char **args);
89void cmd_otr_forget(irc_t *irc, char **args);
90
91const command_t otr_commands[] = {
92        { "connect",     1, &cmd_otr_connect,    0 },
93        { "disconnect",  1, &cmd_otr_disconnect, 0 },
94        { "smp",         2, &cmd_otr_smp,        0 },
95        { "trust",       6, &cmd_otr_trust,      0 },
96        { "info",        0, &cmd_otr_info,       0 },
97        { "keygen",      1, &cmd_otr_keygen,     0 },
98        { "forget",      2, &cmd_otr_forget,     0 },
99        { NULL }
100};
101
102
103/** misc. helpers/subroutines: **/
104
105/* start background thread to generate a (new) key for a given account */
106void otr_keygen(irc_t *irc, const char *handle, const char *protocol);
107
108/* keygen thread main func */
109gpointer otr_keygen_thread_func(gpointer data);
110
111/* mainloop handler for when keygen thread finishes */
112gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond);
113
114/* data to be passed to otr_keygen_thread_func */
115struct kgdata {
116        irc_t *irc;            /* access to OTR userstate */
117        char *keyfile;         /* free me! */
118        const char *handle;      /* don't free! */
119        const char *protocol;    /* don't free! */
120        GMutex *mutex;         /* lock for the 'done' flag, free me! */
121        int done;              /* is the thread done? */
122        gcry_error_t result;   /* return value of otrl_privkey_generate */
123};
124
125/* some yes/no handlers */
126void yes_keygen(gpointer w, void *data);
127void yes_forget_fingerprint(gpointer w, void *data);
128void yes_forget_context(gpointer w, void *data);
129void yes_forget_key(gpointer w, void *data);
130
131/* helper to make sure accountname and protocol match the incoming "opdata" */
132struct im_connection *check_imc(void *opdata, const char *accountname,
133        const char *protocol);
134
135/* determine the nick for a given handle/protocol pair
136   returns "handle/protocol" if not found */
137const char *peernick(irc_t *irc, const char *handle, const char *protocol);
138
139/* turn a hexadecimal digit into its numerical value */
140int hexval(char a);
141
142/* determine the user_t for a given handle/protocol pair
143   returns NULL if not found */
144user_t *peeruser(irc_t *irc, const char *handle, const char *protocol);
145
146/* handle SMP TLVs from a received message */
147void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs);
148
149/* update op/voice flag of given user according to encryption state and settings
150   returns 0 if neither op_buddies nor voice_buddies is set to "encrypted",
151   i.e. msgstate should be announced seperately */
152int otr_update_modeflags(irc_t *irc, user_t *u);
153
154/* show general info about the OTR subsystem; called by 'otr info' */
155void show_general_otr_info(irc_t *irc);
156
157/* show info about a given OTR context */
158void show_otr_context_info(irc_t *irc, ConnContext *ctx);
159
160/* show the list of fingerprints associated with a given context */
161void show_fingerprints(irc_t *irc, ConnContext *ctx);
162
163/* find a fingerprint by prefix (given as any number of hex strings) */
164Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args);
165
166/* find a private key by fingerprint prefix (given as any number of hex strings) */
167OtrlPrivKey *match_privkey(irc_t *irc, const char **args);
168
169
170/*** routines declared in otr.h: ***/
171
172void otr_init(void)
173{
174        if(!g_thread_supported()) g_thread_init(NULL);
175        OTRL_INIT;
176       
177        /* fill global OtrlMessageAppOps */
178        global.otr_ops.policy = &op_policy;
179        global.otr_ops.create_privkey = &op_create_privkey;
180        global.otr_ops.is_logged_in = &op_is_logged_in;
181        global.otr_ops.inject_message = &op_inject_message;
182        global.otr_ops.notify = NULL;
183        global.otr_ops.display_otr_message = &op_display_otr_message;
184        global.otr_ops.update_context_list = NULL;
185        global.otr_ops.protocol_name = NULL;
186        global.otr_ops.protocol_name_free = NULL;
187        global.otr_ops.new_fingerprint = &op_new_fingerprint;
188        global.otr_ops.write_fingerprints = &op_write_fingerprints;
189        global.otr_ops.gone_secure = &op_gone_secure;
190        global.otr_ops.gone_insecure = &op_gone_insecure;
191        global.otr_ops.still_secure = &op_still_secure;
192        global.otr_ops.log_message = &op_log_message;
193        global.otr_ops.max_message_size = &op_max_message_size;
194        global.otr_ops.account_name = &op_account_name;
195        global.otr_ops.account_name_free = NULL;
196}
197
198/* Notice on the otr_mutex:
199
200   The incoming/outgoing message handlers try to lock the otr_mutex. If they succeed,
201   this will prevent a concurrent keygen (possibly spawned by that very command)
202   from messing up the userstate. If the lock fails, that means there already is
203   a keygen in progress. Instead of blocking for an unknown time, they
204   will bail out gracefully, informing the user of this temporary "coma".
205   TODO: Hold back incoming/outgoing messages and process them when keygen completes?
206
207   The other routines do not lock the otr_mutex themselves, it is done as a
208   catch-all in the root command handler. Rationale:
209     a) it's easy to code
210     b) it makes it obvious that no command can get its userstate corrupted
211     c) the "irc" struct is readily available there for feedback to the user
212 */
213
214void otr_load(irc_t *irc)
215{
216        char s[512];
217        account_t *a;
218        gcry_error_t e;
219        int eno;
220
221        log_message(LOGLVL_DEBUG, "otr_load '%s'", irc->nick);
222
223        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->nick);
224        e = otrl_privkey_read(irc->otr_us, s);
225        eno = gcry_error_code_to_errno(e);
226        if(e && eno!=ENOENT) {
227                log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e));
228        }
229        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick);
230        e = otrl_privkey_read_fingerprints(irc->otr_us, s, NULL, NULL);
231        eno = gcry_error_code_to_errno(e);
232        if(e && eno!=ENOENT) {
233                log_message(LOGLVL_ERROR, "otr load: %s: %s", s, strerror(e));
234        }
235       
236        /* check for otr keys on all accounts */
237        for(a=irc->accounts; a; a=a->next) {
238                otr_check_for_key(a);
239        }
240}
241
242void otr_save(irc_t *irc)
243{
244        char s[512];
245        gcry_error_t e;
246
247        log_message(LOGLVL_DEBUG, "otr_save '%s'", irc->nick);
248
249        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->nick);
250        e = otrl_privkey_write_fingerprints(irc->otr_us, s);
251        if(e) {
252                log_message(LOGLVL_ERROR, "otr save: %s: %s", s, strerror(e));
253        }
254        chmod(s, 0600);
255}
256
257void otr_remove(const char *nick)
258{
259        char s[512];
260       
261        log_message(LOGLVL_DEBUG, "otr_remove '%s'", nick);
262
263        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick);
264        unlink(s);
265        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick);
266        unlink(s);
267}
268
269void otr_rename(const char *onick, const char *nnick)
270{
271        char s[512], t[512];
272       
273        log_message(LOGLVL_DEBUG, "otr_rename '%s' -> '%s'", onick, nnick);
274
275        g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick);
276        g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick);
277        rename(s,t);
278        g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick);
279        g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick);
280        rename(s,t);
281}
282
283void otr_check_for_key(account_t *a)
284{
285        irc_t *irc = a->irc;
286        OtrlPrivKey *k;
287       
288        k = otrl_privkey_find(irc->otr_us, a->user, a->prpl->name);
289        if(k) {
290                irc_usermsg(irc, "otr: %s/%s ready",
291                        a->user, a->prpl->name);
292        } else {
293                otr_keygen(irc, a->user, a->prpl->name);
294        }
295}
296
297char *otr_handle_message(struct im_connection *ic, const char *handle, const char *msg)
298{
299        int ignore_msg;
300        char *newmsg = NULL;
301        OtrlTLV *tlvs = NULL;
302        char *colormsg;
303       
304    if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) {
305                user_t *u = user_findhandle(ic, handle);
306               
307                /* fallback for non-otr clients */
308                if(u && !u->encrypted) {
309                        return g_strdup(msg);
310                }
311               
312                irc_usermsg(ic->irc, "encrypted msg from %s during keygen - dropped",
313                        peernick(ic->irc, handle, ic->acc->prpl->name));
314                return NULL;
315        }
316
317        ignore_msg = otrl_message_receiving(ic->irc->otr_us, &global.otr_ops, ic,
318                ic->acc->user, ic->acc->prpl->name, handle, msg, &newmsg,
319                &tlvs, NULL, NULL);
320
321        otr_handle_smp(ic, handle, tlvs);
322       
323        if(ignore_msg) {
324                /* this was an internal OTR protocol message */
325                g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
326                return NULL;
327        } else if(!newmsg) {
328                /* this was a non-OTR message */
329                g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
330                return g_strdup(msg);
331        } else {
332                /* OTR has processed this message */
333                ConnContext *context = otrl_context_find(ic->irc->otr_us, handle,
334                        ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL);
335                if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
336                   set_getbool(&ic->irc->set, "color_encrypted")) {
337                        /* color according to f'print trust */
338                        char color;
339                        const char *trust = context->active_fingerprint->trust;
340                        if(trust && trust[0] != '\0')
341                                color='3';   /* green */
342                        else
343                                color='5';   /* red */
344                        colormsg = g_strdup_printf("\x03%c%s\x0F", color, newmsg);
345                } else {
346                        colormsg = g_strdup(newmsg);
347                }
348                otrl_message_free(newmsg);
349                g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
350                return colormsg;
351        }
352}
353
354int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags)
355{       
356        int st;
357        char *otrmsg = NULL;
358        ConnContext *ctx = NULL;
359       
360    if(!g_static_rec_mutex_trylock(&ic->irc->otr_mutex)) {
361                user_t *u = user_findhandle(ic, handle);
362               
363                /* Fallback for non-otr clients.
364                   Yes, this better shouldn't send private stuff in the clear... */
365                if(u && !u->encrypted) {
366                        return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags);
367                }
368               
369                irc_usermsg(ic->irc, "encrypted message to %s during keygen - not sent",
370                        peernick(ic->irc, handle, ic->acc->prpl->name));
371                return 1;
372    }
373   
374        st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic,
375                ic->acc->user, ic->acc->prpl->name, handle,
376                msg, NULL, &otrmsg, NULL, NULL);
377        if(st) {
378                g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
379                return st;
380        }
381
382        ctx = otrl_context_find(ic->irc->otr_us,
383                        handle, ic->acc->user, ic->acc->prpl->name,
384                        1, NULL, NULL, NULL);
385
386        if(otrmsg) {
387                if(!ctx) {
388                        otrl_message_free(otrmsg);
389                        g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
390                        return 1;
391                }
392                st = otrl_message_fragment_and_send(&global.otr_ops, ic, ctx,
393                        otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
394                otrl_message_free(otrmsg);
395        } else {
396                /* note: otrl_message_sending handles policy, so that if REQUIRE_ENCRYPTION is set,
397                   this case does not occur */
398                st = ic->acc->prpl->buddy_msg( ic, (char *)handle, (char *)msg, flags );
399        }
400       
401        g_static_rec_mutex_unlock(&ic->irc->otr_mutex);
402        return st;
403}
404
405void cmd_otr(irc_t *irc, char **args)
406{
407        const command_t *cmd;
408       
409        if(!args[0])
410                return;
411       
412        if(!args[1])
413                return;
414       
415        for(cmd=otr_commands; cmd->command; cmd++) {
416                if(strcmp(cmd->command, args[1]) == 0)
417                        break;
418        }
419       
420        if(!cmd->command) {
421                irc_usermsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02",
422                        args[0], args[1]);
423                return;
424        }
425       
426        if(!args[cmd->required_parameters+1]) {
427                irc_usermsg(irc, "%s %s: not enough arguments (%d req.)",
428                        args[0], args[1], cmd->required_parameters);
429                return;
430        }
431       
432        cmd->execute(irc, args+1);
433}
434
435
436/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/
437
438OtrlPolicy op_policy(void *opdata, ConnContext *context)
439{
440        struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol);
441        const char *p;
442
443        p = set_getstr(&ic->irc->set, "otr_policy");
444        if(!strcmp(p, "never"))
445                return OTRL_POLICY_NEVER;
446        if(!strcmp(p, "opportunistic"))
447                return OTRL_POLICY_OPPORTUNISTIC;
448        if(!strcmp(p, "manual"))
449                return OTRL_POLICY_MANUAL;
450        if(!strcmp(p, "always"))
451                return OTRL_POLICY_ALWAYS;
452       
453        return OTRL_POLICY_OPPORTUNISTIC;
454}
455
456void op_create_privkey(void *opdata, const char *accountname,
457        const char *protocol)
458{
459        struct im_connection *ic = check_imc(opdata, accountname, protocol);
460        char *s;
461       
462        log_message(LOGLVL_DEBUG, "op_create_privkey '%s' '%s'", accountname, protocol);
463
464        s = g_strdup_printf("oops, no otr privkey for %s - generate one now?",
465                accountname);
466        query_add(ic->irc, ic, s, yes_keygen, NULL, ic->acc);
467}
468
469int op_is_logged_in(void *opdata, const char *accountname,
470        const char *protocol, const char *recipient)
471{
472        struct im_connection *ic = check_imc(opdata, accountname, protocol);
473        user_t *u;
474
475        log_message(LOGLVL_DEBUG, "op_is_logged_in '%s' '%s' '%s'", accountname, protocol, recipient);
476       
477        /* lookup the user_t for the given recipient */
478        u = user_findhandle(ic, recipient);
479        if(u) {
480                if(u->online)
481                        return 1;
482                else
483                        return 0;
484        } else {
485                return -1;
486        }
487}
488
489void op_inject_message(void *opdata, const char *accountname,
490        const char *protocol, const char *recipient, const char *message)
491{
492        struct im_connection *ic = check_imc(opdata, accountname, protocol);
493
494        log_message(LOGLVL_DEBUG, "op_inject_message '%s' '%s' '%s' '%s'", accountname, protocol, recipient, message);
495
496        if (strcmp(accountname, recipient) == 0) {
497                /* huh? injecting messages to myself? */
498                irc_usermsg(ic->irc, "note to self: %s", message);
499        } else {
500                /* need to drop some consts here :-( */
501                /* TODO: get flags into op_inject_message?! */
502                ic->acc->prpl->buddy_msg(ic, (char *)recipient, (char *)message, 0);
503                /* ignoring return value :-/ */
504        }
505}
506
507int op_display_otr_message(void *opdata, const char *accountname,
508        const char *protocol, const char *username, const char *message)
509{
510        struct im_connection *ic = check_imc(opdata, accountname, protocol);
511        char *msg = g_strdup(message);
512
513        log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, message);
514
515        strip_html(msg);
516        irc_usermsg(ic->irc, "otr: %s", msg);
517
518        g_free(msg);
519        return 0;
520}
521
522void op_new_fingerprint(void *opdata, OtrlUserState us,
523        const char *accountname, const char *protocol,
524        const char *username, unsigned char fingerprint[20])
525{
526        struct im_connection *ic = check_imc(opdata, accountname, protocol);
527        char hunam[45];         /* anybody looking? ;-) */
528       
529        otrl_privkey_hash_to_human(hunam, fingerprint);
530        log_message(LOGLVL_DEBUG, "op_new_fingerprint '%s' '%s' '%s' '%s'", accountname, protocol, username, hunam);
531
532        irc_usermsg(ic->irc, "new fingerprint for %s: %s",
533                peernick(ic->irc, username, protocol), hunam);
534}
535
536void op_write_fingerprints(void *opdata)
537{
538        struct im_connection *ic = (struct im_connection *)opdata;
539
540        log_message(LOGLVL_DEBUG, "op_write_fingerprints");
541
542        otr_save(ic->irc);
543}
544
545void op_gone_secure(void *opdata, ConnContext *context)
546{
547        struct im_connection *ic =
548                check_imc(opdata, context->accountname, context->protocol);
549        user_t *u;
550        const char *trust;
551
552        log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
553
554        u = peeruser(ic->irc, context->username, context->protocol);
555        if(!u) {
556                log_message(LOGLVL_ERROR,
557                        "BUG: otr.c: op_gone_secure: user_t for %s/%s/%s not found!",
558                        context->username, context->protocol, context->accountname);
559                return;
560        }
561       
562        trust = context->active_fingerprint->trust;
563        if(trust && trust[0])
564                u->encrypted = 2;
565        else
566                u->encrypted = 1;
567        if(!otr_update_modeflags(ic->irc, u))
568                irc_usermsg(ic->irc, "conversation with %s is now off the record", u->nick);
569}
570
571void op_gone_insecure(void *opdata, ConnContext *context)
572{
573        struct im_connection *ic =
574                check_imc(opdata, context->accountname, context->protocol);
575        user_t *u;
576
577        log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
578
579        u = peeruser(ic->irc, context->username, context->protocol);
580        if(!u) {
581                log_message(LOGLVL_ERROR,
582                        "BUG: otr.c: op_gone_insecure: user_t for %s/%s/%s not found!",
583                        context->username, context->protocol, context->accountname);
584                return;
585        }
586        u->encrypted = 0;
587        if(!otr_update_modeflags(ic->irc, u))
588                irc_usermsg(ic->irc, "conversation with %s is now in the clear", u->nick);
589}
590
591void op_still_secure(void *opdata, ConnContext *context, int is_reply)
592{
593        struct im_connection *ic =
594                check_imc(opdata, context->accountname, context->protocol);
595        user_t *u;
596
597        log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d",
598                context->accountname, context->protocol, context->username, is_reply);
599
600        u = peeruser(ic->irc, context->username, context->protocol);
601        if(!u) {
602                log_message(LOGLVL_ERROR,
603                        "BUG: otr.c: op_still_secure: user_t for %s/%s/%s not found!",
604                        context->username, context->protocol, context->accountname);
605                return;
606        }
607        if(context->active_fingerprint->trust[0])
608                u->encrypted = 2;
609        else
610                u->encrypted = 1;
611        if(!otr_update_modeflags(ic->irc, u))
612                irc_usermsg(ic->irc, "otr connection with %s has been refreshed", u->nick);
613}
614
615void op_log_message(void *opdata, const char *message)
616{
617        char *msg = g_strdup(message);
618       
619        strip_html(msg);
620        log_message(LOGLVL_INFO, "otr: %s", msg);
621        g_free(msg);
622}
623
624int op_max_message_size(void *opdata, ConnContext *context)
625{
626        struct im_connection *ic =
627                check_imc(opdata, context->accountname, context->protocol);
628
629        return ic->acc->prpl->mms;
630}
631
632const char *op_account_name(void *opdata, const char *account, const char *protocol)
633{
634        struct im_connection *ic = (struct im_connection *)opdata;
635
636        log_message(LOGLVL_DEBUG, "op_account_name '%s' '%s'", account, protocol);
637       
638        return peernick(ic->irc, account, protocol);
639}
640
641
642/*** OTR sub-command handlers ***/
643
644void cmd_otr_disconnect(irc_t *irc, char **args)
645{
646        user_t *u;
647
648        u = user_find(irc, args[1]);
649        if(!u || !u->ic) {
650                irc_usermsg(irc, "%s: unknown user", args[1]);
651                return;
652        }
653       
654        otrl_message_disconnect(irc->otr_us, &global.otr_ops,
655                u->ic, u->ic->acc->user, u->ic->acc->prpl->name, u->handle);
656       
657        /* for some reason, libotr (3.1.0) doesn't do this itself: */
658        if(u->encrypted) {
659                ConnContext *ctx;
660                ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
661                        u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
662                if(ctx)
663                        op_gone_insecure(u->ic, ctx);
664                else /* huh? */
665                        u->encrypted = 0;
666        }
667}
668
669void cmd_otr_connect(irc_t *irc, char **args)
670{
671        user_t *u;
672
673        u = user_find(irc, args[1]);
674        if(!u || !u->ic) {
675                irc_usermsg(irc, "%s: unknown user", args[1]);
676                return;
677        }
678        if(!u->online) {
679                irc_usermsg(irc, "%s is offline", args[1]);
680                return;
681        }
682       
683        imc_buddy_msg(u->ic, u->handle, "?OTR?", 0);
684}
685
686void cmd_otr_smp(irc_t *irc, char **args)
687{
688        user_t *u;
689        ConnContext *ctx;
690       
691        u = user_find(irc, args[1]);
692        if(!u || !u->ic) {
693                irc_usermsg(irc, "%s: unknown user", args[1]);
694                return;
695        }
696        if(!u->online) {
697                irc_usermsg(irc, "%s is offline", args[1]);
698                return;
699        }
700       
701        ctx = otrl_context_find(irc->otr_us, u->handle,
702                u->ic->acc->user, u->ic->acc->prpl->name, 1, NULL, NULL, NULL);
703        if(!ctx) {
704                /* huh? out of memory or what? */
705                return;
706        }
707
708        if(ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) {
709                log_message(LOGLVL_INFO,
710                        "SMP already in phase %d, sending abort before reinitiating",
711                        ctx->smstate->nextExpected+1);
712                otrl_message_abort_smp(irc->otr_us, &global.otr_ops, u->ic, ctx);
713                otrl_sm_state_free(ctx->smstate);
714        }
715       
716        /* warning: the following assumes that smstates are cleared whenever an SMP
717           is completed or aborted! */ 
718        if(ctx->smstate->secret == NULL) {
719                irc_usermsg(irc, "smp: initiating with %s...", u->nick);
720                otrl_message_initiate_smp(irc->otr_us, &global.otr_ops,
721                        u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
722                /* smp is now in EXPECT2 */
723        } else {
724                /* if we're still in EXPECT1 but smstate is initialized, we must have
725                   received the SMP1, so let's issue a response */
726                irc_usermsg(irc, "smp: responding to %s...", u->nick);
727                otrl_message_respond_smp(irc->otr_us, &global.otr_ops,
728                        u->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
729                /* smp is now in EXPECT3 */
730        }
731}
732
733void cmd_otr_trust(irc_t *irc, char **args)
734{
735        user_t *u;
736        ConnContext *ctx;
737        unsigned char raw[20];
738        Fingerprint *fp;
739        int i,j;
740       
741        u = user_find(irc, args[1]);
742        if(!u || !u->ic) {
743                irc_usermsg(irc, "%s: unknown user", args[1]);
744                return;
745        }
746       
747        ctx = otrl_context_find(irc->otr_us, u->handle,
748                u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
749        if(!ctx) {
750                irc_usermsg(irc, "%s: no otr context with user", args[1]);
751                return;
752        }
753       
754        /* convert given fingerprint to raw representation */
755        for(i=0; i<5; i++) {
756                for(j=0; j<4; j++) {
757                        char *p = args[2+i]+(2*j);
758                        char *q = p+1;
759                        int x, y;
760                       
761                        if(!*p || !*q) {
762                                irc_usermsg(irc, "failed: truncated fingerprint block %d", i+1);
763                                return;
764                        }
765                       
766                        x = hexval(*p);
767                        y = hexval(*q);
768                        if(x<0) {
769                                irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+1, i+1);
770                                return;
771                        }
772                        if(y<0) {
773                                irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+2, i+1);
774                                return;
775                        }
776
777                        raw[i*4+j] = x*16 + y;
778                }
779        }
780        fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL);
781        if(!fp) {
782                irc_usermsg(irc, "failed: no such fingerprint for %s", args[1]);
783        } else {
784                char *trust = args[7] ? args[7] : "affirmed";
785                otrl_context_set_trust(fp, trust);
786                irc_usermsg(irc, "fingerprint match, trust set to \"%s\"", trust);
787                if(u->encrypted)
788                        u->encrypted = 2;
789                otr_update_modeflags(irc, u);
790        }
791}
792
793void cmd_otr_info(irc_t *irc, char **args)
794{
795        if(!args[1]) {
796                show_general_otr_info(irc);
797        } else {
798                char *arg = g_strdup(args[1]);
799                char *myhandle, *handle, *protocol;
800                ConnContext *ctx;
801               
802                /* interpret arg as 'user/protocol/account' if possible */
803                protocol = strchr(arg, '/');
804                if(protocol) {
805                        *(protocol++) = '\0';
806                        myhandle = strchr(protocol, '/');
807                }
808                if(protocol && myhandle) {
809                        *(myhandle++) = '\0';
810                        handle = arg;
811                        ctx = otrl_context_find(irc->otr_us, handle, myhandle, protocol, 0, NULL, NULL, NULL);
812                        if(!ctx) {
813                                irc_usermsg(irc, "no such context");
814                                g_free(arg);
815                                return;
816                        }
817                } else {
818                        user_t *u = user_find(irc, args[1]);
819                        if(!u || !u->ic) {
820                                irc_usermsg(irc, "%s: unknown user", args[1]);
821                                g_free(arg);
822                                return;
823                        }
824                        ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
825                                u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
826                        if(!ctx) {
827                                irc_usermsg(irc, "no otr context with %s", args[1]);
828                                g_free(arg);
829                                return;
830                        }
831                }
832       
833                /* show how we resolved the (nick) argument, if we did */
834                if(handle!=arg) {
835                        irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1],
836                                ctx->username, ctx->protocol, ctx->accountname, ctx->protocol);
837                }
838                show_otr_context_info(irc, ctx);
839                g_free(arg);
840        }
841}
842
843void cmd_otr_keygen(irc_t *irc, char **args)
844{
845        int i, n;
846        account_t *a;
847       
848        n = atoi(args[1]);
849        if(n<0 || (!n && strcmp(args[1], "0"))) {
850                irc_usermsg(irc, "%s: invalid account number", args[1]);
851                return;
852        }
853       
854        a = irc->accounts;
855        for(i=0; i<n && a; i++, a=a->next);
856        if(!a) {
857                irc_usermsg(irc, "%s: no such account", args[1]);
858                return;
859        }
860       
861        if(otrl_privkey_find(irc->otr_us, a->user, a->prpl->name)) {
862                char *s = g_strdup_printf("account %d already has a key, replace it?", n);
863                query_add(irc, NULL, s, yes_keygen, NULL, a);
864        } else {
865                otr_keygen(irc, a->user, a->prpl->name);
866        }
867}
868
869void yes_forget_fingerprint(gpointer w, void *data)
870{
871        irc_t *irc = (irc_t *)w;
872        Fingerprint *fp = (Fingerprint *)data;
873       
874        if(fp == fp->context->active_fingerprint) {
875                irc_usermsg(irc, "that fingerprint is active, terminate otr connection first");
876                return;
877        }
878               
879        otrl_context_forget_fingerprint(fp, 0);
880}
881
882void yes_forget_context(gpointer w, void *data)
883{
884        irc_t *irc = (irc_t *)w;
885        ConnContext *ctx = (ConnContext *)data;
886       
887        if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
888                irc_usermsg(irc, "active otr connection with %s, terminate it first",
889                        peernick(irc, ctx->username, ctx->protocol));
890                return;
891        }
892               
893        if(ctx->msgstate == OTRL_MSGSTATE_FINISHED)
894                otrl_context_force_plaintext(ctx);
895        otrl_context_forget(ctx);
896}
897
898void yes_forget_key(gpointer w, void *data)
899{
900        OtrlPrivKey *key = (OtrlPrivKey *)data;
901       
902        /* FIXME: For some reason which /completely eludes me/, this call keeps
903           barfing on the gcry_sexp_release inside (invalid pointer free). */
904        otrl_privkey_forget(key);
905}
906
907void cmd_otr_forget(irc_t *irc, char **args)
908{
909        if(!strcmp(args[1], "fingerprint"))
910        {
911                user_t *u;
912                ConnContext *ctx;
913                Fingerprint *fp;
914                char human[54];
915                char *s;
916               
917                if(!args[3]) {
918                        irc_usermsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]);
919                        return;
920                }
921               
922                /* TODO: allow context specs ("user/proto/account") in 'otr forget fingerprint'? */
923                u = user_find(irc, args[2]);
924                if(!u || !u->ic) {
925                        irc_usermsg(irc, "%s: unknown user", args[2]);
926                        return;
927                }
928               
929                ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
930                        u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
931                if(!ctx) {
932                        irc_usermsg(irc, "no otr context with %s", args[2]);
933                        return;
934                }
935               
936                fp = match_fingerprint(irc, ctx, ((const char **)args)+3);
937                if(!fp) {
938                        /* match_fingerprint does error messages */
939                        return;
940                }
941               
942                if(fp == ctx->active_fingerprint) {
943                        irc_usermsg(irc, "that fingerprint is active, terminate otr connection first");
944                        return;
945                }
946               
947                otrl_privkey_hash_to_human(human, fp->fingerprint);
948                s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human);
949                query_add(irc, NULL, s, yes_forget_fingerprint, NULL, fp);
950        }
951       
952        else if(!strcmp(args[1], "context"))
953        {
954                user_t *u;
955                ConnContext *ctx;
956                char *s;
957               
958                /* TODO: allow context specs ("user/proto/account") in 'otr forget contex'? */
959                u = user_find(irc, args[2]);
960                if(!u || !u->ic) {
961                        irc_usermsg(irc, "%s: unknown user", args[2]);
962                        return;
963                }
964               
965                ctx = otrl_context_find(irc->otr_us, u->handle, u->ic->acc->user,
966                        u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
967                if(!ctx) {
968                        irc_usermsg(irc, "no otr context with %s", args[2]);
969                        return;
970                }
971               
972                if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
973                        irc_usermsg(irc, "active otr connection with %s, terminate it first", args[2]);
974                        return;
975                }
976               
977                s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]);
978                query_add(irc, NULL, s, yes_forget_context, NULL, ctx);
979        }
980       
981        else if(!strcmp(args[1], "key"))
982        {
983                OtrlPrivKey *key;
984                char *s;
985               
986                key = match_privkey(irc, ((const char **)args)+2);
987                if(!key) {
988                        /* match_privkey does error messages */
989                        return;
990                }
991               
992                /* TODO: Find out why 'otr forget key' barfs (cf. yes_forget_key) */
993                irc_usermsg(irc, "otr %s %s: not implemented, please edit \x02%s%s.otr_keys\x02 manually :-/",
994                        args[0], args[1], global.conf->configdir, irc->nick);
995                return;
996
997                s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?",
998                        key->accountname, key->protocol);
999                query_add(irc, NULL, s, yes_forget_key, NULL, key);
1000        }
1001       
1002        else
1003        {
1004                irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02",
1005                        args[0], args[1]);
1006        }
1007}
1008
1009
1010/*** local helpers / subroutines: ***/
1011
1012/* Socialist Millionaires' Protocol */
1013void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs)
1014{
1015        irc_t *irc = ic->irc;
1016        OtrlUserState us = irc->otr_us;
1017        OtrlMessageAppOps *ops = &global.otr_ops;
1018        OtrlTLV *tlv = NULL;
1019        ConnContext *context;
1020        NextExpectedSMP nextMsg;
1021        user_t *u;
1022
1023        u = user_findhandle(ic, handle);
1024        if(!u) return;
1025        context = otrl_context_find(us, handle,
1026                ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL);
1027        if(!context) {
1028                /* huh? out of memory or what? */
1029                return;
1030        }
1031        nextMsg = context->smstate->nextExpected;
1032
1033        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
1034        if (tlv) {
1035                if (nextMsg != OTRL_SMP_EXPECT1) {
1036                        irc_usermsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick);
1037                        otrl_message_abort_smp(us, ops, u->ic, context);
1038                        otrl_sm_state_free(context->smstate);
1039                } else {
1040                        irc_usermsg(irc, "smp: initiated by %s"
1041                                " - respond with \x02otr smp %s <secret>\x02",
1042                                u->nick, u->nick);
1043                        /* smp stays in EXPECT1 until user responds */
1044                }
1045        }
1046        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
1047        if (tlv) {
1048                if (nextMsg != OTRL_SMP_EXPECT2) {
1049                        irc_usermsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick);
1050                        otrl_message_abort_smp(us, ops, u->ic, context);
1051                        otrl_sm_state_free(context->smstate);
1052                } else {
1053                        /* SMP2 received, otrl_message_receiving will have sent SMP3 */
1054                        context->smstate->nextExpected = OTRL_SMP_EXPECT4;
1055                }
1056        }
1057        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
1058        if (tlv) {
1059                if (nextMsg != OTRL_SMP_EXPECT3) {
1060                        irc_usermsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick);
1061                        otrl_message_abort_smp(us, ops, u->ic, context);
1062                        otrl_sm_state_free(context->smstate);
1063                } else {
1064                        /* SMP3 received, otrl_message_receiving will have sent SMP4 and set fp trust */
1065                        const char *trust = context->active_fingerprint->trust;
1066                        if(!trust || trust[0]=='\0') {
1067                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1068                                        u->nick);
1069                        } else {
1070                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1071                                        u->nick);
1072                        }
1073                        otrl_sm_state_free(context->smstate);
1074                        /* smp is in back in EXPECT1 */
1075                }
1076        }
1077        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
1078        if (tlv) {
1079                if (nextMsg != OTRL_SMP_EXPECT4) {
1080                        irc_usermsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick);
1081                        otrl_message_abort_smp(us, ops, u->ic, context);
1082                        otrl_sm_state_free(context->smstate);
1083                } else {
1084                        /* SMP4 received, otrl_message_receiving will have set fp trust */
1085                        const char *trust = context->active_fingerprint->trust;
1086                        if(!trust || trust[0]=='\0') {
1087                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1088                                        u->nick);
1089                        } else {
1090                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1091                                        u->nick);
1092                        }
1093                        otrl_sm_state_free(context->smstate);
1094                        /* smp is in back in EXPECT1 */
1095                }
1096        }
1097        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
1098        if (tlv) {
1099                irc_usermsg(irc, "smp: received abort from %s", u->nick);
1100                otrl_sm_state_free(context->smstate);
1101                /* smp is in back in EXPECT1 */
1102        }
1103}
1104
1105/* helper to assert that account and protocol names given to ops below always
1106   match the im_connection passed through as opdata */
1107struct im_connection *check_imc(void *opdata, const char *accountname,
1108        const char *protocol)
1109{
1110        struct im_connection *ic = (struct im_connection *)opdata;
1111
1112        if (strcmp(accountname, ic->acc->user) != 0) {
1113                log_message(LOGLVL_WARNING,
1114                        "otr: internal account name mismatch: '%s' vs '%s'",
1115                        accountname, ic->acc->user);
1116        }
1117        if (strcmp(protocol, ic->acc->prpl->name) != 0) {
1118                log_message(LOGLVL_WARNING,
1119                        "otr: internal protocol name mismatch: '%s' vs '%s'",
1120                        protocol, ic->acc->prpl->name);
1121        }
1122       
1123        return ic;
1124}
1125
1126user_t *peeruser(irc_t *irc, const char *handle, const char *protocol)
1127{
1128        user_t *u;
1129       
1130        log_message(LOGLVL_DEBUG, "peeruser '%s' '%s'", handle, protocol);
1131       
1132        for(u=irc->users; u; u=u->next) {
1133                struct prpl *prpl;
1134                if(!u->ic || !u->handle)
1135                        continue;
1136                prpl = u->ic->acc->prpl;
1137                if(strcmp(prpl->name, protocol) == 0
1138                        && prpl->handle_cmp(u->handle, handle) == 0) {
1139                        return u;
1140                }
1141        }
1142       
1143        return NULL;
1144}
1145
1146int hexval(char a)
1147{
1148        int x=tolower(a);
1149       
1150        if(x>='a' && x<='f')
1151                x = x - 'a' + 10;
1152        else if(x>='0' && x<='9')
1153                x = x - '0';
1154        else
1155                return -1;
1156       
1157        return x;
1158}
1159
1160const char *peernick(irc_t *irc, const char *handle, const char *protocol)
1161{
1162        static char fallback[512];
1163       
1164        user_t *u = peeruser(irc, handle, protocol);
1165        if(u) {
1166                return u->nick;
1167        } else {
1168                g_snprintf(fallback, 511, "%s/%s", handle, protocol);
1169                return fallback;
1170        }
1171}
1172
1173int otr_update_modeflags(irc_t *irc, user_t *u)
1174{
1175        char *vb = set_getstr(&irc->set, "voice_buddies");
1176        char *hb = set_getstr(&irc->set, "halfop_buddies");
1177        char *ob = set_getstr(&irc->set, "op_buddies");
1178        int encrypted = u->encrypted;
1179        int trusted = u->encrypted > 1;
1180        char flags[7];
1181        int nflags=0;
1182        char *p = flags;
1183        char *from;
1184        int i;
1185       
1186        if(!strcmp(vb, "encrypted")) {
1187                *(p++) = encrypted ? '+' : '-';
1188                *(p++) = 'v';
1189                nflags++;
1190        } else if(!strcmp(vb, "trusted")) {
1191                *(p++) = trusted ? '+' : '-';
1192                *(p++) = 'v';
1193                nflags++;
1194        }
1195        if(!strcmp(hb, "encrypted")) {
1196                *(p++) = encrypted ? '+' : '-';
1197                *(p++) = 'h';
1198                nflags++;
1199        } else if(!strcmp(hb, "trusted")) {
1200                *(p++) = trusted ? '+' : '-';
1201                *(p++) = 'h';
1202                nflags++;
1203        }
1204        if(!strcmp(ob, "encrypted")) {
1205                *(p++) = encrypted ? '+' : '-';
1206                *(p++) = 'o';
1207                nflags++;
1208        } else if(!strcmp(ob, "trusted")) {
1209                *(p++) = trusted ? '+' : '-';
1210                *(p++) = 'o';
1211                nflags++;
1212        }
1213        *p = '\0';
1214       
1215        p = g_malloc(nflags * (strlen(u->nick)+1) + 1);
1216        *p = '\0';
1217        if(!p)
1218                return 0;
1219        for(i=0; i<nflags; i++) {
1220                strcat(p, " ");
1221                strcat(p, u->nick);
1222        }
1223        if(set_getbool(&irc->set, "simulate_netsplit"))
1224                from = g_strdup(irc->myhost);
1225        else
1226                from = g_strdup_printf("%s!%s@%s", irc->mynick, irc->mynick, irc->myhost);
1227        irc_write(irc, ":%s MODE %s %s%s", from, irc->channel, flags, p);
1228        g_free(from);
1229        g_free(p);
1230               
1231        return 1;
1232}
1233
1234void show_fingerprints(irc_t *irc, ConnContext *ctx)
1235{
1236        char human[45];
1237        Fingerprint *fp;
1238        const char *trust;
1239        int count=0;
1240       
1241        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1242                if(!fp->fingerprint)
1243                        continue;
1244                count++;
1245                otrl_privkey_hash_to_human(human, fp->fingerprint);
1246                if(!fp->trust || fp->trust[0] == '\0') {
1247                        trust="untrusted";
1248                } else {
1249                        trust=fp->trust;
1250                }
1251                if(fp == ctx->active_fingerprint) {
1252                        irc_usermsg(irc, \x02%s (%s)\x02", human, trust);
1253                } else {
1254                        irc_usermsg(irc, "  %s (%s)", human, trust);
1255                }
1256        }
1257        if(count==0)
1258                irc_usermsg(irc, "  no fingerprints");
1259}
1260
1261Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args)
1262{
1263        Fingerprint *fp, *fp2;
1264        char human[45];
1265        char prefix[45], *p;
1266        int n;
1267        int i,j;
1268       
1269        /* assemble the args into a prefix in standard "human" form */
1270        n=0;
1271        p=prefix;
1272        for(i=0; args[i]; i++) {
1273                for(j=0; args[i][j]; j++) {
1274                        char c = toupper(args[i][j]);
1275                       
1276                        if(n>=40) {
1277                                irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
1278                                return NULL;
1279                        }
1280                       
1281                        if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1282                                *(p++) = c;
1283                        } else {
1284                                irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1285                                return NULL;
1286                        }
1287                       
1288                        n++;
1289                        if(n%8 == 0)
1290                                *(p++) = ' ';
1291                }
1292        }
1293        *p = '\0';
1294        log_message(LOGLVL_DEBUG, "match_fingerprint '%s'", prefix);
1295        log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix));
1296       
1297        /* find first fingerprint with the given prefix */
1298        n = strlen(prefix);
1299        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1300                if(!fp->fingerprint)
1301                        continue;
1302                otrl_privkey_hash_to_human(human, fp->fingerprint);
1303                if(!strncmp(prefix, human, n))
1304                        break;
1305        }
1306        if(!fp) {
1307                irc_usermsg(irc, "%s: no match", prefix);
1308                return NULL;
1309        }
1310       
1311        /* make sure the match, if any, is unique */
1312        for(fp2=fp->next; fp2; fp2=fp2->next) {
1313                if(!fp2->fingerprint)
1314                        continue;
1315                otrl_privkey_hash_to_human(human, fp2->fingerprint);
1316                if(!strncmp(prefix, human, n))
1317                        break;
1318        }
1319        if(fp2) {
1320                irc_usermsg(irc, "%s: multiple matches", prefix);
1321                return NULL;
1322        }
1323       
1324        return fp;
1325}
1326
1327OtrlPrivKey *match_privkey(irc_t *irc, const char **args)
1328{
1329        OtrlPrivKey *k, *k2;
1330        char human[45];
1331        char prefix[45], *p;
1332        int n;
1333        int i,j;
1334       
1335        /* assemble the args into a prefix in standard "human" form */
1336        n=0;
1337        p=prefix;
1338        for(i=0; args[i]; i++) {
1339                for(j=0; args[i][j]; j++) {
1340                        char c = toupper(args[i][j]);
1341                       
1342                        if(n>=40) {
1343                                irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
1344                                return NULL;
1345                        }
1346                       
1347                        if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1348                                *(p++) = c;
1349                        } else {
1350                                irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1351                                return NULL;
1352                        }
1353                       
1354                        n++;
1355                        if(n%8 == 0)
1356                                *(p++) = ' ';
1357                }
1358        }
1359        *p = '\0';
1360        log_message(LOGLVL_DEBUG, "match_privkey '%s'", prefix);
1361        log_message(LOGLVL_DEBUG, "n=%d strlen(prefix)=%d", n, strlen(prefix));
1362       
1363        /* find first key which matches the given prefix */
1364        n = strlen(prefix);
1365        for(k=irc->otr_us->privkey_root; k; k=k->next) {
1366                p = otrl_privkey_fingerprint(irc->otr_us, human, k->accountname, k->protocol);
1367                if(!p) /* gah! :-P */
1368                        continue;
1369                if(!strncmp(prefix, human, n))
1370                        break;
1371        }
1372        if(!k) {
1373                irc_usermsg(irc, "%s: no match", prefix);
1374                return NULL;
1375        }
1376       
1377        /* make sure the match, if any, is unique */
1378        for(k2=k->next; k2; k2=k2->next) {
1379                p = otrl_privkey_fingerprint(irc->otr_us, human, k2->accountname, k2->protocol);
1380                if(!p) /* gah! :-P */
1381                        continue;
1382                if(!strncmp(prefix, human, n))
1383                        break;
1384        }
1385        if(k2) {
1386                irc_usermsg(irc, "%s: multiple matches", prefix);
1387                return NULL;
1388        }
1389       
1390        return k;
1391}
1392
1393void show_general_otr_info(irc_t *irc)
1394{
1395        ConnContext *ctx;
1396        OtrlPrivKey *key;
1397        char human[45];
1398
1399        /* list all privkeys */
1400        irc_usermsg(irc, "\x1fprivate keys:\x1f");
1401        for(key=irc->otr_us->privkey_root; key; key=key->next) {
1402                const char *hash;
1403               
1404                switch(key->pubkey_type) {
1405                case OTRL_PUBKEY_TYPE_DSA:
1406                        irc_usermsg(irc, "  %s/%s - DSA", key->accountname, key->protocol);
1407                        break;
1408                default:
1409                        irc_usermsg(irc, "  %s/%s - type %d", key->accountname, key->protocol,
1410                                key->pubkey_type);
1411                }
1412
1413                /* No, it doesn't make much sense to search for the privkey again by
1414                   account/protocol, but libotr currently doesn't provide a direct routine
1415                   for hashing a given 'OtrlPrivKey'... */
1416                hash = otrl_privkey_fingerprint(irc->otr_us, human, key->accountname, key->protocol);
1417                if(hash) /* should always succeed */
1418                        irc_usermsg(irc, "    %s", human);
1419        }
1420
1421        /* list all contexts */
1422        irc_usermsg(irc, "%s", "");
1423        irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)");
1424        for(ctx=irc->otr_us->context_root; ctx; ctx=ctx->next) {\
1425                user_t *u;
1426                char *userstring;
1427               
1428                u = peeruser(irc, ctx->username, ctx->protocol);
1429                if(u)
1430                        userstring = g_strdup_printf("%s/%s/%s (%s)",
1431                                ctx->username, ctx->protocol, ctx->accountname, u->nick);
1432                else
1433                        userstring = g_strdup_printf("%s/%s/%s",
1434                                ctx->username, ctx->protocol, ctx->accountname);
1435               
1436                if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
1437                        irc_usermsg(irc, \x02%s\x02", userstring);
1438                } else {
1439                        irc_usermsg(irc, "  %s", userstring);
1440                }
1441               
1442                g_free(userstring);
1443        }
1444}
1445
1446void show_otr_context_info(irc_t *irc, ConnContext *ctx)
1447{
1448        switch(ctx->otr_offer) {
1449        case OFFER_NOT:
1450                irc_usermsg(irc, "  otr offer status: none sent");
1451                break;
1452        case OFFER_SENT:
1453                irc_usermsg(irc, "  otr offer status: awaiting reply");
1454                break;
1455        case OFFER_ACCEPTED:
1456                irc_usermsg(irc, "  otr offer status: accepted our offer");
1457                break;
1458        case OFFER_REJECTED:
1459                irc_usermsg(irc, "  otr offer status: ignored our offer");
1460                break;
1461        default:
1462                irc_usermsg(irc, "  otr offer status: %d", ctx->otr_offer);
1463        }
1464
1465        switch(ctx->msgstate) {
1466        case OTRL_MSGSTATE_PLAINTEXT:
1467                irc_usermsg(irc, "  connection state: cleartext");
1468                break;
1469        case OTRL_MSGSTATE_ENCRYPTED:
1470                irc_usermsg(irc, "  connection state: encrypted (v%d)", ctx->protocol_version);
1471                break;
1472        case OTRL_MSGSTATE_FINISHED:
1473                irc_usermsg(irc, "  connection state: shut down");
1474                break;
1475        default:
1476                irc_usermsg(irc, "  connection state: %d", ctx->msgstate);
1477        }
1478
1479        irc_usermsg(irc, "  fingerprints: (bold=active)");     
1480        show_fingerprints(irc, ctx);
1481}
1482
1483void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
1484{
1485        GError *err;
1486        GThread *thr;
1487        struct kgdata *kg;
1488        gint ev;
1489       
1490        irc_usermsg(irc, "generating new private key for %s/%s...", handle, protocol);
1491       
1492        kg = g_new0(struct kgdata, 1);
1493        if(!kg) {
1494                irc_usermsg(irc, "otr keygen failed: out of memory");
1495                return;
1496        }
1497
1498        /* Assemble the job description to be passed to thread and handler */
1499        kg->irc = irc;
1500        kg->keyfile = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, kg->irc->nick);
1501        if(!kg->keyfile) {
1502                irc_usermsg(irc, "otr keygen failed: out of memory");
1503                g_free(kg);
1504                return;
1505        }
1506        kg->handle = handle;
1507        kg->protocol = protocol;
1508        kg->mutex = g_mutex_new();
1509        if(!kg->mutex) {
1510                irc_usermsg(irc, "otr keygen failed: couldn't create mutex");
1511                g_free(kg->keyfile);
1512                g_free(kg);
1513                return;
1514        }
1515        kg->done = 0;
1516
1517        /* Poll for completion of the thread periodically. I would have preferred
1518           to just wait on a pipe but this way it's portable to Windows. *sigh*
1519        */
1520        ev = b_timeout_add(1000, &keygen_finish_handler, kg);
1521        if(!ev) {
1522                irc_usermsg(irc, "otr keygen failed: couldn't register timeout");
1523                g_free(kg->keyfile);
1524                g_mutex_free(kg->mutex);
1525                g_free(kg);
1526                return;
1527        }
1528
1529        thr = g_thread_create(&otr_keygen_thread_func, kg, FALSE, &err);
1530        if(!thr) {
1531                irc_usermsg(irc, "otr keygen failed: %s", err->message);
1532                g_free(kg->keyfile);
1533                g_mutex_free(kg->mutex);
1534                g_free(kg);
1535                b_event_remove(ev);
1536        }
1537}
1538
1539gpointer otr_keygen_thread_func(gpointer data)
1540{
1541        struct kgdata *kg = (struct kgdata *)data;
1542       
1543        /* lock OTR subsystem and do the work */
1544        g_static_rec_mutex_lock(&kg->irc->otr_mutex);
1545        kg->result = otrl_privkey_generate(kg->irc->otr_us, kg->keyfile, kg->handle,
1546                kg->protocol);
1547        chmod(kg->keyfile, 0600);
1548        g_static_rec_mutex_unlock(&kg->irc->otr_mutex);
1549        /* OTR enabled again */
1550       
1551        /* notify mainloop */
1552        g_mutex_lock(kg->mutex);
1553        kg->done = 1;
1554        g_mutex_unlock(kg->mutex);
1555       
1556        return NULL;
1557}
1558
1559gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
1560{
1561        struct kgdata *kg = (struct kgdata *)data;
1562        int done;
1563       
1564        g_mutex_lock(kg->mutex);
1565        done = kg->done;
1566        g_mutex_unlock(kg->mutex);
1567        if(kg->done) {
1568                if(kg->result) {
1569                        irc_usermsg(kg->irc, "otr keygen: %s", strerror(kg->result));
1570                } else {
1571                        irc_usermsg(kg->irc, "otr keygen for %s/%s complete", kg->handle, kg->protocol);
1572                }
1573                g_free(kg->keyfile);
1574                g_mutex_free(kg->mutex);
1575                g_free(kg);
1576                return FALSE; /* unregister timeout */
1577        }
1578
1579        return TRUE;  /* still working, continue checking */
1580}
1581
1582void yes_keygen(gpointer w, void *data)
1583{
1584        account_t *acc = (account_t *)data;
1585       
1586        otr_keygen(acc->irc, acc->user, acc->prpl->name);
1587}
1588
1589
1590#else /* WITH_OTR undefined */
1591
1592void cmd_otr(irc_t *irc, char **args)
1593{
1594        irc_usermsg(irc, "otr: n/a, compiled without OTR support");
1595}
1596
1597#endif
Note: See TracBrowser for help on using the repository browser.