[Add] Implement user and group management.
This commit is contained in:
parent
2e5ddcd2e6
commit
d852d8449f
9
assets/scripts/bin/chgrp.lua
Normal file
9
assets/scripts/bin/chgrp.lua
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- /bin/chgrp - Change file group.
|
||||||
|
local group = arg[1]
|
||||||
|
local path = arg[2]
|
||||||
|
|
||||||
|
if not group or not path then
|
||||||
|
return "chgrp: missing operand"
|
||||||
|
end
|
||||||
|
|
||||||
|
return bettola.chgrp(context, group, path)
|
||||||
9
assets/scripts/bin/chown.lua
Normal file
9
assets/scripts/bin/chown.lua
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
-- /bin/chown - Change file owner.
|
||||||
|
local owner = arg[1]
|
||||||
|
local path = arg[2]
|
||||||
|
|
||||||
|
if not owner or not path then
|
||||||
|
return "chown: missing operand"
|
||||||
|
end
|
||||||
|
|
||||||
|
return bettola.chown(context, owner, path)
|
||||||
@ -16,6 +16,34 @@ local function format_permissions(perms)
|
|||||||
return table.concat(rwx)
|
return table.concat(rwx)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function get_username_from_uid(uid)
|
||||||
|
local passwd_content = bettola.read_file(context, "/etc/passwd")
|
||||||
|
for line in passwd_content:gmatch("(^\n]+)") do
|
||||||
|
local parts = {}
|
||||||
|
for part in line:gmatch("([^:]+)") do
|
||||||
|
table.insert(parts,part)
|
||||||
|
end
|
||||||
|
if #parts >= 3 and tonumber(parts[3]) == uid then
|
||||||
|
return parts[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tostring(uid)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_groupname_from_gid(gid)
|
||||||
|
local group_content = bettola.read_file(context, "/etc/group")
|
||||||
|
for line in group_content:gmatch("([^\n]+)") do
|
||||||
|
local parts = {}
|
||||||
|
for part in line:gmatch("([^:]+)") do
|
||||||
|
table.insert(parts, part)
|
||||||
|
end
|
||||||
|
if #parts >= 3 and tonumber(parts[3]) == gid then
|
||||||
|
return parts[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tostring(gid)
|
||||||
|
end
|
||||||
|
|
||||||
local function get_file_size(node)
|
local function get_file_size(node)
|
||||||
if node.type == 0 then -- FILE_NODE.
|
if node.type == 0 then -- FILE_NODE.
|
||||||
return #node.content
|
return #node.content
|
||||||
@ -24,15 +52,34 @@ local function get_file_size(node)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Cache for UID/GID to name mappings.
|
||||||
|
local uid_to_name_cache = {}
|
||||||
|
local gid_to_name_cache = {}
|
||||||
|
|
||||||
|
local function get_cached_username(uid)
|
||||||
|
if not uid_to_name_cache[uid] then
|
||||||
|
uid_to_name_cache[uid] = get_username_from_uid(uid)
|
||||||
|
end
|
||||||
|
return uid_to_name_cache[uid]
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_cached_groupname(gid)
|
||||||
|
if not gid_to_name_cache[gid] then
|
||||||
|
gid_to_name_cache[gid] = get_groupname_from_gid(gid)
|
||||||
|
end
|
||||||
|
return gid_to_name_cache[gid]
|
||||||
|
end
|
||||||
|
|
||||||
local function ls_long_format(dir)
|
local function ls_long_format(dir)
|
||||||
local output = {}
|
local output = {}
|
||||||
for name, node in pairs(dir.children) do
|
for name, node in pairs(dir.children) do
|
||||||
local line_type = (node.type == 1) and "d" or "-"
|
local line_type = (node.type == 1) and "d" or "-"
|
||||||
local perms = format_permissions(node.permissions)
|
local perms = format_permissions(node.permissions)
|
||||||
local owner = node.owner_id
|
local owner_name = get_cached_username(node.owner_id)
|
||||||
local group = node.group_id
|
local group_name = get_cached_groupname(node.group_id)
|
||||||
local size = get_file_size(node)
|
local size = get_file_size(node)
|
||||||
table.insert(output, string.format("%s%s %d %d %5d %s", line_type, perms, owner, group, size, name))
|
table.insert(output, string.format("%s%s %s %s %5d %s", line_type, perms, owner_name,
|
||||||
|
group_name, size, name))
|
||||||
end
|
end
|
||||||
table.sort(output)
|
table.sort(output)
|
||||||
return table.concat(output, "\n")
|
return table.concat(output, "\n")
|
||||||
|
|||||||
2
assets/scripts/bin/whoami.lua
Normal file
2
assets/scripts/bin/whoami.lua
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
-- /bin/whoami - Print the effective username of the current user.
|
||||||
|
return bettola.get_username(context)
|
||||||
@ -71,8 +71,20 @@ bool DatabaseManager::create_player(const std::string& username, const std::stri
|
|||||||
/* Create default subdirs. */
|
/* Create default subdirs. */
|
||||||
_vfs_repository->create_node(machine_id, &root_id, "home", DIR_NODE,
|
_vfs_repository->create_node(machine_id, &root_id, "home", DIR_NODE,
|
||||||
"", player_id, player_id, 0755);
|
"", player_id, player_id, 0755);
|
||||||
_vfs_repository->create_node(machine_id, &root_id, "etc", DIR_NODE,
|
long long etc_id = _vfs_repository->create_node(machine_id, &root_id, "etc", DIR_NODE,
|
||||||
"", player_id, player_id, 0755);
|
"", 0, 0, 0755);
|
||||||
|
|
||||||
|
/* Create /etc/passwd and /etc/group. */
|
||||||
|
std::string passwd_content = "root:x:0:0\n";
|
||||||
|
passwd_content += username + ":x:" + std::to_string(player_id) + ":"
|
||||||
|
+ std::to_string(player_id) + "\n";
|
||||||
|
_vfs_repository->create_node(machine_id, &etc_id, "passwd", FILE_NODE,
|
||||||
|
passwd_content, 0, 0, 0644);
|
||||||
|
|
||||||
|
std::string group_content = "root:x:\n";
|
||||||
|
group_content += username + ":x:" + std::to_string(player_id) + "\n";
|
||||||
|
_vfs_repository->create_node(machine_id, &etc_id, "group", FILE_NODE,
|
||||||
|
group_content, 0, 0, 0644);
|
||||||
|
|
||||||
/* Create /bin and get it's ID */
|
/* Create /bin and get it's ID */
|
||||||
long long bin_id = _vfs_repository->create_node(machine_id, &root_id, "bin", DIR_NODE,
|
long long bin_id = _vfs_repository->create_node(machine_id, &root_id, "bin", DIR_NODE,
|
||||||
|
|||||||
@ -47,3 +47,11 @@ std::string MachineRepository::get_hostname(long long machine_id) {
|
|||||||
>> hostname;
|
>> hostname;
|
||||||
return hostname;
|
return hostname;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
long long MachineRepository::get_owner_id(long long machine_id) {
|
||||||
|
long long owner_id = -1;
|
||||||
|
_db << "SELECT owner_id FROM machines WHERE id = ?;"
|
||||||
|
<< machine_id
|
||||||
|
>> owner_id;
|
||||||
|
return owner_id;
|
||||||
|
}
|
||||||
|
|||||||
@ -21,6 +21,7 @@ public:
|
|||||||
std::vector<MachineData> get_all_npcs(void);
|
std::vector<MachineData> get_all_npcs(void);
|
||||||
std::vector<MachineData> get_all(void);
|
std::vector<MachineData> get_all(void);
|
||||||
std::string get_hostname(long long machine_id);
|
std::string get_hostname(long long machine_id);
|
||||||
|
long long get_owner_id(long long machine_id);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
sqlite::database& _db;
|
sqlite::database& _db;
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
#include <exception>
|
||||||
#include <sol/call.hpp>
|
#include <sol/call.hpp>
|
||||||
#include <sol/types.hpp>
|
#include <sol/types.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -72,7 +73,9 @@ std::string write_file(Session& context, const std::string& path,
|
|||||||
it->second->content = content;
|
it->second->content = content;
|
||||||
} else {
|
} else {
|
||||||
/* File does not exist, create it. */
|
/* File does not exist, create it. */
|
||||||
vfs_node* new_file = new_node(filename, FILE_NODE, parent_dir);
|
uint32_t current_uid = context.get_home_machine()->owner_id;
|
||||||
|
vfs_node* new_file = new_node(filename, FILE_NODE, parent_dir, current_uid,
|
||||||
|
current_uid, 0644);
|
||||||
new_file->content = content;
|
new_file->content = content;
|
||||||
parent_dir->children[filename] = new_file;
|
parent_dir->children[filename] = new_file;
|
||||||
}
|
}
|
||||||
@ -117,12 +120,18 @@ std::string create_executable(Session& context, const std::string& path,
|
|||||||
delete it->second;
|
delete it->second;
|
||||||
parent_dir->children.erase(it);
|
parent_dir->children.erase(it);
|
||||||
}
|
}
|
||||||
vfs_node* new_exec = new_node(filename, EXEC_NODE, parent_dir);
|
uint32_t current_uid = context.get_home_machine()->owner_id;
|
||||||
|
vfs_node* new_exec = new_node(filename, EXEC_NODE, parent_dir, current_uid,
|
||||||
|
current_uid, 0755);
|
||||||
new_exec->content = util::xor_string(content);
|
new_exec->content = util::xor_string(content);
|
||||||
parent_dir->children[filename] = new_exec;
|
parent_dir->children[filename] = new_exec;
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string read_file(Session& context, const std::string& path) {
|
||||||
|
return context.read_file(path);
|
||||||
|
}
|
||||||
|
|
||||||
std::string cd(Session& context, const std::string& path) {
|
std::string cd(Session& context, const std::string& path) {
|
||||||
vfs_node* current_dir = context.get_current_dir();
|
vfs_node* current_dir = context.get_current_dir();
|
||||||
|
|
||||||
@ -213,6 +222,104 @@ std::string close_terminal(Session& context) {
|
|||||||
return "__CLOSE_CONNECTION__";
|
return "__CLOSE_CONNECTION__";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string get_username(Session& context) {
|
||||||
|
return context.get_username();
|
||||||
|
}
|
||||||
|
|
||||||
|
long long get_uid_from_name(Session& context, const std::string& username) {
|
||||||
|
vfs_node* passwd_node = find_node_by_path(context.get_home_machine()->vfs_root, "/etc/passwd");
|
||||||
|
if(!passwd_node || passwd_node->type != FILE_NODE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss(passwd_node->content);
|
||||||
|
std::string line;
|
||||||
|
while(std::getline(ss, line)) {
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
std::stringstream line_ss(line);
|
||||||
|
std::string part;
|
||||||
|
while(std::getline(line_ss, part, ':')) {
|
||||||
|
parts.push_back(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parts.size() >= 3 && parts[0] == username) {
|
||||||
|
try {
|
||||||
|
return std::stoll(parts[2]);
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string chown(Session& context, const std::string& owner, const std::string& path) {
|
||||||
|
vfs_node* node = find_node_by_path(context.get_session_machine()->vfs_root, path);
|
||||||
|
if(!node) {
|
||||||
|
return "chown: cannot access '" + path + "' No such file or directory";
|
||||||
|
}
|
||||||
|
|
||||||
|
long long new_uid = get_uid_from_name(context, owner);
|
||||||
|
if(new_uid == -1) {
|
||||||
|
return "chown: invalid user: '" + owner + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t current_uid = context.get_home_machine()->owner_id;
|
||||||
|
if(node->owner_id != current_uid && current_uid != 0) {
|
||||||
|
return "chown: changing ownership of '" + path + "': Operation not permitted";
|
||||||
|
}
|
||||||
|
|
||||||
|
node->owner_id = new_uid;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
long long get_gid_from_name(Session& context, const std::string& groupname) {
|
||||||
|
vfs_node* group_node = find_node_by_path(context.get_home_machine()->vfs_root, "/etc/group");
|
||||||
|
if(!group_node || group_node->type != FILE_NODE) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss(group_node->content);
|
||||||
|
std::string line;
|
||||||
|
while(std::getline(ss, line)) {
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
std::stringstream line_ss(line);
|
||||||
|
std::string part;
|
||||||
|
while(std::getline(line_ss, part, ':')) {
|
||||||
|
parts.push_back(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parts.size() >= 3 && parts[0] == groupname) {
|
||||||
|
try {
|
||||||
|
return std::stoll(parts[2]);
|
||||||
|
} catch(const std::exception& e) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string chgrp(Session& context, const std::string& group, const std::string& path) {
|
||||||
|
vfs_node* node = find_node_by_path(context.get_session_machine()->vfs_root, path);
|
||||||
|
if(!node) {
|
||||||
|
return "chgrp: cannot access '" + path + "': No such file ordirectory";
|
||||||
|
}
|
||||||
|
|
||||||
|
long long new_gid = get_gid_from_name(context, group);
|
||||||
|
if(new_gid == -1) {
|
||||||
|
return "chgrp: invalid group: '" + group + "'";
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t current_uid = context.get_home_machine()->owner_id;
|
||||||
|
if(node->owner_id != current_uid && current_uid != 0) {
|
||||||
|
return "chgrp: changing group of '" + path + "': Operation not permitted";
|
||||||
|
}
|
||||||
|
|
||||||
|
node->group_id = new_gid;
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
struct ScpPath {
|
struct ScpPath {
|
||||||
std::string host;
|
std::string host;
|
||||||
std::string path;
|
std::string path;
|
||||||
|
|||||||
@ -15,10 +15,15 @@ std::string write_file(Session& context, const std::string& path,
|
|||||||
const std::string& content);
|
const std::string& content);
|
||||||
std::string create_executable(Session& context, const std::string& path,
|
std::string create_executable(Session& context, const std::string& path,
|
||||||
const std::string& content);
|
const std::string& content);
|
||||||
|
std::string read_file(Session& context, const std::string& path);
|
||||||
std::string ls(Session& context);
|
std::string ls(Session& context);
|
||||||
std::string cd(Session& context, const std::string& path);
|
std::string cd(Session& context, const std::string& path);
|
||||||
std::string scp(Session& context, const std::string& source,
|
std::string scp(Session& context, const std::string& source,
|
||||||
const std::string& destination);
|
const std::string& destination);
|
||||||
|
std::string chown(Session& context, const std::string& owner,
|
||||||
|
const std::string& path);
|
||||||
|
std::string chgrp(Session& context, const std::string& group,
|
||||||
|
const std::string& path);
|
||||||
|
|
||||||
/* NETWORK ACTIONS. */
|
/* NETWORK ACTIONS. */
|
||||||
std::string ssh(Session& context, const std::string& ip);
|
std::string ssh(Session& context, const std::string& ip);
|
||||||
@ -26,6 +31,7 @@ std::string nmap(Session& context, const std::string& ip);
|
|||||||
std::string disconnect(Session& context);
|
std::string disconnect(Session& context);
|
||||||
|
|
||||||
/* SYSTEM ACTIONS. */
|
/* SYSTEM ACTIONS. */
|
||||||
|
std::string get_username(Session& context);
|
||||||
std::string close_terminal(Session& context);
|
std::string close_terminal(Session& context);
|
||||||
|
|
||||||
} /* namespace api */
|
} /* namespace api */
|
||||||
|
|||||||
@ -44,13 +44,17 @@ LuaProcessor::LuaProcessor(Session& context) {
|
|||||||
bettola_api.set_function("ls", &api::ls);
|
bettola_api.set_function("ls", &api::ls);
|
||||||
bettola_api.set_function("write_file", &api::write_file);
|
bettola_api.set_function("write_file", &api::write_file);
|
||||||
bettola_api.set_function("create_executable", &api::create_executable);
|
bettola_api.set_function("create_executable", &api::create_executable);
|
||||||
|
bettola_api.set_function("read_file", &api::read_file);
|
||||||
bettola_api.set_function("get_current_dir", &api::get_current_dir);
|
bettola_api.set_function("get_current_dir", &api::get_current_dir);
|
||||||
bettola_api.set_function("cd", &api::cd);
|
bettola_api.set_function("cd", &api::cd);
|
||||||
bettola_api.set_function("scp", &api::scp);
|
bettola_api.set_function("scp", &api::scp);
|
||||||
|
bettola_api.set_function("chown", &api::chown);
|
||||||
|
bettola_api.set_function("chgrp", &api::chgrp);
|
||||||
bettola_api.set_function("close_terminal", &api::close_terminal);
|
bettola_api.set_function("close_terminal", &api::close_terminal);
|
||||||
bettola_api.set_function("ssh", &api::ssh);
|
bettola_api.set_function("ssh", &api::ssh);
|
||||||
bettola_api.set_function("nmap", &api::nmap);
|
bettola_api.set_function("nmap", &api::nmap);
|
||||||
bettola_api.set_function("disconnect", &api::disconnect);
|
bettola_api.set_function("disconnect", &api::disconnect);
|
||||||
|
bettola_api.set_function("get_username", &api::get_username);
|
||||||
}
|
}
|
||||||
|
|
||||||
LuaProcessor::~LuaProcessor(void) {}
|
LuaProcessor::~LuaProcessor(void) {}
|
||||||
|
|||||||
@ -9,9 +9,10 @@
|
|||||||
|
|
||||||
class Machine {
|
class Machine {
|
||||||
public:
|
public:
|
||||||
Machine(uint32_t id, std::string hostname) :
|
Machine(uint32_t id, std::string hostname, uint32_t owner_id = 0) :
|
||||||
id(id),
|
id(id),
|
||||||
hostname(std::move(hostname)),
|
hostname(std::move(hostname)),
|
||||||
|
owner_id(owner_id),
|
||||||
vfs_root(nullptr),
|
vfs_root(nullptr),
|
||||||
is_vfs_a_copy(false) {}
|
is_vfs_a_copy(false) {}
|
||||||
|
|
||||||
@ -19,6 +20,7 @@ public:
|
|||||||
|
|
||||||
uint32_t id;
|
uint32_t id;
|
||||||
std::string hostname;
|
std::string hostname;
|
||||||
|
uint32_t owner_id;
|
||||||
vfs_node* vfs_root;
|
vfs_node* vfs_root;
|
||||||
std::map<int, std::string> services;
|
std::map<int, std::string> services;
|
||||||
bool is_vfs_a_copy; /* Flag for CoW mechanism. */
|
bool is_vfs_a_copy; /* Flag for CoW mechanism. */
|
||||||
|
|||||||
@ -109,8 +109,9 @@ Machine* MachineManager::load_machine(long long machine_id) {
|
|||||||
printf("DEBUG: load_machine called for machine_id: %lld\n", machine_id);
|
printf("DEBUG: load_machine called for machine_id: %lld\n", machine_id);
|
||||||
|
|
||||||
std::string hostname = _db_manager->machines().get_hostname(machine_id);
|
std::string hostname = _db_manager->machines().get_hostname(machine_id);
|
||||||
|
long long owner_id = _db_manager->machines().get_owner_id(machine_id);
|
||||||
|
|
||||||
Machine* machine = new Machine(machine_id, hostname);
|
Machine* machine = new Machine(machine_id, hostname, owner_id);
|
||||||
|
|
||||||
/* Load all VFS nodes for this machine from the database. */
|
/* Load all VFS nodes for this machine from the database. */
|
||||||
std::map<long long, vfs_node*> node_map;
|
std::map<long long, vfs_node*> node_map;
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include <sol/types.hpp>
|
#include <sol/types.hpp>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "session.h"
|
#include "session.h"
|
||||||
#include "db/database_manager.h"
|
#include "db/database_manager.h"
|
||||||
@ -149,3 +150,34 @@ std::string Session::read_file(const std::string& path) {
|
|||||||
}
|
}
|
||||||
return "Error: file not found.";
|
return "Error: file not found.";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string Session::get_username(void) {
|
||||||
|
if(!_home_machine || !_home_machine->vfs_root) {
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
vfs_node* passwd_node = find_node_by_path(_home_machine->vfs_root, "/etc/passwd");
|
||||||
|
if(!passwd_node || passwd_node->type != FILE_NODE) {
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss(passwd_node->content);
|
||||||
|
std::string line;
|
||||||
|
uint32_t player_id = _home_machine->owner_id;
|
||||||
|
|
||||||
|
while(std::getline(ss, line)) {
|
||||||
|
std::vector<std::string> parts;
|
||||||
|
std::stringstream line_ss(line);
|
||||||
|
std::string part;
|
||||||
|
while(std::getline(line_ss, part, ':')) {
|
||||||
|
parts.push_back(part);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(parts.size() >= 3) {
|
||||||
|
if(std::to_string(player_id) == parts[2]) {
|
||||||
|
return parts[0]; /* Username. */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "unknown";
|
||||||
|
}
|
||||||
|
|||||||
@ -19,6 +19,8 @@ public:
|
|||||||
std::string write_file(const std::string& path, const std::string& content);
|
std::string write_file(const std::string& path, const std::string& content);
|
||||||
std::string read_file(const std::string& path);
|
std::string read_file(const std::string& path);
|
||||||
|
|
||||||
|
std::string get_username(void);
|
||||||
|
|
||||||
/* Public interface for API functions. */
|
/* Public interface for API functions. */
|
||||||
vfs_node* get_current_dir(void);
|
vfs_node* get_current_dir(void);
|
||||||
Machine* get_home_machine(void);
|
Machine* get_home_machine(void);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user