Browse Source

working on multi-tenant server based upon oauth

Oleg Moskalenko 10 years ago
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.
 	- SIGHUP handler fixed.
 	
 	
 7/18/2015 Oleg Moskalenko <[email protected]>
 7/18/2015 Oleg Moskalenko <[email protected]>

+ 3 - 0
INSTALL

@@ -744,6 +744,7 @@ CREATE TABLE oauth_key (
 	timestamp bigint default 0,
 	timestamp bigint default 0,
 	lifetime integer default 0,
 	lifetime integer default 0,
 	as_rs_alg varchar(64) default '',
 	as_rs_alg varchar(64) default '',
+	realm varchar(127) default '',
 	primary key (kid)
 	primary key (kid)
 ); 
 ); 
 
 
@@ -763,6 +764,8 @@ The oauth_key table fields meanings are:
 		"A256GCM", "A128GCM" (see 
 		"A256GCM", "A128GCM" (see 
 		http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-5.1).
 		http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-5.1).
 		The default value is "A256GCM";
 		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.
 # Https access admin users.
 # Leave this table empty if you do not want 
 # 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).
 53) SHA384 and SHA512 support added (experimental).
 
 
 54) native SCTP experimental support.
 54) native SCTP experimental support.
+
+55) Multi-tenant implementation based upon third-party authorization
+(oAuth).
  
  
 Things to be implemented in future (the development roadmap) 
 Things to be implemented in future (the development roadmap) 
 are described in the TODO file.
 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
 .\" Text automatically generated by txt2man
-.TH TURN 1 "01 September 2015" "" ""
+.TH TURN 1 "13 September 2015" "" ""
 .SH GENERAL INFORMATION
 .SH GENERAL INFORMATION
 
 
 \fIturnadmin\fP is a TURN administration tool. This tool can be used to manage 
 \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
 .\" Text automatically generated by txt2man
-.TH TURN 1 "01 September 2015" "" ""
+.TH TURN 1 "13 September 2015" "" ""
 .SH GENERAL INFORMATION
 .SH GENERAL INFORMATION
 
 
 The \fBTURN Server\fP project contains the source code of a TURN server and TURN client 
 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
 .\" Text automatically generated by txt2man
-.TH TURN 1 "01 September 2015" "" ""
+.TH TURN 1 "13 September 2015" "" ""
 .SH GENERAL INFORMATION
 .SH GENERAL INFORMATION
 
 
 A set of turnutils_* programs provides some utility functionality to be used
 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.
 # Common settings script.
 
 
-TURNVERSION=4.4.5.5
+TURNVERSION=4.5.0.0
 BUILDDIR=~/rpmbuild
 BUILDDIR=~/rpmbuild
 ARCH=`uname -p`
 ARCH=`uname -p`
 TURNSERVER_GIT_URL=https://github.com/coturn/coturn.git
 TURNSERVER_GIT_URL=https://github.com/coturn/coturn.git

+ 3 - 3
rpm/turnserver.spec

@@ -1,5 +1,5 @@
 Name:		turnserver
 Name:		turnserver
-Version:	4.4.5.5
+Version:	4.5.0.0
 Release:	0%{dist}
 Release:	0%{dist}
 Summary:	Coturn TURN Server
 Summary:	Coturn TURN Server
 
 
@@ -289,8 +289,8 @@ fi
 %{_includedir}/turn/client/TurnMsgLib.h
 %{_includedir}/turn/client/TurnMsgLib.h
 
 
 %changelog
 %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]>
 * Sat Jul 18 2015 Oleg Moskalenko <[email protected]>
   - Sync to 4.4.5.4
   - Sync to 4.4.5.4
 * Sat Jun 20 2015 Oleg Moskalenko <[email protected]>
 * 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;
 	u64bits timestamp;
 	u32bits lifetime;
 	u32bits lifetime;
 	char as_rs_alg[OAUTH_ALG_SIZE+1];
 	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;
 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, "lifetime", 1);
 	BSON_APPEND_INT32(&fields, "timestamp", 1);
 	BSON_APPEND_INT32(&fields, "timestamp", 1);
 	BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
 	BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
