int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query) { websStatType sbuf; char_t *lpath, *tmp, *date; int bytes, flags, nchars, rc; a_assert(websValid(wp)); a_assert(url && *url); a_assert(path); a_assert(query); flags = websGetRequestFlags(wp); /* * We do whitelist validation in addition to standard URL validation. * The whitelist should really catch anything invalid first. * If the whitelist check fails, rebuild the list and try again. * Also validate if we are not on a secure connection, but the whitelist * entry has the SSL flag set, do not serve the page. */ #ifdef WEBS_WHITELIST_SUPPORT printf ("wp->url: (%s)\n", wp->url); if ((rc = websWhitelistCheck(wp->url)) < 0) { websBuildWhitelist(); if ((rc = websWhitelistCheck(wp->url)) < 0) { websError(wp, 404, T("Cannot open URL: type 1")); return 1; } } if (!(flags & WEBS_SECURE) && (rc & WHITELIST_SSL)) { websError(wp, 500, T("HTTPS access required")); return 1; } #endif /* WEBS_WHITELIST_SUPPORT */ /* * Validate the URL and ensure that ".."s don't give access to unwanted files */ if (websValidateUrl(wp, path) < 0) { /* * preventing a cross-site scripting exploit -- you may restore the * following line of code to revert to the original behavior... websError(wp, 500, T("Invalid URL %s"), url); */ websError(wp, 500, T("Invalid URL")); websBuildWhitelist(); return 1; } lpath = websGetRequestLpath(wp); nchars = gstrlen(lpath) - 1; if (lpath[nchars] == '/' || lpath[nchars] == '\\') { lpath[nchars] = '\0'; } /* * If the file is a directory, redirect using the nominated default page */ if (websPageIsDirectory(lpath)) { nchars = gstrlen(path); if (path[nchars-1] == '/' || path[nchars-1] == '\\') { path[--nchars] = '\0'; } nchars += gstrlen(websDefaultPage) + 2; fmtAlloc(&tmp, nchars, T("%s/%s"), path, websDefaultPage); printf ("websDefaultHandler: tmp(%s)\n", tmp); websRedirect(wp, tmp); bfreeSafe(B_L, tmp); return 1; } printf ("we now open the web pages\n"); printf ("lpath(%s), path(%s)\n", lpath, path); /* * Open the document. Stat for later use. */ if (websPageOpen(wp, lpath, path, O_RDONLY | O_BINARY, 0666) < 0) { /* 10 Dec 02 BgP -- according to * <http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html>, * the proper code to return here is NOT 400 (old code), which is used * to indicate a malformed request. Here, the request is good, but the * error we need to tell the client about is 404 (Not Found). */ /* * 17 Mar 03 BgP -- prevent a cross-site scripting exploit websError(wp, 404, T("Cannot open URL %s"), url); */ websError(wp, 404, T("Cannot open URL, type 2")); websBuildWhitelist(); return 1; } printf ("lpath(%s), path(%s)\n", lpath, path); if (websPageStat(wp, lpath, path, &sbuf) < 0) { /* * 17 Mar 03 BgP * prevent a cross-site scripting exploit websError(wp, 400, T("Cannot stat page for URL %s"), url); */ websError(wp, 400, T("Cannot stat page for URL")); websBuildWhitelist(); return 1; } /* * If the page has not been modified since the user last received it and it * is not dynamically generated each time (ASP), then optimize request by * sending a 304 Use local copy response */ websStats.localHits++; #ifdef WEBS_IF_MODIFIED_SUPPORT if (flags & WEBS_IF_MODIFIED && !(flags & WEBS_ASP)) { if (sbuf.mtime <= wp->since) { websWrite(wp, T("HTTP/1.0 304 Use local copy\r\n")); /* * by license terms the following line of code must * not be modified. */ websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); if (flags & WEBS_KEEP_ALIVE) { websWrite(wp, T("Connection: keep-alive\r\n")); } websWrite(wp, T("\r\n")); websSetRequestFlags(wp, flags |= WEBS_HEADER_DONE); websDone(wp, 304); return 1; } } #endif /* * Output the normal HTTP response header */ if ((date = websGetDateString(NULL)) != NULL) { websWrite(wp, T("HTTP/1.0 200 OK\r\nDate: %s\r\n"), date); /* * The Server HTTP header below must not be modified unless * explicitly allowed by licensing terms. */ #ifdef WEBS_SSL_SUPPORT websWrite(wp, T("Server: %s/%s %s/%s\r\n"), WEBS_NAME, WEBS_VERSION, SSL_NAME, SSL_VERSION); #else websWrite(wp, T("Server: %s/%s\r\n"), WEBS_NAME, WEBS_VERSION); #endif bfree(B_L, date); } flags |= WEBS_HEADER_DONE; /* * If this is an ASP request, ensure the remote browser doesn't cache it. * Send back both HTTP/1.0 and HTTP/1.1 cache control directives */ if (flags & WEBS_ASP) { bytes = 0; websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n")); } else { if ((date = websGetDateString(&sbuf)) != NULL) { websWrite(wp, T("Last-modified: %s\r\n"), date); bfree(B_L, date); } bytes = sbuf.size; } if (bytes) { websWrite(wp, T("Content-length: %d\r\n"), bytes); websSetRequestBytes(wp, bytes); } websWrite(wp, T("Content-type: %s\r\n"), websGetRequestType(wp)); if ((flags & WEBS_KEEP_ALIVE) && !(flags & WEBS_ASP)) { websWrite(wp, T("Connection: keep-alive\r\n")); } websWrite(wp, T("\r\n")); /* * All done if the browser did a HEAD request */ if (flags & WEBS_HEAD_REQUEST) { websDone(wp, 200); return 1; } /* * Evaluate ASP requests */ if (flags & WEBS_ASP) { if (websAspRequest(wp, lpath) < 0) { return 1; } websDone(wp, 200); return 1; } /* * Return the data via background write */ websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent); return 1; }
int websDefaultHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query) { websStatType sbuf; char_t *lpath, *tmp, *date; int bytes, flags, nchars; a_assert(websValid(wp)); a_assert(url && *url); a_assert(path); a_assert(query); /* * Validate the URL and ensure that ".."s don't give access to unwanted files */ flags = websGetRequestFlags(wp); if (websValidateUrl(wp, path) < 0) { websError(wp, 500, T("Invalid URL %s"), url); return 1; } lpath = websGetRequestLpath(wp); nchars = gstrlen(lpath) - 1; if (lpath[nchars] == '/' || lpath[nchars] == '\\') { lpath[nchars] = '\0'; } /* * If the file is a directory, redirect using the nominated default page */ if (websPageIsDirectory(lpath)) { nchars = gstrlen(path); if (path[nchars-1] == '/' || path[nchars-1] == '\\') { path[--nchars] = '\0'; } nchars += gstrlen(websDefaultPage) + 2; fmtAlloc(&tmp, nchars, T("%s/%s"), path, websDefaultPage); websRedirect(wp, tmp); bfreeSafe(B_L, tmp); return 1; } /* * Open the document. Stat for later use. */ if (websPageOpen(wp, lpath, path, SOCKET_RDONLY | SOCKET_BINARY, 0666) < 0) { websError(wp, 400, T("Cannot open URL <b>%s</b>"), url); return 1; } if (websPageStat(wp, lpath, path, &sbuf) < 0) { websError(wp, 400, T("Cannot stat page for URL <b>%s</b>"), url); return 1; } /* * If the page has not been modified since the user last received it and it * is not dynamically generated each time (ASP), then optimize request by * sending a 304 Use local copy response */ websStats.localHits++; #ifdef WEBS_IF_MODIFIED_SUPPORT if (flags & WEBS_IF_MODIFIED && !(flags & WEBS_ASP)) { if (sbuf.mtime <= wp->since) { websWrite(wp, T("HTTP/1.0 304 Use local copy\r\n")); /* * by license terms the following line of code must * not be modified. */ websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); if (flags & WEBS_KEEP_ALIVE) { websWrite(wp, T("Connection: keep-alive\r\n")); } websWrite(wp, T("\r\n")); websSetRequestFlags(wp, flags |= WEBS_HEADER_DONE); websDone(wp, 304); return 1; } } #endif /* * Output the normal HTTP response header */ if ((date = websGetDateString(NULL)) != NULL) { websWrite(wp, T("HTTP/1.0 200 OK\r\nDate: %s\r\n"), date); /* * By license terms the following line of code must not be modified. */ websWrite(wp, T("Server: %s\r\n"), WEBS_NAME); bfree(B_L, date); } flags |= WEBS_HEADER_DONE; /* * If this is an ASP request, ensure the remote browser doesn't cache it. * Send back both HTTP/1.0 and HTTP/1.1 cache control directives */ if (flags & WEBS_ASP) { bytes = 0; websWrite(wp, T("Pragma: no-cache\r\nCache-Control: no-cache\r\n")); } else { if ((date = websGetDateString(&sbuf)) != NULL) { websWrite(wp, T("Last-modified: %s\r\n"), date); bfree(B_L, date); } bytes = sbuf.size; } if (bytes) { websWrite(wp, T("Content-length: %d\r\n"), bytes); websSetRequestBytes(wp, bytes); } websWrite(wp, T("Content-type: %s\r\n"), websGetRequestType(wp)); if ((flags & WEBS_KEEP_ALIVE) && !(flags & WEBS_ASP)) { websWrite(wp, T("Connection: keep-alive\r\n")); } websWrite(wp, T("\r\n")); /* * All done if the browser did a HEAD request */ if (flags & WEBS_HEAD_REQUEST) { websDone(wp, 200); return 1; } /* * Evaluate ASP requests */ if (flags & WEBS_ASP) { if (websAspRequest(wp, lpath) < 0) { return 1; } websDone(wp, 200); return 1; } #ifdef WEBS_SSL_SUPPORT if (wp->flags & WEBS_SECURE) { websDefaultWriteEvent(wp); } else { websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent); } #else /* * For normal web documents, return the data via background write */ websSetRequestSocketHandler(wp, SOCKET_WRITABLE, websDefaultWriteEvent); #endif return 1; }
int websSecurityHandler(webs_t wp, char_t *urlPrefix, char_t *webDir, int arg, char_t *url, char_t *path, char_t *query) { char_t *type, *userid, *password, *accessLimit; int flags, nRet; accessMeth_t am; a_assert(websValid(wp)); a_assert(url && *url); a_assert(path && *path); /* * Get the critical request details */ type = websGetRequestType(wp); password = websGetRequestPassword(wp); userid = websGetRequestUserName(wp); flags = websGetRequestFlags(wp); /* * Get the access limit for the URL. Exit if none found. */ accessLimit = umGetAccessLimit(path); if (accessLimit == NULL) { return 0; } /* * Check to see if URL must be encrypted */ #ifdef WEBS_SSL_SUPPORT nRet = umGetAccessLimitSecure(accessLimit); if (nRet && ((flags & WEBS_SECURE) == 0)) { websStats.access++; websError(wp, 405, T("Access Denied\nSecure access is required.")); trace(3, T("SEC: Non-secure access attempted on <%s>\n"), path); /* bugfix 5/24/02 -- we were leaking the memory pointed to by * 'accessLimit'. Thanks to Simon Byholm. */ bfree(B_L, accessLimit); return 1; } #endif /* * Get the access limit for the URL */ am = umGetAccessMethodForURL(accessLimit); nRet = 0; if ((flags & WEBS_LOCAL_REQUEST) && (debugSecurity == 0)) { /* * Local access is always allowed (defeat when debugging) */ } else if (am == AM_NONE) { /* * URL is supposed to be hidden! Make like it wasn't found. */ websStats.access++; websError(wp, 404, T("Page Not Found")); nRet = 1; } else if (userid && *userid) { if (!umUserExists(userid)) { websStats.access++; websError(wp, 401, T("Access Denied\nUnknown User")); trace(3, T("SEC: Unknown user <%s> attempted to access <%s>\n"), userid, path); nRet = 1; } else if (!umUserCanAccessURL(userid, accessLimit)) { websStats.access++; websError(wp, 403, T("Access Denied\nProhibited User")); nRet = 1; } else if (password && * password) { char_t * userpass = umGetUserPassword(userid); if (userpass) { if (gstrcmp(password, userpass) != 0) { websStats.access++; websError(wp, 401, T("Access Denied\nWrong Password")); trace(3, T("SEC: Password fail for user <%s>") T("attempt to access <%s>\n"), userid, path); nRet = 1; } else { /* * User and password check out. */ } bfree (B_L, userpass); } #ifdef DIGEST_ACCESS_SUPPORT } else if (flags & WEBS_AUTH_DIGEST) { char_t *digestCalc; /* * Check digest for equivalence */ wp->password = umGetUserPassword(userid); a_assert(wp->digest); a_assert(wp->nonce); a_assert(wp->password); digestCalc = websCalcDigest(wp); a_assert(digestCalc); if (gstrcmp(wp->digest, digestCalc) != 0) { bfree (B_L, digestCalc); digestCalc = websCalcUrlDigest(wp); a_assert(digestCalc); if (gstrcmp(wp->digest, digestCalc) != 0) { websStats.access++; websError(wp, 401, T("Access Denied\nWrong Password")); nRet = 1; } } bfree (B_L, digestCalc); #endif } else { /* * No password has been specified */ #ifdef DIGEST_ACCESS_SUPPORT if (am == AM_DIGEST) { wp->flags |= WEBS_AUTH_DIGEST; } #endif websStats.errors++; websError(wp, 401, T("Access to this document requires a password")); nRet = 1; } } else if (am != AM_FULL) { /* * This will cause the browser to display a password / username * dialog */ #ifdef DIGEST_ACCESS_SUPPORT if (am == AM_DIGEST) { wp->flags |= WEBS_AUTH_DIGEST; } #endif websStats.errors++; websError(wp, 401, T("Access to this document requires a User ID")); nRet = 1; } bfree(B_L, accessLimit); return nRet; }