player passwords and privileges in world/auth.txt

--HG--
extra : rebase_source : 7260636295d9068fbeeddf4143c89f2b8a91446c
This commit is contained in:
Perttu Ahola 2011-05-29 21:11:16 +03:00
parent 99c2ac0125
commit 63611932eb
22 changed files with 590 additions and 175 deletions

@ -1,4 +1,5 @@
map/* map/*
world/*
CMakeFiles/* CMakeFiles/*
src/CMakeFiles/* src/CMakeFiles/*
src/Makefile src/Makefile

@ -13,6 +13,7 @@ X:
- Slightly updated map format - Slightly updated map format
- Player passwords - Player passwords
- All textures first searched from texture_path - All textures first searched from texture_path
- Map directory ("map") has been renamed to "world" (just rename it to load an old world)
2011-04-24: 2011-04-24:
- Smooth lighting with simple ambient occlusion - Smooth lighting with simple ambient occlusion

@ -102,6 +102,11 @@
#enable_damage = false #enable_damage = false
#default_password =
# Available privileges: build, teleport, settime, privs, shout
#default_privs = build, shout
# Gives some stuff to players at the beginning # Gives some stuff to players at the beginning
#give_initial_stuff = false #give_initial_stuff = false

@ -50,6 +50,7 @@ configure_file(
) )
set(common_SRCS set(common_SRCS
auth.cpp
collision.cpp collision.cpp
nodemetadata.cpp nodemetadata.cpp
serverobject.cpp serverobject.cpp

244
src/auth.cpp Normal file

@ -0,0 +1,244 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include "auth.h"
#include <fstream>
#include <jmutexautolock.h>
//#include "main.h" // for g_settings
#include <sstream>
#include "strfnd.h"
#include "debug.h"
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::string privsToString(u64 privs)
{
std::ostringstream os(std::ios_base::binary);
if(privs & PRIV_BUILD)
os<<"build,";
if(privs & PRIV_TELEPORT)
os<<"teleport,";
if(privs & PRIV_SETTIME)
os<<"settime,";
if(privs & PRIV_PRIVS)
os<<"privs,";
if(privs & PRIV_SHOUT)
os<<"shout,";
if(os.tellp())
{
// Drop the trailing comma. (Why on earth can't
// you truncate a C++ stream anyway???)
std::string tmp = os.str();
return tmp.substr(0, tmp.length() -1);
}
return os.str();
}
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::string str)
{
u64 privs=0;
Strfnd f(str);
while(f.atend() == false)
{
std::string s = trim(f.next(","));
if(s == "build")
privs |= PRIV_BUILD;
else if(s == "teleport")
privs |= PRIV_TELEPORT;
else if(s == "settime")
privs |= PRIV_SETTIME;
else if(s == "privs")
privs |= PRIV_PRIVS;
else if(s == "shout")
privs |= PRIV_SHOUT;
else
return PRIV_INVALID;
}
return privs;
}
AuthManager::AuthManager(const std::string &authfilepath):
m_authfilepath(authfilepath)
{
m_mutex.Init();
try{
load();
}
catch(SerializationError &e)
{
dstream<<"WARNING: AuthManager: creating "
<<m_authfilepath<<std::endl;
}
}
AuthManager::~AuthManager()
{
save();
}
void AuthManager::load()
{
JMutexAutoLock lock(m_mutex);
dstream<<"AuthManager: loading from "<<m_authfilepath<<std::endl;
std::ifstream is(m_authfilepath.c_str(), std::ios::binary);
if(is.good() == false)
{
dstream<<"AuthManager: failed loading from "<<m_authfilepath<<std::endl;
throw SerializationError("AuthManager::load(): Couldn't open file");
}
for(;;)
{
if(is.eof() || is.good() == false)
break;
// Read a line
std::string line;
std::getline(is, line, '\n');
std::istringstream iss(line);
// Read name
std::string name;
std::getline(iss, name, ':');
// Read password
std::string pwd;
std::getline(iss, pwd, ':');
// Read privileges
std::string stringprivs;
std::getline(iss, stringprivs, ':');
u64 privs = stringToPrivs(stringprivs);
// Store it
AuthData ad;
ad.pwd = pwd;
ad.privs = privs;
m_authdata[name] = ad;
}
}
void AuthManager::save()
{
JMutexAutoLock lock(m_mutex);
dstream<<"AuthManager: saving to "<<m_authfilepath<<std::endl;
std::ofstream os(m_authfilepath.c_str(), std::ios::binary);
if(os.good() == false)
{
dstream<<"AuthManager: failed saving to "<<m_authfilepath<<std::endl;
throw SerializationError("AuthManager::save(): Couldn't open file");
}
for(core::map<std::string, AuthData>::Iterator
i = m_authdata.getIterator();
i.atEnd()==false; i++)
{
std::string name = i.getNode()->getKey();
if(name == "")
continue;
AuthData ad = i.getNode()->getValue();
os<<name<<":"<<ad.pwd<<":"<<privsToString(ad.privs)<<"\n";
}
}
bool AuthManager::exists(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
return false;
return true;
}
void AuthManager::set(const std::string &username, AuthData ad)
{
JMutexAutoLock lock(m_mutex);
m_authdata[username] = ad;
}
void AuthManager::add(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
m_authdata[username] = AuthData();
}
std::string AuthManager::getPassword(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
return n->getValue().pwd;
}
void AuthManager::setPassword(const std::string &username,
const std::string &password)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
AuthData ad = n->getValue();
ad.pwd = password;
n->setValue(ad);
}
u64 AuthManager::getPrivs(const std::string &username)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
return n->getValue().privs;
}
void AuthManager::setPrivs(const std::string &username, u64 privs)
{
JMutexAutoLock lock(m_mutex);
core::map<std::string, AuthData>::Node *n;
n = m_authdata.find(username);
if(n == NULL)
throw AuthNotFoundException("");
AuthData ad = n->getValue();
ad.privs = privs;
n->setValue(ad);
}

99
src/auth.h Normal file

@ -0,0 +1,99 @@
/*
Minetest-c55
Copyright (C) 2011 celeron55, Perttu Ahola <celeron55@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef AUTH_HEADER
#define AUTH_HEADER
#include <string>
#include <jthread.h>
#include <jmutex.h>
#include "common_irrlicht.h"
#include "exceptions.h"
// Player privileges. These form a bitmask stored in the privs field
// of the player, and define things they're allowed to do. See also
// the static methods Player::privsToString and stringToPrivs that
// convert these to human-readable form.
const u64 PRIV_BUILD = 1; // Can build - i.e. modify the world
const u64 PRIV_TELEPORT = 2; // Can teleport
const u64 PRIV_SETTIME = 4; // Can set the time
const u64 PRIV_PRIVS = 8; // Can grant and revoke privileges
const u64 PRIV_SERVER = 16; // Can manage the server (e.g. shutodwn
// ,settings)
const u64 PRIV_SHOUT = 32; // Can broadcast chat messages to all
// players
// Default privileges - these can be overriden for new players using the
// config option "default_privs" - however, this value still applies for
// players that existed before the privileges system was added.
const u64 PRIV_DEFAULT = PRIV_BUILD|PRIV_SHOUT;
const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
const u64 PRIV_INVALID = 0x8000000000000000ULL;
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::string privsToString(u64 privs);
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::string str);
struct AuthData
{
std::string pwd;
u64 privs;
AuthData():
privs(PRIV_DEFAULT)
{
}
};
class AuthNotFoundException : public BaseException
{
public:
AuthNotFoundException(const char *s):
BaseException(s)
{}
};
class AuthManager
{
public:
AuthManager(const std::string &authfilepath);
~AuthManager();
void load();
void save();
bool exists(const std::string &username);
void set(const std::string &username, AuthData ad);
void add(const std::string &username);
std::string getPassword(const std::string &username);
void setPassword(const std::string &username,
const std::string &password);
u64 getPrivs(const std::string &username);
void setPrivs(const std::string &username, u64 privs);
private:
JMutex m_mutex;
std::string m_authfilepath;
core::map<std::string, AuthData> m_authdata;
};
#endif

@ -610,6 +610,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
// to be processed even if the serialisation format has // to be processed even if the serialisation format has
// not been agreed yet, the same as TOCLIENT_INIT. // not been agreed yet, the same as TOCLIENT_INIT.
m_access_denied = true; m_access_denied = true;
m_access_denied_reason = L"Unknown";
if(datasize >= 4)
{
std::string datastring((char*)&data[2], datasize-2);
std::istringstream is(datastring, std::ios_base::binary);
m_access_denied_reason = deSerializeWideString(is);
}
return; return;
} }

@ -385,6 +385,11 @@ public:
return m_access_denied; return m_access_denied;
} }
inline std::wstring accessDeniedReason()
{
return m_access_denied_reason;
}
private: private:
// Virtual methods from con::PeerHandler // Virtual methods from con::PeerHandler
@ -440,6 +445,7 @@ private:
std::string m_password; std::string m_password;
bool m_access_denied; bool m_access_denied;
std::wstring m_access_denied_reason;
InventoryContext m_inventory_context; InventoryContext m_inventory_context;

@ -24,6 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#define PROTOCOL_ID 0x4f457403 #define PROTOCOL_ID 0x4f457403
#define PASSWORD_SIZE 28 // Maximum password length. Allows for
// base64-encoded SHA-1 (27+\0).
enum ToClientCommand enum ToClientCommand
{ {
TOCLIENT_INIT = 0x10, TOCLIENT_INIT = 0x10,
@ -154,6 +157,8 @@ enum ToClientCommand
TOCLIENT_ACCESS_DENIED = 0x35, TOCLIENT_ACCESS_DENIED = 0x35,
/* /*
u16 command u16 command
u16 reason_length
wstring reason
*/ */
}; };

