X-Git-Url: https://git.pterodactylus.net/?p=fms.git;a=blobdiff_plain;f=libs%2Fshttpd%2Fcompat_win32.c;h=05f6036553f137ae12854ed0febdb3305e456624;hp=fb5b1a1f218bbbdb31d9dc304ba1abb1dd77e0a4;hb=1dee4e3cd008a27789bbce05b0eb47b0eb5d121a;hpb=3dc3ac3cfe10b7196a7977e9c041c29fa141c35e diff --git a/libs/shttpd/compat_win32.c b/libs/shttpd/compat_win32.c index fb5b1a1..05f6036 100644 --- a/libs/shttpd/compat_win32.c +++ b/libs/shttpd/compat_win32.c @@ -10,533 +10,50 @@ #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 @@ -549,6 +66,9 @@ my_open(const char *path, int flags, int mode) 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)); } @@ -876,7 +396,7 @@ spawn_process(struct conn *c, const char *prog, char *envblk, 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) @@ -891,7 +411,7 @@ spawn_process(struct conn *c, const char *prog, char *envblk, 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);