From d4d49ee8f4d425e7a4136d65f519728869680951 Mon Sep 17 00:00:00 2001
From: Ciaran Gultnieks <ciaran@ciarang.com>
Date: Fri, 20 May 2011 20:28:03 +0100
Subject: [PATCH] Passwords - password entry at main menu, stored and checked
 by server

---
 src/CMakeLists.txt  |   2 +
 src/base64.cpp      | 123 ++++++++++++++++++++++++++++
 src/base64.h        |   4 +
 src/client.cpp      |  21 ++++-
 src/client.h        |   9 +++
 src/clientserver.h  |   6 ++
 src/game.cpp        |  19 ++++-
 src/game.h          |   1 +
 src/guiMainMenu.cpp |  45 +++++++----
 src/guiMainMenu.h   |   1 +
 src/main.cpp        |  27 +++++++
 src/player.cpp      |   6 ++
 src/player.h        |  13 +++
 src/server.cpp      |  59 +++++++++++---
 src/server.h        |   6 +-
 src/sha1.cpp        | 191 ++++++++++++++++++++++++++++++++++++++++++++
 src/sha1.h          |  35 ++++++++
 17 files changed, 534 insertions(+), 34 deletions(-)
 create mode 100644 src/base64.cpp
 create mode 100644 src/base64.h
 create mode 100644 src/sha1.cpp
 create mode 100644 src/sha1.h

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 42260b3ae..26f4872da 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -95,6 +95,8 @@ set(minetest_SRCS
 	tile.cpp
 	game.cpp
 	main.cpp
+	sha1.cpp
+	base64.cpp
 )
 
 # Server sources
