source: otr.c @ f3597a1

Last change on this file since f3597a1 was f3597a1, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-14T21:01:21Z

revert keygen behaviour to old (lax) behavior

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