Try to show the user_split info for a protocol in "help purple $PROTOCOL".
[bitlbee.git] / otr.c
blob67e27474b3e2cf153ce7090754fb4f495cc841c0
1 /********************************************************************\
2 * BitlBee -- An IRC to other IM-networks gateway *
3 * *
4 * Copyright 2002-2010 Wilmer van der Gaast and others *
5 \********************************************************************/
7 /*
8 OTR support (cf. http://www.cypherpunks.ca/otr/)
10 (c) 2008-2011 Sven Moritz Hallberg <pesco@khjk.org>
11 (c) 2008 funded by stonedcoder.org
13 files used to store OTR data:
14 <configdir>/<nick>.otr_keys
15 <configdir>/<nick>.otr_fprints
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
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.
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.
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
40 #include "bitlbee.h"
41 #include "irc.h"
42 #include "otr.h"
43 #include <sys/types.h>
44 #include <sys/wait.h>
45 #include <unistd.h>
46 #include <assert.h>
47 #include <signal.h>
50 /** OTR interface routines for the OtrlMessageAppOps struct: **/
52 OtrlPolicy op_policy(void *opdata, ConnContext *context);
54 void op_create_privkey(void *opdata, const char *accountname, const char *protocol);
56 int op_is_logged_in(void *opdata, const char *accountname, const char *protocol,
57 const char *recipient);
59 void op_inject_message(void *opdata, const char *accountname, const char *protocol,
60 const char *recipient, const char *message);
62 int op_display_otr_message(void *opdata, const char *accountname, const char *protocol,
63 const char *username, const char *msg);
65 void op_new_fingerprint(void *opdata, OtrlUserState us, const char *accountname,
66 const char *protocol, const char *username, unsigned char fingerprint[20]);
68 void op_write_fingerprints(void *opdata);
70 void op_gone_secure(void *opdata, ConnContext *context);
72 void op_gone_insecure(void *opdata, ConnContext *context);
74 void op_still_secure(void *opdata, ConnContext *context, int is_reply);
76 void op_log_message(void *opdata, const char *message);
78 int op_max_message_size(void *opdata, ConnContext *context);
80 const char *op_account_name(void *opdata, const char *account, const char *protocol);
83 /** otr sub-command handlers: **/
85 static void cmd_otr(irc_t *irc, char **args);
86 void cmd_otr_connect(irc_t *irc, char **args);
87 void cmd_otr_disconnect(irc_t *irc, char **args);
88 void cmd_otr_reconnect(irc_t *irc, char **args);
89 void cmd_otr_smp(irc_t *irc, char **args);
90 void cmd_otr_smpq(irc_t *irc, char **args);
91 void cmd_otr_trust(irc_t *irc, char **args);
92 void cmd_otr_info(irc_t *irc, char **args);
93 void cmd_otr_keygen(irc_t *irc, char **args);
94 void cmd_otr_forget(irc_t *irc, char **args);
96 const command_t otr_commands[] = {
97 { "connect", 1, &cmd_otr_connect, 0 },
98 { "disconnect", 1, &cmd_otr_disconnect, 0 },
99 { "reconnect", 1, &cmd_otr_reconnect, 0 },
100 { "smp", 2, &cmd_otr_smp, 0 },
101 { "smpq", 3, &cmd_otr_smpq, 0 },
102 { "trust", 6, &cmd_otr_trust, 0 },
103 { "info", 0, &cmd_otr_info, 0 },
104 { "keygen", 1, &cmd_otr_keygen, 0 },
105 { "forget", 2, &cmd_otr_forget, 0 },
106 { NULL }
109 typedef struct {
110 void *fst;
111 void *snd;
112 } pair_t;
114 static OtrlMessageAppOps otr_ops; /* collects interface functions required by OTR */
117 /** misc. helpers/subroutines: **/
119 /* check whether we are already generating a key for a given account */
120 int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol);
122 /* start background process to generate a (new) key for a given account */
123 void otr_keygen(irc_t *irc, const char *handle, const char *protocol);
125 /* main function for the forked keygen slave */
126 void keygen_child_main(OtrlUserState us, int infd, int outfd);
128 /* mainloop handler for when a keygen finishes */
129 gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond);
131 /* copy the contents of file a to file b, overwriting it if it exists */
132 void copyfile(const char *a, const char *b);
134 /* read one line of input from a stream, excluding trailing newline */
135 void myfgets(char *s, int size, FILE *stream);
137 /* some yes/no handlers */
138 void yes_keygen(void *data);
139 void yes_forget_fingerprint(void *data);
140 void yes_forget_context(void *data);
141 void yes_forget_key(void *data);
143 /* helper to make sure accountname and protocol match the incoming "opdata" */
144 struct im_connection *check_imc(void *opdata, const char *accountname,
145 const char *protocol);
147 /* determine the nick for a given handle/protocol pair
148 returns "handle/protocol" if not found */
149 const char *peernick(irc_t *irc, const char *handle, const char *protocol);
151 /* turn a hexadecimal digit into its numerical value */
152 int hexval(char a);
154 /* determine the irc_user_t for a given handle/protocol pair
155 returns NULL if not found */
156 irc_user_t *peeruser(irc_t *irc, const char *handle, const char *protocol);
158 /* handle SMP TLVs from a received message */
159 void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs);
161 /* combined handler for the 'otr smp' and 'otr smpq' commands */
162 void otr_smp_or_smpq(irc_t *irc, const char *nick, const char *question,
163 const char *secret);
165 /* update flags within the irc_user structure to reflect OTR status of context */
166 void otr_update_uflags(ConnContext *context, irc_user_t *u);
168 /* update op/voice flag of given user according to encryption state and settings
169 returns 0 if neither op_buddies nor voice_buddies is set to "encrypted",
170 i.e. msgstate should be announced seperately */
171 int otr_update_modeflags(irc_t *irc, irc_user_t *u);
173 /* show general info about the OTR subsystem; called by 'otr info' */
174 void show_general_otr_info(irc_t *irc);
176 /* show info about a given OTR context */
177 void show_otr_context_info(irc_t *irc, ConnContext *ctx);
179 /* show the list of fingerprints associated with a given context */
180 void show_fingerprints(irc_t *irc, ConnContext *ctx);
182 /* find a fingerprint by prefix (given as any number of hex strings) */
183 Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args);
185 /* find a private key by fingerprint prefix (given as any number of hex strings) */
186 OtrlPrivKey *match_privkey(irc_t *irc, const char **args);
188 /* check whether a string is safe to use in a path component */
189 int strsane(const char *s);
191 /* functions to be called for certain events */
192 static const struct irc_plugin otr_plugin;
195 /*** routines declared in otr.h: ***/
197 #ifdef OTR_BI
198 #define init_plugin otr_init
199 #endif
201 void init_plugin(void)
203 OTRL_INIT;
205 /* fill global OtrlMessageAppOps */
206 otr_ops.policy = &op_policy;
207 otr_ops.create_privkey = &op_create_privkey;
208 otr_ops.is_logged_in = &op_is_logged_in;
209 otr_ops.inject_message = &op_inject_message;
210 otr_ops.notify = NULL;
211 otr_ops.display_otr_message = &op_display_otr_message;
212 otr_ops.update_context_list = NULL;
213 otr_ops.protocol_name = NULL;
214 otr_ops.protocol_name_free = NULL;
215 otr_ops.new_fingerprint = &op_new_fingerprint;
216 otr_ops.write_fingerprints = &op_write_fingerprints;
217 otr_ops.gone_secure = &op_gone_secure;
218 otr_ops.gone_insecure = &op_gone_insecure;
219 otr_ops.still_secure = &op_still_secure;
220 otr_ops.log_message = &op_log_message;
221 otr_ops.max_message_size = &op_max_message_size;
222 otr_ops.account_name = &op_account_name;
223 otr_ops.account_name_free = NULL;
225 root_command_add( "otr", 1, cmd_otr, 0 );
226 register_irc_plugin( &otr_plugin );
229 gboolean otr_irc_new(irc_t *irc)
231 set_t *s;
232 GSList *l;
234 irc->otr = g_new0(otr_t, 1);
235 irc->otr->us = otrl_userstate_create();
237 s = set_add( &irc->b->set, "otr_color_encrypted", "true", set_eval_bool, irc );
239 s = set_add( &irc->b->set, "otr_policy", "oppurtunistic", set_eval_list, irc );
240 l = g_slist_prepend( NULL, "never" );
241 l = g_slist_prepend( l, "opportunistic" );
242 l = g_slist_prepend( l, "manual" );
243 l = g_slist_prepend( l, "always" );
244 s->eval_data = l;
246 s = set_add( &irc->b->set, "otr_does_html", "true", set_eval_bool, irc );
248 return TRUE;
251 void otr_irc_free(irc_t *irc)
253 otr_t *otr = irc->otr;
254 otrl_userstate_free(otr->us);
255 if(otr->keygen) {
256 kill(otr->keygen, SIGTERM);
257 waitpid(otr->keygen, NULL, 0);
258 /* TODO: remove stale keygen tempfiles */
260 if(otr->to)
261 fclose(otr->to);
262 if(otr->from)
263 fclose(otr->from);
264 while(otr->todo) {
265 kg_t *p = otr->todo;
266 otr->todo = p->next;
267 g_free(p);
269 g_free(otr);
272 void otr_load(irc_t *irc)
274 char s[512];
275 account_t *a;
276 gcry_error_t e;
277 gcry_error_t enoent = gcry_error_from_errno(ENOENT);
278 int kg=0;
280 if(strsane(irc->user->nick)) {
281 g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, irc->user->nick);
282 e = otrl_privkey_read(irc->otr->us, s);
283 if(e && e!=enoent) {
284 irc_rootmsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
286 g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->user->nick);
287 e = otrl_privkey_read_fingerprints(irc->otr->us, s, NULL, NULL);
288 if(e && e!=enoent) {
289 irc_rootmsg(irc, "otr load: %s: %s", s, gcry_strerror(e));
293 /* check for otr keys on all accounts */
294 for(a=irc->b->accounts; a; a=a->next) {
295 kg = otr_check_for_key(a) || kg;
297 if(kg) {
298 irc_rootmsg(irc, "Notice: "
299 "The accounts above do not have OTR encryption keys associated with them, yet. "
300 "These keys are now being generated in the background. "
301 "You will be notified as they are completed. "
302 "It is not necessary to wait; "
303 "BitlBee can be used normally during key generation. "
304 "You may safely ignore this message if you don't know what OTR is. ;)");
308 void otr_save(irc_t *irc)
310 char s[512];
311 gcry_error_t e;
313 if(strsane(irc->user->nick)) {
314 g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, irc->user->nick);
315 e = otrl_privkey_write_fingerprints(irc->otr->us, s);
316 if(e) {
317 irc_rootmsg(irc, "otr save: %s: %s", s, gcry_strerror(e));
319 chmod(s, 0600);
323 void otr_remove(const char *nick)
325 char s[512];
327 if(strsane(nick)) {
328 g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, nick);
329 unlink(s);
330 g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, nick);
331 unlink(s);
335 void otr_rename(const char *onick, const char *nnick)
337 char s[512], t[512];
339 if(strsane(nnick) && strsane(onick)) {
340 g_snprintf(s, 511, "%s%s.otr_keys", global.conf->configdir, onick);
341 g_snprintf(t, 511, "%s%s.otr_keys", global.conf->configdir, nnick);
342 rename(s,t);
343 g_snprintf(s, 511, "%s%s.otr_fprints", global.conf->configdir, onick);
344 g_snprintf(t, 511, "%s%s.otr_fprints", global.conf->configdir, nnick);
345 rename(s,t);
349 int otr_check_for_key(account_t *a)
351 irc_t *irc = a->bee->ui_data;
352 OtrlPrivKey *k;
354 /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
355 if(a->prpl->options & OPT_NOOTR) {
356 return 0;
359 k = otrl_privkey_find(irc->otr->us, a->user, a->prpl->name);
360 if(k) {
361 irc_rootmsg(irc, "otr: %s/%s ready", a->user, a->prpl->name);
362 return 0;
363 } if(keygen_in_progress(irc, a->user, a->prpl->name)) {
364 irc_rootmsg(irc, "otr: keygen for %s/%s already in progress", a->user, a->prpl->name);
365 return 0;
366 } else {
367 irc_rootmsg(irc, "otr: starting background keygen for %s/%s", a->user, a->prpl->name);
368 otr_keygen(irc, a->user, a->prpl->name);
369 return 1;
373 char *otr_filter_msg_in(irc_user_t *iu, char *msg, int flags)
375 int ignore_msg;
376 char *newmsg = NULL;
377 OtrlTLV *tlvs = NULL;
378 irc_t *irc = iu->irc;
379 struct im_connection *ic = iu->bu->ic;
381 /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
382 if(ic->acc->prpl->options & OPT_NOOTR) {
383 return msg;
386 ignore_msg = otrl_message_receiving(irc->otr->us, &otr_ops, ic,
387 ic->acc->user, ic->acc->prpl->name, iu->bu->handle, msg, &newmsg,
388 &tlvs, NULL, NULL);
390 otr_handle_smp(ic, iu->bu->handle, tlvs);
392 if(ignore_msg) {
393 /* this was an internal OTR protocol message */
394 return NULL;
395 } else if(!newmsg) {
396 /* this was a non-OTR message */
397 return msg;
398 } else {
399 /* OTR has processed this message */
400 ConnContext *context = otrl_context_find(irc->otr->us, iu->bu->handle,
401 ic->acc->user, ic->acc->prpl->name, 0, NULL, NULL, NULL);
403 /* we're done with the original msg, which will be caller-freed. */
404 /* NB: must not change the newmsg pointer, since we free it. */
405 msg = newmsg;
407 if(context && context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
408 /* HTML decoding */
409 /* perform any necessary stripping that the top level would miss */
410 if(set_getbool(&ic->bee->set, "otr_does_html") &&
411 !(ic->flags & OPT_DOES_HTML) &&
412 set_getbool(&ic->bee->set, "strip_html")) {
413 strip_html(msg);
416 /* coloring */
417 if(set_getbool(&ic->bee->set, "otr_color_encrypted")) {
418 int color; /* color according to f'print trust */
419 char *pre="", *sep=""; /* optional parts */
420 const char *trust = context->active_fingerprint->trust;
422 if(trust && trust[0] != '\0')
423 color=3; /* green */
424 else
425 color=5; /* red */
427 /* in a query window, keep "/me " uncolored at the beginning */
428 if(g_strncasecmp(msg, "/me ", 4) == 0
429 && irc_user_msgdest(iu) == irc->user->nick) {
430 msg += 4; /* skip */
431 pre = "/me ";
434 /* comma in first place could mess with the color code */
435 if(msg[0] == ',') {
436 /* insert a space between color spec and message */
437 sep = " ";
440 msg = g_strdup_printf("%s\x03%.2d%s%s\x0F", pre,
441 color, sep, msg);
445 if(msg == newmsg) {
446 msg = g_strdup(newmsg);
448 otrl_message_free(newmsg);
449 return msg;
453 char *otr_filter_msg_out(irc_user_t *iu, char *msg, int flags)
455 int st;
456 char *otrmsg = NULL;
457 char *emsg = msg; /* the message as we hand it to libotr */
458 ConnContext *ctx = NULL;
459 irc_t *irc = iu->irc;
460 struct im_connection *ic = iu->bu->ic;
462 /* don't do OTR on certain (not classic IM) protocols, e.g. twitter */
463 if(ic->acc->prpl->options & OPT_NOOTR) {
464 return msg;
467 ctx = otrl_context_find(irc->otr->us,
468 iu->bu->handle, ic->acc->user, ic->acc->prpl->name,
469 1, NULL, NULL, NULL);
471 /* HTML encoding */
472 /* consider OTR plaintext to be HTML if otr_does_html is set */
473 if(ctx && ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
474 set_getbool(&ic->bee->set, "otr_does_html") &&
475 (g_strncasecmp(msg, "<html>", 6) != 0)) {
476 emsg = escape_html(msg);
479 st = otrl_message_sending(irc->otr->us, &otr_ops, ic,
480 ic->acc->user, ic->acc->prpl->name, iu->bu->handle,
481 emsg, NULL, &otrmsg, NULL, NULL);
482 if(emsg != msg) {
483 g_free(emsg); /* we're done with this one */
485 if(st) {
486 return NULL;
489 if(otrmsg) {
490 if(!ctx) {
491 otrl_message_free(otrmsg);
492 return NULL;
494 st = otrl_message_fragment_and_send(&otr_ops, ic, ctx,
495 otrmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
496 otrl_message_free(otrmsg);
497 } else {
498 /* note: otrl_message_sending handles policy, so that if REQUIRE_ENCRYPTION is set,
499 this case does not occur */
500 return msg;
503 /* TODO: Error reporting should be done here now (if st!=0), probably. */
505 return NULL;
508 static const struct irc_plugin otr_plugin =
510 otr_irc_new,
511 otr_irc_free,
512 otr_filter_msg_out,
513 otr_filter_msg_in,
514 otr_load,
515 otr_save,
516 otr_remove,
519 static void cmd_otr(irc_t *irc, char **args)
521 const command_t *cmd;
523 if(!args[0])
524 return;
526 if(!args[1])
527 return;
529 for(cmd=otr_commands; cmd->command; cmd++) {
530 if(strcmp(cmd->command, args[1]) == 0)
531 break;
534 if(!cmd->command) {
535 irc_rootmsg(irc, "%s: unknown subcommand \"%s\", see \x02help otr\x02",
536 args[0], args[1]);
537 return;
540 if(!args[cmd->required_parameters+1]) {
541 irc_rootmsg(irc, "%s %s: not enough arguments (%d req.)",
542 args[0], args[1], cmd->required_parameters);
543 return;
546 cmd->execute(irc, args+1);
550 /*** OTR "MessageAppOps" callbacks for global.otr_ui: ***/
552 OtrlPolicy op_policy(void *opdata, ConnContext *context)
554 struct im_connection *ic = check_imc(opdata, context->accountname, context->protocol);
555 irc_t *irc = ic->bee->ui_data;
556 const char *p;
558 /* policy override during keygen: if we're missing the key for context but are currently
559 generating it, then that's as much as we can do. => temporarily return NEVER. */
560 if(keygen_in_progress(irc, context->accountname, context->protocol) &&
561 !otrl_privkey_find(irc->otr->us, context->accountname, context->protocol))
562 return OTRL_POLICY_NEVER;
564 p = set_getstr(&ic->bee->set, "otr_policy");
565 if(!strcmp(p, "never"))
566 return OTRL_POLICY_NEVER;
567 if(!strcmp(p, "opportunistic"))
568 return OTRL_POLICY_OPPORTUNISTIC;
569 if(!strcmp(p, "manual"))
570 return OTRL_POLICY_MANUAL;
571 if(!strcmp(p, "always"))
572 return OTRL_POLICY_ALWAYS;
574 return OTRL_POLICY_OPPORTUNISTIC;
577 void op_create_privkey(void *opdata, const char *accountname,
578 const char *protocol)
580 struct im_connection *ic = check_imc(opdata, accountname, protocol);
581 irc_t *irc = ic->bee->ui_data;
583 /* will fail silently if keygen already in progress */
584 otr_keygen(irc, accountname, protocol);
587 int op_is_logged_in(void *opdata, const char *accountname,
588 const char *protocol, const char *recipient)
590 struct im_connection *ic = check_imc(opdata, accountname, protocol);
591 bee_user_t *bu;
593 /* lookup the irc_user_t for the given recipient */
594 bu = bee_user_by_handle(ic->bee, ic, recipient);
595 if(bu) {
596 if(bu->flags & BEE_USER_ONLINE)
597 return 1;
598 else
599 return 0;
600 } else {
601 return -1;
605 void op_inject_message(void *opdata, const char *accountname,
606 const char *protocol, const char *recipient, const char *message)
608 struct im_connection *ic = check_imc(opdata, accountname, protocol);
609 irc_t *irc = ic->bee->ui_data;
611 if (strcmp(accountname, recipient) == 0) {
612 /* huh? injecting messages to myself? */
613 irc_rootmsg(irc, "note to self: %s", message);
614 } else {
615 /* need to drop some consts here :-( */
616 /* TODO: get flags into op_inject_message?! */
617 ic->acc->prpl->buddy_msg(ic, (char *)recipient, (char *)message, 0);
618 /* ignoring return value :-/ */
622 int op_display_otr_message(void *opdata, const char *accountname,
623 const char *protocol, const char *username, const char *message)
625 struct im_connection *ic = check_imc(opdata, accountname, protocol);
626 char *msg = g_strdup(message);
627 irc_t *irc = ic->bee->ui_data;
628 irc_user_t *u = peeruser(irc, username, protocol);
630 strip_html(msg);
631 if(u) {
632 /* display as a notice from this particular user */
633 irc_usernotice(u, "%s", msg);
634 } else {
635 irc_rootmsg(irc, "[otr] %s", msg);
638 g_free(msg);
639 return 0;
642 void op_new_fingerprint(void *opdata, OtrlUserState us,
643 const char *accountname, const char *protocol,
644 const char *username, unsigned char fingerprint[20])
646 struct im_connection *ic = check_imc(opdata, accountname, protocol);
647 irc_t *irc = ic->bee->ui_data;
648 irc_user_t *u = peeruser(irc, username, protocol);
649 char hunam[45]; /* anybody looking? ;-) */
651 otrl_privkey_hash_to_human(hunam, fingerprint);
652 if(u) {
653 irc_usernotice(u, "new fingerprint: %s", hunam);
654 } else {
655 /* this case shouldn't normally happen */
656 irc_rootmsg(irc, "new fingerprint for %s/%s: %s",
657 username, protocol, hunam);
661 void op_write_fingerprints(void *opdata)
663 struct im_connection *ic = (struct im_connection *)opdata;
664 irc_t *irc = ic->bee->ui_data;
666 otr_save(irc);
669 void op_gone_secure(void *opdata, ConnContext *context)
671 struct im_connection *ic =
672 check_imc(opdata, context->accountname, context->protocol);
673 irc_user_t *u;
674 irc_t *irc = ic->bee->ui_data;
676 u = peeruser(irc, context->username, context->protocol);
677 if(!u) {
678 log_message(LOGLVL_ERROR,
679 "BUG: otr.c: op_gone_secure: irc_user_t for %s/%s/%s not found!",
680 context->username, context->protocol, context->accountname);
681 return;
684 otr_update_uflags(context, u);
685 if(!otr_update_modeflags(irc, u)) {
686 char *trust = u->flags & IRC_USER_OTR_TRUSTED ? "trusted" : "untrusted!";
687 irc_usernotice(u, "conversation is now off the record (%s)", trust);
691 void op_gone_insecure(void *opdata, ConnContext *context)
693 struct im_connection *ic =
694 check_imc(opdata, context->accountname, context->protocol);
695 irc_t *irc = ic->bee->ui_data;
696 irc_user_t *u;
698 u = peeruser(irc, context->username, context->protocol);
699 if(!u) {
700 log_message(LOGLVL_ERROR,
701 "BUG: otr.c: op_gone_insecure: irc_user_t for %s/%s/%s not found!",
702 context->username, context->protocol, context->accountname);
703 return;
705 otr_update_uflags(context, u);
706 if(!otr_update_modeflags(irc, u))
707 irc_usernotice(u, "conversation is now in cleartext");
710 void op_still_secure(void *opdata, ConnContext *context, int is_reply)
712 struct im_connection *ic =
713 check_imc(opdata, context->accountname, context->protocol);
714 irc_t *irc = ic->bee->ui_data;
715 irc_user_t *u;
717 u = peeruser(irc, context->username, context->protocol);
718 if(!u) {
719 log_message(LOGLVL_ERROR,
720 "BUG: otr.c: op_still_secure: irc_user_t for %s/%s/%s not found!",
721 context->username, context->protocol, context->accountname);
722 return;
725 otr_update_uflags(context, u);
726 if(!otr_update_modeflags(irc, u)) {
727 char *trust = u->flags & IRC_USER_OTR_TRUSTED ? "trusted" : "untrusted!";
728 irc_usernotice(u, "otr connection has been refreshed (%s)", trust);
732 void op_log_message(void *opdata, const char *message)
734 char *msg = g_strdup(message);
736 strip_html(msg);
737 log_message(LOGLVL_INFO, "otr: %s", msg);
738 g_free(msg);
741 int op_max_message_size(void *opdata, ConnContext *context)
743 struct im_connection *ic =
744 check_imc(opdata, context->accountname, context->protocol);
746 return ic->acc->prpl->mms;
749 const char *op_account_name(void *opdata, const char *account, const char *protocol)
751 struct im_connection *ic = (struct im_connection *)opdata;
752 irc_t *irc = ic->bee->ui_data;
754 return peernick(irc, account, protocol);
758 /*** OTR sub-command handlers ***/
760 void cmd_otr_reconnect(irc_t *irc, char **args)
762 cmd_otr_disconnect(irc, args);
763 cmd_otr_connect(irc, args);
766 void cmd_otr_disconnect(irc_t *irc, char **args)
768 irc_user_t *u;
770 u = irc_user_by_name(irc, args[1]);
771 if(!u || !u->bu || !u->bu->ic) {
772 irc_rootmsg(irc, "%s: unknown user", args[1]);
773 return;
776 otrl_message_disconnect(irc->otr->us, &otr_ops,
777 u->bu->ic, u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, u->bu->handle);
779 /* for some reason, libotr (3.1.0) doesn't do this itself: */
780 if(u->flags & IRC_USER_OTR_ENCRYPTED) {
781 ConnContext *ctx;
782 ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
783 u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
784 if(ctx)
785 op_gone_insecure(u->bu->ic, ctx);
786 else /* huh? */
787 u->flags &= ( IRC_USER_OTR_ENCRYPTED | IRC_USER_OTR_TRUSTED );
791 void cmd_otr_connect(irc_t *irc, char **args)
793 irc_user_t *u;
795 u = irc_user_by_name(irc, args[1]);
796 if(!u || !u->bu || !u->bu->ic) {
797 irc_rootmsg(irc, "%s: unknown user", args[1]);
798 return;
800 if(!(u->bu->flags & BEE_USER_ONLINE)) {
801 irc_rootmsg(irc, "%s is offline", args[1]);
802 return;
805 bee_user_msg(irc->b, u->bu, "?OTR?v2?", 0);
808 void cmd_otr_smp(irc_t *irc, char **args)
810 otr_smp_or_smpq(irc, args[1], NULL, args[2]); /* no question */
813 void cmd_otr_smpq(irc_t *irc, char **args)
815 otr_smp_or_smpq(irc, args[1], args[2], args[3]);
818 void cmd_otr_trust(irc_t *irc, char **args)
820 irc_user_t *u;
821 ConnContext *ctx;
822 unsigned char raw[20];
823 Fingerprint *fp;
824 int i,j;
826 u = irc_user_by_name(irc, args[1]);
827 if(!u || !u->bu || !u->bu->ic) {
828 irc_rootmsg(irc, "%s: unknown user", args[1]);
829 return;
832 ctx = otrl_context_find(irc->otr->us, u->bu->handle,
833 u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
834 if(!ctx) {
835 irc_rootmsg(irc, "%s: no otr context with user", args[1]);
836 return;
839 /* convert given fingerprint to raw representation */
840 for(i=0; i<5; i++) {
841 for(j=0; j<4; j++) {
842 char *p = args[2+i]+(2*j);
843 char *q = p+1;
844 int x, y;
846 if(!*p || !*q) {
847 irc_rootmsg(irc, "failed: truncated fingerprint block %d", i+1);
848 return;
851 x = hexval(*p);
852 y = hexval(*q);
853 if(x<0) {
854 irc_rootmsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+1, i+1);
855 return;
857 if(y<0) {
858 irc_rootmsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+2, i+1);
859 return;
862 raw[i*4+j] = x*16 + y;
865 fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL);
866 if(!fp) {
867 irc_rootmsg(irc, "failed: no such fingerprint for %s", args[1]);
868 } else {
869 char *trust = args[7] ? args[7] : "affirmed";
870 otrl_context_set_trust(fp, trust);
871 irc_rootmsg(irc, "fingerprint match, trust set to \"%s\"", trust);
872 if(u->flags & IRC_USER_OTR_ENCRYPTED)
873 u->flags |= IRC_USER_OTR_TRUSTED;
874 otr_update_modeflags(irc, u);
878 void cmd_otr_info(irc_t *irc, char **args)
880 if(!args[1]) {
881 show_general_otr_info(irc);
882 } else {
883 char *arg = g_strdup(args[1]);
884 char *myhandle, *handle=NULL, *protocol;
885 ConnContext *ctx;
887 /* interpret arg as 'user/protocol/account' if possible */
888 protocol = strchr(arg, '/');
889 myhandle = NULL;
890 if(protocol) {
891 *(protocol++) = '\0';
892 myhandle = strchr(protocol, '/');
894 if(protocol && myhandle) {
895 *(myhandle++) = '\0';
896 handle = arg;
897 ctx = otrl_context_find(irc->otr->us, handle, myhandle, protocol, 0, NULL, NULL, NULL);
898 if(!ctx) {
899 irc_rootmsg(irc, "no such context");
900 g_free(arg);
901 return;
903 } else {
904 irc_user_t *u = irc_user_by_name(irc, args[1]);
905 if(!u || !u->bu || !u->bu->ic) {
906 irc_rootmsg(irc, "%s: unknown user", args[1]);
907 g_free(arg);
908 return;
910 ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
911 u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
912 if(!ctx) {
913 irc_rootmsg(irc, "no otr context with %s", args[1]);
914 g_free(arg);
915 return;
919 /* show how we resolved the (nick) argument, if we did */
920 if(handle!=arg) {
921 irc_rootmsg(irc, "%s is %s/%s; we are %s/%s to them", args[1],
922 ctx->username, ctx->protocol, ctx->accountname, ctx->protocol);
924 show_otr_context_info(irc, ctx);
925 g_free(arg);
929 void cmd_otr_keygen(irc_t *irc, char **args)
931 int i, n;
932 account_t *a;
934 n = atoi(args[1]);
935 if(n<0 || (!n && strcmp(args[1], "0"))) {
936 irc_rootmsg(irc, "%s: invalid account number", args[1]);
937 return;
940 a = irc->b->accounts;
941 for(i=0; i<n && a; i++, a=a->next);
942 if(!a) {
943 irc_rootmsg(irc, "%s: no such account", args[1]);
944 return;
947 if(keygen_in_progress(irc, a->user, a->prpl->name)) {
948 irc_rootmsg(irc, "keygen for account %d already in progress", n);
949 return;
952 if(otrl_privkey_find(irc->otr->us, a->user, a->prpl->name)) {
953 char *s = g_strdup_printf("account %d already has a key, replace it?", n);
954 query_add(irc, NULL, s, yes_keygen, NULL, NULL, a);
955 g_free(s);
956 } else {
957 otr_keygen(irc, a->user, a->prpl->name);
961 void yes_forget_fingerprint(void *data)
963 pair_t *p = (pair_t *)data;
964 irc_t *irc = (irc_t *)p->fst;
965 Fingerprint *fp = (Fingerprint *)p->snd;
967 g_free(p);
969 if(fp == fp->context->active_fingerprint) {
970 irc_rootmsg(irc, "that fingerprint is active, terminate otr connection first");
971 return;
974 otrl_context_forget_fingerprint(fp, 0);
977 void yes_forget_context(void *data)
979 pair_t *p = (pair_t *)data;
980 irc_t *irc = (irc_t *)p->fst;
981 ConnContext *ctx = (ConnContext *)p->snd;
983 g_free(p);
985 if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
986 irc_rootmsg(irc, "active otr connection with %s, terminate it first",
987 peernick(irc, ctx->username, ctx->protocol));
988 return;
991 if(ctx->msgstate == OTRL_MSGSTATE_FINISHED)
992 otrl_context_force_plaintext(ctx);
993 otrl_context_forget(ctx);
996 void yes_forget_key(void *data)
998 OtrlPrivKey *key = (OtrlPrivKey *)data;
1000 otrl_privkey_forget(key);
1001 /* Hm, libotr doesn't seem to offer a function for explicitly /writing/
1002 keyfiles. So the key will be back on the next load... */
1003 /* TODO: Actually erase forgotten keys from storage? */
1006 void cmd_otr_forget(irc_t *irc, char **args)
1008 if(!strcmp(args[1], "fingerprint"))
1010 irc_user_t *u;
1011 ConnContext *ctx;
1012 Fingerprint *fp;
1013 char human[54];
1014 char *s;
1015 pair_t *p;
1017 if(!args[3]) {
1018 irc_rootmsg(irc, "otr %s %s: not enough arguments (2 req.)", args[0], args[1]);
1019 return;
1022 /* TODO: allow context specs ("user/proto/account") in 'otr forget fingerprint'? */
1023 u = irc_user_by_name(irc, args[2]);
1024 if(!u || !u->bu || !u->bu->ic) {
1025 irc_rootmsg(irc, "%s: unknown user", args[2]);
1026 return;
1029 ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
1030 u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
1031 if(!ctx) {
1032 irc_rootmsg(irc, "no otr context with %s", args[2]);
1033 return;
1036 fp = match_fingerprint(irc, ctx, ((const char **)args)+3);
1037 if(!fp) {
1038 /* match_fingerprint does error messages */
1039 return;
1042 if(fp == ctx->active_fingerprint) {
1043 irc_rootmsg(irc, "that fingerprint is active, terminate otr connection first");
1044 return;
1047 otrl_privkey_hash_to_human(human, fp->fingerprint);
1048 s = g_strdup_printf("about to forget fingerprint %s, are you sure?", human);
1049 p = g_malloc(sizeof(pair_t));
1050 if(!p)
1051 return;
1052 p->fst = irc;
1053 p->snd = fp;
1054 query_add(irc, NULL, s, yes_forget_fingerprint, NULL, NULL, p);
1055 g_free(s);
1058 else if(!strcmp(args[1], "context"))
1060 irc_user_t *u;
1061 ConnContext *ctx;
1062 char *s;
1063 pair_t *p;
1065 /* TODO: allow context specs ("user/proto/account") in 'otr forget contex'? */
1066 u = irc_user_by_name(irc, args[2]);
1067 if(!u || !u->bu || !u->bu->ic) {
1068 irc_rootmsg(irc, "%s: unknown user", args[2]);
1069 return;
1072 ctx = otrl_context_find(irc->otr->us, u->bu->handle, u->bu->ic->acc->user,
1073 u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
1074 if(!ctx) {
1075 irc_rootmsg(irc, "no otr context with %s", args[2]);
1076 return;
1079 if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
1080 irc_rootmsg(irc, "active otr connection with %s, terminate it first", args[2]);
1081 return;
1084 s = g_strdup_printf("about to forget otr data about %s, are you sure?", args[2]);
1085 p = g_malloc(sizeof(pair_t));
1086 if(!p)
1087 return;
1088 p->fst = irc;
1089 p->snd = ctx;
1090 query_add(irc, NULL, s, yes_forget_context, NULL, NULL, p);
1091 g_free(s);
1094 else if(!strcmp(args[1], "key"))
1096 OtrlPrivKey *key;
1097 char *s;
1099 key = match_privkey(irc, ((const char **)args)+2);
1100 if(!key) {
1101 /* match_privkey does error messages */
1102 return;
1105 s = g_strdup_printf("about to forget the private key for %s/%s, are you sure?",
1106 key->accountname, key->protocol);
1107 query_add(irc, NULL, s, yes_forget_key, NULL, NULL, key);
1108 g_free(s);
1111 else
1113 irc_rootmsg(irc, "otr %s: unknown subcommand \"%s\", see \x02help otr forget\x02",
1114 args[0], args[1]);
1119 /*** local helpers / subroutines: ***/
1121 /* Socialist Millionaires' Protocol */
1122 void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs)
1124 irc_t *irc = ic->bee->ui_data;
1125 OtrlUserState us = irc->otr->us;
1126 OtrlMessageAppOps *ops = &otr_ops;
1127 OtrlTLV *tlv = NULL;
1128 ConnContext *context;
1129 NextExpectedSMP nextMsg;
1130 irc_user_t *u;
1131 bee_user_t *bu;
1133 bu = bee_user_by_handle(ic->bee, ic, handle);
1134 if(!bu || !(u = bu->ui_data)) return;
1135 context = otrl_context_find(us, handle,
1136 ic->acc->user, ic->acc->prpl->name, 1, NULL, NULL, NULL);
1137 if(!context) {
1138 /* huh? out of memory or what? */
1139 irc_rootmsg(irc, "smp: failed to get otr context for %s", u->nick);
1140 otrl_message_abort_smp(us, ops, u->bu->ic, context);
1141 otrl_sm_state_free(context->smstate);
1142 return;
1144 nextMsg = context->smstate->nextExpected;
1146 if (context->smstate->sm_prog_state == OTRL_SMP_PROG_CHEATED) {
1147 irc_rootmsg(irc, "smp %s: opponent violated protocol, aborting",
1148 u->nick);
1149 otrl_message_abort_smp(us, ops, u->bu->ic, context);
1150 otrl_sm_state_free(context->smstate);
1151 return;
1154 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1Q);
1155 if (tlv) {
1156 if (nextMsg != OTRL_SMP_EXPECT1) {
1157 irc_rootmsg(irc, "smp %s: spurious SMP1Q received, aborting", u->nick);
1158 otrl_message_abort_smp(us, ops, u->bu->ic, context);
1159 otrl_sm_state_free(context->smstate);
1160 } else {
1161 char *question = g_strndup((char *)tlv->data, tlv->len);
1162 irc_rootmsg(irc, "smp: initiated by %s with question: \x02\"%s\"\x02", u->nick,
1163 question);
1164 irc_rootmsg(irc, "smp: respond with \x02otr smp %s <answer>\x02",
1165 u->nick);
1166 g_free(question);
1167 /* smp stays in EXPECT1 until user responds */
1170 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
1171 if (tlv) {
1172 if (nextMsg != OTRL_SMP_EXPECT1) {
1173 irc_rootmsg(irc, "smp %s: spurious SMP1 received, aborting", u->nick);
1174 otrl_message_abort_smp(us, ops, u->bu->ic, context);
1175 otrl_sm_state_free(context->smstate);
1176 } else {
1177 irc_rootmsg(irc, "smp: initiated by %s"
1178 " - respond with \x02otr smp %s <secret>\x02",
1179 u->nick, u->nick);
1180 /* smp stays in EXPECT1 until user responds */
1183 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
1184 if (tlv) {
1185 if (nextMsg != OTRL_SMP_EXPECT2) {
1186 irc_rootmsg(irc, "smp %s: spurious SMP2 received, aborting", u->nick);
1187 otrl_message_abort_smp(us, ops, u->bu->ic, context);
1188 otrl_sm_state_free(context->smstate);
1189 } else {
1190 /* SMP2 received, otrl_message_receiving will have sent SMP3 */
1191 context->smstate->nextExpected = OTRL_SMP_EXPECT4;
1194 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
1195 if (tlv) {
1196 if (nextMsg != OTRL_SMP_EXPECT3) {
1197 irc_rootmsg(irc, "smp %s: spurious SMP3 received, aborting", u->nick);
1198 otrl_message_abort_smp(us, ops, u->bu->ic, context);
1199 otrl_sm_state_free(context->smstate);
1200 } else {
1201 /* SMP3 received, otrl_message_receiving will have sent SMP4 */
1202 if(context->smstate->sm_prog_state == OTRL_SMP_PROG_SUCCEEDED) {
1203 if(context->smstate->received_question) {
1204 irc_rootmsg(irc, "smp %s: correct answer, you are trusted",
1205 u->nick);
1206 } else {
1207 irc_rootmsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1208 u->nick);
1210 } else {
1211 if(context->smstate->received_question) {
1212 irc_rootmsg(irc, "smp %s: wrong answer, you are not trusted",
1213 u->nick);
1214 } else {
1215 irc_rootmsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1216 u->nick);
1219 otrl_sm_state_free(context->smstate);
1220 /* smp is in back in EXPECT1 */
1223 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
1224 if (tlv) {
1225 if (nextMsg != OTRL_SMP_EXPECT4) {
1226 irc_rootmsg(irc, "smp %s: spurious SMP4 received, aborting", u->nick);
1227 otrl_message_abort_smp(us, ops, u->bu->ic, context);
1228 otrl_sm_state_free(context->smstate);
1229 } else {
1230 /* SMP4 received, otrl_message_receiving will have set fp trust */
1231 if(context->smstate->sm_prog_state == OTRL_SMP_PROG_SUCCEEDED) {
1232 irc_rootmsg(irc, "smp %s: secrets proved equal, fingerprint trusted",
1233 u->nick);
1234 } else {
1235 irc_rootmsg(irc, "smp %s: secrets did not match, fingerprint not trusted",
1236 u->nick);
1238 otrl_sm_state_free(context->smstate);
1239 /* smp is in back in EXPECT1 */
1242 tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP_ABORT);
1243 if (tlv) {
1244 irc_rootmsg(irc, "smp: received abort from %s", u->nick);
1245 otrl_sm_state_free(context->smstate);
1246 /* smp is in back in EXPECT1 */
1250 /* combined handler for the 'otr smp' and 'otr smpq' commands */
1251 void otr_smp_or_smpq(irc_t *irc, const char *nick, const char *question,
1252 const char *secret)
1254 irc_user_t *u;
1255 ConnContext *ctx;
1257 u = irc_user_by_name(irc, nick);
1258 if(!u || !u->bu || !u->bu->ic) {
1259 irc_rootmsg(irc, "%s: unknown user", nick);
1260 return;
1262 if(!(u->bu->flags & BEE_USER_ONLINE)) {
1263 irc_rootmsg(irc, "%s is offline", nick);
1264 return;
1267 ctx = otrl_context_find(irc->otr->us, u->bu->handle,
1268 u->bu->ic->acc->user, u->bu->ic->acc->prpl->name, 0, NULL, NULL, NULL);
1269 if(!ctx || ctx->msgstate != OTRL_MSGSTATE_ENCRYPTED) {
1270 irc_rootmsg(irc, "smp: otr inactive with %s, try \x02otr connect"
1271 " %s\x02", nick, nick);
1272 return;
1275 if(ctx->smstate->nextExpected != OTRL_SMP_EXPECT1) {
1276 log_message(LOGLVL_INFO,
1277 "SMP already in phase %d, sending abort before reinitiating",
1278 ctx->smstate->nextExpected+1);
1279 otrl_message_abort_smp(irc->otr->us, &otr_ops, u->bu->ic, ctx);
1280 otrl_sm_state_free(ctx->smstate);
1283 if(question) {
1284 /* this was 'otr smpq', just initiate */
1285 irc_rootmsg(irc, "smp: initiating with %s...", u->nick);
1286 otrl_message_initiate_smp_q(irc->otr->us, &otr_ops, u->bu->ic, ctx,
1287 question, (unsigned char *)secret, strlen(secret));
1288 /* smp is now in EXPECT2 */
1289 } else {
1290 /* this was 'otr smp', initiate or reply */
1291 /* warning: the following assumes that smstates are cleared whenever an SMP
1292 is completed or aborted! */
1293 if(ctx->smstate->secret == NULL) {
1294 irc_rootmsg(irc, "smp: initiating with %s...", u->nick);
1295 otrl_message_initiate_smp(irc->otr->us, &otr_ops,
1296 u->bu->ic, ctx, (unsigned char *)secret, strlen(secret));
1297 /* smp is now in EXPECT2 */
1298 } else {
1299 /* if we're still in EXPECT1 but smstate is initialized, we must have
1300 received the SMP1, so let's issue a response */
1301 irc_rootmsg(irc, "smp: responding to %s...", u->nick);
1302 otrl_message_respond_smp(irc->otr->us, &otr_ops,
1303 u->bu->ic, ctx, (unsigned char *)secret, strlen(secret));
1304 /* smp is now in EXPECT3 */
1309 /* helper to assert that account and protocol names given to ops below always
1310 match the im_connection passed through as opdata */
1311 struct im_connection *check_imc(void *opdata, const char *accountname,
1312 const char *protocol)
1314 struct im_connection *ic = (struct im_connection *)opdata;
1316 if (strcmp(accountname, ic->acc->user) != 0) {
1317 log_message(LOGLVL_WARNING,
1318 "otr: internal account name mismatch: '%s' vs '%s'",
1319 accountname, ic->acc->user);
1321 if (strcmp(protocol, ic->acc->prpl->name) != 0) {
1322 log_message(LOGLVL_WARNING,
1323 "otr: internal protocol name mismatch: '%s' vs '%s'",
1324 protocol, ic->acc->prpl->name);
1327 return ic;
1330 irc_user_t *peeruser(irc_t *irc, const char *handle, const char *protocol)
1332 GSList *l;
1334 for(l=irc->b->users; l; l = l->next) {
1335 bee_user_t *bu = l->data;
1336 struct prpl *prpl;
1337 if(!bu->ui_data || !bu->ic || !bu->handle)
1338 continue;
1339 prpl = bu->ic->acc->prpl;
1340 if(strcmp(prpl->name, protocol) == 0
1341 && prpl->handle_cmp(bu->handle, handle) == 0) {
1342 return bu->ui_data;
1346 return NULL;
1349 int hexval(char a)
1351 int x=tolower(a);
1353 if(x>='a' && x<='f')
1354 x = x - 'a' + 10;
1355 else if(x>='0' && x<='9')
1356 x = x - '0';
1357 else
1358 return -1;
1360 return x;
1363 const char *peernick(irc_t *irc, const char *handle, const char *protocol)
1365 static char fallback[512];
1367 irc_user_t *u = peeruser(irc, handle, protocol);
1368 if(u) {
1369 return u->nick;
1370 } else {
1371 g_snprintf(fallback, 511, "%s/%s", handle, protocol);
1372 return fallback;
1376 void otr_update_uflags(ConnContext *context, irc_user_t *u)
1378 const char *trust;
1380 if(context->active_fingerprint) {
1381 u->flags |= IRC_USER_OTR_ENCRYPTED;
1383 trust = context->active_fingerprint->trust;
1384 if(trust && trust[0])
1385 u->flags |= IRC_USER_OTR_TRUSTED;
1386 else
1387 u->flags &= ~IRC_USER_OTR_TRUSTED;
1388 } else {
1389 u->flags &= ~IRC_USER_OTR_ENCRYPTED;
1393 int otr_update_modeflags(irc_t *irc, irc_user_t *u)
1395 return 0;
1398 void show_fingerprints(irc_t *irc, ConnContext *ctx)
1400 char human[45];
1401 Fingerprint *fp;
1402 const char *trust;
1403 int count=0;
1405 for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1406 if(!fp->fingerprint)
1407 continue;
1408 count++;
1409 otrl_privkey_hash_to_human(human, fp->fingerprint);
1410 if(!fp->trust || fp->trust[0] == '\0') {
1411 trust="untrusted";
1412 } else {
1413 trust=fp->trust;
1415 if(fp == ctx->active_fingerprint) {
1416 irc_rootmsg(irc, " \x02%s (%s)\x02", human, trust);
1417 } else {
1418 irc_rootmsg(irc, " %s (%s)", human, trust);
1421 if(count==0)
1422 irc_rootmsg(irc, " (none)");
1425 Fingerprint *match_fingerprint(irc_t *irc, ConnContext *ctx, const char **args)
1427 Fingerprint *fp, *fp2;
1428 char human[45];
1429 char prefix[45], *p;
1430 int n;
1431 int i,j;
1433 /* assemble the args into a prefix in standard "human" form */
1434 n=0;
1435 p=prefix;
1436 for(i=0; args[i]; i++) {
1437 for(j=0; args[i][j]; j++) {
1438 char c = toupper(args[i][j]);
1440 if(n>=40) {
1441 irc_rootmsg(irc, "too many fingerprint digits given, expected at most 40");
1442 return NULL;
1445 if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1446 *(p++) = c;
1447 } else {
1448 irc_rootmsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1449 return NULL;
1452 n++;
1453 if(n%8 == 0)
1454 *(p++) = ' ';
1457 *p = '\0';
1459 /* find first fingerprint with the given prefix */
1460 n = strlen(prefix);
1461 for(fp=&ctx->fingerprint_root; fp; fp=fp->next) {
1462 if(!fp->fingerprint)
1463 continue;
1464 otrl_privkey_hash_to_human(human, fp->fingerprint);
1465 if(!strncmp(prefix, human, n))
1466 break;
1468 if(!fp) {
1469 irc_rootmsg(irc, "%s: no match", prefix);
1470 return NULL;
1473 /* make sure the match, if any, is unique */
1474 for(fp2=fp->next; fp2; fp2=fp2->next) {
1475 if(!fp2->fingerprint)
1476 continue;
1477 otrl_privkey_hash_to_human(human, fp2->fingerprint);
1478 if(!strncmp(prefix, human, n))
1479 break;
1481 if(fp2) {
1482 irc_rootmsg(irc, "%s: multiple matches", prefix);
1483 return NULL;
1486 return fp;
1489 OtrlPrivKey *match_privkey(irc_t *irc, const char **args)
1491 OtrlPrivKey *k, *k2;
1492 char human[45];
1493 char prefix[45], *p;
1494 int n;
1495 int i,j;
1497 /* assemble the args into a prefix in standard "human" form */
1498 n=0;
1499 p=prefix;
1500 for(i=0; args[i]; i++) {
1501 for(j=0; args[i][j]; j++) {
1502 char c = toupper(args[i][j]);
1504 if(n>=40) {
1505 irc_rootmsg(irc, "too many fingerprint digits given, expected at most 40");
1506 return NULL;
1509 if( (c>='A' && c<='F') || (c>='0' && c<='9') ) {
1510 *(p++) = c;
1511 } else {
1512 irc_rootmsg(irc, "invalid hex digit '%c' in block %d", args[i][j], i+1);
1513 return NULL;
1516 n++;
1517 if(n%8 == 0)
1518 *(p++) = ' ';
1521 *p = '\0';
1523 /* find first key which matches the given prefix */
1524 n = strlen(prefix);
1525 for(k=irc->otr->us->privkey_root; k; k=k->next) {
1526 p = otrl_privkey_fingerprint(irc->otr->us, human, k->accountname, k->protocol);
1527 if(!p) /* gah! :-P */
1528 continue;
1529 if(!strncmp(prefix, human, n))
1530 break;
1532 if(!k) {
1533 irc_rootmsg(irc, "%s: no match", prefix);
1534 return NULL;
1537 /* make sure the match, if any, is unique */
1538 for(k2=k->next; k2; k2=k2->next) {
1539 p = otrl_privkey_fingerprint(irc->otr->us, human, k2->accountname, k2->protocol);
1540 if(!p) /* gah! :-P */
1541 continue;
1542 if(!strncmp(prefix, human, n))
1543 break;
1545 if(k2) {
1546 irc_rootmsg(irc, "%s: multiple matches", prefix);
1547 return NULL;
1550 return k;
1553 void show_general_otr_info(irc_t *irc)
1555 ConnContext *ctx;
1556 OtrlPrivKey *key;
1557 char human[45];
1558 kg_t *kg;
1560 /* list all privkeys (including ones being generated) */
1561 irc_rootmsg(irc, "\x1fprivate keys:\x1f");
1562 for(key=irc->otr->us->privkey_root; key; key=key->next) {
1563 const char *hash;
1565 switch(key->pubkey_type) {
1566 case OTRL_PUBKEY_TYPE_DSA:
1567 irc_rootmsg(irc, " %s/%s - DSA", key->accountname, key->protocol);
1568 break;
1569 default:
1570 irc_rootmsg(irc, " %s/%s - type %d", key->accountname, key->protocol,
1571 key->pubkey_type);
1574 /* No, it doesn't make much sense to search for the privkey again by
1575 account/protocol, but libotr currently doesn't provide a direct routine
1576 for hashing a given 'OtrlPrivKey'... */
1577 hash = otrl_privkey_fingerprint(irc->otr->us, human, key->accountname, key->protocol);
1578 if(hash) /* should always succeed */
1579 irc_rootmsg(irc, " %s", human);
1581 if(irc->otr->sent_accountname) {
1582 irc_rootmsg(irc, " %s/%s - DSA", irc->otr->sent_accountname,
1583 irc->otr->sent_protocol);
1584 irc_rootmsg(irc, " (being generated)");
1586 for(kg=irc->otr->todo; kg; kg=kg->next) {
1587 irc_rootmsg(irc, " %s/%s - DSA", kg->accountname, kg->protocol);
1588 irc_rootmsg(irc, " (queued)");
1590 if(key == irc->otr->us->privkey_root &&
1591 !irc->otr->sent_accountname &&
1592 kg == irc->otr->todo)
1593 irc_rootmsg(irc, " (none)");
1595 /* list all contexts */
1596 irc_rootmsg(irc, "%s", "");
1597 irc_rootmsg(irc, "\x1f" "connection contexts:\x1f (bold=currently encrypted)");
1598 for(ctx=irc->otr->us->context_root; ctx; ctx=ctx->next) {\
1599 irc_user_t *u;
1600 char *userstring;
1602 u = peeruser(irc, ctx->username, ctx->protocol);
1603 if(u)
1604 userstring = g_strdup_printf("%s/%s/%s (%s)",
1605 ctx->username, ctx->protocol, ctx->accountname, u->nick);
1606 else
1607 userstring = g_strdup_printf("%s/%s/%s",
1608 ctx->username, ctx->protocol, ctx->accountname);
1610 if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
1611 irc_rootmsg(irc, " \x02%s\x02", userstring);
1612 } else {
1613 irc_rootmsg(irc, " %s", userstring);
1616 g_free(userstring);
1618 if(ctx == irc->otr->us->context_root)
1619 irc_rootmsg(irc, " (none)");
1622 void show_otr_context_info(irc_t *irc, ConnContext *ctx)
1624 switch(ctx->otr_offer) {
1625 case OFFER_NOT:
1626 irc_rootmsg(irc, " otr offer status: none sent");
1627 break;
1628 case OFFER_SENT:
1629 irc_rootmsg(irc, " otr offer status: awaiting reply");
1630 break;
1631 case OFFER_ACCEPTED:
1632 irc_rootmsg(irc, " otr offer status: accepted our offer");
1633 break;
1634 case OFFER_REJECTED:
1635 irc_rootmsg(irc, " otr offer status: ignored our offer");
1636 break;
1637 default:
1638 irc_rootmsg(irc, " otr offer status: %d", ctx->otr_offer);
1641 switch(ctx->msgstate) {
1642 case OTRL_MSGSTATE_PLAINTEXT:
1643 irc_rootmsg(irc, " connection state: cleartext");
1644 break;
1645 case OTRL_MSGSTATE_ENCRYPTED:
1646 irc_rootmsg(irc, " connection state: encrypted (v%d)", ctx->protocol_version);
1647 break;
1648 case OTRL_MSGSTATE_FINISHED:
1649 irc_rootmsg(irc, " connection state: shut down");
1650 break;
1651 default:
1652 irc_rootmsg(irc, " connection state: %d", ctx->msgstate);
1655 irc_rootmsg(irc, " fingerprints: (bold=active)");
1656 show_fingerprints(irc, ctx);
1659 int keygen_in_progress(irc_t *irc, const char *handle, const char *protocol)
1661 kg_t *kg;
1663 if(!irc->otr->sent_accountname || !irc->otr->sent_protocol)
1664 return 0;
1666 /* are we currently working on this key? */
1667 if(!strcmp(handle, irc->otr->sent_accountname) &&
1668 !strcmp(protocol, irc->otr->sent_protocol))
1669 return 1;
1671 /* do we have it queued for later? */
1672 for(kg=irc->otr->todo; kg; kg=kg->next) {
1673 if(!strcmp(handle, kg->accountname) &&
1674 !strcmp(protocol, kg->protocol))
1675 return 1;
1678 return 0;
1681 void otr_keygen(irc_t *irc, const char *handle, const char *protocol)
1683 /* do nothing if a key for the requested account is already being generated */
1684 if(keygen_in_progress(irc, handle, protocol))
1685 return;
1687 /* see if we already have a keygen child running. if not, start one and put a
1688 handler on its output. */
1689 if(!irc->otr->keygen || waitpid(irc->otr->keygen, NULL, WNOHANG)) {
1690 pid_t p;
1691 int to[2], from[2];
1692 FILE *tof, *fromf;
1694 if(pipe(to) < 0 || pipe(from) < 0) {
1695 irc_rootmsg(irc, "otr keygen: couldn't create pipe: %s", strerror(errno));
1696 return;
1699 tof = fdopen(to[1], "w");
1700 fromf = fdopen(from[0], "r");
1701 if(!tof || !fromf) {
1702 irc_rootmsg(irc, "otr keygen: couldn't streamify pipe: %s", strerror(errno));
1703 return;
1706 p = fork();
1707 if(p<0) {
1708 irc_rootmsg(irc, "otr keygen: couldn't fork: %s", strerror(errno));
1709 return;
1712 if(!p) {
1713 /* child process */
1714 signal(SIGTERM, exit);
1715 keygen_child_main(irc->otr->us, to[0], from[1]);
1716 exit(0);
1719 irc->otr->keygen = p;
1720 irc->otr->to = tof;
1721 irc->otr->from = fromf;
1722 irc->otr->sent_accountname = NULL;
1723 irc->otr->sent_protocol = NULL;
1724 irc->otr->todo = NULL;
1725 b_input_add(from[0], B_EV_IO_READ, keygen_finish_handler, irc);
1728 /* is the keygen slave currently working? */
1729 if(irc->otr->sent_accountname) {
1730 /* enqueue our job for later transmission */
1731 kg_t **kg = &irc->otr->todo;
1732 while(*kg)
1733 kg=&((*kg)->next);
1734 *kg = g_new0(kg_t, 1);
1735 (*kg)->accountname = g_strdup(handle);
1736 (*kg)->protocol = g_strdup(protocol);
1737 } else {
1738 /* send our job over and remember it */
1739 fprintf(irc->otr->to, "%s\n%s\n", handle, protocol);
1740 fflush(irc->otr->to);
1741 irc->otr->sent_accountname = g_strdup(handle);
1742 irc->otr->sent_protocol = g_strdup(protocol);
1746 void keygen_child_main(OtrlUserState us, int infd, int outfd)
1748 FILE *input, *output;
1749 char filename[128], accountname[512], protocol[512];
1750 gcry_error_t e;
1751 int tempfd;
1753 input = fdopen(infd, "r");
1754 output = fdopen(outfd, "w");
1756 while(!feof(input) && !ferror(input) && !feof(output) && !ferror(output)) {
1757 myfgets(accountname, 512, input);
1758 myfgets(protocol, 512, input);
1760 strncpy(filename, "/tmp/bitlbee-XXXXXX", 128);
1761 tempfd = mkstemp(filename);
1762 close(tempfd);
1764 e = otrl_privkey_generate(us, filename, accountname, protocol);
1765 if(e) {
1766 fprintf(output, "\n"); /* this means failure */
1767 fprintf(output, "otr keygen: %s\n", gcry_strerror(e));
1768 unlink(filename);
1769 } else {
1770 fprintf(output, "%s\n", filename);
1771 fprintf(output, "otr keygen for %s/%s complete\n", accountname, protocol);
1773 fflush(output);
1776 fclose(input);
1777 fclose(output);
1780 gboolean keygen_finish_handler(gpointer data, gint fd, b_input_condition cond)
1782 irc_t *irc = (irc_t *)data;
1783 char filename[512], msg[512];
1785 myfgets(filename, 512, irc->otr->from);
1786 myfgets(msg, 512, irc->otr->from);
1788 irc_rootmsg(irc, "%s", msg);
1789 if(filename[0]) {
1790 if(strsane(irc->user->nick)) {
1791 char *kf = g_strdup_printf("%s%s.otr_keys", global.conf->configdir, irc->user->nick);
1792 char *tmp = g_strdup_printf("%s.new", kf);
1793 copyfile(filename, tmp);
1794 unlink(filename);
1795 rename(tmp,kf);
1796 otrl_privkey_read(irc->otr->us, kf);
1797 g_free(kf);
1798 g_free(tmp);
1799 } else {
1800 otrl_privkey_read(irc->otr->us, filename);
1801 unlink(filename);
1805 /* forget this job */
1806 g_free(irc->otr->sent_accountname);
1807 g_free(irc->otr->sent_protocol);
1808 irc->otr->sent_accountname = NULL;
1809 irc->otr->sent_protocol = NULL;
1811 /* see if there are any more in the queue */
1812 if(irc->otr->todo) {
1813 kg_t *p = irc->otr->todo;
1814 /* send the next one over */
1815 fprintf(irc->otr->to, "%s\n%s\n", p->accountname, p->protocol);
1816 fflush(irc->otr->to);
1817 irc->otr->sent_accountname = p->accountname;
1818 irc->otr->sent_protocol = p->protocol;
1819 irc->otr->todo = p->next;
1820 g_free(p);
1821 return TRUE; /* keep watching */
1822 } else {
1823 /* okay, the slave is idle now, so kill him */
1824 fclose(irc->otr->from);
1825 fclose(irc->otr->to);
1826 irc->otr->from = irc->otr->to = NULL;
1827 kill(irc->otr->keygen, SIGTERM);
1828 waitpid(irc->otr->keygen, NULL, 0);
1829 irc->otr->keygen = 0;
1830 return FALSE; /* unregister ourselves */
1834 void copyfile(const char *a, const char *b)
1836 int fda, fdb;
1837 int n;
1838 char buf[1024];
1840 fda = open(a, O_RDONLY);
1841 fdb = open(b, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1843 while((n=read(fda, buf, 1024)) > 0)
1844 write(fdb, buf, n);
1846 close(fda);
1847 close(fdb);
1850 void myfgets(char *s, int size, FILE *stream)
1852 if(!fgets(s, size, stream)) {
1853 s[0] = '\0';
1854 } else {
1855 int n = strlen(s);
1856 if(n>0 && s[n-1] == '\n')
1857 s[n-1] = '\0';
1861 void yes_keygen(void *data)
1863 account_t *acc = (account_t *)data;
1864 irc_t *irc = acc->bee->ui_data;
1866 if(keygen_in_progress(irc, acc->user, acc->prpl->name)) {
1867 irc_rootmsg(irc, "keygen for %s/%s already in progress",
1868 acc->user, acc->prpl->name);
1869 } else {
1870 irc_rootmsg(irc, "starting background keygen for %s/%s",
1871 acc->user, acc->prpl->name);
1872 irc_rootmsg(irc, "you will be notified when it completes");
1873 otr_keygen(irc, acc->user, acc->prpl->name);
1877 /* check whether a string is safe to use in a path component */
1878 int strsane(const char *s)
1880 return strpbrk(s, "/\\") == NULL;
1883 /* vim: set noet ts=4 sw=4: */