message.c 59.7 KB
Newer Older
cypherpunk's avatar
cypherpunk committed
1 2
/*
 *  Off-the-Record Messaging library
Rob Smits's avatar
Rob Smits committed
3 4
 *  Copyright (C) 2004-2012  Ian Goldberg, Rob Smits, Chris Alexander,
 *  			      Willy Lew, Lisa Du, Nikita Borisov
cypherpunk's avatar
cypherpunk committed
5 6 7 8 9 10 11 12 13 14 15 16 17
 *                           <otr@cypherpunks.ca>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of version 2.1 of the GNU Lesser General
 *  Public License as published by the Free Software Foundation.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
Rob Smits's avatar
Rob Smits committed
18
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
cypherpunk's avatar
cypherpunk committed
19 20 21 22 23 24 25 26 27 28 29 30
 */

/* system headers */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/* libgcrypt headers */
#include <gcrypt.h>

/* libotr headers */
#include "privkey.h"
Rob Smits's avatar
Rob Smits committed
31
#include "userstate.h"
cypherpunk's avatar
cypherpunk committed
32
#include "proto.h"
33
#include "auth.h"
cypherpunk's avatar
cypherpunk committed
34
#include "message.h"
cypherpunk's avatar
cypherpunk committed
35
#include "sm.h"
Rob Smits's avatar
Rob Smits committed
36
#include "instag.h"
cypherpunk's avatar
cypherpunk committed
37 38 39

/* The API version */
extern unsigned int otrl_api_version;
cypherpunk's avatar
cypherpunk committed
40 41 42 43 44 45 46 47

/* How long after sending a packet should we wait to send a heartbeat? */
#define HEARTBEAT_INTERVAL 60

/* How old are messages allowed to be in order to be candidates for
 * resending in response to a rekey? */
#define RESEND_INTERVAL 60

cypherpunk's avatar
 
cypherpunk committed
48 49 50 51 52 53 54 55
/* Send a message to the network, fragmenting first if necessary.
 * All messages to be sent to the network should go through this
 * method immediately before they are sent, ie after encryption. */
static gcry_error_t fragment_and_send(const OtrlMessageAppOps *ops,
	void *opdata, ConnContext *context, const char *message,
	OtrlFragmentPolicy fragPolicy, char **returnFragment)
{
    int mms = 0;
Rob Smits's avatar
Rob Smits committed
56

cypherpunk's avatar
 
cypherpunk committed
57
    if (message && ops->inject_message) {
Rob Smits's avatar
Rob Smits committed
58
	int msglen;
cypherpunk's avatar
 
cypherpunk committed
59

Rob Smits's avatar
Rob Smits committed
60
	if (ops->max_message_size) {
cypherpunk's avatar
 
cypherpunk committed
61
	    mms = ops->max_message_size(opdata, context);
Rob Smits's avatar
Rob Smits committed
62 63
	}
	msglen = strlen(message);
cypherpunk's avatar
 
cypherpunk committed
64 65

	/* Don't incur overhead of fragmentation unless necessary */
Rob Smits's avatar
Rob Smits committed
66
	if(mms != 0 && msglen > mms) {
cypherpunk's avatar
 
cypherpunk committed
67 68 69
	    char **fragments;
	    gcry_error_t err;
	    int i;
Rob Smits's avatar
Rob Smits committed
70 71 72
	    int headerlen = context->protocol_version == 3 ? 37 : 19;
	    /* Like ceil(msglen/(mms - headerlen)) */
	    int fragment_count = ((msglen - 1) / (mms - headerlen)) + 1;
cypherpunk's avatar
 
cypherpunk committed
73 74

	    err = otrl_proto_fragment_create(mms, fragment_count, &fragments,
Rob Smits's avatar
Rob Smits committed
75
		    context, message);
cypherpunk's avatar
 
cypherpunk committed
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
	    if (err) {
		return err;
	    }

	    /* Determine which fragments to send and which to return
	     * based on given Fragment Policy.  If the first fragment
	     * should be returned instead of sent, store it. */
	    if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_FIRST) {
		*returnFragment = strdup(fragments[0]);
	    } else {
		ops->inject_message(opdata, context->accountname,
			context->protocol, context->username, fragments[0]);
	    }
	    for (i=1; i<fragment_count-1; i++) {
		ops->inject_message(opdata, context->accountname,
			context->protocol, context->username, fragments[i]);
	    }
	    /* If the last fragment should be stored instead of sent,
	     * store it */
	    if (fragPolicy == OTRL_FRAGMENT_SEND_ALL_BUT_LAST) {
		*returnFragment = strdup(fragments[fragment_count-1]);
	    } else {
		ops->inject_message(opdata, context->accountname,
Rob Smits's avatar
Rob Smits committed
99 100
			context->protocol, context->username,
			fragments[fragment_count-1]);
cypherpunk's avatar
 
cypherpunk committed
101 102 103 104 105 106 107
	    }
	    /* Now free all fragment memory */
	    otrl_proto_fragment_free(&fragments, fragment_count);

	} else {
	    /* No fragmentation necessary */
	    if (fragPolicy == OTRL_FRAGMENT_SEND_ALL) {
Rob Smits's avatar
Rob Smits committed
108 109
		ops->inject_message(opdata, context->accountname,
			context->protocol, context->username, message);
cypherpunk's avatar
 
cypherpunk committed
110 111
	    } else {
		/* Copy and return the entire given message. */
Rob Smits's avatar
Rob Smits committed
112
		*returnFragment = strdup(message);
cypherpunk's avatar
 
cypherpunk committed
113 114 115
	    }
	}
    }
Rob Smits's avatar
Rob Smits committed
116

cypherpunk's avatar
 
cypherpunk committed
117 118 119
    return gcry_error(GPG_ERR_NO_ERROR);
}

Rob Smits's avatar
Rob Smits committed
120 121 122 123 124 125 126 127 128 129 130
static void populate_context_instag(OtrlUserState us, const OtrlMessageAppOps
	*ops, void *opdata, const char *accountname, const char *protocol,
	ConnContext *context) {
    OtrlInsTag *p_instag;

    p_instag = otrl_instag_find(us, accountname, protocol);
    if ((!p_instag) && ops->create_instag) {
	ops->create_instag(opdata, accountname, protocol);
	p_instag = otrl_instag_find(us, accountname, protocol);
    }

131 132 133 134
    if (p_instag && p_instag->instag >= OTRL_MIN_VALID_INSTAG) {
	context->our_instance = p_instag->instag;
    } else {
	context->our_instance = otrl_instag_get_new();
Rob Smits's avatar
Rob Smits committed
135 136 137
    }
}

cypherpunk's avatar
cypherpunk committed
138 139 140 141 142 143 144 145 146 147 148 149 150
/* Deallocate a message allocated by other otrl_message_* routines. */
void otrl_message_free(char *message)
{
    free(message);
}

/* Handle a message about to be sent to the network.  It is safe to pass
 * all messages about to be sent to this routine.  add_appdata is a
 * function that will be called in the event that a new ConnContext is
 * created.  It will be passed the data that you supplied, as well as a
 * pointer to the new ConnContext.  You can use this to add
 * application-specific information to the ConnContext using the
 * "context->app" field, for example.  If you don't need to do this, you
cypherpunk's avatar
 
cypherpunk committed
151
 * can pass NULL for the last two arguments of otrl_message_sending.
cypherpunk's avatar
cypherpunk committed
152 153 154 155
 *
 * tlvs is a chain of OtrlTLVs to append to the private message.  It is
 * usually correct to just pass NULL here.
 *
Rob Smits's avatar
Rob Smits committed
156 157 158 159 160 161 162 163
 * If non-NULL, ops->convert_msg will be called just before encrypting a
 * message.
 *
 * "instag" specifies the instance tag of the buddy (protocol version 3 only).
 * Meta-instances may also be specified (e.g., OTRL_INSTAG_MOST_SECURE).
 * If "contextp" is not NULL, it will be set to the ConnContext used for
 * sending the message.
 *
cypherpunk's avatar
 
cypherpunk committed
164 165 166 167 168 169 170 171 172
 * If no fragmentation or msg injection is wanted, use OTRL_FRAGMENT_SEND_SKIP
 * as the OtrlFragmentPolicy. In this case, this function will assign *messagep
 * with the encrypted msg. If the routine returns non-zero, then the library
 * tried to encrypt the message, but for some reason failed. DO NOT send the
 * message in the clear in that case. If *messagep gets set by the call to
 * something non-NULL, then you should replace your message with the contents
 * of *messagep, and send that instead.
 *
 * Other fragmentation policies are OTRL_FRAGMENT_SEND_ALL,
Rob Smits's avatar
Rob Smits committed
173 174 175
 * OTRL_FRAGMENT_SEND_ALL_BUT_LAST, or OTRL_FRAGMENT_SEND_ALL_BUT_FIRST. In
 * these cases, the appropriate fragments will be automatically sent. For the
 * last two policies, the remaining fragment will be passed in *original_msg.
cypherpunk's avatar
 
cypherpunk committed
176
 *
Rob Smits's avatar
Rob Smits committed
177
 * Call otrl_message_free(*messagep) if you don't need *messagep or when you're
cypherpunk's avatar
cypherpunk committed
178
 * done with it. */
