/* * Copyright (C) 2011, 2012, 2013 Citrix Systems * * 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 #include #include #include #include #include #include #ifndef _MSC_VER #include #endif #include #include #include #include #include #include "dbdrivers/dbdriver.h" #include "mainrelay.h" #include "userdb.h" #include "ns_turn_utils.h" #include "ns_turn_maps.h" #include "ns_turn_server.h" #include "apputils.h" //////////// REALM ////////////// static realm_params_t *default_realm_params_ptr = NULL; static ur_string_map *realms = NULL; static TURN_MUTEX_DECLARE(o_to_realm_mutex); static ur_string_map *o_to_realm = NULL; static secrets_list_t realms_list; static char userdb_type_unknown[] = "Unknown"; static char userdb_type_sqlite[] = "SQLite"; static char userdb_type_postgresql[] = "PostgreSQL"; static char userdb_type_mysql[] = "MySQL/MariaDB"; static char userdb_type_mongo[] = "MongoDB"; static char userdb_type_redis[] = "Redis"; const char *userdb_type_to_string(TURN_USERDB_TYPE t) { switch (t) { case TURN_USERDB_TYPE_SQLITE: return userdb_type_sqlite; break; case TURN_USERDB_TYPE_PQ: return userdb_type_postgresql; break; case TURN_USERDB_TYPE_MYSQL: return userdb_type_mysql; break; case TURN_USERDB_TYPE_MONGO: return userdb_type_mongo; break; case TURN_USERDB_TYPE_REDIS: return userdb_type_redis; break; default: return userdb_type_unknown; }; } void lock_realms(void) { ur_string_map_lock(realms); } void unlock_realms(void) { ur_string_map_unlock(realms); } void update_o_to_realm(ur_string_map *o_to_realm_new) { TURN_MUTEX_LOCK(&o_to_realm_mutex); ur_string_map_free(&o_to_realm); o_to_realm = o_to_realm_new; TURN_MUTEX_UNLOCK(&o_to_realm_mutex); } void create_default_realm(void) { if (default_realm_params_ptr) { return; } static realm_params_t _default_realm_params = {1, {"\0", /* name */ {0, 0, 0}}, {0, NULL}}; /* init everything: */ TURN_MUTEX_INIT_RECURSIVE(&o_to_realm_mutex); init_secrets_list(&realms_list); o_to_realm = ur_string_map_create(free); default_realm_params_ptr = &_default_realm_params; realms = ur_string_map_create(NULL); lock_realms(); default_realm_params_ptr->status.alloc_counters = ur_string_map_create(NULL); unlock_realms(); } void get_default_realm_options(realm_options_t *ro) { if (ro) { lock_realms(); memcpy(ro, &(default_realm_params_ptr->options), sizeof(realm_options_t)); unlock_realms(); } } void set_default_realm_name(char *realm) { lock_realms(); ur_string_map_value_type value = (ur_string_map_value_type)default_realm_params_ptr; STRCPY(default_realm_params_ptr->options.name, realm); ur_string_map_put(realms, (ur_string_map_key_type)default_realm_params_ptr->options.name, value); add_to_secrets_list(&realms_list, realm); unlock_realms(); } realm_params_t *get_realm(char *name) { if (name && name[0]) { lock_realms(); ur_string_map_value_type value = 0; ur_string_map_key_type key = (ur_string_map_key_type)name; if (ur_string_map_get(realms, key, &value)) { unlock_realms(); return (realm_params_t *)value; } else { realm_params_t *ret = (realm_params_t *)malloc(sizeof(realm_params_t)); memcpy(ret, default_realm_params_ptr, sizeof(realm_params_t)); STRCPY(ret->options.name, name); value = (ur_string_map_value_type)ret; ur_string_map_put(realms, key, value); ret->status.alloc_counters = ur_string_map_create(NULL); add_to_secrets_list(&realms_list, name); unlock_realms(); return ret; } } return default_realm_params_ptr; } int get_realm_data(char *name, realm_params_t *rp) { lock_realms(); memcpy(rp, get_realm(name), sizeof(realm_params_t)); unlock_realms(); return 0; } int get_realm_options_by_origin(char *origin, realm_options_t *ro) { ur_string_map_value_type value = 0; TURN_MUTEX_LOCK(&o_to_realm_mutex); if (ur_string_map_get(o_to_realm, (ur_string_map_key_type)origin, &value) && value) { char *realm = strdup((char *)value); TURN_MUTEX_UNLOCK(&o_to_realm_mutex); realm_params_t rp; get_realm_data(realm, &rp); memcpy(ro, &(rp.options), sizeof(realm_options_t)); free(realm); return 1; } else { TURN_MUTEX_UNLOCK(&o_to_realm_mutex); get_default_realm_options(ro); return 0; } } void get_realm_options_by_name(char *realm, realm_options_t *ro) { realm_params_t rp; get_realm_data(realm, &rp); memcpy(ro, &(rp.options), sizeof(realm_options_t)); } int change_total_quota(char *realm, int value) { int ret = value; lock_realms(); realm_params_t *rp = get_realm(realm); rp->options.perf_options.total_quota = value; unlock_realms(); return ret; } int change_user_quota(char *realm, int value) { int ret = value; lock_realms(); realm_params_t *rp = get_realm(realm); rp->options.perf_options.user_quota = value; unlock_realms(); return ret; } static void must_set_admin_realm(void *realm0) { char *realm = (char *)realm0; if (!realm || !realm[0]) { fprintf(stderr, "The operation cannot be completed: the realm must be set.\n"); exit(-1); } } static void must_set_admin_user(void *user0) { char *user = (char *)user0; if (!user || !user[0]) { fprintf(stderr, "The operation cannot be completed: the user must be set.\n"); exit(-1); } } static void must_set_admin_pwd(void *pwd0) { char *pwd = (char *)pwd0; if (!pwd || !pwd[0]) { fprintf(stderr, "The operation cannot be completed: the password must be set.\n"); exit(-1); } } static void must_set_admin_origin(void *origin0) { char *origin = (char *)origin0; if (!origin || !origin[0]) { fprintf(stderr, "The operation cannot be completed: the origin must be set.\n"); exit(-1); } } /////////// SHARED SECRETS ///////////////// void init_secrets_list(secrets_list_t *sl) { if (sl) { memset(sl, 0, sizeof(secrets_list_t)); } } void clean_secrets_list(secrets_list_t *sl) { if (sl) { if (sl->secrets) { size_t i = 0; for (i = 0; i < sl->sz; ++i) { if (sl->secrets[i]) { free(sl->secrets[i]); } } free(sl->secrets); sl->secrets = NULL; sl->sz = 0; } } } size_t get_secrets_list_size(secrets_list_t *sl) { if (sl && sl->secrets) { return sl->sz; } return 0; } const char *get_secrets_list_elem(secrets_list_t *sl, size_t i) { if (get_secrets_list_size(sl) > i) { return sl->secrets[i]; } return NULL; } void add_to_secrets_list(secrets_list_t *sl, const char *elem) { if (sl && elem) { sl->secrets = (char **)realloc(sl->secrets, (sizeof(char *) * (sl->sz + 1))); sl->secrets[sl->sz] = strdup(elem); sl->sz += 1; } } //////////////////////////////////////////// static int get_auth_secrets(secrets_list_t *sl, uint8_t *realm) { int ret = -1; const turn_dbdriver_t *dbd = get_dbdriver(); clean_secrets_list(sl); if (get_secrets_list_size(&turn_params.default_users_db.ram_db.static_auth_secrets)) { size_t i = 0; for (i = 0; i < get_secrets_list_size(&turn_params.default_users_db.ram_db.static_auth_secrets); ++i) { add_to_secrets_list(sl, get_secrets_list_elem(&turn_params.default_users_db.ram_db.static_auth_secrets, i)); } ret = 0; } if (dbd && dbd->get_auth_secrets) { ret = (*dbd->get_auth_secrets)(sl, realm); } return ret; } /* * Timestamp retrieval */ static turn_time_t get_rest_api_timestamp(char *usname) { turn_time_t ts = 0; int ts_set = 0; char *col = strchr(usname, turn_params.rest_api_separator); if (col) { if (col == usname) { usname += 1; } else { char *ptr = usname; int found_non_figure = 0; while (ptr < col) { if (!(ptr[0] >= '0' && ptr[0] <= '9')) { found_non_figure = 1; break; } ++ptr; } if (found_non_figure) { ts = (turn_time_t)atol(col + 1); ts_set = 1; } else { *col = 0; ts = (turn_time_t)atol(usname); ts_set = 1; *col = turn_params.rest_api_separator; } } } if (!ts_set) { ts = (turn_time_t)atol(usname); } return ts; } static char *get_real_username(char *usname) { if (usname[0] && turn_params.use_auth_secret_with_timestamp) { char *col = strchr(usname, turn_params.rest_api_separator); if (col) { if (col == usname) { usname += 1; } else { char *ptr = usname; int found_non_figure = 0; while (ptr < col) { if (!(ptr[0] >= '0' && ptr[0] <= '9')) { found_non_figure = 1; break; } ++ptr; } if (!found_non_figure) { usname = col + 1; } else { *col = 0; usname = strdup(usname); *col = turn_params.rest_api_separator; return usname; } } } } return strdup(usname); } /* * Password retrieval */ int get_user_key(int in_oauth, int *out_oauth, int *max_session_time, uint8_t *usname, uint8_t *realm, hmackey_t key, ioa_network_buffer_handle nbh) { int ret = -1; if (max_session_time) { *max_session_time = 0; } if (in_oauth && out_oauth && usname && usname[0]) { stun_attr_ref sar = stun_attr_get_first_by_type_str(ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), STUN_ATTRIBUTE_OAUTH_ACCESS_TOKEN); if (sar) { int len = stun_attr_get_len(sar); const uint8_t *value = stun_attr_get_value(sar); *out_oauth = 1; if (len > 0 && value) { const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->get_oauth_key) { oauth_key_data_raw rawKey; memset(&rawKey, 0, sizeof(rawKey)); int gres = (*(dbd->get_oauth_key))(usname, &rawKey); if (gres < 0) { return ret; } if (!rawKey.kid[0]) { return ret; } if (rawKey.lifetime) { if (!turn_time_before(turn_time(), (turn_time_t)(rawKey.timestamp + rawKey.lifetime + OAUTH_TIME_DELTA))) { return ret; } } oauth_key_data okd; memset(&okd, 0, sizeof(okd)); convert_oauth_key_data_raw(&rawKey, &okd); char err_msg[1025] = "\0"; size_t err_msg_size = sizeof(err_msg) - 1; oauth_key okey; memset(&okey, 0, sizeof(okey)); if (convert_oauth_key_data(&okd, &okey, err_msg, err_msg_size) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "%s\n", err_msg); return -1; } oauth_token dot; memset((&dot), 0, sizeof(dot)); encoded_oauth_token etoken; memset(&etoken, 0, sizeof(etoken)); if ((size_t)len > sizeof(etoken.token)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Encoded oAuth token is too large\n"); return -1; } memcpy(etoken.token, value, (size_t)len); etoken.size = (size_t)len; const char *server_name = (char *)turn_params.oauth_server_name; if (!(server_name && server_name[0])) { server_name = (char *)realm; if (!(server_name && server_name[0])) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot determine oAuth server name"); return -1; } } if (decode_oauth_token((const uint8_t *)server_name, &etoken, &okey, &dot) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot decode oauth token\n"); return -1; } switch (dot.enc_block.key_length) { case SHA1SIZEBYTES: break; case SHA256SIZEBYTES: case SHA384SIZEBYTES: case SHA512SIZEBYTES: default: TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong size of the MAC key in oAuth token(3): %d\n", (int)dot.enc_block.key_length); return -1; }; password_t pwdtmp; if (stun_check_message_integrity_by_key_str(TURN_CREDENTIALS_LONG_TERM, ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), dot.enc_block.mac_key, pwdtmp, SHATYPE_DEFAULT) > 0) { turn_time_t lifetime = (turn_time_t)(dot.enc_block.lifetime); if (lifetime) { turn_time_t ts = (turn_time_t)(dot.enc_block.timestamp >> 16); turn_time_t to = ts + lifetime + OAUTH_TIME_DELTA; turn_time_t ct = turn_time(); if (!turn_time_before(ct, to)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "oAuth token is too old\n"); return -1; } if (max_session_time) { *max_session_time = to - ct; } } memcpy(key, dot.enc_block.mac_key, dot.enc_block.key_length); if (rawKey.realm[0]) { memcpy(realm, rawKey.realm, sizeof(rawKey.realm)); } ret = 0; } } } } } if (out_oauth && *out_oauth) { return ret; } if (turn_params.use_auth_secret_with_timestamp) { turn_time_t ctime = (turn_time_t)time(NULL); turn_time_t ts = 0; secrets_list_t sl; size_t sll = 0; init_secrets_list(&sl); if (get_auth_secrets(&sl, realm) < 0) { return ret; } ts = get_rest_api_timestamp((char *)usname); if (!turn_time_before(ts, ctime)) { uint8_t hmac[MAXSHASIZE]; unsigned int hmac_len; password_t pwdtmp; hmac[0] = 0; stun_attr_ref sar = stun_attr_get_first_by_type_str( ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), STUN_ATTRIBUTE_MESSAGE_INTEGRITY); if (!sar) { return -1; } int sarlen = stun_attr_get_len(sar); switch (sarlen) { case SHA1SIZEBYTES: hmac_len = SHA1SIZEBYTES; break; case SHA256SIZEBYTES: case SHA384SIZEBYTES: case SHA512SIZEBYTES: default: return -1; }; for (sll = 0; sll < get_secrets_list_size(&sl); ++sll) { const char *secret = get_secrets_list_elem(&sl, sll); if (secret) { if (stun_calculate_hmac(usname, strlen((char *)usname), (const uint8_t *)secret, strlen(secret), hmac, &hmac_len, SHATYPE_DEFAULT) >= 0) { size_t pwd_length = 0; char *pwd = base64_encode(hmac, hmac_len, &pwd_length); if (pwd) { if (pwd_length < 1) { free(pwd); } else { if (stun_produce_integrity_key_str((uint8_t *)usname, realm, (uint8_t *)pwd, key, SHATYPE_DEFAULT) >= 0) { if (stun_check_message_integrity_by_key_str(TURN_CREDENTIALS_LONG_TERM, ioa_network_buffer_data(nbh), ioa_network_buffer_get_size(nbh), key, pwdtmp, SHATYPE_DEFAULT) > 0) { ret = 0; } } free(pwd); if (ret == 0) { break; } } } } } } } clean_secrets_list(&sl); return ret; } ur_string_map_value_type ukey = NULL; ur_string_map_lock(turn_params.default_users_db.ram_db.static_accounts); if (ur_string_map_get(turn_params.default_users_db.ram_db.static_accounts, (ur_string_map_key_type)usname, &ukey)) { ret = 0; } ur_string_map_unlock(turn_params.default_users_db.ram_db.static_accounts); if (ret == 0) { size_t sz = get_hmackey_size(SHATYPE_DEFAULT); memcpy(key, ukey, sz); return 0; } const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->get_user_key) { ret = (*(dbd->get_user_key))(usname, realm, key); } return ret; } uint8_t *start_user_check(turnserver_id id, turn_credential_type ct, int in_oauth, int *out_oauth, uint8_t *usname, uint8_t *realm, get_username_resume_cb resume, ioa_net_data *in_buffer, uint64_t ctxkey, int *postpone_reply) { *postpone_reply = 1; struct auth_message am; memset(&am, 0, sizeof(struct auth_message)); am.id = id; am.ct = ct; am.in_oauth = in_oauth; am.out_oauth = *out_oauth; STRCPY(am.username, usname); STRCPY(am.realm, realm); am.resume_func = resume; memcpy(&(am.in_buffer), in_buffer, sizeof(ioa_net_data)); in_buffer->nbh = NULL; am.ctxkey = ctxkey; send_auth_message_to_auth_server(&am); return NULL; } int check_new_allocation_quota(uint8_t *user, int oauth, uint8_t *realm) { int ret = 0; if (user || oauth) { uint8_t *username = oauth ? (uint8_t *)strdup("") : (uint8_t *)get_real_username((char *)user); realm_params_t *rp = get_realm((char *)realm); ur_string_map_lock(rp->status.alloc_counters); if (rp->options.perf_options.total_quota && (rp->status.total_current_allocs >= rp->options.perf_options.total_quota)) { ret = -1; } else if (username[0]) { ur_string_map_value_type value = 0; if (!ur_string_map_get(rp->status.alloc_counters, (ur_string_map_key_type)username, &value)) { value = (ur_string_map_value_type)1; ur_string_map_put(rp->status.alloc_counters, (ur_string_map_key_type)username, value); ++(rp->status.total_current_allocs); } else { if ((rp->options.perf_options.user_quota) && ((size_t)value >= (size_t)(rp->options.perf_options.user_quota))) { ret = -1; } else { value = (ur_string_map_value_type)(((size_t)value) + 1); ur_string_map_put(rp->status.alloc_counters, (ur_string_map_key_type)username, value); ++(rp->status.total_current_allocs); } } } else { ++(rp->status.total_current_allocs); } free(username); ur_string_map_unlock(rp->status.alloc_counters); } return ret; } void release_allocation_quota(uint8_t *user, int oauth, uint8_t *realm) { if (user) { uint8_t *username = oauth ? (uint8_t *)strdup("") : (uint8_t *)get_real_username((char *)user); realm_params_t *rp = get_realm((char *)realm); ur_string_map_lock(rp->status.alloc_counters); if (username[0]) { ur_string_map_value_type value = 0; ur_string_map_get(rp->status.alloc_counters, (ur_string_map_key_type)username, &value); if (value) { value = (ur_string_map_value_type)(((size_t)value) - 1); if (value == 0) { ur_string_map_del(rp->status.alloc_counters, (ur_string_map_key_type)username); } else { ur_string_map_put(rp->status.alloc_counters, (ur_string_map_key_type)username, value); } } } if (rp->status.total_current_allocs) { --(rp->status.total_current_allocs); } ur_string_map_unlock(rp->status.alloc_counters); free(username); } } ////////////////////////////////// int add_static_user_account(char *user) { /* Realm is either default or empty for users taken from file or command-line */ if (!user || turn_params.use_auth_secret_with_timestamp) { return -1; } char *s = strstr(user, ":"); if (!s || (s == user) || (strlen(s) < 2)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user account: %s\n", user); return -1; } size_t ulen = s - user; // TODO: TURN usernames should be length limited by the RFC. // are user account names as well? If so, we can avoid allocating // and instead use a stack buffer. char *usname = (char *)malloc(ulen + 1); if (!usname) { return -1; } strncpy(usname, user, ulen); usname[ulen] = 0; if (SASLprep((uint8_t *)usname) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong user name: %s\n", user); free(usname); return -1; } s = skip_blanks(s + 1); hmackey_t *key = (hmackey_t *)malloc(sizeof(hmackey_t)); if (!key) { free(usname); return -1; } if (strstr(s, "0x") == s) { char *keysource = s + 2; size_t sz = get_hmackey_size(SHATYPE_DEFAULT); if (strlen(keysource) < sz * 2) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: %s\n", s); } convert_string_key_to_binary(keysource, *key, sz); } else { // this is only for default realm stun_produce_integrity_key_str((uint8_t *)usname, (uint8_t *)get_realm(NULL)->options.name, (uint8_t *)s, *key, SHATYPE_DEFAULT); } // the ur_string_map functions only fail (well... other than allocation failures, which aren't handled) // if the map isn't valid. So we only need to check the result of locking. if (!ur_string_map_lock(turn_params.default_users_db.ram_db.static_accounts)) { free(usname); free(key); return -1; } // key argument (the usname variable) is deep-copied, so ownership isn't transfered, and we still need to free usname // later.. value argument (the key variable) has ownership transfered into this function ur_string_map_put(turn_params.default_users_db.ram_db.static_accounts, (ur_string_map_key_type)usname, (ur_string_map_value_type)*key); ur_string_map_unlock(turn_params.default_users_db.ram_db.static_accounts); turn_params.default_users_db.ram_db.users_number++; free(usname); return 0; } ////////////////// Admin ///////////////////////// static int list_users(uint8_t *realm, int is_admin) { const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd) { if (is_admin) { if (dbd->list_admin_users) { (*dbd->list_admin_users)(0); } } else { if (dbd->list_users) { (*dbd->list_users)(realm, NULL, NULL); } } } return 0; } static int show_secret(uint8_t *realm) { const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->list_secrets) { (*dbd->list_secrets)(realm, NULL, NULL); } return 0; } static int del_secret(uint8_t *secret, uint8_t *realm) { must_set_admin_realm(realm); const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->del_secret) { (*dbd->del_secret)(secret, realm); } return 0; } static int set_secret(uint8_t *secret, uint8_t *realm) { if (!secret || (secret[0] == 0)) { return 0; } must_set_admin_realm(realm); del_secret(secret, realm); const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->set_secret) { (*dbd->set_secret)(secret, realm); } return 0; } static int add_origin(uint8_t *origin0, uint8_t *realm) { uint8_t origin[STUN_MAX_ORIGIN_SIZE + 1]; get_canonic_origin((const char *)origin0, (char *)origin, sizeof(origin) - 1); const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->add_origin) { (*dbd->add_origin)(origin, realm); } return 0; } static int del_origin(uint8_t *origin0) { uint8_t origin[STUN_MAX_ORIGIN_SIZE + 1]; get_canonic_origin((const char *)origin0, (char *)origin, sizeof(origin) - 1); const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->del_origin) { (*dbd->del_origin)(origin); } return 0; } static int list_origins(uint8_t *realm) { const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->list_origins) { (*dbd->list_origins)(realm, NULL, NULL); } return 0; } static int set_realm_option_one(uint8_t *realm, unsigned long value, const char *opt) { if (value == (unsigned long)-1) { return 0; } const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->set_realm_option_one) { (*dbd->set_realm_option_one)(realm, value, opt); } return 0; } static int set_realm_option(uint8_t *realm, perf_options_t *po) { set_realm_option_one(realm, (unsigned long)po->max_bps, "max-bps"); set_realm_option_one(realm, (unsigned long)po->user_quota, "user-quota"); set_realm_option_one(realm, (unsigned long)po->total_quota, "total-quota"); return 0; } static int list_realm_options(uint8_t *realm) { const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->list_realm_options) { (*dbd->list_realm_options)(realm); } return 0; } int adminuser(uint8_t *user, uint8_t *realm, uint8_t *pwd, uint8_t *secret, uint8_t *origin, TURNADMIN_COMMAND_TYPE ct, perf_options_t *po, int is_admin) { hmackey_t key; char skey[sizeof(hmackey_t) * 2 + 1]; if (ct == TA_LIST_USERS) { return list_users(realm, is_admin); } if (ct == TA_LIST_ORIGINS) { return list_origins(realm); } if (ct == TA_SHOW_SECRET) { return show_secret(realm); } if (ct == TA_SET_SECRET) { return set_secret(secret, realm); } if (ct == TA_DEL_SECRET) { return del_secret(secret, realm); } if (ct == TA_ADD_ORIGIN) { must_set_admin_origin(origin); must_set_admin_realm(realm); return add_origin(origin, realm); } if (ct == TA_DEL_ORIGIN) { must_set_admin_origin(origin); return del_origin(origin); } if (ct == TA_SET_REALM_OPTION) { must_set_admin_realm(realm); if (!(po && (po->max_bps != ((band_limit_t)-1) || po->total_quota >= 0 || po->user_quota >= 0))) { fprintf(stderr, "The operation cannot be completed: a realm option must be set.\n"); exit(-1); } return set_realm_option(realm, po); } if (ct == TA_LIST_REALM_OPTIONS) { return list_realm_options(realm); } must_set_admin_user(user); if (ct != TA_DELETE_USER && !is_admin) { must_set_admin_pwd(pwd); { stun_produce_integrity_key_str(user, realm, pwd, key, SHATYPE_DEFAULT); size_t i = 0; size_t sz = get_hmackey_size(SHATYPE_DEFAULT); int maxsz = (int)(sz * 2) + 1; char *s = skey; for (i = 0; (i < sz) && (maxsz > 2); i++) { snprintf(s, (size_t)(sz * 2), "%02x", (unsigned int)key[i]); maxsz -= 2; s += 2; } skey[sz * 2] = 0; } } const turn_dbdriver_t *dbd = get_dbdriver(); if (ct == TA_PRINT_KEY) { printf("0x%s\n", skey); } else if (dbd) { if (!is_admin) { must_set_admin_realm(realm); } if (ct == TA_DELETE_USER) { if (is_admin) { if (dbd->del_admin_user) { (*dbd->del_admin_user)(user); } } else { if (dbd->del_user) { (*dbd->del_user)(user, realm); } } } else if (ct == TA_UPDATE_USER) { if (is_admin) { must_set_admin_pwd(pwd); if (dbd->set_admin_user) { password_t password; generate_new_enc_password((char *)pwd, (char *)password); (*dbd->set_admin_user)(user, realm, password); } } else { if (dbd->set_user_key) { (*dbd->set_user_key)(user, realm, skey); } } } } return 0; } /////////// PING ////////////// void auth_ping(redis_context_handle rch) { const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->auth_ping) { (*dbd->auth_ping)(rch); } } ///////////////// TEST ///////////////// #if defined(DB_TEST) void run_db_test(void) { turn_dbdriver_t *dbd = get_dbdriver(); if (dbd) { printf("DB TEST 1:\n"); dbd->list_oauth_keys(); printf("DB TEST 2:\n"); oauth_key_data_raw key_; oauth_key_data_raw *key = &key_; dbd->get_oauth_key((const uint8_t *)"north", key); printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg); printf("DB TEST 3:\n"); STRCPY(key->as_rs_alg, "as_rs_alg"); STRCPY(key->ikm_key, "ikm_key"); STRCPY(key->kid, "kid"); key->timestamp = 123; key->lifetime = 456; dbd->del_oauth_key((const uint8_t *)"kid"); dbd->set_oauth_key(key); dbd->list_oauth_keys(); printf("DB TEST 4:\n"); dbd->get_oauth_key((const uint8_t *)"kid", key); printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", key->kid, key->ikm_key, (unsigned long long)key->timestamp, (unsigned long)key->lifetime, key->as_rs_alg); printf("DB TEST 5:\n"); dbd->del_oauth_key((const uint8_t *)"kid"); dbd->list_oauth_keys(); printf("DB TEST 6:\n"); dbd->get_oauth_key((const uint8_t *)"north", key); oauth_key_data oakd; convert_oauth_key_data_raw(key, &oakd); printf(" kid=%s, ikm_key=%s, timestamp=%llu, lifetime=%lu, as_rs_alg=%s\n", oakd.kid, oakd.ikm_key, (unsigned long long)oakd.timestamp, (unsigned long)oakd.lifetime, oakd.as_rs_alg); oauth_key oak; char err_msg[1025]; err_msg[0] = 0; if (convert_oauth_key_data(&oakd, &oak, err_msg, sizeof(err_msg) - 1) < 0) { printf(" ERROR: %s\n", err_msg); } else { printf(" OK!\n"); } printf("DB TEST END\n"); } } #endif ///////////////// WHITE/BLACK IP LISTS /////////////////// #if !defined(TURN_NO_RWLOCK) static pthread_rwlock_t *whitelist_rwlock = NULL; static pthread_rwlock_t *blacklist_rwlock = NULL; #else static TURN_MUTEX_DECLARE(whitelist_mutex); static TURN_MUTEX_DECLARE(blacklist_mutex); #endif static ip_range_list_t *ipwhitelist = NULL; static ip_range_list_t *ipblacklist = NULL; void init_dynamic_ip_lists(void) { #if !defined(TURN_NO_RWLOCK) whitelist_rwlock = (pthread_rwlock_t *)malloc(sizeof(pthread_rwlock_t)); pthread_rwlock_init(whitelist_rwlock, NULL); blacklist_rwlock = (pthread_rwlock_t *)malloc(sizeof(pthread_rwlock_t)); pthread_rwlock_init(blacklist_rwlock, NULL); #else TURN_MUTEX_INIT(&whitelist_mutex); TURN_MUTEX_INIT(&blacklist_mutex); #endif } void ioa_lock_whitelist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_rdlock(whitelist_rwlock); #else TURN_MUTEX_LOCK(&whitelist_mutex); #endif } void ioa_unlock_whitelist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_unlock(whitelist_rwlock); #else TURN_MUTEX_UNLOCK(&whitelist_mutex); #endif } static void ioa_wrlock_whitelist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_wrlock(whitelist_rwlock); #else TURN_MUTEX_LOCK(&whitelist_mutex); #endif } const ip_range_list_t *ioa_get_whitelist(ioa_engine_handle e) { UNUSED_ARG(e); return ipwhitelist; } void ioa_lock_blacklist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_rdlock(blacklist_rwlock); #else TURN_MUTEX_LOCK(&blacklist_mutex); #endif } void ioa_unlock_blacklist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_unlock(blacklist_rwlock); #else TURN_MUTEX_UNLOCK(&blacklist_mutex); #endif } static void ioa_wrlock_blacklist(ioa_engine_handle e) { UNUSED_ARG(e); #if !defined(TURN_NO_RWLOCK) pthread_rwlock_wrlock(blacklist_rwlock); #else TURN_MUTEX_LOCK(&blacklist_mutex); #endif } const ip_range_list_t *ioa_get_blacklist(ioa_engine_handle e) { UNUSED_ARG(e); return ipblacklist; } ip_range_list_t *get_ip_list(const char *kind) { ip_range_list_t *ret = (ip_range_list_t *)calloc(sizeof(ip_range_list_t), 1); const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->get_ip_list && !turn_params.no_dynamic_ip_list) { (*dbd->get_ip_list)(kind, ret); } return ret; } void ip_list_free(ip_range_list_t *l) { if (l) { if (l->rs) { free(l->rs); } free(l); } } void update_white_and_black_lists(void) { { ip_range_list_t *wl = get_ip_list("allowed"); ip_range_list_t *owl = NULL; ioa_wrlock_whitelist(NULL); owl = ipwhitelist; ipwhitelist = wl; ioa_unlock_whitelist(NULL); ip_list_free(owl); } { ip_range_list_t *bl = get_ip_list("denied"); ip_range_list_t *obl = NULL; ioa_wrlock_blacklist(NULL); obl = ipblacklist; ipblacklist = bl; ioa_unlock_blacklist(NULL); ip_list_free(obl); } } /////////////// add ACL record /////////////////// int add_ip_list_range(const char *range0, const char *realm, ip_range_list_t *list) { char *range = strdup(range0); char *separator = strchr(range, '-'); if (separator) { *separator = '\0'; } ioa_addr min, max; if (make_ioa_addr((const uint8_t *)range, 0, &min) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", range); free(range); return -1; } if (separator) { if (make_ioa_addr((const uint8_t *)separator + 1, 0, &max) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address format: %s\n", separator + 1); free(range); return -1; } } else { // Doesn't have a '-' character in it, so assume that this is a single address addr_cpy(&max, &min); } if (separator) { *separator = '-'; } ++(list->ranges_number); list->rs = (ip_range_t *)realloc(list->rs, sizeof(ip_range_t) * list->ranges_number); STRCPY(list->rs[list->ranges_number - 1].str, range); if (realm) { STRCPY(list->rs[list->ranges_number - 1].realm, realm); } else { list->rs[list->ranges_number - 1].realm[0] = 0; } free(range); ioa_addr_range_set(&(list->rs[list->ranges_number - 1].enc), &min, &max); return 0; } int check_ip_list_range(const char *range0) { char *range = strdup(range0); char *separator = strchr(range, '-'); if (separator) { *separator = '\0'; } ioa_addr min, max; if (make_ioa_addr((const uint8_t *)range, 0, &min) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", range); free(range); return -1; } if (separator) { if (make_ioa_addr((const uint8_t *)separator + 1, 0, &max) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong address range format: %s\n", separator + 1); free(range); return -1; } } else { // Doesn't have a '-' character in it, so assume that this is a single address addr_cpy(&max, &min); } if (separator) { *separator = '-'; } free(range); return 0; } /////////// REALM ////////////// void reread_realms(void) { { realm_params_t *defrp = get_realm(NULL); lock_realms(); defrp->options.perf_options.max_bps = turn_params.max_bps; defrp->options.perf_options.total_quota = turn_params.total_quota; defrp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); } const turn_dbdriver_t *dbd = get_dbdriver(); if (dbd && dbd->reread_realms && !turn_params.no_dynamic_realms) { (*dbd->reread_realms)(&realms_list); } } ///////////////////////////////