examples: Add winexe re-implemented on current Samba libs
authorVolker Lendecke <vl@samba.org>
Tue, 10 Apr 2018 15:18:18 +0000 (17:18 +0200)
committerJeremy Allison <jra@samba.org>
Tue, 28 Aug 2018 00:03:07 +0000 (02:03 +0200)
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.

Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Autobuild-User(master): Jeremy Allison <jra@samba.org>
Autobuild-Date(master): Tue Aug 28 02:03:07 CEST 2018 on sn-devel-144

examples/winexe/README [new file with mode: 0644]
examples/winexe/winexe.c [new file with mode: 0644]
examples/winexe/winexesvc.c [new file with mode: 0644]
examples/winexe/winexesvc.h [new file with mode: 0644]
examples/winexe/wscript [new file with mode: 0644]
examples/winexe/wscript_build [new file with mode: 0644]
source3/wscript_build
wscript

diff --git a/examples/winexe/README b/examples/winexe/README
new file mode 100644 (file)
index 0000000..c688e5d
--- /dev/null
@@ -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 (file)
index 0000000..cf667a6
--- /dev/null
@@ -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 (file)
index 0000000..02aa9df
--- /dev/null
@@ -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 (file)
index 0000000..92b8375
--- /dev/null
@@ -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 (file)
index 0000000..3380b97
--- /dev/null
@@ -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 (file)
index 0000000..ecad377
--- /dev/null
@@ -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
+                      ''')
index 6e34bfa..f463a8c 100644 (file)
@@ -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 f98d731..619b492 100644 (file)
--- 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')