[Pkg-privacy-commits] [libotr] 114/225: Add a timer_control callback to OtrlMessageAppOps

Ximin Luo infinity0 at moszumanska.debian.org
Sat Aug 22 12:45:07 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 d0eef09c16efab9f24744ef856ce495c6f6b5bcf
Author: Ian Goldberg <iang at cs.uwaterloo.ca>
Date:   Mon Aug 27 11:25:11 2012 -0400

    Add a timer_control callback to OtrlMessageAppOps
    
    The new timer_control callback will instruct the application to call the
    new otrl_message_poll function in order to actually clear out the above
    stale committed keys.
---
 ChangeLog       | 17 ++++++++++
 UPGRADING       | 48 +++++++++++++++++++++++++++++
 src/message.c   | 96 ++++++++++++++++++++++++++++++++++++++++++++++++---------
 src/message.h   | 51 ++++++++++++++++++++++++++++++
 src/userstate.c |  4 ++-
 src/userstate.h |  4 ++-
 6 files changed, 204 insertions(+), 16 deletions(-)

diff --git a/ChangeLog b/ChangeLog
index 8bfadb7..82984bf 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+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:
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/message.c b/src/message.c
index ea6b3b9..5aa14d9 100644
--- a/src/message.c
+++ b/src/message.c
@@ -56,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. */
@@ -444,7 +453,8 @@ 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;
@@ -461,12 +471,19 @@ static gcry_error_t send_or_error_auth(const OtrlMessageAppOps *ops,
 		otrl_context_update_recent_child(context, 1);
 	    }
 
-	    /* If this is a master context, and we're sending a COMMIT
+	    /* 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.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 {
@@ -1129,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 */
@@ -1151,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:
@@ -1164,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;
@@ -1186,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);
 		}
 	    }
 
@@ -1211,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);
 		}
 	    }
@@ -1223,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);
 	    }
 
@@ -1258,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);
 		}
 	    }
@@ -1778,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 */
@@ -1802,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:
@@ -1970,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/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