source: otr.c @ 76e2f62

Last change on this file since 76e2f62 was 76e2f62, checked in by Sven Moritz Hallberg <pesco@…>, at 2010-09-19T17:29:10Z

handle SMP1Q packets (smp init with question)

  • Property mode set to 100644
File size: 46.6 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_SMP1Q);
1102        if (tlv) {
1103                if (nextMsg != OTRL_SMP_EXPECT1) {
1104                        irc_usermsg(irc, "smp %s: spurious SMP1Q 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                        char *question = g_strndup((char *)tlv->data, tlv->len);
1109                        irc_usermsg(irc, "smp: question from %s: \"%s\"", u->nick,
1110                                question);
1111                        irc_usermsg(irc, "smp: respond with \x02otr smp %s <answer>\x02",
1112                                u->nick);
1113                        g_free(question);
1114                        /* smp stays in EXPECT1 until user responds */
1115                }
1116        }
1117        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
1118        if (tlv) {
1119                if (nextMsg != OTRL_SMP_EXPECT1) {
1120                        irc_usermsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick);
1121                        otrl_message_abort_smp(us, ops, u->bu->ic, context);
1122                        otrl_sm_state_free(context->smstate);
1123                } else {
1124                        irc_usermsg(irc, "smp: initiated by %s"
1125                                " - respond with \x02otr smp %s <secret>\x02",
1126                                u->nick, u->nick);
1127                        /* smp stays in EXPECT1 until user responds */
1128                }
1129        }
1130        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
1131        if (tlv) {
1132                if (nextMsg != OTRL_SMP_EXPECT2) {
1133                        irc_usermsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick);
1134                        otrl_message_abort_smp(us, ops, u->bu->ic, context);
1135                        otrl_sm_state_free(context->smstate);
1136                } else {
1137                        /* SMP2 received, otrl_message_receiving will have sent SMP3 */
1138                        context->smstate->nextExpected = OTRL_SMP_EXPECT4;
1139                }
1140        }
1141        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
1142        if (tlv) {
1143                if (nextMsg != OTRL_SMP_EXPECT3) {
1144                        irc_usermsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick);
1145                        otrl_message_abort_smp(us, ops, u->bu->ic, context);
1146                        otrl_sm_state_free(context->smstate);
1147                } else {
1148                        /* SMP3 received, otrl_message_receiving will have sent SMP4 and set fp trust */
1149                        const char *trust = context->active_fingerprint->trust;
1150                        if(!trust || trust[0]=='\0') {
1151                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1152                                        u->nick);
1153                        } else {
1154                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1155                                        u->nick);
1156                        }
1157                        otrl_sm_state_free(context->smstate);
1158                        /* smp is in back in EXPECT1 */
1159                }
1160        }
1161        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
1162        if (tlv) {
1163                if (nextMsg != OTRL_SMP_EXPECT4) {
1164                        irc_usermsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick);
1165                        otrl_message_abort_smp(us, ops, u->bu->ic, context);
1166                        otrl_sm_state_free(context->smstate);
1167                } else {
1168                        /* SMP4 received, otrl_message_receiving will have set fp trust */
1169                        const char *trust = context->active_fingerprint->trust;
1170                        if(!trust || trust[0]=='\0') {
1171                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1172                                        u->nick);
1173                        } else {
1174                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1175                                        u->nick);
1176                        }
1177                        otrl_sm_state_free(context->smstate);
1178                        /* smp is in back in EXPECT1 */
1179                }
1180        }
1181        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
1182        if (tlv) {
1183                irc_usermsg(irc, "smp: received abort from %s", u->nick);
1184                otrl_sm_state_free(context->smstate);
1185                /* smp is in back in EXPECT1 */
1186        }
1187}
1188
1189/* helper to assert that account and protocol names given to ops below always
1190   match the im_connection passed through as opdata */
1191struct im_connection *check_imc(void *opdata, const char *accountname,
1192        const char *protocol)
1193{
1194        struct im_connection *ic = (struct im_connection *)opdata;
1195
1196        if (strcmp(accountname, ic->acc->user) != 0) {
1197                log_message(LOGLVL_WARNING,
1198                        "otr: internal account name mismatch: '%s' vs '%s'",
1199                        accountname, ic->acc->user);
1200        }
1201        if (strcmp(protocol, ic->acc->prpl->name) != 0) {
1202                log_message(LOGLVL_WARNING,
1203                        "otr: internal protocol name mismatch: '%s' vs '%s'",
1204                        protocol, ic->acc->prpl->name);
1205        }
1206       
1207        return ic;
1208}
1209
1210irc_user_t *peeruser(irc_t *irc, const char *handle, const char *protocol)
1211{
1212        GSList *l;
1213       
1214        for(l=irc->b->users; l; l = l->next) {
1215                bee_user_t *bu = l->data;
1216                struct prpl *prpl;
1217                if(!bu->ui_data || !bu->ic || !bu->handle)
1218                        continue;
1219                prpl = bu->ic->acc->prpl;
1220                if(strcmp(prpl->name, protocol) == 0
1221                        && prpl->handle_cmp(bu->handle, handle) == 0) {
1222                        return bu->ui_data;
1223                }
1224        }
1225       
1226        return NULL;
1227}
1228
1229int hexval(char a)
1230{
1231        int x=tolower(a);
1232       
1233        if(x>='a' && x<='f')
1234                x = x - 'a' + 10;
1235        else if(x>='0' && x<='9')
1236                x = x - '0';
1237        else
1238                return -1;
1239       
1240        return x;
1241}
1242
1243const char *peernick(irc_t *irc, const char *handle, const char *protocol)
1244{
1245        static char fallback[512];
1246       
1247        irc_user_t *u = peeruser(irc, handle, protocol);
1248        if(u) {
1249                return u->nick;
1250        } else {
1251                g_snprintf(fallback, 511, "%s/%s", handle, protocol);
1252                return fallback;
1253        }
1254}
1255
1256int otr_update_modeflags(irc_t *irc, irc_user_t *u)
1257{
1258        return 1;
1259}
1260
1261void show_fingerprints(irc_t *irc, ConnContext *ctx)
1262{
1263        char human[45];
1264        Fingerprint *fp;
1265        const char *trust;
1266        int count=0;
1267       
1268        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1269                if(!fp->fingerprint)
1270                        continue;
1271                count++;
1272                otrl_privkey_hash_to_human(human, fp->fingerprint);
1273                if(!fp->trust || fp->trust[0] == '\0') {
1274                        trust="untrusted";
1275                } else {
1276                        trust=fp->trust;
1277                }
1278                if(fp == ctx->active_fingerprint) {
1279                        irc_usermsg(irc, "    \x02%s (%s)\x02", human, trust);
1280                } else {
1281                        irc_usermsg(irc, "    %s (%s)", human, trust);
1282                }
1283        }
1284        if(count==0)
1285                irc_usermsg(irc, "    (none)");
1286}
1287
1288Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args)
1289{
1290        Fingerprint *fp, *fp2;
1291        char human[45];
1292        char prefix[45], *p;
1293        int n;
1294        int i,j;
1295       
1296        /* assemble the args into a prefix in standard "human" form */
1297        n=0;
1298        p=prefix;
1299        for(i=0; args[i]; i++) {
1300                for(j=0; args[i][j]; j++) {
1301                        char c = toupper(args[i][j]);
1302                       
1303                        if(n>=40) {
1304                                irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
1305                                return NULL;
1306                        }
1307                       
1308                        if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1309                                *(p++) = c;
1310                        } else {
1311                                irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1312                                return NULL;
1313                        }
1314                       
1315                        n++;
1316                        if(n%8 == 0)
1317                                *(p++) = ' ';
1318                }
1319        }
1320        *p = '\0';
1321       
1322        /* find first fingerprint with the given prefix */
1323        n = strlen(prefix);
1324        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1325                if(!fp->fingerprint)
1326                        continue;
1327                otrl_privkey_hash_to_human(human, fp->fingerprint);
1328                if(!strncmp(prefix, human, n))
1329                        break;
1330        }
1331        if(!fp) {
1332                irc_usermsg(irc, "%s: no match", prefix);
1333                return NULL;
1334        }
1335       
1336        /* make sure the match, if any, is unique */
1337        for(fp2=fp->next; fp2; fp2=fp2->next) {
1338                if(!fp2->fingerprint)
1339                        continue;
1340                otrl_privkey_hash_to_human(human, fp2->fingerprint);
1341                if(!strncmp(prefix, human, n))
1342                        break;
1343        }
1344        if(fp2) {
1345                irc_usermsg(irc, "%s: multiple matches", prefix);
1346                return NULL;
1347        }
1348       
1349        return fp;
1350}
1351
1352OtrlPrivKey *match_privkey(irc_t *irc, const char **args)
1353{
1354        OtrlPrivKey *k, *k2;
1355        char human[45];
1356        char prefix[45], *p;
1357        int n;
1358        int i,j;
1359       
1360        /* assemble the args into a prefix in standard "human" form */
1361        n=0;
1362        p=prefix;
1363        for(i=0; args[i]; i++) {
1364                for(j=0; args[i][j]; j++) {
1365                        char c = toupper(args[i][j]);
1366                       
1367                        if(n>=40) {
1368                                irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
1369                                return NULL;
1370                        }
1371                       
1372                        if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1373                                *(p++) = c;
1374                        } else {
1375                                irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1376                                return NULL;
1377                        }
1378                       
1379                        n++;
1380                        if(n%8 == 0)
1381                                *(p++) = ' ';
1382                }
1383        }
1384        *p = '\0';
1385       
1386        /* find first key which matches the given prefix */
1387        n = strlen(prefix);
1388        for(k=irc->otr->us->privkey_root; k; k=k->next) {
1389                p = otrl_privkey_fingerprint(irc->otr->us, human, k->accountname, k->protocol);
1390                if(!p) /* gah! :-P */
1391                        continue;
1392                if(!strncmp(prefix, human, n))
1393                        break;
1394        }
1395        if(!k) {
1396                irc_usermsg(irc, "%s: no match", prefix);
1397                return NULL;
1398        }
1399       
1400        /* make sure the match, if any, is unique */
1401        for(k2=k->next; k2; k2=k2->next) {
1402                p = otrl_privkey_fingerprint(irc->otr->us, human, k2->accountname, k2->protocol);
1403                if(!p) /* gah! :-P */
1404                        continue;
1405                if(!strncmp(prefix, human, n))
1406                        break;
1407        }
1408        if(k2) {
1409                irc_usermsg(irc, "%s: multiple matches", prefix);
1410                return NULL;
1411        }
1412       
1413        return k;
1414}
1415
1416void show_general_otr_info(irc_t *irc)
1417{
1418        ConnContext *ctx;
1419        OtrlPrivKey *key;
1420        char human[45];
1421        kg_t *kg;
1422
1423        /* list all privkeys (including ones being generated) */
1424        irc_usermsg(irc, "\x1fprivate keys:\x1f");
1425        for(key=irc->otr->us->privkey_root; key; key=key->next) {
1426                const char *hash;
1427               
1428                switch(key->pubkey_type) {
1429                case OTRL_PUBKEY_TYPE_DSA:
1430                        irc_usermsg(irc, "  %s/%s - DSA", key->accountname, key->protocol);
1431                        break;
1432                default:
1433                        irc_usermsg(irc, "  %s/%s - type %d", key->accountname, key->protocol,
1434                                key->pubkey_type);
1435                }
1436
1437                /* No, it doesn't make much sense to search for the privkey again by
1438                   account/protocol, but libotr currently doesn't provide a direct routine
1439                   for hashing a given 'OtrlPrivKey'... */
1440                hash = otrl_privkey_fingerprint(irc->otr->us, human, key->accountname, key->protocol);
1441                if(hash) /* should always succeed */
1442                        irc_usermsg(irc, "    %s", human);
1443        }
1444        if(irc->otr->sent_accountname) {
1445                irc_usermsg(irc, "  %s/%s - DSA", irc->otr->sent_accountname,
1446                        irc->otr->sent_protocol);
1447                irc_usermsg(irc, "    (being generated)");
1448        }
1449        for(kg=irc->otr->todo; kg; kg=kg->next) {
1450                irc_usermsg(irc, "  %s/%s - DSA", kg->accountname, kg->protocol);
1451                irc_usermsg(irc, "    (queued)");
1452        }
1453        if(key == irc->otr->us->privkey_root &&
1454           !irc->otr->sent_accountname &&
1455           kg == irc->otr->todo)
1456                irc_usermsg(irc, "  (none)");
1457
1458        /* list all contexts */
1459        irc_usermsg(irc, "%s", "");
1460        irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)");
1461        for(ctx=irc->otr->us->context_root; ctx; ctx=ctx->next) {\
1462                irc_user_t *u;
1463                char *userstring;
1464               
1465                u = peeruser(irc, ctx->username, ctx->protocol);
1466                if(u)
1467                        userstring = g_strdup_printf("%s/%s/%s (%s)",
1468                                ctx->username, ctx->protocol, ctx->accountname, u->nick);
1469                else
1470                        userstring = g_strdup_printf("%s/%s/%s",
1471                                ctx->username, ctx->protocol, ctx->accountname);
1472               
1473                if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
1474                        irc_usermsg(irc, \x02%s\x02", userstring);
1475                } else {
1476                        irc_usermsg(irc, "  %s", userstring);
1477                }
1478               
1479                g_free(userstring);
1480        }
1481        if(ctx == irc->otr->us->context_root)
1482                irc_usermsg(irc, "  (none)");
1483}
1484
1485void show_otr_context_info(irc_t *irc, ConnContext *ctx)
1486{
1487        switch(ctx->otr_offer) {
1488        case OFFER_NOT:
1489                irc_usermsg(irc, "  otr offer status: none sent");
1490                break;
1491        case OFFER_SENT:
1492                irc_usermsg(irc, "  otr offer status: awaiting reply");
1493                break;
1494        case OFFER_ACCEPTED:
1495                irc_usermsg(irc, "  otr offer status: accepted our offer");
1496                break;
1497        case OFFER_REJECTED:
1498                irc_usermsg(irc, "  otr offer status: ignored our offer");
1499                break;
1500        default:
1501                irc_usermsg(irc, "  otr offer status: %d", ctx->otr_offer);
1502        }
1503
1504        switch(ctx->msgstate) {
1505        case OTRL_MSGSTATE_PLAINTEXT:
1506                irc_usermsg(irc, "  connection state: cleartext");
1507                break;
1508        case OTRL_MSGSTATE_ENCRYPTED:
1509                irc_usermsg(irc, "  connection state: encrypted (v%d)", ctx->protocol_version);
1510                break;
1511        case OTRL_MSGSTATE_FINISHED:
1512                irc_usermsg(irc, "  connection state: shut down");
1513                break;
1514        default:
1515                irc_usermsg(irc, "  connection state: %d", ctx->msgstate);
1516        }
1517
1518        irc_usermsg(irc, "  fingerprints: (bold=active)");     
1519        show_fingerprints(irc, ctx);
1520}
1521
1522int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol)
1523{
1524        kg_t *kg;
1525       
1526        if(!irc->otr->sent_accountname || !irc->otr->sent_protocol)
1527                return 0;
1528
1529        /* are we currently working on this key? */
1530        if(!strcmp(handle, irc->otr->sent_accountname) &&
1531           !strcmp(protocol, irc->otr->sent_protocol))
1532                return 1;
1533       
1534        /* do we have it queued for later? */
1535        for(kg=irc->otr->todo; kg; kg=kg->next) {
1536                if(!strcmp(handle, kg->accountname) &&
1537                   !strcmp(protocol, kg->protocol))
1538                        return 1;
1539        }
1540       
1541        return 0;
1542}
1543
1544void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
1545{
1546        /* do nothing if a key for the requested account is already being generated */
1547        if(keygen_in_progress(irc, handle, protocol))
1548                return;
1549
1550        /* see if we already have a keygen child running. if not, start one and put a
1551           handler on its output. */
1552        if(!irc->otr->keygen || waitpid(irc->otr->keygen, NULL, WNOHANG)) {
1553                pid_t p;
1554                int to[2], from[2];
1555                FILE *tof, *fromf;
1556               
1557                if(pipe(to) < 0 || pipe(from) < 0) {
1558                        irc_usermsg(irc, "otr keygen: couldn't create pipe: %s", strerror(errno));
1559                        return;
1560                }
1561               
1562                tof = fdopen(to[1], "w");
1563                fromf = fdopen(from[0], "r");
1564                if(!tof || !fromf) {
1565                        irc_usermsg(irc, "otr keygen: couldn't streamify pipe: %s", strerror(errno));
1566                        return;
1567                }
1568               
1569                p = fork();
1570                if(p<0) {
1571                        irc_usermsg(irc, "otr keygen: couldn't fork: %s", strerror(errno));
1572                        return;
1573                }
1574               
1575                if(!p) {
1576                        /* child process */
1577                        signal(SIGTERM, exit);
1578                        keygen_child_main(irc->otr->us, to[0], from[1]);
1579                        exit(0);
1580                }
1581               
1582                irc->otr->keygen = p;
1583                irc->otr->to = tof;
1584                irc->otr->from = fromf;
1585                irc->otr->sent_accountname = NULL;
1586                irc->otr->sent_protocol = NULL;
1587                irc->otr->todo = NULL;
1588                b_input_add(from[0], B_EV_IO_READ, keygen_finish_handler, irc);
1589        }
1590       
1591        /* is the keygen slave currently working? */
1592        if(irc->otr->sent_accountname) {
1593                /* enqueue our job for later transmission */
1594                kg_t **kg = &irc->otr->todo;
1595                while(*kg)
1596                        kg=&((*kg)->next);
1597                *kg = g_new0(kg_t, 1);
1598                (*kg)->accountname = g_strdup(handle);
1599                (*kg)->protocol = g_strdup(protocol);
1600        } else {
1601                /* send our job over and remember it */
1602                fprintf(irc->otr->to, "%s\n%s\n", handle, protocol);
1603                fflush(irc->otr->to);
1604                irc->otr->sent_accountname = g_strdup(handle);
1605                irc->otr->sent_protocol = g_strdup(protocol);
1606        }
1607}
1608
1609void keygen_child_main(OtrlUserState us, int infd, int outfd)
1610{
1611        FILE *input, *output;
1612        char filename[128], accountname[512], protocol[512];
1613        gcry_error_t e;
1614        int tempfd;
1615       
1616        input = fdopen(infd, "r");
1617        output = fdopen(outfd, "w");
1618       
1619        while(!feof(input) && !ferror(input) && !feof(output) && !ferror(output)) {
1620                myfgets(accountname, 512, input);
1621                myfgets(protocol, 512, input);
1622               
1623                strncpy(filename, "/tmp/bitlbee-XXXXXX", 128);
1624                tempfd = mkstemp(filename);
1625                close(tempfd);
1626
1627                e = otrl_privkey_generate(us, filename, accountname, protocol);
1628                if(e) {
1629                        fprintf(output, "\n");  /* this means failure */
1630                        fprintf(output, "otr keygen: %s\n", gcry_strerror(e));
1631                        unlink(filename);
1632                } else {
1633                        fprintf(output, "%s\n", filename);
1634                        fprintf(output, "otr keygen for %s/%s complete\n", accountname, protocol);
1635                }
1636                fflush(output);
1637        }
1638       
1639        fclose(input);
1640        fclose(output);
1641}
1642
1643gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
1644{
1645        irc_t *irc = (irc_t *)data;
1646        char filename[512], msg[512];
1647
1648        myfgets(filename, 512, irc->otr->from);
1649        myfgets(msg, 512, irc->otr->from);
1650       
1651        irc_usermsg(irc, "%s", msg);
1652        if(filename[0]) {
1653                char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->user->nick);
1654                char *tmp = g_strdup_printf("%s.new", kf);
1655                copyfile(filename, tmp);
1656                unlink(filename);
1657                rename(tmp,kf);
1658                otrl_privkey_read(irc->otr->us, kf);
1659                g_free(kf);
1660                g_free(tmp);
1661        }
1662       
1663        /* forget this job */
1664        g_free(irc->otr->sent_accountname);
1665        g_free(irc->otr->sent_protocol);
1666        irc->otr->sent_accountname = NULL;
1667        irc->otr->sent_protocol = NULL;
1668       
1669        /* see if there are any more in the queue */
1670        if(irc->otr->todo) {
1671                kg_t *p = irc->otr->todo;
1672                /* send the next one over */
1673                fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol);
1674                fflush(irc->otr->to);
1675                irc->otr->sent_accountname = p->accountname;
1676                irc->otr->sent_protocol = p->protocol;
1677                irc->otr->todo = p->next;
1678                g_free(p);
1679                return TRUE;   /* keep watching */
1680        } else {
1681                /* okay, the slave is idle now, so kill him */
1682                fclose(irc->otr->from);
1683                fclose(irc->otr->to);
1684                irc->otr->from = irc->otr->to = NULL;
1685                kill(irc->otr->keygen, SIGTERM);
1686                waitpid(irc->otr->keygen, NULL, 0);
1687                irc->otr->keygen = 0;
1688                return FALSE;  /* unregister ourselves */
1689        }
1690}
1691
1692void copyfile(const char *a, const char *b)
1693{
1694        int fda, fdb;
1695        int n;
1696        char buf[1024];
1697       
1698        fda = open(a, O_RDONLY);
1699        fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1700       
1701        while((n=read(fda, buf, 1024)) > 0)
1702                write(fdb, buf, n);
1703       
1704        close(fda);
1705        close(fdb);     
1706}
1707
1708void myfgets(char *s, int size, FILE *stream)
1709{
1710        if(!fgets(s, size, stream)) {
1711                s[0] = '\0';
1712        } else {
1713                int n = strlen(s);
1714                if(n>0 && s[n-1] == '\n')
1715                        s[n-1] = '\0';
1716        }
1717}
1718
1719void yes_keygen(void *data)
1720{
1721        account_t *acc = (account_t *)data;
1722        irc_t *irc = acc->bee->ui_data;
1723       
1724        if(keygen_in_progress(irc, acc->user, acc->prpl->name)) {
1725                irc_usermsg(irc, "keygen for %s/%s already in progress",
1726                        acc->user, acc->prpl->name);
1727        } else {
1728                irc_usermsg(irc, "starting background keygen for %s/%s",
1729                        acc->user, acc->prpl->name);
1730                irc_usermsg(irc, "you will be notified when it completes");
1731                otr_keygen(irc, acc->user, acc->prpl->name);
1732        }
1733}
1734
1735
1736#else /* WITH_OTR undefined */
1737
1738void cmd_otr(irc_t *irc, char **args)
1739{
1740        irc_usermsg(irc, "otr: n/a, compiled without OTR support");
1741}
1742
1743#endif
Note: See TracBrowser for help on using the repository browser.