void WebServer::onClientMessage(TcpClient* client, char* buffer, size_t size) { clearExpiredSessions(); HTTPResponse* resp = 0; HTTPRequest* req = new HTTPRequest(buffer,size); HTTPAuth auth = req->getAuthorization(); bool authOK = validateAuthentication(req,auth); if (authOK) { if (mpListener!=0) { resp = mpListener->processHTTPRequest(req); if (resp!=0 && auth.nonce!="" && mSessions.find(auth.nonce)!=mSessions.end()) { std::string cookie = "session="+auth.nonce; resp->setCookie(cookie); } sendResponse(resp,client); } else { resp = HTTPProtocol::buildBufferedResponse(ServiceUnavailable,"",""); sendResponse(resp,client); } } else { std::string nonce = ""; WebSession ws; if (auth.nonce!="" && mSessions.find(auth.nonce) != mSessions.end()) { nonce = auth.nonce; ws = mSessions[nonce]; } else { nonce = generateNonce(); ws.mNonce = nonce; } time_t t; time(&t); t+=mSessionExpires; ws.mExpires = t; mSessions[nonce] = ws; std::string content = "{\"status\" : \"Authorization needed\"}"; std::string authHeader = "Digest realm=\"dumais\",nonce=\""+nonce+"\""; HTTPResponseCode code = Unauthorized; resp = HTTPProtocol::buildBufferedResponse(code,content,"application/json", authHeader); if (req->wantsAuthenticationHack()) resp->useAuthenticationHack(); sendResponse(resp,client); } delete buffer; }
DDF AbstractHandler::recoverPostData( const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* relayState ) const { // First we need the post recovery cookie. pair<string,const char*> shib_cookie = getPostCookieNameProps(application, relayState); const char* cookie = request.getCookie(shib_cookie.first.c_str()); if (!cookie || !*cookie) return DDF(); // Clear the cookie. string exp(shib_cookie.second); exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT"; response.setCookie(shib_cookie.first.c_str(), exp.c_str()); // Look for StorageService-backed state of the form "ss:SSID:key". const char* state = cookie; if (strstr(state, "ss:") == state) { state += 3; const char* key = strchr(state, ':'); if (key) { string ssid = string(cookie).substr(3, key - state); key++; if (!ssid.empty() && *key) { SPConfig& conf = SPConfig::getConfig(); if (conf.isEnabled(SPConfig::OutOfProcess)) { #ifndef SHIBSP_LITE StorageService* storage = conf.getServiceProvider()->getStorageService(ssid.c_str()); if (storage) { if (storage->readString("PostData", key, &ssid) > 0) { storage->deleteString("PostData", key); istringstream inret(ssid); DDF ret; inret >> ret; return ret; } else { m_log.error("failed to recover form post data using key (%s)", key); } }
void AbstractHandler::preservePostData( const Application& application, const HTTPRequest& request, HTTPResponse& response, const char* relayState ) const { #ifdef HAVE_STRCASECMP if (strcasecmp(request.getMethod(), "POST")) return; #else if (stricmp(request.getMethod(), "POST")) return; #endif // No specs mean no save. const PropertySet* props=application.getPropertySet("Sessions"); pair<bool,const char*> mech = props->getString("postData"); if (!mech.first) { m_log.info("postData property not supplied, form data will not be preserved across SSO"); return; } DDF postData = getPostData(application, request); if (postData.isnull()) return; if (strstr(mech.second,"ss:") == mech.second) { mech.second+=3; if (!*mech.second) { postData.destroy(); throw ConfigurationException("Unsupported postData mechanism ($1).", params(1, mech.second - 3)); } string postkey; if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { DDFJanitor postjan(postData); #ifndef SHIBSP_LITE StorageService* storage = application.getServiceProvider().getStorageService(mech.second); if (storage) { // Use a random key string rsKey; SAMLConfig::getConfig().generateRandomBytes(rsKey,20); rsKey = SAMLArtifact::toHex(rsKey); ostringstream out; out << postData; if (!storage->createString("PostData", rsKey.c_str(), out.str().c_str(), time(NULL) + 600)) throw IOException("Attempted to insert duplicate storage key."); postkey = string(mech.second-3) + ':' + rsKey; } else { m_log.error("storage-backed PostData mechanism with invalid StorageService ID (%s)", mech.second); } #endif } else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) { DDF out,in = DDF("set::PostData").structure(); DDFJanitor jin(in),jout(out); in.addmember("id").string(mech.second); in.add(postData); out = application.getServiceProvider().getListenerService()->send(in); if (!out.isstring()) throw IOException("StorageService-backed PostData mechanism did not return a state key."); postkey = string(mech.second-3) + ':' + out.string(); } // Set a cookie with key info. pair<string,const char*> shib_cookie = getPostCookieNameProps(application, relayState); postkey += shib_cookie.second; response.setCookie(shib_cookie.first.c_str(), postkey.c_str()); } else { postData.destroy(); throw ConfigurationException("Unsupported postData mechanism ($1).", params(1,mech.second)); } }
void AbstractHandler::recoverRelayState( const Application& application, const HTTPRequest& request, HTTPResponse& response, string& relayState, bool clear ) const { SPConfig& conf = SPConfig::getConfig(); // Look for StorageService-backed state of the form "ss:SSID:key". const char* state = relayState.c_str(); if (strstr(state,"ss:")==state) { state += 3; const char* key = strchr(state,':'); if (key) { string ssid = relayState.substr(3, key - state); key++; if (!ssid.empty() && *key) { if (conf.isEnabled(SPConfig::OutOfProcess)) { #ifndef SHIBSP_LITE StorageService* storage = conf.getServiceProvider()->getStorageService(ssid.c_str()); if (storage) { ssid = key; if (storage->readString("RelayState",ssid.c_str(),&relayState)>0) { if (clear) storage->deleteString("RelayState",ssid.c_str()); return; } else relayState.erase(); } else { m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", ssid.c_str()); relayState.erase(); } #endif } else if (conf.isEnabled(SPConfig::InProcess)) { DDF out,in = DDF("get::RelayState").structure(); in.addmember("id").string(ssid.c_str()); in.addmember("key").string(key); in.addmember("clear").integer(clear ? 1 : 0); DDFJanitor jin(in),jout(out); out = application.getServiceProvider().getListenerService()->send(in); if (!out.isstring()) { m_log.error("StorageService-backed RelayState mechanism did not return a state value."); relayState.erase(); } else { relayState = out.string(); return; } } } } } // Look for cookie-backed state of the form "cookie:key". if (strstr(state,"cookie:")==state) { state += 7; if (*state) { // Pull the value from the "relay state" cookie. pair<string,const char*> relay_cookie = application.getCookieNameProps("_shibstate_"); relay_cookie.first = string("_shibstate_") + state; state = request.getCookie(relay_cookie.first.c_str()); if (state && *state) { // URL-decode the value. char* rscopy=strdup(state); XMLToolingConfig::getConfig().getURLEncoder()->decode(rscopy); relayState = rscopy; free(rscopy); if (clear) { string exp(relay_cookie.second); exp += "; expires=Mon, 01 Jan 2001 00:00:00 GMT"; response.setCookie(relay_cookie.first.c_str(), exp.c_str()); } return; } } relayState.erase(); } // Check for "default" value (or the old "cookie" value that might come from stale bookmarks). if (relayState.empty() || relayState == "default" || relayState == "cookie") { pair<bool,const char*> homeURL=application.getString("homeURL"); if (homeURL.first) relayState=homeURL.second; else { // Compute a URL to the root of the site. int port = request.getPort(); const char* scheme = request.getScheme(); relayState = string(scheme) + "://" + request.getHostname(); if ((!strcmp(scheme,"http") && port!=80) || (!strcmp(scheme,"https") && port!=443)) { ostringstream portstr; portstr << port; relayState += ":" + portstr.str(); } relayState += '/'; } } }
void AbstractHandler::preserveRelayState(const Application& application, HTTPResponse& response, string& relayState) const { if (relayState.empty()) return; // No setting means just pass it by value. pair<bool,const char*> mech=getString("relayState"); if (!mech.first || !mech.second || !*mech.second) return; if (!strcmp(mech.second, "cookie")) { // Here we store the state in a cookie and send a fixed // value so we can recognize it on the way back. if (relayState.find("cookie:") != 0) { const URLEncoder* urlenc = XMLToolingConfig::getConfig().getURLEncoder(); pair<string,const char*> shib_cookie=application.getCookieNameProps("_shibstate_"); string stateval = urlenc->encode(relayState.c_str()) + shib_cookie.second; // Generate a random key for the cookie name instead of the fixed name. string rsKey; generateRandomHex(rsKey,5); shib_cookie.first = "_shibstate_" + rsKey; response.setCookie(shib_cookie.first.c_str(),stateval.c_str()); relayState = "cookie:" + rsKey; } } else if (strstr(mech.second,"ss:")==mech.second) { if (relayState.find("ss:") != 0) { mech.second+=3; if (*mech.second) { if (SPConfig::getConfig().isEnabled(SPConfig::OutOfProcess)) { #ifndef SHIBSP_LITE StorageService* storage = application.getServiceProvider().getStorageService(mech.second); if (storage) { string rsKey; generateRandomHex(rsKey,5); if (!storage->createString("RelayState", rsKey.c_str(), relayState.c_str(), time(NULL) + 600)) throw IOException("Attempted to insert duplicate storage key."); relayState = string(mech.second-3) + ':' + rsKey; } else { m_log.error("Storage-backed RelayState with invalid StorageService ID (%s)", mech.second); relayState.erase(); } #endif } else if (SPConfig::getConfig().isEnabled(SPConfig::InProcess)) { DDF out,in = DDF("set::RelayState").structure(); in.addmember("id").string(mech.second); in.addmember("value").unsafe_string(relayState.c_str()); DDFJanitor jin(in),jout(out); out = application.getServiceProvider().getListenerService()->send(in); if (!out.isstring()) throw IOException("StorageService-backed RelayState mechanism did not return a state key."); relayState = string(mech.second-3) + ':' + out.string(); } } } } else throw ConfigurationException("Unsupported relayState mechanism ($1).", params(1,mech.second)); }
void LoginHTTPRequestHandler::createResponse(const HTTPRequest &r) { HTTPResponse response; QString page = "\r\n<html><body>" "<form method=\"POST\">" "%1" "Username: <input type=\"text\" name=\"username\">" "Password: <input type=\"password\" name=\"pass\">" "<INPUT type=\"submit\" value=\"Auth\">" "</form></body></html>"; if("GET" == r.method){ response.setStatusCode(200); response.setReasonPhrase("OK"); QList<QNetworkCookie> cookieList = QtConcurrent::blockingFiltered(r.cookieJar, [] (const QNetworkCookie &cookie) -> bool { return cookie.name() == "loggedin" && cookie.value() == "1"; } ); if(1 == cookieList.size()){ response.setBody("You're logged in!"); } else{ response.setBody(page.arg("")); } emit responseWritten(response); emit endOfWriting(); return; } if("POST" == r.method && !r.postData.isEmpty()){ if(r.postData.contains("username") && "Ion" == r.postData["username"] && r.postData.contains("pass") && "1234" == r.postData["pass"]){ response.setStatusCode(200); response.setReasonPhrase("OK"); //TODO: this could be something randomized in order to avoid replicating QNetworkCookie cookie("loggedin", "1"); cookie.setHttpOnly(true); response.setCookie(cookie); response.setBody("You're logged in!\n"); emit responseWritten(response); emit endOfWriting(); return; } response.setStatusCode(200); response.setReasonPhrase("OK"); response.setBody(page.arg("Login failed, try again!<br>")); emit responseWritten(response); emit endOfWriting(); return; } response.setStatusCode(400); response.setReasonPhrase("Bad request"); emit responseWritten(response); emit endOfWriting(); }