[PATCH] Add examples/winexe
Jeremy Allison
jra at samba.org
Mon Aug 27 21:07:40 UTC 2018
On Mon, Aug 27, 2018 at 04:32:37PM +0200, Volker Lendecke via samba-technical wrote:
> Hi!
>
> A customer asked me to smb2-enable winexe from
> https://sourceforge.net/projects/winexe/. That sourceforge project is
> based on Samba code from 2012 and before. It was not feasible to fix
> the code to modern internal Samba APIs, so I rewrote the core
> Samba-side of things.
>
> This has a few caveats:
>
> It can't be tested in autobuild, we don't have Windows around
>
> The Windows service files are GPLv3 only. Those are 1:1 taken from the
> sourceforge project. The original author seems unreachable, so
> relicensing looks difficult, and I don't feel capable of writing a
> Windows service from scratch.
>
> It requires mingw to build. We could add mingw to sn-devel, so at
> least the build keeps working.
>
> As winexe for some scenarious seems highly interesting, I would like
> to get this into mainline Samba despite those caveats.
>
> Comments?
Oh I *love* patches 1-3, really nice addition to the libsmb/cli*.c
code ! :-).
As for patch #4, I'm good with adding this into our tree, even
if GPLv3-only. After all, you've marked it as such so I think
that's fine.
RB+ and pushed.
Cheers,
Jeremy.
> --
> SerNet GmbH, Bahnhofsallee 1b, 37081 Göttingen
> phone: +49-551-370000-0, fax: +49-551-370000-9
> AG Göttingen, HRB 2816, GF: Dr. Johannes Loxen
> http://www.sernet.de, mailto:kontakt at sernet.de
>
> Meet us at Storage Developer Conference (SDC)
> Santa Clara, CA USA, September 24th-27th 2018
> From ef78b24bded8e2a2221963441b47adf572aca8a1 Mon Sep 17 00:00:00 2001
> From: Volker Lendecke <vl at samba.org>
> Date: Wed, 4 Apr 2018 16:18:28 +0200
> Subject: [PATCH 1/4] libsmb: Add protocol-agnostic cli_read
>
> So far only cli_pull could be called directly without looking at the
> protocol. We did not have a simple read that did the right thing
> depending on the protocol
>
> Signed-off-by: Volker Lendecke <vl at samba.org>
> ---
> source3/libsmb/clireadwrite.c | 122 ++++++++++++++++++++++++++++++++++++++++++
> source3/libsmb/proto.h | 9 ++++
> 2 files changed, 131 insertions(+)
>
> diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
> index 67870d8c40b..a93f35727a0 100644
> --- a/source3/libsmb/clireadwrite.c
> +++ b/source3/libsmb/clireadwrite.c
> @@ -700,6 +700,128 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
> return status;
> }
>
> +struct cli_read_state {
> + struct cli_state *cli;
> + char *buf;
> + size_t buflen;
> + size_t received;
> +};
> +
> +static void cli_read_done(struct tevent_req *subreq);
> +
> +struct tevent_req *cli_read_send(
> + TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + uint16_t fnum,
> + char *buf,
> + off_t offset,
> + size_t size)
> +{
> + struct tevent_req *req, *subreq;
> + struct cli_read_state *state;
> +
> + req = tevent_req_create(mem_ctx, &state, struct cli_read_state);
> + if (req == NULL) {
> + return NULL;
> + }
> + state->cli = cli;
> + state->buf = buf;
> + state->buflen = size;
> +
> + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
> + uint32_t max_size;
> + bool ok;
> +
> + ok = smb2cli_conn_req_possible(state->cli->conn, &max_size);
> + if (!ok) {
> + tevent_req_nterror(
> + req,
> + NT_STATUS_INSUFFICIENT_RESOURCES);
> + return tevent_req_post(req, ev);
> + }
> +
> + /*
> + * downgrade depending on the available credits
> + */
> + size = MIN(max_size, size);
> +
> + subreq = cli_smb2_read_send(
> + state, ev, cli, fnum, offset, size);
> + if (tevent_req_nomem(subreq, req)) {
> + return tevent_req_post(req, ev);
> + }
> + } else {
> + bool ok;
> + ok = smb1cli_conn_req_possible(state->cli->conn);
> + if (!ok) {
> + tevent_req_nterror(
> + req,
> + NT_STATUS_INSUFFICIENT_RESOURCES);
> + return tevent_req_post(req, ev);
> + }
> +
> + subreq = cli_read_andx_send(
> + state, ev, cli, fnum, offset, size);
> + if (tevent_req_nomem(subreq, req)) {
> + return tevent_req_post(req, ev);
> + }
> + }
> +
> + tevent_req_set_callback(subreq, cli_read_done, req);
> +
> + return req;
> +}
> +
> +static void cli_read_done(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct cli_read_state *state = tevent_req_data(
> + req, struct cli_read_state);
> + NTSTATUS status;
> + ssize_t received;
> + uint8_t *buf;
> +
> + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
> + status = cli_smb2_read_recv(subreq, &received, &buf);
> + } else {
> + status = cli_read_andx_recv(subreq, &received, &buf);
> + }
> +
> + if (NT_STATUS_EQUAL(status, NT_STATUS_END_OF_FILE)) {
> + received = 0;
> + status = NT_STATUS_OK;
> + }
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> + if ((received < 0) || (received > state->buflen)) {
> + state->received = 0;
> + tevent_req_nterror(req, NT_STATUS_UNEXPECTED_IO_ERROR);
> + return;
> + }
> +
> + memcpy(state->buf, buf, received);
> + state->received = received;
> + tevent_req_done(req);
> +}
> +
> +NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received)
> +{
> + struct cli_read_state *state = tevent_req_data(
> + req, struct cli_read_state);
> + NTSTATUS status;
> +
> + if (tevent_req_is_nterror(req, &status)) {
> + return status;
> + }
> + if (received != NULL) {
> + *received = state->received;
> + }
> + return NT_STATUS_OK;
> +}
> +
> static NTSTATUS cli_read_sink(char *buf, size_t n, void *priv)
> {
> char **pbuf = (char **)priv;
> diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
> index 2bd61b1d2c2..06d0a0c9e06 100644
> --- a/source3/libsmb/proto.h
> +++ b/source3/libsmb/proto.h
> @@ -839,6 +839,15 @@ NTSTATUS cli_pull(struct cli_state *cli, uint16_t fnum,
> off_t start_offset, off_t size, size_t window_size,
> NTSTATUS (*sink)(char *buf, size_t n, void *priv),
> void *priv, off_t *received);
> +struct tevent_req *cli_read_send(
> + TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + uint16_t fnum,
> + char *buf,
> + off_t offset,
> + size_t size);
> +NTSTATUS cli_read_recv(struct tevent_req *req, size_t *received);
> NTSTATUS cli_read(struct cli_state *cli, uint16_t fnum,
> char *buf, off_t offset, size_t size,
> size_t *nread);
> --
> 2.11.0
>
>
> From 8892ceebe12fb3648a8348202ff3abf835c802ad Mon Sep 17 00:00:00 2001
> From: Volker Lendecke <vl at samba.org>
> Date: Wed, 4 Apr 2018 16:19:52 +0200
> Subject: [PATCH 2/4] libsmb: Rename cli_writeall_send/recv to
> cli_smb1_writeall_send/recv
>
> Preparing a protocol agnostic writeall
>
> Signed-off-by: Volker Lendecke <vl at samba.org>
> ---
> source3/libsmb/clireadwrite.c | 45 ++++++++++++++++++++++---------------------
> 1 file changed, 23 insertions(+), 22 deletions(-)
>
> diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
> index a93f35727a0..844c0b345aa 100644
> --- a/source3/libsmb/clireadwrite.c
> +++ b/source3/libsmb/clireadwrite.c
> @@ -1065,7 +1065,7 @@ NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten)
> return NT_STATUS_OK;
> }
>
> -struct cli_writeall_state {
> +struct cli_smb1_writeall_state {
> struct tevent_context *ev;
> struct cli_state *cli;
> uint16_t fnum;
> @@ -1076,20 +1076,21 @@ struct cli_writeall_state {
> size_t written;
> };
>
> -static void cli_writeall_written(struct tevent_req *req);
> +static void cli_smb1_writeall_written(struct tevent_req *req);
>
> -static struct tevent_req *cli_writeall_send(TALLOC_CTX *mem_ctx,
> - struct tevent_context *ev,
> - struct cli_state *cli,
> - uint16_t fnum,
> - uint16_t mode,
> - const uint8_t *buf,
> - off_t offset, size_t size)
> +static struct tevent_req *cli_smb1_writeall_send(TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + uint16_t fnum,
> + uint16_t mode,
> + const uint8_t *buf,
> + off_t offset, size_t size)
> {
> struct tevent_req *req, *subreq;
> - struct cli_writeall_state *state;
> + struct cli_smb1_writeall_state *state;
>
> - req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state);
> + req = tevent_req_create(mem_ctx, &state,
> + struct cli_smb1_writeall_state);
> if (req == NULL) {
> return NULL;
> }
> @@ -1108,16 +1109,16 @@ static struct tevent_req *cli_writeall_send(TALLOC_CTX *mem_ctx,
> if (tevent_req_nomem(subreq, req)) {
> return tevent_req_post(req, ev);
> }
> - tevent_req_set_callback(subreq, cli_writeall_written, req);
> + tevent_req_set_callback(subreq, cli_smb1_writeall_written, req);
> return req;
> }
>
> -static void cli_writeall_written(struct tevent_req *subreq)
> +static void cli_smb1_writeall_written(struct tevent_req *subreq)
> {
> struct tevent_req *req = tevent_req_callback_data(
> subreq, struct tevent_req);
> - struct cli_writeall_state *state = tevent_req_data(
> - req, struct cli_writeall_state);
> + struct cli_smb1_writeall_state *state = tevent_req_data(
> + req, struct cli_smb1_writeall_state);
> NTSTATUS status;
> size_t written, to_write;
>
> @@ -1148,14 +1149,14 @@ static void cli_writeall_written(struct tevent_req *subreq)
> if (tevent_req_nomem(subreq, req)) {
> return;
> }
> - tevent_req_set_callback(subreq, cli_writeall_written, req);
> + tevent_req_set_callback(subreq, cli_smb1_writeall_written, req);
> }
>
> -static NTSTATUS cli_writeall_recv(struct tevent_req *req,
> - size_t *pwritten)
> +static NTSTATUS cli_smb1_writeall_recv(struct tevent_req *req,
> + size_t *pwritten)
> {
> - struct cli_writeall_state *state = tevent_req_data(
> - req, struct cli_writeall_state);
> + struct cli_smb1_writeall_state *state = tevent_req_data(
> + req, struct cli_smb1_writeall_state);
> NTSTATUS status;
>
> if (tevent_req_is_nterror(req, &status)) {
> @@ -1191,7 +1192,7 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
> req = cli_smb2_writeall_send(frame, ev, cli, fnum, mode,
> buf, offset, size);
> } else {
> - req = cli_writeall_send(frame, ev, cli, fnum, mode,
> + req = cli_smb1_writeall_send(frame, ev, cli, fnum, mode,
> buf, offset, size);
> }
> if (req == NULL) {
> @@ -1203,7 +1204,7 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
> if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> status = cli_smb2_writeall_recv(req, pwritten);
> } else {
> - status = cli_writeall_recv(req, pwritten);
> + status = cli_smb1_writeall_recv(req, pwritten);
> }
> fail:
> TALLOC_FREE(frame);
> --
> 2.11.0
>
>
> From 461cc80d3c1c08b54b042d8f29c9d2e3e3621893 Mon Sep 17 00:00:00 2001
> From: Volker Lendecke <vl at samba.org>
> Date: Wed, 4 Apr 2018 16:32:01 +0200
> Subject: [PATCH 3/4] libsmb: Expose protocol-agnostic cli_writeall_send/recv
>
> Signed-off-by: Volker Lendecke <vl at samba.org>
> ---
> source3/libsmb/clireadwrite.c | 106 +++++++++++++++++++++++++++++++++++++-----
> source3/libsmb/proto.h | 10 ++++
> 2 files changed, 104 insertions(+), 12 deletions(-)
>
> diff --git a/source3/libsmb/clireadwrite.c b/source3/libsmb/clireadwrite.c
> index 844c0b345aa..6bf3df6913f 100644
> --- a/source3/libsmb/clireadwrite.c
> +++ b/source3/libsmb/clireadwrite.c
> @@ -1168,6 +1168,98 @@ static NTSTATUS cli_smb1_writeall_recv(struct tevent_req *req,
> return NT_STATUS_OK;
> }
>
> +struct cli_writeall_state {
> + struct cli_state *cli;
> + size_t written;
> +};
> +
> +static void cli_writeall_done(struct tevent_req *subreq);
> +
> +struct tevent_req *cli_writeall_send(
> + TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + uint16_t fnum,
> + uint16_t mode,
> + const uint8_t *buf,
> + off_t offset,
> + size_t size)
> +{
> + struct tevent_req *req, *subreq;
> + struct cli_writeall_state *state;
> +
> + req = tevent_req_create(mem_ctx, &state, struct cli_writeall_state);
> + if (req == NULL) {
> + return NULL;
> + }
> + state->cli = cli;
> +
> + if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> + subreq = cli_smb2_writeall_send(
> + state,
> + ev,
> + cli,
> + fnum,
> + mode,
> + buf,
> + offset,
> + size);
> + } else {
> + subreq = cli_smb1_writeall_send(
> + state,
> + ev,
> + cli,
> + fnum,
> + mode,
> + buf,
> + offset,
> + size);
> + }
> +
> + if (tevent_req_nomem(subreq, req)) {
> + return tevent_req_post(req, ev);
> + }
> + tevent_req_set_callback(subreq, cli_writeall_done, req);
> +
> + return req;
> +}
> +
> +static void cli_writeall_done(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct cli_writeall_state *state = tevent_req_data(
> + req, struct cli_writeall_state);
> + NTSTATUS status;
> +
> + if (smbXcli_conn_protocol(state->cli->conn) >= PROTOCOL_SMB2_02) {
> + status = cli_smb2_writeall_recv(subreq, &state->written);
> + } else {
> + status = cli_smb1_writeall_recv(subreq, &state->written);
> + }
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> + tevent_req_done(req);
> +}
> +
> +NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten)
> +{
> + struct cli_writeall_state *state = tevent_req_data(
> + req, struct cli_writeall_state);
> + NTSTATUS status;
> +
> + if (tevent_req_is_nterror(req, &status)) {
> + return status;
> + }
> + if (pwritten != NULL) {
> + *pwritten = state->written;
> + }
> + return NT_STATUS_OK;
> +}
> +
> +
> NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
> const uint8_t *buf, off_t offset, size_t size,
> size_t *pwritten)
> @@ -1188,24 +1280,14 @@ NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
> if (ev == NULL) {
> goto fail;
> }
> - if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> - req = cli_smb2_writeall_send(frame, ev, cli, fnum, mode,
> - buf, offset, size);
> - } else {
> - req = cli_smb1_writeall_send(frame, ev, cli, fnum, mode,
> - buf, offset, size);
> - }
> + req = cli_writeall_send(frame, ev, cli, fnum, mode, buf, offset, size);
> if (req == NULL) {
> goto fail;
> }
> if (!tevent_req_poll_ntstatus(req, ev, &status)) {
> goto fail;
> }
> - if (smbXcli_conn_protocol(cli->conn) >= PROTOCOL_SMB2_02) {
> - status = cli_smb2_writeall_recv(req, pwritten);
> - } else {
> - status = cli_smb1_writeall_recv(req, pwritten);
> - }
> + status = cli_writeall_recv(req, pwritten);
> fail:
> TALLOC_FREE(frame);
> return status;
> diff --git a/source3/libsmb/proto.h b/source3/libsmb/proto.h
> index 06d0a0c9e06..d0bc3e78771 100644
> --- a/source3/libsmb/proto.h
> +++ b/source3/libsmb/proto.h
> @@ -868,6 +868,16 @@ struct tevent_req *cli_write_andx_send(TALLOC_CTX *mem_ctx,
> off_t offset, size_t size);
> NTSTATUS cli_write_andx_recv(struct tevent_req *req, size_t *pwritten);
>
> +struct tevent_req *cli_writeall_send(
> + TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + uint16_t fnum,
> + uint16_t mode,
> + const uint8_t *buf,
> + off_t offset,
> + size_t size);
> +NTSTATUS cli_writeall_recv(struct tevent_req *req, size_t *pwritten);
> NTSTATUS cli_writeall(struct cli_state *cli, uint16_t fnum, uint16_t mode,
> const uint8_t *buf, off_t offset, size_t size,
> size_t *pwritten);
> --
> 2.11.0
>
>
> From 1baaf4356640378e76b674525b4757d4d0a36e36 Mon Sep 17 00:00:00 2001
> From: Volker Lendecke <vl at samba.org>
> Date: Tue, 10 Apr 2018 17:18:18 +0200
> Subject: [PATCH 4/4] examples: Add winexe re-implemented on current Samba libs
>
> 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 at samba.org>
> ---
> examples/winexe/README | 18 +
> examples/winexe/winexe.c | 1867 +++++++++++++++++++++++++++++++++++++++++
> examples/winexe/winexesvc.c | 745 ++++++++++++++++
> examples/winexe/winexesvc.h | 42 +
> examples/winexe/wscript | 29 +
> examples/winexe/wscript_build | 87 ++
> source3/wscript_build | 1 +
> wscript | 1 +
> 8 files changed, 2790 insertions(+)
> create mode 100644 examples/winexe/README
> create mode 100644 examples/winexe/winexe.c
> create mode 100644 examples/winexe/winexesvc.c
> create mode 100644 examples/winexe/winexesvc.h
> create mode 100644 examples/winexe/wscript
> create mode 100644 examples/winexe/wscript_build
>
> diff --git a/examples/winexe/README b/examples/winexe/README
> new file mode 100644
> index 00000000000..c688e5d1ba4
> --- /dev/null
> +++ b/examples/winexe/README
> @@ -0,0 +1,18 @@
> +winexe from https://sourceforge.net/projects/winexe/ is a project
> +based on Samba libraries from 2012. According to the winexe git
> +repository the last Samba commit winexe was updated to is 47bbf9886f0c
> +from November 6, 2012. As winexe uses unpublished Samba internal
> +libraries, it broke over time.
> +
> +This is a port of the winexe functionality to more modern Samba
> +versions. It still uses internal APIs, but it being part of the tree
> +means that it is much easier to keep up to date.
> +
> +The Windows service files were taken literally from the original
> +winexe from the sourceforge git. Andrzej Hajda chose GPLv3 only and
> +not GPLv3+. As GPL evolves very slowly, this should not be a practical
> +problem for quite some time.
> +
> +To build it under Linux, you need mingw binaries on your build
> +system. Under Debian stretch, the package names are gcc-mingw-w64 and
> +friends.
> diff --git a/examples/winexe/winexe.c b/examples/winexe/winexe.c
> new file mode 100644
> index 00000000000..cf667a64ebc
> --- /dev/null
> +++ b/examples/winexe/winexe.c
> @@ -0,0 +1,1867 @@
> +/*
> + * Samba Unix/Linux CIFS implementation
> + *
> + * winexe
> + *
> + * Copyright (C) 2018 Volker Lendecke <vl at samba.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 3 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "includes.h"
> +#include <tevent.h>
> +#include <popt.h>
> +#include "version.h"
> +#include "lib/param/param.h"
> +#include "auth/credentials/credentials.h"
> +#include "lib/util/talloc_stack.h"
> +#include "lib/util/tevent_ntstatus.h"
> +#include "lib/util/sys_rw.h"
> +#include "libsmb/proto.h"
> +#include "librpc/gen_ndr/ndr_svcctl_c.h"
> +#include "rpc_client/cli_pipe.h"
> +#include "libcli/smb/smbXcli_base.h"
> +#include "libcli/util/werror.h"
> +#include "lib/async_req/async_sock.h"
> +#include "client.h"
> +
> +#define SVC_INTERACTIVE 1
> +#define SVC_IGNORE_INTERACTIVE 2
> +#define SVC_INTERACTIVE_MASK 3
> +#define SVC_FORCE_UPLOAD 4
> +#define SVC_OS64BIT 8
> +#define SVC_OSCHOOSE 16
> +#define SVC_UNINSTALL 32
> +#define SVC_SYSTEM 64
> +
> +#define SERVICE_NAME "winexesvc"
> +
> +#define PIPE_NAME "ahexec"
> +#define PIPE_NAME_IN "ahexec_stdin%08X"
> +#define PIPE_NAME_OUT "ahexec_stdout%08X"
> +#define PIPE_NAME_ERR "ahexec_stderr%08X"
> +
> +static const char version_message_fmt[] = "winexe version %d.%d\n"
> + "This program may be freely redistributed under the terms of the "
> + "GNU GPLv3\n";
> +
> +struct program_options {
> + char *hostname;
> + char *cmd;
> + struct cli_credentials *credentials;
> + char *runas;
> + char *runas_file;
> + int flags;
> +};
> +
> +static void parse_args(int argc, const char *argv[],
> + TALLOC_CTX *mem_ctx,
> + struct program_options *options,
> + struct loadparm_context *lp_ctx)
> +{
> + poptContext pc;
> + int opt, i;
> + struct cli_credentials *cred;
> +
> + int argc_new;
> + char **argv_new;
> +
> + int flag_interactive = SVC_IGNORE_INTERACTIVE;
> + int flag_ostype = 2;
> + int flag_reinstall = 0;
> + int flag_uninstall = 0;
> + int flag_help = 0;
> + int flag_version = 0;
> + int flag_nopass = 0;
> + char *opt_user = NULL;
> + char *opt_kerberos = NULL;
> + char *opt_auth_file = NULL;
> + char *opt_debuglevel = NULL;
> +
> + struct poptOption long_options[] = {
> + { "help", 'h', POPT_ARG_NONE, &flag_help, 0,
> + "Display help message" },
> + { "version", 'V', POPT_ARG_NONE, &flag_version, 0,
> + "Display version number" },
> + { "user", 'U', POPT_ARG_STRING, &opt_user, 0,
> + "Set the network username", "[DOMAIN/]USERNAME[%PASSWORD]" },
> + { "authentication-file", 'A',
> + POPT_ARG_STRING, &opt_auth_file, 0,
> + "Get the credentials from a file", "FILE" },
> + { "no-pass", 'N', POPT_ARG_NONE, &flag_nopass, 0,
> + "Do not ask for a password", NULL },
> + { "kerberos", 'k', POPT_ARG_STRING, &opt_kerberos, 0,
> + "Use Kerberos, -k [yes|no]" },
> + { "debuglevel", 'd', POPT_ARG_STRING, &opt_debuglevel, 0,
> + "Set debug level", "DEBUGLEVEL" },
> + { "uninstall", 0, POPT_ARG_NONE, &flag_uninstall, 0,
> + "Uninstall winexe service after remote execution", NULL},
> + { "reinstall", 0, POPT_ARG_NONE, &flag_reinstall, 0,
> + "Reinstall winexe service before remote execution", NULL},
> + { "runas", 0, POPT_ARG_STRING, &options->runas, 0,
> + "Run as the given user (BEWARE: this password is sent "
> + "in cleartext over the network!)",
> + "[DOMAIN\\]USERNAME%PASSWORD"},
> + { "runas-file", 0, POPT_ARG_STRING, &options->runas_file, 0,
> + "Run as user options defined in a file", "FILE"},
> + { "interactive", 0, POPT_ARG_INT, &flag_interactive, 0,
> + "Desktop interaction: 0 - disallow, 1 - allow. If allow, "
> + "also use the --system switch (Windows requirement). Vista "
> + "does not support this option.", "0|1"},
> + { "ostype", 0, POPT_ARG_INT, &flag_ostype, 0,
> + "OS type: 0 - 32-bit, 1 - 64-bit, 2 - winexe will decide. "
> + "Determines which version (32-bit or 64-bit) of service "
> + "will be installed.", "0|1|2"},
> + POPT_TABLEEND
> + };
> +
> + ZERO_STRUCTP(options);
> +
> + pc = poptGetContext(argv[0], argc, (const char **) argv, long_options,
> + 0);
> +
> + poptSetOtherOptionHelp(pc, "[OPTION]... //HOST COMMAND\nOptions:");
> +
> + if (((opt = poptGetNextOpt(pc)) != -1) || flag_help || flag_version) {
> + fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR,
> + SAMBA_VERSION_MINOR);
> + if (flag_version) {
> + exit(0);
> + }
> + poptPrintHelp(pc, stdout, 0);
> + if (flag_help) {
> + exit(0);
> + }
> + exit(1);
> + }
> +
> + argv_new = discard_const_p(char *, poptGetArgs(pc));
> +
> + argc_new = argc;
> + for (i = 0; i < argc; i++) {
> + if (!argv_new || argv_new[i] == NULL) {
> + argc_new = i;
> + break;
> + }
> + }
> +
> + if (argc_new != 2 || argv_new[0][0] != '/' || argv_new[0][1] != '/') {
> + fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR,
> + SAMBA_VERSION_MINOR);
> + poptPrintHelp(pc, stdout, 0);
> + exit(1);
> + }
> +
> + if (opt_debuglevel) {
> + lp_set_cmdline("log level", opt_debuglevel);
> + }
> +
> + cred = cli_credentials_init(mem_ctx);
> +
> + if (opt_user) {
> + cli_credentials_parse_string(cred, opt_user, CRED_SPECIFIED);
> + } else if (opt_auth_file) {
> + cli_credentials_parse_file(cred, opt_auth_file,
> + CRED_SPECIFIED);
> + }
> +
> + cli_credentials_guess(cred, lp_ctx);
> + if (!cli_credentials_get_password(cred) && !flag_nopass) {
> + char *p = getpass("Enter password: ");
> + if (*p) {
> + cli_credentials_set_password(cred, p, CRED_SPECIFIED);
> + }
> + }
> +
> + if (opt_kerberos) {
> + cli_credentials_set_kerberos_state(cred,
> + strcmp(opt_kerberos, "yes")
> + ? CRED_MUST_USE_KERBEROS
> + : CRED_DONT_USE_KERBEROS);
> + }
> +
> + if (options->runas == NULL && options->runas_file != NULL) {
> + struct cli_credentials *runas_cred;
> + const char *user;
> + const char *pass;
> +
> + runas_cred = cli_credentials_init(mem_ctx);
> + cli_credentials_parse_file(runas_cred, options->runas_file,
> + CRED_SPECIFIED);
> +
> + user = cli_credentials_get_username(runas_cred);
> + pass = cli_credentials_get_password(runas_cred);
> +
> + if (user && pass) {
> + char buffer[1024];
> + const char *dom;
> +
> + dom = cli_credentials_get_domain(runas_cred);
> + if (dom) {
> + snprintf(buffer, sizeof(buffer), "%s\\%s%%%s",
> + dom, user, pass);
> + } else {
> + snprintf(buffer, sizeof(buffer), "%s%%%s",
> + user, pass);
> + }
> + buffer[sizeof(buffer)-1] = '\0';
> + options->runas = talloc_strdup(mem_ctx, buffer);
> + }
> + }
> +
> + options->credentials = cred;
> +
> + options->hostname = argv_new[0] + 2;
> + options->cmd = argv_new[1];
> +
> + options->flags = flag_interactive;
> + if (flag_reinstall) {
> + options->flags |= SVC_FORCE_UPLOAD;
> + }
> + if (flag_ostype == 1) {
> + options->flags |= SVC_OS64BIT;
> + }
> + if (flag_ostype == 2) {
> + options->flags |= SVC_OSCHOOSE;
> + }
> + if (flag_uninstall) {
> + options->flags |= SVC_UNINSTALL;
> + }
> +}
> +
> +static NTSTATUS winexe_svc_upload(
> + const char *hostname,
> + const char *service_filename,
> + const DATA_BLOB *svc32_exe,
> + const DATA_BLOB *svc64_exe,
> + struct cli_credentials *credentials,
> + int flags)
> +{
> + struct cli_state *cli;
> + uint16_t fnum;
> + NTSTATUS status;
> + const DATA_BLOB *binary = NULL;
> +
> + status = cli_full_connection_creds(
> + &cli,
> + NULL,
> + hostname,
> + NULL,
> + 445,
> + "ADMIN$",
> + "?????",
> + credentials,
> + 0,
> + 0);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("cli_full_connection_creds failed: %s\n",
> + nt_errstr(status));
> + return status;
> + }
> +
> + if (flags & SVC_FORCE_UPLOAD) {
> + status = cli_unlink(cli, service_filename, 0);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("cli_unlink failed: %s\n",
> + nt_errstr(status));
> + }
> + }
> +
> + if (flags & SVC_OSCHOOSE) {
> + status = cli_chkpath(cli, "SysWoW64");
> + if (NT_STATUS_IS_OK(status)) {
> + flags |= SVC_OS64BIT;
> + }
> + }
> +
> + if (flags & SVC_OS64BIT) {
> + binary = svc64_exe;
> + } else {
> + binary = svc32_exe;
> + }
> +
> + if (binary == NULL) {
> + //TODO
> + }
> +
> + status = cli_ntcreate(
> + cli,
> + service_filename,
> + 0, /* CreatFlags */
> + SEC_FILE_WRITE_DATA, /* DesiredAccess */
> + FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
> + FILE_SHARE_WRITE|FILE_SHARE_READ, /* ShareAccess */
> + FILE_OPEN_IF, /* CreateDisposition */
> + FILE_NON_DIRECTORY_FILE, /* CreateOptions */
> + 0, /* SecurityFlags */
> + &fnum,
> + NULL); /* CreateReturns */
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("Could not create %s: %s\n", service_filename,
> + nt_errstr(status));
> + goto done;
> + }
> +
> + status = cli_writeall(
> + cli,
> + fnum,
> + 0,
> + binary->data,
> + 0,
> + binary->length,
> + NULL);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("Could not write file: %s\n", nt_errstr(status));
> + goto close_done;
> + }
> +
> +close_done:
> + status = cli_close(cli, fnum);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("Close(%"PRIu16") failed for %s: %s\n", fnum,
> + service_filename, nt_errstr(status));
> + }
> +done:
> + TALLOC_FREE(cli);
> + return status;
> +}
> +
> +static NTSTATUS winexe_svc_install(
> + struct cli_state *cli,
> + const char *hostname,
> + const char *service_name,
> + const char *service_filename,
> + const DATA_BLOB *svc32_exe,
> + const DATA_BLOB *svc64_exe,
> + struct cli_credentials *credentials,
> + int flags)
> +{
> + TALLOC_CTX *frame = talloc_stackframe();
> + struct rpc_pipe_client *rpccli;
> + struct policy_handle scmanager_handle;
> + struct policy_handle service_handle;
> + struct SERVICE_STATUS service_status;
> + bool need_start = false;
> + bool need_conf = false;
> + NTSTATUS status;
> + WERROR werr;
> +
> + status = cli_rpc_pipe_open_noauth_transport(
> + cli,
> + NCACN_NP,
> + &ndr_table_svcctl,
> + &rpccli);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n",
> + nt_errstr(status));
> + goto done;
> + }
> +
> + status = dcerpc_svcctl_OpenSCManagerW(
> + rpccli->binding_handle,
> + frame,
> + smbXcli_conn_remote_name(cli->conn),
> + NULL,
> + SEC_FLAG_MAXIMUM_ALLOWED,
> + &scmanager_handle,
> + &werr);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
> + nt_errstr(status));
> + goto done;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
> + win_errstr(werr));
> + goto done;
> + }
> +
> + status = dcerpc_svcctl_OpenServiceW(
> + rpccli->binding_handle,
> + frame,
> + &scmanager_handle,
> + service_name,
> + SERVICE_ALL_ACCESS,
> + &service_handle,
> + &werr);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
> + nt_errstr(status));
> + goto close_scmanager;
> + }
> +
> + if (W_ERROR_EQUAL(werr, WERR_SERVICE_DOES_NOT_EXIST)) {
> + status = dcerpc_svcctl_CreateServiceW(
> + rpccli->binding_handle,
> + frame,
> + &scmanager_handle,
> + service_name,
> + NULL,
> + SERVICE_ALL_ACCESS,
> + SERVICE_TYPE_WIN32_OWN_PROCESS |
> + ((flags & SVC_INTERACTIVE) ?
> + SERVICE_TYPE_INTERACTIVE_PROCESS : 0),
> + SVCCTL_DEMAND_START,
> + SVCCTL_SVC_ERROR_NORMAL,
> + service_filename,
> + NULL,
> + NULL,
> + NULL,
> + 0,
> + NULL,
> + NULL,
> + 0,
> + &service_handle,
> + &werr);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_CreateServiceW "
> + "failed: %s\n", nt_errstr(status));
> + goto close_scmanager;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_CreateServiceW "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_scmanager;
> + }
> + }
> +
> + status = dcerpc_svcctl_QueryServiceStatus(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + &service_status,
> + &werr);
> +
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> +
> + if (!(flags & SVC_IGNORE_INTERACTIVE)) {
> + need_conf =
> + !(service_status.type &
> + SERVICE_TYPE_INTERACTIVE_PROCESS) ^
> + !(flags & SVC_INTERACTIVE);
> + }
> +
> + if (service_status.state == SVCCTL_STOPPED) {
> + need_start = true;
> + } else if (need_conf) {
> + status = dcerpc_svcctl_ControlService(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + SVCCTL_CONTROL_STOP,
> + &service_status,
> + &werr);
> +
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> +
> + do {
> + smb_msleep(100);
> +
> + status = dcerpc_svcctl_QueryServiceStatus(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + &service_status,
> + &werr);
> +
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> + } while (service_status.state == SVCCTL_STOP_PENDING);
> +
> + need_start = 1;
> + }
> +
> + if (need_conf) {
> + status = dcerpc_svcctl_ChangeServiceConfigW(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + SERVICE_TYPE_WIN32_OWN_PROCESS |
> + ((flags & SVC_INTERACTIVE) ?
> + SERVICE_TYPE_INTERACTIVE_PROCESS : 0), /* type */
> + UINT32_MAX, /* start_type, SERVICE_NO_CHANGE */
> + UINT32_MAX, /* error_control, SERVICE_NO_CHANGE */
> + NULL, /* binary_path */
> + NULL, /* load_order_group */
> + NULL, /* tag_id */
> + NULL, /* dependencies */
> + NULL, /* service_start_name */
> + NULL, /* password */
> + NULL, /* display_name */
> + &werr);
> +
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> + }
> +
> + if (need_start) {
> + status = winexe_svc_upload(
> + hostname,
> + service_filename,
> + svc32_exe,
> + svc64_exe,
> + credentials,
> + flags);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("winexe_svc_upload failed: %s\n",
> + nt_errstr(status));
> + goto close_service;
> + }
> +
> + status = dcerpc_svcctl_StartServiceW(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + 0, /* num_args */
> + NULL, /* arguments */
> + &werr);
> +
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_StartServiceW "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_StartServiceW "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> +
> + do {
> + smb_msleep(100);
> +
> + status = dcerpc_svcctl_QueryServiceStatus(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + &service_status,
> + &werr);
> +
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> + } while (service_status.state == SVCCTL_START_PENDING);
> +
> + if (service_status.state != SVCCTL_RUNNING) {
> + DBG_WARNING("Failed to start service\n");
> + status = NT_STATUS_UNSUCCESSFUL;
> + goto close_service;
> + }
> + }
> +
> +close_service:
> + {
> + NTSTATUS close_status;
> + WERROR close_werr;
> +
> + close_status = dcerpc_svcctl_CloseServiceHandle(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + &close_werr);
> + if (!NT_STATUS_IS_OK(close_status)) {
> + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
> + "failed: %s\n", nt_errstr(close_status));
> + goto done;
> + }
> + if (!W_ERROR_IS_OK(close_werr)) {
> + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
> + " failed: %s\n", win_errstr(close_werr));
> + goto done;
> + }
> + }
> +
> +close_scmanager:
> + {
> + NTSTATUS close_status;
> + WERROR close_werr;
> +
> + close_status = dcerpc_svcctl_CloseServiceHandle(
> + rpccli->binding_handle,
> + frame,
> + &scmanager_handle,
> + &close_werr);
> + if (!NT_STATUS_IS_OK(close_status)) {
> + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
> + "failed: %s\n", nt_errstr(close_status));
> + goto done;
> + }
> + if (!W_ERROR_IS_OK(close_werr)) {
> + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
> + " failed: %s\n", win_errstr(close_werr));
> + goto done;
> + }
> + }
> +
> +done:
> + TALLOC_FREE(rpccli);
> + TALLOC_FREE(frame);
> + return status;
> +}
> +
> +static NTSTATUS winexe_svc_uninstall(
> + struct cli_state *cli,
> + const char *service_name)
> +{
> + TALLOC_CTX *frame = talloc_stackframe();
> + struct rpc_pipe_client *rpccli;
> + struct policy_handle scmanager_handle;
> + struct policy_handle service_handle;
> + struct SERVICE_STATUS service_status;
> + NTSTATUS status;
> + WERROR werr;
> +
> + status = cli_rpc_pipe_open_noauth_transport(
> + cli,
> + NCACN_NP,
> + &ndr_table_svcctl,
> + &rpccli);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n",
> + nt_errstr(status));
> + goto done;
> + }
> +
> + status = dcerpc_svcctl_OpenSCManagerW(
> + rpccli->binding_handle,
> + frame,
> + smbXcli_conn_remote_name(cli->conn),
> + NULL,
> + SEC_FLAG_MAXIMUM_ALLOWED,
> + &scmanager_handle,
> + &werr);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
> + nt_errstr(status));
> + goto done;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
> + win_errstr(werr));
> + goto done;
> + }
> +
> + status = dcerpc_svcctl_OpenServiceW(
> + rpccli->binding_handle,
> + frame,
> + &scmanager_handle,
> + service_name,
> + SERVICE_ALL_ACCESS,
> + &service_handle,
> + &werr);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
> + nt_errstr(status));
> + goto close_scmanager;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
> + win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_scmanager;
> + }
> +
> + status = dcerpc_svcctl_ControlService(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + SVCCTL_CONTROL_STOP,
> + &service_status,
> + &werr);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> +
> + do {
> + smb_msleep(100);
> +
> + status = dcerpc_svcctl_QueryServiceStatus(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + &service_status,
> + &werr);
> +
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> + } while (service_status.state != SVCCTL_STOPPED);
> +
> + status = dcerpc_svcctl_DeleteService(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + &werr);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("dcerpc_svcctl_DeleteService "
> + "failed: %s\n", nt_errstr(status));
> + goto close_service;
> + }
> + if (!W_ERROR_IS_OK(werr)) {
> + DBG_WARNING("dcerpc_svcctl_DeleteService "
> + "failed: %s\n", win_errstr(werr));
> + status = werror_to_ntstatus(werr);
> + goto close_service;
> + }
> +
> +close_service:
> + {
> + NTSTATUS close_status;
> + WERROR close_werr;
> +
> + close_status = dcerpc_svcctl_CloseServiceHandle(
> + rpccli->binding_handle,
> + frame,
> + &service_handle,
> + &close_werr);
> + if (!NT_STATUS_IS_OK(close_status)) {
> + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
> + "failed: %s\n", nt_errstr(close_status));
> + goto done;
> + }
> + if (!W_ERROR_IS_OK(close_werr)) {
> + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
> + " failed: %s\n", win_errstr(close_werr));
> + goto done;
> + }
> + }
> +
> +close_scmanager:
> + {
> + NTSTATUS close_status;
> + WERROR close_werr;
> +
> + close_status = dcerpc_svcctl_CloseServiceHandle(
> + rpccli->binding_handle,
> + frame,
> + &scmanager_handle,
> + &close_werr);
> + if (!NT_STATUS_IS_OK(close_status)) {
> + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
> + "failed: %s\n", nt_errstr(close_status));
> + goto done;
> + }
> + if (!W_ERROR_IS_OK(close_werr)) {
> + DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
> + " failed: %s\n", win_errstr(close_werr));
> + goto done;
> + }
> + }
> +
> +done:
> + TALLOC_FREE(rpccli);
> + TALLOC_FREE(frame);
> + return status;
> +}
> +
> +struct winexe_out_pipe_state {
> + struct tevent_context *ev;
> + struct cli_state *cli;
> + uint16_t out_pipe;
> + int out_fd;
> + char out_inbuf[256];
> +};
> +
> +static void winexe_out_pipe_opened(struct tevent_req *subreq);
> +static void winexe_out_pipe_got_data(struct tevent_req *subreq);
> +static void winexe_out_pipe_closed(struct tevent_req *subreq);
> +
> +static struct tevent_req *winexe_out_pipe_send(
> + TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + const char *pipe_name,
> + int out_fd)
> +{
> + struct tevent_req *req, *subreq;
> + struct winexe_out_pipe_state *state;
> +
> + req = tevent_req_create(mem_ctx, &state,
> + struct winexe_out_pipe_state);
> + if (req == NULL) {
> + return NULL;
> + }
> + state->ev = ev;
> + state->cli = cli;
> + state->out_fd = out_fd;
> +
> + subreq = cli_ntcreate_send(
> + state,
> + state->ev,
> + state->cli,
> + pipe_name,
> + 0,
> + SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
> + SEC_RIGHTS_FILE_EXECUTE,
> + 0, /* FileAttributes */
> + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
> + FILE_OPEN, /* CreateDisposition */
> + 0, /* CreateOptions */
> + 0); /* SecurityFlags */
> + if (tevent_req_nomem(subreq, req)) {
> + return tevent_req_post(req, ev);
> + }
> + tevent_req_set_callback(subreq, winexe_out_pipe_opened, req);
> + return req;
> +}
> +
> +static void winexe_out_pipe_opened(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_out_pipe_state *state = tevent_req_data(
> + req, struct winexe_out_pipe_state);
> + int timeout;
> + NTSTATUS status;
> +
> + status = cli_ntcreate_recv(subreq, &state->out_pipe, NULL);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + timeout = state->cli->timeout;
> + state->cli->timeout = 0;
> +
> + subreq = cli_read_send(
> + state,
> + state->ev,
> + state->cli,
> + state->out_pipe,
> + state->out_inbuf,
> + 0,
> + sizeof(state->out_inbuf));
> +
> + state->cli->timeout = timeout;
> +
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req);
> +}
> +
> +static void winexe_out_pipe_got_data(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_out_pipe_state *state = tevent_req_data(
> + req, struct winexe_out_pipe_state);
> + NTSTATUS status;
> + int timeout;
> + size_t received;
> + ssize_t written;
> +
> + status = cli_read_recv(subreq, &received);
> + TALLOC_FREE(subreq);
> +
> + DBG_DEBUG("cli_read for %d gave %s\n",
> + state->out_fd,
> + nt_errstr(status));
> +
> + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
> + subreq = cli_close_send(
> + state,
> + state->ev,
> + state->cli,
> + state->out_pipe);
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_out_pipe_closed, req);
> + return;
> + }
> +
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + if (received > 0) {
> + written = sys_write(state->out_fd, state->out_inbuf, received);
> + if (written == -1) {
> + tevent_req_nterror(req, map_nt_error_from_unix(errno));
> + return;
> + }
> + }
> +
> + timeout = state->cli->timeout;
> + state->cli->timeout = 0;
> +
> + subreq = cli_read_send(
> + state,
> + state->ev,
> + state->cli,
> + state->out_pipe,
> + state->out_inbuf,
> + 0,
> + sizeof(state->out_inbuf));
> +
> + state->cli->timeout = timeout;
> +
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req);
> +}
> +
> +static void winexe_out_pipe_closed(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + NTSTATUS status;
> +
> + status = cli_close_recv(subreq);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> + tevent_req_done(req);
> +}
> +
> +static NTSTATUS winexe_out_pipe_recv(struct tevent_req *req)
> +{
> + return tevent_req_simple_recv_ntstatus(req);
> +}
> +
> +struct winexe_in_pipe_state {
> + struct tevent_context *ev;
> + struct cli_state *cli;
> + struct tevent_req *fd_read_req;
> + bool close_requested;
> + bool closing;
> + uint16_t in_pipe;
> + int in_fd;
> + char inbuf[256];
> +};
> +
> +static void winexe_in_pipe_opened(struct tevent_req *subreq);
> +static void winexe_in_pipe_got_data(struct tevent_req *subreq);
> +static void winexe_in_pipe_written(struct tevent_req *subreq);
> +static void winexe_in_pipe_closed(struct tevent_req *subreq);
> +
> +static struct tevent_req *winexe_in_pipe_send(
> + TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + const char *pipe_name,
> + int in_fd)
> +{
> + struct tevent_req *req, *subreq;
> + struct winexe_in_pipe_state *state;
> +
> + req = tevent_req_create(mem_ctx, &state,
> + struct winexe_in_pipe_state);
> + if (req == NULL) {
> + return NULL;
> + }
> + state->ev = ev;
> + state->cli = cli;
> + state->in_fd = in_fd;
> +
> + subreq = cli_ntcreate_send(
> + state,
> + state->ev,
> + state->cli,
> + pipe_name,
> + 0,
> + SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
> + SEC_RIGHTS_FILE_EXECUTE,
> + 0, /* FileAttributes */
> + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
> + FILE_OPEN, /* CreateDisposition */
> + 0, /* CreateOptions */
> + 0); /* SecurityFlags */
> + if (tevent_req_nomem(subreq, req)) {
> + return tevent_req_post(req, ev);
> + }
> + tevent_req_set_callback(subreq, winexe_in_pipe_opened, req);
> + return req;
> +}
> +
> +static void winexe_in_pipe_opened(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_in_pipe_state *state = tevent_req_data(
> + req, struct winexe_in_pipe_state);
> + NTSTATUS status;
> +
> + status = cli_ntcreate_recv(subreq, &state->in_pipe, NULL);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + subreq = wait_for_read_send(
> + state,
> + state->ev,
> + state->in_fd,
> + true);
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req);
> +
> + state->fd_read_req = subreq;
> +}
> +
> +static void winexe_in_pipe_got_data(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_in_pipe_state *state = tevent_req_data(
> + req, struct winexe_in_pipe_state);
> + int err;
> + bool ok;
> + int timeout;
> + ssize_t nread;
> +
> + ok = wait_for_read_recv(subreq, &err);
> + TALLOC_FREE(subreq);
> + if (!ok) {
> + tevent_req_nterror(req, map_nt_error_from_unix(err));
> + return;
> + }
> + state->fd_read_req = NULL;
> +
> + nread = sys_read(state->in_fd, &state->inbuf, sizeof(state->inbuf));
> + if (nread == -1) {
> + tevent_req_nterror(req, map_nt_error_from_unix(errno));
> + return;
> + }
> + if (nread == 0) {
> + tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
> + return;
> + }
> +
> + timeout = state->cli->timeout;
> + state->cli->timeout = 0;
> +
> + subreq = cli_writeall_send(
> + state,
> + state->ev,
> + state->cli,
> + state->in_pipe,
> + 0,
> + (uint8_t *)state->inbuf,
> + 0,
> + nread);
> +
> + state->cli->timeout = timeout;
> +
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_in_pipe_written, req);
> +}
> +
> +static void winexe_in_pipe_written(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_in_pipe_state *state = tevent_req_data(
> + req, struct winexe_in_pipe_state);
> + NTSTATUS status;
> +
> + status = cli_writeall_recv(subreq, NULL);
> + TALLOC_FREE(subreq);
> +
> + DBG_DEBUG("cli_writeall for %d gave %s\n",
> + state->in_fd,
> + nt_errstr(status));
> +
> + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED) ||
> + state->close_requested) {
> + subreq = cli_close_send(
> + state,
> + state->ev,
> + state->cli,
> + state->in_pipe);
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_in_pipe_closed, req);
> + state->closing = true;
> + return;
> + }
> +
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + subreq = wait_for_read_send(
> + state,
> + state->ev,
> + state->in_fd,
> + true);
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req);
> +
> + state->fd_read_req = subreq;
> +}
> +
> +static void winexe_in_pipe_closed(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + NTSTATUS status;
> +
> + status = cli_close_recv(subreq);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> + return tevent_req_done(req);
> +}
> +
> +static NTSTATUS winexe_in_pipe_recv(struct tevent_req *req)
> +{
> + return tevent_req_simple_recv_ntstatus(req);
> +}
> +
> +static bool winexe_in_pipe_close(struct tevent_req *req)
> +{
> + struct winexe_in_pipe_state *state = tevent_req_data(
> + req, struct winexe_in_pipe_state);
> + struct tevent_req *subreq;
> +
> + if (state->closing) {
> + return true;
> + }
> +
> + if (state->fd_read_req == NULL) {
> + /*
> + * cli_writeall active, wait for it to return
> + */
> + state->close_requested = true;
> + return true;
> + }
> +
> + TALLOC_FREE(state->fd_read_req);
> +
> + subreq = cli_close_send(
> + state,
> + state->ev,
> + state->cli,
> + state->in_pipe);
> + if (subreq == NULL) {
> + return false;
> + }
> + tevent_req_set_callback(subreq, winexe_in_pipe_closed, req);
> + state->closing = true;
> +
> + return true;
> +}
> +
> +struct winexe_pipes_state {
> + struct tevent_req *pipes[3];
> +};
> +
> +static void winexe_pipes_stdin_done(struct tevent_req *subreq);
> +static void winexe_pipes_stdout_done(struct tevent_req *subreq);
> +static void winexe_pipes_stderr_done(struct tevent_req *subreq);
> +
> +static struct tevent_req *winexe_pipes_send(
> + TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + const char *pipe_postfix)
> +{
> + struct tevent_req *req;
> + struct winexe_pipes_state *state;
> + char *pipe_name;
> +
> + req = tevent_req_create(mem_ctx, &state, struct winexe_pipes_state);
> + if (req == NULL) {
> + return NULL;
> + }
> +
> + pipe_name = talloc_asprintf(state, "\\ahexec_stdin%s", pipe_postfix);
> + if (tevent_req_nomem(pipe_name, req)) {
> + return tevent_req_post(req, ev);
> + }
> + state->pipes[0] = winexe_in_pipe_send(
> + state,
> + ev,
> + cli,
> + pipe_name,
> + 0);
> + if (tevent_req_nomem(state->pipes[0], req)) {
> + return tevent_req_post(req, ev);
> + }
> + tevent_req_set_callback(state->pipes[0], winexe_pipes_stdin_done, req);
> +
> + pipe_name = talloc_asprintf(state, "\\ahexec_stdout%s", pipe_postfix);
> + if (tevent_req_nomem(pipe_name, req)) {
> + return tevent_req_post(req, ev);
> + }
> + state->pipes[1] = winexe_out_pipe_send(
> + state,
> + ev,
> + cli,
> + pipe_name,
> + 1);
> + if (tevent_req_nomem(state->pipes[1], req)) {
> + return tevent_req_post(req, ev);
> + }
> + tevent_req_set_callback(state->pipes[1], winexe_pipes_stdout_done,
> + req);
> +
> + pipe_name = talloc_asprintf(state, "\\ahexec_stderr%s", pipe_postfix);
> + if (tevent_req_nomem(pipe_name, req)) {
> + return tevent_req_post(req, ev);
> + }
> + state->pipes[2] = winexe_out_pipe_send(
> + state,
> + ev,
> + cli,
> + pipe_name,
> + 2);
> + if (tevent_req_nomem(state->pipes[2], req)) {
> + return tevent_req_post(req, ev);
> + }
> + tevent_req_set_callback(state->pipes[2], winexe_pipes_stderr_done,
> + req);
> +
> + DBG_DEBUG("pipes = %p %p %p\n",
> + state->pipes[0],
> + state->pipes[1],
> + state->pipes[2]);
> +
> + return req;
> +}
> +
> +static void winexe_pipes_stdin_done(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_pipes_state *state = tevent_req_data(
> + req, struct winexe_pipes_state);
> + NTSTATUS status;
> +
> + status = winexe_in_pipe_recv(subreq);
> + TALLOC_FREE(subreq);
> +
> + DBG_DEBUG("stdin returned %s\n", nt_errstr(status));
> +
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + state->pipes[0] = NULL;
> +
> + DBG_DEBUG("pipes = %p %p %p\n",
> + state->pipes[0],
> + state->pipes[1],
> + state->pipes[2]);
> +
> + if ((state->pipes[1] == NULL) && (state->pipes[2] == NULL)) {
> + tevent_req_done(req);
> + }
> +}
> +
> +static void winexe_pipes_stdout_done(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_pipes_state *state = tevent_req_data(
> + req, struct winexe_pipes_state);
> + NTSTATUS status;
> +
> + status = winexe_out_pipe_recv(subreq);
> + TALLOC_FREE(subreq);
> +
> + DBG_DEBUG("stdout returned %s\n", nt_errstr(status));
> +
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + if (state->pipes[0] != NULL) {
> + winexe_in_pipe_close(state->pipes[0]);
> + }
> +
> + state->pipes[1] = NULL;
> +
> + DBG_DEBUG("pipes = %p %p %p\n",
> + state->pipes[0],
> + state->pipes[1],
> + state->pipes[2]);
> +
> + if ((state->pipes[0] == NULL) && (state->pipes[2] == NULL)) {
> + tevent_req_done(req);
> + }
> +}
> +
> +static void winexe_pipes_stderr_done(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_pipes_state *state = tevent_req_data(
> + req, struct winexe_pipes_state);
> + NTSTATUS status;
> +
> + status = winexe_out_pipe_recv(subreq);
> + TALLOC_FREE(subreq);
> +
> + DBG_DEBUG("stderr returned %s\n", nt_errstr(status));
> +
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + if (state->pipes[0] != NULL) {
> + winexe_in_pipe_close(state->pipes[0]);
> + }
> +
> + state->pipes[2] = NULL;
> +
> + DBG_DEBUG("pipes = %p %p %p\n",
> + state->pipes[0],
> + state->pipes[1],
> + state->pipes[2]);
> +
> + if ((state->pipes[0] == NULL) && (state->pipes[1] == NULL)) {
> + tevent_req_done(req);
> + }
> +}
> +
> +static NTSTATUS winexe_pipes_recv(struct tevent_req *req)
> +{
> + return tevent_req_simple_recv_ntstatus(req);
> +}
> +
> +struct winexe_ctrl_state {
> + struct tevent_context *ev;
> + struct cli_state *cli;
> +
> + uint16_t ctrl_pipe;
> + bool ctrl_pipe_done;
> +
> + char ctrl_inbuf[256];
> + char *cmd;
> + int return_code;
> +
> + struct tevent_req *pipes_req;
> +};
> +
> +static void winexe_ctrl_opened(struct tevent_req *subreq);
> +static void winexe_ctrl_got_read(struct tevent_req *subreq);
> +static void winexe_ctrl_wrote_version(struct tevent_req *subreq);
> +static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq);
> +static void winexe_ctrl_pipes_done(struct tevent_req *subreq);
> +static void winexe_ctrl_pipe_closed(struct tevent_req *subreq);
> +
> +static struct tevent_req *winexe_ctrl_send(
> + TALLOC_CTX *mem_ctx,
> + struct tevent_context *ev,
> + struct cli_state *cli,
> + const char *cmd)
> +{
> + struct tevent_req *req, *subreq;
> + struct winexe_ctrl_state *state;
> +
> + req = tevent_req_create(mem_ctx, &state,
> + struct winexe_ctrl_state);
> + if (req == NULL) {
> + return NULL;
> + }
> + state->ev = ev;
> + state->cli = cli;
> +
> + state->cmd = talloc_asprintf(state, "run %s\n", cmd);
> + if (tevent_req_nomem(state->cmd, req)) {
> + return tevent_req_post(req, ev);
> + }
> +
> + subreq = cli_ntcreate_send(
> + state,
> + state->ev,
> + state->cli,
> + "\\" PIPE_NAME,
> + 0,
> + SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
> + SEC_RIGHTS_FILE_EXECUTE,
> + 0, /* FileAttributes */
> + FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
> + FILE_OPEN, /* CreateDisposition */
> + 0, /* CreateOptions */
> + 0); /* SecurityFlags */
> + if (tevent_req_nomem(subreq, req)) {
> + return tevent_req_post(req, ev);
> + }
> + tevent_req_set_callback(subreq, winexe_ctrl_opened, req);
> + return req;
> +}
> +
> +static void winexe_ctrl_opened(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_ctrl_state *state = tevent_req_data(
> + req, struct winexe_ctrl_state);
> + int timeout;
> + NTSTATUS status;
> + static const char cmd[] = "get codepage\nget version\n";
> +
> + status = cli_ntcreate_recv(subreq, &state->ctrl_pipe, NULL);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + timeout = state->cli->timeout;
> + state->cli->timeout = 0;
> +
> + subreq = cli_read_send(
> + state,
> + state->ev,
> + state->cli,
> + state->ctrl_pipe,
> + state->ctrl_inbuf,
> + 0,
> + sizeof(state->ctrl_inbuf)-1);
> +
> + state->cli->timeout = timeout;
> +
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_ctrl_got_read, req);
> +
> + subreq = cli_writeall_send(
> + state,
> + state->ev,
> + state->cli,
> + state->ctrl_pipe,
> + 0,
> + (const uint8_t *)cmd,
> + 0,
> + strlen(cmd));
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_ctrl_wrote_version, req);
> +}
> +
> +static void winexe_ctrl_got_read(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_ctrl_state *state = tevent_req_data(
> + req, struct winexe_ctrl_state);
> + NTSTATUS status;
> + int timeout;
> + size_t received;
> + unsigned int version, return_code;
> + int ret;
> +
> + status = cli_read_recv(subreq, &received);
> + TALLOC_FREE(subreq);
> +
> + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
> + subreq = cli_close_send(
> + state,
> + state->ev,
> + state->cli,
> + state->ctrl_pipe);
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_ctrl_pipe_closed, req);
> + return;
> + }
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + DBG_DEBUG("Got %zu bytes\n", received);
> +
> + timeout = state->cli->timeout;
> + state->cli->timeout = 0;
> +
> + subreq = cli_read_send(
> + state,
> + state->ev,
> + state->cli,
> + state->ctrl_pipe,
> + state->ctrl_inbuf,
> + 0,
> + sizeof(state->ctrl_inbuf)-1);
> +
> + state->cli->timeout = timeout;
> +
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_ctrl_got_read, req);
> +
> + ret = sscanf(state->ctrl_inbuf, "version 0x%x\n", &version);
> + if (ret == 1) {
> + DBG_DEBUG("Got version %x\n", version);
> +
> + subreq = cli_writeall_send(
> + state,
> + state->ev,
> + state->cli,
> + state->ctrl_pipe,
> + 0,
> + (const uint8_t *)state->cmd,
> + 0,
> + strlen(state->cmd));
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_ctrl_wrote_cmd, req);
> + return;
> + }
> +
> + ret = strncmp(state->ctrl_inbuf, "std_io_err ", strlen("std_io_err "));
> + if (ret == 0) {
> + char *p = state->ctrl_inbuf + 11;
> + char *q = strchr(state->ctrl_inbuf, '\n');
> + char *postfix;
> + size_t postfix_len;
> +
> + if (q == NULL) {
> + DBG_DEBUG("Got invalid pipe postfix\n");
> + return;
> + }
> +
> + postfix_len = q - p;
> +
> + postfix = talloc_strndup(state, p, postfix_len);
> + if (tevent_req_nomem(postfix, req)) {
> + return;
> + }
> +
> + DBG_DEBUG("Got pipe postfix %s\n", postfix);
> +
> + subreq = winexe_pipes_send(
> + state,
> + state->ev,
> + state->cli,
> + postfix);
> + if (tevent_req_nomem(subreq, req)) {
> + return;
> + }
> + tevent_req_set_callback(subreq, winexe_ctrl_pipes_done, req);
> +
> + state->pipes_req = subreq;
> +
> + return;
> + }
> +
> + ret = strncmp(state->ctrl_inbuf, "error ", strlen("error "));
> + if (ret == 0) {
> + printf("Error: %s", state->ctrl_inbuf);
> + return;
> + }
> +
> + ret = sscanf(state->ctrl_inbuf, "version 0x%x\n", &return_code);
> + if (ret == 1) {
> + state->return_code = return_code;
> + return;
> + }
> +}
> +
> +static void winexe_ctrl_wrote_version(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + NTSTATUS status;
> +
> + status = cli_writeall_recv(subreq, NULL);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +}
> +
> +static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + NTSTATUS status;
> +
> + status = cli_writeall_recv(subreq, NULL);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +}
> +
> +static void winexe_ctrl_pipe_closed(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_ctrl_state *state = tevent_req_data(
> + req, struct winexe_ctrl_state);
> + NTSTATUS status;
> +
> + status = cli_close_recv(subreq);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + state->ctrl_pipe_done = true;
> + if (state->pipes_req == NULL) {
> + tevent_req_done(req);
> + }
> +}
> +
> +static void winexe_ctrl_pipes_done(struct tevent_req *subreq)
> +{
> + struct tevent_req *req = tevent_req_callback_data(
> + subreq, struct tevent_req);
> + struct winexe_ctrl_state *state = tevent_req_data(
> + req, struct winexe_ctrl_state);
> + NTSTATUS status;
> +
> + status = winexe_pipes_recv(subreq);
> + TALLOC_FREE(subreq);
> + if (tevent_req_nterror(req, status)) {
> + return;
> + }
> +
> + state->pipes_req = NULL;
> + if (state->ctrl_pipe_done) {
> + tevent_req_done(req);
> + }
> +}
> +
> +static NTSTATUS winexe_ctrl_recv(struct tevent_req *req,
> + int *preturn_code)
> +{
> + struct winexe_ctrl_state *state = tevent_req_data(
> + req, struct winexe_ctrl_state);
> + NTSTATUS status;
> +
> + if (tevent_req_is_nterror(req, &status)) {
> + return status;
> + }
> + if (preturn_code != NULL) {
> + *preturn_code = state->return_code;
> + }
> + return NT_STATUS_OK;
> +}
> +
> +static NTSTATUS winexe_ctrl(struct cli_state *cli,
> + const char *cmd,
> + int *preturn_code)
> +{
> + struct tevent_context *ev = NULL;
> + struct tevent_req *req = NULL;
> + NTSTATUS status = NT_STATUS_NO_MEMORY;
> + bool ok;
> +
> + ev = samba_tevent_context_init(cli);
> + if (ev == NULL) {
> + goto done;
> + }
> + req = winexe_ctrl_send(ev, ev, cli, cmd);
> + if (req == NULL) {
> + goto done;
> + }
> + ok = tevent_req_poll_ntstatus(req, ev, &status);
> + if (!ok) {
> + goto done;
> + }
> + status = winexe_ctrl_recv(req, preturn_code);
> +done:
> + TALLOC_FREE(req);
> + TALLOC_FREE(ev);
> + return status;
> +}
> +
> +#ifdef HAVE_WINEXE_CC_WIN32
> +const DATA_BLOB *winexesvc32_exe_binary(void);
> +#endif
> +
> +#ifdef HAVE_WINEXE_CC_WIN64
> +const DATA_BLOB *winexesvc64_exe_binary(void);
> +#endif
> +
> +int main(int argc, const char *argv[])
> +{
> + TALLOC_CTX *frame = talloc_stackframe();
> + struct program_options options = {0};
> + struct loadparm_context *lp_ctx;
> + struct cli_state *cli;
> + const char *service_name = SERVICE_NAME;
> + char *service_filename = NULL;
> +#ifdef HAVE_WINEXE_CC_WIN32
> + const DATA_BLOB *winexesvc32_exe = winexesvc32_exe_binary();
> +#else
> + const DATA_BLOB *winexesvc32_exe = NULL;
> +#endif
> +#ifdef HAVE_WINEXE_CC_WIN64
> + const DATA_BLOB *winexesvc64_exe = winexesvc64_exe_binary();
> +#else
> + const DATA_BLOB *winexesvc64_exe = NULL;
> +#endif
> + NTSTATUS status;
> + int ret = 1;
> + int return_code = 0;
> +
> + lp_ctx = loadparm_init_s3(frame, loadparm_s3_helpers());
> + if (lp_ctx == NULL) {
> + fprintf(stderr, "loadparm_init_s3 failed\n");
> + goto done;
> + }
> +
> + smb_init_locale();
> + setup_logging("winexe", DEBUG_STDOUT);
> +
> + lp_load_global(get_dyn_CONFIGFILE());
> +
> + parse_args(argc, argv, frame, &options, lp_ctx);
> +
> + if (options.cmd == NULL) {
> + fprintf(stderr, "no cmd given\n");
> + goto done;
> + }
> +
> + service_filename = talloc_asprintf(frame, "%s.exe", service_name);
> + if (service_filename == NULL) {
> + DBG_WARNING("talloc_asprintf failed\n");
> + goto done;
> + }
> +
> + status = cli_full_connection_creds(
> + &cli,
> + NULL,
> + options.hostname,
> + NULL,
> + 445,
> + "IPC$",
> + "?????",
> + options.credentials,
> + 0,
> + 0);
> +
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("cli_full_connection_creds failed: %s\n",
> + nt_errstr(status));
> + goto done;
> + }
> +
> + status = winexe_svc_install(
> + cli,
> + options.hostname,
> + service_name,
> + service_filename,
> + winexesvc32_exe,
> + winexesvc64_exe,
> + options.credentials,
> + options.flags);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("winexe_svc_install failed: %s\n",
> + nt_errstr(status));
> + goto done;
> + }
> +
> + status = winexe_ctrl(cli, options.cmd, &return_code);
> + if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
> + /* Normal finish */
> + status = NT_STATUS_OK;
> + }
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("cli_ctrl failed: %s\n",
> + nt_errstr(status));
> + goto done;
> + }
> +
> + if (options.flags & SVC_UNINSTALL) {
> + status = winexe_svc_uninstall(
> + cli,
> + service_name);
> + if (!NT_STATUS_IS_OK(status)) {
> + DBG_WARNING("winexe_svc_uninstall failed: %s\n",
> + nt_errstr(status));
> + goto done;
> + }
> + }
> +
> + ret = return_code;
> +done:
> + TALLOC_FREE(frame);
> + return ret;
> +}
> diff --git a/examples/winexe/winexesvc.c b/examples/winexe/winexesvc.c
> new file mode 100644
> index 00000000000..02aa9df4b3d
> --- /dev/null
> +++ b/examples/winexe/winexesvc.c
> @@ -0,0 +1,745 @@
> +/*
> + * Copyright (C) Andrzej Hajda 2009-2013
> + * Contact: andrzej.hajda at wp.pl
> + *
> + * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf
> + * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab.
> + *
> + * ** NOTE! The following "GPLv3 only" license applies to the winexe
> + * ** service files. This does NOT imply that all of Samba is released
> + * ** under the "GPLv3 only" license.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 3 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <windows.h>
> +#include <aclapi.h>
> +#include <userenv.h>
> +
> +#include <stdio.h>
> +#include <string.h>
> +#include <stdarg.h>
> +#include <stdlib.h>
> +
> +#include "winexesvc.h"
> +
> +#define BUFSIZE 256
> +
> +#if 0
> +#define dbg(arg...) \
> +({\
> + FILE *f = fopen("C:\\" SERVICE_NAME ".log", "at");\
> + if (f) {\
> + fprintf(f, arg);\
> + fclose(f);\
> + }\
> +})
> +#else
> +#define dbg(arg...)
> +#endif
> +
> +static SECURITY_ATTRIBUTES sa;
> +
> +/* Creates SECURITY_ATTRIBUTES sa with full access for BUILTIN\Administrators */
> +static int CreatePipesSA()
> +{
> + DWORD dwRes;
> + PSID pAdminSID = NULL;
> + PACL pACL = NULL;
> + PSECURITY_DESCRIPTOR pSD = NULL;
> + EXPLICIT_ACCESS ea;
> + SID_IDENTIFIER_AUTHORITY SIDAuthNT = {SECURITY_NT_AUTHORITY};
> +
> + /* Create a SID for the BUILTIN\Administrators group. */
> + if (
> + !AllocateAndInitializeSid(
> + &SIDAuthNT, 2,
> + SECURITY_BUILTIN_DOMAIN_RID,
> + DOMAIN_ALIAS_RID_ADMINS,
> + 0, 0, 0, 0, 0, 0, &pAdminSID
> + )
> + ) {
> + dbg("AllocateAndInitializeSid Error %lu\n", GetLastError());
> + return 0;
> + }
> + /* Initialize an EXPLICIT_ACCESS structure for an ACE.
> + The ACE will allow the Administrators group full access to the key.
> + */
> + ea.grfAccessPermissions = FILE_ALL_ACCESS;
> + ea.grfAccessMode = SET_ACCESS;
> + ea.grfInheritance = NO_INHERITANCE;
> + ea.Trustee.TrusteeForm = TRUSTEE_IS_SID;
> + ea.Trustee.TrusteeType = TRUSTEE_IS_GROUP;
> + ea.Trustee.ptstrName = (LPTSTR) pAdminSID;
> +
> + /* Create a new ACL that contains the new ACEs */
> + dwRes = SetEntriesInAcl(1, &ea, NULL, &pACL);
> + if (ERROR_SUCCESS != dwRes) {
> + dbg("SetEntriesInAcl Error %lu\n", GetLastError());
> + return 0;
> + }
> + /* Initialize a security descriptor */
> + pSD = (PSECURITY_DESCRIPTOR) LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
> + if (NULL == pSD) {
> + dbg("LocalAlloc Error %lu\n", GetLastError());
> + return 0;
> + }
> +
> + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION))
> + {
> + dbg("InitializeSecurityDescriptor Error %lu\n", GetLastError());
> + return 0;
> + }
> + /* Add the ACL to the security descriptor */
> + if (
> + !SetSecurityDescriptorDacl(
> + pSD, TRUE, /* bDaclPresent flag */
> + pACL, FALSE /* not a default DACL */
> + )
> + ) {
> + dbg("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
> + return 0;
> + }
> + /* Initialize a security attributes structure */
> + sa.nLength = sizeof(SECURITY_ATTRIBUTES);
> + sa.lpSecurityDescriptor = pSD;
> + sa.bInheritHandle = FALSE;
> + return 1;
> +}
> +
> +typedef struct {
> + HANDLE h;
> + OVERLAPPED o;
> +} OV_HANDLE;
> +
> +static int hgets(char *str, int n, OV_HANDLE *pipe)
> +{
> + DWORD res;
> + DWORD count = 0;
> + --n;
> + while (--n >= 0) {
> + if (!ReadFile(pipe->h, str, 1, NULL, &pipe->o) && GetLastError() != ERROR_IO_PENDING)
> + goto finish;
> + if (!GetOverlappedResult(pipe->h, &pipe->o, &res, TRUE) || !res)
> + goto finish;
> + if (*str == '\n')
> + goto finish;
> + ++count;
> + ++str;
> + }
> +finish:
> + *str = 0;
> + return count;
> +}
> +
> +static int hprintf(OV_HANDLE *pipe, const char *fmt, ...)
> +{
> + int res;
> + char buf[1024];
> + va_list ap;
> + va_start(ap, fmt);
> + vsnprintf(buf, sizeof(buf), fmt, ap);
> + va_end(ap);
> + if (!WriteFile(pipe->h, buf, strlen(buf), NULL, &pipe->o) && GetLastError() == ERROR_IO_PENDING)
> + GetOverlappedResult(pipe->h, &pipe->o, (LPDWORD)&res, TRUE);
> + FlushFileBuffers(pipe->h);
> + return res;
> +}
> +
> +typedef struct {
> + OV_HANDLE *pipe;
> + const char *cmd;
> + HANDLE pin;
> + HANDLE pout;
> + HANDLE perr;
> + HANDLE token;
> + int implevel;
> + int system;
> + int profile;
> + char *runas;
> + int conn_number;
> +} connection_context;
> +
> +typedef int CMD_FUNC(connection_context *);
> +
> +typedef struct {
> + const char *name;
> + CMD_FUNC *func;
> +} CMD_ITEM;
> +
> +static int cmd_set(connection_context *c)
> +{
> + static const char* var_system = "system";
> + static const char* var_implevel = "implevel";
> + static const char* var_runas = "runas";
> + static const char* var_profile = "profile";
> + char *cmdline;
> + int res = 0;
> +
> + cmdline = strchr(c->cmd, ' ');
> + if (!cmdline) {
> + goto finish;
> + }
> + ++cmdline;
> + int l;
> + if ((strstr(cmdline, var_system) == cmdline) && (cmdline[l = strlen(var_system)] == ' ')) {
> + c->system = atoi(cmdline + l + 1);
> + } else if ((strstr(cmdline, var_implevel) == cmdline) && (cmdline[l = strlen(var_implevel)] == ' ')) {
> + c->implevel = atoi(cmdline + l + 1);
> + } else if ((strstr(cmdline, var_profile) == cmdline) && (cmdline[l = strlen(var_profile)] == ' ')) {
> + c->profile = atoi(cmdline + l + 1);
> + } else if ((strstr(cmdline, var_runas) == cmdline) && (cmdline[l = strlen(var_runas)] == ' ')) {
> + c->runas = strdup(cmdline + l + 1);
> + } else {
> + hprintf(c->pipe, "error Unknown commad (%s)\n", c->cmd);
> + goto finish;
> + }
> + res = 1;
> +finish:
> + return res;
> +}
> +
> +static int cmd_get(connection_context *c)
> +{
> + static const char* var_version = "version";
> + static const char* var_codepage = "codepage";
> + char *cmdline;
> + int res = 0;
> +
> + cmdline = strchr(c->cmd, ' ');
> + if (!cmdline) {
> + goto finish;
> + }
> + ++cmdline;
> + int l;
> + if ((strstr(cmdline, var_version) == cmdline)
> + && (cmdline[l = strlen(var_version)] == 0)) {
> + hprintf(c->pipe, "version 0x%04X\n", VERSION);
> + } else if ((strstr(cmdline, var_codepage) == cmdline)
> + && (cmdline[l = strlen(var_codepage)] == 0)) {
> + hprintf(c->pipe, "codepage %d\n", GetOEMCP());
> + } else {
> + hprintf(c->pipe, "error Unknown argument (%s)\n", c->cmd);
> + goto finish;
> + }
> + res = 1;
> +finish:
> + return res;
> +}
> +
> +typedef struct {
> + char *user;
> + char *domain;
> + char *password;
> +} credentials;
> +
> +static int prepare_credentials(char *str, credentials *crd)
> +{
> + char *p;
> + p = strchr(str, '/');
> + if (!p) p = strchr(str, '\\');
> + if (p) {
> + *p++ = 0;
> + crd->domain = str;
> + } else {
> + p = str;
> + crd->domain = ".";
> + }
> + crd->user = p;
> + p = strchr(p, '%');
> + if (p)
> + *p++ = 0;
> + crd->password = p;
> + return 1;
> +}
> +
> +static int get_token(connection_context *c)
> +{
> + int res = 0;
> + int wres;
> + HANDLE token;
> +
> + if (c->runas) {
> + credentials crd;
> + if (!prepare_credentials(c->runas, &crd)) {
> + hprintf(c->pipe, "error Incorrect runas credentials\n");
> + goto finish;
> + }
> + wres = LogonUser(crd.user, crd.domain, crd.password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, &c->token);
> + if (!wres) {
> + hprintf(c->pipe, "error Cannot LogonUser(%s,%s,%s) %d\n",
> + crd.user, crd.domain, crd.password, GetLastError());
> + goto finish;
> + }
> + res = 1;
> + goto finish;
> + } else if (c->system) {
> + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS, &token)) {
> + hprintf(c->pipe, "error Cannot OpenProcessToken %d\n", GetLastError());
> + goto finish;
> + }
> + } else {
> + if (!ImpersonateNamedPipeClient(c->pipe->h)) {
> + hprintf(c->pipe, "error Cannot ImpersonateNamedPipeClient %d\n", GetLastError());
> + goto finish;
> + }
> + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, FALSE, &token)) {
> + hprintf(c->pipe, "error Cannot OpenThreadToken %d\n", GetLastError());
> + goto finishRevertToSelf;
> + }
> + }
> + if (!DuplicateTokenEx(token, MAXIMUM_ALLOWED, 0, c->implevel, TokenPrimary, &c->token)) {
> + hprintf(c->pipe, "error Cannot Duplicate Token %d\n", GetLastError());
> + goto finishCloseToken;
> + }
> + res = 1;
> +finishCloseToken:
> + CloseHandle(token);
> +finishRevertToSelf:
> + if (!c->system) {
> + if (!RevertToSelf()) {
> + hprintf(c->pipe, "error Cannot RevertToSelf %d\n", GetLastError());
> + res = 0;
> + }
> + }
> +finish:
> + return res;
> +}
> +
> +static int load_user_profile(connection_context *c)
> +{
> + PROFILEINFO pi = { .dwSize = sizeof(PROFILEINFO) };
> + DWORD ulen = 256;
> + TCHAR username[ulen];
> +
> + GetUserName(username, &ulen);
> + pi.lpUserName = username;
> +
> + return LoadUserProfile(c->token, &pi);
> +}
> +
> +static int cmd_run(connection_context *c)
> +{
> + char buf[256];
> + int res = 0;
> + char *cmdline;
> + DWORD pipe_nr;
> +
> + cmdline = strchr(c->cmd, ' ');
> + if (!cmdline) {
> + goto finish;
> + }
> + ++cmdline;
> +
> + if (!get_token(c))
> + return 0;
> +
> + pipe_nr = (GetCurrentProcessId() << 16) + (DWORD) c->conn_number;
> +
> + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_IN, (unsigned int) pipe_nr);
> + c->pin = CreateNamedPipe(buf,
> + PIPE_ACCESS_DUPLEX,
> + PIPE_WAIT,
> + 1,
> + BUFSIZE,
> + BUFSIZE,
> + NMPWAIT_USE_DEFAULT_WAIT,
> + &sa);
> + if (c->pin == INVALID_HANDLE_VALUE) {
> + hprintf(c->pipe, "error Cannot create in pipe(%s), error 0x%08X\n", buf, GetLastError());
> + goto finishCloseToken;
> + }
> +
> + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_OUT, (unsigned int) pipe_nr);
> + c->pout = CreateNamedPipe(buf,
> + PIPE_ACCESS_DUPLEX,
> + PIPE_WAIT,
> + 1,
> + BUFSIZE,
> + BUFSIZE,
> + NMPWAIT_USE_DEFAULT_WAIT,
> + &sa);
> + if (c->pout == INVALID_HANDLE_VALUE) {
> + hprintf(c->pipe, "error Cannot create out pipe(%s), error 0x%08X\n", buf, GetLastError());
> + goto finishClosePin;
> + }
> +
> + sprintf(buf, "\\\\.\\pipe\\" PIPE_NAME_ERR, (unsigned int) pipe_nr);
> + c->perr = CreateNamedPipe(buf,
> + PIPE_ACCESS_DUPLEX,
> + PIPE_WAIT,
> + 1,
> + BUFSIZE,
> + BUFSIZE,
> + NMPWAIT_USE_DEFAULT_WAIT,
> + &sa);
> + if (c->perr == INVALID_HANDLE_VALUE) {
> + hprintf(c->pipe, "error Cannot create err pipe(%s), error 0x%08x\n", buf, GetLastError());
> + goto finishClosePout;
> + }
> +
> + /* Send handle to client (it will use it to connect pipes) */
> + hprintf(c->pipe, CMD_STD_IO_ERR " %08X\n", pipe_nr);
> +
> + HANDLE ph[] = { c->pin, c->pout, c->perr };
> + int i;
> +
> + for (i = 0; i < 3; ++i) {
> + if (ConnectNamedPipe(ph[i], NULL))
> + continue;
> + int err = GetLastError();
> + if (err != ERROR_PIPE_CONNECTED) {
> + hprintf(c->pipe, "error ConnectNamedPipe(pin) %d\n", err);
> + while (--i >= 0)
> + DisconnectNamedPipe(ph[i]);
> + goto finishClosePerr;
> + }
> + }
> +
> + SetHandleInformation(c->pin, HANDLE_FLAG_INHERIT, 1);
> + SetHandleInformation(c->pout, HANDLE_FLAG_INHERIT, 1);
> + SetHandleInformation(c->perr, HANDLE_FLAG_INHERIT, 1);
> +
> + if (c->profile)
> + load_user_profile(c);
> +
> + PROCESS_INFORMATION pi;
> + ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
> +
> + STARTUPINFO si;
> + ZeroMemory(&si, sizeof(STARTUPINFO));
> + si.cb = sizeof(STARTUPINFO);
> + si.hStdInput = c->pin;
> + si.hStdOutput = c->pout;
> + si.hStdError = c->perr;
> + si.dwFlags |= STARTF_USESTDHANDLES;
> +
> + if (CreateProcessAsUser(
> + c->token,
> + NULL,
> + cmdline, /* command line */
> + NULL, /* process security attributes */
> + NULL, /* primary thread security attributes */
> + TRUE, /* handles are inherited */
> + 0, /* creation flags */
> + NULL, /* use parent's environment */
> + NULL, /* use parent's current directory */
> + &si, /* STARTUPINFO pointer */
> + &pi) /* receives PROCESS_INFORMATION */
> + ) {
> + HANDLE hlist[2] = {c->pipe->o.hEvent, pi.hProcess};
> + DWORD ec;
> + char str[1];
> +
> + if (!ResetEvent(c->pipe->o.hEvent))
> + dbg("ResetEvent error - %lu\n", GetLastError());
> + if (!ReadFile(c->pipe->h, str, 1, NULL, &c->pipe->o) && GetLastError() != ERROR_IO_PENDING)
> + dbg("ReadFile(control_pipe) error - %lu\n", GetLastError());
> + ec = WaitForMultipleObjects(2, hlist, FALSE, INFINITE);
> + dbg("WaitForMultipleObjects=%lu\n", ec - WAIT_OBJECT_0);
> + if (ec != WAIT_OBJECT_0)
> + GetExitCodeProcess(pi.hProcess, &ec);
> + else
> + TerminateProcess(pi.hProcess, ec = 0x1234);
> + FlushFileBuffers(c->pout);
> + FlushFileBuffers(c->perr);
> + CloseHandle(pi.hProcess);
> + CloseHandle(pi.hThread);
> + hprintf(c->pipe, CMD_RETURN_CODE " %08X\n", ec);
> + } else {
> + hprintf(c->pipe, "error Creating process(%s) %d\n", cmdline, GetLastError());
> + }
> +
> + DisconnectNamedPipe(c->perr);
> + DisconnectNamedPipe(c->pout);
> + DisconnectNamedPipe(c->pin);
> +finishClosePerr:
> + CloseHandle(c->perr);
> +finishClosePout:
> + CloseHandle(c->pout);
> +finishClosePin:
> + CloseHandle(c->pin);
> +finishCloseToken:
> + CloseHandle(c->token);
> +finish:
> + return res;
> +}
> +
> +static CMD_ITEM cmd_table[] = {
> + {"run", cmd_run},
> + {"set", cmd_set},
> + {"get", cmd_get},
> + {NULL, NULL}
> +};
> +
> +typedef struct {
> + OV_HANDLE *pipe;
> + int conn_number;
> +} connection_data;
> +
> +#define MAX_COMMAND_LENGTH (32768)
> +
> +static VOID handle_connection(connection_data *data)
> +{
> + char *cmd = 0;
> + int res;
> + connection_context _c, *c = &_c;
> + cmd = malloc(MAX_COMMAND_LENGTH);
> + if (!cmd) {
> + hprintf(data->pipe,
> + "error: unable to allocate buffer for command\n");
> + return;
> + }
> + ZeroMemory(cmd, MAX_COMMAND_LENGTH);
> + ZeroMemory(c, sizeof(connection_context));
> + c->pipe = data->pipe;
> + c->cmd = cmd;
> + c->conn_number = data->conn_number;
> + free(data);
> + /* FIXME make wait for end of process or ctrl_pipe input */
> + while (1) {
> + res = hgets(cmd, MAX_COMMAND_LENGTH, c->pipe);
> + if (res <= 0) {
> + dbg("Error reading from pipe(%p)\n", c->pipe->h);
> + goto finish;
> + }
> + dbg("Retrieved line: \"%s\"\n", cmd);
> + CMD_ITEM *ci;
> + for (ci = cmd_table; ci->name; ++ci) {
> + if (strstr(cmd, ci->name) != cmd)
> + continue;
> + char c = cmd[strlen(ci->name)];
> + if (!c || (c == ' '))
> + break;
> + }
> + if (ci->name) {
> + if (!ci->func(c))
> + goto finish;
> + } else {
> + hprintf(c->pipe, "error Ignoring unknown command (%s)\n", cmd);
> + }
> + }
> +finish:
> + FlushFileBuffers(c->pipe->h);
> + DisconnectNamedPipe(c->pipe->h);
> + CloseHandle(c->pipe->h);
> + CloseHandle(c->pipe->o.hEvent);
> + free(c->pipe);
> + free(cmd);
> +}
> +
> +static int conn_number = 0;
> +
> +DWORD WINAPI winexesvc_loop(LPVOID lpParameter)
> +{
> + BOOL res;
> +
> + dbg("server_loop: alive\n");
> + if (!CreatePipesSA()) {
> + dbg("CreatePipesSA failed (%08lX)\n", GetLastError());
> + return -1;
> + }
> + dbg("server_loop: CreatePipesSA done\n");
> + for (;;) {
> + dbg("server_loop: Create Pipe\n");
> + OV_HANDLE *pipe;
> + pipe = (OV_HANDLE *)malloc(sizeof(OV_HANDLE));
> + ZeroMemory(&pipe->o, sizeof(OVERLAPPED));
> + pipe->o.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL);
> + pipe->h = CreateNamedPipe("\\\\.\\pipe\\" PIPE_NAME,
> + PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
> + PIPE_WAIT,
> + PIPE_UNLIMITED_INSTANCES,
> + BUFSIZE,
> + BUFSIZE,
> + NMPWAIT_USE_DEFAULT_WAIT,
> + &sa);
> + if (pipe->h == INVALID_HANDLE_VALUE) {
> + dbg("CreatePipe failed(%08lX)\n",
> + GetLastError());
> + CloseHandle(pipe->o.hEvent);
> + free(pipe);
> + return 0;
> + }
> +
> + dbg("server_loop: Connect Pipe\n");
> + if (ConnectNamedPipe(pipe->h, &pipe->o)) {
> + dbg("server_loop: Connect Pipe err %08lX\n", GetLastError());
> + res = FALSE;
> + } else {
> + switch (GetLastError()) {
> + case ERROR_IO_PENDING:
> + dbg("server_loop: Connect Pipe(0) pending\n");
> + DWORD t;
> + res = GetOverlappedResult(pipe->h, &pipe->o, &t, TRUE);
> + break;
> + case ERROR_PIPE_CONNECTED:
> + dbg("server_loop: Connect Pipe(0) connected\n");
> + res = TRUE;
> + break;
> + default:
> + dbg("server_loop: Connect Pipe(0) err %08lX\n", GetLastError());
> + res = FALSE;
> + }
> + }
> +
> + if (res) {
> + connection_data *cd = malloc(sizeof(connection_data));
> + cd->pipe = pipe;
> + cd->conn_number = ++conn_number;
> + dbg("server_loop: CreateThread\n");
> + HANDLE th = CreateThread(NULL, /* no security attribute */
> + 0, /* default stack size */
> + (LPTHREAD_START_ROUTINE)
> + handle_connection,
> + (LPVOID) cd, /* thread parameter */
> + 0, /* not suspended */
> + NULL); /* returns thread ID */
> + if (!th) {
> + dbg("Cannot create thread\n");
> + CloseHandle(pipe->h);
> + CloseHandle(pipe->o.hEvent);
> + free(pipe);
> + } else {
> + CloseHandle(th);
> + dbg("server_loop: Thread created\n");
> + }
> + } else {
> + dbg("server_loop: Pipe not connected\n");
> + CloseHandle(pipe->h);
> + CloseHandle(pipe->o.hEvent);
> + free(pipe);
> + }
> + }
> + dbg("server_loop: STH wrong\n");
> + return 0;
> +}
> +
> +static SERVICE_STATUS winexesvcStatus;
> +static SERVICE_STATUS_HANDLE winexesvcStatusHandle;
> +
> +static VOID WINAPI winexesvcCtrlHandler(DWORD Opcode)
> +{
> + switch (Opcode) {
> + case SERVICE_CONTROL_PAUSE:
> + dbg(SERVICE_NAME ": winexesvcCtrlHandler: pause\n", 0);
> + winexesvcStatus.dwCurrentState = SERVICE_PAUSED;
> + break;
> +
> + case SERVICE_CONTROL_CONTINUE:
> + dbg(SERVICE_NAME ": winexesvcCtrlHandler: continue\n", 0);
> + winexesvcStatus.dwCurrentState = SERVICE_RUNNING;
> + break;
> +
> + case SERVICE_CONTROL_STOP:
> + dbg(SERVICE_NAME ": winexesvcCtrlHandler: stop\n", 0);
> + winexesvcStatus.dwWin32ExitCode = 0;
> + winexesvcStatus.dwCurrentState = SERVICE_STOPPED;
> + winexesvcStatus.dwCheckPoint = 0;
> + winexesvcStatus.dwWaitHint = 0;
> +
> + if (!SetServiceStatus (winexesvcStatusHandle, &winexesvcStatus))
> + dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", GetLastError());
> +
> + dbg(SERVICE_NAME ": Leaving winexesvc\n", 0);
> + return;
> +
> + case SERVICE_CONTROL_INTERROGATE:
> + dbg(SERVICE_NAME ": winexesvcCtrlHandler: interrogate\n", 0);
> + break;
> +
> + default:
> + dbg(SERVICE_NAME ": Unrecognized opcode %ld\n", Opcode);
> + }
> +
> + if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus))
> + dbg(SERVICE_NAME ": SetServiceStatus error 0x%08X\n", GetLastError());
> +
> + return;
> +}
> +
> +static DWORD winexesvcInitialization(DWORD argc, LPTSTR * argv, DWORD * specificError)
> +{
> + HANDLE th = CreateThread(NULL, 0, winexesvc_loop, NULL, 0, NULL);
> + if (th) {
> + CloseHandle(th);
> + return NO_ERROR;
> + }
> + return !NO_ERROR;
> +}
> +
> +static void WINAPI winexesvcStart(DWORD argc, LPTSTR * argv)
> +{
> + DWORD status;
> + DWORD specificError;
> +
> + winexesvcStatus.dwServiceType = SERVICE_WIN32;
> + winexesvcStatus.dwCurrentState = SERVICE_START_PENDING;
> + winexesvcStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
> + winexesvcStatus.dwWin32ExitCode = 0;
> + winexesvcStatus.dwServiceSpecificExitCode = 0;
> + winexesvcStatus.dwCheckPoint = 0;
> + winexesvcStatus.dwWaitHint = 0;
> +
> + dbg(SERVICE_NAME ": RegisterServiceCtrlHandler\n", 0);
> +
> + winexesvcStatusHandle = RegisterServiceCtrlHandler(SERVICE_NAME, winexesvcCtrlHandler);
> +
> + if (winexesvcStatusHandle == (SERVICE_STATUS_HANDLE) 0) {
> + dbg(SERVICE_NAME
> + ": RegisterServiceCtrlHandler failed %d\n",
> + GetLastError());
> + return;
> + }
> + status = winexesvcInitialization(argc, argv, &specificError);
> +
> + if (status != NO_ERROR) {
> + winexesvcStatus.dwCurrentState = SERVICE_STOPPED;
> + winexesvcStatus.dwCheckPoint = 0;
> + winexesvcStatus.dwWaitHint = 0;
> + winexesvcStatus.dwWin32ExitCode = status;
> + winexesvcStatus.dwServiceSpecificExitCode = specificError;
> +
> + SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus);
> + return;
> + }
> +
> + winexesvcStatus.dwCurrentState = SERVICE_RUNNING;
> + winexesvcStatus.dwCheckPoint = 0;
> + winexesvcStatus.dwWaitHint = 0;
> +
> + if (!SetServiceStatus(winexesvcStatusHandle, &winexesvcStatus)) {
> + status = GetLastError();
> + dbg(SERVICE_NAME ": SetServiceStatus error %ld\n", status);
> + }
> +
> + dbg(SERVICE_NAME ": Returning the Main Thread \n", 0);
> +
> + return;
> +}
> +
> +int main(int argc, char *argv[])
> +{
> + SERVICE_TABLE_ENTRY DispatchTable[] = {
> + {SERVICE_NAME, winexesvcStart},
> + {NULL, NULL}
> + };
> +
> + dbg(SERVICE_NAME ": StartServiceCtrlDispatcher %d\n", GetLastError());
> + if (!StartServiceCtrlDispatcher(DispatchTable)) {
> + dbg(SERVICE_NAME
> + ": StartServiceCtrlDispatcher (%d)\n",
> + GetLastError());
> + }
> + return 0;
> +}
> diff --git a/examples/winexe/winexesvc.h b/examples/winexe/winexesvc.h
> new file mode 100644
> index 00000000000..92b83750b70
> --- /dev/null
> +++ b/examples/winexe/winexesvc.h
> @@ -0,0 +1,42 @@
> +/*
> + * Copyright (C) Andrzej Hajda 2009-2013
> + * Contact: andrzej.hajda at wp.pl
> + *
> + * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf
> + * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab.
> + *
> + * ** NOTE! The following "GPLv3 only" license applies to the winexe
> + * ** service files. This does NOT imply that all of Samba is released
> + * ** under the "GPLv3 only" license.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 3 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +/*
> + * Shared by winexe and winexesvc
> + */
> +
> +#define VERSION_MAJOR 1
> +#define VERSION_MINOR 1
> +
> +#define VERSION ((VERSION_MAJOR * 100) + VERSION_MINOR)
> +
> +#define SERVICE_NAME "winexesvc"
> +
> +#define PIPE_NAME "ahexec"
> +#define PIPE_NAME_IN "ahexec_stdin%08X"
> +#define PIPE_NAME_OUT "ahexec_stdout%08X"
> +#define PIPE_NAME_ERR "ahexec_stderr%08X"
> +
> +#define CMD_STD_IO_ERR "std_io_err"
> +#define CMD_RETURN_CODE "return_code"
> diff --git a/examples/winexe/wscript b/examples/winexe/wscript
> new file mode 100644
> index 00000000000..3380b9794f6
> --- /dev/null
> +++ b/examples/winexe/wscript
> @@ -0,0 +1,29 @@
> +#!/usr/bin/env python
> +
> +def configure(conf):
> + AR32 = ['i386', 'i586', 'i686']
> + AR64 = ['x86_64', 'amd64']
> + TC = ['mingw32', 'mingw32msvc', 'w64-mingw32']
> +
> + found = False
> +
> + for a in AR32:
> + for t in TC:
> + if conf.find_program(a + '-' + t + '-gcc', var='WINEXE_CC_WIN32'):
> + found = True
> + break
> + if found:
> + conf.DEFINE('HAVE_WINEXE_CC_WIN32', 1);
> + break
> +
> + for a in AR64:
> + for t in TC:
> + if conf.find_program(a + '-' + t + '-gcc', var='WINEXE_CC_WIN64'):
> + found = True
> + break
> + if found:
> + conf.DEFINE('HAVE_WINEXE_CC_WIN64', 1);
> + break
> +
> + conf.DEFINE("WINEXE_LDFLAGS",
> + "-s -Wall -Wl,-Bstatic -Wl,-Bdynamic -luserenv")
> diff --git a/examples/winexe/wscript_build b/examples/winexe/wscript_build
> new file mode 100644
> index 00000000000..ecad3772461
> --- /dev/null
> +++ b/examples/winexe/wscript_build
> @@ -0,0 +1,87 @@
> +#!/usr/bin/env python
> +
> +import samba_utils
> +
> +def generate_winexesvc_c_from_exe(t):
> + src = t.inputs[0].bldpath(t.env)
> + tgt = t.outputs[0].bldpath(t.env)
> + fn = t.env.SAMBA_GENERATOR_VARS['WINEXE_FN']
> + src_blob = samba_utils.load_file(src)
> +
> + def c_array(src):
> + N = 0
> + result = ''
> + while src:
> + l = src[:8]
> + src = src[8:]
> + h = ' '.join(["0x%02X," % ord(x) for x in l])
> + result += "\t\t%s\n" % (h)
> + return result
> +
> + src_array = c_array(src_blob)
> +
> + contents = '''
> +#include "replace.h"
> +#include "lib/util/data_blob.h"
> +
> +const DATA_BLOB *%s(void);
> +const DATA_BLOB *%s(void)
> +{
> +\tstatic const uint8_t array[] = {
> +%s
> +\t};
> +\tstatic const DATA_BLOB blob = {
> +\t\t.data = discard_const_p(uint8_t, array),
> +\t\t.length = ARRAY_SIZE(array),
> +\t};
> +\treturn &blob;
> +}
> +''' % (fn, fn, src_array)
> +
> + ret = samba_utils.save_file(tgt, contents)
> + assert(ret == True)
> +
> +winexesvc_binaries = ''
> +
> +if bld.env.WINEXE_CC_WIN32:
> + bld.SAMBA_GENERATOR(
> + 'winexesvc32_exe',
> + source='winexesvc.c',
> + target='winexesvc32.exe',
> + rule='${WINEXE_CC_WIN32} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}')
> + vars = {"WINEXE_FN": "winexesvc32_exe_binary"}
> + bld.SAMBA_GENERATOR(
> + 'winexesvc32_exe_binary',
> + source='winexesvc32.exe',
> + target='winexesvc32_exe_binary.c',
> + group='build_source',
> + vars=vars,
> + rule=generate_winexesvc_c_from_exe)
> + winexesvc_binaries += ' winexesvc32_exe_binary.c'
> +
> +if bld.env.WINEXE_CC_WIN64:
> + bld.SAMBA_GENERATOR(
> + 'winexesvc64_exe',
> + source='winexesvc.c',
> + target='winexesvc64.exe',
> + rule='${WINEXE_CC_WIN64} ${SRC} -o ${TGT} ${WINEXE_LDFLAGS}')
> + vars = {"WINEXE_FN": "winexesvc64_exe_binary"}
> + bld.SAMBA_GENERATOR(
> + 'winexesvc64_exe_binary',
> + source='winexesvc64.exe',
> + target='winexesvc64_exe_binary.c',
> + group='build_source',
> + vars=vars,
> + rule=generate_winexesvc_c_from_exe)
> + winexesvc_binaries += ' winexesvc64_exe_binary.c'
> +
> +if winexesvc_binaries != '':
> + bld.SAMBA3_BINARY('winexe',
> + source='winexe.c ' + winexesvc_binaries,
> + deps='''
> + popt
> + samba-credentials
> + LOADPARM_CTX
> + libsmb
> + msrpc3
> + ''')
> diff --git a/source3/wscript_build b/source3/wscript_build
> index 6e34bfaecf0..f463a8c4261 100644
> --- a/source3/wscript_build
> +++ b/source3/wscript_build
> @@ -1345,6 +1345,7 @@ bld.RECURSE('../examples/libsmbclient')
> bld.RECURSE('../examples/pdb')
> bld.RECURSE('../examples/VFS')
> bld.RECURSE('../examples/fuse')
> +bld.RECURSE('../examples/winexe')
> bld.RECURSE('lib/netapi/tests')
> bld.RECURSE('lib/netapi/examples')
> bld.RECURSE('smbd/notifyd')
> diff --git a/wscript b/wscript
> index f98d731e537..619b4923813 100644
> --- a/wscript
> +++ b/wscript
> @@ -128,6 +128,7 @@ def configure(conf):
> conf.RECURSE('lib/replace')
>
> conf.RECURSE('examples/fuse')
> + conf.RECURSE('examples/winexe')
>
> conf.SAMBA_CHECK_PERL(mandatory=True)
> conf.find_program('xsltproc', var='XSLTPROC')
> --
> 2.11.0
>
More information about the samba-technical
mailing list