Username/Password Authentication for UI
This commit is contained in:
+5
-1
@@ -191,7 +191,11 @@ input_t input {
|
|||||||
|
|
||||||
sunshine_t sunshine {
|
sunshine_t sunshine {
|
||||||
2, // min_log_level
|
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) {
|
bool whitespace(char ch) {
|
||||||
|
|||||||
+4
-1
@@ -85,8 +85,11 @@ enum flag_e : std::size_t {
|
|||||||
|
|
||||||
struct sunshine_t {
|
struct sunshine_t {
|
||||||
int min_log_level;
|
int min_log_level;
|
||||||
|
|
||||||
std::bitset<flag::FLAG_SIZE> flags;
|
std::bitset<flag::FLAG_SIZE> flags;
|
||||||
|
std::string credentials_file;
|
||||||
|
std::string username;
|
||||||
|
std::string password;
|
||||||
|
std::string salt;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern video_t video;
|
extern video_t video;
|
||||||
|
|||||||
+27
-2
@@ -13,6 +13,7 @@
|
|||||||
#include <boost/asio/ssl/context.hpp>
|
#include <boost/asio/ssl/context.hpp>
|
||||||
|
|
||||||
#include <Simple-Web-Server/server_http.hpp>
|
#include <Simple-Web-Server/server_http.hpp>
|
||||||
|
#include <Simple-Web-Server/crypto.hpp>
|
||||||
#include <boost/asio/ssl/context_base.hpp>
|
#include <boost/asio/ssl/context_base.hpp>
|
||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
@@ -49,16 +50,40 @@ enum class op_e
|
|||||||
REMOVE
|
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)
|
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);
|
auto ip_type = net::from_address(address);
|
||||||
if(ip_type > http::origin_pin_allowed) {
|
if(ip_type > http::origin_pin_allowed) {
|
||||||
BOOST_LOG(info) << '[' << address << "] -- denied"sv;
|
BOOST_LOG(info) << '[' << address << "] -- denied"sv;
|
||||||
response->write(SimpleWeb::StatusCode::client_error_forbidden);
|
response->write(SimpleWeb::StatusCode::client_error_forbidden);
|
||||||
return false;
|
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 <class T>
|
template <class T>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#include <openssl/pem.h>
|
#include <openssl/pem.h>
|
||||||
#include "crypto.h"
|
#include "crypto.h"
|
||||||
|
#include <iostream>
|
||||||
namespace crypto {
|
namespace crypto {
|
||||||
using big_num_t = util::safe_ptr<BIGNUM, BN_free>;
|
using big_num_t = util::safe_ptr<BIGNUM, BN_free>;
|
||||||
//using rsa_t = util::safe_ptr<RSA, RSA_free>;
|
//using rsa_t = util::safe_ptr<RSA, RSA_free>;
|
||||||
@@ -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) {
|
void md_ctx_destroy(EVP_MD_CTX *ctx) {
|
||||||
EVP_MD_CTX_destroy(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
#include <openssl/sha.h>
|
#include <openssl/sha.h>
|
||||||
#include <openssl/x509.h>
|
#include <openssl/x509.h>
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
#include "utility.h"
|
#include "utility.h"
|
||||||
|
|
||||||
@@ -35,6 +36,7 @@ using bio_t = util::safe_ptr<BIO, BIO_free_all>;
|
|||||||
using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
|
using pkey_t = util::safe_ptr<EVP_PKEY, EVP_PKEY_free>;
|
||||||
|
|
||||||
sha256_t hash(const std::string_view &plaintext);
|
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<uint8_t, 16> &salt, const std::string_view &pin);
|
aes_t gen_aes_key(const std::array<uint8_t, 16> &salt, const std::string_view &pin);
|
||||||
|
|
||||||
x509_t x509(const std::string_view &x);
|
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_view signature(const x509_t &x);
|
||||||
|
|
||||||
std::string rand(std::size_t bytes);
|
std::string rand(std::size_t bytes);
|
||||||
|
std::string rand_string(std::size_t bytes);
|
||||||
|
|
||||||
class cert_chain_t {
|
class cert_chain_t {
|
||||||
public:
|
public:
|
||||||
|
|||||||
@@ -27,8 +27,11 @@ namespace http
|
|||||||
{
|
{
|
||||||
using namespace std::literals;
|
using namespace std::literals;
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
namespace pt = boost::property_tree;
|
||||||
|
|
||||||
int create_creds(const std::string &pkey, const std::string &cert);
|
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);
|
std::string read_file(const char *path);
|
||||||
int write_file(const char *path, const std::string_view &contents);
|
int write_file(const char *path, const std::string_view &contents);
|
||||||
std::string unique_id;
|
std::string unique_id;
|
||||||
@@ -54,6 +57,52 @@ namespace http
|
|||||||
return;
|
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<std::string>("username");
|
||||||
|
config::sunshine.password = inputTree.get<std::string>("password");
|
||||||
|
config::sunshine.salt = inputTree.get<std::string>("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)
|
int create_creds(const std::string &pkey, const std::string &cert)
|
||||||
|
|||||||
Reference in New Issue
Block a user