Permission: add option "host_permissions"
Closes https://github.com/MusicPlayerDaemon/MPD/issues/1115
This commit is contained in:
parent
a636d2127a
commit
77d74b404e
1
NEWS
1
NEWS
|
@ -25,6 +25,7 @@ ver 0.23 (not yet released)
|
||||||
* tags
|
* tags
|
||||||
- new tags "ComposerSort", "Ensemble", "Movement", "MovementNumber", and "Location"
|
- new tags "ComposerSort", "Ensemble", "Movement", "MovementNumber", and "Location"
|
||||||
* split permission "player" from "control"
|
* split permission "player" from "control"
|
||||||
|
* add option "host_permissions"
|
||||||
* new build-time dependency: libfmt
|
* new build-time dependency: libfmt
|
||||||
|
|
||||||
ver 0.22.11 (2021/08/24)
|
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:`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.
|
: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.
|
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
|
.. code-block:: none
|
||||||
|
|
||||||
default_permissions "read"
|
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_password@read,add,control"
|
||||||
password "the_admin_password@read,add,control,admin"
|
password "the_admin_password@read,add,control,admin"
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,9 @@
|
||||||
#include "config/Param.hxx"
|
#include "config/Param.hxx"
|
||||||
#include "config/Data.hxx"
|
#include "config/Data.hxx"
|
||||||
#include "config/Option.hxx"
|
#include "config/Option.hxx"
|
||||||
|
#include "net/AddressInfo.hxx"
|
||||||
|
#include "net/Resolver.hxx"
|
||||||
|
#include "net/ToString.hxx"
|
||||||
#include "util/IterableSplitString.hxx"
|
#include "util/IterableSplitString.hxx"
|
||||||
#include "util/RuntimeError.hxx"
|
#include "util/RuntimeError.hxx"
|
||||||
#include "util/StringView.hxx"
|
#include "util/StringView.hxx"
|
||||||
|
@ -55,6 +58,10 @@ static unsigned permission_default;
|
||||||
static unsigned local_permissions;
|
static unsigned local_permissions;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_TCP
|
||||||
|
static std::map<std::string, unsigned> host_passwords;
|
||||||
|
#endif
|
||||||
|
|
||||||
static unsigned
|
static unsigned
|
||||||
ParsePermission(StringView s)
|
ParsePermission(StringView s)
|
||||||
{
|
{
|
||||||
|
@ -66,10 +73,9 @@ ParsePermission(StringView s)
|
||||||
int(s.size), s.data);
|
int(s.size), s.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
static unsigned parsePermissions(const char *string)
|
static unsigned
|
||||||
|
parsePermissions(std::string_view string)
|
||||||
{
|
{
|
||||||
assert(string != nullptr);
|
|
||||||
|
|
||||||
unsigned permission = 0;
|
unsigned permission = 0;
|
||||||
|
|
||||||
for (const auto i : IterableSplitString(string, PERMISSION_SEPARATOR))
|
for (const auto i : IterableSplitString(string, PERMISSION_SEPARATOR))
|
||||||
|
@ -120,8 +126,40 @@ initPermissions(const ConfigData &config)
|
||||||
: permission_default;
|
: permission_default;
|
||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_TCP
|
||||||
|
for (const auto ¶m : 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
|
int
|
||||||
getPermissionFromPassword(const char *password, unsigned *permission) noexcept
|
getPermissionFromPassword(const char *password, unsigned *permission) noexcept
|
||||||
{
|
{
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
struct ConfigData;
|
struct ConfigData;
|
||||||
|
class SocketAddress;
|
||||||
|
|
||||||
static constexpr unsigned PERMISSION_NONE = 0;
|
static constexpr unsigned PERMISSION_NONE = 0;
|
||||||
static constexpr unsigned PERMISSION_READ = 1;
|
static constexpr unsigned PERMISSION_READ = 1;
|
||||||
|
@ -45,6 +46,12 @@ unsigned
|
||||||
GetLocalPermissions() noexcept;
|
GetLocalPermissions() noexcept;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef HAVE_TCP
|
||||||
|
[[gnu::pure]]
|
||||||
|
int
|
||||||
|
GetPermissionsFromAddress(SocketAddress address) noexcept;
|
||||||
|
#endif
|
||||||
|
|
||||||
void
|
void
|
||||||
initPermissions(const ConfigData &config);
|
initPermissions(const ConfigData &config);
|
||||||
|
|
||||||
|
|
|
@ -32,8 +32,12 @@ GetPermissions(SocketAddress address, int uid) noexcept
|
||||||
#ifdef HAVE_UN
|
#ifdef HAVE_UN
|
||||||
if (address.GetFamily() == AF_LOCAL)
|
if (address.GetFamily() == AF_LOCAL)
|
||||||
return GetLocalPermissions();
|
return GetLocalPermissions();
|
||||||
#else
|
#endif
|
||||||
(void)address;
|
|
||||||
|
#ifdef HAVE_TCP
|
||||||
|
if (int permissions = GetPermissionsFromAddress(address);
|
||||||
|
permissions >= 0)
|
||||||
|
return permissions;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return getDefaultPermissions();
|
return getDefaultPermissions();
|
||||||
|
|
|
@ -48,6 +48,7 @@ enum class ConfigOption {
|
||||||
ZEROCONF_NAME,
|
ZEROCONF_NAME,
|
||||||
ZEROCONF_ENABLED,
|
ZEROCONF_ENABLED,
|
||||||
PASSWORD,
|
PASSWORD,
|
||||||
|
HOST_PERMISSIONS,
|
||||||
LOCAL_PERMISSIONS,
|
LOCAL_PERMISSIONS,
|
||||||
DEFAULT_PERMS,
|
DEFAULT_PERMS,
|
||||||
AUDIO_OUTPUT_FORMAT,
|
AUDIO_OUTPUT_FORMAT,
|
||||||
|
|
|
@ -44,6 +44,7 @@ const ConfigTemplate config_param_templates[] = {
|
||||||
{ "zeroconf_name" },
|
{ "zeroconf_name" },
|
||||||
{ "zeroconf_enabled" },
|
{ "zeroconf_enabled" },
|
||||||
{ "password", true },
|
{ "password", true },
|
||||||
|
{ "host_permissions", true },
|
||||||
{ "local_permissions" },
|
{ "local_permissions" },
|
||||||
{ "default_permissions" },
|
{ "default_permissions" },
|
||||||
{ "audio_output_format" },
|
{ "audio_output_format" },
|
||||||
|
|
|
@ -116,3 +116,32 @@ ToString(SocketAddress address) noexcept
|
||||||
result.append(serv);
|
result.append(serv);
|
||||||
return result;
|
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
|
std::string
|
||||||
ToString(SocketAddress address) noexcept;
|
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
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue