mom040267 11 лет назад
Родитель
Сommit
268de3e3d1

+ 15 - 0
INSTALL

@@ -769,6 +769,21 @@ used for the HMAC key generation.
 The key must be 32 characters (HEX representation of 16 bytes) for SHA1,
 or 64 characters (HEX representation of 32 bytes) for SHA256.
 
+# oAuth key storage table.
+#
+CREATE TABLE oauth_key (
+	kid varchar(128),
+	ikm_key varchar(256) default '',
+	timestamp bigint default 0,
+	lifetime integer default 0,
+	hkdf_hash_func varchar(64) default '',
+	as_rs_alg varchar(64) default '',
+	as_rs_key varchar(256) default '',
+	auth_alg varchar(64) default '',
+	auth_key varchar(256) default '',
+	primary key (kid)
+);
+
 You can use turnadmin program to manage the database - you can either use 
 turnadmin to add/modify/delete users, or you can use turnadmin to produce 
 the hmac keys and modify the database with your favorite tools.

+ 218 - 2
src/apps/relay/dbdrivers/dbd_mongo.c

@@ -234,6 +234,79 @@ static int mongo_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
   bson_destroy(&fields);
   return ret;
 }
+
+static int mongo_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
+
+	mongoc_collection_t * collection = mongo_get_collection("oauth_key");
+
+	if (!collection)
+		return -1;
+
+	bson_t query;
+	bson_init(&query);
+	BSON_APPEND_UTF8(&query, "kid", (const char *)key->kid);
+
+	bson_t fields;
+	bson_init(&fields);
+	BSON_APPEND_INT32(&fields, "lifetime", 1);
+	BSON_APPEND_INT32(&fields, "timestamp", 1);
+	BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
+	BSON_APPEND_INT32(&fields, "as_rs_key", 1);
+	BSON_APPEND_INT32(&fields, "auth_alg", 1);
+	BSON_APPEND_INT32(&fields, "auth_key", 1);
+	BSON_APPEND_INT32(&fields, "hkdf_hash_func", 1);
+	BSON_APPEND_INT32(&fields, "ikm_key", 1);
+
+	mongoc_cursor_t * cursor;
+	cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0,
+			&query, &fields, NULL);
+
+	int ret = -1;
+
+	ns_bzero(key,sizeof(oauth_key_data_raw));
+	STRCPY(key->kid,kid);
+
+	if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR,
+				"Error querying MongoDB collection 'oauth_key'\n");
+	} else {
+		const bson_t * item;
+		uint32_t length;
+		bson_iter_t iter;
+		if (mongoc_cursor_next(cursor, &item)) {
+			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, "as_rs_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->as_rs_key,bson_iter_utf8(&iter, &length));
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->auth_alg,bson_iter_utf8(&iter, &length));
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->auth_key,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));
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "hkdf_hash_func") && BSON_ITER_HOLDS_UTF8(&iter)) {
+				STRCPY(key->hkdf_hash_func,bson_iter_utf8(&iter, &length));
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) {
+				key->timestamp = (u64bits)bson_iter_int64(&iter);
+			}
+			if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) {
+				key->lifetime = (u32bits)bson_iter_int32(&iter);
+			}
+			ret = 0;
+		}
+		mongoc_cursor_destroy(cursor);
+	}
+	mongoc_collection_destroy(collection);
+	bson_destroy(&query);
+	bson_destroy(&fields);
+	return ret;
+}
   
 static int mongo_get_user_pwd(u08bits *usname, st_password_t pwd) {
   mongoc_collection_t * collection = mongo_get_collection("turnusers_st"); 
@@ -302,7 +375,43 @@ static int mongo_set_user_key(u08bits *usname, u08bits *realm, const char *key)
   int ret = -1;
   
   if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
-    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information\n");
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user key information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&doc);
+  bson_destroy(&query);
+  return ret;
+}
+
+static int mongo_set_oauth_key(oauth_key_data_raw *key) {
+
+  mongoc_collection_t * collection = mongo_get_collection("oauth_key");
+
+  if(!collection)
+    return -1;
+
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "kid", (const char *)key->kid);
+
+  bson_t doc;
+  bson_init(&doc);
+  BSON_APPEND_UTF8(&query, "kid", (const char *)key->kid);
+  BSON_APPEND_UTF8(&doc, "as_rs_alg", (const char *)key->as_rs_alg);
+  BSON_APPEND_UTF8(&doc, "as_rs_key", (const char *)key->as_rs_key);
+  BSON_APPEND_UTF8(&doc, "auth_alg", (const char *)key->auth_alg);
+  BSON_APPEND_UTF8(&doc, "auth_key", (const char *)key->auth_key);
+  BSON_APPEND_UTF8(&doc, "hkdf_hash_func", (const char *)key->hkdf_hash_func);
+  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);
+
+  int ret = -1;
+
+  if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth key information\n");
   } else {
     ret = 0;
   }
