BOOL CHTTPClient::SendSSLRequest(char *szHost, char *szHostName, char *szPath, char *szUserID, char *szPassword, long lPort, char *szProxy, long lProxyPort, char *szProxyUser, char *szProxyPswd, char *szUserAgent, char *szPost, long dwTimeout, SSL **pssl, SSL_CTX **pssl_ctx, char *szTransaction) { char *szConnectHost; char *szConnectionHeader = "Connection: close\r\n"; char *szContentLength = "Content-Length: %d\r\n"; char *szContentType = "Content-Type: application/x-www-form-urlencoded\r\n"; char *szProxyConn = "Proxy-Connection: Keep-Alive\r\n"; char *szPragma = "Pragma: no-cache\r\n"; long lConnectPort; char *szBuffer = szSendBuffer; char szWork[256]; char *lpWork; char *szGetMethod = "GET"; char *szPostMethod = "POST"; char *szMethod; BOOL RC = TRUE; BOOL bBIOset = FALSE; Base64Coder cBase64Coder; BIO *bio = 0; int done = 0, i, n; char buf[2048]; // Got to have a host if (!szHost) return(FALSE); // Initialize strings used for cookies cDomain = szHostName; cPath = szPath; if (szTransaction) cTransaction = szTransaction; else cTransaction = ""; // Clean up ssl storage try { if (ssl) { SSL_free(ssl); ssl = 0; } } catch(...) { ssl = 0; } try { // Initialise the ssl library now in rtm startup // Create the context structure. Operate in the normal default SSLv3 // in a SSLv2 header mode to maximize the number of servers // we can connect to. if (!ssl_ctx) { if (!pssl_ctx || !*pssl_ctx) { if ((ssl_ctx = SSL_CTX_new(SSLv23_client_method())) == NULL) { bConnectFailed = TRUE; throw FALSE; } } else ssl_ctx = *pssl_ctx; } // Reset variables for response bHeader = FALSE; bReadComplete = FALSE; bHeadRequest = FALSE; lReceivedLength = 0; lContentLength = 0; cHeader = ""; cContent = ""; // turn on all vendor bug compatibility options SSL_CTX_set_options(ssl_ctx, SSL_OP_ALL); // Create the SSL structure - defaults are inherited from the SSL_CTX if ((ssl = SSL_new(ssl_ctx)) == NULL) { bConnectFailed = TRUE; throw FALSE; }; // Set as client side SSL_set_connect_state(ssl); if (!szProxy) { // No proxy szConnectHost = szHost; lConnectPort = lPort; } else { // Proxy szConnectHost = szProxy; lConnectPort = lProxyPort; }; sprintf(szWork, "%s:%u", szConnectHost, (unsigned int) lConnectPort); // Create a BIO to handle the connect and SSL handling if ((bio = BIO_new_connect(szWork)) == NULL) { bConnectFailed = TRUE; throw FALSE; }; // If using proxy then have to tunnel through to the actual ssl server if (szProxy) { sprintf(szBuffer, "CONNECT %s:%u HTTP/1.0\r\nUser-Agent: %s\r\n", szHostName, (unsigned int) lPort, szUserAgent); if (szProxyUser && szProxyPswd && strlen(szProxyUser) > 0 && strlen(szProxyPswd) > 0) { sprintf(szWork, "%s:%s", szProxyUser, szProxyPswd); cBase64Coder.Encode(szWork); sprintf(szWork, "Proxy-Authorization: Basic %s\r\n", (LPCTSTR) cBase64Coder.EncodedMessage()); strcat(szBuffer, szWork); } strcat(szBuffer, szProxyConn); strcat(szBuffer, szPragma); strcat(szBuffer, "\r\n"); // Send connect request n = strlen(szBuffer); i = 0; do { i = BIO_write(bio, &(szBuffer[i]), n); if (i <= 0) { bConnectFailed = TRUE; BIO_free(bio); throw FALSE; } n -= i; } while(n > 0); // Read response from proxy i = BIO_read(bio, buf, sizeof(buf)); if (i <= 0) { bConnectFailed = TRUE; BIO_free(bio); throw FALSE; }; buf[i] = '\0'; lpWork = strstr(buf, "200"); if (!lpWork) { bConnectFailed = TRUE; BIO_free(bio); throw FALSE; }; }; // Use the newly created connect BIO SSL_set_bio(ssl, bio, bio); bBIOset = TRUE; if (pSession && pssl) { SSL_set_session(ssl, pSession); } else { while(!done) { int i; struct tm *ptr; time_t lt; lt = time(NULL); PROTECT_CALL ptr = localtime(<); char *pszTime = asctime(ptr); RAND_seed((unsigned char *) pszTime, strlen(pszTime)); UNPROTECT_CALL // The all important handshake i = SSL_do_handshake(ssl); switch(SSL_get_error(ssl, i)) { case SSL_ERROR_NONE: // Handshake has finished, so proceed done = 1; break; case SSL_ERROR_WANT_READ: case SSL_ERROR_WANT_WRITE: case SSL_ERROR_WANT_CONNECT: // Perform the handshake again. sleep(1); break; default: bConnectFailed = TRUE; throw FALSE; break; }; }; pSession = SSL_get_session(ssl); }; if (szPost) szMethod = szPostMethod; else szMethod = szGetMethod; // Build the request sprintf(szBuffer, "%s %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\n", szMethod, szPath, szUserAgent, szHostName); if (szUserID) { sprintf(szWork, "%s:%s", szUserID, szPassword); cBase64Coder.Encode(szWork); sprintf(szWork, "Authorization: Basic %s\r\n", (LPCTSTR) cBase64Coder.EncodedMessage()); strcat(szBuffer, szWork); } if (szPost) { strcat(szBuffer, szContentType); sprintf(szWork, szContentLength, strlen(szPost)); strcat(szBuffer, szWork); strcat(szBuffer, szProxyConn); strcat(szBuffer, szPragma); } else { strcat(szBuffer, szConnectionHeader); } if (!cTransaction.empty()) SendCookie(szBuffer); // Indicate end of header strcat(szBuffer, "\r\n"); sDebugSendHeader = szBuffer; if (szPost) strcat(szBuffer, szPost); dwStartTime = ::XPlatGetMilliseconds(); // Send request n = strlen(szBuffer); i = 0; do { i = SSL_write(ssl, &(szBuffer[i]), n); if (i <= 0) { bConnectFailed = FALSE; throw FALSE; } n -= i; } while(n > 0); // Read from the other side of the protocol while(1) { i = SSL_read(ssl, buf, sizeof(buf)); if (i <= 0) break; ProcessData((unsigned char *) buf, i); if (!GetTimeLeft(dwTimeout, dwStartTime)) { // Timed out bConnectFailed = FALSE; throw FALSE; }; }; if (szPost && !strcmp(szStatus, "100")) { printf("status 100"); strcat(szBuffer, szPost); // Reset variables for response bHeader = FALSE; bReadComplete = FALSE; bHeadRequest = FALSE; lReceivedLength = 0; lContentLength = 0; cHeader = ""; cContent = ""; // Send request n = strlen(szBuffer); i = 0; do { i = SSL_write(ssl, &(szBuffer[i]), n); if (i <= 0) { bConnectFailed = FALSE; throw FALSE; } n -= i; } while(n > 0); // Read from the other side of the protocol while(1) { i = SSL_read(ssl, buf, sizeof(buf)); if (i <= 0) break; ProcessData((unsigned char *) buf, i); if (!GetTimeLeft(dwTimeout, dwStartTime)) { // Timed out bConnectFailed = FALSE; throw FALSE; }; }; }; Disconnected(); throw TRUE; } catch(BOOL rc) { RC = rc; } catch(...) { RC = FALSE; if (!bBIOset && bio) { try { BIO_free(bio); } catch(...) { } } } if (pssl && pssl_ctx) { if (*pssl == 0) { *pssl = ssl; *pssl_ctx = ssl_ctx; ssl = 0; } ssl_ctx = 0; } return RC; };
CWebSock::EPageReqResult CWebSock::OnPageRequestInternal(const CString& sURI, CString& sPageRet) { if (CZNC::Get().GetProtectWebSessions() && GetSession()->GetIP() != GetRemoteIP()) { DEBUG("Expected IP: " << GetSession()->GetIP()); DEBUG("Remote IP: " << GetRemoteIP()); PrintErrorPage(403, "Access denied", "This session does not belong to your IP."); return PAGE_DONE; } // Check that they really POSTed from one our forms by checking if they // know the "secret" CSRF check value. Don't do this for login since // CSRF against the login form makes no sense and the login form does a // cookies-enabled check which would break otherwise. if (IsPost() && GetParam("_CSRF_Check") != GetCSRFCheck() && sURI != "/login") { DEBUG("Expected _CSRF_Check: " << GetCSRFCheck()); DEBUG("Actual _CSRF_Check: " << GetParam("_CSRF_Check")); PrintErrorPage(403, "Access denied", "POST requests need to send " "a secret token to prevent cross-site request forgery attacks."); return PAGE_DONE; } SendCookie("SessionId", GetSession()->GetId()); if (GetSession()->IsLoggedIn()) { m_sUser = GetSession()->GetUser()->GetUserName(); m_bLoggedIn = true; } // Handle the static pages that don't require a login if (sURI == "/") { if(!m_bLoggedIn && GetParam("cookie_check", false).ToBool() && GetRequestCookie("SessionId").empty()) { GetSession()->AddError("Your browser does not have cookies enabled for this site!"); } return PrintTemplate("index", sPageRet); } else if (sURI == "/favicon.ico") { return PrintStaticFile("/pub/favicon.ico", sPageRet); } else if (sURI == "/robots.txt") { return PrintStaticFile("/pub/robots.txt", sPageRet); } else if (sURI == "/logout") { GetSession()->SetUser(NULL); SetLoggedIn(false); Redirect("/"); // We already sent a reply return PAGE_DONE; } else if (sURI == "/login") { if (GetParam("submitted").ToBool()) { m_sUser = GetParam("user"); m_sPass = GetParam("pass"); m_bLoggedIn = OnLogin(m_sUser, m_sPass); // AcceptedLogin()/RefusedLogin() will call Redirect() return PAGE_DEFERRED; } Redirect("/"); // the login form is here return PAGE_DONE; } else if (sURI.Left(5) == "/pub/") { return PrintStaticFile(sURI, sPageRet); } else if (sURI.Left(11) == "/skinfiles/") { CString sSkinName = sURI.substr(11); CString::size_type uPathStart = sSkinName.find("/"); if (uPathStart != CString::npos) { CString sFilePath = sSkinName.substr(uPathStart + 1); sSkinName.erase(uPathStart); m_Template.ClearPaths(); m_Template.AppendPath(GetSkinPath(sSkinName) + "pub"); if (PrintFile(m_Template.ExpandFile(sFilePath))) { return PAGE_DONE; } else { return PAGE_NOTFOUND; } } return PAGE_NOTFOUND; } else if (sURI.Left(6) == "/mods/" || sURI.Left(10) == "/modfiles/") { ParsePath(); // Make sure modules are treated as directories if (sURI.Right(1) != "/" && sURI.find(".") == CString::npos && sURI.TrimLeft_n("/mods/").TrimLeft_n("/").find("/") == CString::npos) { Redirect(sURI + "/"); return PAGE_DONE; } CModule *pModule = CZNC::Get().GetModules().FindModule(m_sModName); if (!pModule) { // Check if GetSession()->GetUser() is NULL and display // an error in that case if (!ForceLogin()) return PAGE_DONE; pModule = GetSession()->GetUser()->GetModules().FindModule(m_sModName); } if (!pModule) { return PAGE_NOTFOUND; } else if (pModule->WebRequiresLogin() && !ForceLogin()) { return PAGE_PRINT; } else if (pModule->WebRequiresAdmin() && !GetSession()->IsAdmin()) { PrintErrorPage(403, "Forbidden", "You need to be an admin to access this module"); return PAGE_DONE; } else if (!pModule->IsGlobal() && pModule->GetUser() != GetSession()->GetUser()) { PrintErrorPage(403, "Forbidden", "You must login as " + pModule->GetUser()->GetUserName() + " in order to view this page"); return PAGE_DONE; } else if (pModule->OnWebPreRequest(*this, m_sPage)) { return PAGE_DEFERRED; } VWebSubPages& vSubPages = pModule->GetSubPages(); for (unsigned int a = 0; a < vSubPages.size(); a++) { TWebSubPage& SubPage = vSubPages[a]; bool bActive = (m_sModName == pModule->GetModName() && m_sPage == SubPage->GetName()); if (bActive && SubPage->RequiresAdmin() && !GetSession()->IsAdmin()) { PrintErrorPage(403, "Forbidden", "You need to be an admin to access this page"); return PAGE_DONE; } } if (pModule && !pModule->IsGlobal() && (!IsLoggedIn() || pModule->GetUser() != GetSession()->GetUser())) { AddModLoop("UserModLoop", *pModule); } if (sURI.Left(10) == "/modfiles/") { m_Template.AppendPath(GetSkinPath(GetSkinName()) + "/mods/" + m_sModName + "/files/"); m_Template.AppendPath(pModule->GetModDataDir() + "/files/"); if (PrintFile(m_Template.ExpandFile(m_sPage.TrimLeft_n("/")))) { return PAGE_PRINT; } else { return PAGE_NOTFOUND; } } else { SetPaths(pModule, true); /* if a module returns false from OnWebRequest, it does not want the template to be printed, usually because it did a redirect. */ if (pModule->OnWebRequest(*this, m_sPage, m_Template)) { // If they already sent a reply, let's assume // they did what they wanted to do. if (SentHeader()) { return PAGE_DONE; } return PrintTemplate(m_sPage, sPageRet, pModule); } if (!SentHeader()) { PrintErrorPage(404, "Not Implemented", "The requested module does not acknowledge web requests"); } return PAGE_DONE; } } else { CString sPage(sURI.Trim_n("/")); if (sPage.length() < 32) { for (unsigned int a = 0; a < sPage.length(); a++) { unsigned char c = sPage[a]; if ((c < '0' || c > '9') && (c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && c != '_') { return PAGE_NOTFOUND; } } return PrintTemplate(sPage, sPageRet); } } return PAGE_NOTFOUND; }
BOOL CHTTPClient::SendRequest(const char *szHost, const char *szHostName, const char *szPath, const char *szUserID, const char *szPassword, long lPort, const char *szProxy, long lProxyPort, const char *szProxyUser, const char *szProxyPswd, const char *szUserAgent, const char *szPost, long dwTimeout, const char *szTransaction) { char *szConnectHost; const char *szConnectionHeader = "Connection: close\r\n"; const char *szContentLength = "Content-Length: %d\r\n"; const char *szContentType = "Content-Type: application/x-www-form-urlencoded\r\n"; const char *szProxyConn = "Proxy-Connection: Keep-Alive\r\n"; const char *szPragma = "Pragma: no-cache\r\n"; long lConnectPort; char *szBuffer = szSendBuffer; char szWork[256]; const char *szGetMethod = "GET"; const char *szPostMethod = "POST"; char *szMethod; Base64Coder cBase64Coder; // Got to have a host if (!szHost) return(FALSE); // Initialize strings used for cookies cDomain = szHostName; cPath = szPath; if (szTransaction) cTransaction = szTransaction; else cTransaction = ""; if (szPost) szMethod = (char *) szPostMethod; else szMethod = (char *) szGetMethod; if (!szProxy) { // Build request, no proxy szConnectHost = (char *) szHost; lConnectPort = lPort; sprintf(szBuffer, "%s %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\n", szMethod, szPath, szUserAgent, szHostName); } else { // Build request for proxy szConnectHost = (char *) szProxy; lConnectPort = lProxyPort; sprintf(szBuffer, "%s http://%s%s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\n", szMethod, szHostName, szPath, szUserAgent, szHostName); if (szProxyUser && szProxyPswd && strlen(szProxyUser) > 0 && strlen(szProxyPswd) > 0) { sprintf(szWork, "%s:%s", szProxyUser, szProxyPswd); cBase64Coder.Encode(szWork); sprintf(szWork, "Proxy-Authorization: Basic %s\r\n", (LPCTSTR) cBase64Coder.EncodedMessage()); strcat(szBuffer, szWork); } }; if (szUserID) { sprintf(szWork, "%s:%s", szUserID, szPassword); cBase64Coder.Encode(szWork); sprintf(szWork, "Authorization: Basic %s\r\n", (LPCTSTR) cBase64Coder.EncodedMessage()); strcat(szBuffer, szWork); } if (szPost) { strcat(szBuffer, szContentType); sprintf(szWork, szContentLength, strlen(szPost)); strcat(szBuffer, szWork); strcat(szBuffer, szProxyConn); strcat(szBuffer, szPragma); } else { strcat(szBuffer, szConnectionHeader); } if (!cTransaction.empty()) SendCookie(szBuffer); // Indicate end of header strcat(szBuffer, "\r\n"); sDebugSendHeader = szBuffer; if (szPost) strcat(szBuffer, szPost); // Attempt to connect to server bConnectFailed = FALSE; if (!ConnectToServer(szConnectHost, lConnectPort, dwTimeout)) { bConnectFailed = TRUE; return(FALSE); } // Reset variables for response bHeader = FALSE; bReadComplete = FALSE; bHeadRequest = FALSE; lReceivedLength = 0; lContentLength = 0; cHeader = ""; cContent = ""; dwStartTime = ::XPlatGetMilliseconds(); // Send request if (!SendDataOut(szBuffer, strlen(szBuffer), strlen(szBuffer))) { TerminateConnection(); return(FALSE); }; // Wait for a reply if (cRecvEvent.Lock(dwTimeout) == XPLAT_TIMEOUT) { TerminateConnection(); return(FALSE); }; // Done with connection TerminateConnection(); return(TRUE); };