summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--examples/winexe/README18
-rw-r--r--examples/winexe/winexe.c1867
-rw-r--r--examples/winexe/winexesvc.c745
-rw-r--r--examples/winexe/winexesvc.h42
-rw-r--r--examples/winexe/wscript29
-rw-r--r--examples/winexe/wscript_build87
-rw-r--r--source3/wscript_build1
-rw-r--r--wscript1
8 files changed, 2790 insertions, 0 deletions
diff --git a/examples/winexe/README b/examples/winexe/README
new file mode 100644
index 00000000000..c688e5d1ba4
--- /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 00000000000..cf667a64ebc
--- /dev/null
+++ b/examples/winexe/winexe.c
@@ -0,0 +1,1867 @@
+/*
+ * 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 <tevent.h>
+#include <popt.h>
+#include "version.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 "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;
+ 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,
+ struct loadparm_context *lp_ctx)
+{
+ poptContext pc;
+ int opt, i;
+ struct cli_credentials *cred;
+
+ int argc_new;
+ char **argv_new;
+
+ 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;
+ int flag_nopass = 0;
+ char *opt_user = NULL;
+ char *opt_kerberos = NULL;
+ char *opt_auth_file = NULL;
+ char *opt_debuglevel = NULL;
+
+ struct poptOption long_options[] = {
+ { "help", 'h', POPT_ARG_NONE, &flag_help, 0,
+ "Display help message" },
+ { "version", 'V', POPT_ARG_NONE, &flag_version, 0,
+ "Display version number" },
+ { "user", 'U', POPT_ARG_STRING, &opt_user, 0,
+ "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" },
+ { "authentication-file", 'A',
+ POPT_ARG_STRING, &opt_auth_file, 0,
+ "Get the credentials from a file", "FILE" },
+ { "no-pass", 'N', POPT_ARG_NONE, &flag_nopass, 0,
+ "Do not ask for a password", NULL },
+ { "kerberos", 'k', POPT_ARG_STRING, &opt_kerberos, 0,
+ "Use Kerberos, -k [yes|no]" },
+ { "debuglevel", 'd', POPT_ARG_STRING, &opt_debuglevel, 0,
+ "Set debug level", "DEBUGLEVEL" },
+ { "uninstall", 0, POPT_ARG_NONE, &flag_uninstall, 0,
+ "Uninstall winexe service after remote execution", NULL},
+ { "reinstall", 0, POPT_ARG_NONE, &flag_reinstall, 0,
+ "Reinstall winexe service before remote execution", NULL},
+ { "runas", 0, POPT_ARG_STRING, &options->runas, 0,
+ "Run as the given user (BEWARE: this password is sent "
+ "in cleartext over the network!)",
+ "[DOMAIN\\]USERNAME%PASSWORD"},
+ { "runas-file", 0, POPT_ARG_STRING, &options->runas_file, 0,
+ "Run as user options defined in a file", "FILE"},
+ { "interactive", 0, POPT_ARG_INT, &flag_interactive, 0,
+ "Desktop interaction: 0 - disallow, 1 - allow. If allow, "
+ "also use the --system switch (Windows requirement). Vista "
+ "does not support this option.", "0|1"},
+ { "ostype", 0, POPT_ARG_INT, &flag_ostype, 0,
+ "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.", "0|1|2"},
+ POPT_TABLEEND
+ };
+
+ ZERO_STRUCTP(options);
+
+ pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,
+ 0);
+
+ poptSetOtherOptionHelp(pc, "[OPTION]... //HOST 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);
+ }
+
+ if (opt_debuglevel) {
+ lp_set_cmdline("log level", opt_debuglevel);
+ }
+
+ cred = cli_credentials_init(mem_ctx);
+
+ if (opt_user) {
+ cli_credentials_parse_string(cred, opt_user, CRED_SPECIFIED);
+ } else if (opt_auth_file) {
+ cli_credentials_parse_file(cred, opt_auth_file,
+ CRED_SPECIFIED);
+ }
+
+ cli_credentials_guess(cred, lp_ctx);
+ if (!cli_credentials_get_password(cred) && !flag_nopass) {
+ char *p = getpass("Enter password: ");
+ if (*p) {
+ cli_credentials_set_password(cred, p, CRED_SPECIFIED);
+ }
+ }
+
+ if (opt_kerberos) {
+ cli_credentials_set_kerberos_state(cred,
+ strcmp(opt_kerberos, "yes")
+ ? CRED_MUST_USE_KERBEROS
+ : CRED_DONT_USE_KERBEROS);
+ }
+
+ 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 = cred;
+
+ options->hostname = argv_new[0] + 2;
+ options->cmd = argv_new[1];
+
+ 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,
+ 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;
+ NTSTATUS status;
+ const DATA_BLOB *binary = NULL;
+
+ status = cli_full_connection_creds(
+ &cli,
+ NULL,
+ hostname,
+ NULL,
+ 445,
+ "ADMIN$",
+ "?????",
+ credentials,
+ 0,
+ 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) {
+ //TODO
+ }
+
+ 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 close_done;
+ }
+
+close_done:
+ 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));
+ }
+done:
+ TALLOC_FREE(cli);
+ return status;
+}
+
+static NTSTATUS winexe_svc_install(
+ struct cli_state *cli,
+ const char *hostname,
+ 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;
+
+ status = cli_rpc_pipe_open_noauth_transport(
+ cli,
+ NCACN_NP,
+ &ndr_table_svcctl,
+ &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,
+ smbXcli_conn_remote_name(cli->conn),
+ 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 */
+ NULL, /* service_start_name */
+ NULL, /* password */
+ 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,
+ 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;
+
+ status = cli_rpc_pipe_open_noauth_transport(
+ cli,
+ NCACN_NP,
+ &ndr_table_svcctl,
+ &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,
+ smbXcli_conn_remote_name(cli->conn),
+ 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 */
+ 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);
+ 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 */
+ 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);
+ 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);
+ 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 */
+ 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);
+ 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, "version 0x%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, const char *argv[])
+{
+ TALLOC_CTX *frame = talloc_stackframe();
+ struct program_options options = {0};
+ struct loadparm_context *lp_ctx;
+ struct cli_state *cli;
+ 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;
+
+ lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
+ if (lp_ctx == NULL) {
+ fprintf(stderr, "loadparm_init_s3 failed\n");
+ goto done;
+ }
+
+ smb_init_locale();
+ setup_logging("winexe", DEBUG_STDOUT);
+
+ lp_load_global(get_dyn_CONFIGFILE());
+
+ parse_args(argc, argv, frame, &options, lp_ctx);
+
+ 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,
+ NULL,
+ options.hostname,
+ NULL,
+ 445,
+ "IPC$",
+ "?????",
+ options.credentials,
+ 0,
+ 0);
+
+ 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,
+ 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 00000000000..02aa9df4b3d
--- /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 commad (%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 00000000000..92b83750b70
--- /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 00000000000..3380b9794f6
--- /dev/null
+++ b/examples/winexe/wscript
@@ -0,0 +1,29 @@
+#!/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
+
+ 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 00000000000..ecad3772461
--- /dev/null
+++ b/examples/winexe/wscript_build
@@ -0,0 +1,87 @@
+#!/usr/bin/env python
+
+import samba_utils
+
+def generate_winexesvc_c_from_exe(t):
+ src = t.inputs[0].bldpath(t.env)
+ tgt = t.outputs[0].bldpath(t.env)
+ fn = t.env.SAMBA_GENERATOR_VARS['WINEXE_FN']
+ src_blob = samba_utils.load_file(src)
+
+ def c_array(src):
+ N = 0
+ result = ''
+ while src:
+ l = src[:8]
+ src = src[8:]
+ h = ' '.join(["0x%02X," % ord(x) for x in l])
+ result += "\t\t%s\n" % (h)
+ return result
+
+ src_array = c_array(src_blob)
+
+ 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)
+
+ ret = samba_utils.save_file(tgt, contents)
+ assert(ret == True)
+
+winexesvc_binaries = ''
+
+if bld.env.WINEXE_CC_WIN32:
+ bld.SAMBA_GENERATOR(
+ 'winexesvc32_exe',
+ source='winexesvc.c',
+ target='winexesvc32.exe',
+ rule='${WINEXE_CC_WIN32} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}')
+ 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)
+ winexesvc_binaries += ' winexesvc32_exe_binary.c'
+
+if bld.env.WINEXE_CC_WIN64:
+ bld.SAMBA_GENERATOR(
+ 'winexesvc64_exe',
+ source='winexesvc.c',
+ target='winexesvc64.exe',
+ rule='${WINEXE_CC_WIN64} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}')
+ 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)
+ winexesvc_binaries += ' winexesvc64_exe_binary.c'
+
+if winexesvc_binaries != '':
+ bld.SAMBA3_BINARY('winexe',
+ source='winexe.c ' + winexesvc_binaries,
+ deps='''
+ popt
+ samba-credentials
+ LOADPARM_CTX
+ libsmb
+ msrpc3
+ ''')
diff --git a/source3/wscript_build b/source3/wscript_build
index 6e34bfaecf0..f463a8c4261 100644
--- a/source3/wscript_build
+++ b/source3/wscript_build
@@ -1345,6 +1345,7 @@ bld.RECURSE('../examples/libsmbclient')
bld.RECURSE('../examples/pdb')
bld.RECURSE('../examples/VFS')
bld.RECURSE('../examples/fuse')
+bld.RECURSE('../examples/winexe')
bld.RECURSE('lib/netapi/tests')
bld.RECURSE('lib/netapi/examples')
bld.RECURSE('smbd/notifyd')
diff --git a/wscript b/wscript
index f98d731e537..619b4923813 100644
--- a/wscript
+++ b/wscript
@@ -128,6 +128,7 @@ def configure(conf):
conf.RECURSE('lib/replace')
conf.RECURSE('examples/fuse')
+ conf.RECURSE('examples/winexe')
conf.SAMBA_CHECK_PERL(mandatory=True)
conf.find_program('xsltproc', var='XSLTPROC')