Commit 77d74b40 authored by Max Kellermann's avatar Max Kellermann

Permission: add option "host_permissions"

parent a636d212
......@@ -25,6 +25,7 @@ ver 0.23 (not yet released)
* tags
- new tags "ComposerSort", "Ensemble", "Movement", "MovementNumber", and "Location"
* split permission "player" from "control"
* add option "host_permissions"
* new build-time dependency: libfmt
ver 0.22.11 (2021/08/24)
......
......@@ -650,6 +650,9 @@ By default, all clients are unauthenticated and have a full set of permissions.
:code:`local_permissions` may be used to assign other permissions to clients connecting on a local socket.
:code:`host_permissions` may be used to assign permissions to clients
with a certain IP address.
:code:`password` allows the client to send a password to gain other permissions. This option may be specified multiple times with different passwords.
Note that the :code:`password` option is not secure: passwords are sent in clear-text over the connection, and the client cannot verify the server's identity.
......@@ -659,6 +662,8 @@ Example:
.. code-block:: none
default_permissions "read"
host_permissions "192.168.0.100 read,add,control,admin"
host_permissions "2003:1234:4567::1 read,add,control,admin"
password "the_password@read,add,control"
password "the_admin_password@read,add,control,admin"
......
......@@ -22,6 +22,9 @@
#include "config/Param.hxx"
#include "config/Data.hxx"
#include "config/Option.hxx"
#include "net/AddressInfo.hxx"
#include "net/Resolver.hxx"
#include "net/ToString.hxx"
#include "util/IterableSplitString.hxx"
#include "util/RuntimeError.hxx"
#include "util/StringView.hxx"
......@@ -55,6 +58,10 @@ static unsigned permission_default;
static unsigned local_permissions;
#endif
#ifdef HAVE_TCP
static std::map<std::string, unsigned> host_passwords;
#endif
static unsigned
ParsePermission(StringView s)
{
......@@ -66,10 +73,9 @@ ParsePermission(StringView s)
int(s.size), s.data);
}
static unsigned parsePermissions(const char *string)
static unsigned
parsePermissions(std::string_view string)
{
assert(string != nullptr);
unsigned permission = 0;
for (const auto i : IterableSplitString(string, PERMISSION_SEPARATOR))
......@@ -120,8 +126,40 @@ initPermissions(const ConfigData &config)
: permission_default;
});
#endif
#ifdef HAVE_TCP
for (const auto &param : config.GetParamList(ConfigOption::HOST_PERMISSIONS)) {
permission_default = 0;
param.With([](StringView value){
auto [host_sv, permissions_s] = value.Split(' ');
unsigned permissions = parsePermissions(permissions_s);
const std::string host_s{host_sv};
for (const auto &i : Resolve(host_s.c_str(), 0,
AI_PASSIVE, SOCK_STREAM))
host_passwords.emplace(HostToString(i),
permissions);
});
}
#endif
}
#ifdef HAVE_TCP
int
GetPermissionsFromAddress(SocketAddress address) noexcept
{
if (auto i = host_passwords.find(HostToString(address));
i != host_passwords.end())
return i->second;
return -1;
}
#endif
int
getPermissionFromPassword(const char *password, unsigned *permission) noexcept
{
......
......@@ -23,6 +23,7 @@
#include "config.h"
struct ConfigData;
class SocketAddress;
static constexpr unsigned PERMISSION_NONE = 0;
static constexpr unsigned PERMISSION_READ = 1;
......@@ -45,6 +46,12 @@ unsigned
GetLocalPermissions() noexcept;
#endif
#ifdef HAVE_TCP
[[gnu::pure]]
int
GetPermissionsFromAddress(SocketAddress address) noexcept;
#endif
void
initPermissions(const ConfigData &config);
......
......@@ -32,8 +32,12 @@ GetPermissions(SocketAddress address, int uid) noexcept
#ifdef HAVE_UN
if (address.GetFamily() == AF_LOCAL)
return GetLocalPermissions();
#else
(void)address;
#endif
#ifdef HAVE_TCP
if (int permissions = GetPermissionsFromAddress(address);
permissions >= 0)
return permissions;
#endif
return getDefaultPermissions();
......
......@@ -48,6 +48,7 @@ enum class ConfigOption {
ZEROCONF_NAME,
ZEROCONF_ENABLED,
PASSWORD,
HOST_PERMISSIONS,
LOCAL_PERMISSIONS,
DEFAULT_PERMS,
AUDIO_OUTPUT_FORMAT,
......
......@@ -44,6 +44,7 @@ const ConfigTemplate config_param_templates[] = {
{ "zeroconf_name" },
{ "zeroconf_enabled" },
{ "password", true },
{ "host_permissions", true },
{ "local_permissions" },
{ "default_permissions" },
{ "audio_output_format" },
......
......@@ -116,3 +116,32 @@ ToString(SocketAddress address) noexcept
result.append(serv);
return result;
}
std::string
HostToString(SocketAddress address) noexcept
{
if (address.IsNull())
return "null";
#ifdef HAVE_UN
if (address.GetFamily() == AF_LOCAL)
/* return path of local socket */
return LocalAddressToString(address.CastTo<struct sockaddr_un>(),
address.GetSize());
#endif
#if defined(HAVE_IPV6) && defined(IN6_IS_ADDR_V4MAPPED)
IPv4Address ipv4_buffer;
if (address.IsV4Mapped())
address = ipv4_buffer = address.UnmapV4();
#endif
char host[NI_MAXHOST], serv[NI_MAXSERV];
int ret = getnameinfo(address.GetAddress(), address.GetSize(),
host, sizeof(host), serv, sizeof(serv),
NI_NUMERICHOST|NI_NUMERICSERV);
if (ret != 0)
return "unknown";
return host;
}
......@@ -42,4 +42,12 @@ class SocketAddress;
std::string
ToString(SocketAddress address) noexcept;
/**
* Generates the string representation of a #SocketAddress into the
* specified buffer, without the port number.
*/
[[gnu::pure]]
std::string
HostToString(SocketAddress address) noexcept;
#endif
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment