Skip to content

Commit

Permalink
Added threaded user validation
Browse files Browse the repository at this point in the history
  • Loading branch information
fador committed Aug 7, 2012
1 parent 0b040e1 commit 41e827b
Show file tree
Hide file tree
Showing 10 changed files with 222 additions and 21 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ C++ compiler. MS Visual C++ 10 and GCC 4.4 should be sufficient. The build syste
* Physics for water (currently revising this)
* Flatland and terrain map generation (Also biomegen!)
* Working chests, furnaces & signs
* User validation from minecraft.net
* Protocol Encryption (NEW)

### Configuration Notes
Expand Down Expand Up @@ -71,6 +72,7 @@ Mineserver requires the following libraries:
* [libevent 1.4.14b](http://monkey.org/~provos/libevent/)
* [libnoise 1.0](http://libnoise.sourceforge.net/)
* [openssl/libssl](http://www.openssl.org/)
* [pthread](http://en.wikipedia.org/wiki/POSIX_Threads)

* Installing on Debian and Ubuntu: (For Ubuntu libevent1 -> libevent-1.4-2)

Expand Down
5 changes: 1 addition & 4 deletions files/config.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,12 @@ system.interface.use_cli = true;
system.server_name = "Mineserver testserver";

# Validate usernames against minecraft.net? NOT WORKING
system.user_validation = false;
system.user_validation = true;

# Encryption required for validation
# will encrypt the whole protocol with AES/CFB8
system.protocol_encryption = true;

# Allow connecting when auth is down
system.allow_connect_on_auth_timeout = true;

# Disclose Software Version
system.show_version = true;

Expand Down
5 changes: 5 additions & 0 deletions include/mineserver.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@
#include "plugin_api.h"
#undef MINESERVER

#include <pthread.h>

struct event_base;

class Mineserver
Expand All @@ -77,6 +79,9 @@ class Mineserver
bool m_damage_enabled;
bool m_only_helmets;
struct event m_listenEvent;
pthread_mutex_t m_validation_mutex;
struct userValidation { User* user; bool valid; uint32_t UID; };
std::vector<userValidation> validatedUsers;

#ifdef PROTOCOL_ENCRYPTION
//Protocol encryption
Expand Down
1 change: 1 addition & 0 deletions include/sockets.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@

extern "C" void accept_callback(int fd, short ev, void* arg);
extern "C" void client_callback(int fd, short ev, void* arg);
extern "C" void *user_validation_thread(void *arg);
4 changes: 2 additions & 2 deletions include/user.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ class User
OpenInventory openInv;
std::string secret;
EVP_CIPHER_CTX en, de;

std::string generateDigest();
void initCipher()
{
{
unsigned char key[16], iv[16];
memcpy(&iv,secret.c_str(),16);
memcpy(&key,secret.c_str(),16);
Expand Down
57 changes: 50 additions & 7 deletions src/mineserver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#ifdef DEBUG & _MSC_VER
#ifdef DEBUG
#ifdef _MSC_VER
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
#include <crtdbg.h>
#endif
#endif

#ifdef WIN32
Expand Down Expand Up @@ -127,8 +129,10 @@ int printHelp(int code)
int main(int argc, char* argv[])
{
bool ret = false;
#ifdef DEBUG & _MSC_VER
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#ifdef DEBUG
#ifdef _MSC_VER
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
#endif
#endif
// Try and start a new server instance
try
Expand Down Expand Up @@ -170,7 +174,8 @@ Mineserver::Mineserver(int args, char **argarray)
m_furnaceManager(NULL),
m_packetHandler (NULL),
m_inventory (NULL),
m_mobs (NULL)
m_mobs (NULL),
m_validation_mutex(PTHREAD_MUTEX_INITIALIZER)
{
ServerInstance = this;
InitSignals();
Expand Down Expand Up @@ -283,7 +288,6 @@ Mineserver::Mineserver(int args, char **argarray)

LOG2(INFO, "Welcome to Mineserver v" + VERSION);
LOG2(INFO, "Using zlib "+std::string(ZLIB_VERSION)+" libevent "+std::string(event_get_version()));
#ifdef PROTOCOL_ENCRYPTION

LOG2(INFO, "Generating RSA key pair for protocol encryption");
//Protocol encryption
Expand Down Expand Up @@ -327,7 +331,11 @@ Mineserver::Mineserver(int args, char **argarray)
}

LOG2(INFO, "ServerID: " + serverID);
#endif

if(!m_config->bData("system.user_validation"))
{
serverID = "-";
}

MapGen* mapgen = new MapGen();
MapGen* nethergen = new NetherGen();
Expand Down Expand Up @@ -755,6 +763,41 @@ bool Mineserver::run()
// Check for Furnace activity
furnaceManager()->update();

// Check for user validation results
pthread_mutex_lock(&ServerInstance->m_validation_mutex);
for(int i = 0; i < ServerInstance->validatedUsers.size(); i++)
{
//To make sure user hasn't timed out or anything while validating
User *tempuser = NULL;
for (std::set<User*>::const_iterator it = users().begin(); it != users().end(); ++it)
{
if((*it)->UID == ServerInstance->validatedUsers[i].UID)
{
tempuser = (*it);
break;
}
}

if(tempuser != NULL)
{
if(ServerInstance->validatedUsers[i].valid)
{
LOG(INFO, "Packets", tempuser->nick + " is VALID ");
tempuser->crypted = true;
tempuser->buffer << (int8_t)PACKET_ENCRYPTION_RESPONSE << (int16_t)0 << (int16_t) 0;
tempuser->uncryptedLeft = 5;
}
else
{
tempuser->kick("User not Premium");
}
//Flush
client_callback(tempuser->fd, EV_WRITE, tempuser);
}
}
ServerInstance->validatedUsers.clear();
pthread_mutex_unlock(&ServerInstance->m_validation_mutex);

// Run 1s timer hook
static_cast<Hook0<bool>*>(plugin()->getHook("Timer1000"))->doAll();
}
Expand Down
24 changes: 18 additions & 6 deletions src/packets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@
#include "mob.h"
#include "utf8.h"
#include "protocol.h"
#include "sockets.h"

#ifdef PROTOCOL_ENCRYPTION
#include <openssl/rsa.h>
Expand Down Expand Up @@ -213,13 +214,24 @@ int PacketHandler::encryption_response(User* user)
user->secret = std::string((char *)buffer, ret);
//We're going crypted!
user->initCipher();
user->crypted = true;

//Response
user->buffer << (int8_t)PACKET_ENCRYPTION_RESPONSE << (int16_t)0 << (int16_t) 0;
user->uncryptedLeft = 5; //5 first bytes are uncrypted

//ToDo: validate user

if(!ServerInstance->config()->bData("system.user_validation"))
{
//Response
user->crypted = true;
user->buffer << (int8_t)PACKET_ENCRYPTION_RESPONSE << (int16_t)0 << (int16_t) 0;
user->uncryptedLeft = 5; //5 first bytes are uncrypted
}
else
{
pthread_t validation_thread;
Mineserver::userValidation* valid = new Mineserver::userValidation;
valid->user = user;
valid->UID = user->UID;
pthread_create(&validation_thread,NULL,user_validation_thread,(void*)valid);
}


return PACKET_OK;
}
Expand Down
92 changes: 92 additions & 0 deletions src/sockets.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ typedef int socklen_t;
#include "nbt.h"
#include "mineserver.h"
#include "packets.h"
#include "config.h"


extern int setnonblock(int fd);
Expand Down Expand Up @@ -260,3 +261,94 @@ extern "C" void accept_callback(int fd, short ev, void* arg)
event_set(client->GetEvent(), client_fd, EV_WRITE | EV_READ, client_callback, client);
event_add(client->GetEvent(), NULL);
}


int socket_connect(char* host, int port)
{
struct hostent* hp;
struct sockaddr_in addr;
int on = 1, sock;

if ((hp = gethostbyname(host)) == NULL)
{
return 0;
}

memmove(&addr.sin_addr, hp->h_addr, hp->h_length);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

struct timeval tv;
tv.tv_sec = 5;
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval));
setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&tv, sizeof(struct timeval));

setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (const char*)&on, sizeof(int));

if (sock == -1)
{
return 0;
}

if (connect(sock, (struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1)
{
return 0;
}

return sock;
}

void *user_validation_thread(void *arg)
{
Mineserver::userValidation *user = reinterpret_cast<Mineserver::userValidation *>(arg);
user->valid=false;
std::string url = "/game/checkserver.jsp?user=" + user->user->nick + "&serverId=" + user->user->generateDigest();
LOG(INFO, "Packets", "Validating " + user->user->nick + " against minecraft.net: ");

std::string http_request = "GET " + url + " HTTP/1.1\r\n"
+ "Host: session.minecraft.net\r\n"
+ "Connection: close\r\n\r\n";

int fd = socket_connect((char*)"session.minecraft.net", 80);
if (fd)
{
#ifdef WIN32
send(fd, http_request.c_str(), http_request.length(), NULL);
#else
write(fd, http_request.c_str(), http_request.length());
#endif

#define BUFFER_SIZE 1024
char* buffer = new char[BUFFER_SIZE];
std::string stringbuffer;

#ifdef WIN32
while (int received = recv(fd, buffer, BUFFER_SIZE - 1, NULL) != 0)
{
#else
while (read(fd, buffer, BUFFER_SIZE - 1) != 0)
{
#endif
stringbuffer += std::string(buffer);
}
delete[] buffer;
#ifdef WIN32
closesocket(fd);
#else
close(fd);
#endif

if ((stringbuffer.size() >= 3 && stringbuffer.find("\r\n\r\nYES", 0) != std::string::npos))
{
user->valid = true;
}
}
pthread_mutex_lock(&ServerInstance->m_validation_mutex);
ServerInstance->validatedUsers.push_back(*user);
pthread_mutex_unlock(&ServerInstance->m_validation_mutex);
user->user = NULL;
delete user;
pthread_exit(NULL);
return NULL;
}
49 changes: 49 additions & 0 deletions src/user.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1493,3 +1493,52 @@ void User::setCurrentItemSlot(int16_t item_slot)
{
m_currentItemSlot = item_slot;
}

std::string User::generateDigest()
{
SHA_CTX context;
unsigned char md[20];
std::string out;
int i;
const char hex[] = "0123456789abcdef";

SHA1_Init(&context);
SHA1_Update(&context, (unsigned char*)ServerInstance->serverID.data(), ServerInstance->serverID.size());
SHA1_Update(&context, (unsigned char*)secret.data(), secret.size());
SHA1_Update(&context, (unsigned char*)ServerInstance->publicKey.data(), ServerInstance->publicKey.size());
SHA1_Final(md, &context);

if(md[0] & 0x80)
{
unsigned char carry = 1;
out = '-';
for (i = 19; i >= 0; i--)
{
unsigned short twocomp = (unsigned char)~md[i];
twocomp+=carry;
if(twocomp & 0xff00)
{
twocomp = twocomp&0xff;
}
else
{
carry = 0;
}
md[i] = twocomp;
}
}

for (i = 0; i < 20; i++)
{
if(i || md[i]>>4)
{
out += hex[(md[i]>>4)];
}
if(i || md[i]>>4 || md[i]&0xf)
{
out += hex[(md[i]&0xf)];
}
}

return out;
}
4 changes: 2 additions & 2 deletions win32/mineserver.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>libeay32.lib;ssleay32.lib;Winmm.lib;libnoise.lib;zlibwapi.lib;ws2_32.lib;libevent.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>pthread.lib;libeay32.lib;ssleay32.lib;Winmm.lib;libnoise.lib;zlibwapi.lib;ws2_32.lib;libevent.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>LIBCMT</IgnoreSpecificDefaultLibraries>
</Link>
</ItemDefinitionGroup>
Expand All @@ -238,7 +238,7 @@
<GenerateDebugInformation>false</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>libeay32.lib;ssleay32.lib;Winmm.lib;libnoise.lib;zlibwapi.lib;libevent.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>pthread.lib;libeay32.lib;ssleay32.lib;Winmm.lib;libnoise.lib;zlibwapi.lib;libevent.lib;Ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>LIBCMT</IgnoreSpecificDefaultLibraries>
<AdditionalLibraryDirectories>
</AdditionalLibraryDirectories>
Expand Down

0 comments on commit 41e827b

Please sign in to comment.