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:
Justin Maggard 2015-12-21 14:31:52 -08:00
parent 4ec6cd0053
commit c4166b3e77
7 changed files with 86 additions and 11 deletions

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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);