samba/examples/winexe/winexe.c

1922 lines
43 KiB
C

/*
* Samba Unix/Linux CIFS implementation
*
* winexe
*
* Copyright (C) 2018 Volker Lendecke <vl@samba.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "includes.h"
#include "version.h"
#include <popt.h>
#include <tevent.h>
#include "lib/param/param.h"
#include "auth/credentials/credentials.h"
#include "lib/util/talloc_stack.h"
#include "lib/util/tevent_ntstatus.h"
#include "lib/util/sys_rw.h"
#include "libsmb/proto.h"
#include "librpc/gen_ndr/ndr_svcctl_c.h"
#include "rpc_client/cli_pipe.h"
#include "libcli/smb/smbXcli_base.h"
#include "libcli/util/werror.h"
#include "lib/async_req/async_sock.h"
#include "lib/cmdline/cmdline.h"
#include "client.h"
#define SVC_INTERACTIVE 1
#define SVC_IGNORE_INTERACTIVE 2
#define SVC_INTERACTIVE_MASK 3
#define SVC_FORCE_UPLOAD 4
#define SVC_OS64BIT 8
#define SVC_OSCHOOSE 16
#define SVC_UNINSTALL 32
#define SVC_SYSTEM 64
#define SERVICE_NAME "winexesvc"
#define PIPE_NAME "ahexec"
#define PIPE_NAME_IN "ahexec_stdin%08X"
#define PIPE_NAME_OUT "ahexec_stdout%08X"
#define PIPE_NAME_ERR "ahexec_stderr%08X"
static const char version_message_fmt[] = "winexe version %d.%d\n"
"This program may be freely redistributed under the terms of the "
"GNU GPLv3\n";
struct program_options {
char *hostname;
int port;
char *cmd;
struct cli_credentials *credentials;
char *runas;
char *runas_file;
int flags;
};
static void parse_args(int argc, const char *argv[],
TALLOC_CTX *mem_ctx,
struct program_options *options)
{
poptContext pc;
int opt, i;
int argc_new;
char **argv_new;
int port = 445;
char *port_str = NULL;
int flag_interactive = SVC_IGNORE_INTERACTIVE;
int flag_ostype = 2;
int flag_reinstall = 0;
int flag_uninstall = 0;
int flag_help = 0;
int flag_version = 0;
bool ok;
struct poptOption long_options[] = {
POPT_AUTOHELP
{
.longName = "uninstall",
.shortName = 0,
.argInfo = POPT_ARG_NONE,
.arg = &flag_uninstall,
.val = 0,
.descrip = "Uninstall winexe service after "
"remote execution",
.argDescrip = NULL,
},{
.longName = "reinstall",
.shortName = 0,
.argInfo = POPT_ARG_NONE,
.arg = &flag_reinstall,
.val = 0,
.descrip = "Reinstall winexe service before "
"remote execution",
.argDescrip = NULL,
},{
.longName = "runas",
.shortName = 0,
.argInfo = POPT_ARG_STRING,
.arg = &options->runas,
.val = 0,
.descrip = "Run as the given user (BEWARE: this "
"password is sent in cleartext over "
"the network!)",
.argDescrip = "[DOMAIN\\]USERNAME%PASSWORD",
},{
.longName = "runas-file",
.shortName = 0,
.argInfo = POPT_ARG_STRING,
.arg = &options->runas_file,
.val = 0,
.descrip = "Run as user options defined in a file",
.argDescrip = "FILE",
},{
.longName = "interactive",
.shortName = 0,
.argInfo = POPT_ARG_INT,
.arg = &flag_interactive,
.val = 0,
.descrip = "Desktop interaction: 0 - disallow, "
"1 - allow. If allow, also use the "
"--system switch (Windows requirement). "
"Vista does not support this option.",
.argDescrip = "0|1",
},{
.longName = "ostype",
.shortName = 0,
.argInfo = POPT_ARG_INT,
.arg = &flag_ostype,
.val = 0,
.descrip = "OS type: 0 - 32-bit, 1 - 64-bit, "
"2 - winexe will decide. "
"Determines which version (32-bit or 64-bit)"
" of service will be installed.",
.argDescrip = "0|1|2",
},
POPT_COMMON_SAMBA
POPT_COMMON_CREDENTIALS
POPT_COMMON_VERSION
POPT_TABLEEND
};
ZERO_STRUCTP(options);
ok = samba_cmdline_init(mem_ctx,
SAMBA_CMDLINE_CONFIG_CLIENT,
false /* require_smbconf */);
if (!ok) {
DBG_ERR("Failed to init cmdline parser!\n");
TALLOC_FREE(mem_ctx);
exit(1);
}
pc = samba_popt_get_context(getprogname(),
argc,
argv,
long_options,
0);
if (pc == NULL) {
DBG_ERR("Failed to setup popt context!\n");
TALLOC_FREE(mem_ctx);
exit(1);
}
poptSetOtherOptionHelp(pc, "[OPTION]... //HOST[:PORT] COMMAND\nOptions:");
if (((opt = poptGetNextOpt(pc)) != -1) || flag_help || flag_version) {
fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR,
SAMBA_VERSION_MINOR);
if (flag_version) {
exit(0);
}
poptPrintHelp(pc, stdout, 0);
if (flag_help) {
exit(0);
}
exit(1);
}
argv_new = discard_const_p(char *, poptGetArgs(pc));
argc_new = argc;
for (i = 0; i < argc; i++) {
if (!argv_new || argv_new[i] == NULL) {
argc_new = i;
break;
}
}
if (argc_new != 2 || argv_new[0][0] != '/' || argv_new[0][1] != '/') {
fprintf(stderr, version_message_fmt, SAMBA_VERSION_MAJOR,
SAMBA_VERSION_MINOR);
poptPrintHelp(pc, stdout, 0);
exit(1);
}
port_str = strchr(argv_new[0], ':');
if (port_str) {
if (sscanf(port_str + 1, "%d", &port) != 1 || port <= 0) {
fprintf(stderr, version_message_fmt,
SAMBA_VERSION_MAJOR, SAMBA_VERSION_MINOR);
poptPrintHelp(pc, stdout, 0);
exit(1);
}
*port_str = '\0';
}
if (options->runas == NULL && options->runas_file != NULL) {
struct cli_credentials *runas_cred;
const char *user;
const char *pass;
runas_cred = cli_credentials_init(mem_ctx);
cli_credentials_parse_file(runas_cred, options->runas_file,
CRED_SPECIFIED);
user = cli_credentials_get_username(runas_cred);
pass = cli_credentials_get_password(runas_cred);
if (user && pass) {
char buffer[1024];
const char *dom;
dom = cli_credentials_get_domain(runas_cred);
if (dom) {
snprintf(buffer, sizeof(buffer), "%s\\%s%%%s",
dom, user, pass);
} else {
snprintf(buffer, sizeof(buffer), "%s%%%s",
user, pass);
}
buffer[sizeof(buffer)-1] = '\0';
options->runas = talloc_strdup(mem_ctx, buffer);
}
}
options->credentials = samba_cmdline_get_creds();
options->hostname = talloc_strdup(mem_ctx, argv_new[0] + 2);
if (options->hostname == NULL) {
DBG_ERR("Out of memory\n");
exit(1);
}
options->port = port;
options->cmd = talloc_strdup(mem_ctx, argv_new[1]);
if (options->cmd == NULL) {
DBG_ERR("Out of memory\n");
exit(1);
}
poptFreeContext(pc);
options->flags = flag_interactive;
if (flag_reinstall) {
options->flags |= SVC_FORCE_UPLOAD;
}
if (flag_ostype == 1) {
options->flags |= SVC_OS64BIT;
}
if (flag_ostype == 2) {
options->flags |= SVC_OSCHOOSE;
}
if (flag_uninstall) {
options->flags |= SVC_UNINSTALL;
}
}
static NTSTATUS winexe_svc_upload(
const char *hostname,
int port,
const char *service_filename,
const DATA_BLOB *svc32_exe,
const DATA_BLOB *svc64_exe,
struct cli_credentials *credentials,
int flags)
{
struct cli_state *cli;
uint16_t fnum = 0xffff;
NTSTATUS status;
const DATA_BLOB *binary = NULL;
status = cli_full_connection_creds(
&cli,
NULL,
hostname,
NULL,
port,
"ADMIN$",
"?????",
credentials,
0);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("cli_full_connection_creds failed: %s\n",
nt_errstr(status));
return status;
}
if (flags & SVC_FORCE_UPLOAD) {
status = cli_unlink(cli, service_filename, 0);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("cli_unlink failed: %s\n",
nt_errstr(status));
}
}
if (flags & SVC_OSCHOOSE) {
status = cli_chkpath(cli, "SysWoW64");
if (NT_STATUS_IS_OK(status)) {
flags |= SVC_OS64BIT;
}
}
if (flags & SVC_OS64BIT) {
binary = svc64_exe;
} else {
binary = svc32_exe;
}
if (binary == NULL) {
goto done;
}
status = cli_ntcreate(
cli,
service_filename,
0, /* CreatFlags */
SEC_FILE_WRITE_DATA, /* DesiredAccess */
FILE_ATTRIBUTE_NORMAL, /* FileAttributes */
FILE_SHARE_WRITE|FILE_SHARE_READ, /* ShareAccess */
FILE_OPEN_IF, /* CreateDisposition */
FILE_NON_DIRECTORY_FILE, /* CreateOptions */
0, /* SecurityFlags */
&fnum,
NULL); /* CreateReturns */
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("Could not create %s: %s\n", service_filename,
nt_errstr(status));
goto done;
}
status = cli_writeall(
cli,
fnum,
0,
binary->data,
0,
binary->length,
NULL);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("Could not write file: %s\n", nt_errstr(status));
goto done;
}
done:
if (fnum != 0xffff) {
status = cli_close(cli, fnum);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("Close(%"PRIu16") failed for %s: %s\n",
fnum,
service_filename,
nt_errstr(status));
}
}
TALLOC_FREE(cli);
return status;
}
static NTSTATUS winexe_svc_install(
struct cli_state *cli,
const char *hostname,
int port,
const char *service_name,
const char *service_filename,
const DATA_BLOB *svc32_exe,
const DATA_BLOB *svc64_exe,
struct cli_credentials *credentials,
int flags)
{
TALLOC_CTX *frame = talloc_stackframe();
struct rpc_pipe_client *rpccli;
struct policy_handle scmanager_handle;
struct policy_handle service_handle;
struct SERVICE_STATUS service_status;
bool need_start = false;
bool need_conf = false;
NTSTATUS status;
WERROR werr;
const char *remote_name = smbXcli_conn_remote_name(cli->conn);
const struct sockaddr_storage *remote_sockaddr =
smbXcli_conn_remote_sockaddr(cli->conn);
status = cli_rpc_pipe_open_noauth_transport(
cli,
NCACN_NP,
&ndr_table_svcctl,
remote_name,
remote_sockaddr,
&rpccli);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n",
nt_errstr(status));
goto done;
}
status = dcerpc_svcctl_OpenSCManagerW(
rpccli->binding_handle,
frame,
remote_name,
NULL,
SEC_FLAG_MAXIMUM_ALLOWED,
&scmanager_handle,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
nt_errstr(status));
goto done;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
win_errstr(werr));
goto done;
}
status = dcerpc_svcctl_OpenServiceW(
rpccli->binding_handle,
frame,
&scmanager_handle,
service_name,
SERVICE_ALL_ACCESS,
&service_handle,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
nt_errstr(status));
goto close_scmanager;
}
if (W_ERROR_EQUAL(werr, WERR_SERVICE_DOES_NOT_EXIST)) {
status = dcerpc_svcctl_CreateServiceW(
rpccli->binding_handle,
frame,
&scmanager_handle,
service_name,
NULL,
SERVICE_ALL_ACCESS,
SERVICE_TYPE_WIN32_OWN_PROCESS |
((flags & SVC_INTERACTIVE) ?
SERVICE_TYPE_INTERACTIVE_PROCESS : 0),
SVCCTL_DEMAND_START,
SVCCTL_SVC_ERROR_NORMAL,
service_filename,
NULL,
NULL,
NULL,
0,
NULL,
NULL,
0,
&service_handle,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_CreateServiceW "
"failed: %s\n", nt_errstr(status));
goto close_scmanager;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_CreateServiceW "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_scmanager;
}
}
status = dcerpc_svcctl_QueryServiceStatus(
rpccli->binding_handle,
frame,
&service_handle,
&service_status,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
if (!(flags & SVC_IGNORE_INTERACTIVE)) {
need_conf =
!(service_status.type &
SERVICE_TYPE_INTERACTIVE_PROCESS) ^
!(flags & SVC_INTERACTIVE);
}
if (service_status.state == SVCCTL_STOPPED) {
need_start = true;
} else if (need_conf) {
status = dcerpc_svcctl_ControlService(
rpccli->binding_handle,
frame,
&service_handle,
SVCCTL_CONTROL_STOP,
&service_status,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
do {
smb_msleep(100);
status = dcerpc_svcctl_QueryServiceStatus(
rpccli->binding_handle,
frame,
&service_handle,
&service_status,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
} while (service_status.state == SVCCTL_STOP_PENDING);
need_start = 1;
}
if (need_conf) {
status = dcerpc_svcctl_ChangeServiceConfigW(
rpccli->binding_handle,
frame,
&service_handle,
SERVICE_TYPE_WIN32_OWN_PROCESS |
((flags & SVC_INTERACTIVE) ?
SERVICE_TYPE_INTERACTIVE_PROCESS : 0), /* type */
UINT32_MAX, /* start_type, SERVICE_NO_CHANGE */
UINT32_MAX, /* error_control, SERVICE_NO_CHANGE */
NULL, /* binary_path */
NULL, /* load_order_group */
NULL, /* tag_id */
NULL, /* dependencies */
0, /* dwDependSize */
NULL, /* service_start_name */
NULL, /* password */
0, /* dwPwSize */
NULL, /* display_name */
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_ChangeServiceConfigW "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
}
if (need_start) {
status = winexe_svc_upload(
hostname,
port,
service_filename,
svc32_exe,
svc64_exe,
credentials,
flags);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("winexe_svc_upload failed: %s\n",
nt_errstr(status));
goto close_service;
}
status = dcerpc_svcctl_StartServiceW(
rpccli->binding_handle,
frame,
&service_handle,
0, /* num_args */
NULL, /* arguments */
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_StartServiceW "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_StartServiceW "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
do {
smb_msleep(100);
status = dcerpc_svcctl_QueryServiceStatus(
rpccli->binding_handle,
frame,
&service_handle,
&service_status,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
} while (service_status.state == SVCCTL_START_PENDING);
if (service_status.state != SVCCTL_RUNNING) {
DBG_WARNING("Failed to start service\n");
status = NT_STATUS_UNSUCCESSFUL;
goto close_service;
}
}
close_service:
{
NTSTATUS close_status;
WERROR close_werr;
close_status = dcerpc_svcctl_CloseServiceHandle(
rpccli->binding_handle,
frame,
&service_handle,
&close_werr);
if (!NT_STATUS_IS_OK(close_status)) {
DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
"failed: %s\n", nt_errstr(close_status));
goto done;
}
if (!W_ERROR_IS_OK(close_werr)) {
DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
" failed: %s\n", win_errstr(close_werr));
goto done;
}
}
close_scmanager:
{
NTSTATUS close_status;
WERROR close_werr;
close_status = dcerpc_svcctl_CloseServiceHandle(
rpccli->binding_handle,
frame,
&scmanager_handle,
&close_werr);
if (!NT_STATUS_IS_OK(close_status)) {
DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
"failed: %s\n", nt_errstr(close_status));
goto done;
}
if (!W_ERROR_IS_OK(close_werr)) {
DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
" failed: %s\n", win_errstr(close_werr));
goto done;
}
}
done:
TALLOC_FREE(rpccli);
TALLOC_FREE(frame);
return status;
}
static NTSTATUS winexe_svc_uninstall(
struct cli_state *cli,
const char *service_name)
{
TALLOC_CTX *frame = talloc_stackframe();
struct rpc_pipe_client *rpccli;
struct policy_handle scmanager_handle;
struct policy_handle service_handle;
struct SERVICE_STATUS service_status;
NTSTATUS status;
WERROR werr;
const char *remote_name = smbXcli_conn_remote_name(cli->conn);
const struct sockaddr_storage *remote_sockaddr =
smbXcli_conn_remote_sockaddr(cli->conn);
status = cli_rpc_pipe_open_noauth_transport(
cli,
NCACN_NP,
&ndr_table_svcctl,
remote_name,
remote_sockaddr,
&rpccli);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("cli_rpc_pipe_open_noauth_transport failed: %s\n",
nt_errstr(status));
goto done;
}
status = dcerpc_svcctl_OpenSCManagerW(
rpccli->binding_handle,
frame,
remote_name,
NULL,
SEC_FLAG_MAXIMUM_ALLOWED,
&scmanager_handle,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
nt_errstr(status));
goto done;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_OpenSCManagerW failed: %s\n",
win_errstr(werr));
goto done;
}
status = dcerpc_svcctl_OpenServiceW(
rpccli->binding_handle,
frame,
&scmanager_handle,
service_name,
SERVICE_ALL_ACCESS,
&service_handle,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
nt_errstr(status));
goto close_scmanager;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_OpenServiceW failed: %s\n",
win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_scmanager;
}
status = dcerpc_svcctl_ControlService(
rpccli->binding_handle,
frame,
&service_handle,
SVCCTL_CONTROL_STOP,
&service_status,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_ControlServiceStatus "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
do {
smb_msleep(100);
status = dcerpc_svcctl_QueryServiceStatus(
rpccli->binding_handle,
frame,
&service_handle,
&service_status,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_QueryServiceStatus "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
} while (service_status.state != SVCCTL_STOPPED);
status = dcerpc_svcctl_DeleteService(
rpccli->binding_handle,
frame,
&service_handle,
&werr);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("dcerpc_svcctl_DeleteService "
"failed: %s\n", nt_errstr(status));
goto close_service;
}
if (!W_ERROR_IS_OK(werr)) {
DBG_WARNING("dcerpc_svcctl_DeleteService "
"failed: %s\n", win_errstr(werr));
status = werror_to_ntstatus(werr);
goto close_service;
}
close_service:
{
NTSTATUS close_status;
WERROR close_werr;
close_status = dcerpc_svcctl_CloseServiceHandle(
rpccli->binding_handle,
frame,
&service_handle,
&close_werr);
if (!NT_STATUS_IS_OK(close_status)) {
DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
"failed: %s\n", nt_errstr(close_status));
goto done;
}
if (!W_ERROR_IS_OK(close_werr)) {
DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
" failed: %s\n", win_errstr(close_werr));
goto done;
}
}
close_scmanager:
{
NTSTATUS close_status;
WERROR close_werr;
close_status = dcerpc_svcctl_CloseServiceHandle(
rpccli->binding_handle,
frame,
&scmanager_handle,
&close_werr);
if (!NT_STATUS_IS_OK(close_status)) {
DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
"failed: %s\n", nt_errstr(close_status));
goto done;
}
if (!W_ERROR_IS_OK(close_werr)) {
DBG_WARNING("dcerpc_svcctl_CloseServiceHandle "
" failed: %s\n", win_errstr(close_werr));
goto done;
}
}
done:
TALLOC_FREE(rpccli);
TALLOC_FREE(frame);
return status;
}
struct winexe_out_pipe_state {
struct tevent_context *ev;
struct cli_state *cli;
uint16_t out_pipe;
int out_fd;
char out_inbuf[256];
};
static void winexe_out_pipe_opened(struct tevent_req *subreq);
static void winexe_out_pipe_got_data(struct tevent_req *subreq);
static void winexe_out_pipe_closed(struct tevent_req *subreq);
static struct tevent_req *winexe_out_pipe_send(
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct cli_state *cli,
const char *pipe_name,
int out_fd)
{
struct tevent_req *req, *subreq;
struct winexe_out_pipe_state *state;
req = tevent_req_create(mem_ctx, &state,
struct winexe_out_pipe_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->cli = cli;
state->out_fd = out_fd;
subreq = cli_ntcreate_send(
state,
state->ev,
state->cli,
pipe_name,
0,
SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
SEC_RIGHTS_FILE_EXECUTE,
0, /* FileAttributes */
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
FILE_OPEN, /* CreateDisposition */
0, /* CreateOptions */
SMB2_IMPERSONATION_IMPERSONATION,
0); /* SecurityFlags */
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, winexe_out_pipe_opened, req);
return req;
}
static void winexe_out_pipe_opened(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_out_pipe_state *state = tevent_req_data(
req, struct winexe_out_pipe_state);
int timeout;
NTSTATUS status;
status = cli_ntcreate_recv(subreq, &state->out_pipe, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
timeout = state->cli->timeout;
state->cli->timeout = 0;
subreq = cli_read_send(
state,
state->ev,
state->cli,
state->out_pipe,
state->out_inbuf,
0,
sizeof(state->out_inbuf));
state->cli->timeout = timeout;
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req);
}
static void winexe_out_pipe_got_data(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_out_pipe_state *state = tevent_req_data(
req, struct winexe_out_pipe_state);
NTSTATUS status;
int timeout;
size_t received;
ssize_t written;
status = cli_read_recv(subreq, &received);
TALLOC_FREE(subreq);
DBG_DEBUG("cli_read for %d gave %s\n",
state->out_fd,
nt_errstr(status));
if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
subreq = cli_close_send(
state,
state->ev,
state->cli,
state->out_pipe);
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_out_pipe_closed, req);
return;
}
if (tevent_req_nterror(req, status)) {
return;
}
if (received > 0) {
written = sys_write(state->out_fd, state->out_inbuf, received);
if (written == -1) {
tevent_req_nterror(req, map_nt_error_from_unix(errno));
return;
}
}
timeout = state->cli->timeout;
state->cli->timeout = 0;
subreq = cli_read_send(
state,
state->ev,
state->cli,
state->out_pipe,
state->out_inbuf,
0,
sizeof(state->out_inbuf));
state->cli->timeout = timeout;
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_out_pipe_got_data, req);
}
static void winexe_out_pipe_closed(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
NTSTATUS status;
status = cli_close_recv(subreq);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
tevent_req_done(req);
}
static NTSTATUS winexe_out_pipe_recv(struct tevent_req *req)
{
return tevent_req_simple_recv_ntstatus(req);
}
struct winexe_in_pipe_state {
struct tevent_context *ev;
struct cli_state *cli;
struct tevent_req *fd_read_req;
bool close_requested;
bool closing;
uint16_t in_pipe;
int in_fd;
char inbuf[256];
};
static void winexe_in_pipe_opened(struct tevent_req *subreq);
static void winexe_in_pipe_got_data(struct tevent_req *subreq);
static void winexe_in_pipe_written(struct tevent_req *subreq);
static void winexe_in_pipe_closed(struct tevent_req *subreq);
static struct tevent_req *winexe_in_pipe_send(
TALLOC_CTX *mem_ctx,
struct tevent_context *ev,
struct cli_state *cli,
const char *pipe_name,
int in_fd)
{
struct tevent_req *req, *subreq;
struct winexe_in_pipe_state *state;
req = tevent_req_create(mem_ctx, &state,
struct winexe_in_pipe_state);
if (req == NULL) {
return NULL;
}
state->ev = ev;
state->cli = cli;
state->in_fd = in_fd;
subreq = cli_ntcreate_send(
state,
state->ev,
state->cli,
pipe_name,
0,
SEC_RIGHTS_FILE_READ|SEC_RIGHTS_FILE_WRITE|
SEC_RIGHTS_FILE_EXECUTE,
0, /* FileAttributes */
FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
FILE_OPEN, /* CreateDisposition */
0, /* CreateOptions */
SMB2_IMPERSONATION_IMPERSONATION,
0); /* SecurityFlags */
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, winexe_in_pipe_opened, req);
return req;
}
static void winexe_in_pipe_opened(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_in_pipe_state *state = tevent_req_data(
req, struct winexe_in_pipe_state);
NTSTATUS status;
status = cli_ntcreate_recv(subreq, &state->in_pipe, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
subreq = wait_for_read_send(
state,
state->ev,
state->in_fd,
true);
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_in_pipe_got_data, req);
state->fd_read_req = subreq;
}
static void winexe_in_pipe_got_data(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_in_pipe_state *state = tevent_req_data(
req, struct winexe_in_pipe_state);
int err;
bool ok;
int timeout;
ssize_t nread;
ok = wait_for_read_recv(subreq, &err);
TALLOC_FREE(subreq);
if (!ok) {
tevent_req_nterror(req, map_nt_error_from_unix(err));
return;
}
state->fd_read_req = NULL;
nread = sys_read(state->in_fd, &state->inbuf, sizeof(state->inbuf));
if (nread == -1) {
tevent_req_nterror(req, map_nt_error_from_unix(errno));
return;
}
if (nread == 0) {
tevent_req_nterror(req, NT_STATUS_CONNECTION_DISCONNECTED);
return;
}
timeout = state->cli->timeout;
state->cli->timeout = 0;
subreq = cli_writeall_send(
state,
state->ev,
state->cli,
state->in_pipe,
0,
(uint8_t *)state->inbuf,
0,
nread);
state->cli->timeout = timeout;
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_in_pipe_written, req);
}
static void winexe_in_pipe_written(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_in_pipe_state *state = tevent_req_data(
req, struct winexe_in_pipe_state);
NTSTATUS status;
status = cli_writeall_recv(subreq, NULL);
TALLOC_FREE(subreq);
DBG_DEBUG("cli_writeall for %d gave %s\n",
state->in_fd,
nt_errstr(status));
if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED) ||
state->close_requested) {
subreq = cli_close_send(
state,
state->ev,
state->cli,
state->in_pipe);
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 */
SMB2_IMPERSONATION_IMPERSONATION,
0); /* SecurityFlags */
if (tevent_req_nomem(subreq, req)) {
return tevent_req_post(req, ev);
}
tevent_req_set_callback(subreq, winexe_ctrl_opened, req);
return req;
}
static void winexe_ctrl_opened(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_ctrl_state *state = tevent_req_data(
req, struct winexe_ctrl_state);
int timeout;
NTSTATUS status;
static const char cmd[] = "get codepage\nget version\n";
status = cli_ntcreate_recv(subreq, &state->ctrl_pipe, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
timeout = state->cli->timeout;
state->cli->timeout = 0;
subreq = cli_read_send(
state,
state->ev,
state->cli,
state->ctrl_pipe,
state->ctrl_inbuf,
0,
sizeof(state->ctrl_inbuf)-1);
state->cli->timeout = timeout;
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_ctrl_got_read, req);
subreq = cli_writeall_send(
state,
state->ev,
state->cli,
state->ctrl_pipe,
0,
(const uint8_t *)cmd,
0,
strlen(cmd));
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_ctrl_wrote_version, req);
}
static void winexe_ctrl_got_read(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_ctrl_state *state = tevent_req_data(
req, struct winexe_ctrl_state);
NTSTATUS status;
int timeout;
size_t received;
unsigned int version, return_code;
int ret;
status = cli_read_recv(subreq, &received);
TALLOC_FREE(subreq);
if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
subreq = cli_close_send(
state,
state->ev,
state->cli,
state->ctrl_pipe);
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_ctrl_pipe_closed, req);
return;
}
if (tevent_req_nterror(req, status)) {
return;
}
DBG_DEBUG("Got %zu bytes\n", received);
timeout = state->cli->timeout;
state->cli->timeout = 0;
subreq = cli_read_send(
state,
state->ev,
state->cli,
state->ctrl_pipe,
state->ctrl_inbuf,
0,
sizeof(state->ctrl_inbuf)-1);
state->cli->timeout = timeout;
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_ctrl_got_read, req);
ret = sscanf(state->ctrl_inbuf, "version 0x%x\n", &version);
if (ret == 1) {
DBG_DEBUG("Got version %x\n", version);
subreq = cli_writeall_send(
state,
state->ev,
state->cli,
state->ctrl_pipe,
0,
(const uint8_t *)state->cmd,
0,
strlen(state->cmd));
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_ctrl_wrote_cmd, req);
return;
}
ret = strncmp(state->ctrl_inbuf, "std_io_err ", strlen("std_io_err "));
if (ret == 0) {
char *p = state->ctrl_inbuf + 11;
char *q = strchr(state->ctrl_inbuf, '\n');
char *postfix;
size_t postfix_len;
if (q == NULL) {
DBG_DEBUG("Got invalid pipe postfix\n");
return;
}
postfix_len = q - p;
postfix = talloc_strndup(state, p, postfix_len);
if (tevent_req_nomem(postfix, req)) {
return;
}
DBG_DEBUG("Got pipe postfix %s\n", postfix);
subreq = winexe_pipes_send(
state,
state->ev,
state->cli,
postfix);
if (tevent_req_nomem(subreq, req)) {
return;
}
tevent_req_set_callback(subreq, winexe_ctrl_pipes_done, req);
state->pipes_req = subreq;
return;
}
ret = strncmp(state->ctrl_inbuf, "error ", strlen("error "));
if (ret == 0) {
printf("Error: %s", state->ctrl_inbuf);
return;
}
ret = sscanf(state->ctrl_inbuf, "return_code %x\n", &return_code);
if (ret == 1) {
state->return_code = return_code;
return;
}
}
static void winexe_ctrl_wrote_version(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
NTSTATUS status;
status = cli_writeall_recv(subreq, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
}
static void winexe_ctrl_wrote_cmd(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
NTSTATUS status;
status = cli_writeall_recv(subreq, NULL);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
}
static void winexe_ctrl_pipe_closed(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_ctrl_state *state = tevent_req_data(
req, struct winexe_ctrl_state);
NTSTATUS status;
status = cli_close_recv(subreq);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
state->ctrl_pipe_done = true;
if (state->pipes_req == NULL) {
tevent_req_done(req);
}
}
static void winexe_ctrl_pipes_done(struct tevent_req *subreq)
{
struct tevent_req *req = tevent_req_callback_data(
subreq, struct tevent_req);
struct winexe_ctrl_state *state = tevent_req_data(
req, struct winexe_ctrl_state);
NTSTATUS status;
status = winexe_pipes_recv(subreq);
TALLOC_FREE(subreq);
if (tevent_req_nterror(req, status)) {
return;
}
state->pipes_req = NULL;
if (state->ctrl_pipe_done) {
tevent_req_done(req);
}
}
static NTSTATUS winexe_ctrl_recv(struct tevent_req *req,
int *preturn_code)
{
struct winexe_ctrl_state *state = tevent_req_data(
req, struct winexe_ctrl_state);
NTSTATUS status;
if (tevent_req_is_nterror(req, &status)) {
return status;
}
if (preturn_code != NULL) {
*preturn_code = state->return_code;
}
return NT_STATUS_OK;
}
static NTSTATUS winexe_ctrl(struct cli_state *cli,
const char *cmd,
int *preturn_code)
{
struct tevent_context *ev = NULL;
struct tevent_req *req = NULL;
NTSTATUS status = NT_STATUS_NO_MEMORY;
bool ok;
ev = samba_tevent_context_init(cli);
if (ev == NULL) {
goto done;
}
req = winexe_ctrl_send(ev, ev, cli, cmd);
if (req == NULL) {
goto done;
}
ok = tevent_req_poll_ntstatus(req, ev, &status);
if (!ok) {
goto done;
}
status = winexe_ctrl_recv(req, preturn_code);
done:
TALLOC_FREE(req);
TALLOC_FREE(ev);
return status;
}
#ifdef HAVE_WINEXE_CC_WIN32
const DATA_BLOB *winexesvc32_exe_binary(void);
#endif
#ifdef HAVE_WINEXE_CC_WIN64
const DATA_BLOB *winexesvc64_exe_binary(void);
#endif
int main(int argc, char *argv[])
{
TALLOC_CTX *frame = talloc_stackframe();
const char **const_argv = discard_const_p(const char *, argv);
struct program_options options = {0};
struct cli_state *cli = NULL;
const char *service_name = SERVICE_NAME;
char *service_filename = NULL;
#ifdef HAVE_WINEXE_CC_WIN32
const DATA_BLOB *winexesvc32_exe = winexesvc32_exe_binary();
#else
const DATA_BLOB *winexesvc32_exe = NULL;
#endif
#ifdef HAVE_WINEXE_CC_WIN64
const DATA_BLOB *winexesvc64_exe = winexesvc64_exe_binary();
#else
const DATA_BLOB *winexesvc64_exe = NULL;
#endif
NTSTATUS status;
int ret = 1;
int return_code = 0;
smb_init_locale();
parse_args(argc, const_argv, frame, &options);
samba_cmdline_burn(argc, argv);
if (options.cmd == NULL) {
fprintf(stderr, "no cmd given\n");
goto done;
}
service_filename = talloc_asprintf(frame, "%s.exe", service_name);
if (service_filename == NULL) {
DBG_WARNING("talloc_asprintf failed\n");
goto done;
}
status = cli_full_connection_creds(
&cli,
lp_netbios_name(),
options.hostname,
NULL,
options.port,
"IPC$",
"IPC",
options.credentials,
CLI_FULL_CONNECTION_IPC);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("cli_full_connection_creds failed: %s\n",
nt_errstr(status));
goto done;
}
status = winexe_svc_install(
cli,
options.hostname,
options.port,
service_name,
service_filename,
winexesvc32_exe,
winexesvc64_exe,
options.credentials,
options.flags);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("winexe_svc_install failed: %s\n",
nt_errstr(status));
goto done;
}
status = winexe_ctrl(cli, options.cmd, &return_code);
if (NT_STATUS_EQUAL(status, NT_STATUS_PIPE_DISCONNECTED)) {
/* Normal finish */
status = NT_STATUS_OK;
}
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("cli_ctrl failed: %s\n",
nt_errstr(status));
goto done;
}
if (options.flags & SVC_UNINSTALL) {
status = winexe_svc_uninstall(
cli,
service_name);
if (!NT_STATUS_IS_OK(status)) {
DBG_WARNING("winexe_svc_uninstall failed: %s\n",
nt_errstr(status));
goto done;
}
}
ret = return_code;
done:
TALLOC_FREE(frame);
return ret;
}