- added HTTP authentication (#475)

Modified Files:
	HISTORY README streamdev-server.c server/connection.h
	server/connectionHTTP.c server/connectionHTTP.h
	server/server.c server/server.h
This commit is contained in:
schmirl 2008-10-14 11:05:46 +00:00
parent 992444cb67
commit 86c82c1381
8 changed files with 80 additions and 13 deletions

View File

@ -1,6 +1,7 @@
VDR Plugin 'streamdev' Revision History VDR Plugin 'streamdev' Revision History
--------------------------------------- ---------------------------------------
- added HTTP authentication
- compatibility for VDR 1.7.1 (thanks to Udo Richter) - compatibility for VDR 1.7.1 (thanks to Udo Richter)
- added vdr-1.6.0-intcamdevices.patch (thanks to Anssi Hannula) - added vdr-1.6.0-intcamdevices.patch (thanks to Anssi Hannula)
- fixed problem when switching from one encrypted channel to an other - fixed problem when switching from one encrypted channel to an other

24
README
View File

@ -118,16 +118,20 @@ make [options, if necessary] plugins
---------------------------------- ----------------------------------
Starting with streamdev 0.4.0, all additional files are kept in a directory Starting with streamdev 0.4.0, all additional files are kept in a directory
called "streamdev" inside VDR's plugin config directory. This affects in called "streamdev" inside VDR's plugin config directory. It is the new default
particular the file "streamdevhosts.conf". You will have to move it to its location of externremux.sh and the new place where streamdev-server expects the
new location: file "streamdevhosts.conf". You will have to move this file to its new location:
mv VDRCONFDIR/plugins/streamdevhosts.conf VDRCONFDIR/plugins/streamdev/ mv VDRCONFDIR/plugins/streamdevhosts.conf VDRCONFDIR/plugins/streamdev/
(Directory VDRCONFDIR/plugins/streamdev already exists, as you copied the (Directory VDRCONFDIR/plugins/streamdev already exists, as you copied the
whole folder from the sources directory as suggested above, right?) whole folder from the sources directory as suggested above, right?)
The new default location for externremux.sh is also in this directory. Now check the contents of streamdevhosts.conf. Does it contain a "0.0.0.0/0"
entry? If your VDR machine is connected to the Internet, this line gives
*anyone* full access to streamdev, unless you took some other measures to
prevent this (e.g. firewall). You might want to remove this line and enable
HTTP authentication instead.
3. Usage: 3. Usage:
@ -205,6 +209,18 @@ externremux script.
http://hostname:3000/EXTERN;some_parameter/3 http://hostname:3000/EXTERN;some_parameter/3
If you want to access streamdev's HTTP server from the Internet, do *not* grant
access for anyone by allowing any IP in "streamdevhosts.conf". Instead, pass the
"-a" commandline option to streamdev-server. It takes a username and a password
as argument. Clients with an IP not accepted by "streamdevhosts.conf" will then
have to login. The VDR commandline will have to look like this:
vdr ... -P 'streamdev-server -a vdr:secret' ...
Note the single quotes, as otherwise "-a" will be passed to VDR and not to
streamdev-server. The login ("vdr" in the example above) doesn't have to exist
as a system account.
3.2 Usage VDR-to-VDR server: 3.2 Usage VDR-to-VDR server:
---------------------------- ----------------------------

View File

@ -1,5 +1,5 @@
/* /*
* $Id: connection.h,v 1.5 2007/04/16 11:01:02 schmirl Exp $ * $Id: connection.h,v 1.6 2008/10/14 11:05:47 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SERVER_CONNECTION_H #ifndef VDR_STREAMDEV_SERVER_CONNECTION_H
@ -47,6 +47,9 @@ public:
cServerConnection(const char *Protocol); cServerConnection(const char *Protocol);
virtual ~cServerConnection(); virtual ~cServerConnection();
/* If true, any client IP will be accepted */
virtual bool CanAuthenticate(void) { return false; }
/* Gets called if the client has been accepted by the core */ /* Gets called if the client has been accepted by the core */
virtual void Welcome(void) { } virtual void Welcome(void) { }

View File

@ -1,11 +1,12 @@
/* /*
* $Id: connectionHTTP.c,v 1.13 2008/03/28 15:11:40 schmirl Exp $ * $Id: connectionHTTP.c,v 1.14 2008/10/14 11:05:47 schmirl Exp $
*/ */
#include <ctype.h> #include <ctype.h>
#include "server/connectionHTTP.h" #include "server/connectionHTTP.h"
#include "server/menuHTTP.h" #include "server/menuHTTP.h"
#include "server/server.h"
#include "server/setup.h" #include "server/setup.h"
cConnectionHTTP::cConnectionHTTP(void): cConnectionHTTP::cConnectionHTTP(void):
@ -26,6 +27,11 @@ cConnectionHTTP::~cConnectionHTTP()
delete m_LiveStreamer; delete m_LiveStreamer;
} }
bool cConnectionHTTP::CanAuthenticate(void)
{
return opt_auth != NULL;
}
bool cConnectionHTTP::Command(char *Cmd) bool cConnectionHTTP::Command(char *Cmd)
{ {
Dprintf("command %s\n", Cmd); Dprintf("command %s\n", Cmd);
@ -44,6 +50,15 @@ bool cConnectionHTTP::Command(char *Cmd)
if (strncasecmp(Cmd, "Host:", 5) == 0) { if (strncasecmp(Cmd, "Host:", 5) == 0) {
Dprintf("Host-Header\n"); Dprintf("Host-Header\n");
m_Host = (std::string) skipspace(Cmd + 5); m_Host = (std::string) skipspace(Cmd + 5);
return true;
}
else if (strncasecmp(Cmd, "Authorization:", 14) == 0) {
Cmd = skipspace(Cmd + 14);
if (strncasecmp(Cmd, "Basic", 5) == 0) {
Dprintf("'Authorization Basic'-Header\n");
m_Authorization = (std::string) skipspace(Cmd + 5);
return true;
}
} }
Dprintf("header\n"); Dprintf("header\n");
return true; return true;
@ -56,6 +71,16 @@ bool cConnectionHTTP::Command(char *Cmd)
bool cConnectionHTTP::ProcessRequest(void) bool cConnectionHTTP::ProcessRequest(void)
{ {
Dprintf("process\n"); Dprintf("process\n");
if (!StreamdevHosts.Acceptable(RemoteIpAddr()))
{
if (!opt_auth || m_Authorization.empty() || m_Authorization.compare(opt_auth) != 0) {
isyslog("streamdev-server: HTTP authorization required");
DeferClose();
return Respond("HTTP/1.0 401 Authorization Required")
&& Respond("WWW-authenticate: basic Realm=\"Streamdev-Server\")")
&& Respond("");
}
}
if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) { if (m_Request.substr(0, 4) == "GET " && CmdGET(m_Request.substr(4))) {
switch (m_Job) { switch (m_Job) {
case hjListing: case hjListing:

View File

@ -1,5 +1,5 @@
/* /*
* $Id: connectionHTTP.h,v 1.5 2008/03/28 15:11:40 schmirl Exp $ * $Id: connectionHTTP.h,v 1.6 2008/10/14 11:05:48 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H #ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H
@ -30,6 +30,7 @@ private:
std::string m_Request; std::string m_Request;
std::string m_Host; std::string m_Host;
std::string m_Authorization;
//std::map<std::string,std::string> m_Headers; TODO: later? //std::map<std::string,std::string> m_Headers; TODO: later?
eHTTPStatus m_Status; eHTTPStatus m_Status;
eHTTPJob m_Job; eHTTPJob m_Job;
@ -52,6 +53,8 @@ public:
virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); } virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); }
virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); } virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); }
virtual bool CanAuthenticate(void);
virtual bool Command(char *Cmd); virtual bool Command(char *Cmd);
bool CmdGET(const std::string &Opts); bool CmdGET(const std::string &Opts);

View File

@ -1,5 +1,5 @@
/* /*
* $Id: server.c,v 1.6 2008/04/29 07:00:54 schmirl Exp $ * $Id: server.c,v 1.7 2008/10/14 11:05:48 schmirl Exp $
*/ */
#include "server/server.h" #include "server/server.h"
@ -13,6 +13,7 @@
#include <errno.h> #include <errno.h>
cSVDRPhosts StreamdevHosts; cSVDRPhosts StreamdevHosts;
char *opt_auth = NULL;
char *opt_remux = NULL; char *opt_remux = NULL;
cStreamdevServer *cStreamdevServer::m_Instance = NULL; cStreamdevServer *cStreamdevServer::m_Instance = NULL;
@ -122,7 +123,7 @@ void cStreamdevServer::Action(void)
esyslog("streamdev: too many clients, rejecting %s:%d", esyslog("streamdev: too many clients, rejecting %s:%d",
client->RemoteIp().c_str(), client->RemotePort()); client->RemoteIp().c_str(), client->RemotePort());
client->Reject(); client->Reject();
} else if (!StreamdevHosts.Acceptable(client->RemoteIpAddr())) { } else if (!client->CanAuthenticate() && !StreamdevHosts.Acceptable(client->RemoteIpAddr())) {
esyslog("streamdev: client %s:%d not allowed to connect", esyslog("streamdev: client %s:%d not allowed to connect",
client->RemoteIp().c_str(), client->RemotePort()); client->RemoteIp().c_str(), client->RemotePort());
client->Reject(); client->Reject();

View File

@ -1,5 +1,5 @@
/* /*
* $Id: server.h,v 1.4 2008/04/29 07:00:54 schmirl Exp $ * $Id: server.h,v 1.5 2008/10/14 11:05:48 schmirl Exp $
*/ */
#ifndef VDR_STREAMDEV_SERVER_H #ifndef VDR_STREAMDEV_SERVER_H
@ -13,6 +13,7 @@
#define DEFAULT_EXTERNREMUX (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh")) #define DEFAULT_EXTERNREMUX (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh"))
#define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "streamdevhosts.conf")) #define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "streamdevhosts.conf"))
extern char *opt_auth;
extern char *opt_remux; extern char *opt_remux;
class cStreamdevServer: public cThread { class cStreamdevServer: public cThread {

View File

@ -3,10 +3,11 @@
* *
* See the README file for copyright information and how to reach the author. * See the README file for copyright information and how to reach the author.
* *
* $Id: streamdev-server.c,v 1.10 2008/10/13 11:30:05 schmirl Exp $ * $Id: streamdev-server.c,v 1.11 2008/10/14 11:05:47 schmirl Exp $
*/ */
#include <getopt.h> #include <getopt.h>
#include <vdr/tools.h>
#include "remux/extern.h" #include "remux/extern.h"
#include "streamdev-server.h" #include "streamdev-server.h"
#include "server/setup.h" #include "server/setup.h"
@ -25,6 +26,7 @@ cPluginStreamdevServer::cPluginStreamdevServer(void)
cPluginStreamdevServer::~cPluginStreamdevServer() cPluginStreamdevServer::~cPluginStreamdevServer()
{ {
free(opt_auth);
free(opt_remux); free(opt_remux);
} }
@ -36,20 +38,35 @@ const char *cPluginStreamdevServer::Description(void)
const char *cPluginStreamdevServer::CommandLineHelp(void) const char *cPluginStreamdevServer::CommandLineHelp(void)
{ {
// return a string that describes all known command line options. // return a string that describes all known command line options.
return " -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n"; return
" -a <LOGIN:PASSWORD>, --auth=<LOGIN:PASSWORD> Credentials for HTTP authentication.\n"
" -r <CMD>, --remux=<CMD> Define an external command for remuxing.\n"
;
} }
bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[]) bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[])
{ {
// implement command line argument processing here if applicable. // implement command line argument processing here if applicable.
static const struct option long_options[] = { static const struct option long_options[] = {
{ "auth", required_argument, NULL, 'a' },
{ "remux", required_argument, NULL, 'r' }, { "remux", required_argument, NULL, 'r' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
}; };
int c; int c;
while((c = getopt_long(argc, argv, "r:", long_options, NULL)) != -1) { while((c = getopt_long(argc, argv, "a:r:", long_options, NULL)) != -1) {
switch (c) { switch (c) {
case 'a':
{
if (opt_auth)
free(opt_auth);
int l = strlen(optarg);
cBase64Encoder Base64((uchar*) optarg, l, l * 4 / 3 + 3);
const char *s = Base64.NextLine();
if (s)
opt_auth = strdup(s);
}
break;
case 'r': case 'r':
if (opt_remux) if (opt_remux)
free(opt_remux); free(opt_remux);