#include "defs.h"
-static const char *config_file = CONFIG;
-
-#if !defined(NO_GUI)
-
-static HICON hIcon; /* SHTTPD icon handle */
-HWND hLog; /* Log window */
-
-/*
- * Dialog box control IDs
- */
-#define ID_GROUP 100
-#define ID_SAVE 101
-#define ID_STATUS 102
-#define ID_STATIC 103
-#define ID_SETTINGS 104
-#define ID_QUIT 105
-#define ID_TRAYICON 106
-#define ID_TIMER 107
-#define ID_ICON 108
-#define ID_ADVANCED 109
-#define ID_SHOWLOG 110
-#define ID_LOG 111
-
-#define ID_USER 200
-#define ID_DELTA 1000
-
-static void
-run_server(void *param)
-{
- struct shttpd_ctx *ctx = param;
-
- open_listening_ports(ctx);
-
- while (WaitForSingleObject(ctx->ev[0], 0) != WAIT_OBJECT_0)
- shttpd_poll(ctx, 1000);
-
- SetEvent(ctx->ev[1]);
- shttpd_fini(ctx);
-}
-
-/*
- * Save the configuration back into config file
- */
static void
-save_config(HWND hDlg, FILE *fp)
-{
- const struct opt *opt;
- char text[FILENAME_MAX];
- int id;
-
- if (fp == NULL)
- elog(E_FATAL, NULL, "save_config: cannot open %s", config_file);
-
- for (opt = options; opt->name != NULL; opt++) {
- id = ID_USER + (opt - options); /* Control ID */
-
- /* Do not save if the text is the same as default */
-
- if (opt->flags & OPT_BOOL)
- (void) fprintf(fp, "%s\t%d\n",
- opt->name, IsDlgButtonChecked(hDlg, id));
- else if (GetDlgItemText(hDlg, id, text, sizeof(text)) != 0 &&
- (opt->def == NULL || strcmp(text, opt->def) != 0))
- (void) fprintf(fp, "%s\t%s\n", opt->name, text);
- }
-
- (void) fclose(fp);
-}
-
-static void
-set_control_values(HWND hDlg, const struct shttpd_ctx *ctx)
-{
- const struct opt *opt;
- const union variant *v;
- char buf[FILENAME_MAX];
- int id;
-
- for (opt = options; opt->name != NULL; opt++) {
- id = ID_USER + (opt - options);
- v = (union variant *) ((char *) ctx + opt->ofs);
- if (opt->flags & OPT_BOOL) {
- CheckDlgButton(hDlg, id,
- v->v_int ? BST_CHECKED : BST_UNCHECKED);
- } else if (opt->flags & OPT_INT) {
- my_snprintf(buf, sizeof(buf), "%d", v->v_int);
- SetDlgItemText(hDlg, id, buf);
- } else {
- SetDlgItemText(hDlg, id, v->v_str);
- }
- }
-
-}
-
-static BOOL CALLBACK
-DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+fix_directory_separators(char *path)
{
- static struct shttpd_ctx *ctx, **pctx;
- HANDLE ev;
- const struct opt *opt;
- DWORD tid;
- int id, up;
- char text[256];
-
- switch (msg) {
-
- case WM_CLOSE:
- KillTimer(hDlg, ID_TIMER);
- DestroyWindow(hDlg);
- break;
-
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case ID_SAVE:
- EnableWindow(GetDlgItem(hDlg, ID_SAVE), FALSE);
- save_config(hDlg, fopen(config_file, "w+"));
- ev = ctx->ev[1];
- SetEvent(ctx->ev[0]);
- WaitForSingleObject(ev, INFINITE);
- *pctx = ctx = init_from_argc_argv(config_file, 0, NULL);
- open_listening_ports(ctx);
- _beginthread(run_server, 0, ctx);
- EnableWindow(GetDlgItem(hDlg, ID_SAVE), TRUE);
-
- break;
- }
-
- id = ID_USER + ID_DELTA;
- for (opt = options; opt->name != NULL; opt++, id++)
- if (LOWORD(wParam) == id) {
- OPENFILENAME of;
- BROWSEINFO bi;
- char path[FILENAME_MAX] = "";
-
- memset(&of, 0, sizeof(of));
- of.lStructSize = sizeof(of);
- of.hwndOwner = (HWND) hDlg;
- of.lpstrFile = path;
- of.nMaxFile = sizeof(path);
- of.lpstrInitialDir = ctx->document_root;
- of.Flags = OFN_CREATEPROMPT | OFN_NOCHANGEDIR;
-
- memset(&bi, 0, sizeof(bi));
- bi.hwndOwner = (HWND) hDlg;
- bi.lpszTitle = "Choose WWW root directory:";
- bi.ulFlags = BIF_RETURNONLYFSDIRS;
-
- if (opt->flags & OPT_DIR)
- SHGetPathFromIDList(
- SHBrowseForFolder(&bi), path);
- else
- GetOpenFileName(&of);
-
- if (path[0] != '\0')
- SetWindowText(GetDlgItem(hDlg,
- id - ID_DELTA), path);
- }
-
- break;
-
- case WM_INITDIALOG:
- pctx = (struct shttpd_ctx **) lParam;
- ctx = *pctx;
- SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
- SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
- SetWindowText(hDlg, "SHTTPD settings");
- SetFocus(GetDlgItem(hDlg, ID_SAVE));
- set_control_values(hDlg, ctx);
- break;
- default:
- break;
+ for (; *path != '\0'; path++) {
+ if (*path == '/')
+ *path = '\\';
+ if (*path == '\\')
+ while (path[1] == '\\' || path[1] == '/')
+ (void) memmove(path + 1,
+ path + 2, strlen(path + 2) + 1);
}
-
- return FALSE;
}
-static void *
-align(void *ptr, DWORD alig)
+static int
+protect_against_code_disclosure(const char *path)
{
- ULONG ul = (ULONG) ptr;
+ WIN32_FIND_DATA data;
+ HANDLE handle;
+ const char *p;
- ul += alig;
- ul &= ~alig;
-
- return ((void *) ul);
-}
-
-
-static void
-add_control(unsigned char **mem, DLGTEMPLATE *dia, WORD type, DWORD id,
- DWORD style, WORD x, WORD y, WORD cx, WORD cy, const char *caption)
-{
- DLGITEMTEMPLATE *tp;
- LPWORD p;
-
- dia->cdit++;
-
- *mem = align(*mem, 3);
- tp = (DLGITEMTEMPLATE *) *mem;
-
- tp->id = (WORD)id;
- tp->style = style;
- tp->dwExtendedStyle = 0;
- tp->x = x;
- tp->y = y;
- tp->cx = cx;
- tp->cy = cy;
-
- p = align(*mem + sizeof(*tp), 1);
- *p++ = 0xffff;
- *p++ = type;
+ /*
+ * Protect against CGI code disclosure under Windows.
+ * This is very nasty hole. Windows happily opens files with
+ * some garbage in the end of file name. So fopen("a.cgi ", "r")
+ * actually opens "a.cgi", and does not return an error! And since
+ * "a.cgi " does not have valid CGI extension, this leads to
+ * the CGI code disclosure.
+ * To protect, here we delete all fishy characters from the
+ * end of file name.
+ */
- while (*caption != '\0')
- *p++ = (WCHAR) *caption++;
- *p++ = 0;
- p = align(p, 1);
+ if ((handle = FindFirstFile(path, &data)) == INVALID_HANDLE_VALUE)
+ return (FALSE);
- *p++ = 0;
- *mem = (unsigned char *) p;
-}
-
-static void
-show_settings_dialog(struct shttpd_ctx **ctxp)
-{
-#define HEIGHT 15
-#define WIDTH 400
-#define LABEL_WIDTH 70
-
- unsigned char mem[4096], *p;
- DWORD style;
- DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
- WORD cl, x, y, width, nelems = 0;
- const struct opt *opt;
- static int guard;
-
- static struct {
- DLGTEMPLATE template; /* 18 bytes */
- WORD menu, class;
- wchar_t caption[1];
- WORD fontsiz;
- wchar_t fontface[7];
- } dialog_header = {{WS_CAPTION | WS_POPUP | WS_SYSMENU | WS_VISIBLE |
- DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
- 0, 200, 200, WIDTH, 0}, 0, 0, L"", 8, L"Tahoma"};
-
- if (guard == 0)
- guard++;
- else
- return;
-
- (void) memset(mem, 0, sizeof(mem));
- (void) memcpy(mem, &dialog_header, sizeof(dialog_header));
- p = mem + sizeof(dialog_header);
-
- for (opt = options; opt->name != NULL; opt++) {
-
- style = WS_CHILD | WS_VISIBLE | WS_TABSTOP;
- x = 10 + (WIDTH / 2) * (nelems % 2);
- y = (nelems/2 + 1) * HEIGHT + 5;
- width = WIDTH / 2 - 20 - LABEL_WIDTH;
- if (opt->flags & OPT_INT) {
- style |= ES_NUMBER;
- cl = 0x81;
- style |= WS_BORDER | ES_AUTOHSCROLL;
- } else if (opt->flags & OPT_BOOL) {
- cl = 0x80;
- style |= BS_AUTOCHECKBOX;
- } else if (opt->flags & (OPT_DIR | OPT_FILE)) {
- style |= WS_BORDER | ES_AUTOHSCROLL;
- width -= 20;
- cl = 0x81;
- add_control(&p, dia, 0x80,
- ID_USER + ID_DELTA + (opt - options),
- WS_VISIBLE | WS_CHILD | BS_PUSHBUTTON,
- (WORD) (x + width + LABEL_WIDTH + 5),
- y, 15, 12, "...");
- } else {
- cl = 0x81;
- style |= WS_BORDER | ES_AUTOHSCROLL;
- }
- add_control(&p, dia, 0x82, ID_STATIC, WS_VISIBLE | WS_CHILD,
- x, y, LABEL_WIDTH, HEIGHT, opt->desc);
- add_control(&p, dia, cl, ID_USER + (opt - options), style,
- (WORD) (x + LABEL_WIDTH), y, width, 12, "");
- nelems++;
- }
+ FindClose(handle);
- y = (WORD) (((nelems + 1)/2 + 1) * HEIGHT + 5);
- add_control(&p, dia, 0x80, ID_GROUP, WS_CHILD | WS_VISIBLE |
- BS_GROUPBOX, 5, 5, WIDTH - 10, y, "Settings");
- y += 10;
- add_control(&p, dia, 0x80, ID_SAVE,
- WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
- WIDTH - 70, y, 65, 12, "Save Settings");
-#if 0
- add_control(&p, dia, 0x80, ID_ADVANCED,
- WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | WS_TABSTOP,
- WIDTH - 190, y, 110, 12, "Show Advanced Settings >>");
-#endif
- add_control(&p, dia, 0x82, ID_STATIC,
- WS_CHILD | WS_VISIBLE | WS_DISABLED,
- 5, y, 180, 12,"SHTTPD v." VERSION
- " (http://shttpd.sourceforge.net)");
+ for (p = path + strlen(path); p > path && p[-1] != '\\';)
+ p--;
- dia->cy = ((nelems + 1)/2 + 1) * HEIGHT + 30;
- DialogBoxIndirectParam(NULL, dia, NULL, DlgProc, (LPARAM) ctxp);
- guard--;
-}
-
-static BOOL CALLBACK
-LogProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- static struct shttpd_ctx *ctx;
- static HWND hStatus;
- HWND hEdit;
- RECT rect, rect2, rect3, rect4;
- int len, up, widths[] = {120, 220, 330, 460, -1};
- char text[256], buf[1024 * 64];
-
- switch (msg) {
-
- case WM_CLOSE:
- KillTimer(hDlg, ID_TIMER);
- DestroyWindow(hDlg);
- break;
-
- case WM_APP:
- hEdit = GetDlgItem(hDlg, ID_LOG);
- len = GetWindowText(hEdit, buf, sizeof(buf));
- if (len > sizeof(buf) * 4 / 5)
- len = sizeof(buf) * 4 / 5;
- my_snprintf(buf + len, sizeof(buf) - len,
- "%s\r\n", (char *) lParam);
- SetWindowText(hEdit, buf);
- SendMessage(hEdit, WM_VSCROLL, SB_BOTTOM, 0);
- break;
-
- case WM_TIMER:
- /* Print statistics on a status bar */
- up = current_time - ctx->start_time;
- (void) my_snprintf(text, sizeof(text),
- " Up: %3d h %2d min %2d sec",
- up / 3600, up / 60 % 60, up % 60);
- SendMessage(hStatus, SB_SETTEXT, 0, (LPARAM) text);
- (void) my_snprintf(text, sizeof(text),
- " Requests: %u", ctx->nrequests);
- SendMessage(hStatus, SB_SETTEXT, 1, (LPARAM) text);
- (void) my_snprintf(text, sizeof(text),
- " Sent: %4.2f Mb", (double) ctx->out / 1048576);
- SendMessage(hStatus, SB_SETTEXT, 2, (LPARAM) text);
- (void) my_snprintf(text, sizeof(text),
- " Received: %4.2f Mb", (double) ctx->in / 1048576);
- SendMessage(hStatus, SB_SETTEXT, 3, (LPARAM) text);
- break;
-
- case WM_INITDIALOG:
- ctx = (struct shttpd_ctx *) lParam;
- SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_SMALL,(LPARAM)hIcon);
- SendMessage(hDlg,WM_SETICON,(WPARAM)ICON_BIG,(LPARAM)hIcon);
- hStatus = CreateStatusWindow(WS_CHILD | WS_VISIBLE,
- "", hDlg, ID_STATUS);
- SendMessage(hStatus, SB_SETPARTS, 5, (LPARAM) widths);
- SendMessage(hStatus, SB_SETTEXT, 4, (LPARAM) " Running");
- SetWindowText(hDlg, "SHTTPD web server log");
- SetTimer(hDlg, ID_TIMER, 1000, NULL);
- GetWindowRect(GetDesktopWindow(), &rect3);
- GetWindowRect(hDlg, &rect4);
- GetClientRect(hDlg, &rect);
- GetClientRect(hStatus, &rect2);
- SetWindowPos(GetDlgItem(hDlg, ID_LOG), 0,
- 0, 0, rect.right, rect.bottom - rect2.bottom, 0);
- SetWindowPos(hDlg, HWND_TOPMOST,
- rect3.right - (rect4.right - rect4.left),
- rect3.bottom - (rect4.bottom - rect4.top) - 30,
- 0, 0, SWP_NOSIZE);
- SetFocus(hStatus);
- SendMessage(hDlg, WM_TIMER, 0, 0);
- hLog = hDlg;
- break;
- default:
- break;
- }
-
-
- return (FALSE);
-}
-
-static void
-show_log_window(struct shttpd_ctx *ctx)
-{
- unsigned char mem[4096], *p;
- DWORD style;
- DLGTEMPLATE *dia = (DLGTEMPLATE *) mem;
- WORD cl, x, y, width, nelems = 0;
-
- static struct {
- DLGTEMPLATE template; /* 18 bytes */
- WORD menu, class;
- wchar_t caption[1];
- WORD fontsiz;
- wchar_t fontface[7];
- } dialog_header = {{WS_CAPTION | WS_POPUP | WS_VISIBLE | WS_SYSMENU |
- DS_SETFONT | WS_DLGFRAME, WS_EX_TOOLWINDOW,
- 0, 200, 200, 400, 100}, 0, 0, L"", 8, L"Tahoma"};
-
- if (hLog != NULL)
- return;
-
- (void) memset(mem, 0, sizeof(mem));
- (void) memcpy(mem, &dialog_header, sizeof(dialog_header));
- p = mem + sizeof(dialog_header);
-
- add_control(&p, dia, 0x81, ID_LOG, WS_CHILD | WS_VISIBLE |
- WS_BORDER | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL |
- ES_READONLY, 5, 5, WIDTH - 10, 60, "");
-
- DialogBoxIndirectParam(NULL, dia, NULL, LogProc, (LPARAM) ctx);
-
- hLog = NULL;
-}
-
-static LRESULT CALLBACK
-WindowProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
-{
- static NOTIFYICONDATA ni;
- static struct shttpd_ctx *ctx;
- DWORD tid; /* Thread ID */
- HMENU hMenu;
- POINT pt;
-
- switch (msg) {
- case WM_CREATE:
- ctx = ((CREATESTRUCT *) lParam)->lpCreateParams;
- memset(&ni, 0, sizeof(ni));
- ni.cbSize = sizeof(ni);
- ni.uID = ID_TRAYICON;
- ni.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP;
- ni.hIcon = hIcon;
- ni.hWnd = hWnd;
- my_snprintf(ni.szTip, sizeof(ni.szTip), "SHTTPD web server");
- ni.uCallbackMessage = WM_USER;
- Shell_NotifyIcon(NIM_ADD, &ni);
- ctx->ev[0] = CreateEvent(0, TRUE, FALSE, 0);
- ctx->ev[1] = CreateEvent(0, TRUE, FALSE, 0);
- _beginthread(run_server, 0, ctx);
- break;
- case WM_CLOSE:
- Shell_NotifyIcon(NIM_DELETE, &ni);
- PostQuitMessage(0);
- break;
- case WM_COMMAND:
- switch (LOWORD(wParam)) {
- case ID_SETTINGS:
- show_settings_dialog(&ctx);
- break;
- case ID_QUIT:
- SendMessage(hWnd, WM_CLOSE, wParam, lParam);
- PostQuitMessage(0);
- break;
- case ID_SHOWLOG:
- show_log_window(ctx);
- break;
- }
- break;
- case WM_USER:
- switch (lParam) {
- case WM_RBUTTONUP:
- case WM_LBUTTONUP:
- case WM_LBUTTONDBLCLK:
- hMenu = CreatePopupMenu();
- AppendMenu(hMenu, 0, ID_SETTINGS, "Settings");
- AppendMenu(hMenu, 0, ID_SHOWLOG, "Show Log");
- AppendMenu(hMenu, 0, ID_QUIT, "Exit SHTTPD");
- GetCursorPos(&pt);
- TrackPopupMenu(hMenu, 0, pt.x, pt.y, 0, hWnd, NULL);
- DestroyMenu(hMenu);
- break;
- }
- break;
- }
+ if (!(data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ strcmp(data.cFileName, p) != 0)
+ return (FALSE);
- return (DefWindowProc(hWnd, msg, wParam, lParam));
-}
-
-int WINAPI
-WinMain(HINSTANCE h, HINSTANCE prev, char *cmdline, int show)
-{
- struct shttpd_ctx *ctx;
- WNDCLASS cls;
- HWND hWnd;
- MSG msg;
-
- ctx = init_from_argc_argv(config_file, 0, NULL);
- (void) memset(&cls, 0, sizeof(cls));
-
- hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(ID_ICON));
- if (hIcon == NULL)
- hIcon = LoadIcon(NULL, IDI_APPLICATION);
- cls.lpfnWndProc = (WNDPROC) WindowProc;
- cls.hIcon = hIcon;
- cls.lpszClassName = "shttpd v." VERSION;
-
- if (!RegisterClass(&cls))
- elog(E_FATAL, NULL, "RegisterClass: %d", ERRNO);
- else if ((hWnd = CreateWindow(cls.lpszClassName, "",WS_OVERLAPPEDWINDOW,
- 0, 0, 0, 0, NULL, NULL, NULL, ctx)) == NULL)
- elog(E_FATAL, NULL, "CreateWindow: %d", ERRNO);
-
- while (GetMessage(&msg, (HWND) NULL, 0, 0)) {
- TranslateMessage(&msg);
- DispatchMessage(&msg);
- }
-
- return (0);
-}
-#endif /* NO_GUI */
-
-static void
-fix_directory_separators(char *path)
-{
- for (; *path != '\0'; path++) {
- if (*path == '/')
- *path = '\\';
- if (*path == '\\')
- while (path[1] == '\\' || path[1] == '/')
- (void) memmove(path + 1,
- path + 2, strlen(path + 2) + 1);
- }
+ return (TRUE);
}
int
fix_directory_separators(buf);
MultiByteToWideChar(CP_UTF8, 0, buf, -1, wbuf, sizeof(wbuf));
+ if (protect_against_code_disclosure(buf) == FALSE)
+ return (-1);
+
return (_wopen(wbuf, flags));
}
si.hStdInput = h[0];
/* If CGI file is a script, try to read the interpreter line */
- if (c->ctx->cgi_interpreter == NULL) {
+ if (c->ctx->options[OPT_CGI_INTERPRETER] == NULL) {
if ((fp = fopen(prog, "r")) != NULL) {
(void) fgets(line, sizeof(line), fp);
if (memcmp(line, "#!", 2) != 0)
line + 2, line[2] == '\0' ? "" : " ", prog);
} else {
(void) my_snprintf(cmdline, sizeof(cmdline), "%s %s",
- c->ctx->cgi_interpreter, prog);
+ c->ctx->options[OPT_CGI_INTERPRETER], prog);
}
(void) my_snprintf(line, sizeof(line), "%s", dir);