From 0ea6363172dce933f43b5f3058875f6e6d7fe4d9 Mon Sep 17 00:00:00 2001 From: Elia Zammuto Date: Fri, 28 May 2021 22:49:27 +0200 Subject: [PATCH] Username/Password Authentication for UI --- sunshine/config.cpp | 6 ++++- sunshine/config.h | 5 ++++- sunshine/confighttp.cpp | 29 ++++++++++++++++++++++-- sunshine/crypto.cpp | 21 ++++++++++++++++++ sunshine/crypto.h | 3 +++ sunshine/httpcommon.cpp | 49 +++++++++++++++++++++++++++++++++++++++++ 6 files changed, 109 insertions(+), 4 deletions(-) diff --git a/sunshine/config.cpp b/sunshine/config.cpp index d2a97d2e..4e08cf1d 100644 --- a/sunshine/config.cpp +++ b/sunshine/config.cpp @@ -191,7 +191,11 @@ input_t input { sunshine_t sunshine { 2, // min_log_level - 0 // flags + 0, // flags + "user_credentials.json"s,//User file + ""s,//Username + ""s,//Password + ""s//Password Salt }; bool whitespace(char ch) { diff --git a/sunshine/config.h b/sunshine/config.h index 1dbcabbc..0daf0146 100644 --- a/sunshine/config.h +++ b/sunshine/config.h @@ -85,8 +85,11 @@ enum flag_e : std::size_t { struct sunshine_t { int min_log_level; - std::bitset flags; + std::string credentials_file; + std::string username; + std::string password; + std::string salt; }; extern video_t video; diff --git a/sunshine/confighttp.cpp b/sunshine/confighttp.cpp index e8fe4184..a707d8de 100644 --- a/sunshine/confighttp.cpp +++ b/sunshine/confighttp.cpp @@ -13,6 +13,7 @@ #include #include +#include #include #include "config.h" @@ -49,16 +50,40 @@ enum class op_e REMOVE }; +void send_unauthorized(resp_https_t response, req_https_t request) +{ + auto address = request->remote_endpoint_address(); + BOOST_LOG(info) << '[' << address << "] -- denied"sv; + const SimpleWeb::CaseInsensitiveMultimap headers { + {"WWW-Authenticate",R"(Basic realm="Sunshine Gamestream Host", charset="UTF-8")"} + }; + response->write(SimpleWeb::StatusCode::client_error_unauthorized,headers); +} + bool authenticate(resp_https_t response, req_https_t request) { -auto address = request->remote_endpoint_address(); + auto address = request->remote_endpoint_address(); auto ip_type = net::from_address(address); if(ip_type > http::origin_pin_allowed) { BOOST_LOG(info) << '[' << address << "] -- denied"sv; response->write(SimpleWeb::StatusCode::client_error_forbidden); return false; } - return true; + auto auth = request->header.find("authorization"); + if(auth == request->header.end() ){ + send_unauthorized(response,request); + return false; + } + std::string rawAuth = auth->second; + std::string authData = rawAuth.substr("Basic "sv.length()); + authData = SimpleWeb::Crypto::Base64::decode(authData); + int index = authData.find(':'); + std::string username = authData.substr(0,index); + std::string password = authData.substr(index + 1); + std::string hash = crypto::hash_hexstr(password + config::sunshine.salt); + if(username == config::sunshine.username && hash == config::sunshine.password) return true; + send_unauthorized(response,request); + return false; } template diff --git a/sunshine/crypto.cpp b/sunshine/crypto.cpp index 398c8e09..b4a5ce80 100644 --- a/sunshine/crypto.cpp +++ b/sunshine/crypto.cpp @@ -4,6 +4,7 @@ #include #include "crypto.h" +#include namespace crypto { using big_num_t = util::safe_ptr; //using rsa_t = util::safe_ptr; @@ -338,4 +339,24 @@ bool verify256(const x509_t &x509, const std::string_view &data, const std::stri void md_ctx_destroy(EVP_MD_CTX *ctx) { EVP_MD_CTX_destroy(ctx); } + +std::string rand_string(std::size_t bytes) +{ + std::string alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!%&()=-"; + std::string value = rand(bytes); + for (std::size_t i = 0; i != value.size(); ++i) { + value[i] = alphabet[value[i] % alphabet.length()]; + } + return value; +} + +std::string hash_hexstr(const std::string_view &plaintext) +{ + sha256_t hashBytes = crypto::hash(plaintext); + std::ostringstream hashStream; + hashStream << std::hex << std::setfill( '0' ); + std::for_each( hashBytes.cbegin(), hashBytes.cend(), [&]( int c ) { hashStream << std::setw( 2 ) << c; } ); + std::string hashString = hashStream.str(); + return hashString; +} } diff --git a/sunshine/crypto.h b/sunshine/crypto.h index faf1fc1f..f0781597 100644 --- a/sunshine/crypto.h +++ b/sunshine/crypto.h @@ -11,6 +11,7 @@ #include #include #include +#include #include "utility.h" @@ -35,6 +36,7 @@ using bio_t = util::safe_ptr; using pkey_t = util::safe_ptr; sha256_t hash(const std::string_view &plaintext); +std::string hash_hexstr(const std::string_view &plaintext); aes_t gen_aes_key(const std::array &salt, const std::string_view &pin); x509_t x509(const std::string_view &x); @@ -50,6 +52,7 @@ creds_t gen_creds(const std::string_view &cn, std::uint32_t key_bits); std::string_view signature(const x509_t &x); std::string rand(std::size_t bytes); +std::string rand_string(std::size_t bytes); class cert_chain_t { public: diff --git a/sunshine/httpcommon.cpp b/sunshine/httpcommon.cpp index 53da503a..b8763ffe 100644 --- a/sunshine/httpcommon.cpp +++ b/sunshine/httpcommon.cpp @@ -27,8 +27,11 @@ namespace http { using namespace std::literals; namespace fs = std::filesystem; + namespace pt = boost::property_tree; int create_creds(const std::string &pkey, const std::string &cert); + int generate_user_creds(const std::string &file); + int reload_user_creds(const std::string &file); std::string read_file(const char *path); int write_file(const char *path, const std::string_view &contents); std::string unique_id; @@ -54,8 +57,54 @@ namespace http return; } } + if(!fs::exists(config::sunshine.credentials_file)){ + if(generate_user_creds(config::sunshine.credentials_file)){ + shutdown_event->raise(true); + return; + } + } + if(reload_user_creds(config::sunshine.credentials_file)){ + shutdown_event->raise(true); + return; + } } + int generate_user_creds(const std::string &file) + { + pt::ptree outputTree; + try { + std::string username = "sunshine"; + std::string plainPassword = crypto::rand_string(16); + std::string salt = crypto::rand_string(16); + outputTree.put("username","sunshine"); + outputTree.put("salt",salt); + outputTree.put("password",crypto::hash_hexstr(plainPassword + salt)); + BOOST_LOG(info) << "New credentials has been created"; + BOOST_LOG(info) << "Username: " << username; + BOOST_LOG(info) << "Password: " << plainPassword; + pt::write_json(file,outputTree); + } catch (std::exception &e){ + BOOST_LOG(fatal) << e.what(); + return 1; + } + return 0; + } + + int reload_user_creds(const std::string &file) + { + pt::ptree inputTree; + try { + pt::read_json(file, inputTree); + config::sunshine.username = inputTree.get("username"); + config::sunshine.password = inputTree.get("password"); + config::sunshine.salt = inputTree.get("salt"); + } catch(std::exception &e){ + BOOST_LOG(fatal) << e.what(); + return 1; + } + return 0; + } + int create_creds(const std::string &pkey, const std::string &cert) { fs::path pkey_path = pkey;