cypherpunk's avatar
 
cypherpunk committed
179 180 181
gcry_error_t otrl_message_sending(OtrlUserState us,
	const OtrlMessageAppOps *ops,
	void *opdata, const char *accountname, const char *protocol,
Rob Smits's avatar
Rob Smits committed
182 183 184
	const char *recipient, otrl_instag_t their_instag,
	const char *original_msg, OtrlTLV *tlvs, char **messagep,
	OtrlFragmentPolicy fragPolicy, ConnContext **contextp,
cypherpunk's avatar
cypherpunk committed
185 186 187
	void (*add_appdata)(void *data, ConnContext *context),
	void *data)
{
Rob Smits's avatar
Rob Smits committed
188
    ConnContext * context = NULL;
cypherpunk's avatar
cypherpunk committed
189
    char * msgtosend;
cypherpunk's avatar
 
cypherpunk committed
190 191
    const char * err_msg;
    gcry_error_t err_code, err;
cypherpunk's avatar
cypherpunk committed
192 193
    OtrlPolicy policy = OTRL_POLICY_DEFAULT;
    int context_added = 0;
Rob Smits's avatar
Rob Smits committed
194 195
    int convert_called = 0;
    char *converted_msg = NULL;
cypherpunk's avatar
cypherpunk committed
196

197 198 199 200
    if (messagep) {
	*messagep = NULL;
    }

cypherpunk's avatar
 
cypherpunk committed
201
    err = gcry_error(GPG_ERR_NO_ERROR);	/* Default to no error */
cypherpunk's avatar
cypherpunk committed
202

Rob Smits's avatar
Rob Smits committed
203 204 205 206
    if (contextp) {
	*contextp = NULL;
    }

cypherpunk's avatar
 
cypherpunk committed
207
    if (!accountname || !protocol || !recipient ||
Rob Smits's avatar
Rob Smits committed
208
		!original_msg || !messagep) {
209
	err = gcry_error(GPG_ERR_INV_VALUE);
Rob Smits's avatar
Rob Smits committed
210
	goto fragment;
cypherpunk's avatar
 
cypherpunk committed
211
    }
cypherpunk's avatar
cypherpunk committed
212 213 214

    /* See if we have a fingerprint for this user */
    context = otrl_context_find(us, recipient, accountname, protocol,
Rob Smits's avatar
Rob Smits committed
215
	    their_instag, 1, &context_added, add_appdata, data);
cypherpunk's avatar
cypherpunk committed
216 217 218 219 220 221

    /* Update the context list if we added one */
    if (context_added && ops->update_context_list) {
	ops->update_context_list(opdata);
    }

Rob Smits's avatar
Rob Smits committed
222 223 224 225 226 227 228 229 230 231
    /* Find or generate the instance tag if needed */
    if (!context->our_instance) {
	populate_context_instag(us, ops, opdata, accountname, protocol,
	    context);
    }

    if (contextp) {
	*contextp = context;
    }

cypherpunk's avatar
cypherpunk committed
232 233 234 235 236 237
    /* Check the policy */
    if (ops->policy) {
	policy = ops->policy(opdata, context);
    }

    /* Should we go on at all? */
238
    if ((policy & OTRL_POLICY_VERSION_MASK) == 0) {
Rob Smits's avatar
Rob Smits committed
239 240
	err =  gcry_error(GPG_ERR_NO_ERROR);
	goto fragment;
cypherpunk's avatar
cypherpunk committed
241 242 243
    }

    /* If this is an OTR Query message, don't encrypt it. */
Rob Smits's avatar
Rob Smits committed
244
    if (otrl_proto_message_type(original_msg) == OTRL_MSGTYPE_QUERY) {
cypherpunk's avatar
cypherpunk committed
245
	/* Replace the "?OTR?" with a custom message */
246
	char *bettermsg = otrl_proto_default_query_msg(accountname, policy);
cypherpunk's avatar
cypherpunk committed
247 248 249
	if (bettermsg) {
	    *messagep = bettermsg;
	}
250
	context->otr_offer = OFFER_SENT;
cypherpunk's avatar
 
cypherpunk committed
251 252
	err = gcry_error(GPG_ERR_NO_ERROR);
	goto fragment;
cypherpunk's avatar
cypherpunk committed
253 254
    }

255 256
    /* What is the current message disposition? */
    switch(context->msgstate) {
Rob Smits's avatar
Rob Smits committed
257

258 259 260 261 262
	case OTRL_MSGSTATE_PLAINTEXT:
	    if ((policy & OTRL_POLICY_REQUIRE_ENCRYPTION)) {
		/* We're trying to send an unencrypted message with a policy
		 * that disallows that.  Don't do that, but try to start
		 * up OTR instead. */
cypherpunk's avatar
 
cypherpunk committed
263 264 265
		if (ops->handle_msg_event) {
		    ops->handle_msg_event(opdata,
			    OTRL_MSGEVENT_ENCRYPTION_REQUIRED,
Rob Smits's avatar
Rob Smits committed
266
			    context, NULL, gcry_error(GPG_ERR_NO_ERROR));
cypherpunk's avatar
cypherpunk committed
267
		}
cypherpunk's avatar
 
cypherpunk committed
268 269

		context->context_priv->lastmessage =
Rob Smits's avatar
Rob Smits committed
270
			gcry_malloc_secure(strlen(original_msg) + 1);
cypherpunk's avatar
 
cypherpunk committed
271
		if (context->context_priv->lastmessage) {
272 273
		    char *bettermsg = otrl_proto_default_query_msg(accountname,
			    policy);
Rob Smits's avatar
Rob Smits committed
274
		    strcpy(context->context_priv->lastmessage, original_msg);
cypherpunk's avatar
 
cypherpunk committed
275
		    context->context_priv->lastsent = time(NULL);
Rob Smits's avatar
Rob Smits committed
276
		    otrl_context_update_recent_child(context, 1);
cypherpunk's avatar
 
cypherpunk committed
277
		    context->context_priv->may_retransmit = 2;
278 279
		    if (bettermsg) {
			*messagep = bettermsg;
Rob Smits's avatar
Rob Smits committed
280
			context->otr_offer = OFFER_SENT;
281
		    } else {
cypherpunk's avatar
 
cypherpunk committed
282 283
			err = gcry_error(GPG_ERR_ENOMEM);
			goto fragment;
284 285 286 287 288 289 290 291
		    }
		}
	    } else {
		if ((policy & OTRL_POLICY_SEND_WHITESPACE_TAG) &&
			context->otr_offer != OFFER_REJECTED) {
		    /* See if this user can speak OTR.  Append the
		     * OTR_MESSAGE_TAG to the plaintext message, and see
		     * if he responds. */
Rob Smits's avatar
Rob Smits committed
292
		    size_t msglen = strlen(original_msg);
293 294 295 296 297
		    size_t basetaglen = strlen(OTRL_MESSAGE_TAG_BASE);
		    size_t v1taglen = (policy & OTRL_POLICY_ALLOW_V1) ?
			strlen(OTRL_MESSAGE_TAG_V1) : 0;
		    size_t v2taglen = (policy & OTRL_POLICY_ALLOW_V2) ?
			strlen(OTRL_MESSAGE_TAG_V2) : 0;
Rob Smits's avatar
Rob Smits committed
298 299
		    size_t v3taglen = (policy & OTRL_POLICY_ALLOW_V3) ?
			strlen(OTRL_MESSAGE_TAG_V3) : 0;
300
		    char *taggedmsg = malloc(msglen + basetaglen + v1taglen
Rob Smits's avatar
Rob Smits committed
301
			    + v2taglen + v3taglen + 1);
302
		    if (taggedmsg) {
Rob Smits's avatar
Rob Smits committed
303
			strcpy(taggedmsg, original_msg);
304 305 306 307 308 309 310 311 312
			strcpy(taggedmsg + msglen, OTRL_MESSAGE_TAG_BASE);
			if (v1taglen) {
			    strcpy(taggedmsg + msglen + basetaglen,
				    OTRL_MESSAGE_TAG_V1);
			}
			if (v2taglen) {
			    strcpy(taggedmsg + msglen + basetaglen + v1taglen,
				    OTRL_MESSAGE_TAG_V2);
			}
Rob Smits's avatar
Rob Smits committed
313 314 315 316
			if (v3taglen) {
			    strcpy(taggedmsg + msglen + basetaglen + v1taglen
				    + v2taglen, OTRL_MESSAGE_TAG_V3);
			}
317
			*messagep = taggedmsg;
318
			context->otr_offer = OFFER_SENT;
319
		    }
cypherpunk's avatar
cypherpunk committed
320 321
		}
	    }
322 323
	    break;
	case OTRL_MSGSTATE_ENCRYPTED:
cypherpunk's avatar
 
cypherpunk committed
324
	    /* convert the original message if necessary */
Rob Smits's avatar
Rob Smits committed
325 326 327 328 329 330 331
	    if (ops->convert_msg) {
		ops->convert_msg(opdata, context, OTRL_CONVERT_SENDING,
			&converted_msg, original_msg);

		if (converted_msg) {
		    convert_called = 1;
		}
cypherpunk's avatar
 
cypherpunk committed
332 333
	    }

334
	    /* Create the new, encrypted message */
Rob Smits's avatar
Rob Smits committed
335 336 337 338 339 340 341 342 343 344 345 346
	    if (convert_called) {
		err_code = otrl_proto_create_data(&msgtosend, context,
			converted_msg, tlvs, 0, NULL);

		if (ops->convert_free) {
		    ops->convert_free(opdata, context, converted_msg);
		    converted_msg = NULL;
		}
	    } else {
		err_code = otrl_proto_create_data(&msgtosend, context,
			original_msg, tlvs, 0, NULL);
	    }
cypherpunk's avatar
 
cypherpunk committed
347 348
	    if (!err_code) {
		context->context_priv->lastsent = time(NULL);
Rob Smits's avatar
Rob Smits committed
349
		otrl_context_update_recent_child(context, 1);
350
		*messagep = msgtosend;
cypherpunk's avatar
cypherpunk committed
351
	    } else {
352 353
		/* Uh, oh.  Whatever we do, *don't* send the message in the
		 * clear. */
cypherpunk's avatar
 
cypherpunk committed
354
		if (ops->handle_msg_event) {
Rob Smits's avatar
Rob Smits committed
355 356
		    ops->handle_msg_event(opdata,
			    OTRL_MSGEVENT_ENCRYPTION_ERROR,
Rob Smits's avatar
Rob Smits committed
357
			    context, NULL, gcry_error(GPG_ERR_NO_ERROR));
358
		}
cypherpunk's avatar
 
cypherpunk committed
359
		if (ops->otr_error_message) {
Rob Smits's avatar
Rob Smits committed
360
		    err_msg = ops->otr_error_message(opdata, context,
cypherpunk's avatar
 
cypherpunk committed
361
			OTRL_ERRCODE_ENCRYPTION_ERROR);
Rob Smits's avatar
Rob Smits committed
362
		    *messagep = malloc(strlen(OTR_ERROR_PREFIX) +
cypherpunk's avatar
 
cypherpunk committed
363 364 365 366 367 368 369 370 371 372 373 374
			strlen(err_msg) + 1);
		    if (*messagep) {
			strcpy(*messagep, OTR_ERROR_PREFIX);
			strcat(*messagep, err_msg);
		    }
		    if (ops->otr_error_message_free) {
			ops->otr_error_message_free(opdata, err_msg);
		    }
		    if (!(*messagep)) {
			err = gcry_error(GPG_ERR_ENOMEM);
			goto fragment;
		    }
375
		}
cypherpunk's avatar
cypherpunk committed
376
	    }
377 378
	    break;
	case OTRL_MSGSTATE_FINISHED:
cypherpunk's avatar
 
cypherpunk committed
379 380
	    if (ops->handle_msg_event) {
		ops->handle_msg_event(opdata, OTRL_MSGEVENT_CONNECTION_ENDED,
Rob Smits's avatar
Rob Smits committed
381
		    context, NULL, gcry_error(GPG_ERR_NO_ERROR));
cypherpunk's avatar
cypherpunk committed
382
	    }
cypherpunk's avatar
 
cypherpunk committed
383
	    *messagep = strdup("");
384
	    if (!(*messagep)) {
cypherpunk's avatar
 
cypherpunk committed
385 386
		err = gcry_error(GPG_ERR_ENOMEM);
		goto fragment;
cypherpunk's avatar
cypherpunk committed
387
	    }
388
	    break;
cypherpunk's avatar
cypherpunk committed
389 390
    }

cypherpunk's avatar
 
cypherpunk committed
391 392
fragment:
    if (fragPolicy == OTRL_FRAGMENT_SEND_SKIP ) {
Rob Smits's avatar
Rob Smits committed
393 394
	/* Do not fragment/inject. Default behaviour of libotr3.2.0 */
	return err;
cypherpunk's avatar
 
cypherpunk committed
395 396
    } else {
	/* Fragment and send according to policy */
397
	if (!err && messagep && *messagep) {
Rob Smits's avatar
Rob Smits committed
398 399 400
	    if (context) {
		err = fragment_and_send(ops, NULL, context, *messagep,
			fragPolicy, messagep);
cypherpunk's avatar
 
cypherpunk committed
401
	    }
Rob Smits's avatar
Rob Smits committed
402 403
	}
	return err;
cypherpunk's avatar
 
cypherpunk committed
404
    }
cypherpunk's avatar
cypherpunk committed
405 406
}