@ -69,8 +69,10 @@ void set_default_settings()
// Server stuff // Server stuff
g_settings.setDefault("enable_experimental", "false"); g_settings.setDefault("enable_experimental", "false");
g_settings.setDefault("creative_mode", "false"); g_settings.setDefault("creative_mode", "false");
g_settings.setDefault("enable_damage", "false"); //TODO: Set to true g_settings.setDefault("enable_damage", "false"); //TODO: Set to true when healing is possible
g_settings.setDefault("give_initial_stuff", "false"); g_settings.setDefault("give_initial_stuff", "false");
g_settings.setDefault("default_password", "");
g_settings.setDefault("default_privs", "build, shout");
g_settings.setDefault("objectdata_interval", "0.2"); g_settings.setDefault("objectdata_interval", "0.2");
g_settings.setDefault("active_object_range", "2"); g_settings.setDefault("active_object_range", "2");

@ -426,7 +426,14 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir)
testplayer.deSerialize(is); testplayer.deSerialize(is);
} }
dstream<<"Loaded test player with name "<<testplayer.getName()<<std::endl; if(!string_allowed(testplayer.getName(), PLAYERNAME_ALLOWED_CHARS))
{
dstream<<"Not loading player with invalid name: "
<<testplayer.getName()<<std::endl;
}
dstream<<"Loaded test player with name "<<testplayer.getName()
<<std::endl;
// Search for the player // Search for the player
std::string playername = testplayer.getName(); std::string playername = testplayer.getName();
@ -723,6 +730,7 @@ void ServerEnvironment::step(float dtime)
activateObjects(block); activateObjects(block);
// TODO: Do something // TODO: Do something
// TODO: Implement usage of ActiveBlockModifier
// Here's a quick demonstration // Here's a quick demonstration
v3s16 p0; v3s16 p0;
@ -784,6 +792,7 @@ void ServerEnvironment::step(float dtime)
Everything should bind to inside this single content Everything should bind to inside this single content
searching loop to keep things fast. searching loop to keep things fast.
*/ */
// TODO: Implement usage of ActiveBlockModifier
v3s16 p0; v3s16 p0;
for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++) for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++)

