1
0
Эх сурвалжийг харах

working on multi-tenant server based upon oauth

Oleg Moskalenko 10 жил өмнө
parent
commit
dbc9dee42b

+ 4 - 3
ChangeLog

@@ -1,6 +1,7 @@
-8/26/2015 Oleg Moskalenko <[email protected]>
-Version 4.4.5.5 'Ardee West':
-	- STUN attributes conflict resolution.
+9/13/2015 Oleg Moskalenko <[email protected]>
+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 <[email protected]>

+ 3 - 0
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 

+ 3 - 0
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.

BIN
examples/var/db/turndb


+ 1 - 1
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 

+ 1 - 1
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 

+ 1 - 1
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

+ 1 - 1
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

+ 3 - 3
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 <[email protected]>
-  - Sync to 4.4.5.5
+* Sun Sep 13 2015 Oleg Moskalenko <[email protected]>
+  - Sync to 4.5.0.0
 * Sat Jul 18 2015 Oleg Moskalenko <[email protected]>
   - Sync to 4.4.5.4
 * Sat Jun 20 2015 Oleg Moskalenko <[email protected]>

+ 1 - 0
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;

+ 13 - 3
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);

+ 20 - 13
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));

+ 13 - 10
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));

+ 8 - 5
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);
 		}
 	}
   }

+ 10 - 7
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);
 

+ 1 - 1
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);

+ 6 - 2
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(&lts);
-		dbd->list_oauth_keys(&kids,&teas,&tss,&lts);
+		init_secrets_list(&realms);
+		dbd->list_oauth_keys(&kids,&teas,&tss,&lts,&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(&lts);
+		clean_secrets_list(&realms);
 	}
 
 	return ret;

+ 4 - 4
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"

+ 1 - 1
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 "'"
 

+ 1 - 0
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)
 );
 

+ 18 - 9
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/<username> 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/<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/<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';  
   

+ 6 - 4
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'});

+ 2 - 2
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'

+ 3 - 3
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','');