/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * Copyright (C) 2014 Vivocha S.p.A. * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the project nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "dbd_pgsql.h" #include "../mainrelay.h" #if !defined(TURN_NO_PQ) #include /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int donot_print_connection_success = 0; static PGconn *get_pqdb_connection(void) { persistent_users_db_t *pud = get_persistent_users_db(); PGconn *pqdbconnection = (PGconn *)pthread_getspecific(connection_key); if (pqdbconnection) { ConnStatusType status = PQstatus(pqdbconnection); if (status != CONNECTION_OK) { PQfinish(pqdbconnection); pqdbconnection = NULL; (void)pthread_setspecific(connection_key, pqdbconnection); } } if (!pqdbconnection) { char *errmsg = NULL; PQconninfoOption *co = PQconninfoParse(pud->userdb, &errmsg); if (!co) { if (errmsg) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection <%s>, connection string format error: %s\n", pud->userdb_sanitized, errmsg); free(errmsg); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, unknown connection string format error\n", pud->userdb_sanitized); } } else { PQconninfoFree(co); if (errmsg) { free(errmsg); } pqdbconnection = PQconnectdb(pud->userdb); if (!pqdbconnection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n", pud->userdb_sanitized); } else { ConnStatusType status = PQstatus(pqdbconnection); if (status != CONNECTION_OK) { PQfinish(pqdbconnection); pqdbconnection = NULL; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot open PostgreSQL DB connection: <%s>, runtime error\n", pud->userdb_sanitized); } else if (!donot_print_connection_success) { TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL DB connection success: %s\n", pud->userdb_sanitized); donot_print_connection_success = 1; } } } if (pqdbconnection) { (void)pthread_setspecific(connection_key, pqdbconnection); } } return pqdbconnection; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int pgsql_get_auth_secrets(secrets_list_t *sl, uint8_t *realm) { int ret = -1; PGconn *pqc = get_pqdb_connection(); if (pqc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement, sizeof(statement) - 1, "select value from turn_secret where realm='%s'", realm); 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++) { char *kval = PQgetvalue(res, i, 0); if (kval) { add_to_secrets_list(sl, kval); } } ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_get_user_key(uint8_t *usname, uint8_t *realm, hmackey_t key) { int ret = -1; PGconn *pqc = get_pqdb_connection(); if (pqc) { char statement[TURN_LONG_STRING_SIZE]; /* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */ snprintf(statement, sizeof(statement), "select hmackey from turnusers_lt where name='%s' and realm='%s'", usname, realm); 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 { char *kval = PQgetvalue(res, 0, 0); int len = PQgetlength(res, 0, 0); if (kval) { size_t sz = get_hmackey_size(SHATYPE_DEFAULT); if (((size_t)len < sz * 2) || (strlen(kval) < sz * 2)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: %s, user %s\n", kval, usname); } else { convert_string_key_to_binary(kval, key, sz); ret = 0; } } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong hmackey data for user %s: NULL\n", usname); } } if (res) { PQclear(res); } } return ret; } static int pgsql_get_oauth_key(const uint8_t *kid, oauth_key_data_raw *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; /* direct user input eliminated - there is no SQL injection problem (since version 4.4.5.3) */ snprintf(statement, sizeof(statement), "select ikm_key,timestamp,lifetime,as_rs_alg,realm 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(key->ikm_key, PQgetvalue(res, 0, 0)); key->timestamp = (uint64_t)strtoll(PQgetvalue(res, 0, 1), NULL, 10); key->lifetime = (uint32_t)strtol(PQgetvalue(res, 0, 2), NULL, 10); STRCPY(key->as_rs_alg, PQgetvalue(res, 0, 3)); STRCPY(key->realm, PQgetvalue(res, 0, 4)); STRCPY(key->kid, kid); ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_list_oauth_keys(secrets_list_t *kids, secrets_list_t *teas, secrets_list_t *tss, secrets_list_t *lts, secrets_list_t *realms) { oauth_key_data_raw key_; oauth_key_data_raw *key = &key_; int ret = -1; char statement[TURN_LONG_STRING_SIZE]; snprintf(statement, sizeof(statement), "select ikm_key,timestamp,lifetime,as_rs_alg,realm,kid from oauth_key order by kid"); PGconn *pqc = get_pqdb_connection(); if (pqc) { 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(key->ikm_key, PQgetvalue(res, i, 0)); key->timestamp = (uint64_t)strtoll(PQgetvalue(res, i, 1), NULL, 10); key->lifetime = (uint32_t)strtol(PQgetvalue(res, i, 2), NULL, 10); STRCPY(key->as_rs_alg, PQgetvalue(res, i, 3)); STRCPY(key->realm, PQgetvalue(res, i, 4)); STRCPY(key->kid, PQgetvalue(res, i, 5)); if (kids) { add_to_secrets_list(kids, key->kid); add_to_secrets_list(teas, key->as_rs_alg); add_to_secrets_list(realms, key->realm); { char ts[256]; snprintf(ts, sizeof(ts) - 1, "%llu", (unsigned long long)key->timestamp); add_to_secrets_list(tss, ts); } { char lt[256]; snprintf(lt, sizeof(lt) - 1, "%lu", (unsigned long)key->lifetime); add_to_secrets_list(lts, lt); } } else { printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s, realm=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg, key->realm); } ret = 0; } } if (res) { PQclear(res); } } return ret; } static int pgsql_set_user_key(uint8_t *usname, uint8_t *realm, const char *key) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement, sizeof(statement), "insert into turnusers_lt (realm,name,hmackey) values('%s','%s','%s')", realm, usname, key); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { if (res) { PQclear(res); } snprintf(statement, sizeof(statement), "update turnusers_lt set hmackey='%s' where name='%s' and realm='%s'", key, usname, realm); res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n", PQerrorMessage(pqc)); } else { ret = 0; } } if (res) { PQclear(res); } } 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,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); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { if (res) { PQclear(res); } snprintf( statement, sizeof(statement), "update oauth_key set ikm_key='%s',timestamp=%lu,lifetime=%lu, as_rs_alg='%s', realm='%s' where kid='%s'", key->ikm_key, (unsigned long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg, key->realm, key->kid); res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating oauth_key information: %s\n", PQerrorMessage(pqc)); } else { ret = 0; } } else { ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_del_user(uint8_t *usname, uint8_t *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement, sizeof(statement), "delete from turnusers_lt where name='%s' and realm='%s'", usname, realm); PGresult *res = PQexec(pqc, statement); if (res) { PQclear(res); ret = 0; } } return ret; } static int pgsql_del_oauth_key(const uint8_t *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(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; uint8_t realm0[STUN_MAX_REALM_SIZE + 1] = "\0"; if (!realm) { realm = realm0; } PGconn *pqc = get_pqdb_connection(); if (pqc) { if (realm[0]) { snprintf(statement, sizeof(statement), "select name,realm from turnusers_lt where realm='%s' order by name", realm); } else { snprintf(statement, sizeof(statement), "select name,realm from turnusers_lt order by realm,name"); } 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++) { char *kval = PQgetvalue(res, i, 0); if (kval) { char *rval = PQgetvalue(res, i, 1); if (rval) { if (users) { add_to_secrets_list(users, kval); if (realms) { if (rval && *rval) { add_to_secrets_list(realms, rval); } else { add_to_secrets_list(realms, (char *)realm); } } } else { printf("%s[%s]\n", kval, rval); } } } } ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_list_secrets(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE + 1] = "\0"; if (!realm) { realm = realm0; } char statement[TURN_LONG_STRING_SIZE]; if (realm[0]) { snprintf(statement, sizeof(statement), "select value,realm from turn_secret where realm='%s' order by value", realm); } else { snprintf(statement, sizeof(statement), "select value,realm from turn_secret order by realm,value"); } donot_print_connection_success = 1; 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++) { char *kval = PQgetvalue(res, i, 0); if (kval) { char *rval = PQgetvalue(res, i, 1); if (secrets) { add_to_secrets_list(secrets, kval); if (realms) { if (rval && *rval) { add_to_secrets_list(realms, rval); } else { add_to_secrets_list(realms, (char *)realm); } } } else { printf("%s[%s]\n", kval, rval); } } } ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_del_secret(uint8_t *secret, uint8_t *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { if (!secret || (secret[0] == 0)) { snprintf(statement, sizeof(statement), "delete from turn_secret where realm='%s'", realm); } else { snprintf(statement, sizeof(statement), "delete from turn_secret where value='%s' and realm='%s'", secret, realm); } PGresult *res = PQexec(pqc, statement); if (res) { PQclear(res); ret = 0; } } return ret; } static int pgsql_set_secret(uint8_t *secret, uint8_t *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement, sizeof(statement), "insert into turn_secret (realm,value) values('%s','%s')", realm, secret); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information: %s\n", PQerrorMessage(pqc)); } else { ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_set_permission_ip(const char *kind, uint8_t *realm, const char *ip, int del) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE + 1] = "\0"; if (!realm) { realm = realm0; } donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { if (del) { snprintf(statement, sizeof(statement), "delete from %s_peer_ip where realm = '%s' and ip_range = '%s'", kind, (char *)realm, ip); } else { snprintf(statement, sizeof(statement), "insert into %s_peer_ip (realm,ip_range) values('%s','%s')", kind, (char *)realm, ip); } PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting ip permission information: %s\n", PQerrorMessage(pqc)); } else { ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_add_origin(uint8_t *origin, uint8_t *realm) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement, sizeof(statement), "insert into turn_origin_to_realm (origin,realm) values('%s','%s')", origin, realm); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting origin information: %s\n", PQerrorMessage(pqc)); } else { ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_del_origin(uint8_t *origin) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement, sizeof(statement), "delete from turn_origin_to_realm where origin='%s'", origin); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information: %s\n", PQerrorMessage(pqc)); } else { ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_list_origins(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms) { int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE + 1] = "\0"; if (!realm) { realm = realm0; } donot_print_connection_success = 1; PGconn *pqc = get_pqdb_connection(); if (pqc) { char statement[TURN_LONG_STRING_SIZE]; if (realm && realm[0]) { snprintf(statement, sizeof(statement), "select origin,realm from turn_origin_to_realm where realm='%s' order by origin", realm); } else { snprintf(statement, sizeof(statement), "select origin,realm from turn_origin_to_realm order by realm,origin"); } 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++) { char *kval = PQgetvalue(res, i, 0); if (kval) { char *rval = PQgetvalue(res, i, 1); if (rval) { if (origins) { add_to_secrets_list(origins, kval); if (realms) { if (rval && *rval) { add_to_secrets_list(realms, rval); } else { add_to_secrets_list(realms, (char *)realm); } } } else { printf("%s ==>> %s\n", kval, rval); } } } } ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_set_realm_option_one(uint8_t *realm, unsigned long value, const char *opt) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { { snprintf(statement, sizeof(statement), "delete from turn_realm_option where realm='%s' and opt='%s'", realm, opt); PGresult *res = PQexec(pqc, statement); if (res) { PQclear(res); } } if (value > 0) { snprintf(statement, sizeof(statement), "insert into turn_realm_option (realm,opt,value) values('%s','%s','%lu')", realm, opt, (unsigned long)value); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting realm option information: %s\n", PQerrorMessage(pqc)); } else { ret = 0; } if (res) { PQclear(res); } } } return ret; } static int pgsql_list_realm_options(uint8_t *realm) { int ret = -1; donot_print_connection_success = 1; char statement[TURN_LONG_STRING_SIZE]; PGconn *pqc = get_pqdb_connection(); if (pqc) { if (realm && realm[0]) { snprintf(statement, sizeof(statement), "select realm,opt,value from turn_realm_option where realm='%s' order by realm,opt", realm); } else { snprintf(statement, sizeof(statement), "select realm,opt,value from turn_realm_option order by realm,opt"); } 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++) { char *rval = PQgetvalue(res, i, 0); if (rval) { char *oval = PQgetvalue(res, i, 1); if (oval) { char *vval = PQgetvalue(res, i, 2); if (vval) { printf("%s[%s]=%s\n", oval, rval, vval); } } } } ret = 0; } if (res) { PQclear(res); } } return ret; } static void pgsql_auth_ping(void *rch) { UNUSED_ARG(rch); PGconn *pqc = get_pqdb_connection(); if (pqc) { char statement[TURN_LONG_STRING_SIZE]; STRCPY(statement, "select value from turn_secret"); 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)); } if (res) { PQclear(res); } } } static int pgsql_get_ip_list(const char *kind, ip_range_list_t *list) { int ret = -1; PGconn *pqc = get_pqdb_connection(); if (pqc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement, sizeof(statement), "select ip_range,realm from %s_peer_ip", kind); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_TUPLES_OK)) { static int wrong_table_reported = 0; if (!wrong_table_reported) { wrong_table_reported = 1; TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving PostgreSQL DB information: %s; probably, the tables 'allowed_peer_ip' and/or " "'denied_peer_ip' have to be upgraded to include the realm column.\n", PQerrorMessage(pqc)); } snprintf(statement, sizeof(statement), "select ip_range,'' from %s_peer_ip", kind); res = PQexec(pqc, statement); } if (res && (PQresultStatus(res) == PGRES_TUPLES_OK)) { int i = 0; for (i = 0; i < PQntuples(res); i++) { char *kval = PQgetvalue(res, i, 0); char *rval = PQgetvalue(res, i, 1); if (kval) { add_ip_list_range(kval, rval, list); } } ret = 0; } if (res) { PQclear(res); } } return ret; } static void pgsql_reread_realms(secrets_list_t *realms_list) { PGconn *pqc = get_pqdb_connection(); if (pqc) { char statement[TURN_LONG_STRING_SIZE]; { snprintf(statement, sizeof(statement), "select origin,realm from turn_origin_to_realm"); PGresult *res = PQexec(pqc, statement); if (res && (PQresultStatus(res) == PGRES_TUPLES_OK)) { ur_string_map *o_to_realm_new = ur_string_map_create(free); int i = 0; for (i = 0; i < PQntuples(res); i++) { char *oval = PQgetvalue(res, i, 0); if (oval) { char *rval = PQgetvalue(res, i, 1); if (rval) { get_realm(rval); ur_string_map_value_type value = strdup(rval); if (!ur_string_map_put(o_to_realm_new, (ur_string_map_key_type)oval, value)) { free(value); } } } } update_o_to_realm(o_to_realm_new); } if (res) { PQclear(res); } } { { size_t i = 0; size_t rlsz = 0; lock_realms(); rlsz = realms_list->sz; unlock_realms(); for (i = 0; i < rlsz; ++i) { char *realm = realms_list->secrets[i]; realm_params_t *rp = get_realm(realm); lock_realms(); rp->options.perf_options.max_bps = turn_params.max_bps; unlock_realms(); lock_realms(); rp->options.perf_options.total_quota = turn_params.total_quota; unlock_realms(); lock_realms(); rp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); } } snprintf(statement, sizeof(statement), "select realm,opt,value from turn_realm_option"); PGresult *res = PQexec(pqc, statement); if (res && (PQresultStatus(res) == PGRES_TUPLES_OK)) { int i = 0; for (i = 0; i < PQntuples(res); i++) { char *rval = PQgetvalue(res, i, 0); char *oval = PQgetvalue(res, i, 1); char *vval = PQgetvalue(res, i, 2); if (rval && oval && vval) { realm_params_t *rp = get_realm(rval); if (!strcmp(oval, "max-bps")) { rp->options.perf_options.max_bps = (band_limit_t)strtoul(vval, NULL, 10); } else if (!strcmp(oval, "total-quota")) { rp->options.perf_options.total_quota = (vint)atoi(vval); } else if (!strcmp(oval, "user-quota")) { rp->options.perf_options.user_quota = (vint)atoi(vval); } else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown realm option: %s\n", oval); } } } } if (res) { PQclear(res); } } } } ////////////////////////////////////////////// static int pgsql_get_admin_user(const uint8_t *usname, uint8_t *realm, password_t pwd) { int ret = -1; realm[0] = 0; pwd[0] = 0; PGconn *pqc = get_pqdb_connection(); if (pqc) { char statement[TURN_LONG_STRING_SIZE]; snprintf(statement, sizeof(statement), "select realm,password from admin_user where name='%s'", usname); 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 { const char *kval = PQgetvalue(res, 0, 0); if (kval) { strncpy((char *)realm, kval, STUN_MAX_REALM_SIZE); } kval = (const char *)PQgetvalue(res, 0, 1); if (kval) { strncpy((char *)pwd, kval, STUN_MAX_PWD_SIZE); } ret = 0; } if (res) { PQclear(res); } } return ret; } static int pgsql_set_admin_user(const uint8_t *usname, const uint8_t *realm, const password_t pwd) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success = 1; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement, sizeof(statement), "insert into admin_user (realm,name,password) values('%s','%s','%s')", realm, usname, pwd); PGresult *res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { if (res) { PQclear(res); } snprintf(statement, sizeof(statement), "update admin_user set password='%s',realm='%s' where name='%s'", pwd, realm, usname); res = PQexec(pqc, statement); if (!res || (PQresultStatus(res) != PGRES_COMMAND_OK)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating user information: %s\n", PQerrorMessage(pqc)); } else { ret = 0; } } if (res) { PQclear(res); } } return ret; } static int pgsql_del_admin_user(const uint8_t *usname) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success = 1; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement, sizeof(statement), "delete from admin_user where name='%s'", usname); PGresult *res = PQexec(pqc, statement); if (res) { PQclear(res); ret = 0; } } return ret; } static int pgsql_list_admin_users(int no_print) { int ret = -1; char statement[TURN_LONG_STRING_SIZE]; donot_print_connection_success = 1; PGconn *pqc = get_pqdb_connection(); if (pqc) { snprintf(statement, sizeof(statement), "select name,realm,password from admin_user order by realm,name"); } 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; ret = 0; for (i = 0; i < PQntuples(res); i++) { char *kval = PQgetvalue(res, i, 0); ++ret; if (kval && !no_print) { char *rval = PQgetvalue(res, i, 1); if (rval && *rval) { printf("%s[%s]\n", kval, rval); } else { printf("%s\n", kval); } } } } if (res) { PQclear(res); } return ret; } static void pgsql_disconnect(void) { PGconn *pqdbconnection = (PGconn *)pthread_getspecific(connection_key); if (pqdbconnection) { PQfinish(pqdbconnection); pqdbconnection = NULL; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "PostgreSQL connection was closed.\n"); } ///////////////////////////////////////////////////////////// static const turn_dbdriver_t driver = { &pgsql_get_auth_secrets, &pgsql_get_user_key, &pgsql_set_user_key, &pgsql_del_user, &pgsql_list_users, &pgsql_list_secrets, &pgsql_del_secret, &pgsql_set_secret, &pgsql_add_origin, &pgsql_del_origin, &pgsql_list_origins, &pgsql_set_realm_option_one, &pgsql_list_realm_options, &pgsql_auth_ping, &pgsql_get_ip_list, &pgsql_set_permission_ip, &pgsql_reread_realms, &pgsql_set_oauth_key, &pgsql_get_oauth_key, &pgsql_del_oauth_key, &pgsql_list_oauth_keys, &pgsql_get_admin_user, &pgsql_set_admin_user, &pgsql_del_admin_user, &pgsql_list_admin_users, &pgsql_disconnect}; const turn_dbdriver_t *get_pgsql_dbdriver(void) { return &driver; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// #else const turn_dbdriver_t *get_pgsql_dbdriver(void) { return NULL; } #endif