From 6db4877c8755414d576dbc48c8c9063a5ebe57e1 Mon Sep 17 00:00:00 2001 From: Aleksandr Lysikov <lysikov@inbox.ru> Date: Fri, 17 May 2019 15:34:16 +0500 Subject: [PATCH] added auth and db modules --- .gitignore | 55 ++ CMakeLists.txt | 29 ++ auth.c | 6 + auth/CMakeLists.txt | 14 + auth/db_auth.c | 1195 +++++++++++++++++++++++++++++++++++++++++++ auth/db_auth.h | 86 ++++ db/CMakeLists.txt | 15 + db/db_core.c | 82 +++ db/db_core.h | 17 + db/db_http.c | 74 +++ db/db_http.h | 10 + db/db_http_file.c | 250 +++++++++ db/db_http_file.h | 10 + 13 files changed, 1843 insertions(+) create mode 100755 .gitignore create mode 100755 CMakeLists.txt create mode 100755 auth.c create mode 100755 auth/CMakeLists.txt create mode 100755 auth/db_auth.c create mode 100755 auth/db_auth.h create mode 100755 db/CMakeLists.txt create mode 100755 db/db_core.c create mode 100755 db/db_core.h create mode 100755 db/db_http.c create mode 100755 db/db_http.h create mode 100755 db/db_http_file.c create mode 100755 db/db_http_file.h diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..754c5be --- /dev/null +++ b/.gitignore @@ -0,0 +1,55 @@ +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +.tmp_versions/ +modules.order +Module.symvers +Mkfile.old +dkms.conf +/build/ +/.project +/.cproject diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..16ea0b8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 2.8) +project (dap_server_http_db_auth) + +# fix implicit declaration warnings +add_definitions ("-D_GNU_SOURCE") + +set(CMAKE_C_FLAGS "-std=c11 -Wall -Wextra") + + +find_package(PkgConfig) +pkg_check_modules(MONGO REQUIRED libmongoc-1.0) +pkg_check_modules(BSON REQUIRED libbson-1.0) + +add_subdirectory(db) +add_subdirectory(auth) + + +file(GLOB FILE_SOURCES *.c) +file(GLOB FILE_HEADERS *.h) + +add_library(${PROJECT_NAME} STATIC ${FILE_SOURCES} ${FILE_HEADERS}) + +target_link_libraries(${PROJECT_NAME} dap_crypto dap_auth dap_db) +target_include_directories(${PROJECT_NAME} INTERFACE .) + + +#set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE) +# +#set(${PROJECT_NAME}_INCLUDE_DIRS ${PROJECT_SOURCE_DIR} CACHE INTERNAL "${PROJECT_NAME}: Include Directories" FORCE) \ No newline at end of file diff --git a/auth.c b/auth.c new file mode 100755 index 0000000..798e809 --- /dev/null +++ b/auth.c @@ -0,0 +1,6 @@ +#include <db_auth.h> + +int dap_server_http_db_auth_check_key(uint8_t *key) +{ + exist_user_in_db("da"); +} diff --git a/auth/CMakeLists.txt b/auth/CMakeLists.txt new file mode 100755 index 0000000..cf70903 --- /dev/null +++ b/auth/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 3.0) +project (dap_auth) + +set(AUTH_SRCS db_auth.c) + +add_library(${PROJECT_NAME} STATIC ${AUTH_SRCS}) + +target_link_libraries(dap_auth dap_core + dap_crypto dap_http_server dap_enc_server + dap_stream ${MONGO_LIBRARIES}) + +target_include_directories(dap_auth + INTERFACE . + PRIVATE ${MONGO_INCLUDE_DIRS}) diff --git a/auth/db_auth.c b/auth/db_auth.c new file mode 100755 index 0000000..9e61f21 --- /dev/null +++ b/auth/db_auth.c @@ -0,0 +1,1195 @@ +/* + Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc + All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DAP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <sys/wait.h> +#include <rand/dap_rand.h> + +#include <time.h> + +#include "dap_common.h" +#include "dap_client_remote.h" +#include "dap_http_client.h" +#include "dap_enc.h" +#include "dap_enc_key.h" +#include "dap_enc_ks.h" +#include "dap_enc_http.h" +#include "dap_enc_base64.h" +#include "dap_server.h" +#include "../db/db_core.h" +#include "db_auth.h" +#include "http_status_code.h" +#include "mongoc.h" + + +#define LOG_TAG "db_auth" + +#define OP_CODE_LOGIN_INCORRECT_PSWD "0xf2" +#define OP_CODE_NOT_FOUND_LOGIN_IN_DB "0xf3" +#define OP_CODE_SUBSCRIBE_EXPIRIED "0xf4" +#define OP_CODE_INCORRECT_SYMOLS "0xf6" + +static db_auth_info_t *auths = NULL; +static pthread_mutex_t mutex_on_auth_hash = PTHREAD_MUTEX_INITIALIZER; + +static bool mongod_is_running(void); + +static unsigned char* hash_password(const unsigned char* password, + unsigned char* salt, + size_t salt_size); + +static const char *l_db_name; + +int db_auth_init(const char* db_name) +{ + l_db_name = strdup(db_name); + + if(!mongod_is_running()) + { + return -1; + } + + log_it(L_NOTICE,"Initialized authorization module"); + + return 0; +} + +void db_auth_deinit() +{ + free((char*)l_db_name); +} + +static void _save_cpu_monitor_stats_in_db(dap_server_t *srv) +{ + bson_t *doc = BCON_NEW("average_load", + BCON_DOUBLE((double)srv->cpu_stats.cpu_summary.load)); + char scpu[128]; + for(unsigned i = 0; i < srv->cpu_stats.cpu_cores_count; i++) { + + sprintf(scpu, "cpu%d", srv->cpu_stats.cpus[i].ncpu); + BCON_APPEND(doc, scpu, BCON_DOUBLE((double)srv->cpu_stats.cpus[i].load)); + } + + BCON_APPEND(doc, "time", BCON_DATE_TIME(time(NULL) * 1000)); + + mongoc_collection_t * collection = mongoc_client_get_collection + (traffick_track_db_client, l_db_name, "dap_cpu_monitoring"); + + bool is_ok = mongoc_collection_insert(collection, MONGOC_INSERT_NONE, + doc, NULL, NULL); + if(!is_ok) { + log_it(L_ERROR, "Error save cpu stats in mongo"); + } + mongoc_collection_destroy(collection); + bson_destroy(doc); +} + +void db_auth_traffic_track_callback(dap_server_t *srv) +{ + _save_cpu_monitor_stats_in_db(srv); + + db_auth_info_t *client, *tmp; + + pthread_mutex_lock(&mutex_on_auth_hash); + uint32_t auth_count = HASH_COUNT(auths); + if(auth_count == 0) { + pthread_mutex_unlock(&mutex_on_auth_hash); + return; + } + + bson_t *docs[auth_count]; + { + bson_oid_t oid; + size_t idx = 0; + HASH_ITER(hh, auths, client, tmp) { + bson_oid_init_from_string(&oid, client->id); + docs[idx] = BCON_NEW("user_id", BCON_OID(&oid), + "download_speed", BCON_DOUBLE(client->dap_http_client->client->download_stat.speed_mbs), + "upload_speed", BCON_DOUBLE(client->dap_http_client->client->upload_stat.speed_mbs), + "download_bytes", BCON_INT64(client->dap_http_client->client->download_stat.buf_size_total), + "upload_bytes", BCON_INT64(client->dap_http_client->client->upload_stat.buf_size_total), + "auth_date", BCON_DATE_TIME(client->auth_date * 1000)); + // log_it(L_DEBUG, "%s", bson_as_json(docs[idx], NULL)); + idx++; + } + } + pthread_mutex_unlock(&mutex_on_auth_hash); + + mongoc_collection_t * collection = mongoc_client_get_collection (traffick_track_db_client, l_db_name, "dap_traffic_stats"); + + bool insert_ok = mongoc_collection_insert_bulk(collection, MONGOC_INSERT_NONE, docs, auth_count, NULL, NULL); + if(!insert_ok) { + log_it(L_ERROR, "Can't insert documents in databse"); + } + + for(uint32_t i = 0; i < auth_count; i++) + bson_destroy(docs[i]); + + mongoc_collection_destroy(collection); +} + +/** + * @brief db_auth_info_by_cookie Find user by its cookie + * @param cookie Cookie + * @return Zero if this cookie is not present + */ +db_auth_info_t* db_auth_info_by_cookie(const char * cookie) +{ + db_auth_info_t * ret = NULL; + + if ( cookie == NULL ) + { + log_it(L_ERROR, "cookie is NULL in db_auth_info_by_cookie"); + return NULL; + } + pthread_mutex_lock(&mutex_on_auth_hash); + HASH_FIND_STR(auths, cookie, ret); + pthread_mutex_unlock(&mutex_on_auth_hash); + if(ret == NULL) + log_it(L_NOTICE,"Cookie '%s' not present in the table",cookie); + else + log_it(L_INFO,"Cookie '%s' has been found in the table",cookie); + return ret; +} + +db_auth_info_t* db_search_cookie_in_db(const char * cookie) +{ + mongoc_collection_t* collection_cookie = mongoc_client_get_collection + (mongo_client, l_db_name, "dap_cookie_history"); + + bson_t *query = bson_new(); + + BSON_APPEND_UTF8 (query, "cookie", cookie); + + mongoc_cursor_t *cursor_dap_cookie = mongoc_collection_find + (collection_cookie, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL); + + bson_t *doc_dap_cookie_user; + + if( mongoc_cursor_next (cursor_dap_cookie, (const bson_t**)&doc_dap_cookie_user) == false ) + { + log_it(L_INFO, "Cookie not find in database"); + return NULL; + } + + bson_iter_t iter; + + if ( !(bson_iter_init (&iter, doc_dap_cookie_user) && bson_iter_find (&iter, "login")) ) + { + log_it(L_ERROR, "Login not found in document"); + return NULL; + } + + mongoc_collection_destroy(collection_cookie); + mongoc_cursor_destroy(cursor_dap_cookie); + + if(doc_dap_cookie_user) + bson_destroy(doc_dap_cookie_user); + + if(query) + bson_destroy(query); + + + /* ok user find now get information */ + + mongoc_collection_t* collection_dap_users = mongoc_client_get_collection + (mongo_client, l_db_name, "dap_users"); + + query = bson_new(); + + BSON_APPEND_UTF8 (query, "login", bson_iter_value(&iter)->value.v_utf8.str); + + mongoc_cursor_t* cursor_dap_users = mongoc_collection_find + (collection_dap_users, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL); + + + if( mongoc_cursor_next (cursor_dap_users, (const bson_t**)&doc_dap_cookie_user) == false ) + { + log_it(L_INFO, "User not find in database"); + return NULL; + } + // mongoc_collection_destroy(collection_cookie); + + bson_iter_t sub_iter; + + // ok cookie find, get user information; + db_auth_info_t * ai = DAP_NEW_Z(db_auth_info_t); + + if (bson_iter_init (&iter, doc_dap_cookie_user) && + bson_iter_find_descendant (&iter, "profile.first_name", &sub_iter)) + { + strncpy(ai->first_name, bson_iter_value(&sub_iter)->value.v_utf8.str, + bson_iter_value(&sub_iter)->value.v_utf8.len); + } + + if (bson_iter_init (&iter, doc_dap_cookie_user) && + bson_iter_find_descendant (&iter, "profile.last_name", &sub_iter)) + { + strncpy(ai->last_name,bson_iter_value(&sub_iter)->value.v_utf8.str, + bson_iter_value(&sub_iter)->value.v_utf8.len); + } + + if (bson_iter_init (&iter, doc_dap_cookie_user) && + bson_iter_find_descendant (&iter, "profile.email", &sub_iter)) + { + strncpy(ai->email,bson_iter_value(&sub_iter)->value.v_utf8.str, + bson_iter_value(&sub_iter)->value.v_utf8.len); + } + + strcpy(ai->cookie, cookie); + + mongoc_collection_destroy(collection_dap_users); + mongoc_cursor_destroy(cursor_dap_users); + + if(doc_dap_cookie_user) + bson_destroy(doc_dap_cookie_user); + + if(query) + bson_destroy(query); + + pthread_mutex_lock(&mutex_on_auth_hash); + HASH_ADD_STR(auths,cookie,ai); + pthread_mutex_unlock(&mutex_on_auth_hash); + + return ai; +} + +/** + * @brief db_auth_user_change_password + * @param user + * @param password + * @param new_password + * @return + * @details change password for user ( check correctly current pass, for change to new ) + */ +bool db_auth_user_change_password(const char* user, const char* password, + const char* new_password) +{ + if ( check_user_password(user, password) == false ) + { + log_it(L_WARNING, "Error change password. Old user password not correct" , user); + return false; + } + + return db_auth_change_password(user, new_password); +} + +/** + * @brief db_auth_user_change_password + * @param user + * @param password + * @param new_password + * @return + * @details change passwd without check correct old password ( for admins ) + */ +bool db_auth_change_password(const char* user, const char* new_password) +{ + if ( exist_user_in_db(user) == false ) + { + log_it(L_WARNING, "Error change password. User %s not find" , user); + return false; + } + + mongoc_collection_t *collection_dap_users = mongoc_client_get_collection + (mongo_client, l_db_name, "dap_users"); + + bson_t *query = bson_new(); + + BSON_APPEND_UTF8 (query, "login", user); + + mongoc_cursor_t *cursor_dap_users = mongoc_collection_find + (collection_dap_users, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL); + + bson_t *doc_dap_user; + + mongoc_cursor_next (cursor_dap_users, (const bson_t**)&doc_dap_user); + + bson_error_t error; + + char salt[8]; + RAND_bytes(salt, 8); + + unsigned const char * password_hash = hash_password(new_password, salt, 8); + char salt_b64[8*2] = {0}; + dap_enc_base64_encode(salt, 8, salt_b64,DAP_ENC_DATA_TYPE_B64_URLSAFE); + + if (!password_hash) { + log_it(L_WARNING,"Can not memmory allocate"); + return false; + } + + unsigned char * password_hash_b64 = calloc(4 * DB_AUTH_HASH_LENGTH, sizeof(char)); + + if (!password_hash_b64) { + free((char*)password_hash); + log_it(L_WARNING,"Can not memmory allocate"); + return false; + } + + dap_enc_base64_encode(password_hash, DB_AUTH_HASH_LENGTH * 2, password_hash_b64,DAP_ENC_DATA_TYPE_B64_URLSAFE); + + + if (*password_hash_b64 == 0) { + log_it(L_WARNING,"Bad hash(based64) for user password"); + return false; + } + + bson_t *update = BCON_NEW ("$set", "{", + "passwordHash", BCON_UTF8 (password_hash_b64), + "salt", BCON_UTF8 (salt_b64),"}"); + + if (!mongoc_collection_update (collection_dap_users, MONGOC_UPDATE_NONE, doc_dap_user, update, NULL, &error)) { + log_it(L_WARNING,"%s", error.message); + return false; + } + + mongoc_collection_destroy(collection_dap_users); + + if(query) + bson_destroy(query); + + if(cursor_dap_users) + mongoc_cursor_destroy(cursor_dap_users); + + if(doc_dap_user) + bson_destroy(doc_dap_user); + + free((char*)password_hash); free((char*)password_hash_b64); + + log_it(L_INFO, "user: %s change password to %s", user, new_password); + return true; +} + + +/** + * @brief check_user_password + * @param user + * @param password + * @return false if user password not correct + */ +bool check_user_password(const char* user, const char* password) +{ + if ( exist_user_in_db(user) == false ){ + log_it(L_WARNING,"User %s is not present in DB",user); + return false; + } + + bool is_correct_password = false; + + mongoc_collection_t *collection = mongoc_client_get_collection ( + mongo_client, l_db_name, "dap_users"); + + bson_t *query = bson_new(); + BSON_APPEND_UTF8 (query, "login", user); + + bson_iter_t iter; + bson_t *doc; + + mongoc_cursor_t *cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 0, 0, + (const bson_t*)query, NULL, NULL); + + mongoc_cursor_next (cursor, (const bson_t**)&doc); + char salt[16] = {0}; char salt_from_b64[8] = {0}; + + if ( bson_iter_init (&iter, doc) && bson_iter_find (&iter, "salt") ) + memcpy(salt,bson_iter_value(&iter)->value.v_utf8.str,16); + else { + log_it(L_ERROR, "Not find Salt in user"); return NULL; + } + + dap_enc_base64_decode(salt, 16, salt_from_b64,DAP_ENC_DATA_TYPE_B64); + + unsigned const char* password_hash = hash_password(password, salt_from_b64, 8); + if (!password_hash) { + log_it(L_ERROR, "Can not memmory allocate"); + return NULL; + } + + unsigned char * password_hash_b64 = calloc(4 * DB_AUTH_HASH_LENGTH, sizeof(char)); + + if (!password_hash_b64) { + free((char*)password_hash); + log_it(L_ERROR, "Can not memmory allocate"); + return NULL; + } + + dap_enc_base64_encode(password_hash, DB_AUTH_HASH_LENGTH * 2, password_hash_b64,DAP_ENC_DATA_TYPE_B64); + + if (bson_iter_init (&iter, doc) && bson_iter_find (&iter, "passwordHash")) + { + if ( memcmp(password_hash_b64, bson_iter_value(&iter)->value.v_utf8.str, + DB_AUTH_HASH_LENGTH * 2) == 0 ) + is_correct_password = true; + } + + mongoc_collection_destroy(collection); + + if(cursor) + mongoc_cursor_destroy(cursor); + + if(query) + bson_destroy(query); + + if(doc) + bson_destroy(doc); + + free((char*)password_hash); free((char*)password_hash_b64); + + return is_correct_password; +} + + +static bool db_auth_save_cookie_inform_in_db(const char* login, char* cookie) +{ + bool result = true; + mongoc_collection_t *collection = mongoc_client_get_collection ( + mongo_client, l_db_name, "dap_cookie_history"); + + bson_error_t error; + + bson_t *query = bson_new(); + BSON_APPEND_UTF8 (query, "login", login); + + mongoc_cursor_t *cursor_dap_cookie_history = mongoc_collection_find + (collection, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL); + + struct tm *utc_date_time; + time_t t = time(NULL); + utc_date_time = localtime(&t); + + bson_t *doc_dap_cookie; bson_t *bson_doc = NULL; + if ( mongoc_cursor_next (cursor_dap_cookie_history, (const bson_t**)&doc_dap_cookie) ) + { + bson_doc = BCON_NEW ("$set", "{", + "login", BCON_UTF8 (login), + "cookie", BCON_UTF8 (cookie), + "last_use", BCON_DATE_TIME(mktime (utc_date_time) * 1000), + "}"); + + if (!mongoc_collection_update (collection, MONGOC_UPDATE_UPSERT, doc_dap_cookie, bson_doc, NULL, &error)) { + log_it(L_WARNING,"%s", error.message); + result = false; + } + } + else + { + bson_doc = BCON_NEW("login", BCON_UTF8 (login), + "cookie", BCON_UTF8 (cookie), + "last_use", BCON_DATE_TIME(mktime (utc_date_time) * 1000)); + + if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, bson_doc, NULL, &error)) + { + log_it (L_WARNING, "%s\n", error.message); + result = false; + } + } + + mongoc_collection_destroy(collection); + mongoc_cursor_destroy(cursor_dap_cookie_history); + bson_destroy(query); + if(doc_dap_cookie) + bson_destroy(doc_dap_cookie); + if(bson_doc) + bson_destroy(bson_doc); + + return result; +} + +/** + * @brief db_auth_login Authorization with user/password + * @param login ( login = email ) + * @param password Password + * @param domain + * @return codes: 1 = login ok, 2 = login not found in DataBase, + * 3 = incorrect password; 4 = subscribe client has been expiried + */ +int db_auth_login(const char* login, const char* password, + const char* domain, db_auth_info_t** ai) +{ + *ai = NULL; + bson_t *doc; + + mongoc_collection_t *collection = mongoc_client_get_collection ( + mongo_client, l_db_name, "dap_users"); + + bson_t *query = bson_new(); + + if (strchr(login, '@')) + BSON_APPEND_UTF8 (query, "email", login); + else + BSON_APPEND_UTF8 (query, "login", login); + + mongoc_cursor_t *cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 0, 0, + (const bson_t*)query, NULL, NULL); + + if ( mongoc_cursor_next (cursor, (const bson_t**)&doc) == false ) + { + mongoc_cursor_destroy (cursor); + bson_destroy (query); + mongoc_collection_destroy (collection); + log_it(L_WARNING, "%s not found in DataBase", login); + return 2; + } + + bson_iter_t iter; + + char salt[16] = {0}; char salt_from_b64[8]={0}; + if (bson_iter_init (&iter, doc) && bson_iter_find (&iter, "salt")) + memcpy(salt,bson_iter_value(&iter)->value.v_utf8.str,16); + else { + log_it(L_ERROR, "Not find Salt in user"); return 0; + } + + dap_enc_base64_decode(salt, 16, salt_from_b64,DAP_ENC_DATA_TYPE_B64_URLSAFE); + + unsigned const char* password_hash = hash_password(password, salt_from_b64, 8); + if (!password_hash) { + log_it(L_ERROR, "Can not memmory allocate"); + return 0; + } + + unsigned char * password_hash_b64 = calloc(4 * DB_AUTH_HASH_LENGTH, sizeof(char)); + + if (!password_hash_b64) { + free((char*)password_hash); + log_it(L_ERROR, "Can not memmory allocate"); + return 0; + } + + dap_enc_base64_encode(password_hash, DB_AUTH_HASH_LENGTH * 2, password_hash_b64,DAP_ENC_DATA_TYPE_B64_URLSAFE); + + if (bson_iter_init (&iter, doc) && bson_iter_find (&iter, "expire_date")) + { + if ( bson_iter_date_time(&iter) / 1000 < time(NULL) ) + { + log_it(L_WARNING, "Subscribe %s has been expiried", login); + return 4; + } + } + + if (bson_iter_init (&iter, doc) && bson_iter_find (&iter, "passwordHash")) + { + if ( memcmp(password_hash_b64, bson_iter_value(&iter)->value.v_utf8.str, + DB_AUTH_HASH_LENGTH * 2) == 0 ) + { + { + bool b_error = false; + mongoc_collection_t *collection_dap_domain = mongoc_client_get_collection + (mongo_client, l_db_name, "dap_domains"); + + bson_t *query = bson_new(); + + BSON_APPEND_UTF8 (query, "domain", domain); + + mongoc_cursor_t *cursor_dap_domains = + mongoc_collection_find (collection_dap_domain, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL); + + bson_t *doc_dap_domain; + + if ( mongoc_cursor_next (cursor_dap_domains, (const bson_t**)&doc_dap_domain) == false ) + { + log_it(L_WARNING, "Login Error! " + "Domain not found in DataBase (collection dap_domains)"); + + b_error = true; + } + + mongoc_cursor_destroy (cursor_dap_domains); + bson_destroy (query); + if(doc_dap_domain) + bson_destroy (doc_dap_domain); + mongoc_collection_destroy (collection_dap_domain); + + if(b_error) + return 0; + } + + log_it(L_INFO,"Login accepted"); + + *ai = DAP_NEW_Z(db_auth_info_t); + strncpy((*ai)->user,login,sizeof((*ai)->user)); + strncpy((*ai)->password,password,sizeof((*ai)->password)); + + if ( !bson_iter_init (&iter, doc) ) + log_it(L_ERROR,"Error iter init"); + + bson_oid_t oid; + + if ( bson_iter_find(&iter, "_id") ) + { + bson_oid_init_from_data(&oid, (const uint8_t*) &bson_iter_value(&iter)->value.v_oid.bytes); + bson_oid_to_string(&oid, (*ai)->id); + } + else + log_it(L_ERROR,"Not find Id"); + + bson_iter_t sub_iter; + + if (bson_iter_init (&iter, doc) && + bson_iter_find_descendant (&iter, "profile.first_name", &sub_iter)) + strncpy((*ai)->first_name,bson_iter_value(&sub_iter)->value.v_utf8.str, + sizeof((*ai)->first_name)); + + if (bson_iter_init (&iter, doc) && + bson_iter_find_descendant (&iter, "profile.last_name", &sub_iter)) + strncpy((*ai)->last_name,bson_iter_value(&sub_iter)->value.v_utf8.str, + sizeof((*ai)->last_name)); + + if (bson_iter_init (&iter, doc) && + bson_iter_find_descendant (&iter, "profile.email", &sub_iter)) + strncpy((*ai)->email,bson_iter_value(&sub_iter)->value.v_utf8.str, + sizeof((*ai)->email)); + + for(int i=0; i < sizeof((*ai)->cookie); i++) + (*ai)->cookie[i] = 65 + rand() % 25; + + log_it(L_DEBUG,"Store cookie '%s' in the hash table",(*ai)->cookie); + db_auth_save_cookie_inform_in_db(login, (*ai)->cookie); + pthread_mutex_lock(&mutex_on_auth_hash); + HASH_ADD_STR(auths,cookie,(*ai)); + pthread_mutex_unlock(&mutex_on_auth_hash); + } + } + + free(password_hash_b64); + free((char*)password_hash); + mongoc_cursor_destroy (cursor); + if(query) + bson_destroy (query); + + if(doc) + bson_destroy (doc); + mongoc_collection_destroy (collection); + + if( *ai == NULL ) + { + log_it (L_WARNING, "Incorrect password!"); + return 3; + } + return 1; +} + +/** + * @brief db_auth_register Register new user in database + * @param user Login name + * @param password Password + * @param first_name First name + * @param last_name Last name + * @param email Email + * @details registerUser + * @return + */ +db_auth_info_t * db_auth_register(const char *user,const char *password, + const char *domain, const char * first_name, + const char* last_name, const char * email, + const char * device_type,const char *app_version, + const char *hostaddr, const char *sys_uuid ) +{ + mongoc_collection_t *collection_dap_domain = mongoc_client_get_collection + (mongo_client, l_db_name, "dap_domains"); + + bson_t *query = bson_new(); + + BSON_APPEND_UTF8 (query, "domain", domain); + + mongoc_cursor_t *cursor_dap_domains = mongoc_collection_find + (collection_dap_domain, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL); + + bson_t *doc_dap_domain; + + if ( mongoc_cursor_next (cursor_dap_domains, (const bson_t**)&doc_dap_domain) == false ) + { + log_it(L_WARNING, "Domain not found in DataBase (collection dap_domains) "); + return NULL; + } + + bson_iter_t iter; + bson_iter_init (&iter, doc_dap_domain); + if ( !bson_iter_find (&iter, "_id") ) + { + log_it(L_ERROR, "Where field _id in document?!"); + return NULL; + } + + mongoc_collection_t *collection = mongoc_client_get_collection + (mongo_client, l_db_name, "dap_users"); + bson_error_t error; + + char salt[8]; + RAND_bytes(salt, 8); + + unsigned const char * password_hash = hash_password(password, salt, 8); + char salt_b64[8*2] = {0}; + dap_enc_base64_encode(salt, 8, salt_b64,DAP_ENC_DATA_TYPE_B64_URLSAFE); + + if (!password_hash) { + log_it(L_ERROR, "Can not memmory allocate"); + return NULL; + } + + unsigned char * password_hash_b64 = calloc(4 * DB_AUTH_HASH_LENGTH, sizeof(char)); + + if (!password_hash_b64) { + free((char*)password_hash); + log_it(L_ERROR, "Can not memmory allocate"); + return NULL; + } + + dap_enc_base64_encode(password_hash, DB_AUTH_HASH_LENGTH * 2, password_hash_b64,DAP_ENC_DATA_TYPE_B64_URLSAFE); + + if (*password_hash_b64 == 0) { + log_it(L_ERROR, "Bad hash(based64) for user password"); + return NULL; + } + + bson_t *doc = BCON_NEW("login", user, + "passwordHash", password_hash_b64, + "salt",salt_b64, + "domainId", BCON_OID((bson_oid_t*)bson_iter_value(&iter)->value.v_oid.bytes), + "email", email, + "profile", + "{", + "first_name", first_name, + "last_name", last_name, + "}", + "contacts" , "[","]"); + free((char*)password_hash); + free(password_hash_b64); + + if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, doc, NULL, &error)) { + log_it (L_WARNING, "%s\n", error.message); + + bson_destroy(query); + mongoc_collection_destroy (collection_dap_domain); + bson_destroy(doc_dap_domain); + mongoc_cursor_destroy(cursor_dap_domains); + bson_destroy (doc); + mongoc_collection_destroy (collection); + mongoc_cleanup(); + return NULL; + } + else + { + db_auth_info_t * ai = DAP_NEW_Z(db_auth_info_t); + strncpy(ai->user,user,sizeof(ai->user)); + strncpy(ai->password,password,sizeof(ai->password)); + strncpy(ai->last_name,last_name,sizeof(ai->last_name)); + strncpy(ai->first_name,first_name,sizeof(ai->first_name)); + strncpy(ai->email,email,sizeof(ai->email)); + + for(int i=0;i<sizeof(ai->cookie);i++) + ai->cookie[i]=65+rand()%25; + + pthread_mutex_lock(&mutex_on_auth_hash); + HASH_ADD_STR(auths,cookie,ai); + pthread_mutex_unlock(&mutex_on_auth_hash); + + bson_destroy(query); + mongoc_collection_destroy (collection_dap_domain); + bson_destroy(doc_dap_domain); + mongoc_cursor_destroy(cursor_dap_domains); + bson_destroy (doc); + mongoc_collection_destroy (collection); + mongoc_cleanup(); + + return ai; + } + + return NULL; +} + + +/** + * @brief db_auth_register_channel + * @param login + * @param password + * @details register channel + * @return + */ +db_auth_info_t * db_auth_register_channel(const char* name_channel, const char* domain, + const char* password) +{ + mongoc_collection_t *collection_dap_domain = mongoc_client_get_collection + (mongo_client, l_db_name, "dap_domains"); + + bson_t *query = bson_new(); + + BSON_APPEND_UTF8 (query, "domain", domain); + + mongoc_cursor_t *cursor_dap_domains = + mongoc_collection_find (collection_dap_domain, MONGOC_QUERY_NONE, 0, 0, 0, query, NULL, NULL); + + bson_t *doc_dap_domain; + + if ( mongoc_cursor_next (cursor_dap_domains, (const bson_t**)&doc_dap_domain) == false ) + { + log_it(L_WARNING, "Domain not found in DataBase (collection dap_domains) "); + return NULL; + } + + bson_iter_t iter; + bson_iter_init (&iter, doc_dap_domain); + if ( !bson_iter_find (&iter, "_id") ) + { + log_it(L_ERROR, "Where field _id in document?!"); + return NULL; + } + + mongoc_collection_t *collection = + mongoc_client_get_collection (mongo_client, l_db_name, "dap_channels"); + bson_error_t error; + + char salt[8]; + RAND_bytes(salt, 8); + unsigned const char * password_hash = hash_password(password, salt, 8); + + bson_t *doc = BCON_NEW("name_channel", name_channel, + "passwordHash", password_hash, + "salt",salt, + "domainId", BCON_OID((bson_oid_t*)bson_iter_value(&iter)->value.v_oid.bytes), + "subscribers", "[","]", + "last_id_message", BCON_INT32(0), + "messages","[","]"); + + free((char*)password_hash); + if (!mongoc_collection_insert (collection, MONGOC_INSERT_NONE, doc, NULL, &error)) { + log_it (L_ERROR, "%s\n", error.message); + bson_destroy(query); + bson_destroy(doc_dap_domain); + mongoc_cursor_destroy(cursor_dap_domains); + mongoc_collection_destroy(collection_dap_domain); + bson_destroy (doc); + mongoc_collection_destroy (collection); + mongoc_cleanup(); + return NULL; + } + + db_auth_info_t * ai = DAP_NEW_Z(db_auth_info_t); + strncpy(ai->user,name_channel,sizeof(ai->user)); + strncpy(ai->password,password,sizeof(ai->password)); + + for(int i=0;i<sizeof(ai->cookie);i++) + ai->cookie[i]=65+rand()%25; + + pthread_mutex_lock(&mutex_on_auth_hash); + HASH_ADD_STR(auths,cookie,ai); + pthread_mutex_unlock(&mutex_on_auth_hash); + + bson_destroy(query); + bson_destroy(doc_dap_domain); + mongoc_cursor_destroy(cursor_dap_domains); + mongoc_collection_destroy(collection_dap_domain); + bson_destroy (doc); + mongoc_collection_destroy (collection); + mongoc_cleanup(); + + return ai; +} + +bool exist_user_in_db(const char* user) +{ + bool exist = true; + bson_t *doc = NULL; + + mongoc_collection_t *collection = mongoc_client_get_collection ( + mongo_client, l_db_name, "dap_users"); + + bson_t *query = bson_new(); + BSON_APPEND_UTF8 (query, "login", user); + + mongoc_cursor_t *cursor = mongoc_collection_find (collection, MONGOC_QUERY_NONE, 0, 0, 0, + (const bson_t*)query, NULL, NULL); + + if ( mongoc_cursor_next (cursor, (const bson_t**)&doc) == false ) + { + exist = false; + log_it(L_WARNING, "Login not found in DataBase"); + } + + if(doc) + bson_destroy(doc); + + mongoc_cursor_destroy(cursor); + bson_destroy(query); + mongoc_collection_destroy(collection); + + return exist; +} + +/** + * @brief db_auth_http_proc DB Auth http interface + * @param cl_st HTTP Simple client instance + * @param arg Pointer to bool with okay status (true if everything is ok, by default) + */ +void db_auth_http_proc(enc_http_delegate_t *dg, void * arg) +{ + http_status_code_t * return_code = (http_status_code_t*)arg; + + if((dg->request)&&(strcmp(dg->action,"POST")==0)){ + if(dg->in_query==NULL){ + log_it(L_WARNING,"Empty auth action"); + *return_code = Http_Status_BadRequest; + return; + }else{ + if(strcmp(dg->in_query,"logout")==0 ){ + db_auth_info_t * ai = db_auth_info_by_cookie(dg->cookie); + if(ai){ + log_it(L_DEBUG, "Cookie from %s user accepted, 0x%032llX session",ai->user,ai->id); + pthread_mutex_lock(&mutex_on_auth_hash); + HASH_DEL(auths,ai); + pthread_mutex_unlock(&mutex_on_auth_hash); + free(ai); + enc_http_reply_f(dg, + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" + "<return>Successfuly logouted</return>\n" + ); + *return_code = Http_Status_OK; + }else{ + log_it(L_NOTICE,"Logout action: session 0x%032llX is already logouted (by timeout?)",ai->id); + enc_http_reply_f(dg, + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" + "<err_str>No session in table</err_str>\n" + ); + *return_code = Http_Status_OK; + } + + }else if(strcmp(dg->in_query,"login")==0 ){ + char user[256]; + char password[1024]; + char domain[64]; + + if(sscanf(dg->request_str,"%255s %1023s %63s",user,password,domain)==3){ + log_it(L_INFO, "Trying to login with username '%s'",user); + + if(!check_user_data_for_space(strlen(dg->request_str), (strlen(user)+strlen(password)+strlen(domain)))){ + log_it(L_WARNING,"Wrong symbols in username or password or domain"); + enc_http_reply_f(dg, OP_CODE_INCORRECT_SYMOLS); + *return_code = Http_Status_BadRequest; + return; + } +// if(db_input_validation(user)==0){ +// log_it(L_WARNING,"Wrong symbols in username '%s'",user); +// enc_http_reply_f(dg, OP_CODE_INCORRECT_SYMOLS); +// *return_code = Http_Status_BadRequest; +// return; +// } +// if(db_input_validation(password)==0){ +// log_it(L_WARNING,"Wrong symbols in password"); +// enc_http_reply_f(dg, OP_CODE_INCORRECT_SYMOLS); +// *return_code = Http_Status_BadRequest; +// return; +// } +// if(db_input_validation(domain)==0){ +// log_it(L_WARNING,"Wrong symbols in domain"); +// enc_http_reply_f(dg, OP_CODE_INCORRECT_SYMOLS); +// *return_code = Http_Status_BadRequest; +// return; +// } + + db_auth_info_t * ai = NULL; + short login_result = db_auth_login(user, password, domain, &ai); + switch (login_result) { + case 1: + ai->dap_http_client = dg->http; + ai->auth_date = time(NULL); + enc_http_reply_f(dg, + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" + "<auth_info>\n" + ); + enc_http_reply_f(dg,"\t<first_name>%s</first_name>\n",ai->first_name); + enc_http_reply_f(dg,"\t<last_name>%s</last_name>\n",ai->last_name); + enc_http_reply_f(dg,"\t<cookie>%s</cookie>\n",ai->cookie); + enc_http_reply_f(dg,"</auth_info>"); + log_it(L_INFO, "Login: Successfuly logined user %s",user); + *return_code = Http_Status_OK; + break; + case 2: + enc_http_reply_f(dg, OP_CODE_NOT_FOUND_LOGIN_IN_DB); + *return_code = Http_Status_OK; + break; + case 3: + enc_http_reply_f(dg, OP_CODE_LOGIN_INCORRECT_PSWD); + *return_code = Http_Status_OK; + break; + case 4: + enc_http_reply_f(dg, OP_CODE_SUBSCRIBE_EXPIRIED); + *return_code = Http_Status_PaymentRequired; + break; + default: + log_it(L_DEBUG, "Login: wrong password for user %s",user); + *return_code = Http_Status_BadRequest; + break; + } + }else{ + log_it(L_DEBUG, "Login: wrong auth's request body "); + *return_code = Http_Status_BadRequest; + } + }else if (strcmp(dg->in_query,"register")==0){ + char user[256]; + char password[1024]; + char domain[64]; + char first_name[1024]; + char last_name[1024]; + // char phone_number[1024]; + + char email[1024]; + char device_type[32]; + char app_version[32]; + char sys_uuid[128]; + + log_it(L_INFO, "Request str = %s", dg->request_str); + if(sscanf(dg->request_str,"%255s %63s %1023s %1023s %1023s %1023s %32s %128s" + ,user,password,domain,first_name,last_name,email,device_type,app_version)>=7){ + if(db_input_validation(user)==0){ + log_it(L_WARNING,"Registration: Wrong symbols in the username '%s'",user); + *return_code = Http_Status_BadRequest; + return; + } + if(db_input_validation(password)==0){ + log_it(L_WARNING,"Registration: Wrong symbols in the password"); + *return_code = Http_Status_BadRequest; + return; + } + if(db_input_validation(domain)==0){ + log_it(L_WARNING,"Registration: Wrong symbols in the password"); + *return_code = Http_Status_BadRequest; + return; + } + if(db_input_validation(first_name)==0){ + log_it(L_WARNING,"Registration: Wrong symbols in the first name '%s'",first_name); + *return_code = Http_Status_BadRequest; + return; + } + if(db_input_validation(last_name)==0){ + log_it(L_WARNING,"Registration: Wrong symbols in the last name '%s'",last_name); + *return_code = Http_Status_BadRequest; + return; + } + if(db_input_validation(email)==0){ + log_it(L_WARNING,"Registration: Wrong symbols in the email '%s'",email); + *return_code = Http_Status_BadRequest; + return; + } + + db_auth_info_t * ai = db_auth_register(user,password,domain,first_name,last_name,email, + device_type,app_version,dg->http->client->hostaddr,sys_uuid); + + if(ai != NULL) + { + enc_http_reply_f(dg, + "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" + "<auth_info>\n" + ); + enc_http_reply_f(dg,"\t<first_name>%s</first_name>\n",ai->first_name); + enc_http_reply_f(dg,"\t<last_name>%s</last_name>\n",ai->last_name); + enc_http_reply_f(dg,"\t<cookie>%s</cookie>\n",ai->cookie); + enc_http_reply_f(dg,"</auth_info>"); + + log_it(L_NOTICE,"Registration: new user %s \"%s %s\"<%s> is registred",user,first_name,last_name,email); + } + else { + log_it(L_WARNING, "User not registered. Maybe login already exists"); + } + }else{ + log_it(L_ERROR, "Registration: Wrong auth's request body "); + *return_code = Http_Status_BadRequest; + } + }else{ + log_it(L_ERROR, "Unknown auth command was selected (query_string='%s')",dg->in_query); + *return_code = Http_Status_BadRequest; + } + } + }else{ + log_it(L_ERROR, "Wrong auth request action '%s'",dg->action); + *return_code = Http_Status_BadRequest; + } +} + +static bool mongod_is_running() +{ + int pfd[2]; + pipe(pfd); + + pid_t childpid; + + if((childpid = fork()) == -1) + { + log_it(L_ERROR,"Error fork()"); + return false; + } + + if(childpid == 0) + { + close(STDOUT_FILENO); + dup2(pfd[1], STDOUT_FILENO); + close(pfd[0]); + close(pfd[1]); + execlp("pgrep", "pgrep","mongod", NULL); + exit(0); + } + + waitpid(childpid, 0, 0); + + char readbuffer[10] = {'\0'}; + + int flags = fcntl(pfd[0], F_GETFL, 0); + fcntl(pfd[0], F_SETFL, flags | O_NONBLOCK); + read(pfd[0], readbuffer, sizeof(readbuffer)); + + if(readbuffer[0] == '\0') + { + log_it(L_ERROR,"MongoDB service not running. For start use: \"mongod\" in terminal"); + return false; + } + + close(pfd[0]); + close(pfd[1]); + + return true; +} + +inline static unsigned char* hash_password(const unsigned char* password, unsigned char* salt, size_t salt_size) +{ + unsigned char *md = (unsigned char*) malloc (DB_AUTH_HASH_LENGTH * 2); + + size_t len_pswd = strlen(password); + size_t length_str = len_pswd + salt_size; + char str[length_str]; + + memcpy(str, password, len_pswd); + memcpy(str + len_pswd, salt, salt_size); + SHA512(str, length_str, md); + SHA512(salt, salt_size, md + DB_AUTH_HASH_LENGTH); + + return md; +} + +/// Check user data for correct input. +/// @param before_parsing Line size before parsing. +/// @param after_parsing Line size after parsing. +/// @return Returns true if user data is entered correctly +/// (there are 2 separator spaces), otherwise false. +bool check_user_data_for_space(size_t before_parsing, size_t after_parsing) +{ + return (before_parsing - after_parsing) == 2; +} diff --git a/auth/db_auth.h b/auth/db_auth.h new file mode 100755 index 0000000..8884b58 --- /dev/null +++ b/auth/db_auth.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc + All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DAP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ + + +#ifndef STREAM_AUTH_H +#define STREAM_AUTH_H +#include <stdint.h> +#include <pthread.h> +#include "uthash.h" + +#include "dap_enc_http.h" +#include "dap_http_client.h" +#include "dap_client_remote.h" +#define DB_AUTH_HASH_LENGTH 64 + +typedef struct db_auth_info{ + char cookie[65]; + char id[27]; + char first_name[1024]; + char last_name[1024]; + char email[1024]; + char user[256]; + char password[1024]; + time_t auth_date; + + /* pointer on low-level client only for read! */ + const dap_http_client_t * dap_http_client; + + UT_hash_handle hh; // makes this structure hashable with UTHASH library +} db_auth_info_t; + +extern int db_auth_init(const char* db_name); +extern void db_auth_deinit(void); + + +extern db_auth_info_t* db_auth_info_by_cookie(const char * cookie); +extern db_auth_info_t* db_search_cookie_in_db(const char * cookie); + +extern int db_auth_login(const char* login, const char* password, + const char* domain, db_auth_info_t** ai); + +extern db_auth_info_t * db_auth_register(const char *user,const char *password, + const char *domain, const char * first_name, + const char* last_name, const char * email, + const char * device_type, const char *app_version, + const char *hostaddr, const char *sys_uuid); + +extern db_auth_info_t * db_auth_register_channel(const char* name_channel, const char* domain, + const char* password); +extern bool exist_user_in_db(const char* user); + +extern bool db_auth_user_change_password(const char* user, const char* password, + const char* new_password); + +extern bool db_auth_change_password(const char *user, const char* new_password); + +extern bool check_user_password(const char* user, const char* password); + +extern void db_auth_http_proc(enc_http_delegate_t *dg, void * arg); + +extern void db_auth_traffic_track_callback(dap_server_t *srv); + +/// Check user data for correct input. +/// @param before_parsing Line size before parsing. +/// @param after_parsing Line size after parsing. +/// @return Returns true if user data is entered correctly +/// (there are 2 separator spaces), otherwise false. +extern inline bool check_user_data_for_space(size_t before_parsing, size_t after_parsing); +#endif diff --git a/db/CMakeLists.txt b/db/CMakeLists.txt new file mode 100755 index 0000000..483c09b --- /dev/null +++ b/db/CMakeLists.txt @@ -0,0 +1,15 @@ +cmake_minimum_required(VERSION 2.8) +project (dap_db) + +set(DB_SRCS db_core.c db_http.c db_http_file.c) + +add_library(${PROJECT_NAME} STATIC ${DB_SRCS}) + +target_link_libraries(dap_db + dap_core dap_crypto + dap_http_server dap_enc_server + ${BSON_LIBRARIES} ${MONGO_LIBRARIES}) + +target_include_directories(dap_db + INTERFACE . + PUBLIC ${BSON_INCLUDE_DIRS} ${MONGO_INCLUDE_DIRS}) diff --git a/db/db_core.c b/db/db_core.c new file mode 100755 index 0000000..2f00a7f --- /dev/null +++ b/db/db_core.c @@ -0,0 +1,82 @@ +/* + Copyright (c) 2017-2018 (c) Project "DeM Labs Inc" https://github.com/demlabsinc + All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + DAP is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with any DAP based project. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include <string.h> +#include <bson.h> +#include <bcon.h> +#include <mongoc.h> +#include "dap_common.h" +#include "db_core.h" +#define LOG_TAG "db" + +mongoc_client_t *mongo_client = NULL, + *traffick_track_db_client = NULL; + +int db_core_init(const char *db_path) +{ + mongoc_init(); + + mongo_client = mongoc_client_new (db_path); + traffick_track_db_client = mongoc_client_new (db_path); + log_it(L_DEBUG, "Checking connection to database..."); + if(!mongoc_client_get_server_status(mongo_client, NULL, NULL, NULL)) + { + log_it(L_ERROR, "Can't connect to database"); + return -1; + } + + return 0; +} + +void db_core_deinit() +{ + if(mongo_client) + mongoc_client_destroy(mongo_client); + if(traffick_track_db_client) + mongoc_client_destroy(traffick_track_db_client); + + mongoc_cleanup (); +} + +void db_core_refresh() +{ + +} + + +int db_input_validation(const char * str) +{ + // The compiler will stack "multiple" "strings" "end" "to" "end" + // into "multiplestringsendtoend", so we don't need one giant line. + static const char *nospecial="0123456789" + "abcdefghijklmnopqrstuvwxyz" + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + ".=@?_!#$%"; + + while(*str) // Loop until (*url) == 0. (*url) is about equivalent to url[0]. + { + // Can we find the character at *url in the string 'nospecial'? + // If not, it's a special character and we should return 0. + if(strchr(nospecial, *str) == NULL) return(0); + str++; // Jump to the next character. Adding one to a pointer moves it ahead one element. + } + + return(1); // Return 1 for success. +} diff --git a/db/db_core.h b/db/db_core.h new file mode 100755 index 0000000..dd1af60 --- /dev/null +++ b/db/db_core.h @@ -0,0 +1,17 @@ +#ifndef _DATABASE_H_ +#define _DATABASE_H_ + +#include <stddef.h> +#include <mongoc.h> + +extern int db_core_init(const char *db_path); +extern void db_core_deinit(); + +extern void db_core_refresh(); + +extern int db_input_validation(const char * str); + +extern mongoc_client_t *mongo_client, *traffick_track_db_client; + + +#endif diff --git a/db/db_http.c b/db/db_http.c new file mode 100755 index 0000000..ac0f011 --- /dev/null +++ b/db/db_http.c @@ -0,0 +1,74 @@ +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "utlist.h" + +#include "dap_common.h" + +#include "dap_client_remote.h" +#include "dap_http_client.h" +#include "dap_http_simple.h" + +#include "db_http.h" + +#include "../auth/db_auth.h" +#include "http_status_code.h" + +#include "dap_enc_http.h" + +#define LOG_TAG "db_http" + +#define LAST_USE_KEY(key) ((rsa_key_t*)key->internal)->last_time_use_key + +void db_http_proc(dap_http_simple_t * cl_st, void * arg ); + +int db_http_init() +{ + log_it(L_NOTICE, "Init content manager"); + return 0; +} + +void db_http_deinit() +{ + log_it(L_NOTICE, "Deinit content manager"); +} + + +void db_http_add_proc(struct dap_http * sh, const char * url) +{ + dap_http_simple_proc_add(sh,url,1000000,db_http_proc); +} + +/** + * @brief content_proc Process content list request + * @param sh HTTP simple client instance + * @param arg Return if ok + */ +void db_http_proc(dap_http_simple_t * cl_st, void * arg ) +{ + http_status_code_t * return_code = (http_status_code_t*)arg; + enc_http_delegate_t * dg; + strcpy(cl_st->reply_mime,"application/octet-stream"); + + dg=enc_http_request_decode(cl_st); + if(dg){ + if(strcmp(dg->url_path,"auth")==0){ + db_auth_http_proc(dg, arg); + } else { + + if(dg->url_path) + log_it(L_ERROR,"Wrong DB request %s",dg->url_path); + else + log_it(L_ERROR,"Wrong DB request: nothing after / "); + + *return_code = Http_Status_BadRequest; + } + + enc_http_reply_encode(cl_st,dg); + enc_http_delegate_delete(dg); + }else{ + *return_code = Http_Status_Unauthorized; + log_it(L_WARNING,"No KeyID in the request"); + } +} + diff --git a/db/db_http.h b/db/db_http.h new file mode 100755 index 0000000..bba8dc9 --- /dev/null +++ b/db/db_http.h @@ -0,0 +1,10 @@ +#ifndef _DB_HTTP_H_ +#define _DB_HTTP_H_ + +#include "dap_enc_ks.h" +#include "dap_enc_key.h" + +int db_http_init(void); +void db_http_deinit(void); +void db_http_add_proc(struct dap_http * sh, const char * url); +#endif diff --git a/db/db_http_file.c b/db/db_http_file.c new file mode 100755 index 0000000..a4c2841 --- /dev/null +++ b/db/db_http_file.c @@ -0,0 +1,250 @@ +#include <sys/stat.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> + +#include "dap_common.h" + +//#include "db_content.h" + +#include "dap_http.h" +#include "dap_http_client.h" + +#include "dap_enc.h" +#include "dap_enc_key.h" +#include "dap_enc_ks.h" + +#include "dap_client_remote.h" + +#define LOG_TAG "db_http_file" + +#define AES_BLOCK_SIZE 16 + +typedef struct db_http_file{ + FILE * fd; + size_t file_size; + dap_enc_key_t * key; + size_t position; + char path[4096]; + dap_http_client_t * client; +} db_http_file_t; + +#define DB_HTTP_FILE(a) ((db_http_file_t*) (a)->_internal ) + + +void db_http_file_headers_read(dap_http_client_t * cl_ht, void * arg); +void db_http_file_headers_write(dap_http_client_t * cl_ht, void * arg); +void db_http_file_data_read(dap_http_client_t * cl_ht, void * arg); +void db_http_file_data_write(dap_http_client_t * cl_ht, void * arg); + +static const char *_contents_path; + +int db_http_file_init(const char* content_path) +{ + _contents_path = strdup(content_path); + return 0; +} + +void db_http_file_deinit() +{ + free((char*)_contents_path); +} + +void db_http_file_proc_add(struct dap_http *sh, const char * url_path) +{ + dap_http_add_proc(sh,url_path,NULL,NULL,NULL,db_http_file_headers_read,db_http_file_headers_write, + db_http_file_data_read,db_http_file_data_write,NULL); +} + + + +/** + * @brief db_http_file_headers_read Signal thats HTTP client is now going to output the data + * @param cl_ht HTTP client instance + * @param arg Not used + */ +void db_http_file_headers_read(dap_http_client_t * cl_ht, void * arg) +{ + (void) arg; + cl_ht->state_write=DAP_HTTP_CLIENT_STATE_START; + cl_ht->state_read=cl_ht->keep_alive?DAP_HTTP_CLIENT_STATE_START:DAP_HTTP_CLIENT_STATE_NONE; + dap_client_remote_ready_to_write(cl_ht->client,true); + dap_client_remote_ready_to_read(cl_ht->client,cl_ht->keep_alive); +} + +/** + * @brief db_http_file_headers Prepare response HTTP headers for file folder request + * @param cl_ht HTTP client instane + * @param arg Not used + */ +void db_http_file_headers_write(dap_http_client_t * cl_ht, void * arg) +{ + (void) arg; + + dap_enc_key_t * key= dap_enc_ks_find_http(cl_ht); + if(key){ + uint8_t buf[sizeof(cl_ht->url_path)]; + size_t buf_size=0; + size_t url_path_size=strlen(cl_ht->url_path); + + if(url_path_size){ + if(url_path_size>sizeof(cl_ht->url_path)){ + log_it(L_WARNING, "Too big URL path %lu bytes, shrinking to %lu",url_path_size,sizeof(cl_ht->url_path)); + url_path_size=sizeof(cl_ht->url_path); + } + buf_size=dap_enc_decode(key,cl_ht->url_path,url_path_size,buf,sizeof(cl_ht->url_path), DAP_ENC_DATA_TYPE_B64); + + uint8_t file_variant=0; + if(strcmp(buf,"poster_small")==0){ + file_variant=1; + }else if(strcmp(buf,"poster_big")==0){ + file_variant=2; + } + if(file_variant){ + size_t in_query_string_length=strlen(cl_ht->in_query_string); + + if(in_query_string_length){ + long long cnt_id; + buf_size=dap_enc_decode(key,cl_ht->in_query_string,in_query_string_length,buf,sizeof(cl_ht->url_path),DAP_ENC_DATA_TYPE_B64); + if(sscanf(buf,"id=%lld",&cnt_id)==1){ + char buf2[255]; + snprintf(buf2,sizeof(buf2)-1,"id=%lld",cnt_id); + // db_content_t * cnt=db_content_select(buf2); //erase + void * cnt = NULL; + if(cnt){ + // Produce local path for file to open + char * file_path=NULL; + + /* ERASE */ + /* + if(file_variant==1) + file_path=cnt->poster_small; + else if( file_variant==2) + file_path=cnt->poster_big;*/ + + if(file_path){ + // Init specific file response data for HTTP client instance + cl_ht->_internal=(db_http_file_t *) calloc (1,sizeof(db_http_file_t)); + db_http_file_t* cl_ht_file=DB_HTTP_FILE(cl_ht); + cl_ht_file->client=cl_ht; + cl_ht_file->key=key; + + + snprintf(cl_ht_file->path,sizeof(cl_ht_file->path),"%s/%s", _contents_path, file_path ); + + log_it(L_DEBUG, "Check %s file", cl_ht_file->path); + + struct stat file_stat; + if(stat(cl_ht_file->path,&file_stat)==0){ + cl_ht->out_last_modified=file_stat.st_mtime; + cl_ht->out_content_length=(file_stat.st_size%AES_BLOCK_SIZE )? + (file_stat.st_size +(AES_BLOCK_SIZE- (file_stat.st_size%AES_BLOCK_SIZE) )): + file_stat.st_size; + cl_ht_file->file_size=file_stat.st_size; + cl_ht_file->fd=fopen(cl_ht_file->path,"r"); + if(cl_ht_file->fd == NULL){ + log_it(L_ERROR, "Can't open %s: %s",cl_ht_file->path,strerror(errno)); + cl_ht->reply_status_code=404; + strncpy(cl_ht->reply_reason_phrase,"Not Found",sizeof(cl_ht->reply_reason_phrase)); + }else{ + log_it(L_NOTICE, "Open %s file (%lu bytes raw, %lu bytes encrypted )",cl_ht_file->path,cl_ht_file->file_size,cl_ht->out_content_length); + cl_ht->reply_status_code=200; + dap_client_remote_ready_to_write(cl_ht->client,true); + strncpy(cl_ht->reply_reason_phrase,"OK",sizeof(cl_ht->reply_reason_phrase)); + } + + }else{ + log_it(L_WARNING, "Can't get file info: %s",strerror(errno)); + cl_ht->reply_status_code=404; + strncpy(cl_ht->reply_reason_phrase,"Not Found",sizeof(cl_ht->reply_reason_phrase)); + } + }else{ + log_it(L_WARNING, "Unknown file variant %uc",file_variant); + cl_ht->reply_status_code=404; + strncpy(cl_ht->reply_reason_phrase,"Not Found",sizeof(cl_ht->reply_reason_phrase)); + } + }else{ + log_it(L_WARNING, "Can't find id %lld in database",cnt_id); + cl_ht->reply_status_code=404; + strncpy(cl_ht->reply_reason_phrase,"Not Found",sizeof(cl_ht->reply_reason_phrase)); + } + }else{ + log_it(L_WARNING, "Can't parse decoded in query string '%s'",buf); + cl_ht->reply_status_code=500; + strncpy(cl_ht->reply_reason_phrase,"Not Found",sizeof(cl_ht->reply_reason_phrase)); + } + }else{ + log_it(L_WARNING, "Empty in query string"); + cl_ht->reply_status_code=404; + strncpy(cl_ht->reply_reason_phrase,"Not Found",sizeof(cl_ht->reply_reason_phrase)); + } + }else{ + log_it(L_WARNING, "Wrong path request (decoded string '%s' )", buf ); + cl_ht->reply_status_code=500; + strncpy(cl_ht->reply_reason_phrase,"ERROR",sizeof(cl_ht->reply_reason_phrase)); + } + }else{ + + log_it(L_WARNING, "Empty url path"); + cl_ht->reply_status_code=500; + strncpy(cl_ht->reply_reason_phrase,"ERROR",sizeof(cl_ht->reply_reason_phrase)); + } + }else{ + log_it(L_WARNING, "No KeyID in request"); + cl_ht->reply_status_code=500; + strncpy(cl_ht->reply_reason_phrase,"ERROR",sizeof(cl_ht->reply_reason_phrase)); + } +} + +/** + * @brief db_http_file_read HTTP client callback for reading function for the folder processing + * @param cl_ht HTTP client instance + * @param arg Pointer to int with return bytes number + */ +void db_http_file_data_read(dap_http_client_t * cl_ht, void * arg) +{ + int * bytes_return = (int*) arg; // Return number of read bytes + //Do nothing + *bytes_return=cl_ht->client->buf_in_size; +} + +/** + * @brief db_http_folder_write HTTP client callback for writting function for the folder processing + * @param cl_ht HTTP client instance + * @param arg + */ +void db_http_file_data_write(dap_http_client_t * cl_ht, void * arg) +{ + (void) arg; + db_http_file_t * cl_ht_file= DB_HTTP_FILE(cl_ht); + + uint8_t buf[AES_BLOCK_SIZE*200]; // We thing that its dividing on AES_BLOCKSIZE to have no trailing zeros in encrypted block + size_t buf_size_max=sizeof(buf); + if(cl_ht_file->file_size- cl_ht_file->position<buf_size_max) + buf_size_max=(cl_ht_file->file_size- cl_ht_file->position); + + if(buf_size_max){ + size_t buf_size=0; + buf_size+=fread(buf+buf_size,1,buf_size_max-buf_size,cl_ht_file->fd); + + cl_ht_file->position+=buf_size; + cl_ht->client->buf_out_size=dap_enc_code(cl_ht_file->key, buf,buf_size,cl_ht->client->buf_out,DAP_CLIENT_REMOTE_BUF, DAP_ENC_DATA_TYPE_RAW); + // log_it(L_DEBUG, "Have read %lu bytes from the file (ecrypted size %lu total size %lu expecting %lu)",buf_size,cl_ht->client->buf_out_size,cl_ht_file->position, cl_ht_file->client->out_content_length); + if(feof(cl_ht_file->fd)!=0){ + log_it(L_INFO, "All the file %s is sent out (%lu bytes)",cl_ht_file->path,cl_ht_file->position); + //strncat(cl_ht->client->buf_out+cl_ht->client->buf_out_size,"\r\n",sizeof(cl_ht->client->buf_out)); + fclose(cl_ht_file->fd); + dap_client_remote_ready_to_write(cl_ht->client,false); + cl_ht->client->signal_close=!cl_ht->keep_alive; + cl_ht->state_write=DAP_HTTP_CLIENT_STATE_NONE; + } + }else{ + log_it(L_INFO, "All the file %s is sent out (%lu bytes)",cl_ht_file->path,cl_ht_file->position); + fclose(cl_ht_file->fd); + dap_client_remote_ready_to_write(cl_ht->client,false); + cl_ht->client->signal_close=!cl_ht->keep_alive; + cl_ht->state_write=DAP_HTTP_CLIENT_STATE_NONE; + } +} + diff --git a/db/db_http_file.h b/db/db_http_file.h new file mode 100755 index 0000000..d0d9a2e --- /dev/null +++ b/db/db_http_file.h @@ -0,0 +1,10 @@ +#ifndef _DB_HTTP_FILE_H_ +#define _DB_HTTP_FILE_H_ +struct dap_http; + +extern int db_http_file_init(); +extern void db_http_file_deinit(); + +extern void db_http_file_proc_add(struct dap_http *sh, const char * url_path); + +#endif -- GitLab