407 408 409 410 411
/* If err == 0, send the last auth message for the given context to the
 * appropriate user.  Otherwise, display an appripriate error dialog.
 * Return the value of err that was passed. */
static gcry_error_t send_or_error_auth(const OtrlMessageAppOps *ops,
	void *opdata, gcry_error_t err, ConnContext *context)
cypherpunk's avatar
cypherpunk committed
412 413
{
    if (!err) {
414
	const char *msg = context->auth.lastauthmsg;
cypherpunk's avatar
cypherpunk committed
415
	if (msg && *msg) {
Rob Smits's avatar
Rob Smits committed
416
	    fragment_and_send(ops, opdata, context, msg,
cypherpunk's avatar
 
cypherpunk committed
417 418
		    OTRL_FRAGMENT_SEND_ALL, NULL);
	    context->context_priv->lastsent = time(NULL);
Rob Smits's avatar
Rob Smits committed
419
	    otrl_context_update_recent_child(context, 1);
cypherpunk's avatar
cypherpunk committed
420 421
	}
    } else {
Rob Smits's avatar
Rob Smits committed
422
	if (ops->handle_msg_event) {
cypherpunk's avatar
 
cypherpunk committed
423 424
	    ops->handle_msg_event(opdata, OTRL_MSGEVENT_SETUP_ERROR,
		    context, NULL, err);
Rob Smits's avatar
Rob Smits committed
425
	}
cypherpunk's avatar
cypherpunk committed
426 427 428 429
    }
    return err;
}

