2019-07-17 13:37:18 -04:00
# include <misc/endianness.h>
# include <misc/base64.h>
# include <log/LogUtils.h>
# include <LicenseManager.pb.h>
# include <shared/License.h>
# include "LicenseRequest.pb.h"
# include "shared/License.h"
# include "LicenseServer.h"
# include "WebAPI.h"
# include "StatisticManager.h"
# include "UserManager.h"
using namespace std ;
using namespace std : : chrono ;
using namespace license ;
using namespace ts ;
inline void generate ( char * buffer , size_t length ) {
for ( int index = 0 ; index < length ; index + + )
buffer [ index ] = rand ( ) ;
}
# define TEST_PROTOCOL_STATE(expected) \
if ( client - > protocol . state ! = protocol : : expected ) { \
error = " invalid protocol state " ; \
return false ; \
}
# define ENSURE_PACKET_SIZE(expected) \
if ( packet . data . length ( ) < ( expected ) ) { \
error = " too small packet! " ; \
return false ; \
}
# define PARSE_PROTO(class, var) \
ts : : proto : : license : : class var ; \
if ( ! var . ParseFromString ( packet . data ) ) { \
error = " invalid data! " ; \
return false ; \
}
# define CRYPT_KEY_LENGTH 32
bool LicenseServer : : handleHandshake ( shared_ptr < ConnectedClient > & client , protocol : : packet & packet , std : : string & error ) {
TEST_PROTOCOL_STATE ( HANDSCAKE ) ;
ENSURE_PACKET_SIZE ( 4 ) ;
if ( ( uint8_t ) packet . data [ 0 ] ! = 0xC0 | | ( uint8_t ) packet . data [ 1 ] ! = 0xFF | | ( uint8_t ) packet . data [ 2 ] ! = 0xEE ) {
error = " invalid magic! " ;
return false ;
}
if ( ( uint8_t ) packet . data [ 3 ] ! = LICENSE_PROT_VERSION ) {
error = " invalid version! " ;
return false ;
}
bool manager = false ;
if ( packet . data . length ( ) > = 4 & & ( uint8_t ) packet . data [ 4 ] = = 1 ) {
manager = true ;
//Its a manager!
}
char buffer_cryptkey [ CRYPT_KEY_LENGTH ] ;
generate ( buffer_cryptkey , CRYPT_KEY_LENGTH ) ;
uint8_t buffer [ 128 ] ;
size_t buffer_index = 0 ;
buffer [ buffer_index + + ] = 0xAF ;
buffer [ buffer_index + + ] = 0xFE ;
buffer [ buffer_index + + ] = LICENSE_PROT_VERSION ;
le2be16 ( CRYPT_KEY_LENGTH , buffer , buffer_index , & buffer_index ) ;
memcpy ( & buffer [ buffer_index ] , buffer_cryptkey , CRYPT_KEY_LENGTH ) ;
buffer_index + = CRYPT_KEY_LENGTH ;
if ( manager )
buffer [ buffer_index + + ] = 0x01 ; //Manager accepted
client - > sendPacket ( { protocol : : PACKET_SERVER_HANDSHAKE , string ( ( char * ) buffer , buffer_index ) } ) ;
client - > protocol . cryptKey = string ( buffer_cryptkey , 32 ) ;
if ( manager ) {
client - > protocol . state = protocol : : MANAGER_AUTHORIZATION ;
client - > type = ClientType : : MANAGER ;
} else {
client - > protocol . state = protocol : : SERVER_VALIDATION ;
client - > type = ClientType : : SERVER ;
}
return true ;
}
bool LicenseServer : : handleDisconnect ( shared_ptr < ConnectedClient > & client , protocol : : packet & packet , std : : string & error ) {
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Remote disconnect. Reason: " + packet . data ) ;
this - > closeConnection ( client ) ;
return true ;
}
inline void fill_info ( proto : : license : : LicenseInfo * proto , const shared_ptr < LicenseInfo > & info , const std : : string & key ) {
proto - > set_key ( key ) ;
2019-08-25 16:16:42 -04:00
if ( info ) {
proto - > set_username ( info - > username ) ;
proto - > set_first_name ( info - > first_name ) ;
proto - > set_last_name ( info - > last_name ) ;
proto - > set_email ( info - > email ) ;
proto - > set_type ( info - > type ) ;
proto - > set_created ( duration_cast < milliseconds > ( info - > creation . time_since_epoch ( ) ) . count ( ) ) ;
proto - > set_begin ( duration_cast < milliseconds > ( info - > start . time_since_epoch ( ) ) . count ( ) ) ;
proto - > set_end ( duration_cast < milliseconds > ( info - > end . time_since_epoch ( ) ) . count ( ) ) ;
} else {
proto - > set_username ( " invalid (null) " ) ;
proto - > set_first_name ( " invalid (null) " ) ;
proto - > set_last_name ( " invalid (null) " ) ;
proto - > set_email ( " invalid (null) " ) ;
proto - > set_type ( 0 ) ;
proto - > set_created ( 0 ) ;
proto - > set_begin ( 0 ) ;
proto - > set_end ( 0 ) ;
}
2019-07-17 13:37:18 -04:00
}
std : : string string_to_hex ( const std : : string & input )
{
static const char * const lut = " 0123456789ABCDEF " ;
size_t len = input . length ( ) ;
std : : string output ;
output . reserve ( 2 * len ) ;
for ( size_t i = 0 ; i < len ; + + i )
{
const unsigned char c = input [ i ] ;
output . push_back ( lut [ c > > 4 ] ) ;
output . push_back ( lut [ c & 15 ] ) ;
}
return output ;
}
bool LicenseServer : : handleServerValidation ( shared_ptr < ConnectedClient > & client , protocol : : packet & packet , std : : string & error ) {
TEST_PROTOCOL_STATE ( SERVER_VALIDATION ) ;
PARSE_PROTO ( ServerValidation , pkt ) ;
shared_ptr < License > remoteLicense = nullptr ;
if ( pkt . licensed ( ) & & ! pkt . has_license ( ) ) {
//TODO shutdown server
}
if ( ! pkt . has_info ( ) ) {
error = " invalid data or missing data " ;
return false ;
}
if ( pkt . has_license ( ) ) { //Client has license
remoteLicense = readLocalLicence ( pkt . license ( ) , error ) ;
if ( ! remoteLicense ) {
error = " Could not read remote key: " + error ;
return false ;
} ;
logMessage ( LOG_GENERAL , " [CLIENT][{}] Got remote license. Registered to {}. Key: {} (0x{}) " , client - > address ( ) , remoteLicense - > owner ( ) , base64 : : encode ( remoteLicense - > key ( ) ) , string_to_hex ( remoteLicense - > key ( ) ) ) ;
client - > key = remoteLicense - > key ( ) ;
} else { }
if ( pkt . licensed ( ) & & ! pkt . has_license_info ( ) ) {
error = " Invalid content! " ;
return false ;
}
logMessage ( LOG_GENERAL , " [CLIENT][{}] Got some server information. TeaSpeak-Version: {} uname: {} " , client - > address ( ) , pkt . info ( ) . version ( ) , pkt . info ( ) . uname ( ) ) ;
ts : : proto : : license : : LicenseResponse response ;
//Forces
client - > unique_identifier = pkt . info ( ) . has_unique_id ( ) ? pkt . info ( ) . unique_id ( ) : client - > address ( ) ;
if ( remoteLicense & & pkt . licensed ( ) ) {
auto info = this - > manager - > licenseInfo ( remoteLicense - > key ( ) ) ;
2019-08-25 16:16:42 -04:00
2019-07-17 13:37:18 -04:00
if ( ! info ) {
2019-08-25 16:16:42 -04:00
response . mutable_blacklist ( ) - > set_reason ( " License hasn't been found. " ) ;
2019-07-17 13:37:18 -04:00
response . set_valid ( false ) ;
/*
logMessage ( LOG_GENERAL , " [CLIENT][{}] Unknown license! Adding it to database! " , client - > address ( ) ) ;
auto db_info = make_shared < LicenseInfo > ( ) ;
db_info - > start = system_clock : : now ( ) ;
db_info - > end = remoteLicense - > end ( ) ;
db_info - > last_name = " unknown " ;
db_info - > first_name = " unknown " ;
db_info - > username = remoteLicense - > owner ( ) ;
db_info - > email = " unknonw@unknown " ;
db_info - > creation = system_clock : : now ( ) ;
db_info - > type = remoteLicense - > data . type ;
this - > manager - > registerLicense ( remoteLicense - > key ( ) , db_info , " teaforo-fix " ) ;
info = this - > manager - > licenseInfo ( remoteLicense - > key ( ) ) ;
if ( ! info ) {
error = " could not insert key! " ;
return false ;
}
*/
logMessage ( LOG_GENERAL , " [CLIENT][{}] Remote license hasn't been found in database. Shutting down server! " , client - > address ( ) ) ;
} else {
if ( info - > deleted ) {
2019-08-25 16:16:42 -04:00
response . mutable_blacklist ( ) - > set_reason ( " License has been deleted. " ) ;
2019-07-17 13:37:18 -04:00
response . set_valid ( false ) ;
logMessage ( LOG_GENERAL , " [CLIENT][{}] Remote license has been deleted! Shutting down server! " , client - > address ( ) ) ;
} else {
fill_info ( response . mutable_license_info ( ) , info , remoteLicense - > data . licenceKey ) ;
response . set_valid ( info - > isValid ( ) ) ;
}
}
this - > manager - > logRequest ( remoteLicense - > key ( ) , client - > unique_identifier , client - > address ( ) , pkt . info ( ) . version ( ) , response . valid ( ) ) ;
} else {
response . set_valid ( true ) ;
}
2019-08-25 16:16:42 -04:00
if ( response . valid ( ) )
response . mutable_blacklist ( ) - > set_state ( ts : : proto : : license : : VALID ) ;
else {
if ( ! response . has_license_info ( ) )
fill_info ( response . mutable_license_info ( ) , nullptr , " " ) ; /* "Hack" for old clients which require a license. Else the server would not be stopped */
response . mutable_blacklist ( ) - > set_state ( ts : : proto : : license : : BLACKLISTED ) ; /* "Hack" for all old clients */
client - > invalid_license = true ;
}
2019-07-17 13:37:18 -04:00
client - > sendPacket ( protocol : : packet { protocol : : PACKET_SERVER_VALIDATION_RESPONSE , response } ) ;
client - > protocol . state = protocol : : PROPERTY_ADJUSTMENT ;
return true ;
}
bool LicenseServer : : handlePacketPropertyUpdate ( shared_ptr < ConnectedClient > & client , protocol : : packet & packet , std : : string & error ) {
TEST_PROTOCOL_STATE ( PROPERTY_ADJUSTMENT ) ;
2019-08-25 16:16:42 -04:00
if ( client - > invalid_license ) {
ts : : proto : : license : : PropertyUpdateResponse response ;
response . set_accepted ( true ) ;
response . set_reset_speach ( 0 ) ;
response . set_speach_total_remote ( 0 ) ;
response . set_speach_varianz_corrector ( 0 ) ;
client - > sendPacket ( protocol : : packet { protocol : : PACKET_SERVER_PROPERTY_ADJUSTMENT , response } ) ;
this - > disconnectClient ( client , " finished " ) ;
/* Not sure if we really want here to log these data. May put a lvalid=false within the db? */
return true ;
}
2019-07-17 13:37:18 -04:00
PARSE_PROTO ( PropertyUpdateRequest , pkt ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Got server statistics: " ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Spoken total : " + to_string ( pkt . speach_total ( ) ) ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Spoken dead : " + to_string ( pkt . speach_dead ( ) ) ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Spoken online : " + to_string ( pkt . speach_online ( ) ) ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Spoken varianz : " + to_string ( pkt . speach_varianz ( ) ) ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] ------------------------------- " ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Users online : " + to_string ( pkt . clients_online ( ) ) ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Web Users online : " + to_string ( pkt . web_clients_online ( ) ) ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Queries online : " + to_string ( pkt . queries_online ( ) ) ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Bots online : " + to_string ( pkt . bots_online ( ) ) ) ;
logMessage ( " [CLIENT][ " + client - > address ( ) + " ] Servers : " + to_string ( pkt . servers_online ( ) ) ) ;
this - > manager - > logStatistic ( client - > key , client - > unique_identifier , client - > address ( ) , pkt ) ;
//TODO test stuff!
ts : : proto : : license : : PropertyUpdateResponse response ;
response . set_accepted ( true ) ;
response . set_reset_speach ( pkt . speach_total ( ) < 0 ) ;
response . set_speach_total_remote ( pkt . speach_total ( ) ) ;
response . set_speach_varianz_corrector ( 0 ) ;
client - > sendPacket ( protocol : : packet { protocol : : PACKET_SERVER_PROPERTY_ADJUSTMENT , response } ) ;
this - > disconnectClient ( client , " finished " ) ;
if ( this - > statistics )
this - > statistics - > reset_cache_general ( ) ;
if ( this - > web_statistics )
this - > web_statistics - > async_broadcast_notify_general_update ( ) ;
return true ;
}
bool LicenseServer : : handlePacketAuth ( shared_ptr < ConnectedClient > & client , protocol : : packet & packet , std : : string & error ) {
TEST_PROTOCOL_STATE ( MANAGER_AUTHORIZATION ) ;
PARSE_PROTO ( AuthorizationRequest , pkt ) ;
logMessage ( " [MANAGER][ " + client - > address ( ) + " ] Got login. User: " + pkt . username ( ) + " Password: " + pkt . password ( ) ) ;
ts : : proto : : license : : AuthorizationResponse response ;
response . set_success ( false ) ;
if ( this - > user_manager ) {
auto user_account = this - > user_manager - > find_user ( pkt . username ( ) ) ;
if ( user_account ) {
if ( user_account - > verify_password ( pkt . password ( ) ) ) {
switch ( user_account - > status ( ) ) {
case User : : Status : : ACTIVE :
response . set_success ( true ) ;
break ;
case User : : Status : : BANNED :
response . set_message ( " you have been banned " ) ;
break ;
case User : : Status : : DISABLED :
response . set_message ( " you have been disabled " ) ;
break ;
default :
response . set_message ( " Your account hasn't been activated " ) ;
break ;
}
}
}
} else {
response . set_success ( pkt . password ( ) = = " HelloWorld " ) ;
}
if ( ! response . has_message ( ) & & ! response . success ( ) )
response . set_message ( " username or password mismatch " ) ;
if ( response . success ( ) ) {
logMessage ( " [MANAGER][ " + client - > address ( ) + " ] Got succeeded user login. User: " + pkt . username ( ) + " Password: " + pkt . password ( ) ) ;
client - > username = pkt . username ( ) ;
client - > protocol . state = protocol : : MANAGER_CONNECTED ;
} else
logMessage ( " [MANAGER][ " + client - > address ( ) + " ] Got failed user login. User: " + pkt . username ( ) + " Password: " + pkt . password ( ) ) ;
client - > sendPacket ( protocol : : packet { protocol : : PACKET_SERVER_AUTH_RESPONSE , response } ) ;
return true ;
}
bool LicenseServer : : handlePacketLicenseCreate ( shared_ptr < ConnectedClient > & client , protocol : : packet & packet , std : : string & error ) {
TEST_PROTOCOL_STATE ( MANAGER_CONNECTED ) ;
PARSE_PROTO ( LicenseCreateRequest , pkt ) ;
logMessage ( LOG_GENERAL , " [MANAGER][ " + client - > address ( ) + " ] Register new license to {} {} ({}). E-Mail: {} " , pkt . issuer_first_name ( ) , pkt . issuer_last_name ( ) , pkt . issuer_username ( ) , pkt . issuer_email ( ) ) ;
ts : : proto : : license : : LicenseCreateResponse response ;
auto db_info = make_shared < LicenseInfo > ( ) ;
db_info - > start = system_clock : : time_point ( ) + milliseconds ( pkt . begin ( ) ) ;
db_info - > end = system_clock : : time_point ( ) + milliseconds ( pkt . end ( ) ) ;
db_info - > last_name = pkt . issuer_last_name ( ) ;
db_info - > first_name = pkt . issuer_first_name ( ) ;
db_info - > username = pkt . issuer_username ( ) ;
db_info - > email = pkt . issuer_email ( ) ;
db_info - > creation = system_clock : : now ( ) ;
db_info - > type = static_cast < LicenseType > ( pkt . type ( ) ) ;
auto license = license : : createLocalLicence ( db_info - > type , db_info - > end , db_info - > first_name + db_info - > last_name ) ;
auto parsed_license = license : : readLocalLicence ( license , error ) ;
if ( ! parsed_license ) {
response . set_error ( " failed to register license (parse) " ) ;
} else {
if ( ! this - > manager - > registerLicense ( parsed_license - > key ( ) , db_info , client - > username ) ) {
response . set_error ( " failed to register license " ) ;
} else {
fill_info ( response . mutable_license ( ) , db_info , parsed_license - > key ( ) ) ;
response . set_exported_key ( license ) ;
}
}
client - > sendPacket ( protocol : : packet { protocol : : PACKET_SERVER_LICENSE_CREATE_RESPONSE , response } ) ;
return true ;
}
bool LicenseServer : : handlePacketLicenseList ( shared_ptr < ConnectedClient > & client , protocol : : packet & packet , std : : string & error ) {
TEST_PROTOCOL_STATE ( MANAGER_CONNECTED ) ;
PARSE_PROTO ( LicenseListRequest , pkt ) ;
proto : : license : : LicenseListResponse response ;
response . set_end ( false ) ;
for ( const auto & info : this - > manager - > listLicenses ( pkt . offset ( ) , pkt . count ( ) ) ) {
auto entry = response . add_entries ( ) ;
fill_info ( entry , info . second , info . first ) ;
}
client - > sendPacket ( protocol : : packet { protocol : : PACKET_SERVER_LIST_RESPONSE , response } ) ;
return true ;
}
bool LicenseServer : : handlePacketLicenseDelete ( shared_ptr < ConnectedClient > & client , protocol : : packet & packet , std : : string & error ) {
TEST_PROTOCOL_STATE ( MANAGER_CONNECTED ) ;
PARSE_PROTO ( LicenseDeleteRequest , pkt ) ;
proto : : license : : LicenseDeleteResponse response ;
response . set_succeed ( this - > manager - > deleteLicense ( pkt . key ( ) , pkt . full ( ) ) ) ;
client - > sendPacket ( protocol : : packet { protocol : : PACKET_CLIENT_DELETE_RESPONSE , response } ) ;
return true ;
}