options: Add wide_links config option.
In some environments, it makes sense to validate that symlink destinations remain inside user-defined media_dirs. Make this behavior configurable.
This commit is contained in:
parent
4ec6cd0053
commit
c4166b3e77
@ -750,6 +750,10 @@ init(int argc, char **argv)
|
||||
if (strtobool(ary_options[i].value))
|
||||
SETFLAG(MERGE_MEDIA_DIRS_MASK);
|
||||
break;
|
||||
case WIDE_LINKS:
|
||||
if (strtobool(ary_options[i].value))
|
||||
SETFLAG(WIDE_LINKS_MASK);
|
||||
break;
|
||||
default:
|
||||
DPRINTF(E_ERROR, L_GENERAL, "Unknown option in file %s\n",
|
||||
optionsfile);
|
||||
|
@ -81,3 +81,6 @@ model_number=1
|
||||
# maximum number of simultaneous connections
|
||||
# note: many clients open several simultaneous connections while streaming
|
||||
#max_connections=50
|
||||
|
||||
# set this to yes to allow symlinks that point outside user-defined media_dirs.
|
||||
#wide_links=no
|
||||
|
@ -163,6 +163,10 @@ force_sort_criteria=+upnp:class,+upnp:originalTrackNumber,+dc:title
|
||||
|
||||
.fi
|
||||
|
||||
.IP "\fBwide_links\fP"
|
||||
Set to 'yes' to allow symlinks that point outside user-defined media_dirs.
|
||||
By default, wide symlinks are not followed.
|
||||
|
||||
|
||||
|
||||
.SH VERSION
|
||||
|
@ -64,7 +64,8 @@ static const struct {
|
||||
{ USER_ACCOUNT, "user" },
|
||||
{ FORCE_SORT_CRITERIA, "force_sort_criteria" },
|
||||
{ MAX_CONNECTIONS, "max_connections" },
|
||||
{ MERGE_MEDIA_DIRS, "merge_media_dirs" }
|
||||
{ MERGE_MEDIA_DIRS, "merge_media_dirs" },
|
||||
{ WIDE_LINKS, "wide_links" }
|
||||
};
|
||||
|
||||
int
|
||||
|
@ -57,7 +57,8 @@ enum upnpconfigoptions {
|
||||
USER_ACCOUNT, /* user account to run as */
|
||||
FORCE_SORT_CRITERIA, /* force sorting by a given sort criteria */
|
||||
MAX_CONNECTIONS, /* maximum number of simultaneous connections */
|
||||
MERGE_MEDIA_DIRS /* don't add an extra directory level when there are multiple media dirs */
|
||||
MERGE_MEDIA_DIRS, /* don't add an extra directory level when there are multiple media dirs */
|
||||
WIDE_LINKS /* allow following symlinks outside the defined media_dirs */
|
||||
};
|
||||
|
||||
/* readoptionsfile()
|
||||
|
@ -191,6 +191,7 @@ extern uint32_t runtime_flags;
|
||||
#define NO_PLAYLIST_MASK 0x0008
|
||||
#define SYSTEMD_MASK 0x0010
|
||||
#define MERGE_MEDIA_DIRS_MASK 0x0020
|
||||
#define WIDE_LINKS_MASK 0x0040
|
||||
|
||||
#define SETFLAG(mask) runtime_flags |= mask
|
||||
#define GETFLAG(mask) (runtime_flags & mask)
|
||||
|
79
upnphttp.c
79
upnphttp.c
@ -477,6 +477,21 @@ Send400(struct upnphttp * h)
|
||||
CloseSocket_upnphttp(h);
|
||||
}
|
||||
|
||||
/* very minimalistic 403 error message */
|
||||
static void
|
||||
Send403(struct upnphttp * h)
|
||||
{
|
||||
static const char body403[] =
|
||||
"<HTML><HEAD><TITLE>403 Forbidden</TITLE></HEAD>"
|
||||
"<BODY><H1>Forbidden</H1>You don't have permission to access this resource."
|
||||
"</BODY></HTML>\r\n";
|
||||
h->respflags = FLAG_HTML;
|
||||
BuildResp2_upnphttp(h, 403, "Forbidden",
|
||||
body403, sizeof(body403) - 1);
|
||||
SendResp_upnphttp(h);
|
||||
CloseSocket_upnphttp(h);
|
||||
}
|
||||
|
||||
/* very minimalistic 404 error message */
|
||||
static void
|
||||
Send404(struct upnphttp * h)
|
||||
@ -1328,6 +1343,46 @@ start_dlna_header(struct string_s *str, int respcode, const char *tmode, const c
|
||||
respcode, date, tmode, mime);
|
||||
}
|
||||
|
||||
static int
|
||||
_open_file(const char *orig_path)
|
||||
{
|
||||
struct media_dir_s *media_path;
|
||||
char buf[PATH_MAX];
|
||||
const char *path;
|
||||
int fd;
|
||||
|
||||
if (!GETFLAG(WIDE_LINKS_MASK))
|
||||
{
|
||||
path = realpath(orig_path, buf);
|
||||
if (!path)
|
||||
{
|
||||
DPRINTF(E_ERROR, L_HTTP, "Error resolving path %s: %s\n",
|
||||
orig_path, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (media_path = media_dirs; media_path; media_path = media_path->next)
|
||||
{
|
||||
if (strncmp(path, media_path->path, strlen(media_path->path)) == 0)
|
||||
break;
|
||||
}
|
||||
if (!media_path && strncmp(path, db_path, strlen(db_path)))
|
||||
{
|
||||
DPRINTF(E_ERROR, L_HTTP, "Rejecting wide link %s -> %s\n",
|
||||
orig_path, path);
|
||||
return -403;
|
||||
}
|
||||
}
|
||||
else
|
||||
path = orig_path;
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
if (fd < 0)
|
||||
DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path);
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static void
|
||||
SendResp_icon(struct upnphttp * h, char * icon)
|
||||
{
|
||||
@ -1413,11 +1468,13 @@ SendResp_albumArt(struct upnphttp * h, char * object)
|
||||
}
|
||||
DPRINTF(E_INFO, L_HTTP, "Serving album art ID: %lld [%s]\n", id, path);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
fd = _open_file(path);
|
||||
if( fd < 0 ) {
|
||||
DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path);
|
||||
sqlite3_free(path);
|
||||
Send404(h);
|
||||
if (fd == -403)
|
||||
Send403(h);
|
||||
else
|
||||
Send404(h);
|
||||
return;
|
||||
}
|
||||
sqlite3_free(path);
|
||||
@ -1461,11 +1518,13 @@ SendResp_caption(struct upnphttp * h, char * object)
|
||||
}
|
||||
DPRINTF(E_INFO, L_HTTP, "Serving caption ID: %lld [%s]\n", id, path);
|
||||
|
||||
fd = open(path, O_RDONLY);
|
||||
fd = _open_file(path);
|
||||
if( fd < 0 ) {
|
||||
DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", path);
|
||||
sqlite3_free(path);
|
||||
Send404(h);
|
||||
if (fd == -403)
|
||||
Send403(h);
|
||||
else
|
||||
Send404(h);
|
||||
return;
|
||||
}
|
||||
sqlite3_free(path);
|
||||
@ -1914,10 +1973,12 @@ SendResp_dlnafile(struct upnphttp *h, char *object)
|
||||
}
|
||||
|
||||
offset = h->req_RangeStart;
|
||||
sendfh = open(last_file.path, O_RDONLY);
|
||||
sendfh = _open_file(last_file.path);
|
||||
if( sendfh < 0 ) {
|
||||
DPRINTF(E_ERROR, L_HTTP, "Error opening %s\n", last_file.path);
|
||||
Send404(h);
|
||||
if (sendfh == -403)
|
||||
Send403(h);
|
||||
else
|
||||
Send404(h);
|
||||
goto error;
|
||||
}
|
||||
size = lseek(sendfh, 0, SEEK_END);
|
||||
|
Loading…
x
Reference in New Issue
Block a user