diff --git a/INSTALL b/INSTALL index 61bb168..258bec2 100644 --- a/INSTALL +++ b/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. diff --git a/src/apps/relay/dbdrivers/dbd_mongo.c b/src/apps/relay/dbdrivers/dbd_mongo.c index d6ae37e..ae02593 100644 --- a/src/apps/relay/dbdrivers/dbd_mongo.c +++ b/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) { diff --git a/src/apps/relay/dbdrivers/dbd_mysql.c b/src/apps/relay/dbdrivers/dbd_mysql.c index dee2feb..ee091f6 100644 --- a/src/apps/relay/dbdrivers/dbd_mysql.c +++ b/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) { diff --git a/src/apps/relay/dbdrivers/dbd_pgsql.c b/src/apps/relay/dbdrivers/dbd_pgsql.c index 2873702..fd2548f 100644 --- a/src/apps/relay/dbdrivers/dbd_pgsql.c +++ b/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;iikm_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) { diff --git a/src/apps/relay/dbdrivers/dbd_redis.c b/src/apps/relay/dbdrivers/dbd_redis.c index c368ec3..5f34cf5 100644 --- a/src/apps/relay/dbdrivers/dbd_redis.c +++ b/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;iszkid, 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) { diff --git a/src/apps/relay/dbdrivers/dbdriver.h b/src/apps/relay/dbdrivers/dbdriver.h index 1310254..e68da53 100644 --- a/src/apps/relay/dbdrivers/dbdriver.h +++ b/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); //////////////////////////////////////////// diff --git a/turndb/schema.sql b/turndb/schema.sql index e438bce..cdbb48d 100644 --- a/turndb/schema.sql +++ b/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) );