diff --git a/src/win32-service.c b/src/win32-service.c index f7a3788ca7..38a7e647fd 100644 --- a/src/win32-service.c +++ b/src/win32-service.c @@ -30,6 +30,102 @@ #include "win32-service.h" #include "util-debug.h" +/** Append a character, leaving room for the terminating NUL. */ +static bool SCServiceAppendChar(char *path, size_t size, size_t *len, char c) +{ + if (size == 0 || *len >= size - 1) { + return false; + } + path[(*len)++] = c; + path[*len] = '\0'; + return true; +} + +/** + * Quote an argument for the Windows C runtime. Backslashes are literal except + * before a double quote: emit 2n+1 backslashes before an embedded quote, and + * 2n before the closing quote. Always quote, including empty arguments. + */ +static bool SCServiceAppendArgument(char *path, size_t size, size_t *len, const char *arg) +{ + if (!SCServiceAppendChar(path, size, len, '"')) { + return false; + } + while (*arg != '\0') { + size_t backslashes = 0; + while (*arg == '\\') { + backslashes++; + arg++; + } + for (size_t i = 0; i < backslashes; i++) { + if (!SCServiceAppendChar(path, size, len, '\\')) { + return false; + } + if ((*arg == '"' || *arg == '\0') && !SCServiceAppendChar(path, size, len, '\\')) { + return false; + } + } + if (*arg == '\0') { + break; + } + if (*arg == '"' && !SCServiceAppendChar(path, size, len, '\\')) { + return false; + } + if (!SCServiceAppendChar(path, size, len, *arg++)) { + return false; + } + } + return SCServiceAppendChar(path, size, len, '"'); +} + +/** Build the service command line without accessing SCM. */ +static bool SCServiceBuildCommandLine(char *path, size_t size, const char *module, int argc, + const char *const *argv, const char *service_option) +{ + if (size == 0) { + return false; + } + path[0] = '\0'; + size_t len = 0; + if (!SCServiceAppendArgument(path, size, &len, module)) { + return false; + } + for (int i = 1; i < argc; i++) { + /* Preserve the existing service-option prefix matching. */ + if (strlen(argv[i]) <= strlen(service_option) && + strncmp(service_option, argv[i], strlen(argv[i])) == 0) { + continue; + } + if (!SCServiceAppendChar(path, size, &len, ' ') || + !SCServiceAppendArgument(path, size, &len, argv[i])) { + return false; + } + } + return true; +} + +/** Resolve the executable and reject truncation before making any SCM calls. */ +static bool SCServiceGetCommandLine( + char *path, size_t size, int argc, char **argv, const char *service_option) +{ + char module[2048]; + DWORD len = GetModuleFileName(NULL, module, sizeof(module)); + if (len == 0) { + SCLogError("Can't get path to service binary: %d", (int)GetLastError()); + return false; + } + if (len >= sizeof(module)) { + SCLogError("failed to construct service path string: binary path truncated"); + return false; + } + if (!SCServiceBuildCommandLine( + path, size, module, argc, (const char *const *)argv, service_option)) { + SCLogError("failed to construct service path string: invalid or truncated command line"); + return false; + } + return true; +} + static SERVICE_STATUS_HANDLE service_status_handle = 0; static int service_argc = 0; @@ -214,35 +310,11 @@ int SCServiceInstall(int argc, char **argv) SC_HANDLE service = NULL; SC_HANDLE scm = NULL; int ret = -1; - int i = 0; do { - memset(path, 0, sizeof(path)); - - path[0] = '"'; - if (GetModuleFileName(NULL, path + 1, sizeof(path) - 1) == 0) { - SCLogError("Can't get path to service binary: %d", (int)GetLastError()); + if (!SCServiceGetCommandLine(path, sizeof(path), argc, argv, "--service-install")) { break; } - if (strlcat(path, "\"", sizeof(path)) >= sizeof(path)) { - SCLogError("failed to construct service path string: path truncated: %s", path); - break; - } - - /* skip name of binary itself */ - for (i = 1; i < argc; i++) { - if ((strlen(argv[i]) <= strlen("--service-install")) && (strncmp("--service-install", argv[i], strlen(argv[i])) == 0)) { - continue; - } - if (strlcat(path, " ", sizeof(path)) >= sizeof(path)) { - SCLogError("failed to construct service path string: path truncated: %s", path); - return -1; - } - if (strlcat(path, argv[i], sizeof(path)) >= sizeof(path)) { - SCLogError("failed to construct service path string: path truncated: %s", path); - return -1; - } - } if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { SCLogError("Can't open SCM: %d", (int)GetLastError()); @@ -350,36 +422,12 @@ int SCServiceChangeParams(int argc, char **argv) SC_HANDLE service = NULL; SC_HANDLE scm = NULL; int ret = -1; - int i = 0; do { - memset(path, 0, sizeof(path)); - - path[0] = '"'; - if (GetModuleFileName(NULL, path + 1, sizeof(path) - 1) == 0) { - SCLogError("Can't get path to service binary: %d", (int)GetLastError()); - break; - } - if (strlcat(path, "\"", sizeof(path)) >= sizeof(path)) { - SCLogError("failed to construct service path string: path truncated: %s", path); + if (!SCServiceGetCommandLine(path, sizeof(path), argc, argv, "--service-change-params")) { break; } - /* skip name of binary itself */ - for (i = 1; i < argc; i++) { - if ((strlen(argv[i]) <= strlen("--service-change-params")) && (strncmp("--service-change-params", argv[i], strlen(argv[i])) == 0)) { - continue; - } - if (strlcat(path, " ", sizeof(path)) >= sizeof(path)) { - SCLogError("failed to construct service path string: path truncated: %s", path); - return -1; - } - if (strlcat(path, argv[i], sizeof(path)) >= sizeof(path)) { - SCLogError("failed to construct service path string: path truncated: %s", path); - return -1; - } - } - if ((scm = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) == NULL) { SCLogError("Can't open SCM: %d", (int)GetLastError()); break;