357 lines
11 KiB
C
357 lines
11 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"
|
||
|
|
#define SERVICE_ALL_ACCESS (0xF01FF)
|
||
|
|
#define SERVICE_WIN32_OWN_PROCESS (0x00000010)
|
||
|
|
#define SERVICE_DEMAND_START (0x00000003)
|
||
|
|
#define SERVICE_ERROR_NORMAL (0x00000001)
|
||
|
|
#define SERVICE_CONTROL_STOP (0x00000001)
|
||
|
|
#define NT_STATUS_SERVICE_DOES_NOT_EXIST NT_STATUS(0x00000424)
|
||
|
|
#include "system/filesys.h"
|
||
|
|
#include "libcli/libcli.h"
|
||
|
|
#include "libcli/smb_composite/smb_composite.h"
|
||
|
|
#include "lib/util/util.h"
|
||
|
|
|
||
|
|
#include "winexe.h"
|
||
|
|
|
||
|
|
#define NT_ERR(status, lvl, args...) if (!NT_STATUS_IS_OK(status)) { DEBUG(lvl,("ERROR: " args)); DEBUG(lvl,(". %s.\n", nt_errstr(status))); return status; }
|
||
|
|
#define NT_RES(status, werr) (NT_STATUS_IS_OK(status) ? werror_to_ntstatus(werr) : status)
|
||
|
|
|
||
|
|
NTSTATUS svc_pipe_connect(struct dcerpc_pipe **psvc_pipe,
|
||
|
|
const char *hostname,
|
||
|
|
struct cli_credentials *credentials)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
char *binding;
|
||
|
|
|
||
|
|
asprintf(&binding, "ncacn_np:%s%s", hostname, DEBUGLVL(9)?"[print]":"");
|
||
|
|
status =
|
||
|
|
dcerpc_pipe_connect(NULL, psvc_pipe, binding,
|
||
|
|
&dcerpc_table_svcctl, credentials, NULL);
|
||
|
|
free(binding);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_OpenSCManager(struct dcerpc_pipe * svc_pipe,
|
||
|
|
const char *hostname,
|
||
|
|
struct policy_handle * pscm_handle)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_OpenSCManagerW r;
|
||
|
|
|
||
|
|
r.in.MachineName = hostname;
|
||
|
|
r.in.DatabaseName = NULL;
|
||
|
|
r.in.access_mask = SEC_FLAG_MAXIMUM_ALLOWED;
|
||
|
|
r.out.handle = pscm_handle;
|
||
|
|
status = dcerpc_svcctl_OpenSCManagerW(svc_pipe, NULL, &r);
|
||
|
|
return NT_RES(status, r.out.result);
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_OpenService(struct dcerpc_pipe * svc_pipe,
|
||
|
|
struct policy_handle * pscm_handle,
|
||
|
|
const char *ServiceName,
|
||
|
|
struct policy_handle * psvc_handle)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_OpenServiceW r;
|
||
|
|
|
||
|
|
r.in.scmanager_handle = pscm_handle;
|
||
|
|
r.in.ServiceName = ServiceName;
|
||
|
|
r.in.access_mask = SERVICE_ALL_ACCESS;
|
||
|
|
r.out.handle = psvc_handle;
|
||
|
|
status = dcerpc_svcctl_OpenServiceW(svc_pipe, NULL, &r);
|
||
|
|
return NT_RES(status, r.out.result);
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_CreateService(struct dcerpc_pipe * svc_pipe,
|
||
|
|
struct policy_handle * pscm_handle,
|
||
|
|
const char *ServiceName,
|
||
|
|
uint32_t type,
|
||
|
|
const char *binary_path,
|
||
|
|
struct policy_handle * psvc_handle)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_CreateServiceW r;
|
||
|
|
|
||
|
|
r.in.scmanager_handle = pscm_handle;
|
||
|
|
r.in.service_name = ServiceName;
|
||
|
|
r.in.display_name = NULL;
|
||
|
|
r.in.desired_access = SERVICE_ALL_ACCESS;
|
||
|
|
r.in.type = type;
|
||
|
|
r.in.start_type = SERVICE_DEMAND_START;
|
||
|
|
r.in.error_control = SERVICE_ERROR_NORMAL;
|
||
|
|
r.in.binary_path = binary_path;
|
||
|
|
r.in.load_order_group = NULL;
|
||
|
|
r.in.tag_id = NULL;
|
||
|
|
r.in.dependencies = NULL;
|
||
|
|
r.in.dependencies_size = 0;
|
||
|
|
r.in.service_start_name = NULL;
|
||
|
|
r.in.password = NULL;
|
||
|
|
r.in.password_size = 0;
|
||
|
|
r.out.handle = psvc_handle;
|
||
|
|
r.out.tag_id = NULL;
|
||
|
|
status = dcerpc_svcctl_CreateServiceW(svc_pipe, NULL, &r);
|
||
|
|
return NT_RES(status, r.out.result);
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_ChangeServiceConfig(struct dcerpc_pipe * svc_pipe,
|
||
|
|
struct policy_handle * psvc_handle,
|
||
|
|
uint32_t type,
|
||
|
|
const char *binary_path)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_ChangeServiceConfigW r;
|
||
|
|
|
||
|
|
r.in.handle = psvc_handle;
|
||
|
|
r.in.type = type;
|
||
|
|
r.in.start_type = SERVICE_NO_CHANGE;
|
||
|
|
r.in.error_control = SERVICE_NO_CHANGE;
|
||
|
|
r.in.binary_path = binary_path;
|
||
|
|
r.in.load_order_group = NULL;
|
||
|
|
r.in.tag_id = NULL;
|
||
|
|
r.in.dependencies = NULL;
|
||
|
|
r.in.dependencies_size = 0;
|
||
|
|
r.in.service_start_name = NULL;
|
||
|
|
r.in.password = NULL;
|
||
|
|
r.in.password_size = 0;
|
||
|
|
r.in.display_name = NULL;
|
||
|
|
r.out.tag_id = NULL;
|
||
|
|
status = dcerpc_svcctl_ChangeServiceConfigW(svc_pipe, NULL, &r);
|
||
|
|
return NT_RES(status, r.out.result);
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_StartService(struct dcerpc_pipe * svc_pipe,
|
||
|
|
struct policy_handle * psvc_handle)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_StartServiceW r;
|
||
|
|
|
||
|
|
r.in.handle = psvc_handle;
|
||
|
|
r.in.NumArgs = 0;
|
||
|
|
r.in.Arguments = NULL;
|
||
|
|
status = dcerpc_svcctl_StartServiceW(svc_pipe, NULL, &r);
|
||
|
|
return NT_RES(status, r.out.result);
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_ControlService(struct dcerpc_pipe * svc_pipe,
|
||
|
|
struct policy_handle * psvc_handle,
|
||
|
|
int control, struct SERVICE_STATUS * sstatus)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_ControlService r;
|
||
|
|
|
||
|
|
r.in.handle = psvc_handle;
|
||
|
|
r.in.control = control;
|
||
|
|
r.out.status = sstatus;
|
||
|
|
status = dcerpc_svcctl_ControlService(svc_pipe, NULL, &r);
|
||
|
|
return NT_RES(status, r.out.result);
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_QueryServiceStatus(struct dcerpc_pipe * svc_pipe,
|
||
|
|
struct policy_handle * psvc_handle,
|
||
|
|
struct SERVICE_STATUS * sstatus)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_QueryServiceStatus r;
|
||
|
|
|
||
|
|
r.in.handle = psvc_handle;
|
||
|
|
r.out.status = sstatus;
|
||
|
|
status = dcerpc_svcctl_QueryServiceStatus(svc_pipe, NULL, &r);
|
||
|
|
return NT_RES(status, r.out.result);
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_DeleteService(struct dcerpc_pipe * svc_pipe,
|
||
|
|
struct policy_handle * psvc_handle)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_DeleteService r;
|
||
|
|
|
||
|
|
r.in.handle = psvc_handle;
|
||
|
|
status = dcerpc_svcctl_DeleteService(svc_pipe, NULL, &r);
|
||
|
|
return NT_RES(status, r.out.result);
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_CloseServiceHandle(struct dcerpc_pipe * svc_pipe,
|
||
|
|
struct policy_handle * psvc_handle)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct svcctl_CloseServiceHandle r;
|
||
|
|
|
||
|
|
r.in.handle = psvc_handle;
|
||
|
|
r.out.handle = psvc_handle;
|
||
|
|
status = dcerpc_svcctl_CloseServiceHandle(svc_pipe, NULL, &r);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_UploadService(const char *hostname,
|
||
|
|
struct cli_credentials * credentials, int force)
|
||
|
|
{
|
||
|
|
struct smb_composite_savefile *io;
|
||
|
|
struct smbcli_state *cli;
|
||
|
|
NTSTATUS status;
|
||
|
|
|
||
|
|
status =
|
||
|
|
smbcli_full_connection(NULL, &cli, hostname, "ADMIN$", NULL,
|
||
|
|
credentials, NULL);
|
||
|
|
NT_ERR(status, 1, "Failed to open ADMIN$ share");
|
||
|
|
if (!force) {
|
||
|
|
int fd = smbcli_open(cli->tree, "winexesvc.exe", O_RDONLY, DENY_NONE);
|
||
|
|
if (fd >= 0) {
|
||
|
|
smbcli_close(cli->tree, fd);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
smbcli_unlink(cli->tree, "winexesvc.exe");
|
||
|
|
}
|
||
|
|
io = talloc_zero(cli->tree, struct smb_composite_savefile);
|
||
|
|
io->in.fname = "winexesvc.exe";
|
||
|
|
io->in.data = winexesvc_exe;
|
||
|
|
io->in.size = winexesvc_exe_len;
|
||
|
|
status = smb_composite_savefile(cli->tree, io);
|
||
|
|
NT_ERR(status, 1, "Failed to save ADMIN$/%s", io->in.fname);
|
||
|
|
talloc_free(io);
|
||
|
|
smbcli_tdis(cli);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Start, Creates, Install service if necccesary */
|
||
|
|
NTSTATUS svc_install(const char *hostname,
|
||
|
|
struct cli_credentials * credentials, int flags)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct dcerpc_pipe *svc_pipe;
|
||
|
|
struct policy_handle scm_handle;
|
||
|
|
struct policy_handle svc_handle;
|
||
|
|
int need_start;
|
||
|
|
|
||
|
|
status = svc_pipe_connect(&svc_pipe, hostname, credentials);
|
||
|
|
NT_ERR(status, 1, "Cannot connect to svcctl pipe");
|
||
|
|
status = svc_UploadService(hostname, credentials, flags & SVC_FORCE_UPLOAD);
|
||
|
|
NT_ERR(status, 1, "UploadService failed");
|
||
|
|
status = svc_OpenSCManager(svc_pipe, hostname, &scm_handle);
|
||
|
|
NT_ERR(status, 1, "OpenSCManager failed");
|
||
|
|
status = svc_OpenService(svc_pipe, &scm_handle, "winexesvc",
|
||
|
|
&svc_handle);
|
||
|
|
if (NT_STATUS_EQUAL(status, NT_STATUS_SERVICE_DOES_NOT_EXIST)) {
|
||
|
|
status =
|
||
|
|
svc_CreateService(svc_pipe, &scm_handle, "winexesvc",
|
||
|
|
SERVICE_WIN32_OWN_PROCESS |
|
||
|
|
(flags & SVC_INTERACTIVE ? SERVICE_INTERACTIVE_PROCESS : 0),
|
||
|
|
"winexesvc.exe", &svc_handle);
|
||
|
|
NT_ERR(status, 1, "CreateService failed");
|
||
|
|
} else if (NT_STATUS_IS_OK(status) && !(flags & SVC_IGNORE_INTERACTIVE)) {
|
||
|
|
struct SERVICE_STATUS s;
|
||
|
|
int what, want;
|
||
|
|
status = svc_QueryServiceStatus(svc_pipe, &svc_handle, &s);
|
||
|
|
NT_ERR(status, 1, "QueryServiceStatus failed");
|
||
|
|
what = s.type & SERVICE_INTERACTIVE_PROCESS;
|
||
|
|
want = flags & SVC_INTERACTIVE;
|
||
|
|
if ((what && !want) || (!what && want)) {
|
||
|
|
need_start = 1;
|
||
|
|
if (s.state != SERVICE_STOPPED) {
|
||
|
|
status = svc_ControlService(svc_pipe, &svc_handle,
|
||
|
|
SERVICE_CONTROL_STOP, &s);
|
||
|
|
NT_ERR(status, 1, "StopService failed");
|
||
|
|
}
|
||
|
|
status = svc_ChangeServiceConfig(svc_pipe, &svc_handle,
|
||
|
|
SERVICE_WIN32_OWN_PROCESS |
|
||
|
|
(want ? SERVICE_INTERACTIVE_PROCESS : 0),
|
||
|
|
NULL);
|
||
|
|
NT_ERR(status, 1, "ChangeServiceConfig failed");
|
||
|
|
do {
|
||
|
|
msleep(100);
|
||
|
|
status = svc_QueryServiceStatus(svc_pipe, &svc_handle, &s);
|
||
|
|
NT_ERR(status, 1, "QueryServiceStatus failed");
|
||
|
|
} while (s.state == SERVICE_STOP_PENDING);
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
NT_ERR(status, 1, "OpenService failed");
|
||
|
|
}
|
||
|
|
if ((flags & SVC_IGNORE_INTERACTIVE) || need_start) {
|
||
|
|
status = svc_StartService(svc_pipe, &svc_handle);
|
||
|
|
NT_ERR(status, 1, "StartService failed");
|
||
|
|
}
|
||
|
|
{
|
||
|
|
struct SERVICE_STATUS s;
|
||
|
|
do {
|
||
|
|
msleep(100);
|
||
|
|
status = svc_QueryServiceStatus(svc_pipe, &svc_handle, &s);
|
||
|
|
NT_ERR(status, 1, "QueryServiceStatus failed");
|
||
|
|
} while (s.state == SERVICE_START_PENDING);
|
||
|
|
if (s.state != SERVICE_RUNNING) {
|
||
|
|
DEBUG(0, ("Service cannot start, status=0x%08X\n", s.state));
|
||
|
|
return NT_STATUS_UNSUCCESSFUL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
svc_CloseServiceHandle(svc_pipe, &svc_handle);
|
||
|
|
svc_CloseServiceHandle(svc_pipe, &scm_handle);
|
||
|
|
talloc_free(svc_pipe);
|
||
|
|
return status;
|
||
|
|
}
|
||
|
|
|
||
|
|
NTSTATUS svc_uninstall(const char *hostname,
|
||
|
|
struct cli_credentials * credentials)
|
||
|
|
{
|
||
|
|
NTSTATUS status;
|
||
|
|
struct dcerpc_pipe *svc_pipe;
|
||
|
|
struct policy_handle scm_handle;
|
||
|
|
struct policy_handle svc_handle;
|
||
|
|
struct SERVICE_STATUS svc_status;
|
||
|
|
|
||
|
|
status = svc_pipe_connect(&svc_pipe, hostname, credentials);
|
||
|
|
NT_ERR(status, 1, "Cannot connect to svcctl pipe");
|
||
|
|
status = svc_OpenSCManager(svc_pipe, hostname, &scm_handle);
|
||
|
|
NT_ERR(status, 1, "OpenSCManager failed");
|
||
|
|
status =
|
||
|
|
svc_OpenService(svc_pipe, &scm_handle, "winexesvc",
|
||
|
|
&svc_handle);
|
||
|
|
NT_ERR(status, 1, "OpenService failed");
|
||
|
|
DEBUG(1, ("OpenService - %s\n", nt_errstr(status)));
|
||
|
|
if (NT_STATUS_IS_OK(status)) {
|
||
|
|
status =
|
||
|
|
svc_ControlService(svc_pipe, &svc_handle,
|
||
|
|
SERVICE_CONTROL_STOP, &svc_status);
|
||
|
|
{
|
||
|
|
struct SERVICE_STATUS s;
|
||
|
|
do {
|
||
|
|
msleep(100);
|
||
|
|
status = svc_QueryServiceStatus(svc_pipe, &svc_handle, &s);
|
||
|
|
NT_ERR(status, 1, "QueryServiceStatus failed");
|
||
|
|
} while (s.state == SERVICE_STOP_PENDING);
|
||
|
|
if (s.state != SERVICE_STOPPED) {
|
||
|
|
DEBUG(0, ("Service cannot stop, status=0x%08X\n", s.state));
|
||
|
|
return NT_STATUS_UNSUCCESSFUL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
DEBUG(1, ("StopService - %s\n", nt_errstr(status)));
|
||
|
|
status = svc_DeleteService(svc_pipe, &svc_handle);
|
||
|
|
DEBUG(1, ("DeleteService - %s\n", nt_errstr(status)));
|
||
|
|
status = svc_CloseServiceHandle(svc_pipe, &svc_handle);
|
||
|
|
DEBUG(1, ("CloseServiceHandle - %s\n", nt_errstr(status)));
|
||
|
|
}
|
||
|
|
svc_CloseServiceHandle(svc_pipe, &scm_handle);
|
||
|
|
DEBUG(1, ("CloseSCMHandle - %s\n", nt_errstr(status)));
|
||
|
|
|
||
|
|
struct smbcli_state *cli;
|
||
|
|
status =
|
||
|
|
smbcli_full_connection(NULL, &cli, hostname, "ADMIN$", NULL,
|
||
|
|
credentials, NULL);
|
||
|
|
NT_ERR(status, 1, "Failed to open ADMIN$ share");
|
||
|
|
/* Give winexesvc some time to exit */
|
||
|
|
msleep(300);
|
||
|
|
status = smbcli_unlink(cli->tree, "winexesvc.exe");
|
||
|
|
DEBUG(1, ("Delete winexesvc.exe - %s\n", nt_errstr(status)));
|
||
|
|
status = smbcli_tdis(cli);
|
||
|
|
DEBUG(1, ("Closing ADMIN$ - %s\n", nt_errstr(status)));
|
||
|
|
return status;
|
||
|
|
}
|