From 52b4bfcd8cf144f3fdd7f3b22f3c9ae2e58b7df9 Mon Sep 17 00:00:00 2001 From: schmirl Date: Tue, 14 Oct 2008 11:05:57 +0000 Subject: [PATCH] - added HTTP authentication (#475) Modified Files: Tag: v0_4 HISTORY README streamdev-server.c server/connection.h server/connectionHTTP.c server/connectionHTTP.h server/server.c server/server.h --- HISTORY | 1 + README | 25 ++++++++++++++++++++----- server/connection.h | 5 ++++- server/connectionHTTP.c | 27 ++++++++++++++++++++++++++- server/connectionHTTP.h | 5 ++++- server/server.c | 5 +++-- server/server.h | 3 ++- streamdev-server.c | 23 ++++++++++++++++++++--- 8 files changed, 80 insertions(+), 14 deletions(-) diff --git a/HISTORY b/HISTORY index 681beae..199fc6f 100644 --- a/HISTORY +++ b/HISTORY @@ -1,6 +1,7 @@ VDR Plugin 'streamdev' Revision History --------------------------------------- +- added HTTP authentication - added preprocessor directive for ancient gcc - added Russian translation (thanks to Oleg Roitburd) - fixed assignment of externremux.sh's default location (reported by plautze) diff --git a/README b/README index 39a0312..02d26c4 100644 --- a/README +++ b/README @@ -117,17 +117,20 @@ make [options, if necessary] plugins ---------------------------------- 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 -particular the file "streamdevhosts.conf". You will have to move it to its -new location: +called "streamdev" inside VDR's plugin config directory. It is the new default +location of externremux.sh and the new place where streamdev-server expects the +file "streamdevhosts.conf". You will have to move this file to its new location: mv VDRCONFDIR/plugins/streamdevhosts.conf VDRCONFDIR/plugins/streamdev/ (Directory VDRCONFDIR/plugins/streamdev already exists, as you copied the 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: --------- @@ -204,6 +207,18 @@ externremux script. 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: ---------------------------- diff --git a/server/connection.h b/server/connection.h index fe828d9..0ca7153 100644 --- a/server/connection.h +++ b/server/connection.h @@ -1,5 +1,5 @@ /* - * $Id: connection.h,v 1.5 2007/04/16 11:01:02 schmirl Exp $ + * $Id: connection.h,v 1.5.2.1 2008/10/14 11:05:59 schmirl Exp $ */ #ifndef VDR_STREAMDEV_SERVER_CONNECTION_H @@ -47,6 +47,9 @@ public: cServerConnection(const char *Protocol); 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 */ virtual void Welcome(void) { } diff --git a/server/connectionHTTP.c b/server/connectionHTTP.c index 38a82a3..3263c52 100644 --- a/server/connectionHTTP.c +++ b/server/connectionHTTP.c @@ -1,11 +1,12 @@ /* - * $Id: connectionHTTP.c,v 1.13 2008/03/28 15:11:40 schmirl Exp $ + * $Id: connectionHTTP.c,v 1.13.2.1 2008/10/14 11:05:59 schmirl Exp $ */ #include #include "server/connectionHTTP.h" #include "server/menuHTTP.h" +#include "server/server.h" #include "server/setup.h" cConnectionHTTP::cConnectionHTTP(void): @@ -26,6 +27,11 @@ cConnectionHTTP::~cConnectionHTTP() delete m_LiveStreamer; } +bool cConnectionHTTP::CanAuthenticate(void) +{ + return opt_auth != NULL; +} + bool cConnectionHTTP::Command(char *Cmd) { Dprintf("command %s\n", Cmd); @@ -44,6 +50,15 @@ bool cConnectionHTTP::Command(char *Cmd) if (strncasecmp(Cmd, "Host:", 5) == 0) { Dprintf("Host-Header\n"); 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"); return true; @@ -56,6 +71,16 @@ bool cConnectionHTTP::Command(char *Cmd) bool cConnectionHTTP::ProcessRequest(void) { 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))) { switch (m_Job) { case hjListing: diff --git a/server/connectionHTTP.h b/server/connectionHTTP.h index a3558ad..d7aa22b 100644 --- a/server/connectionHTTP.h +++ b/server/connectionHTTP.h @@ -1,5 +1,5 @@ /* - * $Id: connectionHTTP.h,v 1.5 2008/03/28 15:11:40 schmirl Exp $ + * $Id: connectionHTTP.h,v 1.5.2.1 2008/10/14 11:05:59 schmirl Exp $ */ #ifndef VDR_STREAMDEV_SERVERS_CONNECTIONHTTP_H @@ -30,6 +30,7 @@ private: std::string m_Request; std::string m_Host; + std::string m_Authorization; //std::map m_Headers; TODO: later? eHTTPStatus m_Status; eHTTPJob m_Job; @@ -52,6 +53,8 @@ public: virtual void Attach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Attach(); } virtual void Detach(void) { if (m_LiveStreamer != NULL) m_LiveStreamer->Detach(); } + virtual bool CanAuthenticate(void); + virtual bool Command(char *Cmd); bool CmdGET(const std::string &Opts); diff --git a/server/server.c b/server/server.c index 3245136..f467c33 100644 --- a/server/server.c +++ b/server/server.c @@ -1,5 +1,5 @@ /* - * $Id: server.c,v 1.5.2.1 2008/04/29 07:01:00 schmirl Exp $ + * $Id: server.c,v 1.5.2.2 2008/10/14 11:05:59 schmirl Exp $ */ #include "server/server.h" @@ -13,6 +13,7 @@ #include cSVDRPhosts StreamdevHosts; +char *opt_auth = NULL; char *opt_remux = NULL; cStreamdevServer *cStreamdevServer::m_Instance = NULL; @@ -122,7 +123,7 @@ void cStreamdevServer::Action(void) esyslog("streamdev: too many clients, rejecting %s:%d", client->RemoteIp().c_str(), client->RemotePort()); 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", client->RemoteIp().c_str(), client->RemotePort()); client->Reject(); diff --git a/server/server.h b/server/server.h index cfbc92c..0d83a48 100644 --- a/server/server.h +++ b/server/server.h @@ -1,5 +1,5 @@ /* - * $Id: server.h,v 1.3.2.1 2008/04/29 07:01:00 schmirl Exp $ + * $Id: server.h,v 1.3.2.2 2008/10/14 11:05:59 schmirl Exp $ */ #ifndef VDR_STREAMDEV_SERVER_H @@ -13,6 +13,7 @@ #define DEFAULT_EXTERNREMUX (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "externremux.sh")) #define STREAMDEVHOSTSPATH (*AddDirectory(cPlugin::ConfigDirectory(PLUGIN_NAME_I18N), "streamdevhosts.conf")) +extern char *opt_auth; extern char *opt_remux; class cStreamdevServer: public cThread { diff --git a/streamdev-server.c b/streamdev-server.c index f2c933a..b7e136c 100644 --- a/streamdev-server.c +++ b/streamdev-server.c @@ -3,10 +3,11 @@ * * See the README file for copyright information and how to reach the author. * - * $Id: streamdev-server.c,v 1.7.2.1 2008/04/29 07:00:57 schmirl Exp $ + * $Id: streamdev-server.c,v 1.7.2.2 2008/10/14 11:05:57 schmirl Exp $ */ #include +#include #include "streamdev-server.h" #include "server/setup.h" #include "server/server.h" @@ -26,6 +27,7 @@ cPluginStreamdevServer::cPluginStreamdevServer(void) cPluginStreamdevServer::~cPluginStreamdevServer() { + free(opt_auth); free(opt_remux); } @@ -37,20 +39,35 @@ const char *cPluginStreamdevServer::Description(void) const char *cPluginStreamdevServer::CommandLineHelp(void) { // return a string that describes all known command line options. - return " -r , --remux= Define an external command for remuxing.\n"; + return + " -a , --auth= Credentials for HTTP authentication.\n" + " -r , --remux= Define an external command for remuxing.\n" + ; } bool cPluginStreamdevServer::ProcessArgs(int argc, char *argv[]) { // implement command line argument processing here if applicable. static const struct option long_options[] = { + { "auth", required_argument, NULL, 'a' }, { "remux", required_argument, NULL, 'r' }, { NULL, 0, NULL, 0 } }; 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) { + 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': if (opt_remux) free(opt_remux);