summaryrefslogtreecommitdiffstats
path: root/examples/winexe/winexe.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 17:20:00 +0000
commit8daa83a594a2e98f39d764422bfbdbc62c9efd44 (patch)
tree4099e8021376c7d8c05bdf8503093d80e9c7bad0 /examples/winexe/winexe.c
parentInitial commit. (diff)
downloadsamba-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.c1918
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;
+}