430 431 432 433 434 435 436 437 438 439 440
typedef struct {
    int gone_encrypted;
    OtrlUserState us;
    const OtrlMessageAppOps *ops;
    void *opdata;
    ConnContext *context;
    int ignore_message;
    char **messagep;
} EncrData;

static gcry_error_t go_encrypted(const OtrlAuthInfo *auth, void *asdata)
cypherpunk's avatar
cypherpunk committed
441
{
442 443 444 445
    EncrData *edata = asdata;
    gcry_error_t err = gcry_error(GPG_ERR_NO_ERROR);
    Fingerprint *found_print = NULL;
    int fprint_added = 0;
cypherpunk's avatar
 
cypherpunk committed
446
    OtrlMessageState oldstate = edata->context->msgstate;
cypherpunk's avatar
 
cypherpunk committed
447
    Fingerprint *oldprint = edata->context->active_fingerprint;
448 449 450

    /* See if we're talking to ourselves */
    if (!gcry_mpi_cmp(auth->their_pub, auth->our_dh.pub)) {
Rob Smits's avatar
Rob Smits committed
451 452
	/* Yes, we are. */
	if (edata->ops->handle_msg_event) {
cypherpunk's avatar
 
cypherpunk committed
453 454
	    edata->ops->handle_msg_event(edata->opdata,
		    OTRL_MSGEVENT_MSG_REFLECTED, edata->context,
Rob Smits's avatar
Rob Smits committed
455
		    NULL, gcry_error(GPG_ERR_NO_ERROR));
Rob Smits's avatar
Rob Smits committed
456
	}
457 458
	edata->ignore_message = 1;
	return gcry_error(GPG_ERR_NO_ERROR);
cypherpunk's avatar
cypherpunk committed
459 460
    }

461 462 463 464 465 466 467 468 469 470 471 472 473 474
    found_print = otrl_context_find_fingerprint(edata->context,
	    edata->context->auth.their_fingerprint, 1, &fprint_added);

    if (fprint_added) {
	/* Inform the user of the new fingerprint */
	if (edata->ops->new_fingerprint) {
	    edata->ops->new_fingerprint(edata->opdata, edata->us,
		    edata->context->accountname, edata->context->protocol,
		    edata->context->username,
		    edata->context->auth.their_fingerprint);
	}
	/* Arrange that the new fingerprint be written to disk */
	if (edata->ops->write_fingerprints) {
	    edata->ops->write_fingerprints(edata->opdata);
cypherpunk's avatar
cypherpunk committed
475 476 477
	}
    }

478 479
    /* Is this a new session or just a refresh of an existing one? */
    if (edata->context->msgstate == OTRL_MSGSTATE_ENCRYPTED &&
cypherpunk's avatar
 
cypherpunk committed
480
	    oldprint == found_print &&
Rob Smits's avatar
Rob Smits committed
481 482
	    edata->context->context_priv->our_keyid - 1 ==
	    edata->context->auth.our_keyid &&
cypherpunk's avatar
 
cypherpunk committed
483
	    !gcry_mpi_cmp(edata->context->context_priv->our_old_dh_key.pub,
484
		edata->context->auth.our_dh.pub) &&
cypherpunk's avatar
 
cypherpunk committed
485 486
	    ((edata->context->context_priv->their_keyid > 0 &&
	      edata->context->context_priv->their_keyid ==
487
		    edata->context->auth.their_keyid &&
cypherpunk's avatar
 
cypherpunk committed
488
	      !gcry_mpi_cmp(edata->context->context_priv->their_y,
489
		  edata->context->auth.their_pub)) ||
cypherpunk's avatar
 
cypherpunk committed
490 491
	    (edata->context->context_priv->their_keyid > 1 &&
	     edata->context->context_priv->their_keyid - 1 ==
492
		    edata->context->auth.their_keyid &&
cypherpunk's avatar
 
cypherpunk committed
493 494
	     edata->context->context_priv->their_old_y != NULL &&
	     !gcry_mpi_cmp(edata->context->context_priv->their_old_y,
495 496 497 498
		 edata->context->auth.their_pub)))) {
	/* This is just a refresh of the existing session. */
	if (edata->ops->still_secure) {
	    edata->ops->still_secure(edata->opdata, edata->context,
cypherpunk's avatar
 
cypherpunk committed
499
		    edata->context->auth.initiated);
cypherpunk's avatar
cypherpunk committed
500
	}
501 502
	edata->ignore_message = 1;
	return gcry_error(GPG_ERR_NO_ERROR);
cypherpunk's avatar
cypherpunk committed
503 504
    }

505 506 507 508
    /* Copy the information from the auth into the context */
    memmove(edata->context->sessionid,
	    edata->context->auth.secure_session_id, 20);
    edata->context->sessionid_len =
Rob Smits's avatar
Rob Smits committed
509
	    edata->context->auth.secure_session_id_len;
510
    edata->context->sessionid_half =
Rob Smits's avatar
Rob Smits committed
511
	    edata->context->auth.session_id_half;
cypherpunk's avatar
 
cypherpunk committed
512
    edata->context->protocol_version =
Rob Smits's avatar
Rob Smits committed
513
	    edata->context->auth.protocol_version;
514

Rob Smits's avatar
Rob Smits committed
515 516
    edata->context->context_priv->their_keyid =
	    edata->context->auth.their_keyid;
cypherpunk's avatar
 
cypherpunk committed
517 518
    gcry_mpi_release(edata->context->context_priv->their_y);
    gcry_mpi_release(edata->context->context_priv->their_old_y);
Rob Smits's avatar
Rob Smits committed
519 520
    edata->context->context_priv->their_y =
	    gcry_mpi_copy(edata->context->auth.their_pub);
cypherpunk's avatar
 
cypherpunk committed
521
    edata->context->context_priv->their_old_y = NULL;
522

Rob Smits's avatar
Rob Smits committed
523 524
    if (edata->context->context_priv->our_keyid - 1 !=
	edata->context->auth.our_keyid ||
cypherpunk's avatar
 
cypherpunk committed
525
	gcry_mpi_cmp(edata->context->context_priv->our_old_dh_key.pub,
Rob Smits's avatar
Rob Smits committed
526
		edata->context->auth.our_dh.pub)) {
cypherpunk's avatar
 
cypherpunk committed
527 528 529
	otrl_dh_keypair_free(&(edata->context->context_priv->our_dh_key));
	otrl_dh_keypair_free(&(edata->context->context_priv->our_old_dh_key));
	otrl_dh_keypair_copy(&(edata->context->context_priv->our_old_dh_key),
530
		&(edata->context->auth.our_dh));
Rob Smits's avatar
Rob Smits committed
531 532
	otrl_dh_gen_keypair(
		edata->context->context_priv->our_old_dh_key.groupid,
cypherpunk's avatar
 
cypherpunk committed
533
		&(edata->context->context_priv->our_dh_key));
Rob Smits's avatar
Rob Smits committed
534 535
	edata->context->context_priv->our_keyid = edata->context->auth.our_keyid
		+ 1;
536 537 538
    }

    /* Create the session keys from the DH keys */
cypherpunk's avatar
 
cypherpunk committed
539 540
    otrl_dh_session_free(&(edata->context->context_priv->sesskeys[0][0]));
    err = otrl_dh_session(&(edata->context->context_priv->sesskeys[0][0]),
Rob Smits's avatar
Rob Smits committed
541 542
	    &(edata->context->context_priv->our_dh_key),
	    edata->context->context_priv->their_y);
543
    if (err) return err;
cypherpunk's avatar
 
cypherpunk committed
544 545
    otrl_dh_session_free(&(edata->context->context_priv->sesskeys[1][0]));
    err = otrl_dh_session(&(edata->context->context_priv->sesskeys[1][0]),
Rob Smits's avatar
Rob Smits committed
546 547
	    &(edata->context->context_priv->our_old_dh_key),
	    edata->context->context_priv->their_y);
548 549
    if (err) return err;

cypherpunk's avatar
 
cypherpunk committed
550
    edata->context->context_priv->generation++;
551 552 553 554 555
    edata->context->active_fingerprint = found_print;
    edata->context->msgstate = OTRL_MSGSTATE_ENCRYPTED;

    if (edata->ops->update_context_list) {
	edata->ops->update_context_list(edata->opdata);
cypherpunk's avatar
cypherpunk committed
556
    }
cypherpunk's avatar
 
cypherpunk committed
557
    if (oldstate == OTRL_MSGSTATE_ENCRYPTED && oldprint == found_print) {
cypherpunk's avatar
 
cypherpunk committed
558 559 560 561 562 563 564 565
	if (edata->ops->still_secure) {
	    edata->ops->still_secure(edata->opdata, edata->context,
		    edata->context->auth.initiated);
	}
    } else {
	if (edata->ops->gone_secure) {
	    edata->ops->gone_secure(edata->opdata, edata->context);
	}
566 567 568 569 570 571
    }

    edata->gone_encrypted = 1;

    return gpg_error(GPG_ERR_NO_ERROR);
}
cypherpunk's avatar
cypherpunk committed
572

