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/winexe.c | |
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/winexe.c')
-rw-r--r-- | examples/winexe/winexe.c | 1918 |
1 files changed, 1918 insertions, 0 deletions
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; +} |