diff --git a/src/base64.cpp b/src/base64.cpp
new file mode 100644
index 000000000..2a863d161
--- /dev/null
+++ b/src/base64.cpp
@@ -0,0 +1,123 @@
+/* 
+   base64.cpp and base64.h
+
+   Copyright (C) 2004-2008 René Nyffenegger
+
+   This source code is provided 'as-is', without any express or implied
+   warranty. In no event will the author be held liable for any damages
+   arising from the use of this software.
+
+   Permission is granted to anyone to use this software for any purpose,
+   including commercial applications, and to alter it and redistribute it
+   freely, subject to the following restrictions:
+
+   1. The origin of this source code must not be misrepresented; you must not
+      claim that you wrote the original source code. If you use this source code
+      in a product, an acknowledgment in the product documentation would be
+      appreciated but is not required.
+
+   2. Altered source versions must be plainly marked as such, and must not be
+      misrepresented as being the original source code.
+
+   3. This notice may not be removed or altered from any source distribution.
+
+   René Nyffenegger rene.nyffenegger@adp-gmbh.ch
+
+*/
+
+#include "base64.h"
+#include <iostream>
+
+static const std::string base64_chars = 
+             "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+             "abcdefghijklmnopqrstuvwxyz"
+             "0123456789+/";
+
+
+static inline bool is_base64(unsigned char c) {
+  return (isalnum(c) || (c == '+') || (c == '/'));
+}
+
+std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
+  std::string ret;
+  int i = 0;
+  int j = 0;
+  unsigned char char_array_3[3];
+  unsigned char char_array_4[4];
+
+  while (in_len--) {
+    char_array_3[i++] = *(bytes_to_encode++);
+    if (i == 3) {
+      char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+      char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+      char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+      char_array_4[3] = char_array_3[2] & 0x3f;
+
+      for(i = 0; (i <4) ; i++)
+        ret += base64_chars[char_array_4[i]];
+      i = 0;
+    }
+  }
+
+  if (i)
+  {
+    for(j = i; j < 3; j++)
+      char_array_3[j] = '\0';
+
+    char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
+    char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
+    char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
+    char_array_4[3] = char_array_3[2] & 0x3f;
+
+    for (j = 0; (j < i + 1); j++)
+      ret += base64_chars[char_array_4[j]];
+
+    while((i++ < 3))
+      ret += '=';
+
+  }
+
+  return ret;
+
+}
+
+std::string base64_decode(std::string const& encoded_string) {
+  int in_len = encoded_string.size();
+  int i = 0;
+  int j = 0;
+  int in_ = 0;
+  unsigned char char_array_4[4], char_array_3[3];
+  std::string ret;
+
+  while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
+    char_array_4[i++] = encoded_string[in_]; in_++;
+    if (i ==4) {
+      for (i = 0; i <4; i++)
+        char_array_4[i] = base64_chars.find(char_array_4[i]);
+
+      char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+      char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+      char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+      for (i = 0; (i < 3); i++)
+        ret += char_array_3[i];
+      i = 0;
+    }
+  }
+
+  if (i) {
+    for (j = i; j <4; j++)
+      char_array_4[j] = 0;
+
+    for (j = 0; j <4; j++)
+      char_array_4[j] = base64_chars.find(char_array_4[j]);
+
+    char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
+    char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
+    char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
+
+    for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
+  }
+
+  return ret;
+}
diff --git a/src/base64.h b/src/base64.h
new file mode 100644
index 000000000..65d5db8b2
--- /dev/null
+++ b/src/base64.h
@@ -0,0 +1,4 @@
+#include <string>
+
+std::string base64_encode(unsigned char const* , unsigned int len);
+std::string base64_decode(std::string const& s);
diff --git a/src/client.cpp b/src/client.cpp
index ce862a002..e2cda97c1 100644
--- a/src/client.cpp
+++ b/src/client.cpp
@@ -68,6 +68,7 @@ void * MeshUpdateThread::Thread()
 Client::Client(
 		IrrlichtDevice *device,
 		const char *playername,
+		std::string password,
 		MapDrawControl &control):
 	m_mesh_update_thread(),
 	m_env(
@@ -83,7 +84,9 @@ Client::Client(
 	m_server_ser_ver(SER_FMT_VER_INVALID),
 	m_inventory_updated(false),
 	m_time_of_day(0),
-	m_map_seed(0)
+	m_map_seed(0),
+	m_password(password),
+	m_access_denied(false)
 {
 	m_packetcounter_timer = 0.0;
 	m_delete_unused_sectors_timer = 0.0;
@@ -299,11 +302,14 @@ void Client::step(float dtime)
 			// [0] u16 TOSERVER_INIT
 			// [2] u8 SER_FMT_VER_HIGHEST
 			// [3] u8[20] player_name
-			SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE);
+			// [23] u8[28] password
+			SharedBuffer<u8> data(2+1+PLAYERNAME_SIZE+PASSWORD_SIZE);
 			writeU16(&data[0], TOSERVER_INIT);
 			writeU8(&data[2], SER_FMT_VER_HIGHEST);
 			memset((char*)&data[3], 0, PLAYERNAME_SIZE);
 			snprintf((char*)&data[3], PLAYERNAME_SIZE, "%s", myplayer->getName());
+			snprintf((char*)&data[23], PASSWORD_SIZE, "%s", m_password.c_str());
+
 			// Send as unreliable
 			Send(0, data, false);
 		}
@@ -597,7 +603,16 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
 
 		return;
 	}