+	BSON_APPEND_INT32(&fields, "realm", 1);
 	BSON_APPEND_INT32(&fields, "ikm_key", 1);
 	BSON_APPEND_INT32(&fields, "ikm_key", 1);
 
 
 	mongoc_cursor_t * cursor;
 	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)) {
 			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));
 				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)) {
 			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));
 				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_init(&doc);
   BSON_APPEND_UTF8(&doc, "kid", (const char *)key->kid);
   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, "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_UTF8(&doc, "ikm_key", (const char *)key->ikm_key);
   BSON_APPEND_INT64(&doc, "timestamp", (int64_t)key->timestamp);
   BSON_APPEND_INT64(&doc, "timestamp", (int64_t)key->timestamp);
   BSON_APPEND_INT32(&doc, "lifetime", (int32_t)key->lifetime);
   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;
   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";
   const char * collection_name = "oauth_key";
   mongoc_collection_t * collection = mongo_get_collection(collection_name);
   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, "lifetime", 1);
   BSON_APPEND_INT32(&fields, "timestamp", 1);
   BSON_APPEND_INT32(&fields, "timestamp", 1);
   BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
   BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
+  BSON_APPEND_INT32(&fields, "realm", 1);
   BSON_APPEND_INT32(&fields, "ikm_key", 1);
   BSON_APPEND_INT32(&fields, "ikm_key", 1);
 
 
   mongoc_cursor_t * cursor;
   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)) {
     	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));
     	    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)) {
     	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));
     		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) {
     	if(kids) {
     		add_to_secrets_list(kids,key->kid);
     		add_to_secrets_list(kids,key->kid);
     		add_to_secrets_list(teas,key->as_rs_alg);
     		add_to_secrets_list(teas,key->as_rs_alg);
+    		add_to_secrets_list(realms,key->realm);
 			{
 			{
 				char ts[256];
 				char ts[256];
 				snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
 				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);
 				add_to_secrets_list(lts,lt);
 			}
 			}
     	} else {
     	} 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->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);
     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;
 	int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	char statement[TURN_LONG_STRING_SIZE];
 	/* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */
 	/* 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();
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
 	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);
 			MYSQL_RES *mres = mysql_store_result(myc);
 			if(!mres) {
 			if(!mres) {
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
 				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);
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
 			} else {
 			} else {
 				MYSQL_ROW row = mysql_fetch_row(mres);
 				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]);
 						ns_bcopy(row[3],key->as_rs_alg,lengths[3]);
 						key->as_rs_alg[lengths[3]]=0;
 						key->as_rs_alg[lengths[3]]=0;
 
 
+						ns_bcopy(row[4],key->realm,lengths[4]);
+						key->realm[lengths[4]]=0;
+
 						ret = 0;
 						ret = 0;
 					}
 					}
 				}
 				}
@@ -392,13 +395,13 @@ static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 	return ret;
 	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_;
 	oauth_key_data_raw *key=&key_;
 	oauth_key_data_raw *key=&key_;
 	int ret = -1;
 	int ret = -1;
 	char statement[TURN_LONG_STRING_SIZE];
 	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();
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
 	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);
 			MYSQL_RES *mres = mysql_store_result(myc);
 			if(!mres) {
 			if(!mres) {
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
 				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);
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
 			} else {
 			} else {
 				MYSQL_ROW row = mysql_fetch_row(mres);
 				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]);
 						ns_bcopy(row[3],key->as_rs_alg,lengths[3]);
 						key->as_rs_alg[lengths[3]]=0;
 						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) {
 						if(kids) {
 							add_to_secrets_list(kids,key->kid);
 							add_to_secrets_list(kids,key->kid);
 							add_to_secrets_list(teas,key->as_rs_alg);
 							add_to_secrets_list(teas,key->as_rs_alg);
+							add_to_secrets_list(realms,key->realm);
 							{
 							{
 								char ts[256];
 								char ts[256];
 								snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
 								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);
 								add_to_secrets_list(lts,lt);
 							}
 							}
 						} else {
 						} 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->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);
 					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];
 	char statement[TURN_LONG_STRING_SIZE];
 	MYSQL * myc = get_mydb_connection();
 	MYSQL * myc = get_mydb_connection();
 	if(myc) {
 	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->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);
 		int res = mysql_query(myc, statement);
 		if(res) {
 		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);
 			res = mysql_query(myc, statement);
 			if(res) {
 			if(res) {
 				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth key information: %s\n",mysql_error(myc));
 				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];
 	char statement[TURN_LONG_STRING_SIZE];
 	/* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */
 	/* 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();
 	PGconn * pqc = get_pqdb_connection();
 	if(pqc) {
 	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->timestamp = (u64bits)strtoll(PQgetvalue(res,0,1),NULL,10);
 			key->lifetime = (u32bits)strtol(PQgetvalue(res,0,2),NULL,10);
 			key->lifetime = (u32bits)strtol(PQgetvalue(res,0,2),NULL,10);
 			STRCPY(key->as_rs_alg,PQgetvalue(res,0,3));
 			STRCPY(key->as_rs_alg,PQgetvalue(res,0,3));
+			STRCPY(key->realm,PQgetvalue(res,0,4));
 			STRCPY(key->kid,kid);
 			STRCPY(key->kid,kid);
 			ret = 0;
 			ret = 0;
 		}
 		}
@@ -185,7 +186,7 @@ static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 	return ret;
 	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_;
 	oauth_key_data_raw *key=&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;
 	int ret = -1;
 
 
 	char statement[TURN_LONG_STRING_SIZE];
 	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();
 	PGconn * pqc = get_pqdb_connection();
 	if(pqc) {
 	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->timestamp = (u64bits)strtoll(PQgetvalue(res,i,1),NULL,10);
 				key->lifetime = (u32bits)strtol(PQgetvalue(res,i,2),NULL,10);
 				key->lifetime = (u32bits)strtol(PQgetvalue(res,i,2),NULL,10);
 				STRCPY(key->as_rs_alg,PQgetvalue(res,i,3));
 				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) {
 				if(kids) {
 					add_to_secrets_list(kids,key->kid);
 					add_to_secrets_list(kids,key->kid);
 					add_to_secrets_list(teas,key->as_rs_alg);
 					add_to_secrets_list(teas,key->as_rs_alg);
+					add_to_secrets_list(realms,key->realm);
 					{
 					{
 						char ts[256];
 						char ts[256];
 						snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
 						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);
 						add_to_secrets_list(lts,lt);
 					}
 					}
 				} else {
 				} 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->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;
 				ret = 0;
@@ -275,17 +278,17 @@ static int pgsql_set_oauth_key(oauth_key_data_raw *key) {
   char statement[TURN_LONG_STRING_SIZE];
   char statement[TURN_LONG_STRING_SIZE];
   PGconn *pqc = get_pqdb_connection();
   PGconn *pqc = get_pqdb_connection();
   if(pqc) {
   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->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);
 	  PGresult *res = PQexec(pqc, statement);
 	  if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
 	  if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
 		  if(res) {
 		  if(res) {
 			PQclear(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);
 		  res = PQexec(pqc, statement);
 		  if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
 		  if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
 			  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth_key information: %s\n",PQerrorMessage(pqc));
 			  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(kw) {
 					if(!strcmp(kw,"as_rs_alg")) {
 					if(!strcmp(kw,"as_rs_alg")) {
 						STRCPY(key->as_rs_alg,val);
 						STRCPY(key->as_rs_alg,val);
+					} else if(!strcmp(kw,"realm")) {
+						STRCPY(key->realm,val);
 					} else if(!strcmp(kw,"ikm_key")) {
 					} else if(!strcmp(kw,"ikm_key")) {
 						STRCPY(key->ikm_key,val);
 						STRCPY(key->ikm_key,val);
 					} else if(!strcmp(kw,"timestamp")) {
 					} 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();
   redisContext *rc = get_redis_connection();
   if(rc) {
   if(rc) {
 	char statement[TURN_LONG_STRING_SIZE];
 	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, statement));
 	turnFreeRedisReply(redisCommand(rc, "save"));
 	turnFreeRedisReply(redisCommand(rc, "save"));
     ret = 0;
     ret = 0;
@@ -629,7 +631,7 @@ static int redis_list_users(u08bits *realm, secrets_list_t *users, secrets_list_
 	return ret;
 	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;
   int ret = -1;
   redisContext *rc = get_redis_connection();
   redisContext *rc = get_redis_connection();
   secrets_list_t keys;
   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) {
 		if(kids) {
 			add_to_secrets_list(kids,key->kid);
 			add_to_secrets_list(kids,key->kid);
 			add_to_secrets_list(teas,key->as_rs_alg);
 			add_to_secrets_list(teas,key->as_rs_alg);
+			add_to_secrets_list(realms,key->realm);
 			{
 			{
 				char ts[256];
 				char ts[256];
 				snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
 				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);
 				add_to_secrets_list(lts,lt);
 			}
 			}
 		} else {
 		} 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->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 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_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 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))",
 		"CREATE TABLE admin_user (name varchar(32), realm varchar(127), password varchar(127), primary key (name))",
 		NULL
 		NULL
 	};
 	};
@@ -299,7 +299,7 @@ static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 	int rc = 0;
 	int rc = 0;
 
 
 	/* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */
 	/* 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();
 	sqlite3 *sqliteconnection = get_sqlite_connection();
 	if(sqliteconnection) {
 	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->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);
 				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->as_rs_alg,sqlite3_column_text(st, 3));
+				STRCPY(key->realm,sqlite3_column_text(st, 4));
 				STRCPY(key->kid,kid);
 				STRCPY(key->kid,kid);
 				ret = 0;
 				ret = 0;
 			}
 			}
@@ -331,7 +332,7 @@ static int sqlite_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
 	return ret;
 	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_;
 	oauth_key_data_raw *key=&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];
 	char statement[TURN_LONG_STRING_SIZE];
 	sqlite3_stmt *st = NULL;
 	sqlite3_stmt *st = NULL;
 	int rc = 0;
 	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();
 	sqlite3 *sqliteconnection = get_sqlite_connection();
 	if(sqliteconnection) {
 	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->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);
 					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->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) {
 					if(kids) {
 						add_to_secrets_list(kids,key->kid);
 						add_to_secrets_list(kids,key->kid);
 						add_to_secrets_list(teas,key->as_rs_alg);
 						add_to_secrets_list(teas,key->as_rs_alg);
+						add_to_secrets_list(realms,key->realm);
 						{
 						{
 							char ts[256];
 							char ts[256];
 							snprintf(ts,sizeof(ts)-1,"%llu",(unsigned long long)key->timestamp);
 							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(
 		snprintf(
 						statement,
 						statement,
 						sizeof(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);
 		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 (*set_oauth_key)(oauth_key_data_raw *key);
   int (*get_oauth_key)(const u08bits *kid, 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 (*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 (*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 (*set_admin_user)(const u08bits *usname, const u08bits *realm, const password_t pwd);
   int (*del_admin_user)(const u08bits *usname);
   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;
 	size_t ret = 0;
 	const turn_dbdriver_t * dbd = get_dbdriver();
 	const turn_dbdriver_t * dbd = get_dbdriver();
 	if (dbd && dbd->list_oauth_keys) {
 	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(&kids);
 		init_secrets_list(&teas);
 		init_secrets_list(&teas);
 		init_secrets_list(&tss);
 		init_secrets_list(&tss);
 		init_secrets_list(&lts);
 		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 sz = get_secrets_list_size(&kids);
 		size_t i;
 		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(&kids);
 		clean_secrets_list(&teas);
 		clean_secrets_list(&teas);
+		clean_secrets_list(&tss);
+		clean_secrets_list(&lts);
+		clean_secrets_list(&realms);
 	}
 	}
 
 
 	return ret;
 	return ret;

+ 4 - 4
src/apps/uclient/mainuclient.c

@@ -102,9 +102,9 @@ int oauth = 0;
 oauth_key okey_array[3];
 oauth_key okey_array[3];
 
 
 static oauth_key_data_raw okdr_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 /////////////////
 //////////////// local definitions /////////////////
@@ -137,7 +137,7 @@ static char Usage[] =
   "	-G	Generate extra requests (create permissions, channel bind).\n"
   "	-G	Generate extra requests (create permissions, channel bind).\n"
   "	-B	Random disconnect after a few initial packets.\n"
   "	-B	Random disconnect after a few initial packets.\n"
   "	-Z	Dual allocation (implies -c).\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"
   "Options:\n"
   "	-l	Message length (Default: 100 Bytes).\n"
   "	-l	Message length (Default: 100 Bytes).\n"
   "	-i	Certificate file (for secure connections only, optional).\n"
   "	-i	Certificate file (for secure connections only, optional).\n"

+ 1 - 1
src/ns_turn_defs.h

@@ -31,7 +31,7 @@
 #ifndef __IOADEFS__
 #ifndef __IOADEFS__
 #define __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_SERVER_VERSION_NAME "Ardee West"
 #define TURN_SOFTWARE "Coturn-" TURN_SERVER_VERSION " '" TURN_SERVER_VERSION_NAME "'"
 #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,
 	timestamp bigint default 0,
 	lifetime integer default 0,
 	lifetime integer default 0,
 	as_rs_alg varchar(64) default '',
 	as_rs_alg varchar(64) default '',
+	realm varchar(127),
 	primary key (kid)
 	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 
 		"A256GCM", "A128GCM" (see 
 		http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-5.1).
 		http://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40#section-5.1).
 		The default value is "A256GCM".
 		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:
 5) admin users (over https interface) are maintained as keys of form:
 "turn/admin_user/<username> with hash members "password" and,
 "turn/admin_user/<username> with hash members "password" and,
@@ -54,15 +57,21 @@ optionally, "realm".
 II. Extra realms data in the database
 II. Extra realms data in the database
 
 
 We can use more than one realm with the same instance of the TURN server.
 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:
 	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.
 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 
   	"total_quota" and "user_quota" (same names as the turnserver 
   	configuration options, with the same meanings).
   	configuration options, with the same meanings).
   * The oAuth data for the key with kid "oldempire" and key value
   * 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 admin user 'skarling', realm 'north.gov', with password 'hoodless';
   * The global admin user 'bayaz' with password 'magi';  
   * 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: 'north' });
 db.turn_secret.insert({ realm: 'crinna.org', value: 'library' });
 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({
 db.realm.insert({
   realm: 'north.gov',
   realm: 'north.gov',
@@ -56,10 +56,12 @@ db.realm.insert({
 
 
 db.oauth_key.insert({ kid: 'north', 
 db.oauth_key.insert({ kid: 'north', 
 					ikm_key: 'MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK', 
 					ikm_key: 'MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDEK', 
-					as_rs_alg: 'A256GCM'});
+					as_rs_alg: 'A256GCM',
+					realm: 'crinna.org'});
 db.oauth_key.insert({ kid: 'union', 
 db.oauth_key.insert({ kid: 'union', 
 					ikm_key: 'MTIzNDU2Nzg5MDEyMzQ1Ngo=', 
 					ikm_key: 'MTIzNDU2Nzg5MDEyMzQ1Ngo=', 
-					as_rs_alg: 'A128GCM'});
+					as_rs_alg: 'A128GCM',
+					realm: 'north.gov'});
 db.oauth_key.insert({ kid: 'oldempire', 
 db.oauth_key.insert({ kid: 'oldempire', 
 					ikm_key: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK', 
 					ikm_key: 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK', 
 					as_rs_alg: 'A256GCM'});
 					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/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"
 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/oauth/kid/oldempire ikm_key 'MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIK' as_rs_alg 'A256GCM'
 
 
 hmset turn/admin_user/skarling realm 'north.gov' password '\$5\$6fc35c3b0c7d4633\$27fca7574f9b79d0cb93ae03e45379470cbbdfcacdd6401f97ebc620f31f54f2'
 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('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 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','');