@@ -364,6 +473,29 @@ static int mongo_del_user(u08bits *usname, int is_st, u08bits *realm) {
   bson_destroy(&query);
   return ret;
 }
+
+static int mongo_del_oauth_key(const u08bits *kid) {
+
+  mongoc_collection_t * collection = mongo_get_collection("oauth_key");
+
+  if(!collection)
+    return -1;
+
+  bson_t query;
+  bson_init(&query);
+  BSON_APPEND_UTF8(&query, "kid", (const char *)kid);
+
+  int ret = -1;
+
+  if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) {
+    TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information\n");
+  } else {
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  return ret;
+}
   
 static int mongo_list_users(int is_st, u08bits *realm) {
   const char * collection_name = is_st ? "turnusers_st" : "turnusers_lt";
@@ -425,6 +557,86 @@ static int mongo_list_users(int is_st, u08bits *realm) {
   bson_destroy(&fields);
   return ret;
 }
+
+static int mongo_list_oauth_keys(void) {
+
+  const char * collection_name = "oauth_key";
+  mongoc_collection_t * collection = mongo_get_collection(collection_name);
+
+  if(!collection)
+    return -1;
+
+  bson_t query, child;
+  bson_init(&query);
+  bson_append_document_begin(&query, "$orderby", -1, &child);
+  bson_append_int32(&child, "kid", -1, 1);
+  bson_append_document_end(&query, &child);
+
+  bson_t fields;
+  bson_init(&fields);
+  BSON_APPEND_INT32(&fields, "kid", 1);
+  BSON_APPEND_INT32(&fields, "lifetime", 1);
+  BSON_APPEND_INT32(&fields, "timestamp", 1);
+  BSON_APPEND_INT32(&fields, "as_rs_alg", 1);
+  BSON_APPEND_INT32(&fields, "as_rs_key", 1);
+  BSON_APPEND_INT32(&fields, "auth_alg", 1);
+  BSON_APPEND_INT32(&fields, "auth_key", 1);
+  BSON_APPEND_INT32(&fields, "hkdf_hash_func", 1);
+  BSON_APPEND_INT32(&fields, "ikm_key", 1);
+
+  mongoc_cursor_t * cursor;
+  cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL);
+
+  int ret = -1;
+
+  if (!cursor) {
+		TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection '%s'\n", collection_name);
+  } else {
+    const bson_t * item;
+	oauth_key_data_raw key_;
+	oauth_key_data_raw *key=&key_;
+    uint32_t length;
+    bson_iter_t iter;
+    while (mongoc_cursor_next(cursor, &item)) {
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "kid") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->kid,bson_iter_utf8(&iter, &length));
+    	}
+    	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, "as_rs_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->as_rs_key,bson_iter_utf8(&iter, &length));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_alg") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->auth_alg,bson_iter_utf8(&iter, &length));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "auth_key") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->auth_key,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));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "hkdf_hash_func") && BSON_ITER_HOLDS_UTF8(&iter)) {
+    		STRCPY(key->hkdf_hash_func,bson_iter_utf8(&iter, &length));
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) {
+    		key->timestamp = (u64bits)bson_iter_int64(&iter);
+    	}
+    	if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) {
+    		key->lifetime = (u32bits)bson_iter_int32(&iter);
+    	}
+    	printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+    		key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+    		key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+    }
+    mongoc_cursor_destroy(cursor);
+    ret = 0;
+  }
+  mongoc_collection_destroy(collection);
+  bson_destroy(&query);
+  bson_destroy(&fields);
+  return ret;
+}
   
 static int mongo_show_secret(u08bits *realm) {
   mongoc_collection_t * collection = mongo_get_collection("turn_secret"); 
@@ -921,7 +1133,11 @@ static turn_dbdriver_t driver = {
   &mongo_list_realm_options,
   &mongo_auth_ping,
   &mongo_get_ip_list,
-  &mongo_reread_realms
+  &mongo_reread_realms,
+  &mongo_set_oauth_key,
+  &mongo_get_oauth_key,
+  &mongo_del_oauth_key,
+  &mongo_list_oauth_keys
 };
 
 turn_dbdriver_t * get_mongo_dbdriver(void) {

+ 176 - 1
src/apps/relay/dbdrivers/dbd_mysql.c

@@ -372,6 +372,140 @@ static int mysql_get_user_pwd(u08bits *usname, st_password_t pwd) {
 	}
   return ret;
 }
+
+static int mysql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
+
+	int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key from oauth_key where kid='%s'",(const char*)kid);
+
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		int res = mysql_query(myc, statement);
+		if(res) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
+		} else {
+			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)!=8) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+			} else {
+				MYSQL_ROW row = mysql_fetch_row(mres);
+				if(row && row[0]) {
+					unsigned long *lengths = mysql_fetch_lengths(mres);
+					if(lengths) {
+						STRCPY((char*)key->kid,kid);
+						ns_bcopy(row[0],key->ikm_key,lengths[0]);
+						key->ikm_key[lengths[0]]=0;
+
+						char stimestamp[128];
+						ns_bcopy(row[1],stimestamp,lengths[1]);
+						stimestamp[lengths[1]]=0;
+						key->timestamp = (u64bits)strtoull(stimestamp,NULL,10);
+
+						char slifetime[128];
+						ns_bcopy(row[2],slifetime,lengths[2]);
+						slifetime[lengths[2]]=0;
+						key->lifetime = (u32bits)strtoul(slifetime,NULL,10);
+
+						ns_bcopy(row[3],key->hkdf_hash_func,lengths[3]);
+						key->hkdf_hash_func[lengths[3]]=0;
+
+						ns_bcopy(row[4],key->as_rs_alg,lengths[4]);
+						key->as_rs_alg[lengths[4]]=0;
+
+						ns_bcopy(row[5],key->as_rs_key,lengths[5]);
+						key->as_rs_key[lengths[5]]=0;
+
+						ns_bcopy(row[6],key->auth_alg,lengths[6]);
+						key->auth_alg[lengths[6]]=0;
+
+						ns_bcopy(row[7],key->auth_key,lengths[7]);
+						key->auth_key[lengths[7]]=0;
+
+						ret = 0;
+					}
+				}
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+	return ret;
+}
+
+static int mysql_list_oauth_keys(void) {
+
+	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,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key,kid from oauth_key order by kid");
+
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		int res = mysql_query(myc, statement);
+		if(res) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MySQL DB information: %s\n",mysql_error(myc));
+		} else {
+			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)!=9) {
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown error retrieving MySQL DB information: %s\n",statement);
+			} else {
+				MYSQL_ROW row = mysql_fetch_row(mres);
+				while(row) {
+					unsigned long *lengths = mysql_fetch_lengths(mres);
+					if(lengths) {
+
+						ns_bcopy(row[0],key->ikm_key,lengths[0]);
+						key->ikm_key[lengths[0]]=0;
+
+						char stimestamp[128];
+						ns_bcopy(row[1],stimestamp,lengths[1]);
+						stimestamp[lengths[1]]=0;
+						key->timestamp = (u64bits)strtoull(stimestamp,NULL,10);
+
+						char slifetime[128];
+						ns_bcopy(row[2],slifetime,lengths[2]);
+						slifetime[lengths[2]]=0;
+						key->lifetime = (u32bits)strtoul(slifetime,NULL,10);
+
+						ns_bcopy(row[3],key->hkdf_hash_func,lengths[3]);
+						key->hkdf_hash_func[lengths[3]]=0;
+						ns_bcopy(row[4],key->as_rs_alg,lengths[4]);
+						key->as_rs_alg[lengths[4]]=0;
+
+						ns_bcopy(row[5],key->as_rs_key,lengths[5]);
+						key->as_rs_key[lengths[5]]=0;
+
+						ns_bcopy(row[6],key->auth_alg,lengths[6]);
+						key->auth_alg[lengths[6]]=0;
+
+						ns_bcopy(row[7],key->auth_key,lengths[7]);
+						key->auth_key[lengths[7]]=0;
+
+						ns_bcopy(row[8],key->kid,lengths[8]);
+						key->kid[lengths[8]]=0;
+
+						printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+								key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+								key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+					}
+					row = mysql_fetch_row(mres);
+				}
+			}
+
+			if(mres)
+				mysql_free_result(mres);
+		}
+	}
+
+	return ret;
+}
   
 static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
   int ret = 1;