573 574 575 576 577 578 579 580 581
static void maybe_resend(EncrData *edata)
{
    gcry_error_t err;
    time_t now;

    if (!edata->gone_encrypted) return;

    /* See if there's a message we sent recently that should be resent. */
    now = time(NULL);
cypherpunk's avatar
 
cypherpunk committed
582 583 584
    if (edata->context->context_priv->lastmessage != NULL &&
	    edata->context->context_priv->may_retransmit &&
	    edata->context->context_priv->lastsent >= (now - RESEND_INTERVAL)) {
585
	char *resendmsg;
cypherpunk's avatar
 
cypherpunk committed
586 587 588 589 590 591 592 593
	char *msg_to_send;
	int resending = (edata->context->context_priv->may_retransmit == 1);

	/* Initialize msg_to_send */
	if (resending) {
	    const char *resent_prefix;
	    int used_ops_resentmp = 1;
	    resent_prefix = edata->ops->resent_msg_prefix ?
Rob Smits's avatar
Rob Smits committed
594
				    edata->ops->resent_msg_prefix(edata->opdata,
cypherpunk's avatar
 
cypherpunk committed
595 596 597 598 599 600 601 602 603 604 605 606
				    edata->context) : NULL;
	    if (!resent_prefix) {
		resent_prefix = "[resent]"; /* Assign default prefix */
		used_ops_resentmp = 0;
	    }
	    msg_to_send = malloc(
		    strlen(edata->context->context_priv->lastmessage) +
		    strlen(resent_prefix) + 2);
	    if (msg_to_send) {
		strcpy(msg_to_send, resent_prefix);
		strcat(msg_to_send, " ");
		strcat(msg_to_send, edata->context->context_priv->lastmessage);
607 608
	    } else {
		return;  /* Out of memory; don't try to resend */
cypherpunk's avatar
 
cypherpunk committed
609 610 611 612 613 614 615 616
	    }
	    if (used_ops_resentmp) {
		edata->ops->resent_msg_prefix_free(edata->opdata,
			resent_prefix);
	    }
	} else {
	    msg_to_send = edata->context->context_priv->lastmessage;
	}
617 618 619

	/* Re-encrypt the message with the new keys */
	err = otrl_proto_create_data(&resendmsg,
cypherpunk's avatar
 
cypherpunk committed
620
		edata->context, msg_to_send, NULL, 0, NULL);
621
	if (resending) {
cypherpunk's avatar
 
cypherpunk committed
622 623
		free(msg_to_send);
	}
624 625
	if (!err) {
	    /* Resend the message */
cypherpunk's avatar
 
cypherpunk committed
626 627
	    fragment_and_send(edata->ops, edata->opdata, edata->context,
		    resendmsg, OTRL_FRAGMENT_SEND_ALL, NULL);
628
	    free(resendmsg);
cypherpunk's avatar
 
cypherpunk committed
629
	    edata->context->context_priv->lastsent = now;
Rob Smits's avatar
Rob Smits committed
630
	    otrl_context_update_recent_child(edata->context, 1);
cypherpunk's avatar
 
cypherpunk committed
631
	    if (resending) {
Rob Smits's avatar
Rob Smits committed
632
		/* We're not sending it for the first time; let the user
cypherpunk's avatar
 
cypherpunk committed
633 634 635 636
		 * know we resent it */
		if (edata->ops->handle_msg_event) {
		    edata->ops->handle_msg_event(edata->opdata,
			    OTRL_MSGEVENT_MSG_RESENT, edata->context,
Rob Smits's avatar
Rob Smits committed
637
			    NULL, gcry_error(GPG_ERR_NO_ERROR));
638 639
		}
	    }
cypherpunk's avatar
 
cypherpunk committed
640
	    edata->ignore_message = 1;
641 642
	}
    }
cypherpunk's avatar
cypherpunk committed
643 644
}

cypherpunk's avatar
cypherpunk committed
645 646 647 648 649 650 651 652 653 654 655 656 657 658
/* Set the trust level based on the result of the SMP */
static void set_smp_trust(const OtrlMessageAppOps *ops, void *opdata,
	ConnContext *context, int trusted)
{
    otrl_context_set_trust(context->active_fingerprint, trusted ? "smp" : "");

    /* Write the new info to disk, redraw the ui, and redraw the
     * OTR buttons. */
    if (ops->write_fingerprints) {
	ops->write_fingerprints(opdata);
    }
}

static void init_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
cypherpunk's avatar
 
cypherpunk committed
659 660
	void *opdata, ConnContext *context, const char *question,
	const unsigned char *secret, size_t secretlen, int initiating)
cypherpunk's avatar
cypherpunk committed
661 662 663 664 665 666 667 668
{
    unsigned char *smpmsg = NULL;
    int smpmsglen;
    unsigned char combined_secret[SM_DIGEST_SIZE];
    gcry_error_t err;
    unsigned char our_fp[20];
    unsigned char *combined_buf;
    size_t combined_buf_len;
cypherpunk's avatar
 
cypherpunk committed
669 670
    OtrlTLV *sendtlv;
    char *sendsmp = NULL;
cypherpunk's avatar
cypherpunk committed
671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709

    if (!context || context->msgstate != OTRL_MSGSTATE_ENCRYPTED) return;

    /*
     * Construct the combined secret as a SHA256 hash of:
     * Version byte (0x01), Initiator fingerprint (20 bytes),
     * responder fingerprint (20 bytes), secure session id, input secret
     */
    otrl_privkey_fingerprint_raw(us, our_fp, context->accountname,
	    context->protocol);

    combined_buf_len = 41 + context->sessionid_len + secretlen;
    combined_buf = malloc(combined_buf_len);
    combined_buf[0] = 0x01;
    if (initiating) {
	memmove(combined_buf + 1, our_fp, 20);
	memmove(combined_buf + 21,
		context->active_fingerprint->fingerprint, 20);
    } else {
	memmove(combined_buf + 1,
		context->active_fingerprint->fingerprint, 20);
	memmove(combined_buf + 21, our_fp, 20);
    }
    memmove(combined_buf + 41, context->sessionid,
	    context->sessionid_len);
    memmove(combined_buf + 41 + context->sessionid_len,
	    secret, secretlen);
    gcry_md_hash_buffer(SM_HASH_ALGORITHM, combined_secret, combined_buf,
	    combined_buf_len);
    free(combined_buf);

    if (initiating) {
	otrl_sm_step1(context->smstate, combined_secret, SM_DIGEST_SIZE,
		&smpmsg, &smpmsglen);
    } else {
	otrl_sm_step2b(context->smstate, combined_secret, SM_DIGEST_SIZE,
		&smpmsg, &smpmsglen);
    }

cypherpunk's avatar
 
cypherpunk committed
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724
    /* If we've got a question, attach it to the smpmsg */
    if (question != NULL) {
	size_t qlen = strlen(question);
	unsigned char *qsmpmsg = malloc(qlen + 1 + smpmsglen);
	if (!qsmpmsg) {
	    free(smpmsg);
	    return;
	}
	strcpy((char *)qsmpmsg, question);
	memmove(qsmpmsg + qlen + 1, smpmsg, smpmsglen);
	free(smpmsg);
	smpmsg = qsmpmsg;
	smpmsglen += qlen + 1;
    }

cypherpunk's avatar
cypherpunk committed
725
    /* Send msg with next smp msg content */
cypherpunk's avatar
 
cypherpunk committed
726 727 728
    sendtlv = otrl_tlv_new(initiating ?
	    (question != NULL ? OTRL_TLV_SMP1Q : OTRL_TLV_SMP1)
	    : OTRL_TLV_SMP2,
cypherpunk's avatar
cypherpunk committed
729 730
	    smpmsglen, smpmsg);
    err = otrl_proto_create_data(&sendsmp, context, "", sendtlv,
Rob Smits's avatar
Rob Smits committed
731
	    OTRL_MSGFLAGS_IGNORE_UNREADABLE, NULL);
cypherpunk's avatar
cypherpunk committed
732
    if (!err) {
Rob Smits's avatar
Rob Smits committed
733
	/*  Send it, and set the next expected message to the
cypherpunk's avatar
cypherpunk committed
734
	 *  logical response */
Rob Smits's avatar
Rob Smits committed
735
	err = fragment_and_send(ops, opdata, context,
cypherpunk's avatar
cypherpunk committed
736
		sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
Rob Smits's avatar
Rob Smits committed
737 738
	context->smstate->nextExpected =
		initiating ? OTRL_SMP_EXPECT2 : OTRL_SMP_EXPECT3;
cypherpunk's avatar
cypherpunk committed
739 740 741 742 743 744 745 746 747 748 749
    }
    free(sendsmp);
    otrl_tlv_free(sendtlv);
    free(smpmsg);
}

/* Initiate the Socialist Millionaires' Protocol */
void otrl_message_initiate_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
	void *opdata, ConnContext *context, const unsigned char *secret,
	size_t secretlen)
{
cypherpunk's avatar
 
cypherpunk committed
750 751 752 753 754 755 756 757 758 759
    init_respond_smp(us, ops, opdata, context, NULL, secret, secretlen, 1);
}

