[Pkg-privacy-commits] [libotr] 154/225: Imported Upstream version 4.0.0~rc3

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 12:45:21 UTC 2015


This is an automated email from the git hooks/post-receive script.

infinity0 pushed a commit to branch master
in repository libotr.

commit 684f6fc0dcfcb2b04f5c0b3afb1f75086d4ccb6a
Author: intrigeri <intrigeri at boum.org>
Date:   Tue Feb 18 20:12:57 2014 +0000

    Imported Upstream version 4.0.0~rc3
---
 ChangeLog          |  58 ++++++++++++++++++-
 Protocol-v3.html   |  31 +++++++---
 UPGRADING          |  48 ++++++++++++++++
 src/auth.c         |  83 +++++++++++++++------------
 src/auth.h         |  15 +++--
 src/context.c      |  94 +++++++++++++++++++++++++++++-
 src/context_priv.c |   4 +-
 src/context_priv.h |   6 +-
 src/dh.c           |   1 -
 src/message.c      | 166 +++++++++++++++++++++++++++++++++++++++++++----------
 src/message.h      |  51 ++++++++++++++++
 src/proto.c        |  10 ++++
 src/sm.c           |  24 ++++++++
 src/userstate.c    |   4 +-
 src/userstate.h    |   4 +-
 15 files changed, 507 insertions(+), 92 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 328c3bd..82984bf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,7 +1,63 @@
+2012-08-27
+
+	* src/auth.h:
+	* src/auth.c:
+	* src/message.c: Record the time the last COMMIT was sent from a
+	master context.  This will be used to clear the committed key
+	from the master context once we don't expect any more instances
+	of our buddy to respond with a DHKEY message.
+
+	* UPGRADING:
+	* src/userstate.h:
+	* src/userstate.c:
+	* src/message.h:
+	* src/message.c: Add a timer_control callback to
+	OtrlMessageAppOps in order to actually clear out the above stale
+	committed keys.
+
+2012-08-26
+
+	* src/context.c:
+	* src/context_priv.c:
+	* src/context_priv.h: libotr was exporting exactly two functions
+	without the otrl_ prefix: context_priv_new and
+	context_priv_force_finished.  Change the names of these
+	functions to start with otrl_.  Thanks to David Goulet
+	<dgoulet at ev0ke.net> for noticing it.
+
+	* Protocol-v3.html: Document the v3 whitespace tag, and better
+	document the extra symmetric key.  Thanks to Kjell Braden
+	<kb at pentabarf.de> for noticing the omission.
+
+2012-08-25
+
+	* src/sm.c:
+	* src/context.c:
+	* src/auth.c:
+	* src/message.c: If OTRL_DEBUGGING is non-zero, then a message
+	containing a special debug string ("?OTR!") will cause debug
+	info to be printed to stderr.  (This #define should *not* be set
+	in release code.)
+
+	* src/auth.c:
+	* src/auth.h:
+	* src/message.c: Correct the logic for handling incoming COMMIT
+	messages when we've recently sent our own COMMIT message.
+
+	* src/message.c: Don't update the recent_sent_child field to
+	point to the master context just becuase we sent a version 3
+	COMMIT message (which has no destination instance).
+
 2012-08-24
 
 	* README:
-	* configure.ac: Release 4.0.0
+	* configure.ac: Prepare for release 4.0.0
+
+2012-08-24
+
+	* src/message.c: Consider copying the master auth context to the
+	child, even if the child is already in ENCRYPTED, because we
+	might be trying to refresh a private conversation.
 
 2012-08-22
 
diff --git a/Protocol-v3.html b/Protocol-v3.html
index 9f29186..75828a5 100644
--- a/Protocol-v3.html
+++ b/Protocol-v3.html
@@ -313,6 +313,8 @@ whitespace version tags, if it is present, for backwards
 compatibility)</li>
 <li>"\x20\x20\x09\x09\x20\x20\x09\x20" to indicate a willingness to use
 OTR version 2 with Bob</li>
+<li>"\x20\x20\x09\x09\x20\x20\x09\x09" to indicate a willingness to use
+OTR version 3 with Bob</li>
 </ul>
 <p>If Bob is willing to use OTR with Alice (with a protocol version that
 Alice has offered), he should start the AKE.  On the other hand, if
@@ -623,11 +625,16 @@ you should change the SMP state to SMP_EXPECT1 (see below).</dd>
 <dd>Like a SMP Message 1, but whose value begins with a NUL-terminated
 user-specified question.</dd>
 <dt>Type 8: Extra symmetric key</dt>
-<dd>The value begins with a 4-byte indication of what this symmetric key
-will be used for (file transfer, voice encryption, etc.). After that, the
-contents are use-specific (which file, etc.). There are no currently
-defined uses. See the "extra symmetric key" section below for details on
-how this key is derived.</dd>
+<dd>If you wish to use the extra symmetric key, compute it yourself as
+outlined in the section "Extra symmetric key", below.  Then send this
+type 8 TLV to your buddy to indicate that you'd like to use the extra
+symmetric key for something.  The value of the TLV begins with a 4-byte
+indication of what this symmetric key will be used for (file transfer,
+voice encryption, etc.). After that, the contents are use-specific
+(which file, etc.). There are no currently defined uses.  Note that the
+value of the key itself is <em>not</em> placed into the TLV; your buddy
+will compute it on his/her own.
+</dd>
 </dl>
 <p>SMP Message TLVs (types 2-5) all carry data sharing the same general
 format:</p> 