@@ -390,6 +524,27 @@ static int mysql_set_user_key(u08bits *usname, u08bits *realm, const char *key)
 	}
   return ret;
 }
+
+static int mysql_set_oauth_key(oauth_key_data_raw *key) {
+  int ret = 1;
+	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,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key) values('%s','%s',%llu,%lu,'%s','%s','%s','%s','%s')",
+					  key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime,
+					  key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key);
+		int res = mysql_query(myc, statement);
+		if(res) {
+			snprintf(statement,sizeof(statement),"update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, hkdf_hash_func = '%s', as_rs_alg='%s',as_rs_key='%s',auth_alg='%s',auth_key='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime,
+							  key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,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));
+			}
+		}
+	}
+  return ret;
+}
   
 static int mysql_set_user_pwd(u08bits *usname, st_password_t pwd) {
   int ret = 1;
@@ -430,6 +585,22 @@ static int mysql_del_user(u08bits *usname, int is_st, u08bits *realm) {
 	}
   return ret;
 }
+
+static int mysql_del_oauth_key(const u08bits *kid) {
+	int ret = 1;
+	char statement[TURN_LONG_STRING_SIZE];
+	MYSQL * myc = get_mydb_connection();
+	if(myc) {
+		snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid);
+		int res = mysql_query(myc, statement);
+		if(res) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth key information: %s\n",mysql_error(myc));
+		} else {
+		  ret = 0;
+		}
+	}
+	return ret;
+}
   
 static int mysql_list_users(int is_st, u08bits *realm) {
   int ret = 1;
@@ -893,7 +1064,11 @@ static turn_dbdriver_t driver = {
   &mysql_list_realm_options,
   &mysql_auth_ping,
   &mysql_get_ip_list,
-  &mysql_reread_realms
+  &mysql_reread_realms,
+  &mysql_set_oauth_key,
+  &mysql_get_oauth_key,
+  &mysql_del_oauth_key,
+  &mysql_list_oauth_keys
 };
 
 turn_dbdriver_t * get_mysql_dbdriver(void) {

+ 138 - 2
src/apps/relay/dbdrivers/dbd_pgsql.c

@@ -174,6 +174,86 @@ static int pgsql_get_user_pwd(u08bits *usname, st_password_t pwd) {
 	}
   return ret;
 }
+
+static int pgsql_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
+
+	int ret = 1;
+
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"select ikm_key,timestamp,lifetime,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key from oauth_key where kid='%s'",(const char*)kid);
+
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		PGresult *res = PQexec(pqc, statement);
+
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK) || (PQntuples(res)!=1)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			STRCPY((char*)key->ikm_key,PQgetvalue(res,0,0));
+			key->timestamp = (u64bits)strtoll(PQgetvalue(res,0,1),NULL,10);
+			key->lifetime = (u32bits)strtol(PQgetvalue(res,0,2),NULL,10);
+			STRCPY((char*)key->hkdf_hash_func,PQgetvalue(res,0,3));
+			STRCPY((char*)key->as_rs_alg,PQgetvalue(res,0,4));
+			STRCPY((char*)key->as_rs_key,PQgetvalue(res,0,5));
+			STRCPY((char*)key->auth_alg,PQgetvalue(res,0,6));
+			STRCPY((char*)key->auth_key,PQgetvalue(res,0,7));
+			STRCPY((char*)key->kid,kid);
+			ret = 0;
+		}
+
+		if(res) {
+			PQclear(res);
+		}
+	}
+
+	return ret;
+}
+
+static int pgsql_list_oauth_keys(void) {
+
+	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,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key,kid from oauth_key order by kid");
+
+	PGconn * pqc = get_pqdb_connection();
+	if(pqc) {
+		PGresult *res = PQexec(pqc, statement);
+
+		if(!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) {
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s\n",PQerrorMessage(pqc));
+		} else {
+			int i = 0;
+			for(i=0;i<PQntuples(res);i++) {
+
+				STRCPY((char*)key->ikm_key,PQgetvalue(res,i,0));
+				key->timestamp = (u64bits)strtoll(PQgetvalue(res,i,1),NULL,10);
+				key->lifetime = (u32bits)strtol(PQgetvalue(res,i,2),NULL,10);
+				STRCPY((char*)key->hkdf_hash_func,PQgetvalue(res,i,3));
+				STRCPY((char*)key->as_rs_alg,PQgetvalue(res,i,4));
+				STRCPY((char*)key->as_rs_key,PQgetvalue(res,i,5));
+				STRCPY((char*)key->auth_alg,PQgetvalue(res,i,6));
+				STRCPY((char*)key->auth_key,PQgetvalue(res,i,7));
+				STRCPY((char*)key->kid,PQgetvalue(res,i,8));
+
+				printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+						key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+						key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+
+				ret = 0;
+			}
+		}
+
+		if(res) {
+			PQclear(res);
+		}
+	}
+
+	return ret;
+}
   
 static int pgsql_set_user_key(u08bits *usname, u08bits *realm, const char *key) {
   int ret = 1;
@@ -201,7 +281,38 @@ static int pgsql_set_user_key(u08bits *usname, u08bits *realm, const char *key)
 	}
   return ret;
 }
