source: otr.c @ 2dcaf9a

Last change on this file since 2dcaf9a was 2dcaf9a, checked in by Wilmer van der Gaast <wilmer@…>, at 2010-09-01T22:35:06Z

Load/save code. It'd be better if the OTR module would just save its info
in BitlBee settings that automatically end up in the existing .xml files
(or whatever storage is used), but I realise this is non-trivial.

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