@@ -1209,10 +1216,20 @@ sending AES key.</li>
 receiving AES key.</li>
 </ul>
 <h4>Extra symmetric key</h4>
-<p>OTR version 3 defined an additional symmetric key that is derived
-during the AKE. The extra symmetric key is derived by calculating
+<p>OTR version 3 defines an additional symmetric key that can be derived
+by the communicating parties to use for application-specific purposes,
+such as file transfer, voice encryption, etc.  When one party wishes to
+use the extra symmetric key, he or she creates a type 8 TLV attached to
+a Data Message (see above).  The key itself is then derived using the
+same "secbytes" used to compute the encryption and MAC keys used to
+protect the Data Message.
+The extra symmetric key is derived by calculating
 h2(0xFF) and keeping the entire 256 bits, using the same definition
 of h2 as above.</p>
+<p>Upon receipt of the Data Message containing the type 8 TLV, the
+recipient will compute the extra symmetric key in the same way.  Note
+that the value of the extra symmetric key is <em>not</em> contained in
+the TLV itself.</p>
 <h4>Revealing MAC keys</h4>
 <p>Whenever you are about to forget either one of your old D-H key pairs, or
 one of your correspondent's old D-H public keys, take all of the
diff --git a/UPGRADING b/UPGRADING
index c19cb68..58cfc78 100644
--- a/UPGRADING
+++ b/UPGRADING
@@ -307,6 +307,54 @@ application to tweak formatting on the plaintext if, for example, this
 is something that would normally be done on the plaintext by some other
 entity while the message is in transit.
 
+/* When timer_control is called, turn off any existing periodic
+ * timer.
+ * 
+ * Additionally, if interval > 0, set a new periodic timer
+ * to go off every interval seconds.  When that timer fires, you
+ * must call otrl_message_poll(userstate, uiops, uiopdata); from the
+ * main libotr thread.
+ * 
+ * The timing does not have to be exact; this timer is used to
+ * provide forward secrecy by cleaning up stale private state that
+ * may otherwise stick around in memory.  Note that the
+ * timer_control callback may be invoked from otrl_message_poll
+ * itself, possibly to indicate that interval == 0 (that is, that
+ * there's no more periodic work to be done at this time).
+ * 
+ * If you set this callback to NULL, then you must ensure that your
+ * application calls otrl_message_poll(userstate, uiops, uiopdata);
+ * from the main libotr thread every definterval seconds (where
+ * definterval can be obtained by calling
+ * definterval = otrl_message_poll_get_default_interval(userstate);
+ * right after creating the userstate).  The advantage of
+ * implementing the timer_control callback is that the timer can be
+ * turned on by libotr only when it's needed.
+ * 
+ * It is not a problem (except for a minor performance hit) to call
+ * otrl_message_poll more often than requested, whether
+ * timer_control is implemented or not.
+ * 
+ * If you fail to implement the timer_control callback, and also
+ * fail to periodically call otrl_message_poll, then you open your
+ * users to a possible forward secrecy violation: an attacker that
+ * compromises the user's computer may be able to decrypt a handful
+ * of long-past messages (the first messages of an OTR
+ * conversation).
+ */
+void (*timer_control)(void *opdata, unsigned int interval);
+
+In order to prevent a forward secrecy violation, applications using
+libotr now need to be able to call otrl_message_poll on occasion.  The
+simplest thing to do is just to set up a local timer that calls that
+function every definterval =
+otrl_message_poll_get_default_interval(userstate) seconds.  To avoid
+unnecessary overhead, however, the timer_control callback is available.
+If you set timer_control to non-NULL, it will be called with
+instructions to turn on or off the periodic timer, and to what interval.
+
+You must also be sure to turn off the timer before freeing your
+userstate with otrl_userstate_free.
 
 3.2. Instance Tags
 
diff --git a/src/auth.c b/src/auth.c
index b402d30..107dee8 100644
--- a/src/auth.c
+++ b/src/auth.c
@@ -31,6 +31,40 @@
 #include "proto.h"
 #include "context.h"
 
+#if OTRL_DEBUGGING
+#include <stdio.h>
+
+/* Dump the contents of an OtrlAuthInfo to the FILE *f. */
+void otrl_auth_dump(FILE *f, const OtrlAuthInfo *auth)
+{
+    int i;
+
+    fprintf(f, "  Auth info %p:\n", auth);
+    fprintf(f, "    State: %d (%s)\n", auth->authstate,
+	auth->authstate == OTRL_AUTHSTATE_NONE ? "NONE" :
+	auth->authstate == OTRL_AUTHSTATE_AWAITING_DHKEY ? "AWAITING_DHKEY" :
+	auth->authstate == OTRL_AUTHSTATE_AWAITING_REVEALSIG ?
+	    "AWAITING_REVEALSIG" :
+	auth->authstate == OTRL_AUTHSTATE_AWAITING_SIG ? "AWAITING_SIG" :
+	auth->authstate == OTRL_AUTHSTATE_V1_SETUP ? "V1_SETUP" :
+	"INVALID");
+    fprintf(f, "    Context: %p\n", auth->context);
+    fprintf(f, "    Our keyid:   %u\n", auth->our_keyid);
+    fprintf(f, "    Their keyid: %u\n", auth->their_keyid);
+    fprintf(f, "    Their fingerprint: ");
+    for (i=0;i<20;++i) {
+	fprintf(f, "%02x", auth->their_fingerprint[i]);
+    }
+    fprintf(f, "\n    Initiated = %d\n", auth->initiated);
+    fprintf(f, "\n    Proto version = %d\n", auth->protocol_version);
+    fprintf(f, "\n    Lastauthmsg = %s\n",
+	auth->lastauthmsg ? auth->lastauthmsg : "(nil)");
+    fprintf(f, "\n    Commit sent time = %ld\n",
+	(long) auth->commit_sent_time);
+}
+
+#endif
+
 /*
  * Initialize the fields of an OtrlAuthInfo (already allocated).
  */