-  
+
+static int pgsql_set_oauth_key(oauth_key_data_raw *key) {
+
+  int ret = 1;
+  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,hkdf_hash_func,as_rs_alg,as_rs_key,auth_alg,auth_key) values('%s','%s',%llu,%lu,'%s','%s','%s','%s','%s')",
+			  key->kid,key->ikm_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime,
+			  key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key);
+
+	  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, hkdf_hash_func = '%s', as_rs_alg='%s',as_rs_key='%s',auth_alg='%s',auth_key='%s' where kid='%s'",key->ikm_key,(unsigned long)key->timestamp,(unsigned long)key->lifetime,
+				  key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,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));
+		  } else {
+			  ret = 0;
+		  }
+	  }
+	  if(res) {
+		  PQclear(res);
+	  }
+  }
+  return ret;
+}
+
 static int pgsql_set_user_pwd(u08bits *usname, st_password_t pwd) {
   int ret = 1;
 	char statement[TURN_LONG_STRING_SIZE];
@@ -246,6 +357,27 @@ static int pgsql_del_user(u08bits *usname, int is_st, u08bits *realm) {
 	}
   return ret;
 }
+
+static int pgsql_del_oauth_key(const u08bits *kid) {
+
+  int ret = 1;
+  char statement[TURN_LONG_STRING_SIZE];
+  PGconn *pqc = get_pqdb_connection();
+  if(pqc) {
+	  snprintf(statement,sizeof(statement),"delete from oauth_key where kid = '%s'",(const char*)kid);
+
+	  PGresult *res = PQexec(pqc, statement);
+	  if(!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) {
+		  TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting oauth_key information: %s\n",PQerrorMessage(pqc));
+	  } else {
+		  ret = 0;
+	  }
+	  if(res) {
+		  PQclear(res);
+	  }
+  }
+  return ret;
+}
   
 static int pgsql_list_users(int is_st, u08bits *realm) {
   int ret = 1;
@@ -655,7 +787,11 @@ static turn_dbdriver_t driver = {
   &pgsql_list_realm_options,
   &pgsql_auth_ping,
   &pgsql_get_ip_list,
-  &pgsql_reread_realms
+  &pgsql_reread_realms,
+  &pgsql_set_oauth_key,
+  &pgsql_get_oauth_key,
+  &pgsql_del_oauth_key,
+  &pgsql_list_oauth_keys
 };
 
 turn_dbdriver_t * get_pgsql_dbdriver(void) {

+ 127 - 4
src/apps/relay/dbdrivers/dbd_redis.c

@@ -492,6 +492,53 @@ static int redis_get_user_key(u08bits *usname, u08bits *realm, hmackey_t key) {
 	}
   return ret;
 }
+
+static int redis_get_oauth_key(const u08bits *kid, oauth_key_data_raw *key) {
+  int ret = 1;
+  redisContext * rc = get_redis_connection();
+  if(rc) {
+	char s[TURN_LONG_STRING_SIZE];
+	ns_bzero(key,sizeof(oauth_key_data_raw));
+	STRCPY(key->kid,kid);
+	snprintf(s,sizeof(s),"hgetall turn/oauth/kid/%s", (const char*)kid);
+	redisReply *reply = (redisReply *)redisCommand(rc, s);
+	if(reply) {
+		if (reply->type == REDIS_REPLY_ERROR)
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str);
+		else if (reply->type != REDIS_REPLY_ARRAY) {
+			if (reply->type != REDIS_REPLY_NIL)
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
+		} else {
+			size_t i;
+			for (i = 0; i < (reply->elements)/2; ++i) {
+				char *kw = reply->element[i]->str;
+				if(kw) {
+					if(!strcmp(kw,"as_rs_alg")) {
+						STRCPY(key->as_rs_alg,reply->element[i+1]->str);
+					} else if(!strcmp(kw,"as_rs_key")) {
+						STRCPY(key->as_rs_key,reply->element[i+1]->str);
+					} else if(!strcmp(kw,"auth_key")) {
+						STRCPY(key->auth_key,reply->element[i+1]->str);
+					} else if(!strcmp(kw,"auth_alg")) {
+						STRCPY(key->auth_alg,reply->element[i+1]->str);
+					} else if(!strcmp(kw,"ikm_key")) {
+						STRCPY(key->ikm_key,reply->element[i+1]->str);
+					} else if(!strcmp(kw,"hkdf_hash_func")) {
+						STRCPY(key->hkdf_hash_func,reply->element[i+1]->str);
+					} else if(!strcmp(kw,"timestamp")) {
+						key->timestamp = (u64bits)strtoull(reply->element[i+1]->str,NULL,10);
+					} else if(!strcmp(kw,"lifetime")) {
+						key->lifetime = (u32bits)strtoul(reply->element[i+1]->str,NULL,10);
+					}
+				}
+			}
+		}
+		turnFreeRedisReply(reply);
+		ret = 0;
+	}
+  }
+  return ret;
+}
   
 static int redis_get_user_pwd(u08bits *usname, st_password_t pwd) {
   int ret = 1;
@@ -531,16 +578,30 @@ static int redis_set_user_key(u08bits *usname, u08bits *realm, const char *key)
 	}
   return ret;
 }