@ -112,25 +112,6 @@ public:
private: private:
}; };
/*
Active block modifier interface
*/
class ServerEnvironment;
class ActiveBlockModifier
{
public:
ActiveBlockModifier(){};
virtual ~ActiveBlockModifier(){};
virtual u32 getTriggerContentCount(){ return 1;}
virtual u8 getTriggerContent(u32 i) = 0;
virtual float getActiveInterval() = 0;
virtual u32 getActiveChance() = 0;
virtual void triggerEvent(ServerEnvironment *env, v3s16 p) = 0;
};
/* /*
The server-side environment. The server-side environment.
@ -140,6 +121,7 @@ public:
#include "serverobject.h" #include "serverobject.h"
class Server; class Server;
class ActiveBlockModifier;
class ServerEnvironment : public Environment class ServerEnvironment : public Environment
{ {
@ -178,6 +160,7 @@ public:
/* /*
ActiveObjects ActiveObjects
-------------------------------------------
*/ */
ServerActiveObject* getActiveObject(u16 id); ServerActiveObject* getActiveObject(u16 id);
@ -214,6 +197,13 @@ public:
*/ */
ActiveObjectMessage getActiveObjectMessage(); ActiveObjectMessage getActiveObjectMessage();
/*
ActiveBlockModifiers
-------------------------------------------
*/
void addActiveBlockModifier(ActiveBlockModifier *abm);
private: private:
/* /*
Remove all objects that satisfy (m_removed && m_known_by_count==0) Remove all objects that satisfy (m_removed && m_known_by_count==0)
@ -263,6 +253,29 @@ private:
float m_game_time_fraction_counter; float m_game_time_fraction_counter;
}; };
/*
Active block modifier interface.
These are fed into ServerEnvironment at initialization time;
ServerEnvironment handles deleting them.
*/
class ActiveBlockModifier
{
public:
ActiveBlockModifier(){};
virtual ~ActiveBlockModifier(){};
//virtual core::list<u8> update(ServerEnvironment *env) = 0;
virtual u32 getTriggerContentCount(){ return 1;}
virtual u8 getTriggerContent(u32 i) = 0;
virtual float getActiveInterval() = 0;
// chance of (1 / return value), 0 is disallowed
virtual u32 getActiveChance() = 0;
// This is called usually at interval for 1/chance of the nodes
virtual void triggerEvent(ServerEnvironment *env, v3s16 p) = 0;
};
#ifndef SERVER #ifndef SERVER
#include "clientobject.h" #include "clientobject.h"