@@ -58,6 +92,7 @@ void otrl_auth_new(struct context *context)
     memset(auth->secure_session_id, 0, 20);
     auth->secure_session_id_len = 0;
     auth->lastauthmsg = NULL;
+    auth->commit_sent_time = 0;
     auth->context = context;
 }
 
@@ -96,6 +131,7 @@ void otrl_auth_clear(OtrlAuthInfo *auth)
     auth->secure_session_id_len = 0;
     free(auth->lastauthmsg);
     auth->lastauthmsg = NULL;
+    auth->commit_sent_time = 0;
 }
 
 /*
@@ -266,6 +302,9 @@ gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
     size_t buflen, lenp, enclen, hashlen;
     int res;
 
+    /* Are we the auth for the master context? */
+    int is_master = (auth->context->m_context == auth->context);
+
     res = otrl_base64_otr_decode(commitmsg, &buf, &buflen);
     if (res == -1) goto memerr;
     if (res == -2) goto invval;
@@ -326,8 +365,13 @@ gcry_error_t otrl_auth_handle_commit(OtrlAuthInfo *auth,
 
 	case OTRL_AUTHSTATE_AWAITING_DHKEY:
 	    /* We sent a D-H Commit Message, and we also received one
-	     * back.  Compare the hashgx values to see which one wins. */
-	    if (memcmp(auth->hashgx, hashbuf, 32) > 0) {
+	     * back.  If we're the master context, then the keypair in here
+	     * is probably stale; we just kept it around for a little
+	     * while in case some other logged in instance of our buddy
+	     * replied with a DHKEY message.  In that case, use the
+	     * incoming parameters.  Otherwise, compare the hashgx
+	     * values to see which one wins. */
+	    if (!is_master && memcmp(auth->hashgx, hashbuf, 32) > 0) {
 		/* Ours wins.  Ignore the message we received, and just
 		 * resend the same D-H Commit message again. */
 		free(encbuf);
@@ -1388,41 +1432,6 @@ err:
 
 /*
  * Copy relevant information from the master OtrlAuthInfo to an
- * instance OtrlAuthInfo in response to a D-H Commit with a new
- * instance. The fields copied will depend on the state of the
- * master auth.
- */
-void otrl_auth_copy_on_commit(OtrlAuthInfo *m_auth, OtrlAuthInfo *auth)
-{
-    switch(m_auth->authstate) {
-	case OTRL_AUTHSTATE_NONE:
-	case OTRL_AUTHSTATE_AWAITING_REVEALSIG:
-	    auth->authstate = OTRL_AUTHSTATE_NONE;
-	    break;
-	case OTRL_AUTHSTATE_AWAITING_DHKEY:
-	    /* We sent a D-H Commit Message, and we also received one.
-	     *  Copy our D_H Commit and auth state */
-	    otrl_dh_keypair_free(&(auth->our_dh));
-	    auth->initiated = m_auth->initiated;
-	    otrl_dh_keypair_copy(&(auth->our_dh), &(m_auth->our_dh));
-	    auth->our_keyid = m_auth->our_keyid;
-	    memmove(auth->r, m_auth->r, 16);
-	    if (auth->encgx) free(auth->encgx);
-	    auth->encgx = malloc(m_auth->encgx_len);
-	    memmove(auth->encgx, m_auth->encgx, m_auth->encgx_len);
-	    memmove(auth->hashgx, m_auth->hashgx, 32);
-
-	    auth->authstate = OTRL_AUTHSTATE_AWAITING_DHKEY;
-	    break;
-
-	default:
-	    /* This bad state will be detected and handled later */
-	    break;
-    }
-}
-
-/*
- * Copy relevant information from the master OtrlAuthInfo to an
  * instance OtrlAuthInfo in response to a D-H Key with a new
  * instance. The fields copied will depend on the state of the
  * master auth.
diff --git a/src/auth.h b/src/auth.h
index 4f9eaee..0b9db54 100644
--- a/src/auth.h
+++ b/src/auth.h
@@ -22,6 +22,7 @@
 #define __AUTH_H__
 
 #include <gcrypt.h>
+#include <time.h>
 #include "dh.h"
 
 
@@ -75,6 +76,12 @@ typedef struct {
 					     (base-64 encoded) we sent,
 					     in case we need to
 					     retransmit it. */
+
+    time_t commit_sent_time;              /* The time we last sent the
+                                             lastauthmsg, if it was a
+					     COMMIT message, and this is
+					     a master context.  0
+					     otherwise. */
 } OtrlAuthInfo;
 
 #include "privkey-t.h"
@@ -161,14 +168,6 @@ gcry_error_t otrl_auth_handle_v1_key_exchange(OtrlAuthInfo *auth,
 
 /*
  * Copy relevant information from the master OtrlAuthInfo to an
- * instance OtrlAuthInfo in response to a D-H Commit with a new
- * instance. The fields copied will depend on the state of the
- * master auth.
- */
-void otrl_auth_copy_on_commit(OtrlAuthInfo *m_auth, OtrlAuthInfo *auth);
-
-/*
- * Copy relevant information from the master OtrlAuthInfo to an
  * instance OtrlAuthInfo in response to a D-H Key with a new
  * instance. The fields copied will depend on the state of the
  * master auth.
diff --git a/src/context.c b/src/context.c
index 96c191a..bdde699 100644
--- a/src/context.c
+++ b/src/context.c
@@ -29,6 +29,96 @@
 #include "context.h"
 #include "instag.h"
 
+#if OTRL_DEBUGGING
+#include <stdio.h>
+
+void otrl_auth_dump(FILE *f, const OtrlAuthInfo *auth);
+void otrl_sm_dump(FILE *f, const OtrlSMState *sm);
+
+/* Dump the contents of a context to the FILE *f. */
+void otrl_context_dump(FILE *f, const ConnContext *context)
+{
+    const Fingerprint *fing;
+
+    fprintf(f, "Context %p:\n\n", context);
+
+    fprintf(f, "  Username: %s\n", context->username);
+    fprintf(f, "  Accountname: %s\n", context->accountname);
+    fprintf(f, "  Protocol: %s\n\n", context->protocol);
+    fprintf(f, "  Master context: %p%s\n", context->m_context,
+	    context->m_context == context ? " IS MASTER" : "");
+    fprintf(f, "  Recent recv child: %p\n", context->recent_rcvd_child);
+    fprintf(f, "  Recent sent child: %p\n", context->recent_sent_child);
+    fprintf(f, "  Recent child: %p\n\n", context->recent_child);
+    fprintf(f, "  Our instance:   %08x\n", context->our_instance);
+    fprintf(f, "  Their instance: %08x\n\n", context->their_instance);
+    fprintf(f, "  Msgstate: %d (%s)\n\n", context->msgstate,
+	context->msgstate == OTRL_MSGSTATE_PLAINTEXT ? "PLAINTEXT" :
+	context->msgstate == OTRL_MSGSTATE_ENCRYPTED ? "ENCRYPTED" :
+	context->msgstate == OTRL_MSGSTATE_FINISHED ? "FINISHED" :
+	"INVALID");
+    otrl_auth_dump(f, &context->auth);
+    fprintf(f, "\n  Fingerprints:\n");
+    for (fing = context->fingerprint_root.next; fing; fing = fing->next) {
+	fprintf(f, "    %p ", fing);
+	if (fing->fingerprint == NULL) {
+	    fprintf(f, "(null)");
+	} else {
+	    int i;
+	    for (i=0;i<20;++i) {
+		fprintf(f, "%02x", fing->fingerprint[i]);
+	    }
+	}
+	fprintf(f, " %p", fing->context);
+	if (fing->trust && fing->trust[0]) {
+	    fprintf(f, " %s", fing->trust);
+	}
+	fprintf(f, "\n");
+    }
+    fprintf(f, "\n  Active fingerprint: %p\n\n", context->active_fingerprint);
+    fprintf(f, "  Protocol version: %d\n", context->protocol_version);
+    fprintf(f, "  OTR offer: %d (%s)\n\n", context->otr_offer,
+	context->otr_offer == OFFER_NOT ? "NOT" :
+	context->otr_offer == OFFER_SENT ? "SENT" :
+	context->otr_offer == OFFER_REJECTED ? "REJECTED" :
+	context->otr_offer == OFFER_ACCEPTED ? "ACCEPTED" :
+	"INVALID");
+
+    fprintf(f, "  Application data: %p\n", context->app_data);
+    if (context->smstate == NULL) {
+	fprintf(f, "  SM state: NULL\n");
+    } else {
+	otrl_sm_dump(f, context->smstate);
+    }
+    fprintf(f, "\n");
+}
+
+/* Dump the master context of this context, and all of its children. */
+void otrl_context_siblings_dump(FILE *f, const ConnContext *context)
+{
+    const ConnContext *citer;
+    for (citer = context->m_context;
+	    citer && citer->m_context == context->m_context;
+	    citer = citer->next) {
+	if (citer == context) {
+	    fprintf(f, "*** ");
+	}
+	otrl_context_dump(f, citer);
+    }
+}
+
+/* Dump all contexts. */
+void otrl_context_all_dump(FILE *f, OtrlUserState us)
+{
+    const ConnContext *citer;
+    unsigned int ctxnum = 1;
+    for (citer = us->context_root; citer; citer = citer->next, ++ctxnum) {
+	fprintf(f, "%u. ", ctxnum);
+	otrl_context_dump(f, citer);
+    }
+}
+#endif
+
 /* Create a new connection context. */
 static ConnContext * new_context(const char * user, const char * accountname,
 	const char * protocol)
@@ -64,7 +154,7 @@ static ConnContext * new_context(const char * user, const char * accountname,
     context->otr_offer = OFFER_NOT;
     context->app_data = NULL;
     context->app_data_free = NULL;
-    context->context_priv = context_priv_new();
+    context->context_priv = otrl_context_priv_new();
     assert(context->context_priv != NULL);
     context->next = NULL;
     context->m_context = context;
@@ -330,7 +420,7 @@ void otrl_context_force_finished(ConnContext *context)
     context->sessionid_len = 0;
     context->protocol_version = 0;
     otrl_sm_state_free(context->smstate);
-    context_priv_force_finished(context->context_priv);
+    otrl_context_priv_force_finished(context->context_priv);
 }
 
 /* Force a context into the OTRL_MSGSTATE_PLAINTEXT state. */
diff --git a/src/context_priv.c b/src/context_priv.c
index 7c568c7..47d05b9 100644
--- a/src/context_priv.c
+++ b/src/context_priv.c
@@ -29,7 +29,7 @@
 #include "context_priv.h"
 
 /* Create a new private connection context */
-ConnContextPriv *context_priv_new()
+ConnContextPriv *otrl_context_priv_new()
 {
 	ConnContextPriv *context_priv;
 	context_priv = malloc(sizeof(*context_priv));
@@ -67,7 +67,7 @@ ConnContextPriv *context_priv_new()
 /* Resets the appropriate variables when a context
  * is being force finished
  */
-void context_priv_force_finished(ConnContextPriv *context_priv)
+void otrl_context_priv_force_finished(ConnContextPriv *context_priv)
 {
 	free(context_priv->fragment);
 	context_priv->fragment = NULL;
diff --git a/src/context_priv.h b/src/context_priv.h
index c17b11a..0748074 100644
--- a/src/context_priv.h
+++ b/src/context_priv.h
@@ -86,9 +86,9 @@ typedef struct context_priv {
 } ConnContextPriv;
 
 /* Create a new private connection context. */
-ConnContextPriv *context_priv_new();
+ConnContextPriv *otrl_context_priv_new();
 
-/* Frees up memory that was used in context_priv_new */
-void context_priv_force_finished(ConnContextPriv *context_priv);
+/* Frees up memory that was used in otrl_context_priv_new */
+void otrl_context_priv_force_finished(ConnContextPriv *context_priv);
 
 #endif
diff --git a/src/dh.c b/src/dh.c
index 8a22437..c5425a4 100644
--- a/src/dh.c
+++ b/src/dh.c
@@ -205,7 +205,6 @@ gcry_error_t otrl_dh_session(DH_sesskeys *sess, const DH_keypair *kp,
 
     /* Calculate the extra key (used if applications wish to extract a
      * symmetric key for transferring files, or something like that) */
-    /* XXX: Document this in the protocol spec */
     gabdata[0] = 0xff;
     gcry_md_hash_buffer(GCRY_MD_SHA256, sess->extrakey, gabdata, gablen+5);
 
diff --git a/src/message.c b/src/message.c
index a6c167d..5aa14d9 100644
--- a/src/message.c
+++ b/src/message.c
@@ -35,6 +35,17 @@
 #include "sm.h"
 #include "instag.h"
 
+#if OTRL_DEBUGGING
+#include <stdio.h>
+
+/* If OTRL_DEBUGGING is on, and the user types this string, the current
+ * context and its siblings will be dumped to stderr. */
+const char *OTRL_DEBUGGING_DEBUGSTR = "?OTR!";
+
+void otrl_context_all_dump(FILE *f, OtrlUserState us);
+void otrl_context_siblings_dump(FILE *f, const ConnContext *context);
+#endif
+
 /* The API version */
 extern unsigned int otrl_api_version;
 
@@ -45,6 +56,15 @@ extern unsigned int otrl_api_version;
  * resending in response to a rekey? */
 #define RESEND_INTERVAL 60
 
+/* How long should we wait for the last of the logged-in instances of
+ * our buddy to respond before marking our private key as a candidate
+ * for wiping (in seconds)? */
+#define MAX_AKE_WAIT_TIME 60
+
+/* How frequently should we check our ConnContexts for wipeable private
+ * keys (and wipe them) (in seconds)? */
+#define POLL_DEFAULT_INTERVAL 70
+
 /* 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. */
@@ -240,6 +260,31 @@ gcry_error_t otrl_message_sending(OtrlUserState us,
 	goto fragment;
     }
 
+#if OTRL_DEBUGGING
+    /* If the user typed the magic debug string, dump this context and
+     * its siblings. */
+    {
+	const char *debugtag = strstr(original_msg, OTRL_DEBUGGING_DEBUGSTR);
+
+	if (debugtag) {
+	    const char *debugargs =
+		debugtag + strlen(OTRL_DEBUGGING_DEBUGSTR);
+	    if (debugargs[0] == '!') { /* typed ?OTR!! */
+		otrl_context_all_dump(stderr, us);
+	    } else { /* typed ?OTR! without extra command chars */
+		otrl_context_siblings_dump(stderr, context);
+	    }
+
+	    /* Don't actually send the message */
+	    *messagep = strdup("");
+	    if (!(*messagep)) {
+		err = gcry_error(GPG_ERR_ENOMEM);
+	    }
+	    goto fragment;
+	}
+    }
+#endif
+
     /* If this is an OTR Query message, don't encrypt it. */
     if (otrl_proto_message_type(original_msg) == OTRL_MSGTYPE_QUERY) {
 	/* Replace the "?OTR?" with a custom message */
@@ -408,15 +453,38 @@ fragment:
  * 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)
+	void *opdata, gcry_error_t err, ConnContext *context,
+	OtrlUserState us)
 {
     if (!err) {
 	const char *msg = context->auth.lastauthmsg;
 	if (msg && *msg) {
 	    fragment_and_send(ops, opdata, context, msg,
 		    OTRL_FRAGMENT_SEND_ALL, NULL);
-	    context->context_priv->lastsent = time(NULL);
-	    otrl_context_update_recent_child(context, 1);
+	    time_t now = time(NULL);
+	    /* Update the "last sent" fields, unless this is a version 3
+	     * message typing to update the master context (as happens
+	     * when sending a v3 COMMIT message, for example). */
+	    if (context != context->m_context ||
+		    context->auth.protocol_version != 3) {
+		context->context_priv->lastsent = now;
+		otrl_context_update_recent_child(context, 1);
+	    }
+
+	    /* If this is a master context, and we're sending a v3 COMMIT
+	     * message, update the commit_sent_time timestamp, so we can
+	     * expire it. */
+	    if (context == context->m_context &&
+		    context->auth.authstate == OTRL_AUTHSTATE_AWAITING_DHKEY &&
+		    context->auth.protocol_version == 3) {
+		context->auth.commit_sent_time = now;
+		/* If there's not already a timer running to clean up
+		 * this private key, try to start one. */
+		if (us->timer_running == 0 && ops && ops->timer_control) {
+		    ops->timer_control(opdata, POLL_DEFAULT_INTERVAL);
+		    us->timer_running = 1;
+		}
+	    }
 	}
     } else {
 	if (ops->handle_msg_event) {
@@ -1015,11 +1083,9 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 		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) {
+	    if (msgtype == OTRL_MSGTYPE_DH_KEY) {
 		otrl_auth_copy_on_key(&(m_context->auth), &(context->auth));
-	    } else {
+	    } else if (msgtype != OTRL_MSGTYPE_DH_COMMIT) {
 		return 1;  /* Ignore unexpected message */
 	    }
 
@@ -1027,8 +1093,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 	    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)) {
+	} else if (m_context != context) {
 	    /* Switching from m_context to existing instance context */
 	    if (msgtype == OTRL_MSGTYPE_DH_KEY && m_context->auth.authstate
 		    == OTRL_AUTHSTATE_AWAITING_DHKEY &&
@@ -1038,16 +1103,8 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 		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) {
@@ -1089,11 +1146,11 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 	    switch(otrl_proto_query_bestversion(message, policy)) {
 		case 3:
 		    err = otrl_auth_start_v23(&(context->auth), 3);
-		    send_or_error_auth(ops, opdata, err, context);
+		    send_or_error_auth(ops, opdata, err, context, us);
 		    break;
 		case 2:
 		    err = otrl_auth_start_v23(&(context->auth), 2);
-		    send_or_error_auth(ops, opdata, err, context);
+		    send_or_error_auth(ops, opdata, err, context, us);
 		    break;
 		case 1:
 		    /* Get our private key */
@@ -1111,7 +1168,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 		    if (privkey) {
 			err = otrl_auth_start_v1(&(context->auth), our_dh,
 				our_keyid, privkey);
-			send_or_error_auth(ops, opdata, err, context);
+			send_or_error_auth(ops, opdata, err, context, us);
 		    }
 		    break;
 		default:
@@ -1124,7 +1181,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 
 	case OTRL_MSGTYPE_DH_COMMIT:
 	    err = otrl_auth_handle_commit(&(context->auth), otrtag, version);
-	    send_or_error_auth(ops, opdata, err, context);
+	    send_or_error_auth(ops, opdata, err, context, us);
 
 	    if (edata.ignore_message == -1) edata.ignore_message = 1;
 	    break;
@@ -1146,7 +1203,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 		err = otrl_auth_handle_key(&(context->auth), otrtag,
 			&haveauthmsg, privkey);
 		if (err || haveauthmsg) {
-		    send_or_error_auth(ops, opdata, err, context);
+		    send_or_error_auth(ops, opdata, err, context, us);
 		}
 	    }
 
@@ -1171,7 +1228,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 			otrtag, &haveauthmsg, privkey, go_encrypted,
 			&edata);
 		if (err || haveauthmsg) {
-		    send_or_error_auth(ops, opdata, err, context);
+		    send_or_error_auth(ops, opdata, err, context, us);
 		    maybe_resend(&edata);
 		}
 	    }
@@ -1183,7 +1240,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 	    err = otrl_auth_handle_signature(&(context->auth),
 		    otrtag, &haveauthmsg, go_encrypted, &edata);
 	    if (err || haveauthmsg) {
-		send_or_error_auth(ops, opdata, err, context);
+		send_or_error_auth(ops, opdata, err, context, us);
 		maybe_resend(&edata);
 	    }
 
@@ -1218,7 +1275,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 			message, &haveauthmsg, privkey, our_dh, our_keyid,
 			go_encrypted, &edata);
 		if (err || haveauthmsg) {
-		    send_or_error_auth(ops, opdata, err, context);
+		    send_or_error_auth(ops, opdata, err, context, us);
 		    maybe_resend(&edata);
 		}
 	    }
@@ -1738,11 +1795,11 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 		switch(bestversion) {
 		    case 3:
 			err = otrl_auth_start_v23(&(context->auth), 3);
-			send_or_error_auth(ops, opdata, err, context);
+			send_or_error_auth(ops, opdata, err, context, us);
 			break;
 		    case 2:
 			err = otrl_auth_start_v23(&(context->auth), 2);
-			send_or_error_auth(ops, opdata, err, context);
+			send_or_error_auth(ops, opdata, err, context, us);
 			break;
 		    case 1:
 			/* Get our private key */
@@ -1762,7 +1819,7 @@ int otrl_message_receiving(OtrlUserState us, const OtrlMessageAppOps *ops,
 			if (privkey) {
 			    err = otrl_auth_start_v1(&(context->auth), NULL, 0,
 				    privkey);
-			    send_or_error_auth(ops, opdata, err, context);
+			    send_or_error_auth(ops, opdata, err, context, us);
 			}
 			break;
 		    default:
@@ -1930,3 +1987,54 @@ gcry_error_t otrl_message_symkey(OtrlUserState us,
     /* We weren't in an encrypted session. */
     return gcry_error(GPG_ERR_INV_VALUE);
 }
+
+/* If you do _not_ define a timer_control callback function, set a timer
+ * to go off every definterval =
+ * otrl_message_poll_get_default_interval(userstate) seconds, and call
+ * otrl_message_poll every time the timer goes off. */
+unsigned int otrl_message_poll_get_default_interval(OtrlUserState us)
+{
+    return POLL_DEFAULT_INTERVAL;
+}
+
+/* Call this function every so often, either as directed by the
+ * timer_control callback, or every definterval =
+ * otrl_message_poll_get_default_interval(userstate) seconds if you have
+ * no timer_control callback.  This function must be called from the
+ * main libotr thread.*/
+void otrl_message_poll(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata)
+{
+    /* Wipe private keys last sent before this time */
+    time_t expire_before = time(NULL) - MAX_AKE_WAIT_TIME;
+
+    ConnContext *contextp;
+
+    /* Is there a context still waiting for a DHKEY message, even after
+     * we wipe the stale ones? */
+    int still_waiting = 0;
+
+    if (us == NULL) return;
+    
+    for (contextp = us->context_root; contextp; contextp = contextp->next) {
+	/* If this is a master context, and it's still waiting for a
+	 * v3 DHKEY message, see if it's waited long enough. */
+	if (contextp->m_context == contextp &&
+		contextp->auth.authstate == OTRL_AUTHSTATE_AWAITING_DHKEY &&
+		contextp->auth.protocol_version == 3 &&
+		contextp->auth.commit_sent_time > 0) {
+	    if (contextp->auth.commit_sent_time < expire_before) {
+		otrl_auth_clear(&contextp->auth);
+	    } else {
+		/* Not yet expired */
+		still_waiting = 1;
+	    }
+	}
+    }
+
+    /* If there's nothing more to wait for, stop the timer, if possible. */
+    if (still_waiting == 0 && ops && ops->timer_control) {
+	ops->timer_control(opdata, 0);
+	us->timer_running = 0;
+    }
+}
diff --git a/src/message.h b/src/message.h
index db5053f..45b1305 100644
--- a/src/message.h
+++ b/src/message.h
@@ -255,6 +255,43 @@ typedef struct s_OtrlMessageAppOps {
      /* Deallocate a string returned by convert_msg. */
     void (*convert_free)(void *opdata, ConnContext *context, char *dest);
 
+    /* When timer_control is called, turn off any existing periodic
+     * timer.
+     * 
+     * Additionally, if interval > 0, set a new periodic timer
+     * to go off every interval seconds.  When that timer fires, you
+     * must call otrl_message_poll(userstate, uiops, uiopdata); from the
+     * main libotr thread.
+     * 
+     * The timing does not have to be exact; this timer is used to
+     * provide forward secrecy by cleaning up stale private state that
+     * may otherwise stick around in memory.  Note that the
+     * timer_control callback may be invoked from otrl_message_poll
+     * itself, possibly to indicate that interval == 0 (that is, that
+     * there's no more periodic work to be done at this time).
+     * 
+     * If you set this callback to NULL, then you must ensure that your
+     * application calls otrl_message_poll(userstate, uiops, uiopdata);
+     * from the main libotr thread every definterval seconds (where
+     * definterval can be obtained by calling
+     * definterval = otrl_message_poll_get_default_interval(userstate);
+     * right after creating the userstate).  The advantage of
+     * implementing the timer_control callback is that the timer can be
+     * turned on by libotr only when it's needed.
+     * 
+     * It is not a problem (except for a minor performance hit) to call
+     * otrl_message_poll more often than requested, whether
+     * timer_control is implemented or not.
+     * 
+     * If you fail to implement the timer_control callback, and also
+     * fail to periodically call otrl_message_poll, then you open your
+     * users to a possible forward secrecy violation: an attacker that
+     * compromises the user's computer may be able to decrypt a handful
+     * of long-past messages (the first messages of an OTR
+     * conversation).
+     */
+    void (*timer_control)(void *opdata, unsigned int interval);
+
 } OtrlMessageAppOps;
 
 /* Deallocate a message allocated by other otrl_message_* routines. */
@@ -385,4 +422,18 @@ gcry_error_t otrl_message_symkey(OtrlUserState us,
 	unsigned int use, const unsigned char *usedata, size_t usedatalen,
 	unsigned char *symkey);
 
+/* If you do _not_ define a timer_control callback function, set a timer
+ * to go off every definterval =
+ * otrl_message_poll_get_default_interval(userstate) seconds, and call
+ * otrl_message_poll every time the timer goes off. */
+unsigned int otrl_message_poll_get_default_interval(OtrlUserState us);
+
+/* Call this function every so often, either as directed by the
+ * timer_control callback, or every definterval =
+ * otrl_message_poll_get_default_interval(userstate) seconds if you have
+ * no timer_control callback.  This function must be called from the
+ * main libotr thread.*/
+void otrl_message_poll(OtrlUserState us, const OtrlMessageAppOps *ops,
+	void *opdata);
+
 #endif
diff --git a/src/proto.c b/src/proto.c
index fc33081..3522249 100644
--- a/src/proto.c
+++ b/src/proto.c
@@ -38,6 +38,10 @@
 #include "tlv.h"
 #include "serial.h"
 
+#if OTRL_DEBUGGING
+extern const char *OTRL_DEBUGGING_DEBUGSTR;
+#endif
+
 /* For now, we need to know the API version the client is using so that
  * we don't use any UI callbacks it hasn't set. */
 unsigned int otrl_api_version = 0;
@@ -74,6 +78,12 @@ void otrl_init(unsigned int ver_major, unsigned int ver_minor,
 
     /* Initialize the SM module */
     otrl_sm_init();
+
+#if OTRL_DEBUGGING
+    /* Inform the user that debugging is available */
+    fprintf(stderr, "\nlibotr debugging is available.  Type %s in a message\n"
+	    "  to see debug info.\n\n", OTRL_DEBUGGING_DEBUGSTR);
+#endif
 }
 
 /* Return a pointer to a static string containing the version number of
diff --git a/src/sm.c b/src/sm.c
index 46f87f7..653030a 100644
--- a/src/sm.c
+++ b/src/sm.c
@@ -30,6 +30,30 @@
 #include "sm.h"
 #include "serial.h"
 
+#if OTRL_DEBUGGING
+
+/* Dump the contents of an SMState to the FILE *f. */
+void otrl_sm_dump(FILE *f, const OtrlSMState *sm)
+{
+    fprintf(f, "  SM state:\n");
+    fprintf(f, "    Next expected: %d (%s)\n", sm->nextExpected,
+	sm->nextExpected == OTRL_SMP_EXPECT1 ? "EXPECT1" :
+	sm->nextExpected == OTRL_SMP_EXPECT2 ? "EXPECT2" :
+	sm->nextExpected == OTRL_SMP_EXPECT3 ? "EXPECT3" :
+	sm->nextExpected == OTRL_SMP_EXPECT4 ? "EXPECT4" :
+	sm->nextExpected == OTRL_SMP_EXPECT5 ? "EXPECT5" :
+	"INVALID");
+    fprintf(f, "    Received_Q: %d\n", sm->received_question);
+    fprintf(f, "    Progress state: %d (%s)\n", sm->sm_prog_state,
+	sm->sm_prog_state == OTRL_SMP_PROG_OK ? "OK" :
+	sm->sm_prog_state == OTRL_SMP_PROG_CHEATED ? "CHEATED" :
+	sm->sm_prog_state == OTRL_SMP_PROG_FAILED ? "FAILED" :
+	sm->sm_prog_state == OTRL_SMP_PROG_SUCCEEDED ? "SUCCEEDED" :
+	"INVALID");
+}
+
+#endif
+
 static const int SM_MSG1_LEN = 6;
 static const int SM_MSG2_LEN = 11;
 static const int SM_MSG3_LEN = 8;
diff --git a/src/userstate.c b/src/userstate.c
index 8d099ec..58f5a05 100644
--- a/src/userstate.c
+++ b/src/userstate.c
@@ -41,10 +41,12 @@ OtrlUserState otrl_userstate_create(void)
     us->privkey_root = NULL;
     us->instag_root = NULL;
     us->pending_root = NULL;
+    us->timer_running = 0;
     return us;
 }
 
-/* Free a OtrlUserState */
+/* Free a OtrlUserState.  If you have a timer running for this userstate,
+stop it before freeing the userstate. */
 void otrl_userstate_free(OtrlUserState us)
 {
     otrl_context_forget_all(us);
diff --git a/src/userstate.h b/src/userstate.h
index 691616c..3f1e3b8 100644
--- a/src/userstate.h
+++ b/src/userstate.h
@@ -32,6 +32,7 @@ struct s_OtrlUserState {
     OtrlPrivKey *privkey_root;
     OtrlInsTag *instag_root;
     OtrlPendingPrivKey *pending_root;
+    int timer_running;
 };
 
 /* Create a new OtrlUserState.  Most clients will only need one of
@@ -43,7 +44,8 @@ struct s_OtrlUserState {
  * OtrlUserState. */
 OtrlUserState otrl_userstate_create(void);
 
-/* Free a OtrlUserState */
+/* Free a OtrlUserState.  If you have a timer running for this userstate,
+stop it before freeing the userstate. */
 void otrl_userstate_free(OtrlUserState us);
 
 #endif

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-privacy/packages/libotr.git



More information about the Pkg-privacy-commits mailing list