/* Initiate the Socialist Millionaires' Protocol and send a prompt
 * question to the buddy */
void otrl_message_initiate_smp_q(OtrlUserState us,
	const OtrlMessageAppOps *ops, void *opdata, ConnContext *context,
	const char *question, const unsigned char *secret, size_t secretlen)
{
    init_respond_smp(us, ops, opdata, context, question, secret, secretlen, 1);
cypherpunk's avatar
cypherpunk committed
760 761 762 763 764 765 766
}

/* Respond to a buddy initiating the Socialist Millionaires' Protocol */
void otrl_message_respond_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
	void *opdata, ConnContext *context, const unsigned char *secret,
	size_t secretlen)
{
cypherpunk's avatar
 
cypherpunk committed
767
    init_respond_smp(us, ops, opdata, context, NULL, secret, secretlen, 0);
cypherpunk's avatar
cypherpunk committed
768 769 770 771 772 773 774 775 776 777 778
}

/* Abort the SMP.  Called when an unexpected SMP message breaks the
 * normal flow. */
void otrl_message_abort_smp(OtrlUserState us, const OtrlMessageAppOps *ops,
	void *opdata, ConnContext *context)
{
    OtrlTLV *sendtlv = otrl_tlv_new(OTRL_TLV_SMP_ABORT, 0,
	    (const unsigned char *)"");
    char *sendsmp = NULL;
    gcry_error_t err;
cypherpunk's avatar
 
cypherpunk committed
779

cypherpunk's avatar
 
cypherpunk committed
780 781
    context->smstate->nextExpected = OTRL_SMP_EXPECT1;

cypherpunk's avatar
cypherpunk committed
782 783
    err = otrl_proto_create_data(&sendsmp,
	    context, "", sendtlv,
cypherpunk's avatar
 
cypherpunk committed
784
	    OTRL_MSGFLAGS_IGNORE_UNREADABLE, NULL);
cypherpunk's avatar
cypherpunk committed
785 786
    if (!err) {
	/* Send the abort signal so our buddy knows we've stopped */
cypherpunk's avatar
 
cypherpunk committed
787
	err = fragment_and_send(ops, opdata, context,
cypherpunk's avatar
cypherpunk committed
788 789 790 791 792 793
		sendsmp, OTRL_FRAGMENT_SEND_ALL, NULL);
    }
    free(sendsmp);
    otrl_tlv_free(sendtlv);
}

Rob Smits's avatar
Rob Smits committed
794 795 796 797
static void message_malformed(const OtrlMessageAppOps *ops,
	void *opdata, ConnContext *context) {
    if (ops->handle_msg_event) {
	ops->handle_msg_event(opdata, OTRL_MSGEVENT_RCVDMSG_MALFORMED, context,
Rob Smits's avatar
Rob Smits committed
798
	    NULL, gcry_error(GPG_ERR_NO_ERROR));
Rob Smits's avatar
Rob Smits committed
799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823
    }

    if (ops->inject_message && ops->otr_error_message) {
	const char *err_msg = ops->otr_error_message(opdata, context,
		OTRL_ERRCODE_MSG_MALFORMED);

	if (err_msg) {
	    char *buf = malloc(strlen(OTR_ERROR_PREFIX) + strlen(err_msg) + 1);

	    if (buf) {
		strcpy(buf, OTR_ERROR_PREFIX);
		strcat(buf, err_msg);
		ops->inject_message(opdata, context->accountname,
			context->protocol, context->username, buf);
		free(buf);
	    }

	    if (ops->otr_error_message_free) {
		ops->otr_error_message_free(opdata, err_msg);
	    }
	}
    }
}


cypherpunk's avatar
cypherpunk committed
824 825 826 827 828 829 830
/* Handle a message just received from the network.  It is safe to pass
 * all received messages to this routine.  add_appdata is a function
 * that will be called in the event that a new ConnContext is created.
 * It will be passed the data that you supplied, as well as
 * a pointer to the new ConnContext.  You can use this to add
 * application-specific information to the ConnContext using the
 * "context->app" field, for example.  If you don't need to do this, you
cypherpunk's avatar
 
cypherpunk committed
831 832
 * can pass NULL for the last two arguments of otrl_message_receiving.
 *
Rob Smits's avatar
Rob Smits committed
833 834 835 836 837
 * If non-NULL, ops->convert_msg will be called after a data message is 
 * decrypted.
 *
 * If "contextp" is not NULL, it will be set to the ConnContext used for 
 * receiving the message.
cypherpunk's avatar
cypherpunk committed
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853
 *
 * If otrl_message_receiving returns 1, then the message you received
 * was an internal protocol message, and no message should be delivered
 * to the user.
 *
 * If it returns 0, then check if *messagep was set to non-NULL.  If
 * so, replace the received message with the contents of *messagep, and
 * deliver that to the user instead.  You must call
 * otrl_message_free(*messagep) when you're done with it.  If tlvsp is
 * non-NULL, *tlvsp will be set to a chain of any TLVs that were
 * transmitted along with this message.  You must call
 * otrl_tlv_free(*tlvsp) when you're done with those.
 *
 * If otrl_message_receiving returns 0 and *messagep is NULL, then this
 * was an ordinary, non-OTR message, which should just be delivered to
 * the user without modification. */
cypherpunk's avatar
 
cypherpunk committed
854
int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
cypherpunk's avatar
cypherpunk committed
855 856
	void *opdata, const char *accountname, const char *protocol,
	const char *sender, const char *message, char **newmessagep,
Rob Smits's avatar
Rob Smits committed
857
	OtrlTLV **tlvsp, ConnContext **contextp,