@ -764,8 +764,9 @@ void the_game(
{ {
if(client.accessDenied()) if(client.accessDenied())
{ {
error_message = L"Access denied. Check your password and try again."; error_message = L"Access denied. Reason: "
std::cout<<DTIME<<"Access denied."<<std::endl; +client.accessDeniedReason();
std::cout<<DTIME<<wide_to_narrow(error_message)<<std::endl;
} }
else else
{ {

@ -266,7 +266,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize)
core::rect<s32> rect(0, 0, 130, 30); core::rect<s32> rect(0, 0, 130, 30);
//rect += topleft_server + v2s32(size_server.X-40-130, 100+25); //rect += topleft_server + v2s32(size_server.X-40-130, 100+25);
rect += topleft_server + v2s32(40, 100+25); rect += topleft_server + v2s32(40, 100+25);
Environment->addButton(rect, this, 260, L"Delete map"); Environment->addButton(rect, this, 260, L"Delete world");
} }
} }

@ -260,6 +260,9 @@ SUGG: MovingObject::move and Player::move are basically the same.
- NOTE: There is a simple move implementation now in collision.{h,cpp} - NOTE: There is a simple move implementation now in collision.{h,cpp}
- NOTE: MovingObject will be deleted (MapBlockObject) - NOTE: MovingObject will be deleted (MapBlockObject)
TODO: Add a long step function to objects that is called with the time
difference when block activates
Map: Map:
---- ----
@ -304,14 +307,22 @@ Making it more portable:
Stuff to do before release: Stuff to do before release:
--------------------------- ---------------------------
- Player default privileges and default password - Make grass grow slower; utilize timestamp difference
- Chat privilege
- Some simple block-based dynamic stuff in the world (finish the
ActiveBlockModifier stuff)
- Protocol version field - Protocol version field
- Consider getting some textures from cisoun's texture pack - Consider getting some textures from cisoun's texture pack
- Add a long step function to objects that is called with the time - Ask from Cisoun
difference when block activates - Make sure the fence implementation and data format is good
- Think about using same bits for material for fences and doors, for
example
- Make sure server handles removing grass when a block is placed (etc)
- The client should not do it by itself
- Add mouse inversion in config
- Block cube placement around player's head
- Move mineral to param2, increment map serialization version, add conversion
Stuff to do after release:
---------------------------
- Finish the ActiveBlockModifier stuff and use it for something
====================================================================== ======================================================================
@ -1188,7 +1199,7 @@ int main(int argc, char *argv[])
port = 30000; port = 30000;
// Map directory // Map directory
std::string map_dir = porting::path_userdata+"/map"; std::string map_dir = porting::path_userdata+"/world";
if(cmd_args.exists("map-dir")) if(cmd_args.exists("map-dir"))
map_dir = cmd_args.get("map-dir"); map_dir = cmd_args.get("map-dir");
else if(g_settings.exists("map-dir")) else if(g_settings.exists("map-dir"))
@ -1488,13 +1499,20 @@ int main(int argc, char *argv[])
g_settings.set("creative_mode", itos(menudata.creative_mode)); g_settings.set("creative_mode", itos(menudata.creative_mode));
g_settings.set("enable_damage", itos(menudata.enable_damage)); g_settings.set("enable_damage", itos(menudata.enable_damage));
// Check for valid parameters, restart menu if invalid. /*// Check for valid parameters, restart menu if invalid.
if(playername == "") if(playername == "")
{ {
error_message = L"Name required."; error_message = L"Name required.";
continue; continue;
} }
// Check that name has only valid chars
if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
{
error_message = L"Characters allowed: "
+narrow_to_wide(PLAYERNAME_ALLOWED_CHARS);
continue;
}*/
// Save settings // Save settings
g_settings.set("name", playername); g_settings.set("name", playername);
g_settings.set("address", address); g_settings.set("address", address);

