source: otr.c @ ef93a2f

Last change on this file since ef93a2f was ef93a2f, checked in by Sven Moritz Hallberg <sm@…>, at 2008-02-14T18:48:34Z

chmod 0600 otr save files

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