diff --git a/src/servers/Server_REST/CMakeLists.txt b/src/servers/Server_REST/CMakeLists.txt index 4891ea36..f1aa42f7 100644 --- a/src/servers/Server_REST/CMakeLists.txt +++ b/src/servers/Server_REST/CMakeLists.txt @@ -30,6 +30,7 @@ if(UNIX) endif() endif() else() + add_definitions(-DSODIUM_STATIC) add_definitions(-D_WIN32_WINNT=0x601) add_definitions(-D_CRT_SECURE_NO_WARNINGS) include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/MySQL/") @@ -57,10 +58,13 @@ endif() include_directories(${Boost_INCLUDE_DIR}) +include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/libsodium/include) link_directories(${BOOST_LIBRARYDIR}) link_directories(${SERVER_COMMON_DIR}) link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/sapphire/datReader) +link_directories(${CMAKE_CURRENT_SOURCE_DIR}/../../libraries/external/libsodium/x64/Release/v141/static) + if(CMAKE_SIZEOF_VOID_P EQUAL 4) # 32 bit link @@ -89,7 +93,7 @@ set_target_properties(server_rest PROPERTIES if (UNIX) target_link_libraries (server_rest Common xivdat pthread mysqlclient dl z) else() - target_link_libraries (server_rest Common xivdat libmysql zlib1) + target_link_libraries (server_rest Common xivdat libmysql zlib1 libsodium) endif() target_link_libraries( server_rest ${Boost_LIBRARIES} ${Boost_LIBRARIES} ) diff --git a/src/servers/Server_REST/SapphireAPI.cpp b/src/servers/Server_REST/SapphireAPI.cpp index a1072585..e5db9fbb 100644 --- a/src/servers/Server_REST/SapphireAPI.cpp +++ b/src/servers/Server_REST/SapphireAPI.cpp @@ -13,6 +13,8 @@ #include #include +#include + extern Core::Db::Database g_database; Core::Network::SapphireAPI::SapphireAPI() @@ -27,7 +29,7 @@ Core::Network::SapphireAPI::~SapphireAPI() bool Core::Network::SapphireAPI::login( const std::string& username, const std::string& pass, std::string& sId ) { - std::string query = "SELECT account_id FROM accounts WHERE account_name = '" + username + "' AND account_pass = '" + pass + "';"; + std::string query = "SELECT account_id, account_pass FROM accounts WHERE account_name = '" + username + "';"; // check if a user with that name / password exists auto pQR = g_database.query( query ); @@ -35,6 +37,15 @@ bool Core::Network::SapphireAPI::login( const std::string& username, const std:: if( !pQR ) return false; + // id is assumed to be verified with SQL + // check password here + auto const accountPass = pQR->fetch()[1].getString(); + if ( crypto_pwhash_argon2i_str_verify( accountPass, pass.c_str(), pass.length()) != 0 ) + { + return false; + } + + // user found, proceed int32_t accountId = pQR->fetch()[0].getUInt32(); @@ -73,14 +84,14 @@ bool Core::Network::SapphireAPI::login( const std::string& username, const std:: bool Core::Network::SapphireAPI::insertSession( const uint32_t& accountId, std::string& sId ) { - // create session for the new sessionid and store to sessionlist - auto pSession = boost::make_shared< Session >(); - pSession->setAccountId( accountId ); - pSession->setSessionId( (uint8_t *)sId.c_str() ); + // create session for the new sessionid and store to sessionlist + auto pSession = boost::make_shared< Session >(); + pSession->setAccountId( accountId ); + pSession->setSessionId( (uint8_t *)sId.c_str() ); - m_sessionMap[sId] = pSession; + m_sessionMap[sId] = pSession; - return true; + return true; } @@ -98,11 +109,19 @@ bool Core::Network::SapphireAPI::createAccount( const std::string& username, con pQR = g_database.query( "SELECT MAX(account_id) FROM accounts;" ); int32_t accountId = pQR->fetch()[0].getUInt32() + 1; + + char hash[crypto_pwhash_STRBYTES]; + if (crypto_pwhash_argon2i_str(hash, pass.c_str(), pass.length(), crypto_pwhash_OPSLIMIT_INTERACTIVE, crypto_pwhash_MEMLIMIT_INTERACTIVE) != 0) + { + // Failed to allocate memory + return false; + } + // store the account to the db g_database.execute( "INSERT INTO accounts (account_Id, account_name, account_pass, account_created) VALUE(%i, '%s', '%s', %i);", accountId, username.c_str(), - pass.c_str(), + hash, time( NULL ) ); diff --git a/src/servers/Server_REST/main.cpp b/src/servers/Server_REST/main.cpp index df383565..adcd624e 100644 --- a/src/servers/Server_REST/main.cpp +++ b/src/servers/Server_REST/main.cpp @@ -29,6 +29,8 @@ #include "SapphireAPI.h" +#include + Core::Logger g_log; Core::Db::Database g_database; Core::Data::ExdData g_exdData; @@ -173,6 +175,12 @@ int main(int argc, char* argv[]) g_log.info( "Compiled: " __DATE__ " " __TIME__ ); g_log.info( "===========================================================" ); + if ( sodium_init() == -1 ) + { + g_log.fatal("Failed to initialize libsodium"); + } + g_log.info("Initialized libsodium"); + if (!loadSettings(argc, argv)) { throw std::exception(); @@ -279,7 +287,7 @@ int main(int argc, char* argv[]) std::string sId = pt.get( "sId" ); std::string secret = pt.get( "secret" ); std::string name = pt.get( "name" ); - + // reloadConfig(); int32_t accountId = g_sapphireAPI.checkSession( sId ); @@ -352,36 +360,36 @@ int main(int argc, char* argv[]) }; server.resource["^/sapphire-api/lobby/insertSession"]["POST"] = [&]( shared_ptr response, shared_ptr request ) { - print_request_info( request ); + print_request_info( request ); - try - { - using namespace boost::property_tree; - ptree pt; - read_json( request->content, pt ); + try + { + using namespace boost::property_tree; + ptree pt; + read_json( request->content, pt ); - std::string sId = pt.get( "sId" ); - uint32_t accountId = pt.get( "accountId" ); - std::string secret = pt.get( "secret" ); + std::string sId = pt.get( "sId" ); + uint32_t accountId = pt.get( "accountId" ); + std::string secret = pt.get( "secret" ); // reloadConfig(); - if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { - std::string json_string = "{\"result\":\"invalid_secret\"}"; - *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; - } - else - { - g_sapphireAPI.insertSession( accountId, sId ); - std::string json_string = "{\"result\":\"success\"}"; - *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; - } - } - catch( exception& e ) - { - *response << "HTTP/1.1 500\r\n\r\n"; - g_log.error( e.what() ); - } + if( m_pConfig->getValue< std::string >( "Settings.General.ServerSecret" ) != secret ) { + std::string json_string = "{\"result\":\"invalid_secret\"}"; + *response << "HTTP/1.1 403\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + else + { + g_sapphireAPI.insertSession( accountId, sId ); + std::string json_string = "{\"result\":\"success\"}"; + *response << "HTTP/1.1 200\r\nContent-Length: " << json_string.length() << "\r\n\r\n" << json_string; + } + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } }; @@ -589,88 +597,88 @@ int main(int argc, char* argv[]) }; server.resource["^(/frontier-api/ffxivsupport/view/get_init)(.*)"]["GET"] = [&]( shared_ptr response, shared_ptr request ) { - print_request_info( request ); + print_request_info( request ); - try - { - auto web_root_path = boost::filesystem::canonical( "web" ); - auto path = boost::filesystem::canonical( web_root_path / "news.xml" ); - //Check if path is within web_root_path - if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) || - !equal( web_root_path.begin(), web_root_path.end(), path.begin() ) ) - throw invalid_argument( "path must be within root path" ); - if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) ) - throw invalid_argument( "file does not exist" ); + try + { + auto web_root_path = boost::filesystem::canonical( "web" ); + auto path = boost::filesystem::canonical( web_root_path / "news.xml" ); + //Check if path is within web_root_path + if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) || + !equal( web_root_path.begin(), web_root_path.end(), path.begin() ) ) + throw invalid_argument( "path must be within root path" ); + if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) ) + throw invalid_argument( "file does not exist" ); - std::string cache_control, etag; + std::string cache_control, etag; - // Uncomment the following line to enable Cache-Control - // cache_control="Cache-Control: max-age=86400\r\n"; + // Uncomment the following line to enable Cache-Control + // cache_control="Cache-Control: max-age=86400\r\n"; - auto ifs = make_shared(); - ifs->open( path.string(), ifstream::in | ios::binary | ios::ate ); + auto ifs = make_shared(); + ifs->open( path.string(), ifstream::in | ios::binary | ios::ate ); - if( *ifs ) - { - auto length = ifs->tellg(); - ifs->seekg( 0, ios::beg ); + if( *ifs ) + { + auto length = ifs->tellg(); + ifs->seekg( 0, ios::beg ); - *response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n"; - default_resource_send( server, response, ifs ); - } - else - throw invalid_argument( "could not read file" ); - } - catch( exception& e ) - { - *response << "HTTP/1.1 500\r\n\r\n"; - g_log.error( e.what() ); - } + *response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n"; + default_resource_send( server, response, ifs ); + } + else + throw invalid_argument( "could not read file" ); + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } }; server.resource["^(/frontier-api/ffxivsupport/information/get_headline_all)(.*)"]["GET"] = [&]( shared_ptr response, shared_ptr request ) { - print_request_info( request ); + print_request_info( request ); - try - { - auto web_root_path = boost::filesystem::canonical( "web" ); - auto path = boost::filesystem::canonical( web_root_path / "headlines.xml" ); - //Check if path is within web_root_path - if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) || - !equal( web_root_path.begin(), web_root_path.end(), path.begin() ) ) - throw invalid_argument( "path must be within root path" ); - if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) ) - throw invalid_argument( "file does not exist" ); + try + { + auto web_root_path = boost::filesystem::canonical( "web" ); + auto path = boost::filesystem::canonical( web_root_path / "headlines.xml" ); + //Check if path is within web_root_path + if( distance( web_root_path.begin(), web_root_path.end() ) > distance( path.begin(), path.end() ) || + !equal( web_root_path.begin(), web_root_path.end(), path.begin() ) ) + throw invalid_argument( "path must be within root path" ); + if( !( boost::filesystem::exists( path ) && boost::filesystem::is_regular_file( path ) ) ) + throw invalid_argument( "file does not exist" ); - std::string cache_control, etag; + std::string cache_control, etag; - // Uncomment the following line to enable Cache-Control - // cache_control="Cache-Control: max-age=86400\r\n"; + // Uncomment the following line to enable Cache-Control + // cache_control="Cache-Control: max-age=86400\r\n"; - auto ifs = make_shared(); - ifs->open( path.string(), ifstream::in | ios::binary | ios::ate ); + auto ifs = make_shared(); + ifs->open( path.string(), ifstream::in | ios::binary | ios::ate ); - if( *ifs ) - { - auto length = ifs->tellg(); - ifs->seekg( 0, ios::beg ); + if( *ifs ) + { + auto length = ifs->tellg(); + ifs->seekg( 0, ios::beg ); - *response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n"; - default_resource_send( server, response, ifs ); - } - else - throw invalid_argument( "could not read file" ); - } - catch( exception& e ) - { - *response << "HTTP/1.1 500\r\n\r\n"; - g_log.error( e.what() ); - } + *response << "HTTP/1.1 200 OK\r\n" << cache_control << etag << "Content-Length: " << length << "\r\n\r\n"; + default_resource_send( server, response, ifs ); + } + else + throw invalid_argument( "could not read file" ); + } + catch( exception& e ) + { + *response << "HTTP/1.1 500\r\n\r\n"; + g_log.error( e.what() ); + } }; - //Default GET-example. If no other matches, this anonymous function will be called. + //Default GET-example. If no other matches, this anonymous function will be called. //Will respond with content in the web/-directory, and its subdirectories. //Default file: index.html //Can for instance be used to retrieve an HTML 5 client that uses REST-resources on this server @@ -723,7 +731,7 @@ int main(int argc, char* argv[]) //Wait for server to start so that the client can connect this_thread::sleep_for( chrono::seconds( 1 ) ); - + server_thread.join(); g_log.info( "Started REST server at port " + std::to_string( server.config.port ) );