@ -23,58 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "constants.h" #include "constants.h"
#include "utility.h" #include "utility.h"
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::wstring privsToString(u64 privs)
{
std::wostringstream os(std::ios_base::binary);
if(privs & PRIV_BUILD)
os<<L"build,";
if(privs & PRIV_TELEPORT)
os<<L"teleport,";
if(privs & PRIV_SETTIME)
os<<L"settime,";
if(privs & PRIV_PRIVS)
os<<L"privs,";
if(privs & PRIV_SHOUT)
os<<L"shout,";
if(os.tellp())
{
// Drop the trailing comma. (Why on earth can't
// you truncate a C++ stream anyway???)
std::wstring tmp = os.str();
return tmp.substr(0, tmp.length() -1);
}
return os.str();
}
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::wstring str)
{
u64 privs=0;
std::vector<std::wstring> pr;
pr=str_split(str, ',');
for(std::vector<std::wstring>::iterator i = pr.begin();
i != pr.end(); ++i)
{
if(*i == L"build")
privs |= PRIV_BUILD;
else if(*i == L"teleport")
privs |= PRIV_TELEPORT;
else if(*i == L"settime")
privs |= PRIV_SETTIME;
else if(*i == L"privs")
privs |= PRIV_PRIVS;
else if(*i == L"shout")
privs |= PRIV_SHOUT;
else
return PRIV_INVALID;
}
return privs;
}
Player::Player(): Player::Player():
touching_ground(false), touching_ground(false),
@ -83,7 +31,6 @@ Player::Player():
swimming_up(false), swimming_up(false),
craftresult_is_preview(true), craftresult_is_preview(true),
hp(20), hp(20),
privs(PRIV_DEFAULT),
peer_id(PEER_ID_INEXISTENT), peer_id(PEER_ID_INEXISTENT),
m_pitch(0), m_pitch(0),
m_yaw(0), m_yaw(0),
@ -91,7 +38,6 @@ Player::Player():
m_position(0,0,0) m_position(0,0,0)
{ {
updateName("<not set>"); updateName("<not set>");
updatePassword("");
resetInventory(); resetInventory();
} }
@ -150,7 +96,7 @@ void Player::serialize(std::ostream &os)
Settings args; Settings args;
args.setS32("version", 1); args.setS32("version", 1);
args.set("name", m_name); args.set("name", m_name);
args.set("password", m_password); //args.set("password", m_password);
args.setFloat("pitch", m_pitch); args.setFloat("pitch", m_pitch);
args.setFloat("yaw", m_yaw); args.setFloat("yaw", m_yaw);
args.setV3F("position", m_position); args.setV3F("position", m_position);
@ -185,10 +131,10 @@ void Player::deSerialize(std::istream &is)
//args.getS32("version"); //args.getS32("version");
std::string name = args.get("name"); std::string name = args.get("name");
updateName(name.c_str()); updateName(name.c_str());
std::string password = ""; /*std::string password = "";
if(args.exists("password")) if(args.exists("password"))
password = args.get("password"); password = args.get("password");
updatePassword(password.c_str()); updatePassword(password.c_str());*/
m_pitch = args.getFloat("pitch"); m_pitch = args.getFloat("pitch");
m_yaw = args.getFloat("yaw"); m_yaw = args.getFloat("yaw");
m_position = args.getV3F("position"); m_position = args.getV3F("position");
@ -202,7 +148,7 @@ void Player::deSerialize(std::istream &is)
}catch(SettingNotFoundException &e){ }catch(SettingNotFoundException &e){
hp = 20; hp = 20;
} }
try{ /*try{
std::string sprivs = args.get("privs"); std::string sprivs = args.get("privs");
if(sprivs == "all") if(sprivs == "all")
{ {
@ -215,7 +161,7 @@ void Player::deSerialize(std::istream &is)
} }
}catch(SettingNotFoundException &e){ }catch(SettingNotFoundException &e){
privs = PRIV_DEFAULT; privs = PRIV_DEFAULT;
} }*/
inventory.deSerialize(is); inventory.deSerialize(is);
} }

