Files
2019-02-16 00:16:52 +01:00

351 lines
10 KiB
C

/*
Copyright (C) Andrzej Hajda 2006
Contact: andrzej.hajda@wp.pl
License: GNU General Public License version 2
*/
#include "includes.h"
#include "lib/cmdline/popt_common.h"
#include "librpc/rpc/dcerpc.h"
#include "librpc/gen_ndr/ndr_svcctl_c.h"
#include "librpc/gen_ndr/ndr_security.h"
#include "libcli/libcli.h"
#include "lib/events/events.h"
#include "winexe.h"
#include "winexesvc/shared.h"
#include <sys/fcntl.h>
#include <sys/unistd.h>
#include <sys/termios.h>
#include <signal.h>
struct program_args {
char *hostname;
char *cmd;
struct cli_credentials *credentials;
int reinstall;
int uninstall;
int system;
char *runas;
int interactive;
};
int abort_requested = 0;
void parse_args(int argc, char *argv[], struct program_args *pmyargs)
{
poptContext pc;
int opt, i;
int argc_new;
char **argv_new;
struct poptOption long_options[] = {
POPT_AUTOHELP
POPT_COMMON_SAMBA
POPT_COMMON_CONNECTION
POPT_COMMON_CREDENTIALS
POPT_COMMON_VERSION
{"uninstall", 0, POPT_ARG_NONE, &pmyargs->uninstall, 0,
"Uninstall winexe service after remote execution", NULL},
{"reinstall", 0, POPT_ARG_NONE, &pmyargs->reinstall, 0,
"Reinstall winexe service before remote execution", NULL},
{"system", 0, POPT_ARG_NONE, &pmyargs->system, 0,
"Use SYSTEM account" , NULL},
{"runas", 0, POPT_ARG_STRING, &pmyargs->runas, 0,
"Run as user (BEWARE: password is sent in cleartext over net)" , "[DOMAIN\\]USERNAME%PASSWORD"},
{"interactive", 0, POPT_ARG_INT, &pmyargs->interactive, 0,
"Desktop interaction: 0 - disallow, 1 - allow. If you allow use also --system switch (Win requirement). Vista do not support this option.", NULL},
POPT_TABLEEND
};
pc = poptGetContext(argv[0], argc, (const char **) argv,
long_options, 0);
poptSetOtherOptionHelp(pc, "//host command");
while ((opt = poptGetNextOpt(pc)) != -1) {
DEBUG(0, ("winexe version %d.%02d\nThis program may be freely redistributed under the terms of the GNU GPL\n", VERSION / 100, VERSION % 100));
poptPrintUsage(pc, stdout, 0);
exit(1);
}
argv_new = discard_const_p(char *, poptGetArgs(pc));
argc_new = argc;
for (i = 0; i < argc - 1; i++) {
if (argv_new[i] == NULL) {
argc_new = i;
break;
}
}
if (argc_new != 2 || argv_new[0][0] != '/'
|| argv_new[0][1] != '/') {
DEBUG(0, ("winexe version %d.%02d\nThis program may be freely redistributed under the terms of the GNU GPL\n", VERSION / 100, VERSION % 100));
poptPrintUsage(pc, stdout, 0);
exit(1);
}
pmyargs->hostname = argv_new[0] + 2;
pmyargs->cmd = argv_new[1];
}
enum {STATE_OPENING, STATE_GETTING_VERSION, STATE_RUNNING, STATE_CLOSING, STATE_CLOSING_FOR_REINSTALL };
struct winexe_context {
int state;
struct program_args *args;
struct smbcli_tree *tree;
struct async_context *ac_ctrl;
struct async_context *ac_io;
struct async_context *ac_err;
int return_code;
};
void exit_program(struct winexe_context *c);
void on_ctrl_pipe_error(struct winexe_context *c, int func, NTSTATUS status)
{
DEBUG(1, ("ERROR: on_ctrl_pipe_error - %s\n", nt_errstr(status)));
static int activated = 0;
if (!activated
&& NT_STATUS_EQUAL(status, NT_STATUS_OBJECT_NAME_NOT_FOUND)) {
status =
svc_install(c->args->hostname, c->args->credentials, c->args->interactive);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,
("ERROR: Failed to install service winexesvc - %s\n",
nt_errstr(status)));
c->return_code = 1;
exit_program(c);
}
activated = 1;
async_open(c->ac_ctrl, "\\pipe\\" PIPE_NAME, OPENX_MODE_ACCESS_RDWR);
} else if (func == ASYNC_OPEN_RECV) {
DEBUG(0,
("ERROR: Cannot open control pipe - %s\n",
nt_errstr(status)));
c->return_code = 1;
exit_program(c);
} else if (func == ASYNC_READ_RECV && c->state == STATE_OPENING) {
;
} else
exit_program(c);
}
void on_io_pipe_open(struct winexe_context *c);
void on_io_pipe_read(struct winexe_context *c, const char *data, int len);
void on_io_pipe_error(struct winexe_context *c, int func, NTSTATUS status);
void on_err_pipe_read(struct winexe_context *c, const char *data, int len);
void on_err_pipe_error(struct winexe_context *c, int func, NTSTATUS status);
const char *cmd_check(const char *data, const char *cmd, int len)
{
int lcmd = strlen(cmd);
if (lcmd >= len)
return 0;
if (!strncmp(data, cmd, lcmd)
&& (data[lcmd] == ' ' || data[lcmd] == '\n')) {
return data + lcmd + 1;
}
return 0;
}
void catch_alarm(int sig)
{
abort_requested = 1;
signal(sig, SIG_DFL);
}
static void timer(struct event_context *ev, struct timed_event *te, struct timeval t, void *private)
{
struct winexe_context *c = talloc_get_type(private, struct winexe_context);
if (abort_requested) {
fprintf(stderr, "Aborting...\n");
async_write(c->ac_ctrl, "abort\n", 6);
} else
event_add_timed(c->tree->session->transport->socket->event.ctx, c, timeval_current_ofs(0, 10000), timer, c);
}
void on_ctrl_pipe_open(struct winexe_context *c)
{
char *str = "get version\n";
DEBUG(1, ("CTRL: Sending command: %s", str));
c->state = STATE_GETTING_VERSION;
async_write(c->ac_ctrl, str, strlen(str));
signal(SIGINT, catch_alarm);
signal(SIGTERM, catch_alarm);
event_add_timed(c->tree->session->transport->socket->event.ctx, c, timeval_current_ofs(0, 10000), timer, c);
}
void on_ctrl_pipe_read(struct winexe_context *c, const char *data, int len)
{
const char *p;
if ((p = cmd_check(data, CMD_STD_IO_ERR, len))) {
DEBUG(1, ("CTRL: Recieved command: %.*s", len, data));
unsigned int npipe = strtoul(p, 0, 16);
c->ac_io = talloc_zero(c, struct async_context);
c->ac_io->tree = c->tree;
c->ac_io->cb_ctx = c;
c->ac_io->cb_open = (async_cb_open) on_io_pipe_open;
c->ac_io->cb_read = (async_cb_read) on_io_pipe_read;
c->ac_io->cb_error = (async_cb_error) on_io_pipe_error;
char *fn = talloc_asprintf(c->ac_io, "\\pipe\\" PIPE_NAME_IO, npipe);
async_open(c->ac_io, fn, OPENX_MODE_ACCESS_RDWR);
c->ac_err = talloc_zero(c, struct async_context);
c->ac_err->tree = c->tree;
c->ac_err->cb_ctx = c;
c->ac_err->cb_read = (async_cb_read) on_err_pipe_read;
c->ac_err->cb_error = (async_cb_error) on_err_pipe_error;
fn = talloc_asprintf(c->ac_err, "\\pipe\\" PIPE_NAME_ERR, npipe);
async_open(c->ac_err, fn, OPENX_MODE_ACCESS_RDWR);
} else if ((p = cmd_check(data, CMD_RETURN_CODE, len))) {
c->return_code = strtoul(p, 0, 16);
} else if ((p = cmd_check(data, "version", len))) {
int ver = strtoul(p, 0, 0);
if (ver/10 != VERSION/10) {
DEBUG(1, ("CTRL: Bad version of service (is %d.%02d, expected %d.%02d), reinstalling.\n", ver/100, ver%100, VERSION/100, VERSION%100));
async_close(c->ac_ctrl);
c->state = STATE_CLOSING_FOR_REINSTALL;
} else {
char *str;
if (c->args->runas)
str = talloc_asprintf(c, "set runas %s\nrun %s\n", c->args->runas, c->args->cmd);
else
str = talloc_asprintf(c, "%srun %s\n", c->args->system ? "set system 1\n" : "" , c->args->cmd);
DEBUG(1, ("CTRL: Sending command: %s", str));
async_write(c->ac_ctrl, str, strlen(str));
talloc_free(str);
c->state = STATE_RUNNING;
}
} else if ((p = cmd_check(data, "error", len))) {
DEBUG(0, ("Error: %.*s", len, data));
if (c->state == STATE_GETTING_VERSION) {
DEBUG(0, ("CTRL: Probably old version of service, reinstalling.\n"));
async_close(c->ac_ctrl);
c->state = STATE_CLOSING_FOR_REINSTALL;
}
} else {
DEBUG(0, ("CTRL: Unknown command: %.*s", len, data));
}
}
void on_ctrl_pipe_close(struct winexe_context *c)
{
if (c->state == STATE_CLOSING_FOR_REINSTALL) {
DEBUG(1,("Reinstalling service\n"));
svc_uninstall(c->args->hostname, c->args->credentials);
svc_install(c->args->hostname, c->args->credentials, c->args->interactive | SVC_FORCE_UPLOAD);
c->state = STATE_OPENING;
async_open(c->ac_ctrl, "\\pipe\\" PIPE_NAME, OPENX_MODE_ACCESS_RDWR);
}
}
static void on_stdin_read_event(struct event_context *event_ctx,
struct fd_event *fde, uint16_t flags,
struct winexe_context *c)
{
char buf[256];
int len;
if ((len = read(0, &buf, sizeof(buf))) > 0) {
async_write(c->ac_io, buf, len);
}
}
void on_io_pipe_open(struct winexe_context *c)
{
event_add_fd(c->tree->session->transport->socket->event.ctx,
c->tree, 0, EVENT_FD_READ,
(event_fd_handler_t) on_stdin_read_event, c);
struct termios term;
tcgetattr(0, &term);
term.c_lflag &= ~ICANON;
tcsetattr(0, TCSANOW, &term);
setbuf(stdin, NULL);
}
void on_io_pipe_read(struct winexe_context *c, const char *data, int len)
{
write(1, data, len);
}
void on_io_pipe_error(struct winexe_context *c, int func, NTSTATUS status)
{
async_close(c->ac_io);
}
void on_err_pipe_read(struct winexe_context *c, const char *data, int len)
{
write(2, data, len);
}
void on_err_pipe_error(struct winexe_context *c, int func, NTSTATUS status)
{
async_close(c->ac_err);
}
void exit_program(struct winexe_context *c)
{
if (c->args->uninstall)
svc_uninstall(c->args->hostname, c->args->credentials);
exit(c->return_code);
}
int main(int argc, char *argv[])
{
NTSTATUS status;
struct smbcli_state *cli;
struct program_args myargs = {.reinstall = 0,.uninstall = 0, .system = 0, .interactive = SVC_IGNORE_INTERACTIVE };
parse_args(argc, argv, &myargs);
DEBUG(1, ("winexe version %d.%02d\nThis program may be freely redistributed under the terms of the GNU GPL\n", VERSION / 100, VERSION % 100));
myargs.interactive &= SVC_INTERACTIVE_MASK;
dcerpc_init();
if (myargs.reinstall)
svc_uninstall(myargs.hostname, cmdline_credentials);
if (!(myargs.interactive & SVC_IGNORE_INTERACTIVE)) {
svc_install(myargs.hostname, cmdline_credentials, myargs.interactive | (myargs.reinstall ? SVC_FORCE_UPLOAD : 0));
}
status =
smbcli_full_connection(NULL, &cli, myargs.hostname, "IPC$",
NULL, cmdline_credentials, NULL);
if (!NT_STATUS_IS_OK(status)) {
DEBUG(0,
("ERROR: Failed to open connection - %s\n",
nt_errstr(status)));
return 1;
}
struct winexe_context *c =
talloc_zero(cli->tree, struct winexe_context);
if (c == NULL) {
DEBUG(0,
("ERROR: Failed to allocate struct winexe_context\n"));
return 1;
}
c->tree = cli->tree;
c->ac_ctrl = talloc_zero(cli->tree, struct async_context);
c->ac_ctrl->tree = cli->tree;
c->ac_ctrl->cb_ctx = c;
c->ac_ctrl->cb_open = (async_cb_open) on_ctrl_pipe_open;
c->ac_ctrl->cb_read = (async_cb_read) on_ctrl_pipe_read;
c->ac_ctrl->cb_error = (async_cb_error) on_ctrl_pipe_error;
c->ac_ctrl->cb_close = (async_cb_close) on_ctrl_pipe_close;
c->args = &myargs;
c->args->credentials = cmdline_credentials;
c->return_code = 99;
c->state = STATE_OPENING;
async_open(c->ac_ctrl, "\\pipe\\" PIPE_NAME, OPENX_MODE_ACCESS_RDWR);
event_loop_wait(cli->tree->session->transport->socket->event.ctx);
return 0;
}