source: otr.c @ 934db064

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

Do encryption and decryption. Somehow SMP and other things aren't working
so well yet, at least when testing with Pidgin on the other side. Not sure
where the bug is.

  • Property mode set to 100644
File size: 46.0 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};
451
452static void cmd_otr(irc_t *irc, char **args)
453{
454        const command_t *cmd;
455       
456        if(!args[0])
457                return;
458       
459        if(!args[1])
460                return;
461       
462        for(cmd=otr_commands; cmd->command; cmd++) {
463                if(strcmp(cmd->command, args[1]) == 0)
464                        break;
465        }
466       
467        if(!cmd->command) {
468                irc_usermsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02",
469                        args[0], args[1]);
470                return;
471        }
472       
473        if(!args[cmd->required_parameters+1]) {
474                irc_usermsg(irc, "%s %s: not enough arguments (%d req.)",
475                        args[0], args[1], cmd->required_parameters);
476                return;
477        }
478       
479        cmd->execute(irc, args+1);
480}
481
482
483/*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/
484
485OtrlPolicy op_policy(void *opdata, ConnContext *context)
486{
487        struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol);
488        irc_t *irc = ic->bee->ui_data;
489        const char *p;
490       
491        /* policy override during keygen: if we're missing the key for context but are currently
492           generating it, then that's as much as we can do. => temporarily return NEVER. */
493        if(keygen_in_progress(irc, context->accountname, context->protocol) &&
494           !otrl_privkey_find(irc->otr->us, context->accountname, context->protocol))
495                return OTRL_POLICY_NEVER;
496
497        p = set_getstr(&ic->bee->set, "otr_policy");
498        if(!strcmp(p, "never"))
499                return OTRL_POLICY_NEVER;
500        if(!strcmp(p, "opportunistic"))
501                return OTRL_POLICY_OPPORTUNISTIC;
502        if(!strcmp(p, "manual"))
503                return OTRL_POLICY_MANUAL;
504        if(!strcmp(p, "always"))
505                return OTRL_POLICY_ALWAYS;
506       
507        return OTRL_POLICY_OPPORTUNISTIC;
508}
509
510void op_create_privkey(void *opdata, const char *accountname,
511        const char *protocol)
512{
513        struct im_connection *ic = check_imc(opdata, accountname, protocol);
514        irc_t *irc = ic->bee->ui_data;
515       
516        /* will fail silently if keygen already in progress */
517        otr_keygen(irc, accountname, protocol);
518}
519
520int op_is_logged_in(void *opdata, const char *accountname,
521        const char *protocol, const char *recipient)
522{
523        struct im_connection *ic = check_imc(opdata, accountname, protocol);
524        bee_user_t *bu;
525
526        /* lookup the irc_user_t for the given recipient */
527        bu = bee_user_by_handle(ic->bee, ic, recipient);
528        if(bu) {
529                if(bu->flags & BEE_USER_ONLINE)
530                        return 1;
531                else
532                        return 0;
533        } else {
534                return -1;
535        }
536}
537
538void op_inject_message(void *opdata, const char *accountname,
539        const char *protocol, const char *recipient, const char *message)
540{
541        struct im_connection *ic = check_imc(opdata, accountname, protocol);
542        irc_t *irc = ic->bee->ui_data;
543
544        if (strcmp(accountname, recipient) == 0) {
545                /* huh? injecting messages to myself? */
546                irc_usermsg(irc, "note to self: %s", message);
547        } else {
548                /* need to drop some consts here :-( */
549                /* TODO: get flags into op_inject_message?! */
550                ic->acc->prpl->buddy_msg(ic, (char *)recipient, (char *)message, 0);
551                /* ignoring return value :-/ */
552        }
553}
554
555int op_display_otr_message(void *opdata, const char *accountname,
556        const char *protocol, const char *username, const char *message)
557{
558        struct im_connection *ic = check_imc(opdata, accountname, protocol);
559        char *msg = g_strdup(message);
560        irc_t *irc = ic->bee->ui_data;
561
562        strip_html(msg);
563        irc_usermsg(irc, "otr: %s", msg);
564
565        g_free(msg);
566        return 0;
567}
568
569void op_new_fingerprint(void *opdata, OtrlUserState us,
570        const char *accountname, const char *protocol,
571        const char *username, unsigned char fingerprint[20])
572{
573        struct im_connection *ic = check_imc(opdata, accountname, protocol);
574        irc_t *irc = ic->bee->ui_data;
575        char hunam[45];         /* anybody looking? ;-) */
576       
577        otrl_privkey_hash_to_human(hunam, fingerprint);
578        irc_usermsg(irc, "new fingerprint for %s: %s",
579                peernick(irc, username, protocol), hunam);
580}
581
582void op_write_fingerprints(void *opdata)
583{
584        struct im_connection *ic = (struct im_connection *)opdata;
585        irc_t *irc = ic->bee->ui_data;
586
587        otr_save(irc);
588}
589
590void op_gone_secure(void *opdata, ConnContext *context)
591{
592        struct im_connection *ic =
593                check_imc(opdata, context->accountname, context->protocol);
594        irc_user_t *u;
595        irc_t *irc = ic->bee->ui_data;
596        const char *trust;
597
598        u = peeruser(irc, context->username, context->protocol);
599        if(!u) {
600                log_message(LOGLVL_ERROR,
601                        "BUG: otr.c: op_gone_secure: irc_user_t for %s/%s/%s not found!",
602                        context->username, context->protocol, context->accountname);
603                return;
604        }
605       
606        trust = context->active_fingerprint->trust;
607        if(trust && trust[0])
608                u->flags |= IRC_USER_OTR_ENCRYPTED | IRC_USER_OTR_TRUSTED;
609        else
610                u->flags = ( u->flags & ~IRC_USER_OTR_TRUSTED ) | IRC_USER_OTR_ENCRYPTED;
611        if(!otr_update_modeflags(irc, u))
612                irc_usermsg(irc, "conversation with %s is now off the record", u->nick);
613}
614
615void op_gone_insecure(void *opdata, ConnContext *context)
616{
617        struct im_connection *ic =
618                check_imc(opdata, context->accountname, context->protocol);
619        irc_t *irc = ic->bee->ui_data;
620        irc_user_t *u;
621
622        u = peeruser(irc, context->username, context->protocol);
623        if(!u) {
624                log_message(LOGLVL_ERROR,
625                        "BUG: otr.c: op_gone_insecure: irc_user_t for %s/%s/%s not found!",
626                        context->username, context->protocol, context->accountname);
627                return;
628        }
629        u->flags &= ~( IRC_USER_OTR_ENCRYPTED | IRC_USER_OTR_TRUSTED );
630        if(!otr_update_modeflags(irc, u))
631                irc_usermsg(irc, "conversation with %s is now in the clear", u->nick);
632}
633
634void op_still_secure(void *opdata, ConnContext *context, int is_reply)
635{
636        struct im_connection *ic =
637                check_imc(opdata, context->accountname, context->protocol);
638        irc_t *irc = ic->bee->ui_data;
639        irc_user_t *u;
640
641        u = peeruser(irc, context->username, context->protocol);
642        if(!u) {
643                log_message(LOGLVL_ERROR,
644                        "BUG: otr.c: op_still_secure: irc_user_t for %s/%s/%s not found!",
645                        context->username, context->protocol, context->accountname);
646                return;
647        }
648        if(context->active_fingerprint->trust[0])
649                u->flags |= IRC_USER_OTR_ENCRYPTED | IRC_USER_OTR_TRUSTED;
650        else
651                u->flags = ( u->flags & ~IRC_USER_OTR_TRUSTED ) | IRC_USER_OTR_ENCRYPTED;
652        if(!otr_update_modeflags(irc, u))
653                irc_usermsg(irc, "otr connection with %s has been refreshed", u->nick);
654}
655
656void op_log_message(void *opdata, const char *message)
657{
658        char *msg = g_strdup(message);
659       
660        strip_html(msg);
661        log_message(LOGLVL_INFO, "otr: %s", msg);
662        g_free(msg);
663}
664
665int op_max_message_size(void *opdata, ConnContext *context)
666{
667        struct im_connection *ic =
668                check_imc(opdata, context->accountname, context->protocol);
669
670        return ic->acc->prpl->mms;
671}
672
673const char *op_account_name(void *opdata, const char *account, const char *protocol)
674{
675        struct im_connection *ic = (struct im_connection *)opdata;
676        irc_t *irc = ic->bee->ui_data;
677
678        return peernick(irc, account, protocol);
679}
680
681
682/*** OTR sub-command handlers ***/
683
684void cmd_otr_disconnect(irc_t *irc, char **args)
685{
686        irc_user_t *u;
687
688        u = irc_user_by_name(irc, args[1]);
689        if(!u || !u->bu || !u->bu->ic) {
690                irc_usermsg(irc, "%s: unknown user", args[1]);
691                return;
692        }
693       
694        otrl_message_disconnect(irc->otr->us, &otr_ops,
695                u->bu->ic, u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, u->bu->handle);
696       
697        /* for some reason, libotr (3.1.0) doesn't do this itself: */
698        if(u->flags & IRC_USER_OTR_ENCRYPTED) {
699                ConnContext *ctx;
700                ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
701                        u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
702                if(ctx)
703                        op_gone_insecure(u->bu->ic, ctx);
704                else /* huh? */
705                        u->flags &= ( IRC_USER_OTR_ENCRYPTED | IRC_USER_OTR_TRUSTED );
706        }
707}
708
709void cmd_otr_connect(irc_t *irc, char **args)
710{
711        irc_user_t *u;
712
713        u = irc_user_by_name(irc, args[1]);
714        if(!u || !u->bu || !u->bu->ic) {
715                irc_usermsg(irc, "%s: unknown user", args[1]);
716                return;
717        }
718        if(!(u->bu->flags & BEE_USER_ONLINE)) {
719                irc_usermsg(irc, "%s is offline", args[1]);
720                return;
721        }
722       
723        bee_user_msg(irc->b, u->bu, "?OTR?", 0);
724}
725
726void cmd_otr_smp(irc_t *irc, char **args)
727{
728        irc_user_t *u;
729        ConnContext *ctx;
730       
731        u = irc_user_by_name(irc, args[1]);
732        if(!u || !u->bu || !u->bu->ic) {
733                irc_usermsg(irc, "%s: unknown user", args[1]);
734                return;
735        }
736        if(!(u->bu->flags & BEE_USER_ONLINE)) {
737                irc_usermsg(irc, "%s is offline", args[1]);
738                return;
739        }
740       
741        ctx = otrl_context_find(irc->otr->us, u->bu->handle,
742                u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, 1, NULL, NULL, NULL);
743        if(!ctx) {
744                /* huh? out of memory or what? */
745                return;
746        }
747
748        if(ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) {
749                log_message(LOGLVL_INFO,
750                        "SMP already in phase %d, sending abort before reinitiating",
751                        ctx->smstate->nextExpected+1);
752                otrl_message_abort_smp(irc->otr->us, &otr_ops, u->bu->ic, ctx);
753                otrl_sm_state_free(ctx->smstate);
754        }
755       
756        /* warning: the following assumes that smstates are cleared whenever an SMP
757           is completed or aborted! */ 
758        if(ctx->smstate->secret == NULL) {
759                irc_usermsg(irc, "smp: initiating with %s...", u->nick);
760                otrl_message_initiate_smp(irc->otr->us, &otr_ops,
761                        u->bu->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
762                /* smp is now in EXPECT2 */
763        } else {
764                /* if we're still in EXPECT1 but smstate is initialized, we must have
765                   received the SMP1, so let's issue a response */
766                irc_usermsg(irc, "smp: responding to %s...", u->nick);
767                otrl_message_respond_smp(irc->otr->us, &otr_ops,
768                        u->bu->ic, ctx, (unsigned char *)args[2], strlen(args[2]));
769                /* smp is now in EXPECT3 */
770        }
771}
772
773void cmd_otr_trust(irc_t *irc, char **args)
774{
775        irc_user_t *u;
776        ConnContext *ctx;
777        unsigned char raw[20];
778        Fingerprint *fp;
779        int i,j;
780       
781        u = irc_user_by_name(irc, args[1]);
782        if(!u || !u->bu || !u->bu->ic) {
783                irc_usermsg(irc, "%s: unknown user", args[1]);
784                return;
785        }
786       
787        ctx = otrl_context_find(irc->otr->us, u->bu->handle,
788                u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
789        if(!ctx) {
790                irc_usermsg(irc, "%s: no otr context with user", args[1]);
791                return;
792        }
793       
794        /* convert given fingerprint to raw representation */
795        for(i=0; i<5; i++) {
796                for(j=0; j<4; j++) {
797                        char *p = args[2+i]+(2*j);
798                        char *q = p+1;
799                        int x, y;
800                       
801                        if(!*p || !*q) {
802                                irc_usermsg(irc, "failed: truncated fingerprint block %d", i+1);
803                                return;
804                        }
805                       
806                        x = hexval(*p);
807                        y = hexval(*q);
808                        if(x<0) {
809                                irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+1, i+1);
810                                return;
811                        }
812                        if(y<0) {
813                                irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+2, i+1);
814                                return;
815                        }
816
817                        raw[i*4+j] = x*16 + y;
818                }
819        }
820        fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL);
821        if(!fp) {
822                irc_usermsg(irc, "failed: no such fingerprint for %s", args[1]);
823        } else {
824                char *trust = args[7] ? args[7] : "affirmed";
825                otrl_context_set_trust(fp, trust);
826                irc_usermsg(irc, "fingerprint match, trust set to \"%s\"", trust);
827                if(u->flags & IRC_USER_OTR_ENCRYPTED)
828                        u->flags |= IRC_USER_OTR_TRUSTED;
829                otr_update_modeflags(irc, u);
830        }
831}
832
833void cmd_otr_info(irc_t *irc, char **args)
834{
835        if(!args[1]) {
836                show_general_otr_info(irc);
837        } else {
838                char *arg = g_strdup(args[1]);
839                char *myhandle, *handle=NULL, *protocol;
840                ConnContext *ctx;
841               
842                /* interpret arg as 'user/protocol/account' if possible */
843                protocol = strchr(arg, '/');
844                myhandle = NULL;
845                if(protocol) {
846                        *(protocol++) = '\0';
847                        myhandle = strchr(protocol, '/');
848                }
849                if(protocol && myhandle) {
850                        *(myhandle++) = '\0';
851                        handle = arg;
852                        ctx = otrl_context_find(irc->otr->us, handle, myhandle, protocol, 0, NULL, NULL, NULL);
853                        if(!ctx) {
854                                irc_usermsg(irc, "no such context");
855                                g_free(arg);
856                                return;
857                        }
858                } else {
859                        irc_user_t *u = irc_user_by_name(irc, args[1]);
860                        if(!u || !u->bu || !u->bu->ic) {
861                                irc_usermsg(irc, "%s: unknown user", args[1]);
862                                g_free(arg);
863                                return;
864                        }
865                        ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
866                                u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
867                        if(!ctx) {
868                                irc_usermsg(irc, "no otr context with %s", args[1]);
869                                g_free(arg);
870                                return;
871                        }
872                }
873       
874                /* show how we resolved the (nick) argument, if we did */
875                if(handle!=arg) {
876                        irc_usermsg(irc, "%s is %s/%s; we are %s/%s to them", args[1],
877                                ctx->username, ctx->protocol, ctx->accountname, ctx->protocol);
878                }
879                show_otr_context_info(irc, ctx);
880                g_free(arg);
881        }
882}
883
884void cmd_otr_keygen(irc_t *irc, char **args)
885{
886        int i, n;
887        account_t *a;
888       
889        n = atoi(args[1]);
890        if(n<0 || (!n && strcmp(args[1], "0"))) {
891                irc_usermsg(irc, "%s: invalid account number", args[1]);
892                return;
893        }
894       
895        a = irc->b->accounts;
896        for(i=0; i<n && a; i++, a=a->next);
897        if(!a) {
898                irc_usermsg(irc, "%s: no such account", args[1]);
899                return;
900        }
901       
902        if(keygen_in_progress(irc, a->user, a->prpl->name)) {
903                irc_usermsg(irc, "keygen for account %d already in progress", n);
904                return;
905        }
906       
907        if(otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) {
908                char *s = g_strdup_printf("account %d already has a key, replace it?", n);
909                query_add(irc, NULL, s, yes_keygen, NULL, NULL, a);
910                g_free(s);
911        } else {
912                otr_keygen(irc, a->user, a->prpl->name);
913        }
914}
915
916void yes_forget_fingerprint(void *data)
917{
918        pair_t *p = (pair_t *)data;
919        irc_t *irc = (irc_t *)p->fst;
920        Fingerprint *fp = (Fingerprint *)p->snd;
921
922        g_free(p);
923       
924        if(fp == fp->context->active_fingerprint) {
925                irc_usermsg(irc, "that fingerprint is active, terminate otr connection first");
926                return;
927        }
928               
929        otrl_context_forget_fingerprint(fp, 0);
930}
931
932void yes_forget_context(void *data)
933{
934        pair_t *p = (pair_t *)data;
935        irc_t *irc = (irc_t *)p->fst;
936        ConnContext *ctx = (ConnContext *)p->snd;
937
938        g_free(p);
939       
940        if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
941                irc_usermsg(irc, "active otr connection with %s, terminate it first",
942                        peernick(irc, ctx->username, ctx->protocol));
943                return;
944        }
945               
946        if(ctx->msgstate == OTRL_MSGSTATE_FINISHED)
947                otrl_context_force_plaintext(ctx);
948        otrl_context_forget(ctx);
949}
950
951void yes_forget_key(void *data)
952{
953        OtrlPrivKey *key = (OtrlPrivKey *)data;
954       
955        otrl_privkey_forget(key);
956        /* Hm, libotr doesn't seem to offer a function for explicitly /writing/
957           keyfiles. So the key will be back on the next load... */
958        /* TODO: Actually erase forgotten keys from storage? */
959}
960
961void cmd_otr_forget(irc_t *irc, char **args)
962{
963        if(!strcmp(args[1], "fingerprint"))
964        {
965                irc_user_t *u;
966                ConnContext *ctx;
967                Fingerprint *fp;
968                char human[54];
969                char *s;
970                pair_t *p;
971               
972                if(!args[3]) {
973                        irc_usermsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]);
974                        return;
975                }
976               
977                /* TODO: allow context specs ("user/proto/account") in 'otr forget fingerprint'? */
978                u = irc_user_by_name(irc, args[2]);
979                if(!u || !u->bu || !u->bu->ic) {
980                        irc_usermsg(irc, "%s: unknown user", args[2]);
981                        return;
982                }
983               
984                ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
985                        u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
986                if(!ctx) {
987                        irc_usermsg(irc, "no otr context with %s", args[2]);
988                        return;
989                }
990               
991                fp = match_fingerprint(irc, ctx, ((const char **)args)+3);
992                if(!fp) {
993                        /* match_fingerprint does error messages */
994                        return;
995                }
996               
997                if(fp == ctx->active_fingerprint) {
998                        irc_usermsg(irc, "that fingerprint is active, terminate otr connection first");
999                        return;
1000                }
1001               
1002                otrl_privkey_hash_to_human(human, fp->fingerprint);
1003                s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human);
1004                p = g_malloc(sizeof(pair_t));
1005                if(!p)
1006                        return;
1007                p->fst = irc;
1008                p->snd = fp;
1009                query_add(irc, NULL, s, yes_forget_fingerprint, NULL, NULL, p);
1010                g_free(s);
1011        }
1012       
1013        else if(!strcmp(args[1], "context"))
1014        {
1015                irc_user_t *u;
1016                ConnContext *ctx;
1017                char *s;
1018                pair_t *p;
1019               
1020                /* TODO: allow context specs ("user/proto/account") in 'otr forget contex'? */
1021                u = irc_user_by_name(irc, args[2]);
1022                if(!u || !u->bu || !u->bu->ic) {
1023                        irc_usermsg(irc, "%s: unknown user", args[2]);
1024                        return;
1025                }
1026               
1027                ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
1028                        u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
1029                if(!ctx) {
1030                        irc_usermsg(irc, "no otr context with %s", args[2]);
1031                        return;
1032                }
1033               
1034                if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
1035                        irc_usermsg(irc, "active otr connection with %s, terminate it first", args[2]);
1036                        return;
1037                }
1038               
1039                s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]);
1040                p = g_malloc(sizeof(pair_t));
1041                if(!p)
1042                        return;
1043                p->fst = irc;
1044                p->snd = ctx;
1045                query_add(irc, NULL, s, yes_forget_context, NULL, NULL, p);
1046                g_free(s);
1047        }
1048       
1049        else if(!strcmp(args[1], "key"))
1050        {
1051                OtrlPrivKey *key;
1052                char *s;
1053               
1054                key = match_privkey(irc, ((const char **)args)+2);
1055                if(!key) {
1056                        /* match_privkey does error messages */
1057                        return;
1058                }
1059               
1060                s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?",
1061                        key->accountname, key->protocol);
1062                query_add(irc, NULL, s, yes_forget_key, NULL, NULL, key);
1063                g_free(s);
1064        }
1065       
1066        else
1067        {
1068                irc_usermsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02",
1069                        args[0], args[1]);
1070        }
1071}
1072
1073
1074/*** local helpers / subroutines: ***/
1075
1076/* Socialist Millionaires' Protocol */
1077void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs)
1078{
1079        irc_t *irc = ic->bee->ui_data;
1080        OtrlUserState us = irc->otr->us;
1081        OtrlMessageAppOps *ops = &otr_ops;
1082        OtrlTLV *tlv = NULL;
1083        ConnContext *context;
1084        NextExpectedSMP nextMsg;
1085        irc_user_t *u;
1086        bee_user_t *bu;
1087
1088        bu = bee_user_by_handle(ic->bee, ic, handle);
1089        if(!bu || !(u = bu->ui_data)) return;
1090        context = otrl_context_find(us, handle,
1091                ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL);
1092        if(!context) {
1093                /* huh? out of memory or what? */
1094                return;
1095        }
1096        nextMsg = context->smstate->nextExpected;
1097
1098        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
1099        if (tlv) {
1100                if (nextMsg != OTRL_SMP_EXPECT1) {
1101                        irc_usermsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick);
1102                        otrl_message_abort_smp(us, ops, u->bu->ic, context);
1103                        otrl_sm_state_free(context->smstate);
1104                } else {
1105                        irc_usermsg(irc, "smp: initiated by %s"
1106                                " - respond with \x02otr smp %s <secret>\x02",
1107                                u->nick, u->nick);
1108                        /* smp stays in EXPECT1 until user responds */
1109                }
1110        }
1111        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
1112        if (tlv) {
1113                if (nextMsg != OTRL_SMP_EXPECT2) {
1114                        irc_usermsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick);
1115                        otrl_message_abort_smp(us, ops, u->bu->ic, context);
1116                        otrl_sm_state_free(context->smstate);
1117                } else {
1118                        /* SMP2 received, otrl_message_receiving will have sent SMP3 */
1119                        context->smstate->nextExpected = OTRL_SMP_EXPECT4;
1120                }
1121        }
1122        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
1123        if (tlv) {
1124                if (nextMsg != OTRL_SMP_EXPECT3) {
1125                        irc_usermsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick);
1126                        otrl_message_abort_smp(us, ops, u->bu->ic, context);
1127                        otrl_sm_state_free(context->smstate);
1128                } else {
1129                        /* SMP3 received, otrl_message_receiving will have sent SMP4 and set fp trust */
1130                        const char *trust = context->active_fingerprint->trust;
1131                        if(!trust || trust[0]=='\0') {
1132                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1133                                        u->nick);
1134                        } else {
1135                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1136                                        u->nick);
1137                        }
1138                        otrl_sm_state_free(context->smstate);
1139                        /* smp is in back in EXPECT1 */
1140                }
1141        }
1142        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
1143        if (tlv) {
1144                if (nextMsg != OTRL_SMP_EXPECT4) {
1145                        irc_usermsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick);
1146                        otrl_message_abort_smp(us, ops, u->bu->ic, context);
1147                        otrl_sm_state_free(context->smstate);
1148                } else {
1149                        /* SMP4 received, otrl_message_receiving will have set fp trust */
1150                        const char *trust = context->active_fingerprint->trust;
1151                        if(!trust || trust[0]=='\0') {
1152                                irc_usermsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1153                                        u->nick);
1154                        } else {
1155                                irc_usermsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1156                                        u->nick);
1157                        }
1158                        otrl_sm_state_free(context->smstate);
1159                        /* smp is in back in EXPECT1 */
1160                }
1161        }
1162        tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
1163        if (tlv) {
1164                irc_usermsg(irc, "smp: received abort from %s", u->nick);
1165                otrl_sm_state_free(context->smstate);
1166                /* smp is in back in EXPECT1 */
1167        }
1168}
1169
1170/* helper to assert that account and protocol names given to ops below always
1171   match the im_connection passed through as opdata */
1172struct im_connection *check_imc(void *opdata, const char *accountname,
1173        const char *protocol)
1174{
1175        struct im_connection *ic = (struct im_connection *)opdata;
1176
1177        if (strcmp(accountname, ic->acc->user) != 0) {
1178                log_message(LOGLVL_WARNING,
1179                        "otr: internal account name mismatch: '%s' vs '%s'",
1180                        accountname, ic->acc->user);
1181        }
1182        if (strcmp(protocol, ic->acc->prpl->name) != 0) {
1183                log_message(LOGLVL_WARNING,
1184                        "otr: internal protocol name mismatch: '%s' vs '%s'",
1185                        protocol, ic->acc->prpl->name);
1186        }
1187       
1188        return ic;
1189}
1190
1191irc_user_t *peeruser(irc_t *irc, const char *handle, const char *protocol)
1192{
1193        GSList *l;
1194       
1195        for(l=irc->b->users; l; l = l->next) {
1196                bee_user_t *bu = l->data;
1197                struct prpl *prpl;
1198                if(!bu->ui_data || !bu->ic || !bu->handle)
1199                        continue;
1200                prpl = bu->ic->acc->prpl;
1201                if(strcmp(prpl->name, protocol) == 0
1202                        && prpl->handle_cmp(bu->handle, handle) == 0) {
1203                        return bu->ui_data;
1204                }
1205        }
1206       
1207        return NULL;
1208}
1209
1210int hexval(char a)
1211{
1212        int x=tolower(a);
1213       
1214        if(x>='a' && x<='f')
1215                x = x - 'a' + 10;
1216        else if(x>='0' && x<='9')
1217                x = x - '0';
1218        else
1219                return -1;
1220       
1221        return x;
1222}
1223
1224const char *peernick(irc_t *irc, const char *handle, const char *protocol)
1225{
1226        static char fallback[512];
1227       
1228        irc_user_t *u = peeruser(irc, handle, protocol);
1229        if(u) {
1230                return u->nick;
1231        } else {
1232                g_snprintf(fallback, 511, "%s/%s", handle, protocol);
1233                return fallback;
1234        }
1235}
1236
1237int otr_update_modeflags(irc_t *irc, irc_user_t *u)
1238{
1239        return 1;
1240}
1241
1242void show_fingerprints(irc_t *irc, ConnContext *ctx)
1243{
1244        char human[45];
1245        Fingerprint *fp;
1246        const char *trust;
1247        int count=0;
1248       
1249        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1250                if(!fp->fingerprint)
1251                        continue;
1252                count++;
1253                otrl_privkey_hash_to_human(human, fp->fingerprint);
1254                if(!fp->trust || fp->trust[0] == '\0') {
1255                        trust="untrusted";
1256                } else {
1257                        trust=fp->trust;
1258                }
1259                if(fp == ctx->active_fingerprint) {
1260                        irc_usermsg(irc, "    \x02%s (%s)\x02", human, trust);
1261                } else {
1262                        irc_usermsg(irc, "    %s (%s)", human, trust);
1263                }
1264        }
1265        if(count==0)
1266                irc_usermsg(irc, "    (none)");
1267}
1268
1269Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args)
1270{
1271        Fingerprint *fp, *fp2;
1272        char human[45];
1273        char prefix[45], *p;
1274        int n;
1275        int i,j;
1276       
1277        /* assemble the args into a prefix in standard "human" form */
1278        n=0;
1279        p=prefix;
1280        for(i=0; args[i]; i++) {
1281                for(j=0; args[i][j]; j++) {
1282                        char c = toupper(args[i][j]);
1283                       
1284                        if(n>=40) {
1285                                irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
1286                                return NULL;
1287                        }
1288                       
1289                        if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1290                                *(p++) = c;
1291                        } else {
1292                                irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1293                                return NULL;
1294                        }
1295                       
1296                        n++;
1297                        if(n%8 == 0)
1298                                *(p++) = ' ';
1299                }
1300        }
1301        *p = '\0';
1302       
1303        /* find first fingerprint with the given prefix */
1304        n = strlen(prefix);
1305        for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1306                if(!fp->fingerprint)
1307                        continue;
1308                otrl_privkey_hash_to_human(human, fp->fingerprint);
1309                if(!strncmp(prefix, human, n))
1310                        break;
1311        }
1312        if(!fp) {
1313                irc_usermsg(irc, "%s: no match", prefix);
1314                return NULL;
1315        }
1316       
1317        /* make sure the match, if any, is unique */
1318        for(fp2=fp->next; fp2; fp2=fp2->next) {
1319                if(!fp2->fingerprint)
1320                        continue;
1321                otrl_privkey_hash_to_human(human, fp2->fingerprint);
1322                if(!strncmp(prefix, human, n))
1323                        break;
1324        }
1325        if(fp2) {
1326                irc_usermsg(irc, "%s: multiple matches", prefix);
1327                return NULL;
1328        }
1329       
1330        return fp;
1331}
1332
1333OtrlPrivKey *match_privkey(irc_t *irc, const char **args)
1334{
1335        OtrlPrivKey *k, *k2;
1336        char human[45];
1337        char prefix[45], *p;
1338        int n;
1339        int i,j;
1340       
1341        /* assemble the args into a prefix in standard "human" form */
1342        n=0;
1343        p=prefix;
1344        for(i=0; args[i]; i++) {
1345                for(j=0; args[i][j]; j++) {
1346                        char c = toupper(args[i][j]);
1347                       
1348                        if(n>=40) {
1349                                irc_usermsg(irc, "too many fingerprint digits given, expected at most 40");
1350                                return NULL;
1351                        }
1352                       
1353                        if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1354                                *(p++) = c;
1355                        } else {
1356                                irc_usermsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1357                                return NULL;
1358                        }
1359                       
1360                        n++;
1361                        if(n%8 == 0)
1362                                *(p++) = ' ';
1363                }
1364        }
1365        *p = '\0';
1366       
1367        /* find first key which matches the given prefix */
1368        n = strlen(prefix);
1369        for(k=irc->otr->us->privkey_root; k; k=k->next) {
1370                p = otrl_privkey_fingerprint(irc->otr->us, human, k->accountname, k->protocol);
1371                if(!p) /* gah! :-P */
1372                        continue;
1373                if(!strncmp(prefix, human, n))
1374                        break;
1375        }
1376        if(!k) {
1377                irc_usermsg(irc, "%s: no match", prefix);
1378                return NULL;
1379        }
1380       
1381        /* make sure the match, if any, is unique */
1382        for(k2=k->next; k2; k2=k2->next) {
1383                p = otrl_privkey_fingerprint(irc->otr->us, human, k2->accountname, k2->protocol);
1384                if(!p) /* gah! :-P */
1385                        continue;
1386                if(!strncmp(prefix, human, n))
1387                        break;
1388        }
1389        if(k2) {
1390                irc_usermsg(irc, "%s: multiple matches", prefix);
1391                return NULL;
1392        }
1393       
1394        return k;
1395}
1396
1397void show_general_otr_info(irc_t *irc)
1398{
1399        ConnContext *ctx;
1400        OtrlPrivKey *key;
1401        char human[45];
1402        kg_t *kg;
1403
1404        /* list all privkeys (including ones being generated) */
1405        irc_usermsg(irc, "\x1fprivate keys:\x1f");
1406        for(key=irc->otr->us->privkey_root; key; key=key->next) {
1407                const char *hash;
1408               
1409                switch(key->pubkey_type) {
1410                case OTRL_PUBKEY_TYPE_DSA:
1411                        irc_usermsg(irc, "  %s/%s - DSA", key->accountname, key->protocol);
1412                        break;
1413                default:
1414                        irc_usermsg(irc, "  %s/%s - type %d", key->accountname, key->protocol,
1415                                key->pubkey_type);
1416                }
1417
1418                /* No, it doesn't make much sense to search for the privkey again by
1419                   account/protocol, but libotr currently doesn't provide a direct routine
1420                   for hashing a given 'OtrlPrivKey'... */
1421                hash = otrl_privkey_fingerprint(irc->otr->us, human, key->accountname, key->protocol);
1422                if(hash) /* should always succeed */
1423                        irc_usermsg(irc, "    %s", human);
1424        }
1425        if(irc->otr->sent_accountname) {
1426                irc_usermsg(irc, "  %s/%s - DSA", irc->otr->sent_accountname,
1427                        irc->otr->sent_protocol);
1428                irc_usermsg(irc, "    (being generated)");
1429        }
1430        for(kg=irc->otr->todo; kg; kg=kg->next) {
1431                irc_usermsg(irc, "  %s/%s - DSA", kg->accountname, kg->protocol);
1432                irc_usermsg(irc, "    (queued)");
1433        }
1434        if(key == irc->otr->us->privkey_root &&
1435           !irc->otr->sent_accountname &&
1436           kg == irc->otr->todo)
1437                irc_usermsg(irc, "  (none)");
1438
1439        /* list all contexts */
1440        irc_usermsg(irc, "%s", "");
1441        irc_usermsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)");
1442        for(ctx=irc->otr->us->context_root; ctx; ctx=ctx->next) {\
1443                irc_user_t *u;
1444                char *userstring;
1445               
1446                u = peeruser(irc, ctx->username, ctx->protocol);
1447                if(u)
1448                        userstring = g_strdup_printf("%s/%s/%s (%s)",
1449                                ctx->username, ctx->protocol, ctx->accountname, u->nick);
1450                else
1451                        userstring = g_strdup_printf("%s/%s/%s",
1452                                ctx->username, ctx->protocol, ctx->accountname);
1453               
1454                if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
1455                        irc_usermsg(irc, \x02%s\x02", userstring);
1456                } else {
1457                        irc_usermsg(irc, "  %s", userstring);
1458                }
1459               
1460                g_free(userstring);
1461        }
1462        if(ctx == irc->otr->us->context_root)
1463                irc_usermsg(irc, "  (none)");
1464}
1465
1466void show_otr_context_info(irc_t *irc, ConnContext *ctx)
1467{
1468        switch(ctx->otr_offer) {
1469        case OFFER_NOT:
1470                irc_usermsg(irc, "  otr offer status: none sent");
1471                break;
1472        case OFFER_SENT:
1473                irc_usermsg(irc, "  otr offer status: awaiting reply");
1474                break;
1475        case OFFER_ACCEPTED:
1476                irc_usermsg(irc, "  otr offer status: accepted our offer");
1477                break;
1478        case OFFER_REJECTED:
1479                irc_usermsg(irc, "  otr offer status: ignored our offer");
1480                break;
1481        default:
1482                irc_usermsg(irc, "  otr offer status: %d", ctx->otr_offer);
1483        }
1484
1485        switch(ctx->msgstate) {
1486        case OTRL_MSGSTATE_PLAINTEXT:
1487                irc_usermsg(irc, "  connection state: cleartext");
1488                break;
1489        case OTRL_MSGSTATE_ENCRYPTED:
1490                irc_usermsg(irc, "  connection state: encrypted (v%d)", ctx->protocol_version);
1491                break;
1492        case OTRL_MSGSTATE_FINISHED:
1493                irc_usermsg(irc, "  connection state: shut down");
1494                break;
1495        default:
1496                irc_usermsg(irc, "  connection state: %d", ctx->msgstate);
1497        }
1498
1499        irc_usermsg(irc, "  fingerprints: (bold=active)");     
1500        show_fingerprints(irc, ctx);
1501}
1502
1503int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol)
1504{
1505        kg_t *kg;
1506       
1507        if(!irc->otr->sent_accountname || !irc->otr->sent_protocol)
1508                return 0;
1509
1510        /* are we currently working on this key? */
1511        if(!strcmp(handle, irc->otr->sent_accountname) &&
1512           !strcmp(protocol, irc->otr->sent_protocol))
1513                return 1;
1514       
1515        /* do we have it queued for later? */
1516        for(kg=irc->otr->todo; kg; kg=kg->next) {
1517                if(!strcmp(handle, kg->accountname) &&
1518                   !strcmp(protocol, kg->protocol))
1519                        return 1;
1520        }
1521       
1522        return 0;
1523}
1524
1525void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
1526{
1527        /* do nothing if a key for the requested account is already being generated */
1528        if(keygen_in_progress(irc, handle, protocol))
1529                return;
1530
1531        /* see if we already have a keygen child running. if not, start one and put a
1532           handler on its output. */
1533        if(!irc->otr->keygen || waitpid(irc->otr->keygen, NULL, WNOHANG)) {
1534                pid_t p;
1535                int to[2], from[2];
1536                FILE *tof, *fromf;
1537               
1538                if(pipe(to) < 0 || pipe(from) < 0) {
1539                        irc_usermsg(irc, "otr keygen: couldn't create pipe: %s", strerror(errno));
1540                        return;
1541                }
1542               
1543                tof = fdopen(to[1], "w");
1544                fromf = fdopen(from[0], "r");
1545                if(!tof || !fromf) {
1546                        irc_usermsg(irc, "otr keygen: couldn't streamify pipe: %s", strerror(errno));
1547                        return;
1548                }
1549               
1550                p = fork();
1551                if(p<0) {
1552                        irc_usermsg(irc, "otr keygen: couldn't fork: %s", strerror(errno));
1553                        return;
1554                }
1555               
1556                if(!p) {
1557                        /* child process */
1558                        signal(SIGTERM, exit);
1559                        keygen_child_main(irc->otr->us, to[0], from[1]);
1560                        exit(0);
1561                }
1562               
1563                irc->otr->keygen = p;
1564                irc->otr->to = tof;
1565                irc->otr->from = fromf;
1566                irc->otr->sent_accountname = NULL;
1567                irc->otr->sent_protocol = NULL;
1568                irc->otr->todo = NULL;
1569                b_input_add(from[0], B_EV_IO_READ, keygen_finish_handler, irc);
1570        }
1571       
1572        /* is the keygen slave currently working? */
1573        if(irc->otr->sent_accountname) {
1574                /* enqueue our job for later transmission */
1575                kg_t **kg = &irc->otr->todo;
1576                while(*kg)
1577                        kg=&((*kg)->next);
1578                *kg = g_new0(kg_t, 1);
1579                (*kg)->accountname = g_strdup(handle);
1580                (*kg)->protocol = g_strdup(protocol);
1581        } else {
1582                /* send our job over and remember it */
1583                fprintf(irc->otr->to, "%s\n%s\n", handle, protocol);
1584                fflush(irc->otr->to);
1585                irc->otr->sent_accountname = g_strdup(handle);
1586                irc->otr->sent_protocol = g_strdup(protocol);
1587        }
1588}
1589
1590void keygen_child_main(OtrlUserState us, int infd, int outfd)
1591{
1592        FILE *input, *output;
1593        char filename[128], accountname[512], protocol[512];
1594        gcry_error_t e;
1595        int tempfd;
1596       
1597        input = fdopen(infd, "r");
1598        output = fdopen(outfd, "w");
1599       
1600        while(!feof(input) && !ferror(input) && !feof(output) && !ferror(output)) {
1601                myfgets(accountname, 512, input);
1602                myfgets(protocol, 512, input);
1603               
1604                strncpy(filename, "/tmp/bitlbee-XXXXXX", 128);
1605                tempfd = mkstemp(filename);
1606                close(tempfd);
1607
1608                e = otrl_privkey_generate(us, filename, accountname, protocol);
1609                if(e) {
1610                        fprintf(output, "\n");  /* this means failure */
1611                        fprintf(output, "otr keygen: %s\n", gcry_strerror(e));
1612                        unlink(filename);
1613                } else {
1614                        fprintf(output, "%s\n", filename);
1615                        fprintf(output, "otr keygen for %s/%s complete\n", accountname, protocol);
1616                }
1617                fflush(output);
1618        }
1619       
1620        fclose(input);
1621        fclose(output);
1622}
1623
1624gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
1625{
1626        irc_t *irc = (irc_t *)data;
1627        char filename[512], msg[512];
1628
1629        myfgets(filename, 512, irc->otr->from);
1630        myfgets(msg, 512, irc->otr->from);
1631       
1632        irc_usermsg(irc, "%s", msg);
1633        if(filename[0]) {
1634                char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->user->nick);
1635                char *tmp = g_strdup_printf("%s.new", kf);
1636                copyfile(filename, tmp);
1637                unlink(filename);
1638                rename(tmp,kf);
1639                otrl_privkey_read(irc->otr->us, kf);
1640                g_free(kf);
1641                g_free(tmp);
1642        }
1643       
1644        /* forget this job */
1645        g_free(irc->otr->sent_accountname);
1646        g_free(irc->otr->sent_protocol);
1647        irc->otr->sent_accountname = NULL;
1648        irc->otr->sent_protocol = NULL;
1649       
1650        /* see if there are any more in the queue */
1651        if(irc->otr->todo) {
1652                kg_t *p = irc->otr->todo;
1653                /* send the next one over */
1654                fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol);
1655                fflush(irc->otr->to);
1656                irc->otr->sent_accountname = p->accountname;
1657                irc->otr->sent_protocol = p->protocol;
1658                irc->otr->todo = p->next;
1659                g_free(p);
1660                return TRUE;   /* keep watching */
1661        } else {
1662                /* okay, the slave is idle now, so kill him */
1663                fclose(irc->otr->from);
1664                fclose(irc->otr->to);
1665                kill(irc->otr->keygen, SIGTERM);
1666                waitpid(irc->otr->keygen, NULL, 0);
1667                irc->otr->keygen = 0;
1668                return FALSE;  /* unregister ourselves */
1669        }
1670}
1671
1672void copyfile(const char *a, const char *b)
1673{
1674        int fda, fdb;
1675        int n;
1676        char buf[1024];
1677       
1678        fda = open(a, O_RDONLY);
1679        fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1680       
1681        while((n=read(fda, buf, 1024)) > 0)
1682                write(fdb, buf, n);
1683       
1684        close(fda);
1685        close(fdb);     
1686}
1687
1688void myfgets(char *s, int size, FILE *stream)
1689{
1690        if(!fgets(s, size, stream)) {
1691                s[0] = '\0';
1692        } else {
1693                int n = strlen(s);
1694                if(n>0 && s[n-1] == '\n')
1695                        s[n-1] = '\0';
1696        }
1697}
1698
1699void yes_keygen(void *data)
1700{
1701        account_t *acc = (account_t *)data;
1702        irc_t *irc = acc->bee->ui_data;
1703       
1704        if(keygen_in_progress(irc, acc->user, acc->prpl->name)) {
1705                irc_usermsg(irc, "keygen for %s/%s already in progress",
1706                        acc->user, acc->prpl->name);
1707        } else {
1708                irc_usermsg(irc, "starting background keygen for %s/%s",
1709                        acc->user, acc->prpl->name);
1710                irc_usermsg(irc, "you will be notified when it completes");
1711                otr_keygen(irc, acc->user, acc->prpl->name);
1712        }
1713}
1714
1715
1716#else /* WITH_OTR undefined */
1717
1718void cmd_otr(irc_t *irc, char **args)
1719{
1720        irc_usermsg(irc, "otr: n/a, compiled without OTR support");
1721}
1722
1723#endif
Note: See TracBrowser for help on using the repository browser.