@ -25,39 +25,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "collision.h" #include "collision.h"
#define PLAYERNAME_SIZE 20 #define PLAYERNAME_SIZE 20
#define PASSWORD_SIZE 28 // Maximum password length. Allows for
// base64-encoded SHA-1.
#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.," #define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
// Player privileges. These form a bitmask stored in the privs field
// of the player, and define things they're allowed to do. See also
// the static methods Player::privsToString and stringToPrivs that
// convert these to human-readable form.
const u64 PRIV_BUILD = 1; // Can build - i.e. modify the world
const u64 PRIV_TELEPORT = 2; // Can teleport
const u64 PRIV_SETTIME = 4; // Can set the time
const u64 PRIV_PRIVS = 8; // Can grant and revoke privileges
const u64 PRIV_SERVER = 16; // Can manage the server (e.g. shutodwn
// ,settings)
const u64 PRIV_SHOUT = 32; // Can broadcast chat messages to all
// players
// Default privileges - these can be overriden for new players using the
// config option "default_privs" - however, this value still applies for
// players that existed before the privileges system was added.
const u64 PRIV_DEFAULT = PRIV_BUILD|PRIV_SHOUT;
const u64 PRIV_ALL = 0x7FFFFFFFFFFFFFFFULL;
const u64 PRIV_INVALID = 0x8000000000000000ULL;
// Convert a privileges value into a human-readable string,
// with each component separated by a comma.
std::wstring privsToString(u64 privs);
// Converts a comma-seperated list of privilege values into a
// privileges value. The reverse of privsToString(). Returns
// PRIV_INVALID if there is anything wrong with the input.
u64 stringToPrivs(std::wstring str);
class Map; class Map;
@ -128,16 +97,6 @@ public:
return m_name; 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 bool isLocal() const = 0;
virtual void updateLight(u8 light_at_pos) {}; virtual void updateLight(u8 light_at_pos) {};
@ -174,7 +133,6 @@ public:
protected: protected:
char m_name[PLAYERNAME_SIZE]; char m_name[PLAYERNAME_SIZE];
char m_password[PASSWORD_SIZE];
f32 m_pitch; f32 m_pitch;
f32 m_yaw; f32 m_yaw;
v3f m_speed; v3f m_speed;

