2
* Off-the-Record Messaging (OTR) module for the irssi IRC client
3
* Copyright (C) 2008 Uli Meis <a.sporto+bee@gmail.com>
5
* This program is free software; you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation; either version 2 of the License, or
8
* (at your option) any later version.
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
15
* You should have received a copy of the GNU General Public License
16
* along with this program; if not, write to the Free Software
17
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,USA
24
OtrlUserState otr_state = NULL;
25
extern OtrlMessageAppOps otr_ops;
26
static int otrinited = FALSE;
27
GSList *plistunknown = NULL;
28
GSList *plistknown = NULL;
31
GRegex *regex_policies;
45
otr_state = otrl_userstate_create();
47
/* load keys and fingerprints */
56
g_regex_new("([^,]+) (never|manual|handlews|opportunistic|always)"
60
return otr_state==NULL;
70
otrl_userstate_free(otr_state);
76
otr_setpolicies("",FALSE);
77
otr_setpolicies("",TRUE);
80
g_regex_unref(regex_policies);
89
void context_free_app_info(void *data)
91
struct co_info *coi = data;
93
g_free(coi->msgqueue);
96
IRCCTX_FREE(coi->ircctx);
100
* Add app data to context.
101
* See struct co_info for details.
103
void context_add_app_info(void *data,ConnContext *co)
105
IRC_CTX *ircctx = IRCCTX_DUP(data);
106
struct co_info *coi = g_malloc(sizeof(struct co_info));
108
memset(coi,0,sizeof(struct co_info));
110
co->app_data_free = context_free_app_info;
112
coi->ircctx = ircctx;
113
sprintf(coi->better_msg_two,formats[TXT_OTR_BETTER_TWO].def,co->accountname);
117
* Get a context from a pair.
119
ConnContext *otr_getcontext(const char *accname,const char *nick,
120
int create,void *data)
122
ConnContext *co = otrl_context_find(
129
context_add_app_info,
132
/* context came from a fingerprint */
133
if (co&&data&&!co->app_data)
134
context_add_app_info(data,co);
140
* Hand the given message to OTR.
141
* Returns NULL if OTR handled the message and
142
* the original message otherwise.
144
char *otr_send(IRC_CTX *ircctx, const char *msg,const char *to)
146
const char *nick = IRCCTX_NICK(ircctx);
147
const char *address = IRCCTX_ADDR(ircctx);
149
char *newmessage = NULL;
153
sprintf(accname, "%s@%s", nick, address);
155
err = otrl_message_sending(
165
context_add_app_info,
169
otr_notice(ircctx,to,TXT_SEND_FAILED,msg);
173
if (newmessage==NULL)
176
/* OTR message. Need to do fragmentation */
178
if (!(co = otr_getcontext(accname,to,FALSE,ircctx))) {
179
otr_notice(ircctx,to,TXT_SEND_CHANGE);
183
err = otrl_message_fragment_and_send(
188
OTRL_FRAGMENT_SEND_ALL,
192
otr_notice(ircctx,to,TXT_SEND_FRAGMENT,msg);
194
otr_debug(ircctx,to,TXT_SEND_CONVERTED,newmessage);
199
struct ctxlist_ *otr_contexts() {
200
ConnContext *context;
202
struct ctxlist_ *ctxlist = NULL, *ctxhead = NULL;
203
struct fplist_ *fplist,*fphead;
208
for(context = otr_state->context_root; context;
209
context = context->next) {
211
ctxhead = ctxlist = g_malloc0(sizeof(struct ctxlist_));
213
ctxlist = ctxlist->next = g_malloc0(sizeof(struct
215
switch (context->msgstate) {
216
case OTRL_MSGSTATE_PLAINTEXT: ctxlist->state = STUNENCRYPTED;break;
217
case OTRL_MSGSTATE_ENCRYPTED: ctxlist->state = STENCRYPTED;break;
218
case OTRL_MSGSTATE_FINISHED: ctxlist->state = STFINISHED;break;
219
default: ctxlist->state = STUNKNOWN;break;
221
ctxlist->username = context->username;
222
ctxlist->accountname = context->accountname;
224
fplist = fphead = NULL;
225
for (fprint = context->fingerprint_root.next; fprint;
226
fprint = fprint->next) {
228
fphead = fplist = g_malloc0(sizeof(struct
231
fplist = fplist->next = g_malloc0(sizeof(struct
233
trust = fprint->trust ? : "";
235
sprintf(fp+i*2, "%02x",
236
fprint->fingerprint[i]);
237
fplist->fp = g_strdup(fp);
239
fplist->authby = NOAUTH;
240
else if (strcmp(trust,"smp")==0)
241
fplist->authby = AUTHSMP;
243
fplist->authby = AUTHMAN;
246
ctxlist->fplist = fphead;
252
* Get the OTR status of this conversation.
254
int otr_getstatus(char *mynick, char *nick, char *server)
260
sprintf(accname, "%s@%s", mynick, server);
262
if (!(co = otr_getcontext(accname,nick,FALSE,NULL))) {
268
switch (co->msgstate) {
269
case OTRL_MSGSTATE_PLAINTEXT:
270
return TXT_ST_PLAINTEXT;
271
case OTRL_MSGSTATE_ENCRYPTED: {
272
char *trust = co->active_fingerprint->trust;
273
int ex = co->smstate->nextExpected;
275
if (trust&&(*trust!='\0'))
276
return strcmp(trust,"smp")==0 ? TXT_ST_TRUST_SMP : TXT_ST_TRUST_MANUAL;
279
case OTRL_SMP_EXPECT1:
280
return TXT_ST_UNTRUSTED;
281
case OTRL_SMP_EXPECT2:
282
return TXT_ST_SMP_WAIT_2;
283
case OTRL_SMP_EXPECT3:
284
case OTRL_SMP_EXPECT4:
285
return TXT_ST_SMP_FINALIZE;
287
return TXT_ST_SMP_UNKNOWN;
290
case OTRL_MSGSTATE_FINISHED:
291
return TXT_ST_FINISHED;
293
return TXT_ST_UNKNOWN;
298
* Finish the conversation.
300
void otr_finish(IRC_CTX *ircctx, char *nick, const char *peername, int inquery)
305
char *pserver = NULL;
308
pserver = strchr(peername,'@');
311
ircctx = server_find_address(pserver+1);
315
nick = (char*)peername;
318
sprintf((char*)accname, "%s@%s", IRCCTX_NICK(ircctx), IRCCTX_ADDR(ircctx));
320
if (!(co = otr_getcontext(accname,nick,FALSE,NULL))) {
322
otr_noticest(TXT_CTX_NOT_FOUND,
329
otrl_message_disconnect(otr_state,&otr_ops,ircctx,accname,
333
otr_info(ircctx,nick,TXT_CMD_FINISH,nick,IRCCTX_ADDR(ircctx));
335
otr_infost(TXT_CMD_FINISH,nick,IRCCTX_ADDR(ircctx));
340
/* finish if /otr finish has been issued. Reset if
341
* we're called cause the query window has been closed. */
343
coi->finished = inquery;
351
ConnContext *context;
354
for(context = otr_state->context_root; context;
355
context = context->next) {
356
struct co_info *coi = context->app_data;
358
if (context->msgstate!=OTRL_MSGSTATE_ENCRYPTED)
361
otrl_message_disconnect(otr_state,&otr_ops,coi->ircctx,
362
context->accountname,
366
otr_infost(TXT_CMD_FINISH,context->username,
367
IRCCTX_ADDR(coi->ircctx));
372
otr_infost(TXT_CMD_FINISHALL_NONE);
378
void otr_trust(IRC_CTX *ircctx, char *nick, const char *peername)
383
char *pserver = NULL;
386
pserver = strchr(peername,'@');
389
ircctx = server_find_address(pserver+1);
393
nick = (char*)peername;
396
sprintf((char*)accname, "%s@%s", IRCCTX_NICK(ircctx), IRCCTX_ADDR(ircctx));
398
if (!(co = otr_getcontext(accname,nick,FALSE,NULL))) {
399
otr_noticest(TXT_CTX_NOT_FOUND,
406
otrl_context_set_trust(co->active_fingerprint,"manual");
409
coi->smp_failed = FALSE;
411
otr_notice(ircctx,nick,TXT_FP_TRUST,nick);
418
* Abort any ongoing SMP authentication.
420
void otr_abort_auth(ConnContext *co, IRC_CTX *ircctx, const char *nick)
426
coi->received_smp_init = FALSE;
428
otr_notice(ircctx,nick,
429
co->smstate->nextExpected!=OTRL_SMP_EXPECT1 ?
430
TXT_AUTH_ABORTED_ONGOING :
433
otrl_message_abort_smp(otr_state,&otr_ops,ircctx,co);
437
* implements /otr authabort
439
void otr_authabort(IRC_CTX *ircctx, char *nick, const char *peername)
443
char *pserver = NULL;
446
pserver = strchr(peername,'@');
449
ircctx = server_find_address(pserver+1);
453
nick = (char*)peername;
456
sprintf((char*)accname, "%s@%s", IRCCTX_NICK(ircctx), IRCCTX_ADDR(ircctx));
458
if (!(co = otr_getcontext(accname,nick,FALSE,NULL))) {
459
otr_noticest(TXT_CTX_NOT_FOUND,
466
otr_abort_auth(co,ircctx,nick);
473
* Initiate or respond to SMP authentication.
475
void otr_auth(IRC_CTX *ircctx, char *nick, const char *peername, const char *secret)
480
char *pserver = NULL;
483
pserver = strchr(peername,'@');
486
ircctx = server_find_address(pserver+1);
490
nick = (char*)peername;
493
sprintf((char*)accname, "%s@%s", IRCCTX_NICK(ircctx), IRCCTX_ADDR(ircctx));
495
if (!(co = otr_getcontext(accname,nick,FALSE,NULL))) {
496
otr_noticest(TXT_CTX_NOT_FOUND,
503
if (co->msgstate!=OTRL_MSGSTATE_ENCRYPTED) {
504
otr_notice(ircctx,nick,TXT_AUTH_NEEDENC);
510
/* Aborting an ongoing auth */
511
if (co->smstate->nextExpected!=OTRL_SMP_EXPECT1)
512
otr_abort_auth(co,ircctx,nick);
514
coi->smp_failed = FALSE;
516
/* reset trust level */
517
if (co->active_fingerprint) {
518
char *trust = co->active_fingerprint->trust;
519
if (trust&&(*trust!='\0')) {
520
otrl_context_set_trust(co->active_fingerprint, "");
525
if (!coi->received_smp_init)
526
otrl_message_initiate_smp(
531
(unsigned char*)secret,
534
otrl_message_respond_smp(
539
(unsigned char*)secret,
542
otr_notice(ircctx,nick,
543
coi->received_smp_init ?
544
TXT_AUTH_RESPONDING :
547
statusbar_items_redraw("otr");
554
* Handles incoming TLVs of the SMP authentication type. We're not only updating
555
* our own state but also giving libotr a leg up so it gets through the auth.
557
void otr_handle_tlvs(OtrlTLV *tlvs, ConnContext *co,
559
IRC_CTX *ircctx, const char *from)
563
OtrlTLV *tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP1);
565
if (co->smstate->nextExpected != OTRL_SMP_EXPECT1) {
566
otr_notice(ircctx,from,TXT_AUTH_HAVE_OLD,
570
otr_notice(ircctx,from,TXT_AUTH_PEER,
572
coi->received_smp_init = TRUE;
576
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP2);
578
if (co->smstate->nextExpected != OTRL_SMP_EXPECT2) {
579
otr_notice(ircctx,from,
580
TXT_AUTH_PEER_REPLY_WRONG,
584
otr_notice(ircctx,from,
585
TXT_AUTH_PEER_REPLIED,
587
co->smstate->nextExpected = OTRL_SMP_EXPECT4;
591
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP3);
593
if (co->smstate->nextExpected != OTRL_SMP_EXPECT3) {
594
otr_notice(ircctx,from,
595
TXT_AUTH_PEER_WRONG_SMP3,
599
char *trust = co->active_fingerprint->trust;
600
if (trust&&(*trust!='\0')) {
601
otr_notice(ircctx,from,
602
TXT_AUTH_SUCCESSFUL);
604
otr_notice(ircctx,from,
606
coi->smp_failed = TRUE;
608
co->smstate->nextExpected = OTRL_SMP_EXPECT1;
609
coi->received_smp_init = FALSE;
613
tlv = otrl_tlv_find(tlvs, OTRL_TLV_SMP4);
615
if (co->smstate->nextExpected != OTRL_SMP_EXPECT4) {
616
otr_notice(ircctx,from,
617
TXT_AUTH_PEER_WRONG_SMP4,
621
char *trust = co->active_fingerprint->trust;
622
if (trust&&(*trust!='\0')) {
623
otr_notice(ircctx,from,
624
TXT_AUTH_SUCCESSFUL);
626
/* unreachable since 4 is never sent out on
628
otr_notice(ircctx,from,
630
coi->smp_failed = TRUE;
632
co->smstate->nextExpected = OTRL_SMP_EXPECT1;
633
coi->received_smp_init = FALSE;
637
otr_abort_auth(co,ircctx,from);
639
tlv = otrl_tlv_find(tlvs, OTRL_TLV_DISCONNECTED);
641
otr_notice(ircctx,from,TXT_PEER_FINISHED,from);
643
statusbar_items_redraw("otr");
647
* Hand the given message to OTR.
648
* Returns NULL if its an OTR protocol message and
649
* the (possibly) decrypted message otherwise.
651
char *otr_receive(IRC_CTX *ircctx, const char *msg,const char *from)
654
char *newmessage = NULL;
661
sprintf(accname, "%s@%s", IRCCTX_NICK(ircctx), IRCCTX_ADDR(ircctx));
663
if (!(co = otr_getcontext(accname,from,TRUE,ircctx))) {
664
otr_noticest(TXT_CTX_NOT_CREATE,
671
/* Really lame but I don't see how you could do this in a generic
672
* way unless the IRC server would somehow mark continuation messages.
674
if ((strcmp(msg,coi->better_msg_two)==0)||
675
(strcmp(msg,formats[TXT_OTR_BETTER_THREE].def)==0)) {
676
otr_debug(ircctx,from,TXT_RECEIVE_IGNORE_QUERY);
680
/* The server might have split lines that were too long
681
* (bitlbee does that). The heuristic is simple: If we can find ?OTR:
682
* in the message but it doesn't end with a ".", queue it and wait
685
lastmsg = co->app_data;
687
if (coi->msgqueue) { /* already something in the queue */
688
strcpy(coi->msgqueue+strlen(coi->msgqueue),msg);
691
if ((strlen(msg)>OTR_MAX_MSG_SIZE)&&
692
(msg[strlen(msg)-1]!='.')&&
693
(msg[strlen(msg)-1]!=','))
696
otr_debug(ircctx,from,TXT_RECEIVE_DEQUEUED,
697
strlen(coi->msgqueue));
700
coi->msgqueue = NULL;
702
/* this is freed thru our caller by otrl_message_free.
703
* Currently ok since that just uses free().
706
} else if (strstr(msg,"?OTR:")&&
707
(strlen(msg)>OTR_MAX_MSG_SIZE)&&
708
(msg[strlen(msg)-1]!='.')&&
709
(msg[strlen(msg)-1]!=',')) {
710
coi->msgqueue = malloc(4096*sizeof(char));
711
strcpy(coi->msgqueue,msg);
712
otr_debug(ircctx,from,TXT_RECEIVE_QUEUED,strlen(msg));
716
ignore_message = otrl_message_receiving(
730
otr_handle_tlvs(tlvs,co,coi,ircctx,from);
732
if (ignore_message) {
733
otr_debug(ircctx,from,
734
TXT_RECEIVE_IGNORE, strlen(msg),accname,from,msg);
739
otr_debug(ircctx,from,TXT_RECEIVE_CONVERTED);
741
return newmessage ? : (char*)msg;
744
void otr_setpolicies(const char *policies, int known)
747
GMatchInfo *match_info;
748
GSList *plist = known ? plistknown : plistunknown;
753
struct plistentry *ple = p->data;
754
g_pattern_spec_free(ple->namepat);
756
} while ((p = g_slist_next(p)));
762
g_regex_match(regex_policies,policies,0,&match_info);
764
while(g_match_info_matches(match_info)) {
765
struct plistentry *ple = (struct plistentry *)g_malloc0(sizeof(struct plistentry));
766
char *pol = g_match_info_fetch(match_info, 2);
768
ple->namepat = g_pattern_spec_new(g_match_info_fetch(match_info, 1));
772
ple->policy = OTRL_POLICY_NEVER;
775
ple->policy = OTRL_POLICY_MANUAL;
778
ple->policy = OTRL_POLICY_MANUAL|OTRL_POLICY_WHITESPACE_START_AKE;
781
ple->policy = OTRL_POLICY_OPPORTUNISTIC;
784
ple->policy = OTRL_POLICY_ALWAYS;
788
plist = g_slist_append(plist,ple);
792
g_match_info_next(match_info, NULL);
795
g_match_info_free(match_info);
800
plistunknown = plist;