diff --git a/ChangeLog b/ChangeLog index e338c4fd..d623f88a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,7 @@ -8/26/2015 Oleg Moskalenko -Version 4.4.5.5 'Ardee West': - - STUN attributes conflict resolution. +9/13/2015 Oleg Moskalenko +Version 4.5.0.0 'Ardee West': + - multiple realms based on oAuth (third-party authorization); + - STUN attributes conflict resolution; - SIGHUP handler fixed. 7/18/2015 Oleg Moskalenko diff --git a/INSTALL b/INSTALL index a1f1d081..0326a165 100644 --- a/INSTALL +++ b/INSTALL @@ -744,6 +744,7 @@ CREATE TABLE oauth_key ( timestamp bigint default 0, lifetime integer default 0, as_rs_alg varchar(64) default '', + realm varchar(127) default '', primary key (kid) ); @@ -763,6 +764,8 @@ The oauth_key table fields meanings are: "A256GCM", "A128GCM" (see http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-5.1). The default value is "A256GCM"; + + realm - (optional) can be used to set the user realm (if the field is not empty). # Https access admin users. # Leave this table empty if you do not want diff --git a/STATUS b/STATUS index 6afe79d5..8f088d2e 100644 --- a/STATUS +++ b/STATUS @@ -123,6 +123,9 @@ supported in the client library). 53) SHA384 and SHA512 support added (experimental). 54) native SCTP experimental support. + +55) Multi-tenant implementation based upon third-party authorization +(oAuth). Things to be implemented in future (the development roadmap) are described in the TODO file. diff --git a/examples/var/db/turndb b/examples/var/db/turndb index ad8e8cbd..3dc7eff0 100644 Binary files a/examples/var/db/turndb and b/examples/var/db/turndb differ diff --git a/man/man1/turnadmin.1 b/man/man1/turnadmin.1 index 0502b22c..a63ecbbc 100644 --- a/man/man1/turnadmin.1 +++ b/man/man1/turnadmin.1 @@ -1,5 +1,5 @@ .\" Text automatically generated by txt2man -.TH TURN 1 "01 September 2015" "" "" +.TH TURN 1 "13 September 2015" "" "" .SH GENERAL INFORMATION \fIturnadmin\fP is a TURN administration tool. This tool can be used to manage diff --git a/man/man1/turnserver.1 b/man/man1/turnserver.1 index 990b0bcc..17bad32d 100644 --- a/man/man1/turnserver.1 +++ b/man/man1/turnserver.1 @@ -1,5 +1,5 @@ .\" Text automatically generated by txt2man -.TH TURN 1 "01 September 2015" "" "" +.TH TURN 1 "13 September 2015" "" "" .SH GENERAL INFORMATION The \fBTURN Server\fP project contains the source code of a TURN server and TURN client diff --git a/man/man1/turnutils.1 b/man/man1/turnutils.1 index d6a550a5..bf0f4cae 100644 --- a/man/man1/turnutils.1 +++ b/man/man1/turnutils.1 @@ -1,5 +1,5 @@ .\" Text automatically generated by txt2man -.TH TURN 1 "01 September 2015" "" "" +.TH TURN 1 "13 September 2015" "" "" .SH GENERAL INFORMATION A set of turnutils_* programs provides some utility functionality to be used diff --git a/rpm/build.settings.sh b/rpm/build.settings.sh index 89548d36..09ec393b 100755 --- a/rpm/build.settings.sh +++ b/rpm/build.settings.sh @@ -2,7 +2,7 @@ # Common settings script. -TURNVERSION=4.4.5.5 +TURNVERSION=4.5.0.0 BUILDDIR=~/rpmbuild ARCH=`uname -p` TURNSERVER_GIT_URL=https://github.com/coturn/coturn.git diff --git a/rpm/turnserver.spec b/rpm/turnserver.spec index d29767c9..05414bdb 100644 --- a/rpm/turnserver.spec +++ b/rpm/turnserver.spec @@ -1,5 +1,5 @@ Name: turnserver -Version: 4.4.5.5 +Version: 4.5.0.0 Release: 0%{dist} Summary: Coturn TURN Server @@ -289,8 +289,8 @@ fi %{_includedir}/turn/client/TurnMsgLib.h %changelog -* Wed Aug 26 2015 Oleg Moskalenko - - Sync to 4.4.5.5 +* Sun Sep 13 2015 Oleg Moskalenko + - Sync to 4.5.0.0 * Sat Jul 18 2015 Oleg Moskalenko - Sync to 4.4.5.4 * Sat Jun 20 2015 Oleg Moskalenko diff --git a/src/apps/common/apputils.h b/src/apps/common/apputils.h index 6ff61bfd..a0f0ac5c 100644 --- a/src/apps/common/apputils.h +++ b/src/apps/common/apputils.h @@ -142,6 +142,7 @@ struct _oauth_key_data_raw { u64bits timestamp; u32bits lifetime; char as_rs_alg[OAUTH_ALG_SIZE+1]; + char realm[STUN_MAX_REALM_SIZE+1]; }; typedef struct _oauth_key_data_raw oauth_key_data_raw; diff --git a/src/apps/relay/dbdrivers/dbd_mongo.c b/src/apps/relay/dbdrivers/dbd_mongo.c index e06127e8..95ba28dd 100644 --- a/src/apps/relay/dbdrivers/dbd_mongo.c +++ b/src/apps/relay/dbdrivers/dbd_mongo.c @@ -255,6 +255,7 @@ static int mongo_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { BSON_APPEND_INT32(&fields, "lifetime", 1); BSON_APPEND_INT32(&fields, "timestamp", 1); BSON_APPEND_INT32(&fields, "as_rs_alg", 1); + BSON_APPEND_INT32(&fields, "realm", 1); BSON_APPEND_INT32(&fields, "ikm_key", 1); mongoc_cursor_t * cursor; @@ -277,6 +278,9 @@ static int mongo_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_alg") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(key->as_rs_alg,bson_iter_utf8(&iter, &length)); } + if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { + STRCPY(key->realm,bson_iter_utf8(&iter, &length)); + } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ikm_key") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(key->ikm_key,bson_iter_utf8(&iter, &length)); } @@ -341,6 +345,7 @@ static int mongo_set_oauth_key(oauth_key_data_raw *key) { bson_init(&doc); BSON_APPEND_UTF8(&doc, "kid", (const char *)key->kid); BSON_APPEND_UTF8(&doc, "as_rs_alg", (const char *)key->as_rs_alg); + BSON_APPEND_UTF8(&doc, "realm", (const char *)key->realm); BSON_APPEND_UTF8(&doc, "ikm_key", (const char *)key->ikm_key); BSON_APPEND_INT64(&doc, "timestamp", (int64_t)key->timestamp); BSON_APPEND_INT32(&doc, "lifetime", (int32_t)key->lifetime); @@ -477,7 +482,7 @@ static int mongo_list_users(u08bits *realm, secrets_list_t *users, secrets_list_ return ret; } -static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts) { +static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) { const char * collection_name = "oauth_key"; mongoc_collection_t * collection = mongo_get_collection(collection_name); @@ -501,6 +506,7 @@ static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre BSON_APPEND_INT32(&fields, "lifetime", 1); BSON_APPEND_INT32(&fields, "timestamp", 1); BSON_APPEND_INT32(&fields, "as_rs_alg", 1); + BSON_APPEND_INT32(&fields, "realm", 1); BSON_APPEND_INT32(&fields, "ikm_key", 1); mongoc_cursor_t * cursor; @@ -525,6 +531,9 @@ static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "as_rs_alg") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(key->as_rs_alg,bson_iter_utf8(&iter, &length)); } + if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { + STRCPY(key->realm,bson_iter_utf8(&iter, &length)); + } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ikm_key") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(key->ikm_key,bson_iter_utf8(&iter, &length)); } @@ -537,6 +546,7 @@ static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre if(kids) { add_to_secrets_list(kids,key->kid); add_to_secrets_list(teas,key->as_rs_alg); + add_to_secrets_list(realms,key->realm); { char ts[256]; snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp); @@ -548,9 +558,9 @@ static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre add_to_secrets_list(lts,lt); } } else { - printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", + printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s, realm=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, - key->as_rs_alg); + key->as_rs_alg, key->realm); } } mongoc_cursor_destroy(cursor); diff --git a/src/apps/relay/dbdrivers/dbd_mysql.c b/src/apps/relay/dbdrivers/dbd_mysql.c index b8bb30fa..2e5543dd 100644 --- a/src/apps/relay/dbdrivers/dbd_mysql.c +++ b/src/apps/relay/dbdrivers/dbd_mysql.c @@ -345,7 +345,7 @@ static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; /* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */ - snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg from oauth_key where kid='%s'",(const char*)kid); + snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm from oauth_key where kid='%s'",(const char*)kid); MYSQL * myc = get_mydb_connection(); if(myc) { @@ -356,7 +356,7 @@ static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); - } else if(mysql_field_count(myc)!=4) { + } else if(mysql_field_count(myc)!=5) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { MYSQL_ROW row = mysql_fetch_row(mres); @@ -380,6 +380,9 @@ static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { ns_bcopy(row[3],key->as_rs_alg,lengths[3]); key->as_rs_alg[lengths[3]]=0; + ns_bcopy(row[4],key->realm,lengths[4]); + key->realm[lengths[4]]=0; + ret = 0; } } @@ -392,13 +395,13 @@ static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { return ret; } -static int mysql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts) { +static int mysql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) { oauth_key_data_raw key_; oauth_key_data_raw *key=&key_; int ret = -1; char statement[TURN_LONG_STRING_SIZE]; - snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,kid from oauth_key order by kid"); + snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm,kid from oauth_key order by kid"); MYSQL * myc = get_mydb_connection(); if(myc) { @@ -409,7 +412,7 @@ static int mysql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre MYSQL_RES *mres = mysql_store_result(myc); if(!mres) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc)); - } else if(mysql_field_count(myc)!=5) { + } else if(mysql_field_count(myc)!=6) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement); } else { MYSQL_ROW row = mysql_fetch_row(mres); @@ -433,12 +436,16 @@ static int mysql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre ns_bcopy(row[3],key->as_rs_alg,lengths[3]); key->as_rs_alg[lengths[3]]=0; - ns_bcopy(row[4],key->kid,lengths[4]); - key->kid[lengths[4]]=0; + ns_bcopy(row[4],key->realm,lengths[4]); + key->realm[lengths[4]]=0; + + ns_bcopy(row[5],key->kid,lengths[5]); + key->kid[lengths[5]]=0; if(kids) { add_to_secrets_list(kids,key->kid); add_to_secrets_list(teas,key->as_rs_alg); + add_to_secrets_list(realms,key->realm); { char ts[256]; snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp); @@ -450,9 +457,9 @@ static int mysql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre add_to_secrets_list(lts,lt); } } else { - printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", + printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s, realm=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, - key->as_rs_alg); + key->as_rs_alg,key->realm); } } row = mysql_fetch_row(mres); @@ -496,13 +503,13 @@ static int mysql_set_oauth_key(oauth_key_data_raw *key) char statement[TURN_LONG_STRING_SIZE]; MYSQL * myc = get_mydb_connection(); if(myc) { - snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg) values('%s','%s',%llu,%lu,'%s')", + snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('%s','%s',%llu,%lu,'%s','%s')", key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime, - key->as_rs_alg); + key->as_rs_alg,key->realm); int res = mysql_query(myc, statement); if(res) { - snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime, - key->as_rs_alg,key->kid); + snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s', realm='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime, + key->as_rs_alg,key->realm,key->kid); res = mysql_query(myc, statement); if(res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth key information: %s\n",mysql_error(myc)); diff --git a/src/apps/relay/dbdrivers/dbd_pgsql.c b/src/apps/relay/dbdrivers/dbd_pgsql.c index b7b72718..2393e984 100644 --- a/src/apps/relay/dbdrivers/dbd_pgsql.c +++ b/src/apps/relay/dbdrivers/dbd_pgsql.c @@ -160,7 +160,7 @@ static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { char statement[TURN_LONG_STRING_SIZE]; /* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */ - snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg from oauth_key where kid='%s'",(const char*)kid); + snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm from oauth_key where kid='%s'",(const char*)kid); PGconn * pqc = get_pqdb_connection(); if(pqc) { @@ -173,6 +173,7 @@ static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { key->timestamp = (u64bits)strtoll(PQgetvalue(res,0,1),NULL,10); key->lifetime = (u32bits)strtol(PQgetvalue(res,0,2),NULL,10); STRCPY(key->as_rs_alg,PQgetvalue(res,0,3)); + STRCPY(key->realm,PQgetvalue(res,0,4)); STRCPY(key->kid,kid); ret = 0; } @@ -185,7 +186,7 @@ static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { return ret; } -static int pgsql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts) { +static int pgsql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) { oauth_key_data_raw key_; oauth_key_data_raw *key=&key_; @@ -193,7 +194,7 @@ static int pgsql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre int ret = -1; char statement[TURN_LONG_STRING_SIZE]; - snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,kid from oauth_key order by kid"); + snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm,kid from oauth_key order by kid"); PGconn * pqc = get_pqdb_connection(); if(pqc) { @@ -209,11 +210,13 @@ static int pgsql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre key->timestamp = (u64bits)strtoll(PQgetvalue(res,i,1),NULL,10); key->lifetime = (u32bits)strtol(PQgetvalue(res,i,2),NULL,10); STRCPY(key->as_rs_alg,PQgetvalue(res,i,3)); - STRCPY(key->kid,PQgetvalue(res,i,4)); + STRCPY(key->realm,PQgetvalue(res,i,4)); + STRCPY(key->kid,PQgetvalue(res,i,5)); if(kids) { add_to_secrets_list(kids,key->kid); add_to_secrets_list(teas,key->as_rs_alg); + add_to_secrets_list(realms,key->realm); { char ts[256]; snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp); @@ -225,9 +228,9 @@ static int pgsql_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre add_to_secrets_list(lts,lt); } } else { - printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", + printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s, realm=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, - key->as_rs_alg); + key->as_rs_alg,key->realm); } ret = 0; @@ -275,17 +278,17 @@ static int pgsql_set_oauth_key(oauth_key_data_raw *key) { char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if(pqc) { - snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg) values('%s','%s',%llu,%lu,'%s')", + snprintf(statement,sizeof(statement),"insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('%s','%s',%llu,%lu,'%s','%s')", key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime, - key->as_rs_alg); + key->as_rs_alg,key->realm); PGresult *res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { if(res) { PQclear(res); } - snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime, - key->as_rs_alg,key->kid); + snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s', realm='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime, + key->as_rs_alg,key->realm,key->kid); res = PQexec(pqc, statement); if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth_key information: %s\n",PQerrorMessage(pqc)); diff --git a/src/apps/relay/dbdrivers/dbd_redis.c b/src/apps/relay/dbdrivers/dbd_redis.c index 3619f816..3b0f6322 100644 --- a/src/apps/relay/dbdrivers/dbd_redis.c +++ b/src/apps/relay/dbdrivers/dbd_redis.c @@ -477,6 +477,8 @@ static int redis_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { if(kw) { if(!strcmp(kw,"as_rs_alg")) { STRCPY(key->as_rs_alg,val); + } else if(!strcmp(kw,"realm")) { + STRCPY(key->realm,val); } else if(!strcmp(kw,"ikm_key")) { STRCPY(key->ikm_key,val); } else if(!strcmp(kw,"timestamp")) { @@ -512,8 +514,8 @@ static int redis_set_oauth_key(oauth_key_data_raw *key) { redisContext *rc = get_redis_connection(); if(rc) { char statement[TURN_LONG_STRING_SIZE]; - snprintf(statement,sizeof(statement),"hmset turn/oauth/kid/%s ikm_key %s as_rs_alg %s timestamp %llu lifetime %lu", - key->kid,key->ikm_key,key->as_rs_alg,(unsigned long long)key->timestamp,(unsigned long)key->lifetime); + snprintf(statement,sizeof(statement),"hmset turn/oauth/kid/%s ikm_key %s as_rs_alg %s timestamp %llu lifetime %lu realm %s", + key->kid,key->ikm_key,key->as_rs_alg,(unsigned long long)key->timestamp,(unsigned long)key->lifetime,key->realm); turnFreeRedisReply(redisCommand(rc, statement)); turnFreeRedisReply(redisCommand(rc, "save")); ret = 0; @@ -629,7 +631,7 @@ static int redis_list_users(u08bits *realm, secrets_list_t *users, secrets_list_ return ret; } -static int redis_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts) { +static int redis_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) { int ret = -1; redisContext *rc = get_redis_connection(); secrets_list_t keys; @@ -668,6 +670,7 @@ static int redis_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre if(kids) { add_to_secrets_list(kids,key->kid); add_to_secrets_list(teas,key->as_rs_alg); + add_to_secrets_list(realms,key->realm); { char ts[256]; snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp); @@ -679,9 +682,9 @@ static int redis_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secre add_to_secrets_list(lts,lt); } } else { - printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", + printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s, realm=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, - key->as_rs_alg); + key->as_rs_alg,key->realm); } } } diff --git a/src/apps/relay/dbdrivers/dbd_sqlite.c b/src/apps/relay/dbdrivers/dbd_sqlite.c index 26f32b16..28f236d8 100644 --- a/src/apps/relay/dbdrivers/dbd_sqlite.c +++ b/src/apps/relay/dbdrivers/dbd_sqlite.c @@ -157,7 +157,7 @@ static void init_sqlite_database(sqlite3 *sqliteconnection) { "CREATE TABLE denied_peer_ip (realm varchar(127) default '', ip_range varchar(256), primary key (realm,ip_range))", "CREATE TABLE turn_origin_to_realm (origin varchar(127),realm varchar(127),primary key (origin))", "CREATE TABLE turn_realm_option (realm varchar(127) default '', opt varchar(32), value varchar(128), primary key (realm,opt))", - "CREATE TABLE oauth_key (kid varchar(128),ikm_key varchar(256),timestamp bigint default 0,lifetime integer default 0,as_rs_alg varchar(64) default '',primary key (kid))", + "CREATE TABLE oauth_key (kid varchar(128),ikm_key varchar(256),timestamp bigint default 0,lifetime integer default 0,as_rs_alg varchar(64) default '',realm varchar(127) default '',primary key (kid))", "CREATE TABLE admin_user (name varchar(32), realm varchar(127), password varchar(127), primary key (name))", NULL }; @@ -299,7 +299,7 @@ static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { int rc = 0; /* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */ - snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg from oauth_key where kid='%s'",(const char*)kid); + snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm from oauth_key where kid='%s'",(const char*)kid); sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { @@ -315,6 +315,7 @@ static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { key->timestamp = (u64bits)strtoll((const char*)sqlite3_column_text(st, 1),NULL,10); key->lifetime = (u32bits)strtol((const char*)sqlite3_column_text(st, 2),NULL,10); STRCPY(key->as_rs_alg,sqlite3_column_text(st, 3)); + STRCPY(key->realm,sqlite3_column_text(st, 4)); STRCPY(key->kid,kid); ret = 0; } @@ -331,7 +332,7 @@ static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) { return ret; } -static int sqlite_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts) { +static int sqlite_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) { oauth_key_data_raw key_; oauth_key_data_raw *key=&key_; @@ -343,7 +344,7 @@ static int sqlite_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secr char statement[TURN_LONG_STRING_SIZE]; sqlite3_stmt *st = NULL; int rc = 0; - snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,kid from oauth_key order by kid"); + snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,as_rs_alg,realm,kid from oauth_key order by kid"); sqlite3 *sqliteconnection = get_sqlite_connection(); if(sqliteconnection) { @@ -361,11 +362,13 @@ static int sqlite_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secr key->timestamp = (u64bits)strtoll((const char*)sqlite3_column_text(st, 1),NULL,10); key->lifetime = (u32bits)strtol((const char*)sqlite3_column_text(st, 2),NULL,10); STRCPY(key->as_rs_alg,sqlite3_column_text(st, 3)); - STRCPY(key->kid,sqlite3_column_text(st, 4)); + STRCPY(key->realm,sqlite3_column_text(st, 4)); + STRCPY(key->kid,sqlite3_column_text(st, 5)); if(kids) { add_to_secrets_list(kids,key->kid); add_to_secrets_list(teas,key->as_rs_alg); + add_to_secrets_list(realms,key->realm); { char ts[256]; snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp); @@ -449,8 +452,8 @@ static int sqlite_set_oauth_key(oauth_key_data_raw *key) snprintf( statement, sizeof(statement), - "insert or replace into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg) values('%s','%s',%llu,%lu,'%s')", - key->kid, key->ikm_key, (unsigned long long) key->timestamp, (unsigned long) key->lifetime, key->as_rs_alg); + "insert or replace into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('%s','%s',%llu,%lu,'%s','%s')", + key->kid, key->ikm_key, (unsigned long long) key->timestamp, (unsigned long) key->lifetime, key->as_rs_alg, key->realm); sqlite_lock(1); diff --git a/src/apps/relay/dbdrivers/dbdriver.h b/src/apps/relay/dbdrivers/dbdriver.h index d013e165..b6ad537d 100644 --- a/src/apps/relay/dbdrivers/dbdriver.h +++ b/src/apps/relay/dbdrivers/dbdriver.h @@ -68,7 +68,7 @@ typedef struct _turn_dbdriver_t { int (*set_oauth_key)(oauth_key_data_raw *key); int (*get_oauth_key)(const u08bits *kid, oauth_key_data_raw *key); int (*del_oauth_key)(const u08bits *kid); - int (*list_oauth_keys)(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts); + int (*list_oauth_keys)(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms); int (*get_admin_user)(const u08bits *usname, u08bits *realm, password_t pwd); int (*set_admin_user)(const u08bits *usname, const u08bits *realm, const password_t pwd); int (*del_admin_user)(const u08bits *usname); diff --git a/src/apps/relay/turn_admin_server.c b/src/apps/relay/turn_admin_server.c index 4de1d6fe..916b51ee 100644 --- a/src/apps/relay/turn_admin_server.c +++ b/src/apps/relay/turn_admin_server.c @@ -2773,12 +2773,13 @@ static size_t https_print_oauth_keys(struct str_buffer* sb) size_t ret = 0; const turn_dbdriver_t * dbd = get_dbdriver(); if (dbd && dbd->list_oauth_keys) { - secrets_list_t kids,teas,tss,lts; + secrets_list_t kids,teas,tss,lts,realms; init_secrets_list(&kids); init_secrets_list(&teas); init_secrets_list(&tss); init_secrets_list(<s); - dbd->list_oauth_keys(&kids,&teas,&tss,<s); + init_secrets_list(&realms); + dbd->list_oauth_keys(&kids,&teas,&tss,<s,&realms); size_t sz = get_secrets_list_size(&kids); size_t i; @@ -2824,6 +2825,9 @@ static size_t https_print_oauth_keys(struct str_buffer* sb) clean_secrets_list(&kids); clean_secrets_list(&teas); + clean_secrets_list(&tss); + clean_secrets_list(<s); + clean_secrets_list(&realms); } return ret; diff --git a/src/apps/uclient/mainuclient.c b/src/apps/uclient/mainuclient.c index bfef972d..39bc96de 100644 --- a/src/apps/uclient/mainuclient.c +++ b/src/apps/uclient/mainuclient.c @@ -102,9 +102,9 @@ int oauth = 0; oauth_key okey_array[3]; static oauth_key_data_raw okdr_array[3] = { - {"north","MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK",0,0,"A256GCM"}, - {"union","MTIzNDU2Nzg5MDEyMzQ1Ngo=",0,0,"A128GCM"}, - {"oldempire","MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK",0,0,"A256GCM"} + {"north","MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK",0,0,"A256GCM","crinna.org"}, + {"union","MTIzNDU2Nzg5MDEyMzQ1Ngo=",0,0,"A128GCM","north.gov"}, + {"oldempire","MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK",0,0,"A256GCM",""} }; //////////////// local definitions ///////////////// @@ -137,7 +137,7 @@ static char Usage[] = " -G Generate extra requests (create permissions, channel bind).\n" " -B Random disconnect after a few initial packets.\n" " -Z Dual allocation (implies -c).\n" - " -J Use oAuth with default test key kid='north' or 'oldempire'.\n" + " -J Use oAuth with default test keys kid='north', 'union' or 'oldempire'.\n" "Options:\n" " -l Message length (Default: 100 Bytes).\n" " -i Certificate file (for secure connections only, optional).\n" diff --git a/src/ns_turn_defs.h b/src/ns_turn_defs.h index b717591b..5b730b5d 100644 --- a/src/ns_turn_defs.h +++ b/src/ns_turn_defs.h @@ -31,7 +31,7 @@ #ifndef __IOADEFS__ #define __IOADEFS__ -#define TURN_SERVER_VERSION "4.4.5.5" +#define TURN_SERVER_VERSION "4.5.0.0" #define TURN_SERVER_VERSION_NAME "Ardee West" #define TURN_SOFTWARE "Coturn-" TURN_SERVER_VERSION " '" TURN_SERVER_VERSION_NAME "'" diff --git a/turndb/schema.sql b/turndb/schema.sql index 928fac64..1c9eb60e 100644 --- a/turndb/schema.sql +++ b/turndb/schema.sql @@ -43,6 +43,7 @@ CREATE TABLE oauth_key ( timestamp bigint default 0, lifetime integer default 0, as_rs_alg varchar(64) default '', + realm varchar(127), primary key (kid) ); diff --git a/turndb/schema.userdb.redis b/turndb/schema.userdb.redis index e57c0a14..fc59a7b2 100644 --- a/turndb/schema.userdb.redis +++ b/turndb/schema.userdb.redis @@ -46,6 +46,9 @@ and they will be almost immediately "seen" by the turnserver process. "A256GCM", "A128GCM" (see http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-5.1). The default value is "A256GCM". + + realm - optionally, a kid can be assigned to a realm that is different + from the default realm. 5) admin users (over https interface) are maintained as keys of form: "turn/admin_user/ with hash members "password" and, @@ -54,15 +57,21 @@ optionally, "realm". II. Extra realms data in the database We can use more than one realm with the same instance of the TURN server. -This is done through the ORIGIN mechanism - users with different ORIGINS -are placed into different realms. The database includes information about the -relationships between the ORIGIN and realms, and about the extra realms -database numbers. +This is done in two ways: + + 1) through the third-party authentication option. An oAuth kid can be optionally + assigned to a realm. When the user provides kid, and the database record + for that kid contains a non-empty non-default realm, then the user is treated + as belonging to that realm. + 2) the ORIGIN mechanism - users with different ORIGINS + are placed into different realms. The database includes information about the + relationships between the ORIGIN and realms, and about the extra realms + database numbers. The relationship between ORIGIN and realm is set as keys of form: -"turn/origin/" with the realm-names as the value. Many different -ORIGIN keys may have the same realm. If the ORIGIN value is not found in -the database or the ORIGIN field is missed in the initial allocate -request, then the default realm is assumed. + "turn/origin/" with the realm-names as the value. Many different + ORIGIN keys may have the same realm. If the ORIGIN value is not found in + the database or the ORIGIN field is missed in the initial allocate + request, then the default realm is assumed. III) Example of a Redis default user database setup. @@ -82,7 +91,7 @@ This example sets user database for: "total_quota" and "user_quota" (same names as the turnserver configuration options, with the same meanings). * The oAuth data for the key with kid "oldempire" and key value - "12345678901234567890123456789012". + "12345678901234567890123456789012", and default realm. * The admin user 'skarling', realm 'north.gov', with password 'hoodless'; * The global admin user 'bayaz' with password 'magi'; diff --git a/turndb/testmongosetup.sh b/turndb/testmongosetup.sh index e1c5021b..9ca50fc0 100755 --- a/turndb/testmongosetup.sh +++ b/turndb/testmongosetup.sh @@ -28,8 +28,8 @@ db.turn_secret.insert({ realm: 'north.gov', value: 'bloody9' }); db.turn_secret.insert({ realm: 'crinna.org', value: 'north' }); db.turn_secret.insert({ realm: 'crinna.org', value: 'library' }); -db.admin_user.insert({ name: 'skarling', realm: 'north.gov', password: '$5$6fc35c3b0c7d4633$27fca7574f9b79d0cb93ae03e45379470cbbdfcacdd6401f97ebc620f31f54f2' }); -db.admin_user.insert({ name: 'bayaz', realm: '', password: '$5$e018513e9de69e73$5cbdd2e29e04ca46aeb022268a7460d3a3468de193dcb2b95f064901769f455f' }); +db.admin_user.insert({ name: 'skarling', realm: 'north.gov', password: '\$5\$6fc35c3b0c7d4633\$27fca7574f9b79d0cb93ae03e45379470cbbdfcacdd6401f97ebc620f31f54f2' }); +db.admin_user.insert({ name: 'bayaz', realm: '', password: '\$5\$e018513e9de69e73\$5cbdd2e29e04ca46aeb022268a7460d3a3468de193dcb2b95f064901769f455f' }); db.realm.insert({ realm: 'north.gov', @@ -56,10 +56,12 @@ db.realm.insert({ db.oauth_key.insert({ kid: 'north', ikm_key: 'MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK', - as_rs_alg: 'A256GCM'}); + as_rs_alg: 'A256GCM', + realm: 'crinna.org'}); db.oauth_key.insert({ kid: 'union', ikm_key: 'MTIzNDU2Nzg5MDEyMzQ1Ngo=', - as_rs_alg: 'A128GCM'}); + as_rs_alg: 'A128GCM', + realm: 'north.gov'}); db.oauth_key.insert({ kid: 'oldempire', ikm_key: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK', as_rs_alg: 'A256GCM'}); diff --git a/turndb/testredisdbsetup.sh b/turndb/testredisdbsetup.sh index 9c7196b7..6fa4d118 100755 --- a/turndb/testredisdbsetup.sh +++ b/turndb/testredisdbsetup.sh @@ -38,8 +38,8 @@ sadd turn/realm/crinna.org/allowed-peer-ip "172.17.13.202" sadd turn/realm/north.gov/denied-peer-ip "172.17.13.133-172.17.14.56" "172.17.17.133-172.17.19.56" "123::45" sadd turn/realm/crinna.org/denied-peer-ip "123::77" -hmset turn/oauth/kid/north ikm_key 'MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK' as_rs_alg 'A256GCM' -hmset turn/oauth/kid/union ikm_key 'MTIzNDU2Nzg5MDEyMzQ1Ngo=' as_rs_alg 'A128GCM' +hmset turn/oauth/kid/north ikm_key 'MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK' as_rs_alg 'A256GCM' realm 'crinna.org' +hmset turn/oauth/kid/union ikm_key 'MTIzNDU2Nzg5MDEyMzQ1Ngo=' as_rs_alg 'A128GCM' realm 'north.gov' hmset turn/oauth/kid/oldempire ikm_key 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK' as_rs_alg 'A256GCM' hmset turn/admin_user/skarling realm 'north.gov' password '\$5\$6fc35c3b0c7d4633\$27fca7574f9b79d0cb93ae03e45379470cbbdfcacdd6401f97ebc620f31f54f2' diff --git a/turndb/testsqldbsetup.sql b/turndb/testsqldbsetup.sql index ea7e1132..c9330fe6 100644 --- a/turndb/testsqldbsetup.sql +++ b/turndb/testsqldbsetup.sql @@ -31,6 +31,6 @@ insert into denied_peer_ip (ip_range) values('123::45'); insert into denied_peer_ip (realm,ip_range) values('north.gov','172.17.17.133-172.17.19.56'); insert into denied_peer_ip (realm,ip_range) values('crinna.org','123::77'); -insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg) values('north','MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK',0,0,'A256GCM'); -insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg) values('union','MTIzNDU2Nzg5MDEyMzQ1Ngo=',0,0,'A128GCM'); -insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg) values('oldempire','MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK',0,0,'A256GCM'); +insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('north','MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK',0,0,'A256GCM','crinna.org'); +insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('union','MTIzNDU2Nzg5MDEyMzQ1Ngo=',0,0,'A128GCM','north.gov'); +insert into oauth_key (kid,ikm_key,timestamp,lifetime,as_rs_alg,realm) values('oldempire','MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK',0,0,'A256GCM','');