@ -967,6 +967,7 @@ Server::Server(
): ):
m_env(new ServerMap(mapsavedir), this), m_env(new ServerMap(mapsavedir), this),
m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
m_authmanager(mapsavedir+"/auth.txt"),
m_thread(this), m_thread(this),
m_emergethread(this), m_emergethread(this),
m_time_counter(0), m_time_counter(0),
@ -1646,7 +1647,7 @@ void Server::AsyncRunStep()
} }
} }
// Save map // Save map, players and auth stuff
{ {
float &counter = m_savemap_timer; float &counter = m_savemap_timer;
counter += dtime; counter += dtime;
@ -1654,8 +1655,11 @@ void Server::AsyncRunStep()
{ {
counter = 0.0; counter = 0.0;
// Auth stuff
m_authmanager.save();
// Map
JMutexAutoLock lock(m_env_mutex); JMutexAutoLock lock(m_env_mutex);
if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true) if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
{ {
// Save only changed parts // Save only changed parts
@ -1795,7 +1799,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
playername[i] = data[3+i]; playername[i] = data[3+i];
} }
playername[PLAYERNAME_SIZE-1] = 0; playername[PLAYERNAME_SIZE-1] = 0;
if(playername[0]=='\0')
{
derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
SendAccessDenied(m_con, peer_id,
L"Empty name");
return;
}
if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
{
derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
SendAccessDenied(m_con, peer_id,
L"Name contains unallowed characters");
return;
}
// Get password // Get password
char password[PASSWORD_SIZE]; char password[PASSWORD_SIZE];
if(datasize == 2+1+PLAYERNAME_SIZE) if(datasize == 2+1+PLAYERNAME_SIZE)
@ -1811,15 +1831,37 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
} }
password[PASSWORD_SIZE-1] = 0; password[PASSWORD_SIZE-1] = 0;
} }
Player *checkplayer = m_env.getPlayer(playername);
if(checkplayer != NULL && strcmp(checkplayer->getPassword(),password)) std::string checkpwd;
if(m_authmanager.exists(playername))
{
checkpwd = m_authmanager.getPassword(playername);
}
else
{
checkpwd = g_settings.get("default_password");
}
if(password != checkpwd)
{ {
derr_server<<DTIME<<"Server: peer_id="<<peer_id derr_server<<DTIME<<"Server: peer_id="<<peer_id
<<": supplied invalid password for " <<": supplied invalid password for "
<<playername<<std::endl; <<playername<<std::endl;
SendAccessDenied(m_con, peer_id); SendAccessDenied(m_con, peer_id, L"Invalid password");
return; return;
} }
// Add player to auth manager
if(m_authmanager.exists(playername) == false)
{
derr_server<<DTIME<<"Server: adding player "<<playername
<<" to auth manager"<<std::endl;
m_authmanager.add(playername);
m_authmanager.setPassword(playername, checkpwd);
m_authmanager.setPrivs(playername,
stringToPrivs(g_settings.get("default_privs")));
m_authmanager.save();
}
// Get player // Get player
Player *player = emergePlayer(playername, password, peer_id); Player *player = emergePlayer(playername, password, peer_id);
@ -3020,19 +3062,51 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
if(datasize != 2+PASSWORD_SIZE*2) if(datasize != 2+PASSWORD_SIZE*2)
return; return;
char password[PASSWORD_SIZE]; /*char password[PASSWORD_SIZE];
for(u32 i=0; i<PASSWORD_SIZE-1; i++) for(u32 i=0; i<PASSWORD_SIZE-1; i++)
password[i] = data[2+i]; password[i] = data[2+i];
password[PASSWORD_SIZE-1] = 0; password[PASSWORD_SIZE-1] = 0;*/
if(strcmp(player->getPassword(),password)) std::string oldpwd;
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
{ {
char c = data[2+i];
if(c == 0)
break;
oldpwd += c;
}
std::string newpwd;
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
{
char c = data[2+PASSWORD_SIZE+i];
if(c == 0)
break;
newpwd += c;
}
std::string playername = player->getName();
if(m_authmanager.exists(playername) == false)
{
dstream<<"Server: playername not found in authmanager"<<std::endl;
// Wrong old password supplied!!
SendChatMessage(peer_id, L"playername not found in authmanager");
return;
}
std::string checkpwd = m_authmanager.getPassword(playername);
if(oldpwd != checkpwd)
{
dstream<<"Server: invalid old password"<<std::endl;
// Wrong old password supplied!! // Wrong old password supplied!!
SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed."); SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
return; return;
} }
for(u32 i=0; i<PASSWORD_SIZE-1; i++)
password[i] = data[30+i]; m_authmanager.setPassword(playername, newpwd);
player->updatePassword(password);
dstream<<"Server: password change successful for "<<playername
<<std::endl;
SendChatMessage(peer_id, L"Password change successful"); SendChatMessage(peer_id, L"Password change successful");
} }
else else
@ -3215,12 +3289,14 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
con.Send(peer_id, 0, data, true); con.Send(peer_id, 0, data, true);
} }
void Server::SendAccessDenied(con::Connection &con, u16 peer_id) void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason)
{ {
DSTACK(__FUNCTION_NAME); DSTACK(__FUNCTION_NAME);
std::ostringstream os(std::ios_base::binary); std::ostringstream os(std::ios_base::binary);
writeU16(os, TOCLIENT_ACCESS_DENIED); writeU16(os, TOCLIENT_ACCESS_DENIED);
os<<serializeWideString(reason);
// Make data buffer // Make data buffer
std::string s = os.str(); std::string s = os.str();
@ -4207,7 +4283,10 @@ Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id
//player->peer_id = PEER_ID_INEXISTENT; //player->peer_id = PEER_ID_INEXISTENT;
player->peer_id = peer_id; player->peer_id = peer_id;
player->updateName(name); player->updateName(name);
player->updatePassword(password); m_authmanager.add(name);
m_authmanager.setPassword(name, password);
m_authmanager.setPrivs(name,
stringToPrivs(g_settings.get("default_privs")));
if(g_settings.exists("default_privs")) if(g_settings.exists("default_privs"))
player->privs = g_settings.getU64("default_privs"); player->privs = g_settings.getU64("default_privs");

@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "porting.h" #include "porting.h"
#include "map.h" #include "map.h"
#include "inventory.h" #include "inventory.h"
#include "auth.h"
/* /*
Some random functions Some random functions
@ -438,7 +439,8 @@ private:
*/ */
static void SendHP(con::Connection &con, u16 peer_id, u8 hp); static void SendHP(con::Connection &con, u16 peer_id, u8 hp);
static void SendAccessDenied(con::Connection &con, u16 peer_id); static void SendAccessDenied(con::Connection &con, u16 peer_id,
const std::wstring &reason);
/* /*
Non-static send methods Non-static send methods
@ -514,6 +516,9 @@ private:
JMutex m_con_mutex; JMutex m_con_mutex;
// Connected clients (behind the con mutex) // Connected clients (behind the con mutex)
core::map<u16, RemoteClient*> m_clients; core::map<u16, RemoteClient*> m_clients;
// User authentication
AuthManager m_authmanager;
/* /*
Threads Threads

@ -35,7 +35,7 @@ void cmd_privs(std::wostringstream &os,
{ {
// Show our own real privs, without any adjustments // Show our own real privs, without any adjustments
// made for admin status // made for admin status
os<<L"-!- " + privsToString(ctx->player->privs); os<<L"-!- " + narrow_to_wide(privsToString(ctx->player->privs));
return; return;
} }
@ -52,7 +52,7 @@ void cmd_privs(std::wostringstream &os,
return; return;
} }
os<<L"-!- " + privsToString(tp->privs); os<<L"-!- " + narrow_to_wide(privsToString(tp->privs));
} }
void cmd_grantrevoke(std::wostringstream &os, void cmd_grantrevoke(std::wostringstream &os,
@ -70,7 +70,7 @@ void cmd_grantrevoke(std::wostringstream &os,
return; return;
} }
u64 newprivs = stringToPrivs(ctx->parms[2]); u64 newprivs = stringToPrivs(wide_to_narrow(ctx->parms[2]));
if(newprivs == PRIV_INVALID) if(newprivs == PRIV_INVALID)
{ {
os<<L"-!- Invalid privileges specified"; os<<L"-!- Invalid privileges specified";
@ -90,7 +90,7 @@ void cmd_grantrevoke(std::wostringstream &os,
tp->privs &= ~newprivs; tp->privs &= ~newprivs;
os<<L"-!- Privileges change to "; os<<L"-!- Privileges change to ";
os<<privsToString(tp->privs); os<<narrow_to_wide(privsToString(tp->privs));
} }
void cmd_time(std::wostringstream &os, void cmd_time(std::wostringstream &os,

@ -301,7 +301,7 @@ int main(int argc, char *argv[])
} }
// Figure out path to map // Figure out path to map
std::string map_dir = porting::path_userdata+"/map"; std::string map_dir = porting::path_userdata+"/world";
if(cmd_args.exists("map-dir")) if(cmd_args.exists("map-dir"))
map_dir = cmd_args.get("map-dir"); map_dir = cmd_args.get("map-dir");
else if(g_settings.exists("map-dir")) else if(g_settings.exists("map-dir"))

@ -1984,17 +1984,23 @@ inline std::string serializeString(const std::string &plain)
return s; return s;
} }
/*// Reads a string with the length as the first two bytes // Creates a string with the length as the first two bytes from wide string
inline std::string deSerializeString(const std::string encoded) inline std::string serializeWideString(const std::wstring &plain)
{ {
u16 s_size = readU16((u8*)&encoded.c_str()[0]); //assert(plain.size() <= 65535);
if(s_size > encoded.length() - 2) if(plain.size() > 65535)
return ""; throw SerializationError("String too long for serializeString");
char buf[2];
writeU16((u8*)buf, plain.size());
std::string s; std::string s;
s.reserve(s_size); s.append(buf, 2);
s.append(&encoded.c_str()[2], s_size); for(u32 i=0; i<plain.size(); i++)
{
writeU16((u8*)buf, plain[i]);
s.append(buf, 2);
}
return s; return s;
}*/ }
// Reads a string with the length as the first two bytes // Reads a string with the length as the first two bytes
inline std::string deSerializeString(std::istream &is) inline std::string deSerializeString(std::istream &is)
@ -2014,6 +2020,27 @@ inline std::string deSerializeString(std::istream &is)
return s; return s;
} }
// Reads a wide string with the length as the first two bytes
inline std::wstring deSerializeWideString(std::istream &is)
{
char buf[2];
is.read(buf, 2);
if(is.gcount() != 2)
throw SerializationError("deSerializeString: size not read");
u16 s_size = readU16((u8*)buf);
if(s_size == 0)
return L"";
std::wstring s;
s.reserve(s_size);
for(u32 i=0; i<s_size; i++)
{
is.read(&buf[0], 2);
wchar_t c16 = readU16((u8*)buf);
s.append(&c16, 1);
}
return s;
}
// Creates a string with the length as the first four bytes // Creates a string with the length as the first four bytes
inline std::string serializeLongString(const std::string &plain) inline std::string serializeLongString(const std::string &plain)
{ {
@ -2025,18 +2052,6 @@ inline std::string serializeLongString(const std::string &plain)
return s; return s;
} }
/*// Reads a string with the length as the first four bytes
inline std::string deSerializeLongString(const std::string encoded)
{
u32 s_size = readU32((u8*)&encoded.c_str()[0]);
if(s_size > encoded.length() - 4)
return "";
std::string s;
s.reserve(s_size);
s.append(&encoded.c_str()[4], s_size);
return s;
}*/
// Reads a string with the length as the first four bytes // Reads a string with the length as the first four bytes
inline std::string deSerializeLongString(std::istream &is) inline std::string deSerializeLongString(std::istream &is)
{ {