/** * \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 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 whether this policy file allows the given URL access * * \param url The URL to check if it is allowed by the policy file * \param to The URL that is being requested by a resource at \c url * \return \c true if allowed, otherwise \c false */ bool URLPolicyFile::allowsAccessFrom(const URLInfo& url, const URLInfo& to) { //File must be loaded if(!isLoaded()) return false; //This policy file doesn't apply to the given URL if(!isMaster() && !to.isSubOf(url)) return false; //Check if the file is invalid or ignored if(!isValid() || isIgnored()) return false; list<PolicyAllowAccessFrom*>::const_iterator i = allowAccessFrom.begin(); for(; i != allowAccessFrom.end(); ++i) { //This allow-access-from entry applies to our domain AND it allows our domain if((*i)->allowsAccessFrom(url)) return true; } return false; }
/** * \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; }