cypherpunk's avatar
cypherpunk committed
858 859 860
	void (*add_appdata)(void *data, ConnContext *context),
	void *data)
{
Rob Smits's avatar
Rob Smits committed
861
    ConnContext *context, *m_context, *best_context;
862
    OtrlMessageType msgtype;
cypherpunk's avatar
cypherpunk committed
863 864
    int context_added = 0;
    OtrlPolicy policy = OTRL_POLICY_DEFAULT;
cypherpunk's avatar
 
cypherpunk committed
865
    int fragment_assembled = 0;
Rob Smits's avatar
Rob Smits committed
866
    char *unfragmessage = NULL, *otrtag = NULL;
867
    EncrData edata;
868
    otrl_instag_t our_instance = 0, their_instance = 0;
Rob Smits's avatar
Rob Smits committed
869 870
    int version;
    gcry_error_t err;
cypherpunk's avatar
cypherpunk committed
871 872

    if (!accountname || !protocol || !sender || !message || !newmessagep)
Rob Smits's avatar
Rob Smits committed
873
	return 0;
cypherpunk's avatar
cypherpunk committed
874 875 876 877

    *newmessagep = NULL;
    if (tlvsp) *tlvsp = NULL;

Rob Smits's avatar
Rob Smits committed
878 879 880 881 882 883 884 885
    if (contextp) {
	*contextp = NULL;
    }

    /* Find the master context and state with this correspondent */
    m_context = otrl_context_find(us, sender, accountname,
	    protocol, OTRL_INSTAG_MASTER, 1, &context_added, add_appdata, data);
    context = m_context;
cypherpunk's avatar
cypherpunk committed
886 887 888 889 890 891

    /* Update the context list if we added one */
    if (context_added && ops->update_context_list) {
	ops->update_context_list(opdata);
    }

Rob Smits's avatar
Rob Smits committed
892 893 894 895 896 897 898 899 900 901
    best_context = otrl_context_find(us, sender, accountname,
	    protocol, OTRL_INSTAG_BEST, 0, NULL, add_appdata, data);

    /* Find or generate the instance tag if needed */
    if (!context->our_instance) {
	populate_context_instag(us, ops, opdata, accountname, protocol,
		context);
    }


cypherpunk's avatar
cypherpunk committed
902 903 904 905 906 907
    /* Check the policy */
    if (ops->policy) {
	policy = ops->policy(opdata, context);
    }

    /* Should we go on at all? */
908
    if ((policy & OTRL_POLICY_VERSION_MASK) == 0) {
Rob Smits's avatar
Rob Smits committed
909
	return 0;
cypherpunk's avatar
cypherpunk committed
910 911
    }

Rob Smits's avatar
Rob Smits committed
912 913 914 915 916 917 918 919 920 921 922 923
    otrtag = strstr(message, "?OTR");
    if (otrtag) {
	/* See if we have a V3 fragment */
	if (strstr(message, "?OTR|")) {
	    /* Get the instance tag from fragment header*/
	    sscanf(otrtag, "?OTR|%x|%x,", &their_instance, &our_instance);
	    /* Ignore message if it is intended for a different instance */
	    if (our_instance && context->our_instance != our_instance) {

		    if (ops->handle_msg_event) {
			ops->handle_msg_event(opdata,
				OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE,
Rob Smits's avatar
Rob Smits committed
924
				m_context, NULL, gcry_error(GPG_ERR_NO_ERROR));
Rob Smits's avatar
Rob Smits committed
925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
		    }
		    return 1;
	    }
	    /* Get the context for this instance */
	    if (their_instance >= OTRL_MIN_VALID_INSTAG) {
		context = otrl_context_find(us, sender, accountname,
			protocol, their_instance, 1, &context_added,
			add_appdata, data);
	    } else {
		message_malformed(ops, opdata, context);
		return 1;
	    }
	}
	switch(otrl_proto_fragment_accumulate(&unfragmessage,
		context, message)) {
	    case OTRL_FRAGMENT_UNFRAGMENTED:
		/* Do nothing */
		break;
	    case OTRL_FRAGMENT_INCOMPLETE:
		/* We've accumulated this fragment, but we don't have a
		 * complete message yet */
		return 1;
	    case OTRL_FRAGMENT_COMPLETE:
		/* We've got a new complete message, in unfragmessage. */
		fragment_assembled = 1;
		message = unfragmessage;
		otrtag = strstr(message, "?OTR");
		break;
	}
cypherpunk's avatar
 
cypherpunk committed
954 955
    }

cypherpunk's avatar
cypherpunk committed
956 957 958
    /* What type of message is it?  Note that this just checks the
     * header; it's not necessarily a _valid_ message of this type. */
    msgtype = otrl_proto_message_type(message);
Rob Smits's avatar
Rob Smits committed
959
    version = otrl_proto_message_version(message);
cypherpunk's avatar
cypherpunk committed
960 961

    /* See if they responded to our OTR offer */
962 963
    if ((policy & OTRL_POLICY_SEND_WHITESPACE_TAG)) {
	if (msgtype != OTRL_MSGTYPE_NOTOTR) {
cypherpunk's avatar
cypherpunk committed
964 965 966 967 968 969
	    context->otr_offer = OFFER_ACCEPTED;
	} else if (context->otr_offer == OFFER_SENT) {
	    context->otr_offer = OFFER_REJECTED;
	}
    }

Rob Smits's avatar
Rob Smits committed
970 971 972 973 974 975 976 977
    /* Check that this version is allowed by the policy */
    if (((version == 3) && !(policy & OTRL_POLICY_ALLOW_V3))
	|| ((version == 2) && !(policy & OTRL_POLICY_ALLOW_V2))
	|| ((version == 1) && !(policy & OTRL_POLICY_ALLOW_V1))) {
	    return 1;
    }
    /* Check the to and from instance tags */
    if (version == 3) {
978 979 980 981
	err = gcry_error(GPG_ERR_INV_VALUE);
	if (otrtag) {
	    err = otrl_proto_instance(otrtag, &their_instance, &our_instance);
	}
Rob Smits's avatar
Rob Smits committed
982
	if (!err) {
Rob Smits's avatar
Rob Smits committed
983 984 985 986
	    if ((msgtype == OTRL_MSGTYPE_DH_COMMIT && our_instance &&
		    context->our_instance != our_instance) ||
		    (msgtype != OTRL_MSGTYPE_DH_COMMIT &&
		    context->our_instance != our_instance)) {
Rob Smits's avatar
Rob Smits committed
987 988 989
		if (ops->handle_msg_event) {
		    ops->handle_msg_event(opdata,
			    OTRL_MSGEVENT_RCVDMSG_FOR_OTHER_INSTANCE,
Rob Smits's avatar
Rob Smits committed
990
			    m_context, NULL, gcry_error(GPG_ERR_NO_ERROR));
Rob Smits's avatar
Rob Smits committed
991 992 993 994 995 996 997 998 999 1000 1001
		}
		return 1; /* ignore message intended for a different instance */
	    }

	    if (their_instance >= OTRL_MIN_VALID_INSTAG) {
		context = otrl_context_find(us, sender, accountname,
			protocol, their_instance, 1, &context_added,
			add_appdata, data);
	    }
	}

1002 1003 1004 1005 1006
	if (err || their_instance < OTRL_MIN_VALID_INSTAG) {
	    message_malformed(ops, opdata, context);
	    return 1;
	}

Rob Smits's avatar
Rob Smits committed
1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060
	if (context_added) {
	    /* Context added because of new instance (either here or when
	     * accumulating fragments */
	    /* Copy information from m_context to the new instance context */
	    context->auth.protocol_version = 3;
	    context->protocol_version = 3;

	    if (context_added) {
		context->msgstate = m_context->msgstate;
	    }

	    if (msgtype == OTRL_MSGTYPE_DH_COMMIT) {
		otrl_auth_copy_on_commit(&(m_context->auth), &(context->auth));
	    } else if (msgtype == OTRL_MSGTYPE_DH_KEY) {
		otrl_auth_copy_on_key(&(m_context->auth), &(context->auth));
	    } else {
		return 1;  /* Ignore unexpected message */
	    }

	    /* Update the context list */
	    if (ops->update_context_list) {
		ops->update_context_list(opdata);
	    }
	} else if (m_context != context && (context->msgstate !=
		OTRL_MSGSTATE_ENCRYPTED || context->otr_offer == OFFER_SENT)) {
	    /* Switching from m_context to existing instance context */
	    if (msgtype == OTRL_MSGTYPE_DH_KEY && m_context->auth.authstate
		    == OTRL_AUTHSTATE_AWAITING_DHKEY &&
		    !(context->auth.authstate ==
		    OTRL_AUTHSTATE_AWAITING_DHKEY)) {
		context->msgstate = m_context->msgstate;
		context->auth.protocol_version = 3;
		context->protocol_version = 3;
		otrl_auth_copy_on_key(&(m_context->auth), &(context->auth));
	    } else if (msgtype == OTRL_MSGTYPE_DH_COMMIT &&
		    m_context->auth.authstate == OTRL_AUTHSTATE_AWAITING_DHKEY
		    && !(context->auth.authstate ==
		    OTRL_AUTHSTATE_AWAITING_DHKEY)) {
		context->msgstate = m_context->msgstate;
		context->auth.protocol_version = 3;
		context->protocol_version = 3;
		otrl_auth_copy_on_commit(&(m_context->auth), &(context->auth));
		}
	    }
    }

    if (contextp) {
	*contextp = context;
    }

    /* update time of last received message */
    context->context_priv->lastrecv = time(NULL);
    otrl_context_update_recent_child(context, 0);

1061 1062 1063 1064 1065 1066 1067 1068
    edata.gone_encrypted = 0;
    edata.us = us;
    edata.context = context;
    edata.ops = ops;
    edata.opdata = opdata;
    edata.ignore_message = -1;
    edata.messagep = newmessagep;

cypherpunk's avatar
cypherpunk committed
1069
    switch(msgtype) {
1070 1071 1072 1073 1074 1075
	unsigned int bestversion;
	const char *startwhite, *endwhite;
	DH_keypair *our_dh;
	unsigned int our_keyid;
	OtrlPrivKey *privkey;
	int haveauthmsg;
Rob Smits's avatar
Rob Smits committed
1076

1077 1078 1079 1080
	case OTRL_MSGTYPE_QUERY:
	    /* See if we should use an existing DH keypair, or generate
	     * a fresh one. */
	    if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
cypherpunk's avatar
 
cypherpunk committed
1081 1082
		our_dh = &(context->context_priv->our_old_dh_key);
		our_keyid = context->context_priv->our_keyid - 1;
1083 1084 1085 1086 1087 1088 1089
	    } else {
		our_dh = NULL;
		our_keyid = 0;
	    }

	    /* Find the best version of OTR that we both speak */
	    switch(otrl_proto_query_bestversion(message, policy)) {
Rob Smits's avatar
Rob Smits committed
1090 1091 1092 1093
		case 3:
		    err = otrl_auth_start_v23(&(context->auth), 3);
		    send_or_error_auth(ops, opdata, err, context);
		    break;
1094
		case 2:
Rob Smits's avatar
Rob Smits committed
1095
		    err = otrl_auth_start_v23(&(context->auth), 2);
1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108
		    send_or_error_auth(ops, opdata, err, context);
		    break;
		case 1:
		    /* Get our private key */
		    privkey = otrl_privkey_find(us, context->accountname,
			    context->protocol);
		    if (privkey == NULL) {
			/* We've got no private key! */
			if (ops->create_privkey) {
			    ops->create_privkey(opdata, context->accountname,
				    context->protocol);
			    privkey = otrl_privkey_find(us,
				    context->accountname, context->protocol);
cypherpunk's avatar
cypherpunk committed
1109 1110
			}
		    }
1111 1112 1113 1114 1115
		    if (privkey) {
			err = otrl_auth_start_v1(&(context->auth), our_dh,
				our_keyid, privkey);
			send_or_error_auth(ops, opdata, err, context);
		    }
cypherpunk's avatar
cypherpunk committed
1116
		    break;
1117 1118
		default:
		    /* Just ignore this message */
cypherpunk's avatar
cypherpunk committed
1119 1120 1121
		    break;
	    }
	    /* Don't display the Query message to the user. */
1122
	    if (edata.ignore_message == -1) edata.ignore_message = 1;
cypherpunk's avatar
cypherpunk committed
1123 1124
	    break;

1125
	case OTRL_MSGTYPE_DH_COMMIT:
Rob Smits's avatar
Rob Smits committed
1126 1127
	    err = otrl_auth_handle_commit(&(context->auth), otrtag, version);
	    send_or_error_auth(ops, opdata, err, context);
1128 1129 1130 1131 1132

	    if (edata.ignore_message == -1) edata.ignore_message = 1;
	    break;

	case OTRL_MSGTYPE_DH_KEY:
Rob Smits's avatar
Rob Smits committed
1133 1134 1135 1136 1137 1138 1139 1140 1141 1142
	    /* Get our private key */
	    privkey = otrl_privkey_find(us, context->accountname,
		    context->protocol);
	    if (privkey == NULL) {
		/* We've got no private key! */
		if (ops->create_privkey) {
		    ops->create_privkey(opdata, context->accountname,
			    context->protocol);
		    privkey = otrl_privkey_find(us,
			    context->accountname, context->protocol);
1143
		}
Rob Smits's avatar
Rob Smits committed
1144 1145 1146 1147 1148 1149
	    }
	    if (privkey) {
		err = otrl_auth_handle_key(&(context->auth), otrtag,
			&haveauthmsg, privkey);
		if (err || haveauthmsg) {
		    send_or_error_auth(ops, opdata, err, context);
1150 1151
		}
	    }
cypherpunk's avatar
cypherpunk committed
1152

1153 1154 1155 1156
	    if (edata.ignore_message == -1) edata.ignore_message = 1;
	    break;

	case OTRL_MSGTYPE_REVEALSIG:
Rob Smits's avatar
Rob Smits committed
1157 1158 1159 1160 1161 1162 1163 1164 1165 1166
	    /* Get our private key */
	    privkey = otrl_privkey_find(us, context->accountname,
		    context->protocol);
	    if (privkey == NULL) {
		/* We've got no private key! */
		if (ops->create_privkey) {
		    ops->create_privkey(opdata, context->accountname,
			    context->protocol);
		    privkey = otrl_privkey_find(us,
			    context->accountname, context->protocol);
1167
		}
Rob Smits's avatar
Rob Smits committed
1168 1169 1170 1171 1172 1173 1174 1175
	    }
	    if (privkey) {
		err = otrl_auth_handle_revealsig(&(context->auth),
			otrtag, &haveauthmsg, privkey, go_encrypted,
			&edata);
		if (err || haveauthmsg) {
		    send_or_error_auth(ops, opdata, err, context);
		    maybe_resend(&edata);
1176 1177
		}
	    }
cypherpunk's avatar
cypherpunk committed
1178

1179 1180
	    if (edata.ignore_message == -1) edata.ignore_message = 1;
	    break;
cypherpunk's avatar
cypherpunk committed
1181

1182
	case OTRL_MSGTYPE_SIGNATURE:
Rob Smits's avatar
Rob Smits committed
1183 1184 1185 1186 1187
	    err = otrl_auth_handle_signature(&(context->auth),
		    otrtag, &haveauthmsg, go_encrypted, &edata);
	    if (err || haveauthmsg) {
		send_or_error_auth(ops, opdata, err, context);
		maybe_resend(&edata);
1188
	    }
cypherpunk's avatar
 
cypherpunk committed
1189

1190 1191
	    if (edata.ignore_message == -1) edata.ignore_message = 1;
	    break;
cypherpunk's avatar
cypherpunk committed
1192

1193
	case OTRL_MSGTYPE_V1_KEYEXCH:
Rob Smits's avatar
Rob Smits committed
1194 1195 1196 1197 1198 1199 1200 1201 1202
	    /* See if we should use an existing DH keypair, or generate
	     * a fresh one. */
	    if (context->msgstate == OTRL_MSGSTATE_ENCRYPTED) {
		our_dh = &(context->context_priv->our_old_dh_key);
		our_keyid = context->context_priv->our_keyid - 1;
	    } else {
		our_dh = NULL;
		our_keyid = 0;
	    }