+
+static int redis_set_oauth_key(oauth_key_data_raw *key) {
+  int ret = 1;
+  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' hkdf_hash_func '%s' as_rs_alg '%s' as_rs_key '%s' auth_alg '%s' auth_key '%s' timestamp %llu lifetime %lu",
+			key->kid,key->ikm_key,key->hkdf_hash_func,key->as_rs_alg,key->as_rs_key,key->auth_alg,key->auth_key,(unsigned long long)key->timestamp,(unsigned long)key->lifetime);
+	turnFreeRedisReply(redisCommand(rc, statement));
+	turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+  }
+  return ret;
+}
   
 static int redis_set_user_pwd(u08bits *usname, st_password_t pwd) {
   int ret = 1;
 	redisContext *rc = get_redis_connection();
 	if(rc) {
 		char statement[TURN_LONG_STRING_SIZE];
-	  snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",usname,pwd);
-	  turnFreeRedisReply(redisCommand(rc, statement));
+		snprintf(statement,sizeof(statement),"set turn/user/%s/password %s",usname,pwd);
+		turnFreeRedisReply(redisCommand(rc, statement));
 		turnFreeRedisReply(redisCommand(rc, "save"));
-    ret = 0;
+		ret = 0;
 	}
   return ret;
 }
@@ -565,6 +626,19 @@ static int redis_del_user(u08bits *usname, int is_st, u08bits *realm) {
 	}
   return ret;
 }
