Commit 7515af93 authored by Johan Pascal's avatar Johan Pascal

Zid Cache resilient to first negotiation interruption or failure

+ do not store in cache any peer information until first negotiation succeeds
parent 41b2163c
...@@ -170,6 +170,7 @@ typedef struct bzrtpCallbacks_struct { ...@@ -170,6 +170,7 @@ typedef struct bzrtpCallbacks_struct {
#define BZRTP_ERROR_CHANNELALREADYSTARTED 0x0100 #define BZRTP_ERROR_CHANNELALREADYSTARTED 0x0100
#define BZRTP_ERROR_CACHEDISABLED 0x0200 #define BZRTP_ERROR_CACHEDISABLED 0x0200
#define BZRTP_ERROR_CACHEMIGRATIONFAILED 0x0400 #define BZRTP_ERROR_CACHEMIGRATIONFAILED 0x0400
#define BZRTP_ERROR_CACHE_PEERNOTFOUND 0x0800
/* channel status definition */ /* channel status definition */
#define BZRTP_CHANNEL_NOTFOUND 0x1000 #define BZRTP_CHANNEL_NOTFOUND 0x1000
...@@ -432,21 +433,6 @@ BZRTP_EXPORT int bzrtp_initCache(void *db); ...@@ -432,21 +433,6 @@ BZRTP_EXPORT int bzrtp_initCache(void *db);
*/ */
BZRTP_EXPORT int bzrtp_getSelfZID(void *db, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext); BZRTP_EXPORT int bzrtp_getSelfZID(void *db, const char *selfURI, uint8_t selfZID[12], bctbx_rng_context_t *RNGContext);
/**
* @brief get the cache internal id used to bind local uri(hence local ZID associated to it)<->peer uri/peer ZID.
* Providing a valid local URI(already present in cache), a peer ZID and peer URI will return the zuid creating it if needed
* Any pair ZID/sipURI shall identify an account on a device.
*
* @param[in/out] db the opened sqlite database pointer
* @param[in] selfURI local URI, must be already associated to a ZID in the DB(association is performed by any call of getSelfZID on this URI)
* @param[in] peerURI peer URI
* @param[in] peerZID peer ZID
* @param[out] zuid the internal db reference to the data row matching this particular pair of correspondant
*
* @return 0 on success, error code otherwise
*/
BZRTP_EXPORT int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerURI, const uint8_t peerZID[12], int *zuid);
/** /**
* @brief Write(insert or update) data in cache, adressing it by zuid (ZID/URI binding id used in cache) * @brief Write(insert or update) data in cache, adressing it by zuid (ZID/URI binding id used in cache)
* Get arrays of column names, values to be inserted, lengths of theses values * Get arrays of column names, values to be inserted, lengths of theses values
......
...@@ -47,4 +47,25 @@ ...@@ -47,4 +47,25 @@
*/ */
BZRTP_EXPORT int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]); BZRTP_EXPORT int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]);
/**
* @brief get the cache internal id used to bind local uri(hence local ZID associated to it)<->peer uri/peer ZID.
* Providing a valid local URI(already present in cache), a peer ZID and peer URI will return the zuid creating it if needed and requested
* Any pair ZID/sipURI shall identify an account on a device.
*
* @param[in/out] db the opened sqlite database pointer
* @param[in] selfURI local URI, must be already associated to a ZID in the DB(association is performed by any call of getSelfZID on this URI)
* @param[in] peerURI peer URI
* @param[in] peerZID peer ZID
* @param[in] insertFlag A boolean managing insertion or not of a new row:
* - BZRTP_ZIDCACHE_DONT_INSERT_ZUID : if not found identity binding won't lead to insertion and return zuid will be 0
* - BZRTP_ZIDCACHE_INSERT_ZUID : if not found, insert a new row in ziduri table and return newly inserted zuid
* @param[out] zuid the internal db reference to the data row matching this particular pair of correspondant
* if identity binding is not found and insertFlag set to 0, this value is set to 0
*
* @return 0 on success, BZRTP_ERROR_CACHE_PEERNOTFOUND if peer was not in and the insert flag is not set to BZRTP_ZIDCACHE_INSERT_ZUID, error code otherwise
*/
#define BZRTP_ZIDCACHE_DONT_INSERT_ZUID 0
#define BZRTP_ZIDCACHE_INSERT_ZUID 1
BZRTP_EXPORT int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerURI, const uint8_t peerZID[12], const uint8_t insertFlag, int *zuid);
#endif /* ZIDCACHE_H */ #endif /* ZIDCACHE_H */
...@@ -2248,6 +2248,12 @@ int bzrtp_updateCachedSecrets(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t ...@@ -2248,6 +2248,12 @@ int bzrtp_updateCachedSecrets(bzrtpContext_t *zrtpContext, bzrtpChannelContext_t
bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"retained secret", 15, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, RETAINED_SECRET_LENGTH, (void (*)(uint8_t *, uint8_t, uint8_t *, uint32_t, uint8_t, uint8_t *))zrtpChannelContext->hmacFunction, zrtpContext->cachedSecret.rs1); bzrtp_keyDerivationFunction(zrtpChannelContext->s0, zrtpChannelContext->hashLength, (uint8_t *)"retained secret", 15, zrtpChannelContext->KDFContext, zrtpChannelContext->KDFContextLength, RETAINED_SECRET_LENGTH, (void (*)(uint8_t *, uint8_t, uint8_t *, uint32_t, uint8_t, uint8_t *))zrtpChannelContext->hmacFunction, zrtpContext->cachedSecret.rs1);
colValues[0]=zrtpContext->cachedSecret.rs1; colValues[0]=zrtpContext->cachedSecret.rs1;
/* before writing into cache, we must check we have the zuid correctly set, if not (it's our first successfull exchange with peer), insert it*/
if (zrtpContext->zuid==0) {
bzrtp_cache_getZuid((void *)zrtpContext->zidCache, zrtpContext->selfURI, zrtpContext->peerURI, zrtpContext->peerZID, BZRTP_ZIDCACHE_INSERT_ZUID, &zrtpContext->zuid);
}
bzrtp_cache_write(zrtpContext->zidCache, zrtpContext->zuid, "zrtp", colNames, colValues, colLength, 2); bzrtp_cache_write(zrtpContext->zidCache, zrtpContext->zuid, "zrtp", colNames, colValues, colLength, 2);
/* if exist, call the callback function to perform custom cache operation that may use s0(writing exported key into cache) */ /* if exist, call the callback function to perform custom cache operation that may use s0(writing exported key into cache) */
......
...@@ -293,7 +293,7 @@ int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]) ...@@ -293,7 +293,7 @@ int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12])
} }
/* get all secrets from zrtp table, ORDER BY is just to ensure consistent return in case of inconsistent table) */ /* get all secrets from zrtp table, ORDER BY is just to ensure consistent return in case of inconsistent table) */
stmt = sqlite3_mprintf("SELECT z.zuid, z.rs1, z.rs2, z.aux, z.pbx, z.pvs FROM ziduri as zu LEFT JOIN zrtp as z ON z.zuid=zu.zuid WHERE zu.selfuri=? AND zu.peeruri=? AND zu.zid=? ORDER BY zu.zuid LIMIT 1;"); stmt = sqlite3_mprintf("SELECT z.zuid, z.rs1, z.rs2, z.aux, z.pbx, z.pvs FROM ziduri as zu INNER JOIN zrtp as z ON z.zuid=zu.zuid WHERE zu.selfuri=? AND zu.peeruri=? AND zu.zid=? ORDER BY zu.zuid LIMIT 1;");
ret = sqlite3_prepare_v2(context->zidCache, stmt, -1, &sqlStmt, NULL); ret = sqlite3_prepare_v2(context->zidCache, stmt, -1, &sqlStmt, NULL);
sqlite3_free(stmt); sqlite3_free(stmt);
if (ret != SQLITE_OK) { if (ret != SQLITE_OK) {
...@@ -307,8 +307,8 @@ int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]) ...@@ -307,8 +307,8 @@ int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12])
if (ret!=SQLITE_ROW) { if (ret!=SQLITE_ROW) {
sqlite3_finalize(sqlStmt); sqlite3_finalize(sqlStmt);
if (ret == SQLITE_DONE) {/* not found in cache, just leave cached secrets reset, but retrieve (or create) zuid */ if (ret == SQLITE_DONE) {/* not found in cache, just leave cached secrets reset, but retrieve zuid, do not insert new peer ZID at this step, it must be done only when negotiation succeeds */
return bzrtp_cache_getZuid((void *)context->zidCache, context->selfURI, context->peerURI, context->peerZID, &context->zuid); return bzrtp_cache_getZuid((void *)context->zidCache, context->selfURI, context->peerURI, context->peerZID, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &context->zuid);
} else { /* we had an error querying the DB... */ } else { /* we had an error querying the DB... */
return BZRTP_ZIDCACHE_UNABLETOREAD; return BZRTP_ZIDCACHE_UNABLETOREAD;
} }
...@@ -363,20 +363,25 @@ int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12]) ...@@ -363,20 +363,25 @@ int bzrtp_getPeerAssociatedSecrets(bzrtpContext_t *context, uint8_t peerZID[12])
return 0; return 0;
} }
/** /**
* @brief get the cache internal id used to bind local uri(hence local ZID associated to it)<->peer uri/peer ZID. * @brief get the cache internal id used to bind local uri(hence local ZID associated to it)<->peer uri/peer ZID.
* Providing a valid local URI(already present in cache), a peer ZID and peer URI will return the zuid creating it if needed * Providing a valid local URI(already present in cache), a peer ZID and peer URI will return the zuid creating it if needed and requested
* Any pair ZID/sipURI shall identify an account on a device. * Any pair ZID/sipURI shall identify an account on a device.
* *
* @param[in/out] db the opened sqlite database pointer * @param[in/out] db the opened sqlite database pointer
* @param[in] selfURI local URI, must be already associated to a ZID in the DB(association is performed by any call of getSelfZID on this URI) * @param[in] selfURI local URI, must be already associated to a ZID in the DB(association is performed by any call of getSelfZID on this URI)
* @param[in] peerURI peer URI * @param[in] peerURI peer URI
* @param[in] peerZID peer ZID * @param[in] peerZID peer ZID
* @param[in] insertFlag A boolean managing insertion or not of a new row:
* - BZRTP_ZIDCACHE_DONT_INSERT_ZUID : if not found identity binding won't lead to insertion and return zuid will be 0
* - BZRTP_ZIDCACHE_INSERT_ZUID : if not found, insert a new row in ziduri table and return newly inserted zuid
* @param[out] zuid the internal db reference to the data row matching this particular pair of correspondant * @param[out] zuid the internal db reference to the data row matching this particular pair of correspondant
* if identity binding is not found and insertFlag set to BZRTP_ZIDCACHE_DONT_INSERT_ZUID, this value is set to 0
* *
* @return 0 on success, error code otherwise * @return 0 on success, BZRTP_ERROR_CACHE_PEERNOTFOUND if peer was not in and the insert flag is not set to BZRTP_ZIDCACHE_INSERT_ZUID, error code otherwise
*/ */
int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerURI, const uint8_t peerZID[12], int *zuid) { int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerURI, const uint8_t peerZID[12], const uint8_t insertFlag, int *zuid) {
char *stmt=NULL; char *stmt=NULL;
int ret; int ret;
sqlite3_stmt *sqlStmt = NULL; sqlite3_stmt *sqlStmt = NULL;
...@@ -403,41 +408,47 @@ int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerUR ...@@ -403,41 +408,47 @@ int bzrtp_cache_getZuid(void *dbPointer, const char *selfURI, const char *peerUR
if (ret!=SQLITE_ROW) { /* We didn't found this binding in the DB */ if (ret!=SQLITE_ROW) { /* We didn't found this binding in the DB */
sqlite3_finalize(sqlStmt); sqlite3_finalize(sqlStmt);
if (ret == SQLITE_DONE) { /* query executed correctly, just our data is not there */ if (ret == SQLITE_DONE) { /* query executed correctly, just our data is not there */
uint8_t *localZID = NULL; /* shall we insert it? */
char *errmsg=NULL; if (insertFlag == BZRTP_ZIDCACHE_INSERT_ZUID) {
uint8_t *localZID = NULL;
/* check that we have a self ZID matching the self URI and insert a new row */ char *errmsg=NULL;
stmt = sqlite3_mprintf("SELECT zid FROM ziduri WHERE selfuri=%Q AND peeruri='self' ORDER BY zuid LIMIT 1;",selfURI);
ret = sqlite3_exec(db,stmt,callback_getSelfZID,&localZID,&errmsg); /* check that we have a self ZID matching the self URI and insert a new row */
sqlite3_free(stmt); stmt = sqlite3_mprintf("SELECT zid FROM ziduri WHERE selfuri=%Q AND peeruri='self' ORDER BY zuid LIMIT 1;",selfURI);
if (ret != SQLITE_OK) { ret = sqlite3_exec(db,stmt,callback_getSelfZID,&localZID,&errmsg);
sqlite3_free(errmsg); sqlite3_free(stmt);
return BZRTP_ZIDCACHE_UNABLETOREAD;
}
if (localZID==NULL) { /* this sip URI is not in our DB, do not create an association with the peer ZID/URI binding */
return BZRTP_ZIDCACHE_BADINPUTDATA;
} else { /* yes we know this URI on local device, add a row in the ziduri table */
free(localZID);
stmt = sqlite3_mprintf("INSERT INTO ziduri (zid,selfuri,peeruri) VALUES(?,?,?);");
ret = sqlite3_prepare_v2(db, stmt, -1, &sqlStmt, NULL);
if (ret != SQLITE_OK) { if (ret != SQLITE_OK) {
return BZRTP_ZIDCACHE_UNABLETOUPDATE; sqlite3_free(errmsg);
return BZRTP_ZIDCACHE_UNABLETOREAD;
} }
sqlite3_free(stmt);
sqlite3_bind_blob(sqlStmt, 1, peerZID, 12, SQLITE_TRANSIENT); if (localZID==NULL) { /* this sip URI is not in our DB, do not create an association with the peer ZID/URI binding */
sqlite3_bind_text(sqlStmt, 2, selfURI,-1,SQLITE_TRANSIENT); return BZRTP_ZIDCACHE_BADINPUTDATA;
sqlite3_bind_text(sqlStmt, 3, peerURI,-1,SQLITE_TRANSIENT); } else { /* yes we know this URI on local device, add a row in the ziduri table */
free(localZID);
stmt = sqlite3_mprintf("INSERT INTO ziduri (zid,selfuri,peeruri) VALUES(?,?,?);");
ret = sqlite3_prepare_v2(db, stmt, -1, &sqlStmt, NULL);
if (ret != SQLITE_OK) {
return BZRTP_ZIDCACHE_UNABLETOUPDATE;
}
sqlite3_free(stmt);
sqlite3_bind_blob(sqlStmt, 1, peerZID, 12, SQLITE_TRANSIENT);
sqlite3_bind_text(sqlStmt, 2, selfURI,-1,SQLITE_TRANSIENT);
sqlite3_bind_text(sqlStmt, 3, peerURI,-1,SQLITE_TRANSIENT);
ret = sqlite3_step(sqlStmt); ret = sqlite3_step(sqlStmt);
if (ret!=SQLITE_DONE) { if (ret!=SQLITE_DONE) {
return BZRTP_ZIDCACHE_UNABLETOUPDATE; return BZRTP_ZIDCACHE_UNABLETOUPDATE;
}
sqlite3_finalize(sqlStmt);
/* get the zuid created */
*zuid = (int)sqlite3_last_insert_rowid(db);
return 0;
} }
sqlite3_finalize(sqlStmt); } else { /* no found and not inserted */
/* get the zuid created */ *zuid = 0;
*zuid = (int)sqlite3_last_insert_rowid(db); return BZRTP_ERROR_CACHE_PEERNOTFOUND;
return 0;
} }
} else { /* we had an error querying the DB... */ } else { /* we had an error querying the DB... */
return BZRTP_ZIDCACHE_UNABLETOREAD; return BZRTP_ZIDCACHE_UNABLETOREAD;
...@@ -479,6 +490,10 @@ int bzrtp_cache_write(void *dbPointer, int zuid, const char *tableName, const ch ...@@ -479,6 +490,10 @@ int bzrtp_cache_write(void *dbPointer, int zuid, const char *tableName, const ch
return BZRTP_ZIDCACHE_RUNTIME_CACHELESS; return BZRTP_ZIDCACHE_RUNTIME_CACHELESS;
} }
if (zuid == 0) { /* we're giving an invalid zuid, means we were not able to retrieve it previously, just do nothing */
return BZRTP_ERROR_CACHE_PEERNOTFOUND;
}
/* As the zuid row may not be already present in the table, we must run an insert or update SQL command which is not provided by sqlite3 */ /* As the zuid row may not be already present in the table, we must run an insert or update SQL command which is not provided by sqlite3 */
/* UPSERT strategy: run update first and check for changes */ /* UPSERT strategy: run update first and check for changes */
......
This diff is collapsed.
...@@ -95,9 +95,9 @@ static void test_cache_getSelfZID(void) { ...@@ -95,9 +95,9 @@ static void test_cache_getSelfZID(void) {
BC_ASSERT_EQUAL(memcmp(selfZIDalice, aliceContext->selfZID, 12), 0, int, "%d"); BC_ASSERT_EQUAL(memcmp(selfZIDalice, aliceContext->selfZID, 12), 0, int, "%d");
/* try to get a zuid on alice/bob+patternZIDbob, at first the row shall be created */ /* try to get a zuid on alice/bob+patternZIDbob, at first the row shall be created */
BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, &zuidalicebob), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, BZRTP_ZIDCACHE_INSERT_ZUID, &zuidalicebob), 0, int, "%x");
/* ask for it again and check they are the same */ /* ask for it again and check they are the same */
BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, &zuidCheck), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidCheck), 0, int, "%x");
BC_ASSERT_EQUAL(zuidalicebob, zuidCheck, int, "%d"); BC_ASSERT_EQUAL(zuidalicebob, zuidCheck, int, "%d");
/* Then write in cache zrtp table */ /* Then write in cache zrtp table */
...@@ -130,7 +130,7 @@ static void test_cache_getSelfZID(void) { ...@@ -130,7 +130,7 @@ static void test_cache_getSelfZID(void) {
BC_ASSERT_EQUAL(memcmp(selfZIDalice, patternZIDalice, 12), 0, int, "%x"); BC_ASSERT_EQUAL(memcmp(selfZIDalice, patternZIDalice, 12), 0, int, "%x");
/* test the getZuid function */ /* test the getZuid function */
BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, &zuidalicebob), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_cache_getZuid((void *)aliceDB, "alice@sip.linphone.org", "bob@sip.linphone.org", patternZIDbob, BZRTP_ZIDCACHE_DONT_INSERT_ZUID, &zuidalicebob), 0, int, "%x");
BC_ASSERT_EQUAL(zuidalicebob, 5, int, "%d"); /* from the pattern DB: the zuid for alice/bob+provided ZID is 5 */ BC_ASSERT_EQUAL(zuidalicebob, 5, int, "%d"); /* from the pattern DB: the zuid for alice/bob+provided ZID is 5 */
BC_ASSERT_EQUAL(bzrtp_getSelfZID(aliceDB, "ecila@sip.linphone.org", selfZIDecila, aliceContext->RNGContext), 0, int, "%x"); BC_ASSERT_EQUAL(bzrtp_getSelfZID(aliceDB, "ecila@sip.linphone.org", selfZIDecila, aliceContext->RNGContext), 0, int, "%x");
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment