/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- accessPolicyString
- host_check_blackwhite_lists
- pathIsUnderDir
- local_check
- host_check
- allowHost
- allowXMLSocket
- allow
//
// Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
// 2011 Free Software Foundation, Inc
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
#include "URLAccessManager.h"
#include "URL.h"
#include "log.h"
#include "StringPredicates.h"
#include "rc.h" // for rcfile
#include "GnashSystemNetHeaders.h"
#include <cerrno>
#include <algorithm> // for find / find_if
#include <cstring> // for strerror
#include <cstdio>
#include <map>
#include <string>
#include <vector>
#include <cassert>
namespace gnash {
namespace URLAccessManager {
/// Possible access policies for URLs
enum AccessPolicy {
/// Forbid access
BLOCK,
/// Allow access
GRANT
};
const char*
accessPolicyString(AccessPolicy policy)
{
switch(policy)
{
case BLOCK:
return "BLOCKED";
case GRANT:
return "GRANTED";
default:
return "UNKNOWN";
}
}
// The default AccessPolicy when prompting user is not possible
// (this happens when input is not a tty, at the moment)
//static AccessPolicy defaultAccessPolicy = GRANT;
/// A cache of AccessPolicy defined for URLs
typedef std::map<std::string, AccessPolicy> AccessPolicyCache;
/// A global AccessPolicyCache
static AccessPolicyCache policyCache;
// check host against black/white lists
// return true if we allow load from host, false otherwise
// it is assumed localhost/localdomain was already checked
static bool
host_check_blackwhite_lists(const std::string& host)
{
using std::vector;
using std::string;
RcInitFile& rcfile = RcInitFile::getDefaultInstance();
const std::vector<std::string>& whitelist = rcfile.getWhiteList();
if (!whitelist.empty()) {
// TODO: case-insensitive matching ?
std::vector<std::string>::const_iterator it =
std::find(whitelist.begin(), whitelist.end(), host);
if (it != whitelist.end()) {
log_security(_("Load from host %s granted (whitelisted)"),
host);
return true;
}
// if there is a whitelist, anything NOT listed is denied
log_security(_("Load from host %s forbidden "
"(not in non-empty whitelist)"),
host);
return false;
}
const std::vector<std::string>& blacklist = rcfile.getBlackList();
// TODO: case-insensitive matching ?
std::vector<std::string>::const_iterator it =
std::find(blacklist.begin(), blacklist.end(), host);
if (it != blacklist.end()) {
log_security(_("Load from host %s forbidden (blacklisted)"),
host);
return false;
}
log_security(_("Load from host %s granted (default)"), host);
return true;
}
static bool
pathIsUnderDir(const std::string& path, const std::string& dir)
{
size_t dirLen = dir.length();
if ( dirLen > path.length() ) return false; // can't contain it, right ?
// Path must be equal to dir for the whole dir length
//
// TODO: this is pretty lame, can do better with some normalization
// we'd need a generic splitPathInComponents.. maybe as a static
// public method of gnash::URL ?
//
if ( path.compare(0, dirLen, dir) ) return false;
return true;
}
/// Return true if we allow load of the local resource, false otherwise.
//
static bool
local_check(const std::string& path, const URL& baseUrl)
{
// GNASH_REPORT_FUNCTION;
assert( ! path.empty() );
// Don't allow local access if starting movie is a network resource.
if (baseUrl.protocol() != "file") {
log_security(_("Load of file %s forbidden"
" (starting url %s is not a local resource)"),
path, baseUrl.str());
return false;
}
RcInitFile& rcfile = RcInitFile::getDefaultInstance();
typedef RcInitFile::PathList PathList;
const PathList& sandbox = rcfile.getLocalSandboxPath();
for (PathList::const_iterator i=sandbox.begin(), e=sandbox.end();
i!=e; ++i)
{
const std::string& dir = *i;
if ( pathIsUnderDir(path, dir) )
{
log_security(_("Load of file %s granted (under local sandbox %s)"),
path, dir);
return true;
}
}
// TODO: dump local sandboxes here ? (or maybe send the info to the GUI properties
// view
log_security(_("Load of file %s forbidden (not under local sandboxes)"),
path);
return false;
}
/// Return true if we allow load from host, false otherwise.
//
/// This function will check for localhost/localdomain (if requested)
/// and finally call host_check_blackwhitelists
///
static bool
host_check(const std::string& host)
{
// GNASH_REPORT_FUNCTION;
//log_security("Checking security of host: %s", host.c_str());
assert( ! host.empty() );
RcInitFile& rcfile = RcInitFile::getDefaultInstance();
bool check_domain = rcfile.useLocalDomain();
bool check_localhost = rcfile.useLocalHost();
// Don't bother gettin hostname if we're not going to need it
if ( ! ( check_domain || check_localhost ) )
{
return host_check_blackwhite_lists(host);
}
//
// Get hostname
//
#define MAXHOSTNAMELEN 200
char name[MAXHOSTNAMELEN];
if (::gethostname(name, MAXHOSTNAMELEN) == -1)
{
// FIXME: strerror is NOT thread-safe
log_error(_("gethostname failed: %s"), std::strerror(errno));
return host_check_blackwhite_lists(host);
}
// From GETHOSTNAME(2):
// In case the NUL-terminated hostname does not fit,
// no error is returned, but the hostname is truncated. It is unspecified
// whether the truncated hostname will be NUL-terminated.
name[MAXHOSTNAMELEN - 1] = '\0'; // unlikely, still worth making sure...
// ok, let's use std::strings... we're a C++ program after all !
std::string hostname(name); // the hostname
std::string domainname; // the domainname
// Split hostname/domainname or take it all as an hostname if
// no dot is found
std::string::size_type dotloc = hostname.find('.', 0);
if ( dotloc != std::string::npos ) {
domainname = hostname.substr(dotloc+1);
hostname.erase(dotloc);
}
if ( check_domain && domainname != host ) {
log_security(_("Load from host %s forbidden (not in the local domain)"),
host);
return false;
}
if ( check_localhost && hostname != host ) {
log_security(_("Load from host %s forbidden (not on the local host)"),
host);
return false;
}
return host_check_blackwhite_lists(host);
}
bool
allowHost(const std::string& host)
{
if (host.size() == 0) {
return true;
}
return host_check(host);
}
bool
allowXMLSocket(const std::string& host, short port)
{
if (port < 1024) {
log_security("Attempt to connect to disallowed port %s", port);
return false;
}
return allowHost(host);
}
bool
allow(const URL& url, const URL& baseurl)
{
log_security(_("Checking security of URL '%s'"), url);
// We might reintroduce use of an AccessPolicy cache
std::string host = url.hostname();
// Local resources can be accessed only if they are
// in a directory listed as local sandbox
if (host.size() == 0)
{
if (url.protocol() != "file")
{
log_error(_("Network connection without hostname requested"));
return false;
}
return local_check(url.path(), baseurl);
}
return host_check(host);
}
} // AccessManager
} // namespace gnash