diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-19 17:20:00 +0000 |
commit | 8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch) | |
tree | 4099e8021376c7d8c05bdf8503093d80e9c7bad0 /examples/winexe | |
parent | Initial commit. (diff) | |
download | samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.tar.xz samba-8daa83a594a2e98f39d764422bfbdbc62c9efd44.zip |
Adding upstream version 2:4.20.0+dfsg.upstream/2%4.20.0+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'examples/winexe')
-rw-r--r-- | examples/winexe/README | 18 | ||||
-rw-r--r-- | examples/winexe/winexe.c | 1918 | ||||
-rw-r--r-- | examples/winexe/winexesvc.c | 745 | ||||
-rw-r--r-- | examples/winexe/winexesvc.h | 42 | ||||
-rw-r--r-- | examples/winexe/wscript | 31 | ||||
-rw-r--r-- | examples/winexe/wscript_build | 118 |
6 files changed, 2872 insertions, 0 deletions
diff --git a/examples/winexe/README b/examples/winexe/README new file mode 100644 index 0000000..c688e5d --- /dev/null +++ b/examples/winexe/README @@ -0,0 +1,18 @@ +winexe from https://sourceforge.net/projects/winexe/ is a project +based on Samba libraries from 2012. According to the winexe git +repository the last Samba commit winexe was updated to is 47bbf9886f0c +from November 6, 2012. As winexe uses unpublished Samba internal +libraries, it broke over time. + +This is a port of the winexe functionality to more modern Samba +versions. It still uses internal APIs, but it being part of the tree +means that it is much easier to keep up to date. + +The Windows service files were taken literally from the original +winexe from the sourceforge git. Andrzej Hajda chose GPLv3 only and +not GPLv3+. As GPL evolves very slowly, this should not be a practical +problem for quite some time. + +To build it under Linux, you need mingw binaries on your build +system. Under Debian stretch, the package names are gcc-mingw-w64 and +friends. diff --git a/examples/winexe/winexe.c b/examples/winexe/winexe.c new file mode 100644 index 0000000..5c2529c --- /dev/null +++ b/examples/winexe/winexe.c @@ -0,0 +1,1918 @@ +/* + * Samba Unix/Linux CIFS implementation + * + * winexe + * + * Copyright (C) 2018 Volker Lendecke <vl@samba.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "includes.h" +#include "version.h" +#include <popt.h> +#include <tevent.h> +#include "lib/param/param.h" +#include "auth/credentials/credentials.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/tevent_ntstatus.h" +#include "lib/util/sys_rw.h" +#include "libsmb/proto.h" +#include "librpc/gen_ndr/ndr_svcctl_c.h" +#include "rpc_client/cli_pipe.h" +#include "libcli/smb/smbXcli_base.h" +#include "libcli/util/werror.h" +#include "lib/async_req/async_sock.h" +#include "lib/cmdline/cmdline.h" +#include "client.h" + +#define SVC_INTERACTIVE 1 +#define SVC_IGNORE_INTERACTIVE 2 +#define SVC_INTERACTIVE_MASK 3 +#define SVC_FORCE_UPLOAD 4 +#define SVC_OS64BIT 8 +#define SVC_OSCHOOSE 16 +#define SVC_UNINSTALL 32 +#define SVC_SYSTEM 64 + +#define SERVICE_NAME "winexesvc" + +#define PIPE_NAME "ahexec" +#define PIPE_NAME_IN "ahexec_stdin%08X" +#define PIPE_NAME_OUT "ahexec_stdout%08X" +#define PIPE_NAME_ERR "ahexec_stderr%08X" + +static const char version_message_fmt[] = "winexe version %d.%d\n" + "This program may be freely redistributed under the terms of the " + "GNU GPLv3\n"; + +struct program_options { + char *hostname; + int port; + char *cmd; + struct cli_credentials *credentials; + char *runas; + char *runas_file; + int flags; +}; + +static void parse_args(int argc, const char *argv[], + TALLOC_CTX *mem_ctx, + struct program_options *options) +{ + poptContext pc; + int opt, i; + + int argc_new; + char **argv_new; + + int port = 445; + char *port_str = NULL; + + int flag_interactive = SVC_IGNORE_INTERACTIVE; + int flag_ostype = 2; + int flag_reinstall = 0; + int flag_uninstall = 0; + int flag_help = 0; + int flag_version = 0; + bool ok; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { + .longName = "uninstall", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &flag_uninstall, + .val = 0, + .descrip = "Uninstall winexe service after " + "remote execution", + .argDescrip = NULL, + },{ + .longName = "reinstall", + .shortName = 0, + .argInfo = POPT_ARG_NONE, + .arg = &flag_reinstall, + .val = 0, + .descrip = "Reinstall winexe service before " + "remote execution", + .argDescrip = NULL, + },{ + .longName = "runas", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &options->runas, + .val = 0, + .descrip = "Run as the given user (BEWARE: this " + "password is sent in cleartext over " + "the network!)", + .argDescrip = "[DOMAIN\\]USERNAME%PASSWORD", + },{ + .longName = "runas-file", + .shortName = 0, + .argInfo = POPT_ARG_STRING, + .arg = &options->runas_file, + .val = 0, + .descrip = "Run as user options defined in a file", + .argDescrip = "FILE", + },{ + .longName = "interactive", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &flag_interactive, + .val = 0, + .descrip = "Desktop interaction: 0 - disallow, " + "1 - allow. If allow, also use the " + "--system switch (Windows requirement). " + "Vista does not support this option.", + .argDescrip = "0|1", + },{ + .longName = "ostype", + .shortName = 0, + .argInfo = POPT_ARG_INT, + .arg = &flag_ostype, + .val = 0, + .descrip = "OS type: 0 - 32-bit, 1 - 64-bit, " + "2 - winexe will decide. " + "Determines which version (32-bit or 64-bit)" + " of service will be installed.", + .argDescrip = "0|1|2", + }, + POPT_COMMON_SAMBA + POPT_COMMON_CREDENTIALS + POPT_COMMON_VERSION + POPT_TABLEEND + }; + + ZERO_STRUCTP(options); + + ok = samba_cmdline_init(mem_ctx, + SAMBA_CMDLINE_CONFIG_CLIENT, + false /* require_smbconf */); + if (!ok) { + DBG_ERR("Failed to init cmdline parser!\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + + pc = samba_popt_get_context(getprogname(), + argc, + argv, + long_options, + 0); + if (pc == NULL) { + DBG_ERR("Failed to setup popt context!\n"); + TALLOC_FREE(mem_ctx); + exit(1); + } + + poptSetOtherOptionHelp(pc, "[OPTION]... //HOST[:PORT] COMMAND\nOptions:"); + + if (((opt = poptGetNextOpt(pc)) != -1) || flag_help || flag_version) { + fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR, + SAMBA_VERSION_MINOR); + if (flag_version) { + exit(0); + } + poptPrintHelp(pc, stdout, 0); + if (flag_help) { + exit(0); + } + exit(1); + } + + argv_new = discard_const_p(char *, poptGetArgs(pc)); + + argc_new = argc; + for (i = 0; i < argc; i++) { + if (!argv_new || argv_new[i] == NULL) { + argc_new = i; + break; + } + } + + if (argc_new != 2 || argv_new[0][0] != '/' || argv_new[0][1] != '/') { + fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR, + SAMBA_VERSION_MINOR); + poptPrintHelp(pc, stdout, 0); + exit(1); + } + + port_str = strchr(argv_new[0], ':'); + if (port_str) { + if (sscanf(port_str + 1, "%d", &port) != 1 || port <= 0) { + fprintf(stderr, version_message_fmt, + SAMBA_VERSION_MAJOR, SAMBA_VERSION_MINOR); + poptPrintHelp(pc, stdout, 0); + exit(1); + } + *port_str = '\0'; + } + + if (options->runas == NULL && options->runas_file != NULL) { + struct cli_credentials *runas_cred; + const char *user; + const char *pass; + + runas_cred = cli_credentials_init(mem_ctx); + cli_credentials_parse_file(runas_cred, options->runas_file, + CRED_SPECIFIED); + + user = cli_credentials_get_username(runas_cred); + pass = cli_credentials_get_password(runas_cred); + + if (user && pass) { + char buffer[1024]; + const char *dom; + + dom = cli_credentials_get_domain(runas_cred); + if (dom) { + snprintf(buffer, sizeof(buffer), "%s\\%s%%%s", + dom, user, pass); + } else { + snprintf(buffer, sizeof(buffer), "%s%%%s", + user, pass); + } + buffer[sizeof(buffer)-1] = '\0'; + options->runas = talloc_strdup(mem_ctx, buffer); + } + } + + options->credentials = samba_cmdline_get_creds(); + + options->hostname = talloc_strdup(mem_ctx, argv_new[0] + 2); + if (options->hostname == NULL) { + DBG_ERR("Out of memory\n"); + exit(1); + } + options->port = port; + options->cmd = talloc_strdup(mem_ctx, argv_new[1]); + if (options->cmd == NULL) { + DBG_ERR("Out of memory\n"); + exit(1); + } + + poptFreeContext(pc); + + options->flags = flag_interactive; + if (flag_reinstall) { + options->flags |= SVC_FORCE_UPLOAD; + } + if (flag_ostype == 1) { + options->flags |= SVC_OS64BIT; + } + if (flag_ostype == 2) { + options->flags |= SVC_OSCHOOSE; + } + if (flag_uninstall) { + options->flags |= SVC_UNINSTALL; + } +} + +static NTSTATUS winexe_svc_upload( + const char *hostname, + int port, + const char *service_filename, + const DATA_BLOB *svc32_exe, + const DATA_BLOB *svc64_exe, + struct cli_credentials *credentials, + int flags) +{ + struct cli_state *cli; + uint16_t fnum = 0xffff; + NTSTATUS status; + const DATA_BLOB *binary = NULL; + + status = cli_full_connection_creds( + &cli, + NULL, + hostname, + NULL, + port, + "ADMIN$", + "?????", + credentials, + 0); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("cli_full_connection_creds failed: %s\n", + nt_errstr(status)); + return status; + } + + if (flags & SVC_FORCE_UPLOAD) { + status = cli_unlink(cli, service_filename, 0); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("cli_unlink failed: %s\n", + nt_errstr(status)); + } + } + + if (flags & SVC_OSCHOOSE) { + status = cli_chkpath(cli, "SysWoW64"); + if (NT_STATUS_IS_OK(status)) { + flags |= SVC_OS64BIT; + } + } + + if (flags & SVC_OS64BIT) { + binary = svc64_exe; + } else { + binary = svc32_exe; + } + + if (binary == NULL) { + goto done; + } + + status = cli_ntcreate( + cli, + service_filename, + 0, /* CreatFlags */ + SEC_FILE_WRITE_DATA, /* DesiredAccess */ + FILE_ATTRIBUTE_NORMAL, /* FileAttributes */ + FILE_SHARE_WRITE|FILE_SHARE_READ, /* ShareAccess */ + FILE_OPEN_IF, /* CreateDisposition */ + FILE_NON_DIRECTORY_FILE, /* CreateOptions */ + 0, /* SecurityFlags */ + &fnum, + NULL); /* CreateReturns */ + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("Could not create %s: %s\n", service_filename, + nt_errstr(status)); + goto done; + } + + status = cli_writeall( + cli, + fnum, + 0, + binary->data, + 0, + binary->length, + NULL); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("Could not write file: %s\n", nt_errstr(status)); + goto done; + } + +done: + if (fnum != 0xffff) { + status = cli_close(cli, fnum); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("Close(%"PRIu16") failed for %s: %s\n", + fnum, + service_filename, + nt_errstr(status)); + } + } + + TALLOC_FREE(cli); + return status; +} + +static NTSTATUS winexe_svc_install( + struct cli_state *cli, + const char *hostname, + int port, + const char *service_name, + const char *service_filename, + const DATA_BLOB *svc32_exe, + const DATA_BLOB *svc64_exe, + struct cli_credentials *credentials, + int flags) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct rpc_pipe_client *rpccli; + struct policy_handle scmanager_handle; + struct policy_handle service_handle; + struct SERVICE_STATUS service_status; + bool need_start = false; + bool need_conf = false; + NTSTATUS status; + WERROR werr; + const char *remote_name = smbXcli_conn_remote_name(cli->conn); + const struct sockaddr_storage *remote_sockaddr = + smbXcli_conn_remote_sockaddr(cli->conn); + + status = cli_rpc_pipe_open_noauth_transport( + cli, + NCACN_NP, + &ndr_table_svcctl, + remote_name, + remote_sockaddr, + &rpccli); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = dcerpc_svcctl_OpenSCManagerW( + rpccli->binding_handle, + frame, + remote_name, + NULL, + SEC_FLAG_MAXIMUM_ALLOWED, + &scmanager_handle, + &werr); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n", + win_errstr(werr)); + goto done; + } + + status = dcerpc_svcctl_OpenServiceW( + rpccli->binding_handle, + frame, + &scmanager_handle, + service_name, + SERVICE_ALL_ACCESS, + &service_handle, + &werr); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n", + nt_errstr(status)); + goto close_scmanager; + } + + if (W_ERROR_EQUAL(werr, WERR_SERVICE_DOES_NOT_EXIST)) { + status = dcerpc_svcctl_CreateServiceW( + rpccli->binding_handle, + frame, + &scmanager_handle, + service_name, + NULL, + SERVICE_ALL_ACCESS, + SERVICE_TYPE_WIN32_OWN_PROCESS | + ((flags & SVC_INTERACTIVE) ? + SERVICE_TYPE_INTERACTIVE_PROCESS : 0), + SVCCTL_DEMAND_START, + SVCCTL_SVC_ERROR_NORMAL, + service_filename, + NULL, + NULL, + NULL, + 0, + NULL, + NULL, + 0, + &service_handle, + &werr); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_CreateServiceW " + "failed: %s\n", nt_errstr(status)); + goto close_scmanager; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_CreateServiceW " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_scmanager; + } + } + + status = dcerpc_svcctl_QueryServiceStatus( + rpccli->binding_handle, + frame, + &service_handle, + &service_status, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + + if (!(flags & SVC_IGNORE_INTERACTIVE)) { + need_conf = + !(service_status.type & + SERVICE_TYPE_INTERACTIVE_PROCESS) ^ + !(flags & SVC_INTERACTIVE); + } + + if (service_status.state == SVCCTL_STOPPED) { + need_start = true; + } else if (need_conf) { + status = dcerpc_svcctl_ControlService( + rpccli->binding_handle, + frame, + &service_handle, + SVCCTL_CONTROL_STOP, + &service_status, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_ControlServiceStatus " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_ControlServiceStatus " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + + do { + smb_msleep(100); + + status = dcerpc_svcctl_QueryServiceStatus( + rpccli->binding_handle, + frame, + &service_handle, + &service_status, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + } while (service_status.state == SVCCTL_STOP_PENDING); + + need_start = 1; + } + + if (need_conf) { + status = dcerpc_svcctl_ChangeServiceConfigW( + rpccli->binding_handle, + frame, + &service_handle, + SERVICE_TYPE_WIN32_OWN_PROCESS | + ((flags & SVC_INTERACTIVE) ? + SERVICE_TYPE_INTERACTIVE_PROCESS : 0), /* type */ + UINT32_MAX, /* start_type, SERVICE_NO_CHANGE */ + UINT32_MAX, /* error_control, SERVICE_NO_CHANGE */ + NULL, /* binary_path */ + NULL, /* load_order_group */ + NULL, /* tag_id */ + NULL, /* dependencies */ + 0, /* dwDependSize */ + NULL, /* service_start_name */ + NULL, /* password */ + 0, /* dwPwSize */ + NULL, /* display_name */ + &werr); + + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + } + + if (need_start) { + status = winexe_svc_upload( + hostname, + port, + service_filename, + svc32_exe, + svc64_exe, + credentials, + flags); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("winexe_svc_upload failed: %s\n", + nt_errstr(status)); + goto close_service; + } + + status = dcerpc_svcctl_StartServiceW( + rpccli->binding_handle, + frame, + &service_handle, + 0, /* num_args */ + NULL, /* arguments */ + &werr); + + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_StartServiceW " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_StartServiceW " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + + do { + smb_msleep(100); + + status = dcerpc_svcctl_QueryServiceStatus( + rpccli->binding_handle, + frame, + &service_handle, + &service_status, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + } while (service_status.state == SVCCTL_START_PENDING); + + if (service_status.state != SVCCTL_RUNNING) { + DBG_WARNING("Failed to start service\n"); + status = NT_STATUS_UNSUCCESSFUL; + goto close_service; + } + } + +close_service: + { + NTSTATUS close_status; + WERROR close_werr; + + close_status = dcerpc_svcctl_CloseServiceHandle( + rpccli->binding_handle, + frame, + &service_handle, + &close_werr); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle " + "failed: %s\n", nt_errstr(close_status)); + goto done; + } + if (!W_ERROR_IS_OK(close_werr)) { + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle " + " failed: %s\n", win_errstr(close_werr)); + goto done; + } + } + +close_scmanager: + { + NTSTATUS close_status; + WERROR close_werr; + + close_status = dcerpc_svcctl_CloseServiceHandle( + rpccli->binding_handle, + frame, + &scmanager_handle, + &close_werr); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle " + "failed: %s\n", nt_errstr(close_status)); + goto done; + } + if (!W_ERROR_IS_OK(close_werr)) { + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle " + " failed: %s\n", win_errstr(close_werr)); + goto done; + } + } + +done: + TALLOC_FREE(rpccli); + TALLOC_FREE(frame); + return status; +} + +static NTSTATUS winexe_svc_uninstall( + struct cli_state *cli, + const char *service_name) +{ + TALLOC_CTX *frame = talloc_stackframe(); + struct rpc_pipe_client *rpccli; + struct policy_handle scmanager_handle; + struct policy_handle service_handle; + struct SERVICE_STATUS service_status; + NTSTATUS status; + WERROR werr; + const char *remote_name = smbXcli_conn_remote_name(cli->conn); + const struct sockaddr_storage *remote_sockaddr = + smbXcli_conn_remote_sockaddr(cli->conn); + + status = cli_rpc_pipe_open_noauth_transport( + cli, + NCACN_NP, + &ndr_table_svcctl, + remote_name, + remote_sockaddr, + &rpccli); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = dcerpc_svcctl_OpenSCManagerW( + rpccli->binding_handle, + frame, + remote_name, + NULL, + SEC_FLAG_MAXIMUM_ALLOWED, + &scmanager_handle, + &werr); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n", + nt_errstr(status)); + goto done; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n", + win_errstr(werr)); + goto done; + } + + status = dcerpc_svcctl_OpenServiceW( + rpccli->binding_handle, + frame, + &scmanager_handle, + service_name, + SERVICE_ALL_ACCESS, + &service_handle, + &werr); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n", + nt_errstr(status)); + goto close_scmanager; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n", + win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_scmanager; + } + + status = dcerpc_svcctl_ControlService( + rpccli->binding_handle, + frame, + &service_handle, + SVCCTL_CONTROL_STOP, + &service_status, + &werr); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_ControlServiceStatus " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_ControlServiceStatus " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + + do { + smb_msleep(100); + + status = dcerpc_svcctl_QueryServiceStatus( + rpccli->binding_handle, + frame, + &service_handle, + &service_status, + &werr); + + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + } while (service_status.state != SVCCTL_STOPPED); + + status = dcerpc_svcctl_DeleteService( + rpccli->binding_handle, + frame, + &service_handle, + &werr); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("dcerpc_svcctl_DeleteService " + "failed: %s\n", nt_errstr(status)); + goto close_service; + } + if (!W_ERROR_IS_OK(werr)) { + DBG_WARNING("dcerpc_svcctl_DeleteService " + "failed: %s\n", win_errstr(werr)); + status = werror_to_ntstatus(werr); + goto close_service; + } + +close_service: + { + NTSTATUS close_status; + WERROR close_werr; + + close_status = dcerpc_svcctl_CloseServiceHandle( + rpccli->binding_handle, + frame, + &service_handle, + &close_werr); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle " + "failed: %s\n", nt_errstr(close_status)); + goto done; + } + if (!W_ERROR_IS_OK(close_werr)) { + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle " + " failed: %s\n", win_errstr(close_werr)); + goto done; + } + } + +close_scmanager: + { + NTSTATUS close_status; + WERROR close_werr; + + close_status = dcerpc_svcctl_CloseServiceHandle( + rpccli->binding_handle, + frame, + &scmanager_handle, + &close_werr); + if (!NT_STATUS_IS_OK(close_status)) { + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle " + "failed: %s\n", nt_errstr(close_status)); + goto done; + } + if (!W_ERROR_IS_OK(close_werr)) { + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle " + " failed: %s\n", win_errstr(close_werr)); + goto done; + } + } + +done: + TALLOC_FREE(rpccli); + TALLOC_FREE(frame); + return status; +} + +struct winexe_out_pipe_state { + struct tevent_context *ev; + struct cli_state *cli; + uint16_t out_pipe; + int out_fd; + char out_inbuf[256]; +}; + +static void winexe_out_pipe_opened(struct tevent_req *subreq); +static void winexe_out_pipe_got_data(struct tevent_req *subreq); +static void winexe_out_pipe_closed(struct tevent_req *subreq); + +static struct tevent_req *winexe_out_pipe_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *pipe_name, + int out_fd) +{ + struct tevent_req *req, *subreq; + struct winexe_out_pipe_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct winexe_out_pipe_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->out_fd = out_fd; + + subreq = cli_ntcreate_send( + state, + state->ev, + state->cli, + pipe_name, + 0, + SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE| + SEC_RIGHTS_FILE_EXECUTE, + 0, /* FileAttributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, /* CreateDisposition */ + 0, /* CreateOptions */ + SMB2_IMPERSONATION_IMPERSONATION, + 0); /* SecurityFlags */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, winexe_out_pipe_opened, req); + return req; +} + +static void winexe_out_pipe_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_out_pipe_state *state = tevent_req_data( + req, struct winexe_out_pipe_state); + int timeout; + NTSTATUS status; + + status = cli_ntcreate_recv(subreq, &state->out_pipe, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + timeout = state->cli->timeout; + state->cli->timeout = 0; + + subreq = cli_read_send( + state, + state->ev, + state->cli, + state->out_pipe, + state->out_inbuf, + 0, + sizeof(state->out_inbuf)); + + state->cli->timeout = timeout; + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req); +} + +static void winexe_out_pipe_got_data(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_out_pipe_state *state = tevent_req_data( + req, struct winexe_out_pipe_state); + NTSTATUS status; + int timeout; + size_t received; + ssize_t written; + + status = cli_read_recv(subreq, &received); + TALLOC_FREE(subreq); + + DBG_DEBUG("cli_read for %d gave %s\n", + state->out_fd, + nt_errstr(status)); + + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) { + subreq = cli_close_send(state, + state->ev, + state->cli, + state->out_pipe, + 0); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_out_pipe_closed, req); + return; + } + + if (tevent_req_nterror(req, status)) { + return; + } + + if (received > 0) { + written = sys_write(state->out_fd, state->out_inbuf, received); + if (written == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return; + } + } + + timeout = state->cli->timeout; + state->cli->timeout = 0; + + subreq = cli_read_send( + state, + state->ev, + state->cli, + state->out_pipe, + state->out_inbuf, + 0, + sizeof(state->out_inbuf)); + + state->cli->timeout = timeout; + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req); +} + +static void winexe_out_pipe_closed(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_close_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + tevent_req_done(req); +} + +static NTSTATUS winexe_out_pipe_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct winexe_in_pipe_state { + struct tevent_context *ev; + struct cli_state *cli; + struct tevent_req *fd_read_req; + bool close_requested; + bool closing; + uint16_t in_pipe; + int in_fd; + char inbuf[256]; +}; + +static void winexe_in_pipe_opened(struct tevent_req *subreq); +static void winexe_in_pipe_got_data(struct tevent_req *subreq); +static void winexe_in_pipe_written(struct tevent_req *subreq); +static void winexe_in_pipe_closed(struct tevent_req *subreq); + +static struct tevent_req *winexe_in_pipe_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *pipe_name, + int in_fd) +{ + struct tevent_req *req, *subreq; + struct winexe_in_pipe_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct winexe_in_pipe_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + state->in_fd = in_fd; + + subreq = cli_ntcreate_send( + state, + state->ev, + state->cli, + pipe_name, + 0, + SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE| + SEC_RIGHTS_FILE_EXECUTE, + 0, /* FileAttributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, /* CreateDisposition */ + 0, /* CreateOptions */ + SMB2_IMPERSONATION_IMPERSONATION, + 0); /* SecurityFlags */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, winexe_in_pipe_opened, req); + return req; +} + +static void winexe_in_pipe_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_in_pipe_state *state = tevent_req_data( + req, struct winexe_in_pipe_state); + NTSTATUS status; + + status = cli_ntcreate_recv(subreq, &state->in_pipe, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = wait_for_read_send( + state, + state->ev, + state->in_fd, + true); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req); + + state->fd_read_req = subreq; +} + +static void winexe_in_pipe_got_data(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_in_pipe_state *state = tevent_req_data( + req, struct winexe_in_pipe_state); + int err; + bool ok; + int timeout; + ssize_t nread; + + ok = wait_for_read_recv(subreq, &err); + TALLOC_FREE(subreq); + if (!ok) { + tevent_req_nterror(req, map_nt_error_from_unix(err)); + return; + } + state->fd_read_req = NULL; + + nread = sys_read(state->in_fd, &state->inbuf, sizeof(state->inbuf)); + if (nread == -1) { + tevent_req_nterror(req, map_nt_error_from_unix(errno)); + return; + } + if (nread == 0) { + tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED); + return; + } + + timeout = state->cli->timeout; + state->cli->timeout = 0; + + subreq = cli_writeall_send( + state, + state->ev, + state->cli, + state->in_pipe, + 0, + (uint8_t *)state->inbuf, + 0, + nread); + + state->cli->timeout = timeout; + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_in_pipe_written, req); +} + +static void winexe_in_pipe_written(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_in_pipe_state *state = tevent_req_data( + req, struct winexe_in_pipe_state); + NTSTATUS status; + + status = cli_writeall_recv(subreq, NULL); + TALLOC_FREE(subreq); + + DBG_DEBUG("cli_writeall for %d gave %s\n", + state->in_fd, + nt_errstr(status)); + + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED) || + state->close_requested) { + subreq = cli_close_send(state, + state->ev, + state->cli, + state->in_pipe, + 0); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_in_pipe_closed, req); + state->closing = true; + return; + } + + if (tevent_req_nterror(req, status)) { + return; + } + + subreq = wait_for_read_send( + state, + state->ev, + state->in_fd, + true); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req); + + state->fd_read_req = subreq; +} + +static void winexe_in_pipe_closed(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_close_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + return tevent_req_done(req); +} + +static NTSTATUS winexe_in_pipe_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +static bool winexe_in_pipe_close(struct tevent_req *req) +{ + struct winexe_in_pipe_state *state = tevent_req_data( + req, struct winexe_in_pipe_state); + struct tevent_req *subreq; + + if (state->closing) { + return true; + } + + if (state->fd_read_req == NULL) { + /* + * cli_writeall active, wait for it to return + */ + state->close_requested = true; + return true; + } + + TALLOC_FREE(state->fd_read_req); + + subreq = + cli_close_send(state, state->ev, state->cli, state->in_pipe, 0); + if (subreq == NULL) { + return false; + } + tevent_req_set_callback(subreq, winexe_in_pipe_closed, req); + state->closing = true; + + return true; +} + +struct winexe_pipes_state { + struct tevent_req *pipes[3]; +}; + +static void winexe_pipes_stdin_done(struct tevent_req *subreq); +static void winexe_pipes_stdout_done(struct tevent_req *subreq); +static void winexe_pipes_stderr_done(struct tevent_req *subreq); + +static struct tevent_req *winexe_pipes_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *pipe_postfix) +{ + struct tevent_req *req; + struct winexe_pipes_state *state; + char *pipe_name; + + req = tevent_req_create(mem_ctx, &state, struct winexe_pipes_state); + if (req == NULL) { + return NULL; + } + + pipe_name = talloc_asprintf(state, "\\ahexec_stdin%s", pipe_postfix); + if (tevent_req_nomem(pipe_name, req)) { + return tevent_req_post(req, ev); + } + state->pipes[0] = winexe_in_pipe_send( + state, + ev, + cli, + pipe_name, + 0); + if (tevent_req_nomem(state->pipes[0], req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->pipes[0], winexe_pipes_stdin_done, req); + + pipe_name = talloc_asprintf(state, "\\ahexec_stdout%s", pipe_postfix); + if (tevent_req_nomem(pipe_name, req)) { + return tevent_req_post(req, ev); + } + state->pipes[1] = winexe_out_pipe_send( + state, + ev, + cli, + pipe_name, + 1); + if (tevent_req_nomem(state->pipes[1], req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->pipes[1], winexe_pipes_stdout_done, + req); + + pipe_name = talloc_asprintf(state, "\\ahexec_stderr%s", pipe_postfix); + if (tevent_req_nomem(pipe_name, req)) { + return tevent_req_post(req, ev); + } + state->pipes[2] = winexe_out_pipe_send( + state, + ev, + cli, + pipe_name, + 2); + if (tevent_req_nomem(state->pipes[2], req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(state->pipes[2], winexe_pipes_stderr_done, + req); + + DBG_DEBUG("pipes = %p %p %p\n", + state->pipes[0], + state->pipes[1], + state->pipes[2]); + + return req; +} + +static void winexe_pipes_stdin_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_pipes_state *state = tevent_req_data( + req, struct winexe_pipes_state); + NTSTATUS status; + + status = winexe_in_pipe_recv(subreq); + TALLOC_FREE(subreq); + + DBG_DEBUG("stdin returned %s\n", nt_errstr(status)); + + if (tevent_req_nterror(req, status)) { + return; + } + + state->pipes[0] = NULL; + + DBG_DEBUG("pipes = %p %p %p\n", + state->pipes[0], + state->pipes[1], + state->pipes[2]); + + if ((state->pipes[1] == NULL) && (state->pipes[2] == NULL)) { + tevent_req_done(req); + } +} + +static void winexe_pipes_stdout_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_pipes_state *state = tevent_req_data( + req, struct winexe_pipes_state); + NTSTATUS status; + + status = winexe_out_pipe_recv(subreq); + TALLOC_FREE(subreq); + + DBG_DEBUG("stdout returned %s\n", nt_errstr(status)); + + if (tevent_req_nterror(req, status)) { + return; + } + + if (state->pipes[0] != NULL) { + winexe_in_pipe_close(state->pipes[0]); + } + + state->pipes[1] = NULL; + + DBG_DEBUG("pipes = %p %p %p\n", + state->pipes[0], + state->pipes[1], + state->pipes[2]); + + if ((state->pipes[0] == NULL) && (state->pipes[2] == NULL)) { + tevent_req_done(req); + } +} + +static void winexe_pipes_stderr_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_pipes_state *state = tevent_req_data( + req, struct winexe_pipes_state); + NTSTATUS status; + + status = winexe_out_pipe_recv(subreq); + TALLOC_FREE(subreq); + + DBG_DEBUG("stderr returned %s\n", nt_errstr(status)); + + if (tevent_req_nterror(req, status)) { + return; + } + + if (state->pipes[0] != NULL) { + winexe_in_pipe_close(state->pipes[0]); + } + + state->pipes[2] = NULL; + + DBG_DEBUG("pipes = %p %p %p\n", + state->pipes[0], + state->pipes[1], + state->pipes[2]); + + if ((state->pipes[0] == NULL) && (state->pipes[1] == NULL)) { + tevent_req_done(req); + } +} + +static NTSTATUS winexe_pipes_recv(struct tevent_req *req) +{ + return tevent_req_simple_recv_ntstatus(req); +} + +struct winexe_ctrl_state { + struct tevent_context *ev; + struct cli_state *cli; + + uint16_t ctrl_pipe; + bool ctrl_pipe_done; + + char ctrl_inbuf[256]; + char *cmd; + int return_code; + + struct tevent_req *pipes_req; +}; + +static void winexe_ctrl_opened(struct tevent_req *subreq); +static void winexe_ctrl_got_read(struct tevent_req *subreq); +static void winexe_ctrl_wrote_version(struct tevent_req *subreq); +static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq); +static void winexe_ctrl_pipes_done(struct tevent_req *subreq); +static void winexe_ctrl_pipe_closed(struct tevent_req *subreq); + +static struct tevent_req *winexe_ctrl_send( + TALLOC_CTX *mem_ctx, + struct tevent_context *ev, + struct cli_state *cli, + const char *cmd) +{ + struct tevent_req *req, *subreq; + struct winexe_ctrl_state *state; + + req = tevent_req_create(mem_ctx, &state, + struct winexe_ctrl_state); + if (req == NULL) { + return NULL; + } + state->ev = ev; + state->cli = cli; + + state->cmd = talloc_asprintf(state, "run %s\n", cmd); + if (tevent_req_nomem(state->cmd, req)) { + return tevent_req_post(req, ev); + } + + subreq = cli_ntcreate_send( + state, + state->ev, + state->cli, + "\\" PIPE_NAME, + 0, + SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE| + SEC_RIGHTS_FILE_EXECUTE, + 0, /* FileAttributes */ + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, + FILE_OPEN, /* CreateDisposition */ + 0, /* CreateOptions */ + SMB2_IMPERSONATION_IMPERSONATION, + 0); /* SecurityFlags */ + if (tevent_req_nomem(subreq, req)) { + return tevent_req_post(req, ev); + } + tevent_req_set_callback(subreq, winexe_ctrl_opened, req); + return req; +} + +static void winexe_ctrl_opened(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_ctrl_state *state = tevent_req_data( + req, struct winexe_ctrl_state); + int timeout; + NTSTATUS status; + static const char cmd[] = "get codepage\nget version\n"; + + status = cli_ntcreate_recv(subreq, &state->ctrl_pipe, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + timeout = state->cli->timeout; + state->cli->timeout = 0; + + subreq = cli_read_send( + state, + state->ev, + state->cli, + state->ctrl_pipe, + state->ctrl_inbuf, + 0, + sizeof(state->ctrl_inbuf)-1); + + state->cli->timeout = timeout; + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_ctrl_got_read, req); + + subreq = cli_writeall_send( + state, + state->ev, + state->cli, + state->ctrl_pipe, + 0, + (const uint8_t *)cmd, + 0, + strlen(cmd)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_ctrl_wrote_version, req); +} + +static void winexe_ctrl_got_read(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_ctrl_state *state = tevent_req_data( + req, struct winexe_ctrl_state); + NTSTATUS status; + int timeout; + size_t received; + unsigned int version, return_code; + int ret; + + status = cli_read_recv(subreq, &received); + TALLOC_FREE(subreq); + + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) { + subreq = cli_close_send(state, + state->ev, + state->cli, + state->ctrl_pipe, + 0); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_ctrl_pipe_closed, req); + return; + } + if (tevent_req_nterror(req, status)) { + return; + } + + DBG_DEBUG("Got %zu bytes\n", received); + + timeout = state->cli->timeout; + state->cli->timeout = 0; + + subreq = cli_read_send( + state, + state->ev, + state->cli, + state->ctrl_pipe, + state->ctrl_inbuf, + 0, + sizeof(state->ctrl_inbuf)-1); + + state->cli->timeout = timeout; + + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_ctrl_got_read, req); + + ret = sscanf(state->ctrl_inbuf, "version 0x%x\n", &version); + if (ret == 1) { + DBG_DEBUG("Got version %x\n", version); + + subreq = cli_writeall_send( + state, + state->ev, + state->cli, + state->ctrl_pipe, + 0, + (const uint8_t *)state->cmd, + 0, + strlen(state->cmd)); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_ctrl_wrote_cmd, req); + return; + } + + ret = strncmp(state->ctrl_inbuf, "std_io_err ", strlen("std_io_err ")); + if (ret == 0) { + char *p = state->ctrl_inbuf + 11; + char *q = strchr(state->ctrl_inbuf, '\n'); + char *postfix; + size_t postfix_len; + + if (q == NULL) { + DBG_DEBUG("Got invalid pipe postfix\n"); + return; + } + + postfix_len = q - p; + + postfix = talloc_strndup(state, p, postfix_len); + if (tevent_req_nomem(postfix, req)) { + return; + } + + DBG_DEBUG("Got pipe postfix %s\n", postfix); + + subreq = winexe_pipes_send( + state, + state->ev, + state->cli, + postfix); + if (tevent_req_nomem(subreq, req)) { + return; + } + tevent_req_set_callback(subreq, winexe_ctrl_pipes_done, req); + + state->pipes_req = subreq; + + return; + } + + ret = strncmp(state->ctrl_inbuf, "error ", strlen("error ")); + if (ret == 0) { + printf("Error: %s", state->ctrl_inbuf); + return; + } + + ret = sscanf(state->ctrl_inbuf, "return_code %x\n", &return_code); + if (ret == 1) { + state->return_code = return_code; + return; + } +} + +static void winexe_ctrl_wrote_version(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_writeall_recv(subreq, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } +} + +static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + NTSTATUS status; + + status = cli_writeall_recv(subreq, NULL); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } +} + +static void winexe_ctrl_pipe_closed(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_ctrl_state *state = tevent_req_data( + req, struct winexe_ctrl_state); + NTSTATUS status; + + status = cli_close_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->ctrl_pipe_done = true; + if (state->pipes_req == NULL) { + tevent_req_done(req); + } +} + +static void winexe_ctrl_pipes_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data( + subreq, struct tevent_req); + struct winexe_ctrl_state *state = tevent_req_data( + req, struct winexe_ctrl_state); + NTSTATUS status; + + status = winexe_pipes_recv(subreq); + TALLOC_FREE(subreq); + if (tevent_req_nterror(req, status)) { + return; + } + + state->pipes_req = NULL; + if (state->ctrl_pipe_done) { + tevent_req_done(req); + } +} + +static NTSTATUS winexe_ctrl_recv(struct tevent_req *req, + int *preturn_code) +{ + struct winexe_ctrl_state *state = tevent_req_data( + req, struct winexe_ctrl_state); + NTSTATUS status; + + if (tevent_req_is_nterror(req, &status)) { + return status; + } + if (preturn_code != NULL) { + *preturn_code = state->return_code; + } + return NT_STATUS_OK; +} + +static NTSTATUS winexe_ctrl(struct cli_state *cli, + const char *cmd, + int *preturn_code) +{ + struct tevent_context *ev = NULL; + struct tevent_req *req = NULL; + NTSTATUS status = NT_STATUS_NO_MEMORY; + bool ok; + + ev = samba_tevent_context_init(cli); + if (ev == NULL) { + goto done; + } + req = winexe_ctrl_send(ev, ev, cli, cmd); + if (req == NULL) { + goto done; + } + ok = tevent_req_poll_ntstatus(req, ev, &status); + if (!ok) { + goto done; + } + status = winexe_ctrl_recv(req, preturn_code); +done: + TALLOC_FREE(req); + TALLOC_FREE(ev); + return status; +} + +#ifdef HAVE_WINEXE_CC_WIN32 +const DATA_BLOB *winexesvc32_exe_binary(void); +#endif + +#ifdef HAVE_WINEXE_CC_WIN64 +const DATA_BLOB *winexesvc64_exe_binary(void); +#endif + +int main(int argc, char *argv[]) +{ + TALLOC_CTX *frame = talloc_stackframe(); + const char **const_argv = discard_const_p(const char *, argv); + struct program_options options = {0}; + struct cli_state *cli = NULL; + const char *service_name = SERVICE_NAME; + char *service_filename = NULL; +#ifdef HAVE_WINEXE_CC_WIN32 + const DATA_BLOB *winexesvc32_exe = winexesvc32_exe_binary(); +#else + const DATA_BLOB *winexesvc32_exe = NULL; +#endif +#ifdef HAVE_WINEXE_CC_WIN64 + const DATA_BLOB *winexesvc64_exe = winexesvc64_exe_binary(); +#else + const DATA_BLOB *winexesvc64_exe = NULL; +#endif + NTSTATUS status; + int ret = 1; + int return_code = 0; + + smb_init_locale(); + + parse_args(argc, const_argv, frame, &options); + + samba_cmdline_burn(argc, argv); + + if (options.cmd == NULL) { + fprintf(stderr, "no cmd given\n"); + goto done; + } + + service_filename = talloc_asprintf(frame, "%s.exe", service_name); + if (service_filename == NULL) { + DBG_WARNING("talloc_asprintf failed\n"); + goto done; + } + + status = cli_full_connection_creds( + &cli, + lp_netbios_name(), + options.hostname, + NULL, + options.port, + "IPC$", + "IPC", + options.credentials, + CLI_FULL_CONNECTION_IPC); + + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("cli_full_connection_creds failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = winexe_svc_install( + cli, + options.hostname, + options.port, + service_name, + service_filename, + winexesvc32_exe, + winexesvc64_exe, + options.credentials, + options.flags); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("winexe_svc_install failed: %s\n", + nt_errstr(status)); + goto done; + } + + status = winexe_ctrl(cli, options.cmd, &return_code); + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) { + /* Normal finish */ + status = NT_STATUS_OK; + } + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("cli_ctrl failed: %s\n", + nt_errstr(status)); + goto done; + } + + if (options.flags & SVC_UNINSTALL) { + status = winexe_svc_uninstall( + cli, + service_name); + if (!NT_STATUS_IS_OK(status)) { + DBG_WARNING("winexe_svc_uninstall failed: %s\n", + nt_errstr(status)); + goto done; + } + } + + ret = return_code; +done: + TALLOC_FREE(frame); + return ret; +} diff --git a/examples/winexe/winexesvc.c b/examples/winexe/winexesvc.c new file mode 100644 index 0000000..3d2ebcc --- /dev/null +++ b/examples/winexe/winexesvc.c @@ -0,0 +1,745 @@ +/* + * Copyright (C) Andrzej Hajda 2009-2013 + * Contact: andrzej.hajda@wp.pl + * + * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf + * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab. + * + * ** NOTE! The following "GPLv3 only" license applies to the winexe + * ** service files. This does NOT imply that all of Samba is released + * ** under the "GPLv3 only" license. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 3 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include <windows.h> +#include <aclapi.h> +#include <userenv.h> + +#include <stdio.h> +#include <string.h> +#include <stdarg.h> +#include <stdlib.h> + +#include "winexesvc.h" + +#define BUFSIZE 256 + +#if 0 +#define dbg(arg...) \ +({\ + FILE *f = fopen("C:\\" SERVICE_NAME ".log", "at");\ + if (f) {\ + fprintf(f, arg);\ + fclose(f);\ + }\ +}) +#else +#define dbg(arg...) +#endif + +static SECURITY_ATTRIBUTES sa; + +/* Creates SECURITY_ATTRIBUTES sa with full access for BUILTIN\Administrators */ +static int CreatePipesSA() +{ + DWORD dwRes; + PSID pAdminSID = NULL; + PACL pACL = NULL; + PSECURITY_DESCRIPTOR pSD = NULL; + EXPLICIT_ACCESS ea; + SID_IDENTIFIER_AUTHORITY SIDAuthNT = {SECURITY_NT_AUTHORITY}; + + /* Create a SID for the BUILTIN\Administrators group. */ + if ( + !AllocateAndInitializeSid( + &SIDAuthNT, 2, + SECURITY_BUILTIN_DOMAIN_RID, + DOMAIN_ALIAS_RID_ADMINS, + 0, 0, 0, 0, 0, 0, &pAdminSID + ) + ) { + dbg("AllocateAndInitializeSid Error %lu\n", GetLastError()); + return 0; + } + /* Initialize an EXPLICIT_ACCESS structure for an ACE. + The ACE will allow the Administrators group full access to the key. + */ + ea.grfAccessPermissions = FILE_ALL_ACCESS; + ea.grfAccessMode = SET_ACCESS; + ea.grfInheritance = NO_INHERITANCE; + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID; + ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP; + ea.Trustee.ptstrName = (LPTSTR) pAdminSID; + + /* Create a new ACL that contains the new ACEs */ + dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL); + if (ERROR_SUCCESS != dwRes) { + dbg("SetEntriesInAcl Error %lu\n", GetLastError()); + return 0; + } + /* Initialize a security descriptor */ + pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + if (NULL == pSD) { + dbg("LocalAlloc Error %lu\n", GetLastError()); + return 0; + } + + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) + { + dbg("InitializeSecurityDescriptor Error %lu\n", GetLastError()); + return 0; + } + /* Add the ACL to the security descriptor */ + if ( + !SetSecurityDescriptorDacl( + pSD, TRUE, /* bDaclPresent flag */ + pACL, FALSE /* not a default DACL */ + ) + ) { + dbg("SetSecurityDescriptorDacl Error %lu\n", GetLastError()); + return 0; + } + /* Initialize a security attributes structure */ + sa.nLength = sizeof(SECURITY_ATTRIBUTES); + sa.lpSecurityDescriptor = pSD; + sa.bInheritHandle = FALSE; + return 1; +} + +typedef struct { + HANDLE h; + OVERLAPPED o; +} OV_HANDLE; + +static int hgets(char *str, int n, OV_HANDLE *pipe) +{ + DWORD res; + DWORD count = 0; + --n; + while (--n >= 0) { + if (!ReadFile(pipe->h, str, 1, NULL, &pipe->o) && GetLastError() != ERROR_IO_PENDING) + goto finish; + if (!GetOverlappedResult(pipe->h, &pipe->o, &res, TRUE) || !res) + goto finish; + if (*str == '\n') + goto finish; + ++count; + ++str; + } +finish: + *str = 0; + return count; +} + +static int hprintf(OV_HANDLE *pipe, const char *fmt, ...) +{ + int res; + char buf[1024]; + va_list ap; + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + if (!WriteFile(pipe->h, buf, strlen(buf), NULL, &pipe->o) && GetLastError() == ERROR_IO_PENDING) + GetOverlappedResult(pipe->h, &pipe->o, (LPDWORD)&res, TRUE); + FlushFileBuffers(pipe->h); + return res; +} + +typedef struct { + OV_HANDLE *pipe; + const char *cmd; + HANDLE pin; + HANDLE pout; + HANDLE perr; + HANDLE token; + int implevel; + int system; + int profile; + char *runas; + int conn_number; +} connection_context; + +typedef int CMD_FUNC(connection_context *); + +typedef struct { + const char *name; + CMD_FUNC *func; +} CMD_ITEM; + +static int cmd_set(connection_context *c) +{ + static const char* var_system = "system"; + static const char* var_implevel = "implevel"; + static const char* var_runas = "runas"; + static const char* var_profile = "profile"; + char *cmdline; + int res = 0; + + cmdline = strchr(c->cmd, ' '); + if (!cmdline) { + goto finish; + } + ++cmdline; + int l; + if ((strstr(cmdline, var_system) == cmdline) && (cmdline[l = strlen(var_system)] == ' ')) { + c->system = atoi(cmdline + l + 1); + } else if ((strstr(cmdline, var_implevel) == cmdline) && (cmdline[l = strlen(var_implevel)] == ' ')) { + c->implevel = atoi(cmdline + l + 1); + } else if ((strstr(cmdline, var_profile) == cmdline) && (cmdline[l = strlen(var_profile)] == ' ')) { + c->profile = atoi(cmdline + l + 1); + } else if ((strstr(cmdline, var_runas) == cmdline) && (cmdline[l = strlen(var_runas)] == ' ')) { + c->runas = strdup(cmdline + l + 1); + } else { + hprintf(c->pipe, "error Unknown command (%s)\n", c->cmd); + goto finish; + } + res = 1; +finish: + return res; +} + +static int cmd_get(connection_context *c) +{ + static const char* var_version = "version"; + static const char* var_codepage = "codepage"; + char *cmdline; + int res = 0; + + cmdline = strchr(c->cmd, ' '); + if (!cmdline) { + goto finish; + } + ++cmdline; + int l; + if ((strstr(cmdline, var_version) == cmdline) + && (cmdline[l = strlen(var_version)] == 0)) { + hprintf(c->pipe, "version 0x%04X\n", VERSION); + } else if ((strstr(cmdline, var_codepage) == cmdline) + && (cmdline[l = strlen(var_codepage)] == 0)) { + hprintf(c->pipe, "codepage %d\n", GetOEMCP()); + } else { + hprintf(c->pipe, "error Unknown argument (%s)\n", c->cmd); + goto finish; + } + res = 1; +finish: + return res; +} + +typedef struct { + char *user; + char *domain; + char *password; +} credentials; + +static int prepare_credentials(char *str, credentials *crd) +{ + char *p; + p = strchr(str, '/'); + if (!p) p = strchr(str, '\\'); + if (p) { + *p++ = 0; + crd->domain = str; + } else { + p = str; + crd->domain = "."; + } + crd->user = p; + p = strchr(p, '%'); + if (p) + *p++ = 0; + crd->password = p; + return 1; +} + +static int get_token(connection_context *c) +{ + int res = 0; + int wres; + HANDLE token; + + if (c->runas) { + credentials crd; + if (!prepare_credentials(c->runas, &crd)) { + hprintf(c->pipe, "error Incorrect runas credentials\n"); + goto finish; + } + wres = LogonUser(crd.user, crd.domain, crd.password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &c->token); + if (!wres) { + hprintf(c->pipe, "error Cannot LogonUser(%s,%s,%s) %d\n", + crd.user, crd.domain, crd.password, GetLastError()); + goto finish; + } + res = 1; + goto finish; + } else if (c->system) { + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) { + hprintf(c->pipe, "error Cannot OpenProcessToken %d\n", GetLastError()); + goto finish; + } + } else { + if (!ImpersonateNamedPipeClient(c->pipe->h)) { + hprintf(c->pipe, "error Cannot ImpersonateNamedPipeClient %d\n", GetLastError()); + goto finish; + } + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &token)) { + hprintf(c->pipe, "error Cannot OpenThreadToken %d\n", GetLastError()); + goto finishRevertToSelf; + } + } + if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, 0, c->implevel, TokenPrimary, &c->token)) { + hprintf(c->pipe, "error Cannot Duplicate Token %d\n", GetLastError()); + goto finishCloseToken; + } + res = 1; +finishCloseToken: + CloseHandle(token); +finishRevertToSelf: + if (!c->system) { + if (!RevertToSelf()) { + hprintf(c->pipe, "error Cannot RevertToSelf %d\n", GetLastError()); + res = 0; + } + } +finish: + return res; +} + +static int load_user_profile(connection_context *c) +{ + PROFILEINFO pi = { .dwSize = sizeof(PROFILEINFO) }; + DWORD ulen = 256; + TCHAR username[ulen]; + + GetUserName(username, &ulen); + pi.lpUserName = username; + + return LoadUserProfile(c->token, &pi); +} + +static int cmd_run(connection_context *c) +{ + char buf[256]; + int res = 0; + char *cmdline; + DWORD pipe_nr; + + cmdline = strchr(c->cmd, ' '); + if (!cmdline) { + goto finish; + } + ++cmdline; + + if (!get_token(c)) + return 0; + + pipe_nr = (GetCurrentProcessId() << 16) + (DWORD) c->conn_number; + + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_IN, (unsigned int) pipe_nr); + c->pin = CreateNamedPipe(buf, + PIPE_ACCESS_DUPLEX, + PIPE_WAIT, + 1, + BUFSIZE, + BUFSIZE, + NMPWAIT_USE_DEFAULT_WAIT, + &sa); + if (c->pin == INVALID_HANDLE_VALUE) { + hprintf(c->pipe, "error Cannot create in pipe(%s), error 0x%08X\n", buf, GetLastError()); + goto finishCloseToken; + } + + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_OUT, (unsigned int) pipe_nr); + c->pout = CreateNamedPipe(buf, + PIPE_ACCESS_DUPLEX, + PIPE_WAIT, + 1, + BUFSIZE, + BUFSIZE, + NMPWAIT_USE_DEFAULT_WAIT, + &sa); + if (c->pout == INVALID_HANDLE_VALUE) { + hprintf(c->pipe, "error Cannot create out pipe(%s), error 0x%08X\n", buf, GetLastError()); + goto finishClosePin; + } + + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_ERR, (unsigned int) pipe_nr); + c->perr = CreateNamedPipe(buf, + PIPE_ACCESS_DUPLEX, + PIPE_WAIT, + 1, + BUFSIZE, + BUFSIZE, + NMPWAIT_USE_DEFAULT_WAIT, + &sa); + if (c->perr == INVALID_HANDLE_VALUE) { + hprintf(c->pipe, "error Cannot create err pipe(%s), error 0x%08x\n", buf, GetLastError()); + goto finishClosePout; + } + + /* Send handle to client (it will use it to connect pipes) */ + hprintf(c->pipe, CMD_STD_IO_ERR " %08X\n", pipe_nr); + + HANDLE ph[] = { c->pin, c->pout, c->perr }; + int i; + + for (i = 0; i < 3; ++i) { + if (ConnectNamedPipe(ph[i], NULL)) + continue; + int err = GetLastError(); + if (err != ERROR_PIPE_CONNECTED) { + hprintf(c->pipe, "error ConnectNamedPipe(pin) %d\n", err); + while (--i >= 0) + DisconnectNamedPipe(ph[i]); + goto finishClosePerr; + } + } + + SetHandleInformation(c->pin, HANDLE_FLAG_INHERIT, 1); + SetHandleInformation(c->pout, HANDLE_FLAG_INHERIT, 1); + SetHandleInformation(c->perr, HANDLE_FLAG_INHERIT, 1); + + if (c->profile) + load_user_profile(c); + + PROCESS_INFORMATION pi; + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); + + STARTUPINFO si; + ZeroMemory(&si, sizeof(STARTUPINFO)); + si.cb = sizeof(STARTUPINFO); + si.hStdInput = c->pin; + si.hStdOutput = c->pout; + si.hStdError = c->perr; + si.dwFlags |= STARTF_USESTDHANDLES; + + if (CreateProcessAsUser( + c->token, + NULL, + cmdline, /* command line */ + NULL, /* process security attributes */ + NULL, /* primary thread security attributes */ + TRUE, /* handles are inherited */ + 0, /* creation flags */ + NULL, /* use parent's environment */ + NULL, /* use parent's current directory */ + &si, /* STARTUPINFO pointer */ + &pi) /* receives PROCESS_INFORMATION */ + ) { + HANDLE hlist[2] = {c->pipe->o.hEvent, pi.hProcess}; + DWORD ec; + char str[1]; + + if (!ResetEvent(c->pipe->o.hEvent)) + dbg("ResetEvent error - %lu\n", GetLastError()); + if (!ReadFile(c->pipe->h, str, 1, NULL, &c->pipe->o) && GetLastError() != ERROR_IO_PENDING) + dbg("ReadFile(control_pipe) error - %lu\n", GetLastError()); + ec = WaitForMultipleObjects(2, hlist, FALSE, INFINITE); + dbg("WaitForMultipleObjects=%lu\n", ec - WAIT_OBJECT_0); + if (ec != WAIT_OBJECT_0) + GetExitCodeProcess(pi.hProcess, &ec); + else + TerminateProcess(pi.hProcess, ec = 0x1234); + FlushFileBuffers(c->pout); + FlushFileBuffers(c->perr); + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + hprintf(c->pipe, CMD_RETURN_CODE " %08X\n", ec); + } else { + hprintf(c->pipe, "error Creating process(%s) %d\n", cmdline, GetLastError()); + } + + DisconnectNamedPipe(c->perr); + DisconnectNamedPipe(c->pout); + DisconnectNamedPipe(c->pin); +finishClosePerr: + CloseHandle(c->perr); +finishClosePout: + CloseHandle(c->pout); +finishClosePin: + CloseHandle(c->pin); +finishCloseToken: + CloseHandle(c->token); +finish: + return res; +} + +static CMD_ITEM cmd_table[] = { + {"run", cmd_run}, + {"set", cmd_set}, + {"get", cmd_get}, + {NULL, NULL} +}; + +typedef struct { + OV_HANDLE *pipe; + int conn_number; +} connection_data; + +#define MAX_COMMAND_LENGTH (32768) + +static VOID handle_connection(connection_data *data) +{ + char *cmd = 0; + int res; + connection_context _c, *c = &_c; + cmd = malloc(MAX_COMMAND_LENGTH); + if (!cmd) { + hprintf(data->pipe, + "error: unable to allocate buffer for command\n"); + return; + } + ZeroMemory(cmd, MAX_COMMAND_LENGTH); + ZeroMemory(c, sizeof(connection_context)); + c->pipe = data->pipe; + c->cmd = cmd; + c->conn_number = data->conn_number; + free(data); + /* FIXME make wait for end of process or ctrl_pipe input */ + while (1) { + res = hgets(cmd, MAX_COMMAND_LENGTH, c->pipe); + if (res <= 0) { + dbg("Error reading from pipe(%p)\n", c->pipe->h); + goto finish; + } + dbg("Retrieved line: \"%s\"\n", cmd); + CMD_ITEM *ci; + for (ci = cmd_table; ci->name; ++ci) { + if (strstr(cmd, ci->name) != cmd) + continue; + char c = cmd[strlen(ci->name)]; + if (!c || (c == ' ')) + break; + } + if (ci->name) { + if (!ci->func(c)) + goto finish; + } else { + hprintf(c->pipe, "error Ignoring unknown command (%s)\n", cmd); + } + } +finish: + FlushFileBuffers(c->pipe->h); + DisconnectNamedPipe(c->pipe->h); + CloseHandle(c->pipe->h); + CloseHandle(c->pipe->o.hEvent); + free(c->pipe); + free(cmd); +} + +static int conn_number = 0; + +DWORD WINAPI winexesvc_loop(LPVOID lpParameter) +{ + BOOL res; + + dbg("server_loop: alive\n"); + if (!CreatePipesSA()) { + dbg("CreatePipesSA failed (%08lX)\n", GetLastError()); + return -1; + } + dbg("server_loop: CreatePipesSA done\n"); + for (;;) { + dbg("server_loop: Create Pipe\n"); + OV_HANDLE *pipe; + pipe = (OV_HANDLE *)malloc(sizeof(OV_HANDLE)); + ZeroMemory(&pipe->o, sizeof(OVERLAPPED)); + pipe->o.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + pipe->h = CreateNamedPipe("\\\\.\\pipe\\" PIPE_NAME, + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, + BUFSIZE, + BUFSIZE, + NMPWAIT_USE_DEFAULT_WAIT, + &sa); + if (pipe->h == INVALID_HANDLE_VALUE) { + dbg("CreatePipe failed(%08lX)\n", + GetLastError()); + CloseHandle(pipe->o.hEvent); + free(pipe); + return 0; + } + + dbg("server_loop: Connect Pipe\n"); + if (ConnectNamedPipe(pipe->h, &pipe->o)) { + dbg("server_loop: Connect Pipe err %08lX\n", GetLastError()); + res = FALSE; + } else { + switch (GetLastError()) { + case ERROR_IO_PENDING: + dbg("server_loop: Connect Pipe(0) pending\n"); + DWORD t; + res = GetOverlappedResult(pipe->h, &pipe->o, &t, TRUE); + break; + case ERROR_PIPE_CONNECTED: + dbg("server_loop: Connect Pipe(0) connected\n"); + res = TRUE; + break; + default: + dbg("server_loop: Connect Pipe(0) err %08lX\n", GetLastError()); + res = FALSE; + } + } + + if (res) { + connection_data *cd = malloc(sizeof(connection_data)); + cd->pipe = pipe; + cd->conn_number = ++conn_number; + dbg("server_loop: CreateThread\n"); + HANDLE th = CreateThread(NULL, /* no security attribute */ + 0, /* default stack size */ + (LPTHREAD_START_ROUTINE) + handle_connection, + (LPVOID) cd, /* thread parameter */ + 0, /* not suspended */ + NULL); /* returns thread ID */ + if (!th) { + dbg("Cannot create thread\n"); + CloseHandle(pipe->h); + CloseHandle(pipe->o.hEvent); + free(pipe); + } else { + CloseHandle(th); + dbg("server_loop: Thread created\n"); + } + } else { + dbg("server_loop: Pipe not connected\n"); + CloseHandle(pipe->h); + CloseHandle(pipe->o.hEvent); + free(pipe); + } + } + dbg("server_loop: STH wrong\n"); + return 0; +} + +static SERVICE_STATUS winexesvcStatus; +static SERVICE_STATUS_HANDLE winexesvcStatusHandle; + +static VOID WINAPI winexesvcCtrlHandler(DWORD Opcode) +{ + switch (Opcode) { + case SERVICE_CONTROL_PAUSE: + dbg(SERVICE_NAME ": winexesvcCtrlHandler: pause\n", 0); + winexesvcStatus.dwCurrentState = SERVICE_PAUSED; + break; + + case SERVICE_CONTROL_CONTINUE: + dbg(SERVICE_NAME ": winexesvcCtrlHandler: continue\n", 0); + winexesvcStatus.dwCurrentState = SERVICE_RUNNING; + break; + + case SERVICE_CONTROL_STOP: + dbg(SERVICE_NAME ": winexesvcCtrlHandler: stop\n", 0); + winexesvcStatus.dwWin32ExitCode = 0; + winexesvcStatus.dwCurrentState = SERVICE_STOPPED; + winexesvcStatus.dwCheckPoint = 0; + winexesvcStatus.dwWaitHint = 0; + + if (!SetServiceStatus (winexesvcStatusHandle, &winexesvcStatus)) + dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", GetLastError()); + + dbg(SERVICE_NAME ": Leaving winexesvc\n", 0); + return; + + case SERVICE_CONTROL_INTERROGATE: + dbg(SERVICE_NAME ": winexesvcCtrlHandler: interrogate\n", 0); + break; + + default: + dbg(SERVICE_NAME ": Unrecognized opcode %ld\n", Opcode); + } + + if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus)) + dbg(SERVICE_NAME ": SetServiceStatus error 0x%08X\n", GetLastError()); + + return; +} + +static DWORD winexesvcInitialization(DWORD argc, LPTSTR * argv, DWORD * specificError) +{ + HANDLE th = CreateThread(NULL, 0, winexesvc_loop, NULL, 0, NULL); + if (th) { + CloseHandle(th); + return NO_ERROR; + } + return !NO_ERROR; +} + +static void WINAPI winexesvcStart(DWORD argc, LPTSTR * argv) +{ + DWORD status; + DWORD specificError; + + winexesvcStatus.dwServiceType = SERVICE_WIN32; + winexesvcStatus.dwCurrentState = SERVICE_START_PENDING; + winexesvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE; + winexesvcStatus.dwWin32ExitCode = 0; + winexesvcStatus.dwServiceSpecificExitCode = 0; + winexesvcStatus.dwCheckPoint = 0; + winexesvcStatus.dwWaitHint = 0; + + dbg(SERVICE_NAME ": RegisterServiceCtrlHandler\n", 0); + + winexesvcStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, winexesvcCtrlHandler); + + if (winexesvcStatusHandle == (SERVICE_STATUS_HANDLE) 0) { + dbg(SERVICE_NAME + ": RegisterServiceCtrlHandler failed %d\n", + GetLastError()); + return; + } + status = winexesvcInitialization(argc, argv, &specificError); + + if (status != NO_ERROR) { + winexesvcStatus.dwCurrentState = SERVICE_STOPPED; + winexesvcStatus.dwCheckPoint = 0; + winexesvcStatus.dwWaitHint = 0; + winexesvcStatus.dwWin32ExitCode = status; + winexesvcStatus.dwServiceSpecificExitCode = specificError; + + SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus); + return; + } + + winexesvcStatus.dwCurrentState = SERVICE_RUNNING; + winexesvcStatus.dwCheckPoint = 0; + winexesvcStatus.dwWaitHint = 0; + + if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus)) { + status = GetLastError(); + dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", status); + } + + dbg(SERVICE_NAME ": Returning the Main Thread \n", 0); + + return; +} + +int main(int argc, char *argv[]) +{ + SERVICE_TABLE_ENTRY DispatchTable[] = { + {SERVICE_NAME, winexesvcStart}, + {NULL, NULL} + }; + + dbg(SERVICE_NAME ": StartServiceCtrlDispatcher %d\n", GetLastError()); + if (!StartServiceCtrlDispatcher(DispatchTable)) { + dbg(SERVICE_NAME + ": StartServiceCtrlDispatcher (%d)\n", + GetLastError()); + } + return 0; +} diff --git a/examples/winexe/winexesvc.h b/examples/winexe/winexesvc.h new file mode 100644 index 0000000..92b8375 --- /dev/null +++ b/examples/winexe/winexesvc.h @@ -0,0 +1,42 @@ +/* + * Copyright (C) Andrzej Hajda 2009-2013 + * Contact: andrzej.hajda@wp.pl + * + * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf + * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab. + * + * ** NOTE! The following "GPLv3 only" license applies to the winexe + * ** service files. This does NOT imply that all of Samba is released + * ** under the "GPLv3 only" license. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 3 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +/* + * Shared by winexe and winexesvc + */ + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 1 + +#define VERSION ((VERSION_MAJOR * 100) + VERSION_MINOR) + +#define SERVICE_NAME "winexesvc" + +#define PIPE_NAME "ahexec" +#define PIPE_NAME_IN "ahexec_stdin%08X" +#define PIPE_NAME_OUT "ahexec_stdout%08X" +#define PIPE_NAME_ERR "ahexec_stderr%08X" + +#define CMD_STD_IO_ERR "std_io_err" +#define CMD_RETURN_CODE "return_code" diff --git a/examples/winexe/wscript b/examples/winexe/wscript new file mode 100644 index 0000000..6b311b1 --- /dev/null +++ b/examples/winexe/wscript @@ -0,0 +1,31 @@ +#!/usr/bin/env python + +def configure(conf): + AR32 = ['i386', 'i586', 'i686'] + AR64 = ['x86_64', 'amd64'] + TC = ['mingw32', 'mingw32msvc', 'w64-mingw32'] + + found = False + + for a in AR32: + for t in TC: + if conf.find_program(a + '-' + t + '-gcc', var='WINEXE_CC_WIN32'): + found = True + break + if found: + conf.DEFINE('HAVE_WINEXE_CC_WIN32', 1); + break + + found = False + + for a in AR64: + for t in TC: + if conf.find_program(a + '-' + t + '-gcc', var='WINEXE_CC_WIN64'): + found = True + break + if found: + conf.DEFINE('HAVE_WINEXE_CC_WIN64', 1); + break + + conf.DEFINE("WINEXE_LDFLAGS", + "-s -Wall -Wl,-Bstatic -Wl,-Bdynamic -luserenv") diff --git a/examples/winexe/wscript_build b/examples/winexe/wscript_build new file mode 100644 index 0000000..3646834 --- /dev/null +++ b/examples/winexe/wscript_build @@ -0,0 +1,118 @@ +#!/usr/bin/env python + +import samba_utils + +def generate_winexesvc_c_from_exe(t): + '''generate a C source file with the contents of the given binary''' + src = t.inputs[0].bldpath(t.env) + tgt = t.outputs[0].bldpath(t.env) + fn = t.env.SAMBA_GENERATOR_VARS['WINEXE_FN'] + + try: + with open(src, 'rb') as f: + src_blob = f.read() + f.close() + except: + print('Failed to read %s to convert to C array' % (src)) + return -1 + + def c_array(src): + N = 0 + result = '' + while src: + l = src[:8] + src = src[8:] + # Even files opened in binary mode are read as type "str" in + # Python 2, so we need to get the integer ordinal of each + # character in the string before we try to convert it to hex. + if isinstance(l, str): + h = ' '.join(["0x%02X," % ord(x) for x in l]) + # Files opened in binary mode are read as type "bytes" in + # Python 3, so we can convert each individual integer in the + # array of bytes to hex directly. + else: + h = ' '.join(["0x%02X," % x for x in l]) + result += "\t\t%s\n" % (h) + return result + + src_array = c_array(src_blob) + if len(src_array) <= 0: + print('Failed to convert %s to C array' % (src)) + return -1 + + contents = ''' +#include "replace.h" +#include "lib/util/data_blob.h" + +const DATA_BLOB *%s(void); +const DATA_BLOB *%s(void) +{ +\tstatic const uint8_t array[] = { +%s +\t}; +\tstatic const DATA_BLOB blob = { +\t\t.data = discard_const_p(uint8_t, array), +\t\t.length = ARRAY_SIZE(array), +\t}; +\treturn &blob; +} +''' % (fn, fn, src_array) + + if not samba_utils.save_file(tgt, contents): + print('Failed to write C source file %s' % (tgt)) + return -1 + return 0 + +winexesvc_binaries = '' + +bld.SAMBA_GENERATOR( + 'winexesvc32_exe', + source='winexesvc.c', + target='winexesvc32.exe', + rule='${WINEXE_CC_WIN32} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}', + enabled=bld.env.build_winexe and bld.env.WINEXE_CC_WIN32) + +vars = {"WINEXE_FN": "winexesvc32_exe_binary"} +bld.SAMBA_GENERATOR( + 'winexesvc32_exe_binary', + source='winexesvc32.exe', + target='winexesvc32_exe_binary.c', + group='build_source', + vars=vars, + rule=generate_winexesvc_c_from_exe, + enabled=bld.env.build_winexe and bld.env.WINEXE_CC_WIN32) + +if bld.env.WINEXE_CC_WIN32: + winexesvc_binaries += ' winexesvc32_exe_binary.c' + +bld.SAMBA_GENERATOR( + 'winexesvc64_exe', + source='winexesvc.c', + target='winexesvc64.exe', + rule='${WINEXE_CC_WIN64} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}', + enabled=bld.env.build_winexe and bld.env.WINEXE_CC_WIN64) + +vars = {"WINEXE_FN": "winexesvc64_exe_binary"} +bld.SAMBA_GENERATOR( + 'winexesvc64_exe_binary', + source='winexesvc64.exe', + target='winexesvc64_exe_binary.c', + group='build_source', + vars=vars, + rule=generate_winexesvc_c_from_exe, + enabled=bld.env.build_winexe and bld.env.WINEXE_CC_WIN64) + +if bld.env.WINEXE_CC_WIN64: + winexesvc_binaries += ' winexesvc64_exe_binary.c' + +if winexesvc_binaries != '': + bld.SAMBA3_BINARY('winexe', + source='winexe.c ' + winexesvc_binaries, + deps=''' + CMDLINE_S3 + samba-credentials + LOADPARM_CTX + libsmb + msrpc3 + ''', + enabled=bld.env.build_winexe) |