/* 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_core.h" #include "db_auth.h" #include "http_status_code.h" #include "SimpleFIPS202.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 const char *l_db_name; dap_enc_http_callback_t s_callback_success = NULL; 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); } /** * @brief db_auth_set_callbacks * @param a_callback_success */ void db_auth_set_callbacks(dap_enc_http_callback_t a_callback_success) { s_callback_success = a_callback_success; } 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 = DAP_NEW_Z_SIZE(bson_t*,auth_count*sizeof(bson_t*)); { 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((int64_t) client->dap_http_client->client->download_stat.buf_size_total), "upload_bytes", BCON_INT64((int64_t) 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"); bson_error_t l_error={0}; bson_t l_reply ={0}; bool insert_ok = mongoc_collection_insert_many(collection,(const bson_t*) docs, auth_count, NULL,&l_reply, &l_error); 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); DAP_DELETE(docs); } /** * @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 * password_hash_b64 = dap_server_db_hash_password_b64(new_password); if (password_hash_b64 == NULL) { log_it(L_WARNING,"Bad hash(based64) for user password"); return false; } bson_t *update = BCON_NEW ("$set", "{", "passwordHash", BCON_UTF8 (password_hash_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); DAP_DELETE( password_hash_b64 ); log_it(L_INFO, "user: %s change password to %s", user, new_password); return true; } /** * @brief db_auth_set_field_str * @param a_user * @param a_field_name * @param a_field_value * @return */ bool db_auth_set_field_str(const char* a_user, const char* a_field_name, const char * a_field_value) { if ( exist_user_in_db(a_user) == false ) { log_it(L_WARNING, "Error set field str. User %s not find" , a_user); return false; } mongoc_collection_t *l_collection_dap_users = mongoc_client_get_collection (mongo_client, l_db_name, "dap_cookie_history"); bson_t *l_query = bson_new(); BSON_APPEND_UTF8 (l_query, "login", a_user); mongoc_cursor_t *l_cursor_dap_users = mongoc_collection_find (l_collection_dap_users, MONGOC_QUERY_NONE, 0, 0, 0, l_query, NULL, NULL); bson_t *l_doc_dap_user; mongoc_cursor_next (l_cursor_dap_users, (const bson_t**)&l_doc_dap_user); bson_error_t l_error; bson_t *l_update = BCON_NEW ("$set", "{", a_field_name, BCON_UTF8 (a_field_value), "}"); if (!mongoc_collection_update (l_collection_dap_users, MONGOC_UPDATE_NONE, l_doc_dap_user, l_update, NULL, &l_error)) { log_it(L_WARNING,"%s", l_error.message); return false; } mongoc_collection_destroy(l_collection_dap_users); if(l_query) bson_destroy(l_query); if(l_cursor_dap_users) mongoc_cursor_destroy(l_cursor_dap_users); if(l_doc_dap_user) bson_destroy(l_doc_dap_user); log_it(L_INFO, "set field \"%s\" with string \"%s\"", a_field_name, a_field_value ); return true; } /** * @brief check_user_password * @param user * @param password * @return false if user password not correct */ bool check_user_password(const char* a_user, const char* a_password) { if ( exist_user_in_db(a_user) == false ){ log_it(L_WARNING,"User %s is not present in DB",a_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", a_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); char* l_password_hash_b64 = dap_server_db_hash_password_b64(a_password); if (!l_password_hash_b64) { log_it(L_ERROR, "Can not memmory allocate"); return NULL; } if (bson_iter_init (&iter, doc) && bson_iter_find (&iter, "passwordHash")) { if ( memcmp(l_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); DAP_DELETE( l_password_hash_b64 ); return is_correct_password; } static bool s_save_cookie_inform_in_db(const char* login, db_auth_info_t * a_auth_info) { 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 (a_auth_info->cookie), "pkey", BCON_UTF8 (a_auth_info->pkey), "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 (a_auth_info->cookie), "pkey", BCON_UTF8 (a_auth_info->pkey), "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 dap_server_db_hash_password * @param password * @return */ unsigned char* dap_server_db_hash_password(const char* a_password) { static const unsigned char s_salt[]={ 0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08 }; static const size_t s_salt_size=sizeof (s_salt); unsigned char *md = DAP_NEW_Z_SIZE(unsigned char, DB_AUTH_HASH_LENGTH * 2); size_t a_password_length = strlen(a_password); size_t l_str_length = a_password_length + s_salt_size; unsigned char *l_str = DAP_NEW_Z_SIZE(unsigned char, l_str_length); memcpy(l_str, a_password, a_password_length); memcpy(l_str + a_password_length, s_salt, s_salt_size); SHA3_512(md, l_str, l_str_length); SHA3_512(md + DB_AUTH_HASH_LENGTH, s_salt, s_salt_size); DAP_DELETE( l_str ); return md; } char* dap_server_db_hash_password_b64(const char* a_password) { unsigned char* l_hash = dap_server_db_hash_password( a_password); char * l_hash_str = DAP_NEW_Z_SIZE(char, 4 * DB_AUTH_HASH_LENGTH+1 ) ; if (!l_hash_str) { DAP_DELETE( (char*)l_hash); log_it(L_ERROR, "Can not memmory allocate"); return NULL; } dap_enc_base64_encode(l_hash, DB_AUTH_HASH_LENGTH * 2, l_hash_str,DAP_ENC_DATA_TYPE_B64_URLSAFE); return l_hash_str; } /** * @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; unsigned const char* password_hash = dap_server_db_hash_password(password); 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; } }else log_it(L_NOTICE, "Haven't found expire_date in collection"); 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! " "Domains 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)-1); strncpy((*ai)->password,password,sizeof((*ai)->password)-1); 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)-1); 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)-1); 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)-1); 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); s_save_cookie_inform_in_db(login, (*ai)); pthread_mutex_lock(&mutex_on_auth_hash); HASH_ADD_STR(auths,cookie,(*ai)); pthread_mutex_unlock(&mutex_on_auth_hash); }else{ log_it(L_WARNING, "Input password has hash %s but expected to have %s",password_hash_b64, bson_iter_value(&iter)->value.v_utf8.str ); } }else{ log_it(L_WARNING, "No passwordHash in data"); } 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 * l_password_hash_b64 = dap_server_db_hash_password_b64(password); if (!l_password_hash_b64) { log_it(L_ERROR, "Can not memmory allocate"); return NULL; } if (*l_password_hash_b64 == 0) { log_it(L_ERROR, "Bad hash(based64) for user password"); return NULL; } bson_t *doc = BCON_NEW("login", user, "passwordHash", l_password_hash_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(l_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)-1); strncpy(ai->password,password,sizeof(ai->password)-1); strncpy(ai->last_name,last_name,sizeof(ai->last_name)-1); strncpy(ai->first_name,first_name,sizeof(ai->first_name)-1); strncpy(ai->email,email,sizeof(ai->email)-1); 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 * l_password_hash_b64 = dap_server_db_hash_password_b64(password); bson_t *doc = BCON_NEW("name_channel", name_channel, "passwordHash", l_password_hash_b64, "domainId", BCON_OID((bson_oid_t*)bson_iter_value(&iter)->value.v_oid.bytes), "subscribers", "[","]", "last_id_message", BCON_INT32(0), "messages","[","]"); DAP_DELETE( l_password_hash_b64 ); 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)-1); strncpy(ai->password,password,sizeof(ai->password)-1); for(size_t 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 a_delegate HTTP Simple client instance * @param a_arg Pointer to bool with okay status (true if everything is ok, by default) */ void db_auth_http_proc(enc_http_delegate_t *a_delegate, void * a_arg) { http_status_code_t * return_code = (http_status_code_t*)a_arg; if((a_delegate->request)&&(strcmp(a_delegate->action,"POST")==0)){ if(a_delegate->in_query==NULL){ log_it(L_WARNING,"Empty auth action"); *return_code = Http_Status_BadRequest; return; }else{ if(strcmp(a_delegate->in_query,"logout")==0 ){ db_auth_info_t * ai = db_auth_info_by_cookie(a_delegate->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(a_delegate, "<?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(a_delegate, "<?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(a_delegate->in_query,"login")==0 ){ char l_user[256]={0}; char l_password[1024]={0}; char l_domain[64]={0}; char l_pkey[4096]={0}; char l_something[64] = {0}; if(sscanf(a_delegate->request_str,"%255s %1023s %63s %4095s %64s",l_user,l_password,l_domain,l_pkey,l_something)>=3){ log_it(L_INFO, "Trying to login with username '%s'",l_user); if(!check_user_data_for_space(strlen(a_delegate->request_str), strlen(l_user) + strlen(l_password) + strlen(l_domain) + strlen(l_pkey) + strlen(l_something))){ log_it(L_WARNING,"Wrong symbols in username or password or domain, misfit is %d", strlen(a_delegate->request_str) - strlen(l_user) - strlen(l_password) - strlen(l_domain) - strlen(l_pkey) - strlen(l_something)); //log_it(L_WARNING, "l_user size: %d", strlen(l_user)); //log_it(L_WARNING, "l_pass size: %d", strlen(l_password)); //log_it(L_WARNING, "l_pkey size: %d", strlen(l_pkey)); log_it(L_DEBUG,"%s@%s", l_user, l_password); enc_http_reply_f(a_delegate, 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 * l_auth_info = NULL; short login_result = db_auth_login(l_user, l_password, l_domain, &l_auth_info); switch (login_result) { case 1: l_auth_info->dap_http_client = a_delegate->http; l_auth_info->auth_date = time(NULL); if (l_pkey[0] ) strncpy(l_auth_info->pkey,l_pkey, sizeof (l_auth_info->pkey)-1); enc_http_reply_f(a_delegate, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" "<auth_info>\n" ); enc_http_reply_f(a_delegate,"\t<first_name>%s</first_name>\n",l_auth_info->first_name); enc_http_reply_f(a_delegate,"\t<last_name>%s</last_name>\n",l_auth_info->last_name); enc_http_reply_f(a_delegate,"\t<cookie>%s</cookie>\n",l_auth_info->cookie); if (s_callback_success) s_callback_success (a_delegate, l_auth_info); // Here if smbd want to add smth to the output enc_http_reply_f(a_delegate,"</auth_info>"); log_it(L_INFO, "Login: Successfuly logined user %s",l_user); *return_code = Http_Status_OK; break; case 2: enc_http_reply_f(a_delegate, OP_CODE_NOT_FOUND_LOGIN_IN_DB); *return_code = Http_Status_OK; break; case 3: enc_http_reply_f(a_delegate, OP_CODE_LOGIN_INCORRECT_PSWD); *return_code = Http_Status_OK; break; case 4: enc_http_reply_f(a_delegate, OP_CODE_SUBSCRIBE_EXPIRIED); *return_code = Http_Status_PaymentRequired; break; default: log_it(L_DEBUG, "Login: wrong password for user %s",l_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(a_delegate->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", a_delegate->request_str); if(sscanf(a_delegate->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,a_delegate->http->client->hostaddr,sys_uuid); if(ai != NULL) { enc_http_reply_f(a_delegate, "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n" "<auth_info>\n" ); enc_http_reply_f(a_delegate,"\t<first_name>%s</first_name>\n",ai->first_name); enc_http_reply_f(a_delegate,"\t<last_name>%s</last_name>\n",ai->last_name); enc_http_reply_f(a_delegate,"\t<cookie>%s</cookie>\n",ai->cookie); enc_http_reply_f(a_delegate,"</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')",a_delegate->in_query); *return_code = Http_Status_BadRequest; } } }else{ log_it(L_ERROR, "Wrong auth request action '%s'",a_delegate->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; } /// 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) == 3; }