+
+static int redis_del_oauth_key(const u08bits *kid) {
+  int ret = 1;
+  redisContext *rc = get_redis_connection();
+  if(rc) {
+	char statement[TURN_LONG_STRING_SIZE];
+	snprintf(statement,sizeof(statement),"del turn/oauth/kid/%s",(const char*)kid);
+	turnFreeRedisReply(redisCommand(rc, statement));
+	turnFreeRedisReply(redisCommand(rc, "save"));
+    ret = 0;
+  }
+  return ret;
+}
   
 static int redis_list_users(int is_st, u08bits *realm) {
   int ret = 1;
@@ -656,6 +730,51 @@ static int redis_list_users(int is_st, u08bits *realm) {
 	}
   return ret;
 }
+
+static int redis_list_oauth_keys(void) {
+  int ret = 1;
+  redisContext *rc = get_redis_connection();
+  secrets_list_t keys;
+  size_t isz = 0;
+  init_secrets_list(&keys);
+
+  if(rc) {
+	  redisReply *reply = NULL;
+
+	  reply = (redisReply*)redisCommand(rc, "keys turn/oauth/kid/*");
+	  if(reply) {
+
+		if (reply->type == REDIS_REPLY_ERROR)
+			TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error: %s\n", reply->str);
+		else if (reply->type != REDIS_REPLY_ARRAY) {
+			if (reply->type != REDIS_REPLY_NIL)
+				TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unexpected type: %d\n", reply->type);
+		} else {
+			size_t i;
+			for (i = 0; i < reply->elements; ++i) {
+				add_to_secrets_list(&keys,reply->element[i]->str);
+			}
+		}
+		turnFreeRedisReply(reply);
+	}
+  }
+
+  for(isz=0;isz<keys.sz;++isz) {
+	char *s = keys.secrets[isz];
+	oauth_key_data_raw key_;
+	oauth_key_data_raw *key=&key_;
+	if(redis_get_oauth_key((const u08bits*)s,key) == 0) {
+		printf("  kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, hkdf_hash_func=%s, as_rs_alg=%s, as_rs_key=%s, auth_alg=%s, auth_key=%s\n",
+		    key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->hkdf_hash_func,
+		    key->as_rs_alg, key->as_rs_key, key->auth_alg, key->auth_key);
+	}
+  }
+
+  clean_secrets_list(&keys);
+  ret = 0;
+
+  return ret;
+}
   
 static int redis_show_secret(u08bits *realm) {
   int ret = 1;
@@ -1134,7 +1253,11 @@ static turn_dbdriver_t driver = {
   &redis_list_realm_options,
   &redis_auth_ping,
   &redis_get_ip_list,
-  &redis_reread_realms
+  &redis_reread_realms,
+  &redis_set_oauth_key,
+  &redis_get_oauth_key,
+  &redis_del_oauth_key,
+  &redis_list_oauth_keys
 };
 
 turn_dbdriver_t * get_redis_dbdriver(void) {

+ 20 - 14
src/apps/relay/dbdrivers/dbdriver.h

@@ -42,6 +42,22 @@ extern "C" {
 
 ////////////////////////////////////////////
 
+struct _oauth_key_data_raw {
+	char kid[OAUTH_KID_SIZE+1];
+	char ikm_key[OAUTH_KEY_SIZE+1];
+	u64bits timestamp;
+	u32bits lifetime;
+	char hkdf_hash_func[OAUTH_HASH_FUNC_SIZE+1];
+	char as_rs_alg[OAUTH_ALG_SIZE+1];
+	char as_rs_key[OAUTH_KEY_SIZE+1];
+	char auth_alg[OAUTH_ALG_SIZE+1];
+	char auth_key[OAUTH_KEY_SIZE+1];
+};
+
+typedef struct _oauth_key_data_raw oauth_key_data_raw;
+
+////////////////////////////////////////////
+
 typedef struct _turn_dbdriver_t {
   int (*get_auth_secrets)(secrets_list_t *sl, u08bits *realm);
   int (*get_user_key)(u08bits *usname, u08bits *realm, hmackey_t key);
@@ -61,6 +77,10 @@ typedef struct _turn_dbdriver_t {
   void (*auth_ping)(void * rch);
   int (*get_ip_list)(const char *kind, ip_range_list_t * list);
   void (*reread_realms)(secrets_list_t * realms_list);
+  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)(void);
 } turn_dbdriver_t;
 
 /////////// USER DB CHECK //////////////////
@@ -71,20 +91,6 @@ turn_dbdriver_t * get_dbdriver(void);
 
 ////////////// OAUTH UTILS ////////////////
 
-struct _oauth_key_data_raw {
-	char kid[OAUTH_KID_SIZE+1];
-	char ikm_key[OAUTH_KEY_SIZE+1];
-	u64bits timestamp;
-	u32bits lifetime;
-	char hkdf_hash_func[OAUTH_HASH_FUNC_SIZE+1];
-	char as_rs_alg[OAUTH_ALG_SIZE+1];
-	char as_rs_key[OAUTH_KEY_SIZE+1];
-	char auth_alg[OAUTH_ALG_SIZE+1];
-	char auth_key[OAUTH_KEY_SIZE+1];
-};
-
-typedef struct _oauth_key_data_raw oauth_key_data_raw;
-
 void convert_oauth_key_data_raw(const oauth_key_data_raw *raw, oauth_key_data *oakd);
 
 ////////////////////////////////////////////

+ 8 - 8
turndb/schema.sql

@@ -42,13 +42,13 @@ CREATE TABLE turn_realm_option (
 
 CREATE TABLE oauth_key (
 	kid varchar(128),
-	ikm_key varchar(256),
-	timestamp bigint,
-	lifetime integer,
-	hkdf_hash_func varchar(64),
-	as_rs_alg varchar(64),
-	as_rs_key varchar(256),
-	auth_alg varchar(64),
-	auth_key varchar(256),
+	ikm_key varchar(256) default '',
+	timestamp bigint default 0,
+	lifetime integer default 0,
+	hkdf_hash_func varchar(64) default '',
+	as_rs_alg varchar(64) default '',
+	as_rs_key varchar(256) default '',
+	auth_alg varchar(64) default '',
+	auth_key varchar(256) default '',
 	primary key (kid)
 );