/** * \brief Checks if the current sandbox is allowed access to the given URL. * * \param url The URL to evaluate the sandbox against * \param allowedSandboxesRemote The sandboxes that are allowed to access remote URLs in this case. * Can be a bitwise expression of sandboxes. * \param allowedSandboxesLocal The sandboxes that are allowed to access local URLs in this case. * Can be a bitwise expression of sandboxes. * \return \c ALLOWED if allowed or otherwise \c NA_REMOTE_SANDBOX/NA_LOCAL_SANDBOX depending on the reason. */ SecurityManager::EVALUATIONRESULT SecurityManager::evaluateSandboxURL(const URLInfo& url, int allowedSandboxesRemote, int allowedSandboxesLocal) { //The URL is remote and the current sandbox doesn't match the allowed sandboxes for remote URLs if(url.getProtocol() != "file" && (~allowedSandboxesRemote) & sandboxType) return NA_REMOTE_SANDBOX; //The URL is local and the current sandbox doesn't match the allowed sandboxes for local URLs if(url.getProtocol() == "file" && (~allowedSandboxesLocal) & sandboxType) return NA_LOCAL_SANDBOX; return ALLOWED; }
/** * \brief Checks URL policy files to see if the player is allowed access to the given UR * * Waits for mutex at start and releases mutex when finished * \param url The URL to evaluate * \param loadPendingPolicies Whether to load (and thus check) pending policies before evaluating * \return \c ALLOWED if allowed or otherwise \c NA_CROSSDOMAIN_POLICY */ SecurityManager::EVALUATIONRESULT SecurityManager::evaluatePoliciesURL(const URLInfo& url, bool loadPendingPolicies) { //This check doesn't apply to local files if(url.getProtocol() == "file" && sys->getOrigin().getProtocol() == "file") return ALLOWED; LOG(LOG_NO_INFO, _("SECURITY: Evaluating URL for cross domain policies:")); LOG(LOG_NO_INFO, _("SECURITY: --> URL: ") << url); LOG(LOG_NO_INFO, _("SECURITY: --> Origin: ") << sys->getOrigin()); //The URL has exactly the same domain name as the origin, always allowed if(url.getProtocol() == sys->getOrigin().getProtocol() && url.getHostname() == sys->getOrigin().getHostname()) { LOG(LOG_NO_INFO, _("SECURITY: Same hostname as origin, allowing")); return ALLOWED; } //Search for the policy files to check URLPFileList* files = searchURLPolicyFiles(url, loadPendingPolicies); sem_wait(&mutex); //-- Lock acquired //Check the policy files if(files != NULL) { URLPFileListConstIt it = files->begin(); for(; it != files->end(); ++it) { if((*it)->allowsAccessFrom(sys->getOrigin(), url)) { LOG(LOG_NO_INFO, _("SECURITY: ALLOWED: A policy file explicitly allowed access")); delete files; //++ Release lock sem_post(&mutex); return ALLOWED; } } } LOG(LOG_NO_INFO, _("SECURITY: DISALLOWED: No policy file explicitly allowed access")); delete files; //++ Release lock sem_post(&mutex); return NA_CROSSDOMAIN_POLICY; }
/** * \brief Checks URL policy files to see if the player is allowed access to the given UR * * Waits for mutex at start and releases mutex when finished * \param url The URL to evaluate * \param loadPendingPolicies Whether to load (and thus check) pending policies before evaluating * \return \c ALLOWED if allowed or otherwise \c NA_CROSSDOMAIN_POLICY */ SecurityManager::EVALUATIONRESULT SecurityManager::evaluatePoliciesURL(const URLInfo& url, bool loadPendingPolicies) { //This check doesn't apply to local files if(url.getProtocol() == "file" && getSys()->getOrigin().getProtocol() == "file") return ALLOWED; //Streaming from RTMP is always allowed (see //http://forums.adobe.com/thread/422391) if(url.isRTMP()) return ALLOWED; LOG(LOG_INFO, _("SECURITY: Evaluating URL for cross domain policies:")); LOG(LOG_INFO, _("SECURITY: --> URL: ") << url); LOG(LOG_INFO, _("SECURITY: --> Origin: ") << getSys()->getOrigin()); //The URL has exactly the same domain name as the origin, always allowed if(url.getProtocol() == getSys()->getOrigin().getProtocol() && url.getHostname() == getSys()->getOrigin().getHostname()) { LOG(LOG_INFO, _("SECURITY: Same hostname as origin, allowing")); return ALLOWED; } //Search for the policy files to check URLPFileList* files = searchURLPolicyFiles(url, loadPendingPolicies); RecMutex::Lock l(mutex); //Check the policy files if(files != NULL) { URLPFileListConstIt it = files->begin(); for(; it != files->end(); ++it) { if((*it)->allowsAccessFrom(getSys()->getOrigin(), url)) { LOG(LOG_INFO, _("SECURITY: ALLOWED: A policy file explicitly allowed access")); delete files; return ALLOWED; } } } LOG(LOG_INFO, _("SECURITY: DISALLOWED: No policy file explicitly allowed access")); delete files; return NA_CROSSDOMAIN_POLICY; }
/** * \brief Checks if the URL doesn't point to a resource higher up the directory hierarchy than * the current directory * * \param url The URL to evaluate * \return \c ALLOWED if allowed or otherwise \c NA_RESTRICT_LOCAL_DIRECTORY */ SecurityManager::EVALUATIONRESULT SecurityManager::evaluateLocalDirectoryURL(const URLInfo& url) { //The URL is local and points to a directory above the origin if(url.getProtocol() == "file" && !url.isSubOf(getSys()->getOrigin())) return NA_RESTRICT_LOCAL_DIRECTORY; return ALLOWED; }
/** * \brief Checks if the port in the given URL isn't restricted, depending on the protocol of the URL * * \param url The URL to evaluate * \return \c ALLOWED if allowed or otherwise \c NA_PORT */ SecurityManager::EVALUATIONRESULT SecurityManager::evaluatePortURL(const URLInfo& url) { if(url.getProtocol() == "http" || url.getProtocol() == "https") if(url.getPort() == 20 || url.getPort() == 21) return NA_PORT; if(url.getProtocol() == "http" || url.getProtocol() == "https" || url.getProtocol() == "ftp") { switch(url.getPort()) { case 1: case 7: case 9: case 11: case 13: case 15: case 17: case 19: case 22: case 23: case 25: case 37: case 42: case 43: case 53: case 77: case 79: case 87: case 95: case 101: case 102: case 103: case 104: case 109: case 110: case 111: case 113: case 115: case 117: case 119: case 123: case 135: case 139: case 143: case 179: case 389: case 465: case 512: case 513: case 514: case 515: case 526: case 530: case 531: case 532: case 540: case 556: case 563: case 587: case 601: case 636: case 993: case 995: case 2049: case 4045: case 6000: return NA_PORT; } } return ALLOWED; }
/** * \brief Checks if the entry allows access from the given URL * * \param url The URL to check this entry against * \return \c true if this entry allows the given URL access, otherwise \c false */ bool PolicyAllowAccessFrom::allowsAccessFrom(const URLInfo& url) const { //TODO: resolve domain names using DNS before checking for a match? //See section 1.5.9 in specification //TODO: add check for to-ports and secure for SOCKET type policy files //Check if domains match if(!URLInfo::matchesDomain(domain, url.getHostname())) return false; //Check if the requesting URL is secure, if needed if(file->getType() == PolicyFile::URL && dynamic_cast<URLPolicyFile*>(file)->getSubtype() == URLPolicyFile::HTTPS && secure && url.getProtocol() != "https") return false; return true; }
/** * \brief Checks if the entry allows a given request header from a given URL * * \param url The URL to check this entry against * \param header The header to check this entry against * \return \c true if this entry allows the given URL to send the given request header, otherwise \c false */ bool PolicyAllowHTTPRequestHeadersFrom::allowsHTTPRequestHeaderFrom(const URLInfo& url, const string& header) const { if(file->getSubtype() != URLPolicyFile::HTTP && file->getSubtype() != URLPolicyFile::HTTPS) return false; //Check if domains match if(!URLInfo::matchesDomain(domain, url.getHostname())) return false; //Check if the requesting URL is secure, if needed if(file->getSubtype() == URLPolicyFile::HTTPS && secure && url.getProtocol() != "https") return false; //Check if the header is explicitly allowed bool headerFound = false; string expression; for(list<string*>::const_iterator i = headers.begin(); i != headers.end(); ++i) { expression = (**i); transform(expression.begin(), expression.end(), expression.begin(), ::tolower); if(expression == header || expression == "*") { headerFound = true; break; } //Match suffix wildcards else if(expression[expression.length()-1] == '*' && header.substr(0, expression.length()-1) == expression.substr(0, expression.length()-1)) { headerFound = true; break; } } if(!headerFound) return false; return true; }
/** * \brief Checks URL policy files to see if the player is allowed to send a given request header * as part of a request for the given URL * * Waits for mutex at start and releases mutex when finished * \param url The URL of the request to which the request header belongs * \param header The request header to evaluate * \param loadPendingPolicies Whether or not to load (and thus check) pending policy files * \return \c ALLOWED if allowed or otherwise \c NA_HEADER */ SecurityManager::EVALUATIONRESULT SecurityManager::evaluateHeader(const URLInfo& url, const tiny_string& header, bool loadPendingPolicies) { //This check doesn't apply to local files if(url.getProtocol() == "file" && getSys()->getOrigin().getProtocol() == "file") return ALLOWED; LOG(LOG_INFO, _("SECURITY: Evaluating header for cross domain policies ('") << header << "'):"); LOG(LOG_INFO, _("SECURITY: --> URL: ") << url); LOG(LOG_INFO, _("SECURITY: --> Origin: ") << getSys()->getOrigin()); string headerStrLower(header.raw_buf()); transform(headerStrLower.begin(), headerStrLower.end(), headerStrLower.begin(), ::tolower); string headerStr = headerStrLower; if(headerStr.find("_") != string::npos) headerStr.replace(headerStr.find("_"), 1, "-"); //Disallowed headers, in any case if(headerStr == "accept-charset" && headerStr == "accept-encoding" && headerStr == "accept-ranges" && headerStr == "age" && headerStr == "allow" && headerStr == "allowed" && headerStr == "authorization" && headerStr == "charge-to" && headerStr == "connect" && headerStr == "connection" && headerStr == "content-length" && headerStr == "content-location" && headerStr == "content-range" && headerStr == "cookie" && headerStr == "date" && headerStr == "delete" && headerStr == "etag" && headerStr == "expect" && headerStr == "get" && headerStr == "head" && headerStr == "host" && headerStr == "if-modified-since" && headerStr == "keep-alive" && headerStr == "last-modified" && headerStr == "location" && headerStr == "max-forwards" && headerStr == "options" && headerStr == "origin" && headerStr == "post" && headerStr == "proxy-authenticate" && headerStr == "proxy-authorization" && headerStr == "proxy-connection" && headerStr == "public" && headerStr == "put" && headerStr == "range" && headerStr == "referer" && headerStr == "request-range" && headerStr == "retry-after" && headerStr == "server" && headerStr == "te" && headerStr == "trace" && headerStr == "trailer" && headerStr == "transfer-encoding" && headerStr == "upgrade" && headerStr == "uri" && headerStr == "user-agent" && headerStr == "vary" && headerStr == "via" && headerStr == "warning" && headerStr == "www-authenticate" && headerStr == "x-flash-version") { LOG(LOG_INFO, _("SECURITY: DISALLOWED: Header is restricted")); return NA_HEADER; } //The URL has exactly the same domain name as the origin, always allowed if(url.getProtocol() == getSys()->getOrigin().getProtocol() && url.getHostname() == getSys()->getOrigin().getHostname()) { LOG(LOG_INFO, _("SECURITY: ALLOWED: Same hostname as origin")); return ALLOWED; } //Search for the policy files to check URLPFileList* files = searchURLPolicyFiles(url, loadPendingPolicies); RecMutex::Lock l(mutex); //Check the policy files if(files != NULL) { URLPFileListConstIt it = files->begin(); for(; it != files->end(); ++it) { if((*it)->allowsHTTPRequestHeaderFrom(getSys()->getOrigin(), url, headerStrLower)) { LOG(LOG_INFO, _("SECURITY: ALLOWED: A policy file explicitly allowed the header")); delete files; return ALLOWED; } } } LOG(LOG_INFO, _("SECURITY: DISALLOWED: No policy file explicitly allowed the header")); delete files; return NA_CROSSDOMAIN_POLICY; }