/* * 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 "../mainrelay.h" #include "dbd_mongo.h" #if !defined(TURN_NO_MONGO) #include #include /////////////////////////////////////////////////////////////////////////////////////////////////////////// const char * MONGO_DEFAULT_DB = "turn"; struct _MONGO { mongoc_uri_t * uri; mongoc_client_t * client; const char * database; }; typedef struct _MONGO MONGO; static void mongo_logger(mongoc_log_level_t log_level, const char * log_domain, const char * message, void * user_data) { UNUSED_ARG(log_domain); UNUSED_ARG(user_data); TURN_LOG_LEVEL l = TURN_LOG_LEVEL_INFO; UNUSED_ARG(l); switch(log_level) { case MONGOC_LOG_LEVEL_ERROR: l = TURN_LOG_LEVEL_ERROR; break; case MONGOC_LOG_LEVEL_WARNING: l = TURN_LOG_LEVEL_WARNING; break; default: l = TURN_LOG_LEVEL_INFO; break; } TURN_LOG_FUNC(l, "%s\n", message); } static void MongoFree(MONGO * info) { if(info) { if(info->uri) mongoc_uri_destroy(info->uri); if(info->client) mongoc_client_destroy(info->client); free(info); } } static MONGO * get_mongodb_connection(void) { persistent_users_db_t * pud = get_persistent_users_db(); MONGO * mydbconnection = (MONGO *) pthread_getspecific(connection_key); if (!mydbconnection) { mongoc_init(); mongoc_log_set_handler(&mongo_logger, NULL); mydbconnection = (MONGO *) malloc(sizeof(MONGO)); bzero(mydbconnection, sizeof(MONGO)); mydbconnection->uri = mongoc_uri_new(pud->userdb); if (!mydbconnection->uri) { TURN_LOG_FUNC( TURN_LOG_LEVEL_ERROR, "Cannot open parse MongoDB URI <%s>, connection string format error\n", pud->userdb); MongoFree(mydbconnection); mydbconnection = NULL; } else { mydbconnection->client = mongoc_client_new_from_uri( mydbconnection->uri); if (!mydbconnection->client) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Cannot initialize MongoDB connection\n"); MongoFree(mydbconnection); mydbconnection = NULL; } else { mydbconnection->database = mongoc_uri_get_database( mydbconnection->uri); if (!mydbconnection->database) mydbconnection->database = MONGO_DEFAULT_DB; if(mydbconnection) { (void) pthread_setspecific(connection_key, mydbconnection); } TURN_LOG_FUNC( TURN_LOG_LEVEL_INFO, "Opened MongoDB URI <%s>\n", pud->userdb); } } } return mydbconnection; } static mongoc_collection_t * mongo_get_collection(const char * name) { MONGO * mc = get_mongodb_connection(); if(!mc) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error getting a connection to MongoDB\n"); return NULL; } mongoc_collection_t * collection; collection = mongoc_client_get_collection(mc->client, mc->database, name); if (!collection) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error retrieving MongoDB collection '%s'\n", name); } return collection; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// static int mongo_get_auth_secrets(secrets_list_t *sl, uint8_t *realm) { mongoc_collection_t * collection = mongo_get_collection("turn_secret"); if(!collection) return -1; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "value", 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 'turn_secret'\n"); } else { const bson_t * item; uint32_t length; bson_iter_t iter; const char * value; while(mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "value") && BSON_ITER_HOLDS_UTF8(&iter)) { value = bson_iter_utf8(&iter, &length); add_to_secrets_list(sl, value); } } mongoc_cursor_destroy(cursor); ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static int mongo_get_user_key(uint8_t *usname, uint8_t *realm, hmackey_t key) { mongoc_collection_t * collection = mongo_get_collection("turnusers_lt"); if(!collection) return -1; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "name", (const char *)usname); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "hmackey", 1); mongoc_cursor_t * cursor; cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0, &query, &fields, NULL); int ret = -1; if (!cursor) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'turnusers_lt'\n"); } else { const bson_t * item; uint32_t length; bson_iter_t iter; const char * value; if (mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "hmackey") && BSON_ITER_HOLDS_UTF8(&iter)) { value = bson_iter_utf8(&iter, &length); size_t sz = get_hmackey_size(SHATYPE_DEFAULT) * 2; if(length < sz) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key format: string length=%d (must be %d): user %s\n", (int)length, (int)sz, usname); } else { char kval[sizeof(hmackey_t) + sizeof(hmackey_t) + 1]; bcopy(value, kval, sz); kval[sz] = 0; if(convert_string_key_to_binary(kval, key, sz / 2) < 0) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Wrong key: %s, user %s\n", kval, usname); } else { ret = 0; } } } } mongoc_cursor_destroy(cursor); } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static int mongo_get_oauth_key(const uint8_t *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 *)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, "realm", 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; 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, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(key->realm,bson_iter_utf8(&iter, &length)); } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ikm_key") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(key->ikm_key,bson_iter_utf8(&iter, &length)); } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) { key->timestamp = (uint64_t)bson_iter_int64(&iter); } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) { key->lifetime = (uint32_t)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_set_user_key(uint8_t *usname, uint8_t *realm, const char *key) { mongoc_collection_t * collection = mongo_get_collection("turnusers_lt"); if(!collection) return -1; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "name", (const char *)usname); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); bson_t doc; bson_init(&doc); BSON_APPEND_UTF8(&doc, "name", (const char *)usname); BSON_APPEND_UTF8(&doc, "realm", (const char *)realm); BSON_APPEND_UTF8(&doc, "hmackey", (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 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(&doc, "kid", (const char *)key->kid); BSON_APPEND_UTF8(&doc, "as_rs_alg", (const char *)key->as_rs_alg); BSON_APPEND_UTF8(&doc, "realm", (const char *)key->realm); BSON_APPEND_UTF8(&doc, "ikm_key", (const char *)key->ikm_key); BSON_APPEND_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; } mongoc_collection_destroy(collection); bson_destroy(&doc); bson_destroy(&query); return ret; } static int mongo_del_user(uint8_t *usname, uint8_t *realm) { mongoc_collection_t * collection = mongo_get_collection("turnusers_lt"); if(!collection) return -1; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "name", (const char *)usname); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); int ret = -1; if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting user key information\n"); } else { ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); return ret; } static int mongo_del_oauth_key(const uint8_t *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(uint8_t *realm, secrets_list_t *users, secrets_list_t *realms) { const char * collection_name = "turnusers_lt"; mongoc_collection_t * collection = mongo_get_collection(collection_name); uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; if(!collection) return -1; bson_t query, child; bson_init(&query); bson_append_document_begin(&query, "$orderby", -1, &child); bson_append_int32(&child, "realm", -1, 1); bson_append_int32(&child, "name", -1, 1); bson_append_document_end(&query, &child); bson_append_document_begin(&query, "$query", -1, &child); if (realm && realm[0]) { BSON_APPEND_UTF8(&child, "realm", (const char *)realm); } bson_append_document_end(&query, &child); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "name", 1); BSON_APPEND_INT32(&fields, "realm", 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; uint32_t length; bson_iter_t iter; bson_iter_t iter_realm; const char * value; while (mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "name") && BSON_ITER_HOLDS_UTF8(&iter)) { value = bson_iter_utf8(&iter, &length); if (length) { const char *rval = ""; if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) { rval = bson_iter_utf8(&iter_realm, &length); } if(users) { add_to_secrets_list(users,value); if(realms) { if(rval && *rval) { add_to_secrets_list(realms,rval); } else { add_to_secrets_list(realms,(char*)realm); } } } else { printf("%s[%s]\n", value, rval); } } } } mongoc_cursor_destroy(cursor); ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static int mongo_list_oauth_keys(secrets_list_t *kids,secrets_list_t *teas,secrets_list_t *tss,secrets_list_t *lts,secrets_list_t *realms) { const char * collection_name = "oauth_key"; mongoc_collection_t * collection = mongo_get_collection(collection_name); if(!collection) return -1; bson_t query; bson_init(&query); bson_t child; bson_append_document_begin(&query, "$orderby", -1, &child); bson_append_int32(&child, "kid", -1, 1); bson_append_document_end(&query, &child); bson_append_document_begin(&query, "$query", -1, &child); 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, "realm", 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)) { bzero(key,sizeof(oauth_key_data_raw)); 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, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(key->realm,bson_iter_utf8(&iter, &length)); } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "ikm_key") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(key->ikm_key,bson_iter_utf8(&iter, &length)); } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "timestamp") && BSON_ITER_HOLDS_INT64(&iter)) { key->timestamp = (uint64_t)bson_iter_int64(&iter); } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "lifetime") && BSON_ITER_HOLDS_INT32(&iter)) { key->lifetime = (uint32_t)bson_iter_int32(&iter); } 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); } } mongoc_cursor_destroy(cursor); ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static int mongo_list_secrets(uint8_t *realm, secrets_list_t *secrets, secrets_list_t *realms) { mongoc_collection_t * collection = mongo_get_collection("turn_secret"); uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; if(!collection) return -1; bson_t query, child; bson_init(&query); bson_append_document_begin(&query, "$orderby", -1, &child); bson_append_int32(&child, "realm", -1, 1); bson_append_int32(&child, "value", -1, 1); bson_append_document_end(&query, &child); bson_append_document_begin(&query, "$query", -1, &child); if (realm && realm[0]) { BSON_APPEND_UTF8(&child, "realm", (const char *)realm); } bson_append_document_end(&query, &child); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "value", 1); BSON_APPEND_INT32(&fields, "realm", 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 'turn_secret'\n"); } else { const bson_t * item; uint32_t length; bson_iter_t iter; bson_iter_t iter_realm; const char * value; while (mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "value") && BSON_ITER_HOLDS_UTF8(&iter)) { value = bson_iter_utf8(&iter, &length); if (length) { const char *rval = ""; if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) { rval = bson_iter_utf8(&iter_realm, &length); } if(secrets) { add_to_secrets_list(secrets,value); if(realms) { if(rval && *rval) { add_to_secrets_list(realms,rval); } else { add_to_secrets_list(realms,(char*)realm); } } } else { printf("%s[%s]\n", value, rval); } } } } mongoc_cursor_destroy(cursor); ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static int mongo_del_secret(uint8_t *secret, uint8_t *realm) { mongoc_collection_t * collection = mongo_get_collection("turn_secret"); if(!collection) return -1; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); if(secret && (secret[0]!=0)) { BSON_APPEND_UTF8(&query, "value", (const char *)secret); } mongoc_collection_delete(collection, MONGOC_DELETE_NONE, &query, NULL, NULL); mongoc_collection_destroy(collection); bson_destroy(&query); return 0; } static int mongo_set_secret(uint8_t *secret, uint8_t *realm) { mongoc_collection_t * collection = mongo_get_collection("turn_secret"); if(!collection) return -1; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); BSON_APPEND_UTF8(&query, "value", (const char *)secret); int res = mongoc_collection_insert(collection, MONGOC_INSERT_NONE, &query, NULL, NULL); mongoc_collection_destroy(collection); bson_destroy(&query); if (!res) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating secret key information\n"); return -1; } else { return 0; } } static int mongo_set_permission_ip(const char *kind, uint8_t *realm, const char* ip, int del) { char sub_collection_name[129]; snprintf(sub_collection_name,sizeof(sub_collection_name)-1,"%s_peer_ip",kind); mongoc_collection_t * collection = mongo_get_collection("realm"); if(!collection) return -1; int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; bson_t query, doc, child; bson_init(&query); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); bson_init(&doc); if(del) { bson_append_document_begin(&doc, "$pull", -1, &child); } else { bson_append_document_begin(&doc, "$addToSet", -1, &child); } BSON_APPEND_UTF8(&child, sub_collection_name, (const char *)ip); bson_append_document_end(&doc, &child); mongoc_update_flags_t flags = MONGOC_UPDATE_NONE; if(del) { flags = MONGOC_UPDATE_MULTI_UPDATE; } else { flags = MONGOC_UPDATE_UPSERT; } if (!mongoc_collection_update(collection, flags, &query, &doc, NULL, NULL)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting permission ip information\n"); } else { ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&doc); return ret; } static int mongo_add_origin(uint8_t *origin, uint8_t *realm) { mongoc_collection_t * collection = mongo_get_collection("realm"); if(!collection) return -1; int ret = -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; bson_t query, doc, child; bson_init(&query); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); bson_init(&doc); bson_append_document_begin(&doc, "$addToSet", -1, &child); BSON_APPEND_UTF8(&child, "origin", (const char *)origin); bson_append_document_end(&doc, &child); if (!mongoc_collection_update(collection, MONGOC_UPDATE_UPSERT, &query, &doc, NULL, NULL)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error inserting/updating realm origin information\n"); } else { ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&doc); return ret; } static int mongo_del_origin(uint8_t *origin) { mongoc_collection_t * collection = mongo_get_collection("realm"); if(!collection) return -1; int ret = -1; bson_t query, doc, child; bson_init(&query); bson_init(&doc); bson_append_document_begin(&doc, "$pull", -1, &child); BSON_APPEND_UTF8(&child, "origin", (const char *)origin); bson_append_document_end(&doc, &child); if (!mongoc_collection_update(collection, MONGOC_UPDATE_MULTI_UPDATE, &query, &doc, NULL, NULL)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information\n"); } else { ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&doc); return ret; } static int mongo_list_origins(uint8_t *realm, secrets_list_t *origins, secrets_list_t *realms) { mongoc_collection_t * collection = mongo_get_collection("realm"); if(!collection) return -1; uint8_t realm0[STUN_MAX_REALM_SIZE+1] = "\0"; if(!realm) realm=realm0; bson_t query, child; bson_init(&query); bson_append_document_begin(&query, "$orderby", -1, &child); BSON_APPEND_INT32(&child, "realm", 1); bson_append_document_end(&query, &child); bson_append_document_begin(&query, "$query", -1, &child); if (realm && realm[0]) { BSON_APPEND_UTF8(&child, "realm", (const char *)realm); } bson_append_document_end(&query, &child); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "origin", 1); BSON_APPEND_INT32(&fields, "realm", 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 'realm'\n"); } else { const bson_t * item; uint32_t length; bson_iter_t iter; while (mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { const char * _realm = bson_iter_utf8(&iter, &length); if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "origin") && BSON_ITER_HOLDS_ARRAY(&iter)) { const uint8_t *docbuf = NULL; uint32_t doclen = 0; bson_t origin_array; bson_iter_t origin_iter; bson_iter_array(&iter, &doclen, &docbuf); bson_init_static(&origin_array, docbuf, doclen); if (bson_iter_init(&origin_iter, &origin_array)) { while(bson_iter_next(&origin_iter)) { if (BSON_ITER_HOLDS_UTF8(&origin_iter)) { const char * _origin = bson_iter_utf8(&origin_iter, &length); if(origins) { add_to_secrets_list(origins,_origin); if(realms) { add_to_secrets_list(realms,_realm); } } else { printf("%s ==>> %s\n", _realm, _origin); } } } } } } } mongoc_cursor_destroy(cursor); ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static int mongo_set_realm_option_one(uint8_t *realm, unsigned long value, const char* opt) { mongoc_collection_t * collection = mongo_get_collection("realm"); if(!collection) return -1; bson_t query, doc, child; bson_init(&query); BSON_APPEND_UTF8(&query, "realm", (const char *)realm); bson_init(&doc); size_t klen = 9 + strlen(opt); char * _k = (char *)malloc(klen); strcpy(_k, "options."); strcat(_k, opt); if (value > 0) { bson_append_document_begin(&doc, "$set", -1, &child); BSON_APPEND_INT32(&child, _k, (int32_t)value); bson_append_document_end(&doc, &child); } else { bson_append_document_begin(&doc, "$unset", -1, &child); BSON_APPEND_INT32(&child, _k, 1); bson_append_document_end(&doc, &child); } free(_k); int ret = -1; if (!mongoc_collection_update(collection, MONGOC_UPDATE_MULTI_UPDATE, &query, &doc, NULL, NULL)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting origin information\n"); } else { ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&doc); return ret; } static int mongo_list_realm_options(uint8_t *realm) { mongoc_collection_t * collection = mongo_get_collection("realm"); if(!collection) return -1; bson_t query, child; bson_init(&query); bson_append_document_begin(&query, "$orderby", -1, &child); BSON_APPEND_INT32(&child, "realm", 1); bson_append_document_end(&query, &child); bson_append_document_begin(&query, "$query", -1, &child); if (realm && realm[0]) { BSON_APPEND_UTF8(&child, "realm", (const char *)realm); } bson_append_document_end(&query, &child); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "options", 1); BSON_APPEND_INT32(&fields, "realm", 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 'realm'\n"); } else { const bson_t * item; uint32_t length; bson_iter_t iter; while (mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { const char * _realm = bson_iter_utf8(&iter, &length); if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "options") && BSON_ITER_HOLDS_DOCUMENT(&iter)) { const uint8_t *docbuf = NULL; uint32_t doclen = 0; bson_t options; bson_iter_t options_iter; bson_iter_document(&iter, &doclen, &docbuf); bson_init_static(&options, docbuf, doclen); if (bson_iter_init(&options_iter, &options)) { while(bson_iter_next(&options_iter)) { const char * _k = bson_iter_key(&options_iter); if (BSON_ITER_HOLDS_DOUBLE(&options_iter)) { int32_t _v = (int32_t)bson_iter_double(&options_iter); printf("%s[%s]=%d\n", _k, _realm, _v); } else if (BSON_ITER_HOLDS_INT32(&options_iter)) { int32_t _v = bson_iter_int32(&options_iter); printf("%s[%s]=%d\n", _k, _realm, _v); } else if (BSON_ITER_HOLDS_INT64(&options_iter)) { int32_t _v = (int32_t)bson_iter_int64(&options_iter); printf("%s[%s]=%d\n", _k, _realm, _v); } } } } } } mongoc_cursor_destroy(cursor); ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static void mongo_auth_ping(void * rch) { UNUSED_ARG(rch); // NOOP } static int mongo_read_realms_ip_lists(const char *kind, ip_range_list_t * list) { int ret = 0; char field_name[129]; sprintf(field_name, "%s_peer_ip", kind); mongoc_collection_t * collection = mongo_get_collection("realm"); if (!collection) return ret; bson_t query; bson_init(&query); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "realm", 1); BSON_APPEND_INT32(&fields, field_name, 1); mongoc_cursor_t * cursor; cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL); if (!cursor) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'realm'\n"); ret = -1; } else { const bson_t * item; uint32_t length; bson_iter_t iter; char realm[513]; while (mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { STRCPY(realm,bson_iter_utf8(&iter, &length)); if (bson_iter_init(&iter, item) && bson_iter_find(&iter, field_name) && BSON_ITER_HOLDS_ARRAY(&iter)) { const uint8_t *docbuf = NULL; uint32_t doclen = 0; bson_t ip_range_array; bson_iter_t ip_range_iter; bson_iter_array(&iter, &doclen, &docbuf); bson_init_static(&ip_range_array, docbuf, doclen); if (bson_iter_init(&ip_range_iter, &ip_range_array)) { while (bson_iter_next(&ip_range_iter)) { if (BSON_ITER_HOLDS_UTF8(&ip_range_iter)) { const char* ip_range = bson_iter_utf8(&ip_range_iter, &length); add_ip_list_range(ip_range, realm, list); } } } } } } mongoc_cursor_destroy(cursor); } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static int mongo_get_ip_list(const char *kind, ip_range_list_t * list) { return mongo_read_realms_ip_lists(kind, list); } static void mongo_reread_realms(secrets_list_t * realms_list) { UNUSED_ARG(realms_list); mongoc_collection_t * collection = mongo_get_collection("realm"); if (!collection) return; bson_t query; bson_init(&query); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "realm", 1); BSON_APPEND_INT32(&fields, "origin", 1); BSON_APPEND_INT32(&fields, "options", 1); mongoc_cursor_t * cursor; cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 0, 0, &query, &fields, NULL); if (!cursor) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'realm'\n"); } else { ur_string_map *o_to_realm_new = ur_string_map_create(free); const bson_t * item; uint32_t length; bson_iter_t iter; while (mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { char * _realm = strdup(bson_iter_utf8(&iter, &length)); get_realm(_realm); if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "origin") && BSON_ITER_HOLDS_ARRAY(&iter)) { const uint8_t *docbuf = NULL; uint32_t doclen = 0; bson_t origin_array; bson_iter_t origin_iter; bson_iter_array(&iter, &doclen, &docbuf); bson_init_static(&origin_array, docbuf, doclen); if (bson_iter_init(&origin_iter, &origin_array)) { while (bson_iter_next(&origin_iter)) { if (BSON_ITER_HOLDS_UTF8(&origin_iter)) { char* _origin = strdup(bson_iter_utf8(&origin_iter, &length)); char *rval = strdup(_realm); ur_string_map_value_type value = (ur_string_map_value_type) (rval); ur_string_map_put(o_to_realm_new, (ur_string_map_key_type) _origin, value); free(_origin); } } } } realm_params_t* rp = get_realm(_realm); lock_realms(); rp->options.perf_options.max_bps = turn_params.max_bps; rp->options.perf_options.total_quota = turn_params.total_quota; rp->options.perf_options.user_quota = turn_params.user_quota; unlock_realms(); if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "options") && BSON_ITER_HOLDS_DOCUMENT(&iter)) { const uint8_t *docbuf = NULL; uint32_t doclen = 0; bson_t options; bson_iter_t options_iter; bson_iter_document(&iter, &doclen, &docbuf); bson_init_static(&options, docbuf, doclen); if (bson_iter_init(&options_iter, &options)) { while (bson_iter_next(&options_iter)) { const char * _k = bson_iter_key(&options_iter); uint64_t _v = 0; if (BSON_ITER_HOLDS_DOUBLE(&options_iter)) { _v = (uint64_t) bson_iter_double(&options_iter); } else if (BSON_ITER_HOLDS_INT32(&options_iter)) { _v = (uint64_t)bson_iter_int32(&options_iter); } else if (BSON_ITER_HOLDS_INT64(&options_iter)) { _v = (uint64_t) bson_iter_int64(&options_iter); } if (_v) { if (!strcmp(_k, "max-bps")) rp->options.perf_options.max_bps = (band_limit_t) _v; else if (!strcmp(_k, "total-quota")) rp->options.perf_options.total_quota = (vint) _v; else if (!strcmp(_k, "user-quota")) rp->options.perf_options.user_quota = (vint) _v; else { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Unknown realm option: %s\n", _k); } } } } } free(_realm); } } update_o_to_realm(o_to_realm_new); mongoc_cursor_destroy(cursor); } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); } ///////////////////////////////////////////////// static int mongo_get_admin_user(const uint8_t *usname, uint8_t *realm, password_t pwd) { mongoc_collection_t * collection = mongo_get_collection("admin_user"); if(!collection) return -1; realm[0]=0; pwd[0]=0; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "name", (const char *)usname); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "realm", 1); BSON_APPEND_INT32(&fields, "password", 1); mongoc_cursor_t * cursor; cursor = mongoc_collection_find(collection, MONGOC_QUERY_NONE, 0, 1, 0, &query, &fields, NULL); int ret = -1; if (!cursor) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error querying MongoDB collection 'admin_user'\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, "realm") && BSON_ITER_HOLDS_UTF8(&iter)) { strncpy((char*)realm,bson_iter_utf8(&iter, &length),STUN_MAX_REALM_SIZE); ret = 0; } if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "password") && BSON_ITER_HOLDS_UTF8(&iter)) { strncpy((char*)pwd,bson_iter_utf8(&iter, &length),STUN_MAX_PWD_SIZE); ret = 0; } } mongoc_cursor_destroy(cursor); } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static int mongo_set_admin_user(const uint8_t *usname, const uint8_t *realm, const password_t pwd) { mongoc_collection_t * collection = mongo_get_collection("admin_user"); if(!collection) return -1; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "name", (const char *)usname); bson_t doc; bson_init(&doc); BSON_APPEND_UTF8(&doc, "name", (const char *)usname); BSON_APPEND_UTF8(&doc, "realm", (const char *)realm); BSON_APPEND_UTF8(&doc, "password", (const char *)pwd); 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 admin user information\n"); } else { ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&doc); bson_destroy(&query); return ret; } static int mongo_del_admin_user(const uint8_t *usname) { mongoc_collection_t * collection = mongo_get_collection("admin_user"); if(!collection) return -1; bson_t query; bson_init(&query); BSON_APPEND_UTF8(&query, "name", (const char *)usname); int ret = -1; if (!mongoc_collection_delete(collection, MONGOC_DELETE_SINGLE_REMOVE, &query, NULL, NULL)) { TURN_LOG_FUNC(TURN_LOG_LEVEL_ERROR, "Error deleting admin user information\n"); } else { ret = 0; } mongoc_collection_destroy(collection); bson_destroy(&query); return ret; } static int mongo_list_admin_users(int no_print) { const char * collection_name = "admin_user"; 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, "name", -1, 1); bson_append_document_end(&query, &child); bson_append_document_begin(&query, "$query", -1, &child); bson_append_document_end(&query, &child); bson_t fields; bson_init(&fields); BSON_APPEND_INT32(&fields, "name", 1); BSON_APPEND_INT32(&fields, "realm", 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; uint32_t length; bson_iter_t iter; bson_iter_t iter_realm; const char * value; ret = 0; while (mongoc_cursor_next(cursor, &item)) { if (bson_iter_init(&iter, item) && bson_iter_find(&iter, "name") && BSON_ITER_HOLDS_UTF8(&iter)) { value = bson_iter_utf8(&iter, &length); if (length) { const char *realm = ""; if (bson_iter_init(&iter_realm, item) && bson_iter_find(&iter_realm, "realm") && BSON_ITER_HOLDS_UTF8(&iter_realm)) { realm = bson_iter_utf8(&iter_realm, &length); } ++ret; if(!no_print) { if(realm && *realm) { printf("%s[%s]\n", value, realm); } else { printf("%s\n", value); } } } } } mongoc_cursor_destroy(cursor); } mongoc_collection_destroy(collection); bson_destroy(&query); bson_destroy(&fields); return ret; } static void mongo_disconnect(void) { MONGO * mongoconnection = (MONGO *) pthread_getspecific(connection_key); if (mongoconnection) { MongoFree(mongoconnection); mongoconnection = NULL; } TURN_LOG_FUNC(TURN_LOG_LEVEL_INFO, "MongoDB connection was closed.\n"); } ////////////////////////////////////////////////////////// static const turn_dbdriver_t driver = { &mongo_get_auth_secrets, &mongo_get_user_key, &mongo_set_user_key, &mongo_del_user, &mongo_list_users, &mongo_list_secrets, &mongo_del_secret, &mongo_set_secret, &mongo_add_origin, &mongo_del_origin, &mongo_list_origins, &mongo_set_realm_option_one, &mongo_list_realm_options, &mongo_auth_ping, &mongo_get_ip_list, &mongo_set_permission_ip, &mongo_reread_realms, &mongo_set_oauth_key, &mongo_get_oauth_key, &mongo_del_oauth_key, &mongo_list_oauth_keys, &mongo_get_admin_user, &mongo_set_admin_user, &mongo_del_admin_user, &mongo_list_admin_users, &mongo_disconnect }; const turn_dbdriver_t * get_mongo_dbdriver(void) { return &driver; } /////////////////////////////////////////////////////////////////////////////////////////////////////////// #else const turn_dbdriver_t * get_mongo_dbdriver(void) { return NULL; } #endif