mirror of
https://github.com/VDR4Arch/vdr.git
synced 2023-10-10 13:36:52 +02:00
New option '-g'; fixed security hole CAN-2005-0071 when grabbing to file
This commit is contained in:
parent
924827fcbe
commit
3f21bf20c5
@ -1370,6 +1370,8 @@ Darren Salt <linux@youmustbejoking.demon.co.uk>
|
|||||||
'--no-kbd'
|
'--no-kbd'
|
||||||
for adding '__attribute__' to functions that use printf() like parameters
|
for adding '__attribute__' to functions that use printf() like parameters
|
||||||
for suggesting to write grabbed images to the SVDRP connection encoded in base64
|
for suggesting to write grabbed images to the SVDRP connection encoded in base64
|
||||||
|
for suggesting to open the file handle in the SVDRP GRAB command in a way that
|
||||||
|
it won't follow symbolic links, and to canonicalize the file name
|
||||||
|
|
||||||
Sean Carlos <seanc@libero.it>
|
Sean Carlos <seanc@libero.it>
|
||||||
for translating OSD texts to the Italian language
|
for translating OSD texts to the Italian language
|
||||||
@ -1571,3 +1573,7 @@ Bob Withers <bwit@pobox.com>
|
|||||||
for publishing a Base64 encoder at http://www.ruffboy.com/download.htm
|
for publishing a Base64 encoder at http://www.ruffboy.com/download.htm
|
||||||
(http://www.ruffboy.com/code/Base64.zip), part of which was used when writing
|
(http://www.ruffboy.com/code/Base64.zip), part of which was used when writing
|
||||||
the cBase64Encoder class
|
the cBase64Encoder class
|
||||||
|
|
||||||
|
Javier Fernández-Sanguino Peña <jfs@computer.org>
|
||||||
|
for reporting a security hole in the way the SVDRP command GRAB writes the
|
||||||
|
image file
|
||||||
|
16
HISTORY
16
HISTORY
@ -3963,7 +3963,7 @@ Video Disk Recorder Revision History
|
|||||||
commands may now be executed at any time, and the message will be displayed
|
commands may now be executed at any time, and the message will be displayed
|
||||||
(no more "pending message").
|
(no more "pending message").
|
||||||
|
|
||||||
2005-12-29: Version 1.3.38
|
2005-12-30: Version 1.3.38
|
||||||
|
|
||||||
- Fixed handling second audio and Dolby Digital PIDs for encrypted channels
|
- Fixed handling second audio and Dolby Digital PIDs for encrypted channels
|
||||||
(was broken in version 1.3.37).
|
(was broken in version 1.3.37).
|
||||||
@ -4023,3 +4023,17 @@ Video Disk Recorder Revision History
|
|||||||
(encoded in base64) if the given file name consists of only the file
|
(encoded in base64) if the given file name consists of only the file
|
||||||
extension (".jpg", ".jpeg" or ".pnm"), or if only "-" is given as file
|
extension (".jpg", ".jpeg" or ".pnm"), or if only "-" is given as file
|
||||||
name (based on a suggestion from Darren Salt).
|
name (based on a suggestion from Darren Salt).
|
||||||
|
- The new command line option '-g' must be given if the SVDRP command GRAB
|
||||||
|
shall be allowed to write image files to disk. The parameter to this option
|
||||||
|
must be the full path name of an existing directory, without any "..", double
|
||||||
|
'/' or symlinks. By default, or if "-g- is given, grabbing to files is
|
||||||
|
not allowed any more because of potential security risks.
|
||||||
|
- Modified the way the SVDRP command GRAB writes the grabbed image to a file
|
||||||
|
to avoid a security hole (CAN-2005-0071, reported by Javier Fernández-Sanguino
|
||||||
|
Peña):
|
||||||
|
+ The file handle is now opened in a way that it won't follow symbolic links
|
||||||
|
(suggested by Darren Salt).
|
||||||
|
+ The given file name is now canonicalized, so that it won't contain any
|
||||||
|
".." or symlinks (suggested by Darren Salt).
|
||||||
|
+ Grabbing to files is limited to the directory given in the the command
|
||||||
|
line option '-g'. By default grabbing to files is not allowed any more.
|
||||||
|
10
device.c
10
device.c
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: device.c 1.113 2005/12/29 14:51:41 kls Exp $
|
* $Id: device.c 1.114 2005/12/30 13:48:29 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
@ -330,12 +330,12 @@ uchar *cDevice::GrabImage(int &Size, bool Jpeg, int Quality, int SizeX, int Size
|
|||||||
bool cDevice::GrabImageFile(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
|
bool cDevice::GrabImageFile(const char *FileName, bool Jpeg, int Quality, int SizeX, int SizeY)
|
||||||
{
|
{
|
||||||
int result = 0;
|
int result = 0;
|
||||||
FILE *f = fopen(FileName, "wb");
|
int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
|
||||||
if (f) {
|
if (fd >= 0) {
|
||||||
int ImageSize;
|
int ImageSize;
|
||||||
uchar *Image = GrabImage(ImageSize, Jpeg, Quality, SizeX, SizeY);
|
uchar *Image = GrabImage(ImageSize, Jpeg, Quality, SizeX, SizeY);
|
||||||
if (Image) {
|
if (Image) {
|
||||||
if (fwrite(Image, ImageSize, 1, f) == 1)
|
if (safe_write(fd, Image, ImageSize) == ImageSize)
|
||||||
isyslog("grabbed image to %s", FileName);
|
isyslog("grabbed image to %s", FileName);
|
||||||
else {
|
else {
|
||||||
LOG_ERROR_STR(FileName);
|
LOG_ERROR_STR(FileName);
|
||||||
@ -345,7 +345,7 @@ bool cDevice::GrabImageFile(const char *FileName, bool Jpeg, int Quality, int Si
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
result |= 1;
|
result |= 1;
|
||||||
fclose(f);
|
close(fd);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_ERROR_STR(FileName);
|
LOG_ERROR_STR(FileName);
|
||||||
|
54
svdrp.c
54
svdrp.c
@ -10,7 +10,7 @@
|
|||||||
* and interact with the Video Disk Recorder - or write a full featured
|
* and interact with the Video Disk Recorder - or write a full featured
|
||||||
* graphical interface that sits on top of an SVDRP connection.
|
* graphical interface that sits on top of an SVDRP connection.
|
||||||
*
|
*
|
||||||
* $Id: svdrp.c 1.87 2005/12/30 10:27:23 kls Exp $
|
* $Id: svdrp.c 1.88 2005/12/30 15:11:16 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "svdrp.h"
|
#include "svdrp.h"
|
||||||
@ -361,6 +361,8 @@ const char *GetHelpPage(const char *Cmd, const char **p)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *cSVDRP::grabImageDir = NULL;
|
||||||
|
|
||||||
cSVDRP::cSVDRP(int Port)
|
cSVDRP::cSVDRP(int Port)
|
||||||
:socket(Port)
|
:socket(Port)
|
||||||
{
|
{
|
||||||
@ -663,6 +665,7 @@ void cSVDRP::CmdGRAB(const char *Option)
|
|||||||
const char *delim = " \t";
|
const char *delim = " \t";
|
||||||
char *strtok_next;
|
char *strtok_next;
|
||||||
FileName = strtok_r(p, delim, &strtok_next);
|
FileName = strtok_r(p, delim, &strtok_next);
|
||||||
|
// image type:
|
||||||
char *Extension = strrchr(FileName, '.');
|
char *Extension = strrchr(FileName, '.');
|
||||||
if (Extension) {
|
if (Extension) {
|
||||||
if (strcasecmp(Extension, ".jpg") == 0 || strcasecmp(Extension, ".jpeg") == 0)
|
if (strcasecmp(Extension, ".jpg") == 0 || strcasecmp(Extension, ".jpeg") == 0)
|
||||||
@ -682,6 +685,7 @@ void cSVDRP::CmdGRAB(const char *Option)
|
|||||||
Reply(501, "Missing filename extension in \"%s\"", FileName);
|
Reply(501, "Missing filename extension in \"%s\"", FileName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// image quality (and obsolete type):
|
||||||
if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
|
if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
|
||||||
if (strcasecmp(p, "JPEG") == 0 || strcasecmp(p, "PNM") == 0) {
|
if (strcasecmp(p, "JPEG") == 0 || strcasecmp(p, "PNM") == 0) {
|
||||||
// tolerate for backward compatibility
|
// tolerate for backward compatibility
|
||||||
@ -696,6 +700,7 @@ void cSVDRP::CmdGRAB(const char *Option)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// image size:
|
||||||
if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
|
if ((p = strtok_r(NULL, delim, &strtok_next)) != NULL) {
|
||||||
if (isnumber(p))
|
if (isnumber(p))
|
||||||
SizeX = atoi(p);
|
SizeX = atoi(p);
|
||||||
@ -720,19 +725,52 @@ void cSVDRP::CmdGRAB(const char *Option)
|
|||||||
Reply(501, "Unexpected parameter \"%s\"", p);
|
Reply(501, "Unexpected parameter \"%s\"", p);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// canonicalize the file name:
|
||||||
|
char RealFileName[PATH_MAX];
|
||||||
|
if (FileName) {
|
||||||
|
if (grabImageDir) {
|
||||||
|
char *s;
|
||||||
|
asprintf(&s, "%s/%s", grabImageDir, FileName);
|
||||||
|
FileName = s;
|
||||||
|
char *slash = strrchr(FileName, '/'); // there definitely is one
|
||||||
|
*slash = 0;
|
||||||
|
char *r = realpath(FileName, RealFileName);
|
||||||
|
*slash = '/';
|
||||||
|
if (!r) {
|
||||||
|
LOG_ERROR_STR(FileName);
|
||||||
|
Reply(501, "Illegal file name \"%s\"", FileName);
|
||||||
|
free(s);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
strcat(RealFileName, slash);
|
||||||
|
FileName = RealFileName;
|
||||||
|
free(s);
|
||||||
|
if (strncmp(FileName, grabImageDir, strlen(grabImageDir)) != 0) {
|
||||||
|
Reply(501, "Illegal file name \"%s\"", FileName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Reply(550, "Grabbing to file not allowed (use \"GRAB -\" instead)");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// actual grabbing:
|
||||||
int ImageSize;
|
int ImageSize;
|
||||||
uchar *Image = cDevice::PrimaryDevice()->GrabImage(ImageSize, Jpeg, Quality, SizeX, SizeY);
|
uchar *Image = cDevice::PrimaryDevice()->GrabImage(ImageSize, Jpeg, Quality, SizeX, SizeY);
|
||||||
if (Image) {
|
if (Image) {
|
||||||
if (FileName) {
|
if (FileName) {
|
||||||
FILE *f = fopen(FileName, "wb");
|
int fd = open(FileName, O_WRONLY | O_CREAT | O_NOFOLLOW | O_TRUNC, DEFFILEMODE);
|
||||||
if (f) {
|
if (fd >= 0) {
|
||||||
if (fwrite(Image, ImageSize, 1, f) == 1)
|
if (safe_write(fd, Image, ImageSize) == ImageSize) {
|
||||||
|
isyslog("grabbed image to %s", FileName);
|
||||||
Reply(250, "Grabbed image %s", Option);
|
Reply(250, "Grabbed image %s", Option);
|
||||||
|
}
|
||||||
else {
|
else {
|
||||||
LOG_ERROR_STR(FileName);
|
LOG_ERROR_STR(FileName);
|
||||||
Reply(451, "Can't write to '%s'", FileName);
|
Reply(451, "Can't write to '%s'", FileName);
|
||||||
}
|
}
|
||||||
fclose(f);
|
close(fd);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG_ERROR_STR(FileName);
|
LOG_ERROR_STR(FileName);
|
||||||
@ -1530,4 +1568,10 @@ bool cSVDRP::Process(void)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cSVDRP::SetGrabImageDir(const char *GrabImageDir)
|
||||||
|
{
|
||||||
|
free(grabImageDir);
|
||||||
|
grabImageDir = GrabImageDir ? strdup(GrabImageDir) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
//TODO more than one connection???
|
//TODO more than one connection???
|
||||||
|
4
svdrp.h
4
svdrp.h
@ -4,7 +4,7 @@
|
|||||||
* See the main source file 'vdr.c' for copyright information and
|
* See the main source file 'vdr.c' for copyright information and
|
||||||
* how to reach the author.
|
* how to reach the author.
|
||||||
*
|
*
|
||||||
* $Id: svdrp.h 1.26 2005/11/27 15:26:42 kls Exp $
|
* $Id: svdrp.h 1.27 2005/12/30 14:46:38 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef __SVDRP_H
|
#ifndef __SVDRP_H
|
||||||
@ -49,6 +49,7 @@ private:
|
|||||||
int length;
|
int length;
|
||||||
char *cmdLine;
|
char *cmdLine;
|
||||||
time_t lastActivity;
|
time_t lastActivity;
|
||||||
|
static char *grabImageDir;
|
||||||
void Close(bool Timeout = false);
|
void Close(bool Timeout = false);
|
||||||
bool Send(const char *s, int length = -1);
|
bool Send(const char *s, int length = -1);
|
||||||
void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
void Reply(int Code, const char *fmt, ...) __attribute__ ((format (printf, 3, 4)));
|
||||||
@ -87,6 +88,7 @@ public:
|
|||||||
~cSVDRP();
|
~cSVDRP();
|
||||||
bool HasConnection(void) { return file.IsOpen(); }
|
bool HasConnection(void) { return file.IsOpen(); }
|
||||||
bool Process(void);
|
bool Process(void);
|
||||||
|
static void SetGrabImageDir(const char *GrabImageDir);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //__SVDRP_H
|
#endif //__SVDRP_H
|
||||||
|
9
vdr.1
9
vdr.1
@ -8,7 +8,7 @@
|
|||||||
.\" License as specified in the file COPYING that comes with the
|
.\" License as specified in the file COPYING that comes with the
|
||||||
.\" vdr distribution.
|
.\" vdr distribution.
|
||||||
.\"
|
.\"
|
||||||
.\" $Id: vdr.1 1.15 2005/10/09 12:31:03 kls Exp $
|
.\" $Id: vdr.1 1.16 2005/12/30 15:09:01 kls Exp $
|
||||||
.\"
|
.\"
|
||||||
.TH vdr 1 "19 Dec 2004" "1.3.18" "Video Disk Recorder"
|
.TH vdr 1 "19 Dec 2004" "1.3.18" "Video Disk Recorder"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
@ -59,6 +59,13 @@ Use \fB\-E\-\fR to disable this.
|
|||||||
If \fIfile\fR is a directory, the file \fIepg.data\fR
|
If \fIfile\fR is a directory, the file \fIepg.data\fR
|
||||||
will be created in that directory.
|
will be created in that directory.
|
||||||
.TP
|
.TP
|
||||||
|
.BI \-g,\ \-\-grab= dir
|
||||||
|
Write images from the SVDRP command GRAB into the
|
||||||
|
given directory \fIdir\fR. \fIdir\fR must be the full path name of an
|
||||||
|
existing directory, without any "..", double '/'
|
||||||
|
or symlinks. By default, or if \fB\-g\-\fR is given,
|
||||||
|
grabbing images to disk is disabled.
|
||||||
|
.TP
|
||||||
.B \-h, \-\-help
|
.B \-h, \-\-help
|
||||||
Print a help message and exit.
|
Print a help message and exit.
|
||||||
.TP
|
.TP
|
||||||
|
11
vdr.c
11
vdr.c
@ -22,7 +22,7 @@
|
|||||||
*
|
*
|
||||||
* The project's page is at http://www.cadsoft.de/vdr
|
* The project's page is at http://www.cadsoft.de/vdr
|
||||||
*
|
*
|
||||||
* $Id: vdr.c 1.222 2005/12/18 14:38:30 kls Exp $
|
* $Id: vdr.c 1.223 2005/12/30 15:07:47 kls Exp $
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <getopt.h>
|
#include <getopt.h>
|
||||||
@ -144,6 +144,7 @@ int main(int argc, char *argv[])
|
|||||||
{ "daemon", no_argument, NULL, 'd' },
|
{ "daemon", no_argument, NULL, 'd' },
|
||||||
{ "device", required_argument, NULL, 'D' },
|
{ "device", required_argument, NULL, 'D' },
|
||||||
{ "epgfile", required_argument, NULL, 'E' },
|
{ "epgfile", required_argument, NULL, 'E' },
|
||||||
|
{ "grab", required_argument, NULL, 'g' },
|
||||||
{ "help", no_argument, NULL, 'h' },
|
{ "help", no_argument, NULL, 'h' },
|
||||||
{ "lib", required_argument, NULL, 'L' },
|
{ "lib", required_argument, NULL, 'L' },
|
||||||
{ "lirc", optional_argument, NULL, 'l' | 0x100 },
|
{ "lirc", optional_argument, NULL, 'l' | 0x100 },
|
||||||
@ -164,7 +165,7 @@ int main(int argc, char *argv[])
|
|||||||
};
|
};
|
||||||
|
|
||||||
int c;
|
int c;
|
||||||
while ((c = getopt_long(argc, argv, "a:c:dD:E:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {
|
while ((c = getopt_long(argc, argv, "a:c:dD:E:g:hl:L:mp:P:r:s:t:v:Vw:", long_options, NULL)) != -1) {
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case 'a': AudioCommand = optarg;
|
case 'a': AudioCommand = optarg;
|
||||||
break;
|
break;
|
||||||
@ -183,6 +184,8 @@ int main(int argc, char *argv[])
|
|||||||
break;
|
break;
|
||||||
case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL);
|
case 'E': EpgDataFileName = (*optarg != '-' ? optarg : NULL);
|
||||||
break;
|
break;
|
||||||
|
case 'g': cSVDRP::SetGrabImageDir(*optarg != '-' ? optarg : NULL);
|
||||||
|
break;
|
||||||
case 'h': DisplayHelp = true;
|
case 'h': DisplayHelp = true;
|
||||||
break;
|
break;
|
||||||
case 'l': {
|
case 'l': {
|
||||||
@ -290,6 +293,10 @@ int main(int argc, char *argv[])
|
|||||||
" '-E-' disables this\n"
|
" '-E-' disables this\n"
|
||||||
" if FILE is a directory, the default EPG file will be\n"
|
" if FILE is a directory, the default EPG file will be\n"
|
||||||
" created in that directory\n"
|
" created in that directory\n"
|
||||||
|
" -g DIR --grab=DIR write images from the SVDRP command GRAB into the\n"
|
||||||
|
" given DIR; DIR must be the full path name of an\n"
|
||||||
|
" existing directory, without any \"..\", double '/'\n"
|
||||||
|
" or symlinks (default: none, same as -g-)\n"
|
||||||
" -h, --help print this help and exit\n"
|
" -h, --help print this help and exit\n"
|
||||||
" -l LEVEL, --log=LEVEL set log level (default: 3)\n"
|
" -l LEVEL, --log=LEVEL set log level (default: 3)\n"
|
||||||
" 0 = no logging, 1 = errors only,\n"
|
" 0 = no logging, 1 = errors only,\n"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user