aboutsummaryrefslogtreecommitdiffstats
path: root/otr.c
diff options
context:
space:
mode:
Diffstat (limited to 'otr.c')
-rw-r--r--otr.c286
1 files changed, 236 insertions, 50 deletions
diff --git a/otr.c b/otr.c
index 78952699..60581ee6 100644
--- a/otr.c
+++ b/otr.c
@@ -34,8 +34,6 @@ void op_write_fingerprints(void *opdata);
void op_gone_secure(void *opdata, ConnContext *context);
-void op_gone_secure(void *opdata, ConnContext *context);
-
void op_gone_insecure(void *opdata, ConnContext *context);
void op_still_secure(void *opdata, ConnContext *context, int is_reply);
@@ -52,7 +50,8 @@ const char *op_account_name(void *opdata, const char *account, const char *proto
/* TODO: void cmd_otr_keygen(irc_t *irc, char **args); */
void cmd_otr_abort(irc_t *irc, char **args); /* TODO: does this cmd even make sense? */
void cmd_otr_request(irc_t *irc, char **args); /* TODO: do we even need this? */
-void cmd_otr_auth(irc_t *irc, char **args);
+void cmd_otr_smp(irc_t *irc, char **args);
+void cmd_otr_trust(irc_t *irc, char **args);
/* TODO: void cmd_otr_affirm(irc_t *irc, char **args); */
void cmd_otr_fprints(irc_t *irc, char **args);
void cmd_otr_info(irc_t *irc, char **args);
@@ -61,7 +60,8 @@ void cmd_otr_policy(irc_t *irc, char **args);
const command_t otr_commands[] = {
{ "abort", 1, &cmd_otr_abort, 0 },
{ "request", 1, &cmd_otr_request, 0 },
- { "auth", 2, &cmd_otr_auth, 0 },
+ { "smp", 2, &cmd_otr_smp, 0 },
+ { "trust", 6, &cmd_otr_trust, 0 },
{ "fprints", 0, &cmd_otr_fprints, 0 },
{ "info", 1, &cmd_otr_info, 0 },
{ "policy", 0, &cmd_otr_policy, 0 },
@@ -96,12 +96,22 @@ void no_keygen(gpointer w, void *data);
struct im_connection *check_imc(void *opdata, const char *accountname,
const char *protocol);
-/* determine the nick for a given handle/protocol pair */
+/* determine the nick for a given handle/protocol pair
+ returns "handle/protocol" if not found */
const char *peernick(irc_t *irc, const char *handle, const char *protocol);
+/* determine the user_t for a given handle/protocol pair
+ returns NULL if not found */
+user_t *peeruser(irc_t *irc, const char *handle, const char *protocol);
+
/* handle SMP TLVs from a received message */
void otr_handle_smp(struct im_connection *ic, const char *handle, OtrlTLV *tlvs);
+/* update op/voice flag of given user according to encryption state and settings
+ returns 0 if neither op_buddies nor voice_buddies is set to "encrypted",
+ i.e. msgstate should be announced seperately */
+int otr_update_modeflags(irc_t *irc, user_t *u);
+
/* show the list of fingerprints associated with a given context */
void show_fingerprints(irc_t *irc, ConnContext *ctx);
@@ -238,8 +248,15 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha
char *colormsg;
if(!g_mutex_trylock(ic->irc->otr_mutex)) {
- /* TODO: queue msgs received during keygen for later */
- irc_usermsg(ic->irc, "msg from %s during keygen - dropped",
+ user_t *u = user_findhandle(ic, handle);
+
+ /* fallback for non-otr clients */
+ if(u && !u->encrypted) {
+ return g_strdup(msg);
+ }
+
+ /* TODO: queue msgs received during keygen for later? */
+ irc_usermsg(ic->irc, "otr msg from %s during keygen - dropped",
peernick(ic->irc, handle, ic->acc->prpl->name));
return NULL;
}
@@ -282,14 +299,24 @@ char *otr_handle_message(struct im_connection *ic, const char *handle, const cha
int otr_send_message(struct im_connection *ic, const char *handle, const char *msg, int flags)
{
- int st;
- char *otrmsg = NULL;
- ConnContext *ctx = NULL;
-
+ int st;
+ char *otrmsg = NULL;
+ ConnContext *ctx = NULL;
+
if(!g_mutex_trylock(ic->irc->otr_mutex)) {
- irc_usermsg(ic->irc, "msg to %s during keygen - not sent",
- peernick(ic->irc, handle, ic->acc->prpl->name));
- return 1;
+ user_t *u = user_findhandle(ic, handle);
+
+ /* Fallback for non-otr clients.
+ Yes, we must be very sure this doesn't send stuff in the clear where it
+ shouldn't... */
+ if(u && !u->encrypted) {
+ return ic->acc->prpl->buddy_msg(ic, (char *)handle, (char *)msg, flags);
+ }
+
+ /* otherwise refuse to send */
+ irc_usermsg(ic->irc, "otr msg to %s not sent during keygen",
+ peernick(ic->irc, handle, ic->acc->prpl->name));
+ return 1;
}
st = otrl_message_sending(ic->irc->otr_us, &global.otr_ops, ic,
@@ -413,14 +440,17 @@ void op_inject_message(void *opdata, const char *accountname,
}
int op_display_otr_message(void *opdata, const char *accountname,
- const char *protocol, const char *username, const char *msg)
+ const char *protocol, const char *username, const char *message)
{
struct im_connection *ic = check_imc(opdata, accountname, protocol);
+ char *msg = g_strdup(message);
- log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, msg);
+ log_message(LOGLVL_DEBUG, "op_display_otr_message '%s' '%s' '%s' '%s'", accountname, protocol, username, message);
- irc_usermsg(ic->irc, "%s", msg);
+ strip_html(msg);
+ irc_usermsg(ic->irc, "otr: %s", msg);
+ g_free(msg);
return 0;
}
@@ -451,51 +481,84 @@ void op_gone_secure(void *opdata, ConnContext *context)
{
struct im_connection *ic =
check_imc(opdata, context->accountname, context->protocol);
+ user_t *u;
log_message(LOGLVL_DEBUG, "op_gone_secure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
- irc_usermsg(ic->irc, "conversation with %s is now off the record",
- peernick(ic->irc, context->username, context->protocol));
+ u = peeruser(ic->irc, context->username, context->protocol);
+ if(!u) {
+ log_message(LOGLVL_ERROR,
+ "BUG: otr.c: op_gone_secure: user_t for %s/%s not found!",
+ context->username, context->protocol);
+ return;
+ }
+ if(context->active_fingerprint->trust[0])
+ u->encrypted = 2;
+ else
+ u->encrypted = 1;
+ if(!otr_update_modeflags(ic->irc, u))
+ irc_usermsg(ic->irc, "conversation with %s is now off the record", u->nick);
}
void op_gone_insecure(void *opdata, ConnContext *context)
{
struct im_connection *ic =
check_imc(opdata, context->accountname, context->protocol);
+ user_t *u;
log_message(LOGLVL_DEBUG, "op_gone_insecure '%s' '%s' '%s'", context->accountname, context->protocol, context->username);
- irc_usermsg(ic->irc, "conversation with %s is now in the clear",
- peernick(ic->irc, context->username, context->protocol));
+ u = peeruser(ic->irc, context->username, context->protocol);
+ if(!u) {
+ log_message(LOGLVL_ERROR,
+ "BUG: otr.c: op_gone_insecure: user_t for %s/%s not found!",
+ context->username, context->protocol);
+ return;
+ }
+ u->encrypted = 0;
+ if(!otr_update_modeflags(ic->irc, u))
+ irc_usermsg(ic->irc, "conversation with %s is now in the clear", u->nick);
}
void op_still_secure(void *opdata, ConnContext *context, int is_reply)
{
struct im_connection *ic =
check_imc(opdata, context->accountname, context->protocol);
+ user_t *u;
log_message(LOGLVL_DEBUG, "op_still_secure '%s' '%s' '%s' is_reply=%d",
context->accountname, context->protocol, context->username, is_reply);
- irc_usermsg(ic->irc, "otr connection with %s has been refreshed",
- peernick(ic->irc, context->username, context->protocol));
+ u = peeruser(ic->irc, context->username, context->protocol);
+ if(!u) {
+ log_message(LOGLVL_ERROR,
+ "BUG: otr.c: op_still_secure: user_t for %s/%s not found!",
+ context->username, context->protocol);
+ return;
+ }
+ if(context->active_fingerprint->trust[0])
+ u->encrypted = 2;
+ else
+ u->encrypted = 1;
+ if(!otr_update_modeflags(ic->irc, u))
+ irc_usermsg(ic->irc, "otr connection with %s has been refreshed", u->nick);
}
void op_log_message(void *opdata, const char *message)
{
- log_message(LOGLVL_INFO, "%s", message);
+ char *msg = g_strdup(message);
+
+ strip_html(msg);
+ log_message(LOGLVL_INFO, "otr: %s", msg);
+ g_free(msg);
}
int op_max_message_size(void *opdata, ConnContext *context)
{
- /* TODO: make max_message_size a property of the prpl.
- the values here are taken from the libotr UPGRADING file */
- if(!strcmp(context->protocol, "msn"))
- return 1409;
- if(!strcmp(context->protocol, "yahoo"))
- return 832;
- if(!strcmp(context->protocol, "oscar"))
- return 2343;
+ struct im_connection *ic =
+ check_imc(opdata, context->accountname, context->protocol);
+
+ return ic->acc->prpl->mms;
}
const char *op_account_name(void *opdata, const char *account, const char *protocol)
@@ -541,7 +604,7 @@ void cmd_otr_request(irc_t *irc, char **args)
imc_buddy_msg(u->ic, u->handle, "?OTR?", 0);
}
-void cmd_otr_auth(irc_t *irc, char **args)
+void cmd_otr_smp(irc_t *irc, char **args)
{
user_t *u;
ConnContext *ctx;
@@ -588,6 +651,80 @@ void cmd_otr_auth(irc_t *irc, char **args)
}
}
+int hexval(char a)
+{
+ int x=tolower(a);
+
+ if(x>='a' && x<='f')
+ x = x - 'a' + 10;
+ else if(x>='0' && x<='9')
+ x = x - '0';
+ else
+ return -1;
+
+ return x;
+}
+
+void cmd_otr_trust(irc_t *irc, char **args)
+{
+ user_t *u;
+ ConnContext *ctx;
+ unsigned char raw[20];
+ Fingerprint *fp;
+ int i,j;
+
+ u = user_find(irc, args[1]);
+ if(!u || !u->ic) {
+ irc_usermsg(irc, "%s: unknown user", args[1]);
+ return;
+ }
+
+ ctx = otrl_context_find(irc->otr_us, u->handle,
+ u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
+ if(!ctx) {
+ irc_usermsg(irc, "%s: no otr context with user", args[1]);
+ return;
+ }
+
+ /* convert given fingerprint to raw representation */
+ for(i=0; i<5; i++) {
+ for(j=0; j<4; j++) {
+ char *p = args[2+i]+(2*j);
+ char *q = p+1;
+ int x, y;
+
+ if(!*p || !*q) {
+ irc_usermsg(irc, "failed: truncated fingerprint block %d", i+1);
+ return;
+ }
+
+ x = hexval(*p);
+ y = hexval(*q);
+ if(x<0) {
+ irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+1, i+1);
+ return;
+ }
+ if(y<0) {
+ irc_usermsg(irc, "failed: %d. hex digit of block %d out of range", 2*j+2, i+1);
+ return;
+ }
+
+ raw[i*4+j] = x*16 + y;
+ }
+ }
+ fp = otrl_context_find_fingerprint(ctx, raw, 0, NULL);
+ if(!fp) {
+ irc_usermsg(irc, "failed: no such fingerprint for %s", args[1]);
+ } else {
+ char *trust = args[7] ? args[7] : "affirmed";
+ otrl_context_set_trust(fp, trust);
+ irc_usermsg(irc, "fingerprint match, trust set to \"%s\"", trust);
+ if(u->encrypted)
+ u->encrypted = 2;
+ otr_update_modeflags(irc, u);
+ }
+}
+
void cmd_otr_fprints(irc_t *irc, char **args)
{
if(args[1]) {
@@ -648,7 +785,7 @@ void cmd_otr_info(irc_t *irc, char **args)
ctx = otrl_context_find(irc->otr_us, u->handle,
u->ic->acc->user, u->ic->acc->prpl->name, 0, NULL, NULL, NULL);
if(!ctx) {
- irc_usermsg(irc, "no otr info on %s", args[1]);
+ irc_usermsg(irc, "no otr context with %s", args[1]);
return;
}
@@ -671,18 +808,21 @@ void cmd_otr_info(irc_t *irc, char **args)
ctx->username, ctx->protocol, ctx->accountname, ctx->protocol);
irc_usermsg(irc, " otr offer status: %s", offer_status);
irc_usermsg(irc, " connection state: %s", message_state);
- irc_usermsg(irc, " protocol version: %d", ctx->protocol_version);
- fp = ctx->active_fingerprint;
- if(!fp) {
- irc_usermsg(irc, " active f'print: none");
- } else {
- otrl_privkey_hash_to_human(human, fp->fingerprint);
- if(!fp->trust || fp->trust[0] == '\0') {
- trust="untrusted";
+
+ if(ctx->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
+ irc_usermsg(irc, " protocol version: %d", ctx->protocol_version);
+ fp = ctx->active_fingerprint;
+ if(!fp) {
+ irc_usermsg(irc, " active f'print: none?");
} else {
- trust=fp->trust;
+ otrl_privkey_hash_to_human(human, fp->fingerprint);
+ if(!fp->trust || fp->trust[0] == '\0') {
+ trust="untrusted";
+ } else {
+ trust=fp->trust;
+ }
+ irc_usermsg(irc, " active f'print: %s (%s)", human, trust);
}
- irc_usermsg(irc, " active f'print: %s (%s)", human, trust);
}
}
@@ -808,12 +948,10 @@ struct im_connection *check_imc(void *opdata, const char *accountname,
return ic;
}
-const char *peernick(irc_t *irc, const char *handle, const char *protocol)
+user_t *peeruser(irc_t *irc, const char *handle, const char *protocol)
{
user_t *u;
- static char fallback[512];
- g_snprintf(fallback, 511, "%s/%s", handle, protocol);
for(u=irc->users; u; u=u->next) {
struct prpl *prpl;
if(!u->ic || !u->handle)
@@ -821,11 +959,55 @@ const char *peernick(irc_t *irc, const char *handle, const char *protocol)
prpl = u->ic->acc->prpl;
if(strcmp(prpl->name, protocol) == 0
&& prpl->handle_cmp(u->handle, handle) == 0) {
- return u->nick;
+ return u;
}
}
- return fallback;
+ return NULL;
+}
+
+const char *peernick(irc_t *irc, const char *handle, const char *protocol)
+{
+ static char fallback[512];
+
+ user_t *u = peeruser(irc, handle, protocol);
+ if(u) {
+ return u->nick;
+ } else {
+ g_snprintf(fallback, 511, "%s/%s", handle, protocol);
+ return fallback;
+ }
+}
+
+int otr_update_modeflags(irc_t *irc, user_t *u)
+{
+ char *vo = set_getstr(&irc->set, "voice_buddies");
+ char *oo = set_getstr(&irc->set, "op_buddies");
+ char eflag=0, tflag=0;
+ int e = u->encrypted;
+ int t = (u->encrypted > 1);
+
+ if(!strcmp(vo, "encrypted"))
+ eflag='v';
+ else if(!strcmp(oo, "encrypted"))
+ eflag='o';
+ if(!strcmp(vo, "trusted"))
+ tflag='v';
+ else if(!strcmp(oo, "trusted"))
+ tflag='o';
+
+ if(!eflag)
+ return 0;
+
+ if(tflag) {
+ irc_write( irc, ":%s!%s@%s MODE %s %c%c%c%c %s %s", irc->mynick, irc->mynick, irc->myhost,
+ irc->channel, e?'+':'-', eflag, t?'+':'-', tflag, u->nick, u->nick );
+ } else {
+ irc_write( irc, ":%s!%s@%s MODE %s %c%c %s", irc->mynick, irc->mynick, irc->myhost,
+ irc->channel, e?'+':'-', eflag, u->nick );
+ }
+
+ return 1;
}
void show_fingerprints(irc_t *irc, ConnContext *ctx)
@@ -965,9 +1147,13 @@ void no_keygen(gpointer w, void *data)
{
account_t *acc = (account_t *)data;
- /* TODO: remember that we didn't want a key? */
irc_usermsg(acc->irc, "proceeding without key, otr inoperable on %s/%s",
acc->user, acc->prpl->name);
+ /* TODO:
+ irc_usermsg(acc->irc, "setting otr policy for %s/%s to \"never\"",
+ acc->user, acc->prpl->name);
+ set_setstr(acc->set, "otr_policy", "never");
+ */
}