[misc] New net_peer_address_is_trusted with cfg set default to "lan"
New default for "trusted_networks" = "lan". This will check peer addresses against the addresses/netmasks of the interfaces to establish whether the peer is local. Fixes #1754
This commit is contained in:
parent
bf73e51262
commit
c30f44fd01
|
@ -40,9 +40,9 @@ general {
|
||||||
|
|
||||||
# Sets who is allowed to connect without authorisation. This applies to
|
# Sets who is allowed to connect without authorisation. This applies to
|
||||||
# client types like Remotes, DAAP clients (iTunes) and to the web
|
# client types like Remotes, DAAP clients (iTunes) and to the web
|
||||||
# interface. Options are "any", "localhost" or the prefix to one or
|
# interface. Options are "any", "lan", "localhost" or the prefix to one
|
||||||
# more ipv4/6 networks. The default is { "localhost", "192.168", "fd" }
|
# or more ipv4/6 networks. The default is { "lan" }
|
||||||
# trusted_networks = { "localhost", "192.168", "fd" }
|
# trusted_networks = { "lan" }
|
||||||
|
|
||||||
# Enable/disable IPv6
|
# Enable/disable IPv6
|
||||||
# ipv6 = no
|
# ipv6 = no
|
||||||
|
|
|
@ -52,7 +52,7 @@ static cfg_opt_t sec_general[] =
|
||||||
CFG_STR("admin_password", NULL, CFGF_NONE),
|
CFG_STR("admin_password", NULL, CFGF_NONE),
|
||||||
CFG_INT("websocket_port", 3688, CFGF_NONE),
|
CFG_INT("websocket_port", 3688, CFGF_NONE),
|
||||||
CFG_STR("websocket_interface", NULL, CFGF_NONE),
|
CFG_STR("websocket_interface", NULL, CFGF_NONE),
|
||||||
CFG_STR_LIST("trusted_networks", "{localhost,192.168,fd}", CFGF_NONE),
|
CFG_STR_LIST("trusted_networks", "{lan}", CFGF_NONE),
|
||||||
CFG_BOOL("ipv6", cfg_false, CFGF_NONE),
|
CFG_BOOL("ipv6", cfg_false, CFGF_NONE),
|
||||||
CFG_STR("bind_address", NULL, CFGF_NONE),
|
CFG_STR("bind_address", NULL, CFGF_NONE),
|
||||||
CFG_STR("cache_path", STATEDIR "/cache/" PACKAGE "/cache.db", CFGF_NONE),
|
CFG_STR("cache_path", STATEDIR "/cache/" PACKAGE "/cache.db", CFGF_NONE),
|
||||||
|
|
13
src/httpd.c
13
src/httpd.c
|
@ -463,8 +463,7 @@ serve_file(struct httpd_request *hreq)
|
||||||
bool slashed;
|
bool slashed;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
/* Check authentication */
|
if (!httpd_request_is_authorized(hreq))
|
||||||
if (!httpd_admin_check_auth(hreq))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ret = snprintf(path, sizeof(path), "%s%s", webroot_directory, hreq->path);
|
ret = snprintf(path, sizeof(path), "%s%s", webroot_directory, hreq->path);
|
||||||
|
@ -1264,12 +1263,18 @@ httpd_send_error(struct httpd_request *hreq, int error, const char *reason)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
httpd_admin_check_auth(struct httpd_request *hreq)
|
httpd_request_is_trusted(struct httpd_request *hreq)
|
||||||
|
{
|
||||||
|
return httpd_backend_peer_is_trusted(hreq->backend);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
httpd_request_is_authorized(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
const char *passwd;
|
const char *passwd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (net_peer_address_is_trusted(hreq->peer_address))
|
if (httpd_request_is_trusted(hreq))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
passwd = cfg_getstr(cfg_getsec(cfg, "general"), "admin_password");
|
passwd = cfg_getstr(cfg_getsec(cfg, "general"), "admin_password");
|
||||||
|
|
|
@ -150,7 +150,7 @@ artworkapi_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
int status_code;
|
int status_code;
|
||||||
|
|
||||||
if (!httpd_admin_check_auth(hreq))
|
if (!httpd_request_is_authorized(hreq))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!hreq->handler)
|
if (!hreq->handler)
|
||||||
|
|
|
@ -693,7 +693,7 @@ daap_request_authorize(struct httpd_request *hreq)
|
||||||
char *passwd;
|
char *passwd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (net_peer_address_is_trusted(hreq->peer_address))
|
if (httpd_request_is_trusted(hreq))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
// Regular DAAP clients like iTunes will login with /login, and we will reply
|
// Regular DAAP clients like iTunes will login with /login, and we will reply
|
||||||
|
@ -898,7 +898,7 @@ daap_reply_login(struct httpd_request *hreq)
|
||||||
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 32));
|
CHECK_ERR(L_DAAP, evbuffer_expand(hreq->out_body, 32));
|
||||||
|
|
||||||
param = httpd_query_value_find(hreq->query, "pairing-guid");
|
param = httpd_query_value_find(hreq->query, "pairing-guid");
|
||||||
if (param && !net_peer_address_is_trusted(hreq->peer_address))
|
if (param && !httpd_request_is_trusted(hreq))
|
||||||
{
|
{
|
||||||
if (strlen(param) < 3)
|
if (strlen(param) < 3)
|
||||||
{
|
{
|
||||||
|
|
|
@ -600,7 +600,7 @@ dacp_request_authorize(struct httpd_request *hreq)
|
||||||
int32_t id;
|
int32_t id;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (net_peer_address_is_trusted(hreq->peer_address))
|
if (httpd_request_is_trusted(hreq))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
param = httpd_query_value_find(hreq->query, "session-id");
|
param = httpd_query_value_find(hreq->query, "session-id");
|
||||||
|
|
|
@ -266,8 +266,15 @@ httpd_send_error(struct httpd_request *hreq, int error, const char *reason);
|
||||||
void
|
void
|
||||||
httpd_redirect_to(struct httpd_request *hreq, const char *path);
|
httpd_redirect_to(struct httpd_request *hreq, const char *path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The request either came from a trusted peer (based on ip address) checked by
|
||||||
|
* httpd_request_is_trusted() or was WWW-authenticated via httpd_basic_auth()
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
httpd_admin_check_auth(struct httpd_request *hreq);
|
httpd_request_is_authorized(struct httpd_request *hreq);
|
||||||
|
|
||||||
|
bool
|
||||||
|
httpd_request_is_trusted(struct httpd_request *hreq);
|
||||||
|
|
||||||
int
|
int
|
||||||
httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passwd, const char *realm);
|
httpd_basic_auth(struct httpd_request *hreq, const char *user, const char *passwd, const char *realm);
|
||||||
|
@ -348,6 +355,9 @@ httpd_backend_input_buffer_get(httpd_backend *backend);
|
||||||
int
|
int
|
||||||
httpd_backend_peer_get(const char **addr, uint16_t *port, httpd_backend *backend, httpd_backend_data *backend_data);
|
httpd_backend_peer_get(const char **addr, uint16_t *port, httpd_backend *backend, httpd_backend_data *backend_data);
|
||||||
|
|
||||||
|
bool
|
||||||
|
httpd_backend_peer_is_trusted(httpd_backend *backend);
|
||||||
|
|
||||||
int
|
int
|
||||||
httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend);
|
httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend);
|
||||||
|
|
||||||
|
|
|
@ -4644,7 +4644,7 @@ jsonapi_request(struct httpd_request *hreq)
|
||||||
{
|
{
|
||||||
int status_code;
|
int status_code;
|
||||||
|
|
||||||
if (!httpd_admin_check_auth(hreq))
|
if (!httpd_request_is_authorized(hreq))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -537,6 +537,22 @@ httpd_backend_peer_get(const char **addr, uint16_t *port, httpd_backend *backend
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
httpd_backend_peer_is_trusted(httpd_backend *backend)
|
||||||
|
{
|
||||||
|
const struct sockaddr *addr;
|
||||||
|
|
||||||
|
httpd_connection *conn = evhttp_request_get_connection(backend);
|
||||||
|
if (!conn)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
addr = evhttp_connection_get_addr(conn);
|
||||||
|
if (!addr)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return net_peer_address_is_trusted((union net_sockaddr *)addr);
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend)
|
httpd_backend_method_get(enum httpd_methods *method, httpd_backend *backend)
|
||||||
{
|
{
|
||||||
|
|
|
@ -288,7 +288,7 @@ rsp_request_authorize(struct httpd_request *hreq)
|
||||||
char *passwd;
|
char *passwd;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
if (net_peer_address_is_trusted(hreq->peer_address))
|
if (httpd_request_is_trusted(hreq))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
passwd = cfg_getstr(cfg_getsec(cfg, "library"), "password");
|
passwd = cfg_getstr(cfg_getsec(cfg, "library"), "password");
|
||||||
|
|
119
src/misc.c
119
src/misc.c
|
@ -124,20 +124,114 @@ static char *buildopts[] =
|
||||||
|
|
||||||
/* ------------------------ Network utility functions ----------------------- */
|
/* ------------------------ Network utility functions ----------------------- */
|
||||||
|
|
||||||
|
static bool
|
||||||
|
prefix_is_equal(uint8_t *addr1, uint8_t *addr2, uint8_t *mask, size_t len)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < len; i++)
|
||||||
|
{
|
||||||
|
if ((addr1[i] & mask[i]) != (addr2[i] & mask[i]))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checks if the address is in any of the interface subnets
|
||||||
|
static bool
|
||||||
|
net_address_is_local(union net_sockaddr *naddr)
|
||||||
|
{
|
||||||
|
struct ifaddrs *ifaddrs;
|
||||||
|
struct ifaddrs *iap;
|
||||||
|
void *if_addr;
|
||||||
|
void *if_netmask;
|
||||||
|
void *addr;
|
||||||
|
int addr_len;
|
||||||
|
sa_family_t addr_family;
|
||||||
|
bool is_local = false;
|
||||||
|
|
||||||
|
// Point addr to the actual address data
|
||||||
|
if (naddr->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&naddr->sin6.sin6_addr))
|
||||||
|
{
|
||||||
|
addr = naddr->sin6.sin6_addr.s6_addr + 12; // ipv4 over ipv6 prefix is 12 bytes
|
||||||
|
addr_len = sizeof(struct in6_addr) - 12;
|
||||||
|
addr_family = AF_INET;
|
||||||
|
}
|
||||||
|
else if (naddr->sa.sa_family == AF_INET6)
|
||||||
|
{
|
||||||
|
addr = naddr->sin6.sin6_addr.s6_addr;
|
||||||
|
addr_len = sizeof(struct in6_addr);
|
||||||
|
addr_family = AF_INET6;
|
||||||
|
}
|
||||||
|
else if (naddr->sa.sa_family == AF_INET)
|
||||||
|
{
|
||||||
|
addr = &naddr->sin.sin_addr.s_addr;
|
||||||
|
addr_len = sizeof(struct in_addr);
|
||||||
|
addr_family = AF_INET;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
getifaddrs(&ifaddrs);
|
||||||
|
|
||||||
|
for (iap = ifaddrs; iap && !is_local; iap = iap->ifa_next)
|
||||||
|
{
|
||||||
|
if (!iap->ifa_addr || !iap->ifa_netmask)
|
||||||
|
continue;
|
||||||
|
if (iap->ifa_addr->sa_family != addr_family || iap->ifa_netmask->sa_family != addr_family)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (addr_family == AF_INET6)
|
||||||
|
{
|
||||||
|
if_addr = ((struct sockaddr_in6 *)iap->ifa_addr)->sin6_addr.s6_addr;
|
||||||
|
if_netmask = ((struct sockaddr_in6 *)iap->ifa_netmask)->sin6_addr.s6_addr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if_addr = &((struct sockaddr_in *)iap->ifa_addr)->sin_addr.s_addr;
|
||||||
|
if_netmask = &((struct sockaddr_in *)iap->ifa_netmask)->sin_addr.s_addr;
|
||||||
|
}
|
||||||
|
|
||||||
|
is_local = prefix_is_equal(addr, if_addr, if_netmask, addr_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
freeifaddrs(ifaddrs);
|
||||||
|
|
||||||
|
return is_local;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
net_address_has_prefix(union net_sockaddr *naddr, const char *prefix)
|
||||||
|
{
|
||||||
|
char buf[64];
|
||||||
|
const char *addr = buf;
|
||||||
|
|
||||||
|
if (net_address_get(buf, sizeof(buf), naddr) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (naddr->sa.sa_family == AF_INET6 && IN6_IS_ADDR_V4MAPPED(&naddr->sin6.sin6_addr))
|
||||||
|
addr += strlen("::ffff:");
|
||||||
|
|
||||||
|
if (strncmp(addr, prefix, strlen(prefix)) == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
net_peer_address_is_trusted(const char *addr)
|
net_peer_address_is_trusted(union net_sockaddr *naddr)
|
||||||
{
|
{
|
||||||
cfg_t *section;
|
cfg_t *section;
|
||||||
const char *network;
|
const char *network;
|
||||||
int i;
|
int i;
|
||||||
int n;
|
int n;
|
||||||
|
|
||||||
if (!addr)
|
if (!naddr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (strncmp(addr, "::ffff:", strlen("::ffff:")) == 0)
|
|
||||||
addr += strlen("::ffff:");
|
|
||||||
|
|
||||||
section = cfg_getsec(cfg, "general");
|
section = cfg_getsec(cfg, "general");
|
||||||
|
|
||||||
n = cfg_size(section, "trusted_networks");
|
n = cfg_size(section, "trusted_networks");
|
||||||
|
@ -148,14 +242,17 @@ net_peer_address_is_trusted(const char *addr)
|
||||||
if (!network || network[0] == '\0')
|
if (!network || network[0] == '\0')
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (strncmp(network, addr, strlen(network)) == 0)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if ((strcmp(network, "localhost") == 0) && (strcmp(addr, "127.0.0.1") == 0 || strcmp(addr, "::1") == 0))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (strcmp(network, "any") == 0)
|
if (strcmp(network, "any") == 0)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (strcmp(network, "lan") == 0 && net_address_is_local(naddr))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (strcmp(network, "localhost"))
|
||||||
|
network = (naddr->sa.sa_family == AF_INET6) ? "::1" : "127.0.0.1";
|
||||||
|
|
||||||
|
if (net_address_has_prefix(naddr, network))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -25,7 +25,7 @@ union net_sockaddr
|
||||||
|
|
||||||
// Checks if the address is in a network that is configured as trusted
|
// Checks if the address is in a network that is configured as trusted
|
||||||
bool
|
bool
|
||||||
net_peer_address_is_trusted(const char *addr);
|
net_peer_address_is_trusted(union net_sockaddr *naddr);
|
||||||
|
|
||||||
int
|
int
|
||||||
net_address_get(char *addr, size_t addr_len, union net_sockaddr *naddr);
|
net_address_get(char *addr, size_t addr_len, union net_sockaddr *naddr);
|
||||||
|
|
29
src/mpd.c
29
src/mpd.c
|
@ -4479,31 +4479,6 @@ mpd_input_filter(struct evbuffer *src, struct evbuffer *dst, ev_ssize_t lim, enu
|
||||||
return BEV_OK;
|
return BEV_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char *
|
|
||||||
sockaddr_to_string(const struct sockaddr *address, char *addr_str, int addr_str_len)
|
|
||||||
{
|
|
||||||
struct sockaddr_in *addr;
|
|
||||||
struct sockaddr_in6 *addr6;
|
|
||||||
const char *ret;
|
|
||||||
|
|
||||||
if (address->sa_family == AF_INET)
|
|
||||||
{
|
|
||||||
addr = (struct sockaddr_in *)address;
|
|
||||||
ret = evutil_inet_ntop(AF_INET, &addr->sin_addr, addr_str, addr_str_len);
|
|
||||||
}
|
|
||||||
else if (address->sa_family == AF_INET6)
|
|
||||||
{
|
|
||||||
addr6 = (struct sockaddr_in6 *)address;
|
|
||||||
ret = evutil_inet_ntop(AF_INET6, &addr6->sin6_addr, addr_str, addr_str_len);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ret = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The connection listener callback function is invoked when a new connection was received.
|
* The connection listener callback function is invoked when a new connection was received.
|
||||||
*
|
*
|
||||||
|
@ -4525,7 +4500,6 @@ mpd_accept_conn_cb(struct evconnlistener *listener,
|
||||||
*/
|
*/
|
||||||
struct event_base *base = evconnlistener_get_base(listener);
|
struct event_base *base = evconnlistener_get_base(listener);
|
||||||
struct bufferevent *bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
|
struct bufferevent *bev = bufferevent_socket_new(base, sock, BEV_OPT_CLOSE_ON_FREE);
|
||||||
char addr_str[INET6_ADDRSTRLEN];
|
|
||||||
struct mpd_client_ctx *client_ctx = calloc(1, sizeof(struct mpd_client_ctx));
|
struct mpd_client_ctx *client_ctx = calloc(1, sizeof(struct mpd_client_ctx));
|
||||||
|
|
||||||
if (!client_ctx)
|
if (!client_ctx)
|
||||||
|
@ -4538,8 +4512,7 @@ mpd_accept_conn_cb(struct evconnlistener *listener,
|
||||||
client_ctx->authenticated = !cfg_getstr(cfg_getsec(cfg, "library"), "password");
|
client_ctx->authenticated = !cfg_getstr(cfg_getsec(cfg, "library"), "password");
|
||||||
if (!client_ctx->authenticated)
|
if (!client_ctx->authenticated)
|
||||||
{
|
{
|
||||||
sockaddr_to_string(address, addr_str, sizeof(addr_str));
|
client_ctx->authenticated = net_peer_address_is_trusted((union net_sockaddr *)address);
|
||||||
client_ctx->authenticated = net_peer_address_is_trusted(addr_str);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
client_ctx->next = mpd_clients;
|
client_ctx->next = mpd_clients;
|
||||||
|
|
Loading…
Reference in New Issue