-	
+
+	if(command == TOCLIENT_ACCESS_DENIED)
+	{
+		// The server didn't like our password. Note, this needs
+		// to be processed even if the serialisation format has
+		// not been agreed yet, the same as TOCLIENT_INIT.
+		m_access_denied = true;
+		return;
+	}
+
 	if(ser_version == SER_FMT_VER_INVALID)
 	{
 		dout_client<<DTIME<<"WARNING: Client: Server serialization"
diff --git a/src/client.h b/src/client.h
index ee73cc42c..222f83ab7 100644
--- a/src/client.h
+++ b/src/client.h
@@ -207,6 +207,7 @@ class Client : public con::PeerHandler, public InventoryManager
 	Client(
 			IrrlichtDevice *device,
 			const char *playername,
+			std::string password,
 			MapDrawControl &control
 			);
 	
@@ -377,6 +378,11 @@ class Client : public con::PeerHandler, public InventoryManager
 	// Get event from queue. CE_NONE is returned if queue is empty.
 	ClientEvent getClientEvent();
 	
+	inline bool accessDenied()
+	{
+		return m_access_denied;
+	}
+
 private:
 	
 	// Virtual methods from con::PeerHandler
@@ -430,6 +436,9 @@ class Client : public con::PeerHandler, public InventoryManager
 	// The seed returned by the server in TOCLIENT_INIT is stored here
 	u64 m_map_seed;
 	
+	std::string m_password;
+	bool m_access_denied;
+
 	InventoryContext m_inventory_context;
 
 	Queue<ClientEvent> m_client_event_queue;
diff --git a/src/clientserver.h b/src/clientserver.h
index 46ffa5eab..a64a11f08 100644
--- a/src/clientserver.h
+++ b/src/clientserver.h
@@ -150,6 +150,11 @@ enum ToClientCommand
 		f1000 player pitch
 		f1000 player yaw
 	*/
+
+	TOCLIENT_ACCESS_DENIED = 0x35,
+	/*
+		u16 command
+	*/
 };
 
 enum ToServerCommand
@@ -161,6 +166,7 @@ enum ToServerCommand
 		[0] u16 TOSERVER_INIT
 		[2] u8 SER_FMT_VER_HIGHEST
 		[3] u8[20] player_name
+		[23] u8[28] password
 	*/
 
 	TOSERVER_INIT2 = 0x11,
