diff options
author | Perttu Ahola <celeron55@gmail.com> | 2011-05-29 21:11:16 +0300 |
---|---|---|
committer | Perttu Ahola <celeron55@gmail.com> | 2011-05-29 21:11:16 +0300 |
commit | 63611932ebae93620386b26cfa82f7c4552b22ff (patch) | |
tree | 57c065bebf3ba53857dbed80e48cf67cd7e63038 | |
parent | 99c2ac0125f9bbae0a4b06e6e7145f76f4dfbbf4 (diff) | |
download | minetest-63611932ebae93620386b26cfa82f7c4552b22ff.tar.gz minetest-63611932ebae93620386b26cfa82f7c4552b22ff.tar.bz2 minetest-63611932ebae93620386b26cfa82f7c4552b22ff.zip |
player passwords and privileges in world/auth.txt
--HG--
extra : rebase_source : 7260636295d9068fbeeddf4143c89f2b8a91446c
-rw-r--r-- | .hgignore | 1 | ||||
-rw-r--r-- | doc/changelog.txt | 1 | ||||
-rw-r--r-- | minetest.conf.example | 5 | ||||
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/auth.cpp | 244 | ||||
-rw-r--r-- | src/auth.h | 99 | ||||
-rw-r--r-- | src/client.cpp | 7 | ||||
-rw-r--r-- | src/client.h | 6 | ||||
-rw-r--r-- | src/clientserver.h | 5 | ||||
-rw-r--r-- | src/defaultsettings.cpp | 4 | ||||
-rw-r--r-- | src/environment.cpp | 11 | ||||
-rw-r--r-- | src/environment.h | 51 | ||||
-rw-r--r-- | src/game.cpp | 5 | ||||
-rw-r--r-- | src/guiMainMenu.cpp | 2 | ||||
-rw-r--r-- | src/main.cpp | 36 | ||||
-rw-r--r-- | src/player.cpp | 64 | ||||
-rw-r--r-- | src/player.h | 46 | ||||
-rw-r--r-- | src/server.cpp | 107 | ||||
-rw-r--r-- | src/server.h | 7 | ||||
-rw-r--r-- | src/servercommand.cpp | 8 | ||||
-rw-r--r-- | src/servermain.cpp | 2 | ||||
-rw-r--r-- | src/utility.h | 55 |
22 files changed, 591 insertions, 176 deletions
@@ -1,4 +1,5 @@ map/* +world/* CMakeFiles/* src/CMakeFiles/* src/Makefile diff --git a/doc/changelog.txt b/doc/changelog.txt index f43a68f12..cb3594a98 100644 --- a/doc/changelog.txt +++ b/doc/changelog.txt @@ -13,6 +13,7 @@ X: - Slightly updated map format - Player passwords - 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: - Smooth lighting with simple ambient occlusion diff --git a/minetest.conf.example b/minetest.conf.example index acdc19b0a..5e6393228 100644 --- a/minetest.conf.example +++ b/minetest.conf.example @@ -102,6 +102,11 @@ #enable_damage = false +#default_password = + +# Available privileges: build, teleport, settime, privs, shout +#default_privs = build, shout + # Gives some stuff to players at the beginning #give_initial_stuff = false diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 49982d310..f912f68cd 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -50,6 +50,7 @@ configure_file( ) set(common_SRCS + auth.cpp collision.cpp nodemetadata.cpp serverobject.cpp diff --git a/src/auth.cpp b/src/auth.cpp new file mode 100644 index 000000000..49985e697 --- /dev/null +++ b/src/auth.cpp @@ -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); +} + diff --git a/src/auth.h b/src/auth.h new file mode 100644 index 000000000..472409d46 --- /dev/null +++ b/src/auth.h @@ -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 + diff --git a/src/client.cpp b/src/client.cpp index 5869dc77b..79bbd8021 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -610,6 +610,13 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) // to be processed even if the serialisation format has // not been agreed yet, the same as TOCLIENT_INIT. 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; } diff --git a/src/client.h b/src/client.h index f661838ce..1bfbe6296 100644 --- a/src/client.h +++ b/src/client.h @@ -385,6 +385,11 @@ public: return m_access_denied; } + inline std::wstring accessDeniedReason() + { + return m_access_denied_reason; + } + private: // Virtual methods from con::PeerHandler @@ -440,6 +445,7 @@ private: std::string m_password; bool m_access_denied; + std::wstring m_access_denied_reason; InventoryContext m_inventory_context; diff --git a/src/clientserver.h b/src/clientserver.h index 256aed362..7972762c0 100644 --- a/src/clientserver.h +++ b/src/clientserver.h @@ -24,6 +24,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #define PROTOCOL_ID 0x4f457403 +#define PASSWORD_SIZE 28 // Maximum password length. Allows for + // base64-encoded SHA-1 (27+\0). + enum ToClientCommand { TOCLIENT_INIT = 0x10, @@ -154,6 +157,8 @@ enum ToClientCommand TOCLIENT_ACCESS_DENIED = 0x35, /* u16 command + u16 reason_length + wstring reason */ }; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index f11b0b2d7..1d758a2a4 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -69,8 +69,10 @@ void set_default_settings() // Server stuff g_settings.setDefault("enable_experimental", "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("default_password", ""); + g_settings.setDefault("default_privs", "build, shout"); g_settings.setDefault("objectdata_interval", "0.2"); g_settings.setDefault("active_object_range", "2"); diff --git a/src/environment.cpp b/src/environment.cpp index 3ebfef0c5..c93d11ca8 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -426,7 +426,14 @@ void ServerEnvironment::deSerializePlayers(const std::string &savedir) 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 std::string playername = testplayer.getName(); @@ -723,6 +730,7 @@ void ServerEnvironment::step(float dtime) activateObjects(block); // TODO: Do something + // TODO: Implement usage of ActiveBlockModifier // Here's a quick demonstration v3s16 p0; @@ -784,6 +792,7 @@ void ServerEnvironment::step(float dtime) Everything should bind to inside this single content searching loop to keep things fast. */ + // TODO: Implement usage of ActiveBlockModifier v3s16 p0; for(p0.X=0; p0.X<MAP_BLOCKSIZE; p0.X++) diff --git a/src/environment.h b/src/environment.h index ac290f932..f5cce5933 100644 --- a/src/environment.h +++ b/src/environment.h @@ -113,25 +113,6 @@ 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. This is not thread-safe. Server uses an environment mutex. @@ -140,6 +121,7 @@ public: #include "serverobject.h" class Server; +class ActiveBlockModifier; class ServerEnvironment : public Environment { @@ -178,6 +160,7 @@ public: /* ActiveObjects + ------------------------------------------- */ ServerActiveObject* getActiveObject(u16 id); @@ -214,6 +197,13 @@ public: */ ActiveObjectMessage getActiveObjectMessage(); + /* + ActiveBlockModifiers + ------------------------------------------- + */ + + void addActiveBlockModifier(ActiveBlockModifier *abm); + private: /* Remove all objects that satisfy (m_removed && m_known_by_count==0) @@ -263,6 +253,29 @@ private: 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 #include "clientobject.h" diff --git a/src/game.cpp b/src/game.cpp index 603a86da3..cc758be7e 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -764,8 +764,9 @@ void the_game( { if(client.accessDenied()) { - error_message = L"Access denied. Check your password and try again."; - std::cout<<DTIME<<"Access denied."<<std::endl; + error_message = L"Access denied. Reason: " + +client.accessDeniedReason(); + std::cout<<DTIME<<wide_to_narrow(error_message)<<std::endl; } else { diff --git a/src/guiMainMenu.cpp b/src/guiMainMenu.cpp index a30e006a6..ef0a013f1 100644 --- a/src/guiMainMenu.cpp +++ b/src/guiMainMenu.cpp @@ -266,7 +266,7 @@ void GUIMainMenu::regenerateGui(v2u32 screensize) core::rect<s32> rect(0, 0, 130, 30); //rect += topleft_server + v2s32(size_server.X-40-130, 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"); } } diff --git a/src/main.cpp b/src/main.cpp index f67d53475..11d50bfd1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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: MovingObject will be deleted (MapBlockObject)
+TODO: Add a long step function to objects that is called with the time
+ difference when block activates
+
Map:
----
@@ -304,14 +307,22 @@ Making it more portable: Stuff to do before release:
---------------------------
-- Player default privileges and default password
-- Chat privilege
-- Some simple block-based dynamic stuff in the world (finish the
- ActiveBlockModifier stuff)
+- Make grass grow slower; utilize timestamp difference
- Protocol version field
- Consider getting some textures from cisoun's texture pack
-- Add a long step function to objects that is called with the time
- difference when block activates
+ - Ask from Cisoun
+- 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;
// Map directory
- std::string map_dir = porting::path_userdata+"/map";
+ std::string map_dir = porting::path_userdata+"/world";
if(cmd_args.exists("map-dir"))
map_dir = cmd_args.get("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("enable_damage", itos(menudata.enable_damage));
- // Check for valid parameters, restart menu if invalid.
+ /*// Check for valid parameters, restart menu if invalid.
if(playername == "")
{
error_message = L"Name required.";
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
g_settings.set("name", playername);
g_settings.set("address", address);
diff --git a/src/player.cpp b/src/player.cpp index 147b6c97a..efb2f3447 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -23,58 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.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(): touching_ground(false), @@ -83,7 +31,6 @@ Player::Player(): swimming_up(false), craftresult_is_preview(true), hp(20), - privs(PRIV_DEFAULT), peer_id(PEER_ID_INEXISTENT), m_pitch(0), m_yaw(0), @@ -91,7 +38,6 @@ Player::Player(): m_position(0,0,0) { updateName("<not set>"); - updatePassword(""); resetInventory(); } @@ -150,7 +96,7 @@ void Player::serialize(std::ostream &os) Settings args; args.setS32("version", 1); args.set("name", m_name); - args.set("password", m_password); + //args.set("password", m_password); args.setFloat("pitch", m_pitch); args.setFloat("yaw", m_yaw); args.setV3F("position", m_position); @@ -185,10 +131,10 @@ void Player::deSerialize(std::istream &is) //args.getS32("version"); std::string name = args.get("name"); updateName(name.c_str()); - std::string password = ""; + /*std::string password = ""; if(args.exists("password")) password = args.get("password"); - updatePassword(password.c_str()); + updatePassword(password.c_str());*/ m_pitch = args.getFloat("pitch"); m_yaw = args.getFloat("yaw"); m_position = args.getV3F("position"); @@ -202,7 +148,7 @@ void Player::deSerialize(std::istream &is) }catch(SettingNotFoundException &e){ hp = 20; } - try{ + /*try{ std::string sprivs = args.get("privs"); if(sprivs == "all") { @@ -215,7 +161,7 @@ void Player::deSerialize(std::istream &is) } }catch(SettingNotFoundException &e){ privs = PRIV_DEFAULT; - } + }*/ inventory.deSerialize(is); } diff --git a/src/player.h b/src/player.h index 4b776a03f..157a25b5b 100644 --- a/src/player.h +++ b/src/player.h @@ -25,39 +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-_.," - -// 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); + +#define PLAYERNAME_ALLOWED_CHARS "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_" class Map; @@ -128,16 +97,6 @@ public: 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) {}; @@ -174,7 +133,6 @@ public: 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 9248e6298..4569d028e 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -967,6 +967,7 @@ Server::Server( ): m_env(new ServerMap(mapsavedir), this), m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this), + m_authmanager(mapsavedir+"/auth.txt"), m_thread(this), m_emergethread(this), m_time_counter(0), @@ -1646,7 +1647,7 @@ void Server::AsyncRunStep() } } - // Save map + // Save map, players and auth stuff { float &counter = m_savemap_timer; counter += dtime; @@ -1654,8 +1655,11 @@ void Server::AsyncRunStep() { counter = 0.0; + // Auth stuff + m_authmanager.save(); + + // Map JMutexAutoLock lock(m_env_mutex); - if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true) { // Save only changed parts @@ -1795,7 +1799,23 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) playername[i] = data[3+i]; } 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 char password[PASSWORD_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; } - 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 <<": supplied invalid password for " <<playername<<std::endl; - SendAccessDenied(m_con, peer_id); + SendAccessDenied(m_con, peer_id, L"Invalid password"); 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 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) return; - char password[PASSWORD_SIZE]; + /*char password[PASSWORD_SIZE]; for(u32 i=0; i<PASSWORD_SIZE-1; i++) password[i] = data[2+i]; - password[PASSWORD_SIZE-1] = 0; - if(strcmp(player->getPassword(),password)) + password[PASSWORD_SIZE-1] = 0;*/ + 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!! SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed."); return; } - for(u32 i=0; i<PASSWORD_SIZE-1; i++) - password[i] = data[30+i]; - player->updatePassword(password); + + m_authmanager.setPassword(playername, newpwd); + + dstream<<"Server: password change successful for "<<playername + <<std::endl; SendChatMessage(peer_id, L"Password change successful"); } else @@ -3215,12 +3289,14 @@ 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) +void Server::SendAccessDenied(con::Connection &con, u16 peer_id, + const std::wstring &reason) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_ACCESS_DENIED); + os<<serializeWideString(reason); // Make data buffer 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; 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")) player->privs = g_settings.getU64("default_privs"); diff --git a/src/server.h b/src/server.h index 54330856c..a6da801be 100644 --- a/src/server.h +++ b/src/server.h @@ -32,6 +32,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "map.h" #include "inventory.h" +#include "auth.h" /* Some random functions @@ -438,7 +439,8 @@ private: */ 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 @@ -514,6 +516,9 @@ private: JMutex m_con_mutex; // Connected clients (behind the con mutex) core::map<u16, RemoteClient*> m_clients; + + // User authentication + AuthManager m_authmanager; /* Threads diff --git a/src/servercommand.cpp b/src/servercommand.cpp index 5bb4f67f8..e14f326f4 100644 --- a/src/servercommand.cpp +++ b/src/servercommand.cpp @@ -35,7 +35,7 @@ void cmd_privs(std::wostringstream &os, { // Show our own real privs, without any adjustments // made for admin status - os<<L"-!- " + privsToString(ctx->player->privs); + os<<L"-!- " + narrow_to_wide(privsToString(ctx->player->privs)); return; } @@ -52,7 +52,7 @@ void cmd_privs(std::wostringstream &os, return; } - os<<L"-!- " + privsToString(tp->privs); + os<<L"-!- " + narrow_to_wide(privsToString(tp->privs)); } void cmd_grantrevoke(std::wostringstream &os, @@ -70,7 +70,7 @@ void cmd_grantrevoke(std::wostringstream &os, return; } - u64 newprivs = stringToPrivs(ctx->parms[2]); + u64 newprivs = stringToPrivs(wide_to_narrow(ctx->parms[2])); if(newprivs == PRIV_INVALID) { os<<L"-!- Invalid privileges specified"; @@ -90,7 +90,7 @@ void cmd_grantrevoke(std::wostringstream &os, tp->privs &= ~newprivs; os<<L"-!- Privileges change to "; - os<<privsToString(tp->privs); + os<<narrow_to_wide(privsToString(tp->privs)); } void cmd_time(std::wostringstream &os, diff --git a/src/servermain.cpp b/src/servermain.cpp index 254b1f28a..f3b17000c 100644 --- a/src/servermain.cpp +++ b/src/servermain.cpp @@ -301,7 +301,7 @@ int main(int argc, char *argv[]) } // 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")) map_dir = cmd_args.get("map-dir"); else if(g_settings.exists("map-dir")) diff --git a/src/utility.h b/src/utility.h index c7513e94d..f18d31278 100644 --- a/src/utility.h +++ b/src/utility.h @@ -1984,17 +1984,23 @@ inline std::string serializeString(const std::string &plain) return s; } -/*// Reads a string with the length as the first two bytes -inline std::string deSerializeString(const std::string encoded) +// Creates a string with the length as the first two bytes from wide string +inline std::string serializeWideString(const std::wstring &plain) { - u16 s_size = readU16((u8*)&encoded.c_str()[0]); - if(s_size > encoded.length() - 2) - return ""; + //assert(plain.size() <= 65535); + if(plain.size() > 65535) + throw SerializationError("String too long for serializeString"); + char buf[2]; + writeU16((u8*)buf, plain.size()); std::string s; - s.reserve(s_size); - s.append(&encoded.c_str()[2], s_size); + s.append(buf, 2); + for(u32 i=0; i<plain.size(); i++) + { + writeU16((u8*)buf, plain[i]); + s.append(buf, 2); + } return s; -}*/ +} // Reads a string with the length as the first two bytes inline std::string deSerializeString(std::istream &is) @@ -2014,6 +2020,27 @@ inline std::string deSerializeString(std::istream &is) 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 inline std::string serializeLongString(const std::string &plain) { @@ -2025,18 +2052,6 @@ inline std::string serializeLongString(const std::string &plain) 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 inline std::string deSerializeLongString(std::istream &is) { |