diff --git a/src/game.cpp b/src/game.cpp
index d6b1117c1..69e673fa4 100644
--- a/src/game.cpp
+++ b/src/game.cpp
@@ -634,6 +634,7 @@ void the_game(
 	gui::IGUIFont* font,
 	std::string map_dir,
 	std::string playername,
+	std::string password,
 	std::string address,
 	u16 port,
 	std::wstring &error_message
@@ -689,7 +690,7 @@ void the_game(
 	*/
 
 	std::cout<<DTIME<<"Creating client"<<std::endl;
-	Client client(device, playername.c_str(), draw_control);
+	Client client(device, playername.c_str(), password, draw_control);
 			
 	Address connect_address(0,0,0,0, port);
 	try{
@@ -728,6 +729,10 @@ void the_game(
 				could_connect = true;
 				break;
 			}
+			if(client.accessDenied())
+			{
+				break;
+			}
 			// Wait for 10 seconds
 			if(time_counter >= 10.0)
 			{
@@ -756,8 +761,16 @@ void the_game(
 
 	if(could_connect == false)
 	{
-		std::cout<<DTIME<<"Timed out."<<std::endl;
-		error_message = L"Connection timed out.";
+		if(client.accessDenied())
+		{
+			error_message = L"Access denied. Check your password and try again.";
+			std::cout<<DTIME<<"Access denied."<<std::endl;
+		}
+		else
+		{
+			error_message = L"Connection timed out.";
+			std::cout<<DTIME<<"Timed out."<<std::endl;
+		}
 		gui_loadingtext->remove();
 		return;
 	}
diff --git a/src/game.h b/src/game.h
index 7cba1299e..eb289b8f2 100644
--- a/src/game.h
+++ b/src/game.h
@@ -67,6 +67,7 @@ void the_game(
 	gui::IGUIFont* font,
 	std::string map_dir,
 	std::string playername,
+	std::string password,
 	std::string address,
 	u16 port,
 	std::wstring &error_message
diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp
index ac02f79e3..a30e006a6 100644
--- a/src/guiMainMenu.cpp
+++ b/src/guiMainMenu.cpp
@@ -164,30 +164,38 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 		//t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_UPPERLEFT);
 	}
 
-	// Nickname
+	// Nickname + password
 	{
-		core::rect<s32> rect(0, 0, 100, 20);
-		rect += topleft_client + v2s32(40, 50+6);
-		const wchar_t *text = L"Nickname";
+		core::rect<s32> rect(0, 0, 110, 20);
+		rect += topleft_client + v2s32(35, 50+6);
+		const wchar_t *text = L"Name/Password";
 		Environment->addStaticText(text, rect, false, true, this, -1);
 	}
 	{
-		core::rect<s32> rect(0, 0, 250, 30);
+		core::rect<s32> rect(0, 0, 230, 30);
 		rect += topleft_client + v2s32(160, 50);
 		gui::IGUIElement *e = 
 		Environment->addEditBox(text_name.c_str(), rect, true, this, 258);
 		if(text_name == L"")
 			Environment->setFocus(e);
 	}
+	{
+		core::rect<s32> rect(0, 0, 120, 30);
+		rect += topleft_client + v2s32(size_client.X-60-100, 50);
+		gui::IGUIEditBox *e =
+		Environment->addEditBox(L"", rect, true, this, 264);
+		e->setPasswordBox(true);
+
+	}
 	// Address + port
 	{
-		core::rect<s32> rect(0, 0, 100, 20);
-		rect += topleft_client + v2s32(40, 100+6);
-		const wchar_t *text = L"Address + Port";
+		core::rect<s32> rect(0, 0, 110, 20);
+		rect += topleft_client + v2s32(35, 100+6);
+		const wchar_t *text = L"Address/Port";
 		Environment->addStaticText(text, rect, false, true, this, -1);
 	}
 	{
-		core::rect<s32> rect(0, 0, 250, 30);
+		core::rect<s32> rect(0, 0, 230, 30);
 		rect += topleft_client + v2s32(160, 100);
 		gui::IGUIElement *e = 
 		Environment->addEditBox(text_address.c_str(), rect, true, this, 256);
@@ -195,9 +203,9 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 			Environment->setFocus(e);
 	}
 	{
-		core::rect<s32> rect(0, 0, 100, 30);
+		core::rect<s32> rect(0, 0, 120, 30);
 		//rect += topleft_client + v2s32(160+250+20, 125);
-		rect += topleft_client + v2s32(size_client.X-40-100, 100);
+		rect += topleft_client + v2s32(size_client.X-60-100, 100);
 		Environment->addEditBox(text_port.c_str(), rect, true, this, 257);
 	}
 	{
@@ -208,13 +216,13 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	}
 	{
 		core::rect<s32> rect(0, 0, 250, 30);
-		rect += topleft_client + v2s32(40, 150);
+		rect += topleft_client + v2s32(35, 150);
 		Environment->addCheckBox(fancy_trees, rect, this, 263,
 				L"Fancy trees");
 	}
 	{
 		core::rect<s32> rect(0, 0, 250, 30);
-		rect += topleft_client + v2s32(40, 150+30);
+		rect += topleft_client + v2s32(35, 150+30);
 		Environment->addCheckBox(smooth_lighting, rect, this, 262,
 				L"Smooth Lighting");
 	}
@@ -245,12 +253,12 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
 	// Server parameters
 	{
 		core::rect<s32> rect(0, 0, 250, 30);
-		rect += topleft_server + v2s32(40, 30);
+		rect += topleft_server + v2s32(35, 30);
 		Environment->addCheckBox(creative_mode, rect, this, 259, L"Creative Mode");
 	}
 	{
 		core::rect<s32> rect(0, 0, 250, 30);
-		rect += topleft_server + v2s32(40, 60);
+		rect += topleft_server + v2s32(35, 60);
 		Environment->addCheckBox(enable_damage, rect, this, 261, L"Enable Damage");
 	}
 	// Map delete button
@@ -296,6 +304,11 @@ void GUIMainMenu::acceptInput()
 		if(e != NULL)
 			m_data->name = e->getText();
 	}
+	{
+		gui::IGUIElement *e = getElementFromId(264);
+		if(e != NULL)
+			m_data->password = e->getText();
+	}
 	{
 		gui::IGUIElement *e = getElementFromId(256);
 		if(e != NULL)
@@ -380,7 +393,7 @@ bool GUIMainMenu::OnEvent(const SEvent& event)
 		{
 			switch(event.GUIEvent.Caller->getID())
 			{
-			case 256: case 257: case 258:
+				case 256: case 257: case 258: case 264:
 				acceptInput();
 				quitMenu();
 				return true;
diff --git a/src/guiMainMenu.h b/src/guiMainMenu.h
index 4999d68ba..edd519024 100644
--- a/src/guiMainMenu.h
+++ b/src/guiMainMenu.h
@@ -46,6 +46,7 @@ struct MainMenuData
 	std::wstring address;
 	std::wstring port;
 	std::wstring name;
+	std::wstring password;
 	bool fancy_trees;
 	bool smooth_lighting;
 	// Server options
diff --git a/src/main.cpp b/src/main.cpp
index 5d607b2d8..0b181a36d 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -325,6 +325,8 @@ Making it more portable:
 #include "materials.h"
 #include "game.h"
 #include "keycode.h"
+#include "sha1.h"
+#include "base64.h"
 
 // This makes textures
 ITextureSource *g_texturesource = NULL;
@@ -1170,6 +1172,7 @@ int main(int argc, char *argv[])
 		return 0;
 	}
 
+
 	/*
 		More parameters
 	*/
@@ -1324,11 +1327,15 @@ int main(int argc, char *argv[])
 	*/
 	std::wstring error_message = L"";
 
+	// The password entered during the menu screen,
+	std::string password;
+
 	/*
 		Menu-game loop
 	*/
 	while(device->run() && kill == false)
 	{
+
 		// This is used for catching disconnects
 		try
 		{
@@ -1428,6 +1435,25 @@ int main(int argc, char *argv[])
 				}
 
 				playername = wide_to_narrow(menudata.name);
+
+				// Get an sha-1 hash of the player's name combined with
+				// the password entered. That's what the server uses as
+				// their password. (Exception : if the password field is
+				// blank, we send a blank password - this is for backwards
+				// compatibility with password-less players).
+				if(menudata.password.length() > 0)
+				{
+						std::string slt=playername + wide_to_narrow(menudata.password);
+						SHA1 *sha1 = new SHA1();
+						sha1->addBytes(slt.c_str(), slt.length());
+						unsigned char *digest = sha1->getDigest();
+						password = base64_encode(digest, 20);
+				}
+				else
+				{
+						password = "";
+				}
+
 				address = wide_to_narrow(menudata.address);
 				int newport = stoi(wide_to_narrow(menudata.port));
 				if(newport != 0)
@@ -1474,6 +1500,7 @@ int main(int argc, char *argv[])
 				font,
 				map_dir,
 				playername,
+				password,
 				address,
 				port,
 				error_message
diff --git a/src/player.cpp b/src/player.cpp
index 3f92e899c..539244709 100644
--- a/src/player.cpp
+++ b/src/player.cpp
@@ -87,6 +87,7 @@ Player::Player():
 	m_position(0,0,0)
 {
 	updateName("<not set>");
+	updatePassword("");
 	resetInventory();
 }
 
@@ -145,6 +146,7 @@ void Player::serialize(std::ostream &os)
 	Settings args;
 	args.setS32("version", 1);
 	args.set("name", m_name);
+	args.set("password", m_password);
 	args.setFloat("pitch", m_pitch);
 	args.setFloat("yaw", m_yaw);
 	args.setV3F("position", m_position);
@@ -179,6 +181,10 @@ void Player::deSerialize(std::istream &is)
 	//args.getS32("version");
 	std::string name = args.get("name");
 	updateName(name.c_str());
+	std::string password = "";
+	if(args.exists("password"))
+		password = args.get("password");
+	updatePassword(password.c_str());
 	m_pitch = args.getFloat("pitch");
 	m_yaw = args.getFloat("yaw");
 	m_position = args.getV3F("position");
diff --git a/src/player.h b/src/player.h
index be93766fd..925252e49 100644
--- a/src/player.h
+++ b/src/player.h
@@ -25,6 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "collision.h"
 
 #define PLAYERNAME_SIZE 20
+#define PASSWORD_SIZE 28       // Maximum password length. Allows for
+                               // base64-encoded SHA-1.
 
 #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.,"
 
@@ -121,6 +123,16 @@ class Player
 		return m_name;
 	}
 
+	virtual void updatePassword(const char *password)
+	{
+		snprintf(m_password, PASSWORD_SIZE, "%s", password);
+	}
+
+	const char * getPassword()
+	{
+		return m_password;
+	}
+
 	virtual bool isLocal() const = 0;
 
 	virtual void updateLight(u8 light_at_pos) {};
@@ -157,6 +169,7 @@ class Player
 
 protected:
 	char m_name[PLAYERNAME_SIZE];
+	char m_password[PASSWORD_SIZE];
 	f32 m_pitch;
 	f32 m_yaw;
 	v3f m_speed;
diff --git a/src/server.cpp b/src/server.cpp
index d211186eb..051ca85fb 100644
--- a/src/server.cpp
+++ b/src/server.cpp
@@ -1734,8 +1734,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		// [0] u16 TOSERVER_INIT
 		// [2] u8 SER_FMT_VER_HIGHEST
 		// [3] u8[20] player_name
+		// [23] u8[28] password <--- can be sent without this, from old versions
 
-		if(datasize < 3)
+		if(datasize < 2+1+PLAYERNAME_SIZE)
 			return;
 
 		derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
@@ -1767,17 +1768,41 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
 		*/
 		
 		// Get player name
-		const u32 playername_size = 20;
-		char playername[playername_size];
-		for(u32 i=0; i<playername_size-1; i++)
+		char playername[PLAYERNAME_SIZE];
+		for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
 		{
 			playername[i] = data[3+i];
 		}
-		playername[playername_size-1] = 0;
-		
+		playername[PLAYERNAME_SIZE-1] = 0;
+	
+		// Get password
+		char password[PASSWORD_SIZE];
+		if(datasize == 2+1+PLAYERNAME_SIZE)
+		{
+			// old version - assume blank password
+			*password = 0;
+		}
+		else
+		{
+				for(u32 i=0; i<PASSWORD_SIZE-1; i++)
+				{
+					password[i] = data[23+i];
+				}
+				password[PASSWORD_SIZE-1] = 0;
+		}
+		Player *checkplayer = m_env.getPlayer(playername);
+		if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password))
+		{
+			derr_server<<DTIME<<"Server: peer_id="<<peer_id
+					<<": supplied invalid password for "
+					<<playername<<std::endl;
+			SendAccessDenied(m_con, peer_id);
+			return;
+		}
+
 		// Get player
-		Player *player = emergePlayer(playername, "", peer_id);
-		//Player *player = m_env.getPlayer(peer_id);
+		Player *player = emergePlayer(playername, password, peer_id);
+
 
 		/*{
 			// DEBUG: Test serialization
@@ -3138,6 +3163,20 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
 	con.Send(peer_id, 0, data, true);
 }
 
+void Server::SendAccessDenied(con::Connection &con, u16 peer_id)
+{
+	DSTACK(__FUNCTION_NAME);
+	std::ostringstream os(std::ios_base::binary);
+
+	writeU16(os, TOCLIENT_ACCESS_DENIED);
+
+	// Make data buffer
+	std::string s = os.str();
+	SharedBuffer<u8> data((u8*)s.c_str(), s.size());
+	// Send as reliable
+	con.Send(peer_id, 0, data, true);
+}
+
 /*
 	Non-static send methods
 */
@@ -4052,8 +4091,7 @@ v3f findSpawnPos(ServerMap &map)
 			), BS);
 }
 
-Player *Server::emergePlayer(const char *name, const char *password,
-		u16 peer_id)
+Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
 {
 	/*
 		Try to get an existing player
@@ -4099,6 +4137,7 @@ Player *Server::emergePlayer(const char *name, const char *password,
 		//player->peer_id = PEER_ID_INEXISTENT;
 		player->peer_id = peer_id;
 		player->updateName(name);
+		player->updatePassword(password);
 
 		/*
 			Set player position
diff --git a/src/server.h b/src/server.h
index d8b47aef9..289f09618 100644
--- a/src/server.h
+++ b/src/server.h
@@ -436,6 +436,7 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	*/
 	
 	static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
+	static void SendAccessDenied(con::Connection &con, u16 peer_id);
 	
 	/*
 		Non-static send methods
@@ -476,11 +477,12 @@ class Server : public con::PeerHandler, public MapEventReceiver,
 	/*
 		Get a player from memory or creates one.
 		If player is already connected, return NULL
+		The password is not checked here - it is only used to
+		set the password if a new player is created.
 
 		Call with env and con locked.
 	*/
-	Player *emergePlayer(const char *name, const char *password,
-			u16 peer_id);
+	Player *emergePlayer(const char *name, const char *password, u16 peer_id);
 
 	/*
 		Update water pressure.
diff --git a/src/sha1.cpp b/src/sha1.cpp
new file mode 100644
index 000000000..93df10969
--- /dev/null
+++ b/src/sha1.cpp
@@ -0,0 +1,191 @@
+/* sha1.cpp
+
+Copyright (c) 2005 Michael D. Leonhard
+
+http://tamale.net/
+
+This file is licensed under the terms described in the
+accompanying LICENSE file.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#include "sha1.h"
+
+// print out memory in hexadecimal
+void SHA1::hexPrinter( unsigned char* c, int l )
+{
+	assert( c );
+	assert( l > 0 );
+	while( l > 0 )
+	{
+		printf( " %02x", *c );
+		l--;
+		c++;
+	}
+}
+
+// circular left bit rotation.  MSB wraps around to LSB
+Uint32 SHA1::lrot( Uint32 x, int bits )
+{
+	return (x<<bits) | (x>>(32 - bits));
+};
+
+// Save a 32-bit unsigned integer to memory, in big-endian order
+void SHA1::storeBigEndianUint32( unsigned char* byte, Uint32 num )
+{
+	assert( byte );
+	byte[0] = (unsigned char)(num>>24);
+	byte[1] = (unsigned char)(num>>16);
+	byte[2] = (unsigned char)(num>>8);
+	byte[3] = (unsigned char)num;
+}
+
+
+// Constructor *******************************************************
+SHA1::SHA1()
+{
+	// make sure that the data type is the right size
+	assert( sizeof( Uint32 ) * 5 == 20 );
+	
+	// initialize
+	H0 = 0x67452301;
+	H1 = 0xefcdab89;
+	H2 = 0x98badcfe;
+	H3 = 0x10325476;
+	H4 = 0xc3d2e1f0;
+	unprocessedBytes = 0;
+	size = 0;
+}
+
+// Destructor ********************************************************
+SHA1::~SHA1()
+{
+	// erase data
+	H0 = H1 = H2 = H3 = H4 = 0;
+	for( int c = 0; c < 64; c++ ) bytes[c] = 0;
+	unprocessedBytes = size = 0;
+}
+
+// process ***********************************************************
+void SHA1::process()
+{
+	assert( unprocessedBytes == 64 );
+	//printf( "process: " ); hexPrinter( bytes, 64 ); printf( "\n" );
+	int t;
+	Uint32 a, b, c, d, e, K, f, W[80];
+	// starting values
+	a = H0;
+	b = H1;
+	c = H2;
+	d = H3;
+	e = H4;
+	// copy and expand the message block
+	for( t = 0; t < 16; t++ ) W[t] = (bytes[t*4] << 24)
+									+(bytes[t*4 + 1] << 16)
+									+(bytes[t*4 + 2] << 8)
+									+ bytes[t*4 + 3];
+	for(; t< 80; t++ ) W[t] = lrot( W[t-3]^W[t-8]^W[t-14]^W[t-16], 1 );
+	
+	/* main loop */
+	Uint32 temp;
+	for( t = 0; t < 80; t++ )
+	{
+		if( t < 20 ) {
+			K = 0x5a827999;
+			f = (b & c) | ((b ^ 0xFFFFFFFF) & d);//TODO: try using ~
+		} else if( t < 40 ) {
+			K = 0x6ed9eba1;
+			f = b ^ c ^ d;
+		} else if( t < 60 ) {
+			K = 0x8f1bbcdc;
+			f = (b & c) | (b & d) | (c & d);
+		} else {
+			K = 0xca62c1d6;
+			f = b ^ c ^ d;
+		}
+		temp = lrot(a,5) + f + e + W[t] + K;
+		e = d;
+		d = c;
+		c = lrot(b,30);
+		b = a;
+		a = temp;
+		//printf( "t=%d %08x %08x %08x %08x %08x\n",t,a,b,c,d,e );
+	}
+	/* add variables */
+	H0 += a;
+	H1 += b;
+	H2 += c;
+	H3 += d;
+	H4 += e;
+	//printf( "Current: %08x %08x %08x %08x %08x\n",H0,H1,H2,H3,H4 );
+	/* all bytes have been processed */
+	unprocessedBytes = 0;
+}
+
+// addBytes **********************************************************
+void SHA1::addBytes( const char* data, int num )
+{
+	assert( data );
+	assert( num > 0 );
+	// add these bytes to the running total
+	size += num;
+	// repeat until all data is processed
+	while( num > 0 )
+	{
+		// number of bytes required to complete block
+		int needed = 64 - unprocessedBytes;
+		assert( needed > 0 );
+		// number of bytes to copy (use smaller of two)
+		int toCopy = (num < needed) ? num : needed;
+		// Copy the bytes
+		memcpy( bytes + unprocessedBytes, data, toCopy );
+		// Bytes have been copied
+		num -= toCopy;
+		data += toCopy;
+		unprocessedBytes += toCopy;
+		
+		// there is a full block
+		if( unprocessedBytes == 64 ) process();
+	}
+}
+
+// digest ************************************************************
+unsigned char* SHA1::getDigest()
+{
+	// save the message size
+	Uint32 totalBitsL = size << 3;
+	Uint32 totalBitsH = size >> 29;
+	// add 0x80 to the message
+	addBytes( "\x80", 1 );
+	
+	unsigned char footer[64] = {
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+	// block has no room for 8-byte filesize, so finish it
+	if( unprocessedBytes > 56 )
+		addBytes( (char*)footer, 64 - unprocessedBytes);
+	assert( unprocessedBytes <= 56 );
+	// how many zeros do we need
+	int neededZeros = 56 - unprocessedBytes;
+	// store file size (in bits) in big-endian format
+	storeBigEndianUint32( footer + neededZeros    , totalBitsH );
+	storeBigEndianUint32( footer + neededZeros + 4, totalBitsL );
+	// finish the final block
+	addBytes( (char*)footer, neededZeros + 8 );
+	// allocate memory for the digest bytes
+	unsigned char* digest = (unsigned char*)malloc( 20 );
+	// copy the digest bytes
+	storeBigEndianUint32( digest, H0 );
+	storeBigEndianUint32( digest + 4, H1 );
+	storeBigEndianUint32( digest + 8, H2 );
+	storeBigEndianUint32( digest + 12, H3 );
+	storeBigEndianUint32( digest + 16, H4 );
+	// return the digest
+	return digest;
+}
diff --git a/src/sha1.h b/src/sha1.h
new file mode 100644
index 000000000..2f92fe8d1
--- /dev/null
+++ b/src/sha1.h
@@ -0,0 +1,35 @@
+/* sha1.h
+
+Copyright (c) 2005 Michael D. Leonhard
+
+http://tamale.net/
+
+This file is licensed under the terms described in the
+accompanying LICENSE file.
+*/
+
+#ifndef SHA1_HEADER
+typedef unsigned int Uint32;
+
+class SHA1
+{
+	private:
+		// fields
+		Uint32 H0, H1, H2, H3, H4;
+		unsigned char bytes[64];
+		int unprocessedBytes;
+		Uint32 size;
+		void process();
+	public:
+		SHA1();
+		~SHA1();
+		void addBytes( const char* data, int num );
+		unsigned char* getDigest();
+		// utility methods
+		static Uint32 lrot( Uint32 x, int bits );
+		static void storeBigEndianUint32( unsigned char* byte, Uint32 num );
+		static void hexPrinter( unsigned char* c, int l );
+};
+
+#define SHA1_HEADER
+#endif
-- 
GitLab