2 * Copyright (C) Andrzej Hajda 2009-2013
3 * Contact: andrzej.hajda@wp.pl
5 * Source of this file: https://git.code.sf.net/p/winexe/winexe-waf
6 * commit b787d2a2c4b1abc3653bad10aec943b8efcd7aab.
8 * ** NOTE! The following "GPLv3 only" license applies to the winexe
9 * ** service files. This does NOT imply that all of Samba is released
10 * ** under the "GPLv3 only" license.
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * version 3 as published by the Free Software Foundation.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, see <http://www.gnu.org/licenses/>.
34 #include "winexesvc.h"
41 FILE *f = fopen("C:\\" SERVICE_NAME ".log", "at");\
51 static SECURITY_ATTRIBUTES sa
;
53 /* Creates SECURITY_ATTRIBUTES sa with full access for BUILTIN\Administrators */
54 static int CreatePipesSA()
57 PSID pAdminSID
= NULL
;
59 PSECURITY_DESCRIPTOR pSD
= NULL
;
61 SID_IDENTIFIER_AUTHORITY SIDAuthNT
= {SECURITY_NT_AUTHORITY
};
63 /* Create a SID for the BUILTIN\Administrators group. */
65 !AllocateAndInitializeSid(
67 SECURITY_BUILTIN_DOMAIN_RID
,
68 DOMAIN_ALIAS_RID_ADMINS
,
69 0, 0, 0, 0, 0, 0, &pAdminSID
72 dbg("AllocateAndInitializeSid Error %lu\n", GetLastError());
75 /* Initialize an EXPLICIT_ACCESS structure for an ACE.
76 The ACE will allow the Administrators group full access to the key.
78 ea
.grfAccessPermissions
= FILE_ALL_ACCESS
;
79 ea
.grfAccessMode
= SET_ACCESS
;
80 ea
.grfInheritance
= NO_INHERITANCE
;
81 ea
.Trustee
.TrusteeForm
= TRUSTEE_IS_SID
;
82 ea
.Trustee
.TrusteeType
= TRUSTEE_IS_GROUP
;
83 ea
.Trustee
.ptstrName
= (LPTSTR
) pAdminSID
;
85 /* Create a new ACL that contains the new ACEs */
86 dwRes
= SetEntriesInAcl(1, &ea
, NULL
, &pACL
);
87 if (ERROR_SUCCESS
!= dwRes
) {
88 dbg("SetEntriesInAcl Error %lu\n", GetLastError());
91 /* Initialize a security descriptor */
92 pSD
= (PSECURITY_DESCRIPTOR
) LocalAlloc(LPTR
, SECURITY_DESCRIPTOR_MIN_LENGTH
);
94 dbg("LocalAlloc Error %lu\n", GetLastError());
98 if (!InitializeSecurityDescriptor(pSD
, SECURITY_DESCRIPTOR_REVISION
))
100 dbg("InitializeSecurityDescriptor Error %lu\n", GetLastError());
103 /* Add the ACL to the security descriptor */
105 !SetSecurityDescriptorDacl(
106 pSD
, TRUE
, /* bDaclPresent flag */
107 pACL
, FALSE
/* not a default DACL */
110 dbg("SetSecurityDescriptorDacl Error %lu\n", GetLastError());
113 /* Initialize a security attributes structure */
114 sa
.nLength
= sizeof(SECURITY_ATTRIBUTES
);
115 sa
.lpSecurityDescriptor
= pSD
;
116 sa
.bInheritHandle
= FALSE
;
125 static int hgets(char *str
, int n
, OV_HANDLE
*pipe
)
131 if (!ReadFile(pipe
->h
, str
, 1, NULL
, &pipe
->o
) && GetLastError() != ERROR_IO_PENDING
)
133 if (!GetOverlappedResult(pipe
->h
, &pipe
->o
, &res
, TRUE
) || !res
)
145 static int hprintf(OV_HANDLE
*pipe
, const char *fmt
, ...)
151 vsnprintf(buf
, sizeof(buf
), fmt
, ap
);
153 if (!WriteFile(pipe
->h
, buf
, strlen(buf
), NULL
, &pipe
->o
) && GetLastError() == ERROR_IO_PENDING
)
154 GetOverlappedResult(pipe
->h
, &pipe
->o
, (LPDWORD
)&res
, TRUE
);
155 FlushFileBuffers(pipe
->h
);
171 } connection_context
;
173 typedef int CMD_FUNC(connection_context
*);
180 static int cmd_set(connection_context
*c
)
182 static const char* var_system
= "system";
183 static const char* var_implevel
= "implevel";
184 static const char* var_runas
= "runas";
185 static const char* var_profile
= "profile";
189 cmdline
= strchr(c
->cmd
, ' ');
195 if ((strstr(cmdline
, var_system
) == cmdline
) && (cmdline
[l
= strlen(var_system
)] == ' ')) {
196 c
->system
= atoi(cmdline
+ l
+ 1);
197 } else if ((strstr(cmdline
, var_implevel
) == cmdline
) && (cmdline
[l
= strlen(var_implevel
)] == ' ')) {
198 c
->implevel
= atoi(cmdline
+ l
+ 1);
199 } else if ((strstr(cmdline
, var_profile
) == cmdline
) && (cmdline
[l
= strlen(var_profile
)] == ' ')) {
200 c
->profile
= atoi(cmdline
+ l
+ 1);
201 } else if ((strstr(cmdline
, var_runas
) == cmdline
) && (cmdline
[l
= strlen(var_runas
)] == ' ')) {
202 c
->runas
= strdup(cmdline
+ l
+ 1);
204 hprintf(c
->pipe
, "error Unknown command (%s)\n", c
->cmd
);
212 static int cmd_get(connection_context
*c
)
214 static const char* var_version
= "version";
215 static const char* var_codepage
= "codepage";
219 cmdline
= strchr(c
->cmd
, ' ');
225 if ((strstr(cmdline
, var_version
) == cmdline
)
226 && (cmdline
[l
= strlen(var_version
)] == 0)) {
227 hprintf(c
->pipe
, "version 0x%04X\n", VERSION
);
228 } else if ((strstr(cmdline
, var_codepage
) == cmdline
)
229 && (cmdline
[l
= strlen(var_codepage
)] == 0)) {
230 hprintf(c
->pipe
, "codepage %d\n", GetOEMCP());
232 hprintf(c
->pipe
, "error Unknown argument (%s)\n", c
->cmd
);
246 static int prepare_credentials(char *str
, credentials
*crd
)
249 p
= strchr(str
, '/');
250 if (!p
) p
= strchr(str
, '\\');
266 static int get_token(connection_context
*c
)
274 if (!prepare_credentials(c
->runas
, &crd
)) {
275 hprintf(c
->pipe
, "error Incorrect runas credentials\n");
278 wres
= LogonUser(crd
.user
, crd
.domain
, crd
.password
, LOGON32_LOGON_INTERACTIVE
, LOGON32_PROVIDER_DEFAULT
, &c
->token
);
280 hprintf(c
->pipe
, "error Cannot LogonUser(%s,%s,%s) %d\n",
281 crd
.user
, crd
.domain
, crd
.password
, GetLastError());
286 } else if (c
->system
) {
287 if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ALL_ACCESS
, &token
)) {
288 hprintf(c
->pipe
, "error Cannot OpenProcessToken %d\n", GetLastError());
292 if (!ImpersonateNamedPipeClient(c
->pipe
->h
)) {
293 hprintf(c
->pipe
, "error Cannot ImpersonateNamedPipeClient %d\n", GetLastError());
296 if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS
, FALSE
, &token
)) {
297 hprintf(c
->pipe
, "error Cannot OpenThreadToken %d\n", GetLastError());
298 goto finishRevertToSelf
;
301 if (!DuplicateTokenEx(token
, MAXIMUM_ALLOWED
, 0, c
->implevel
, TokenPrimary
, &c
->token
)) {
302 hprintf(c
->pipe
, "error Cannot Duplicate Token %d\n", GetLastError());
303 goto finishCloseToken
;
310 if (!RevertToSelf()) {
311 hprintf(c
->pipe
, "error Cannot RevertToSelf %d\n", GetLastError());
319 static int load_user_profile(connection_context
*c
)
321 PROFILEINFO pi
= { .dwSize
= sizeof(PROFILEINFO
) };
323 TCHAR username
[ulen
];
325 GetUserName(username
, &ulen
);
326 pi
.lpUserName
= username
;
328 return LoadUserProfile(c
->token
, &pi
);
331 static int cmd_run(connection_context
*c
)
338 cmdline
= strchr(c
->cmd
, ' ');
347 pipe_nr
= (GetCurrentProcessId() << 16) + (DWORD
) c
->conn_number
;
349 sprintf(buf
, "\\\\.\\pipe\\" PIPE_NAME_IN
, (unsigned int) pipe_nr
);
350 c
->pin
= CreateNamedPipe(buf
,
356 NMPWAIT_USE_DEFAULT_WAIT
,
358 if (c
->pin
== INVALID_HANDLE_VALUE
) {
359 hprintf(c
->pipe
, "error Cannot create in pipe(%s), error 0x%08X\n", buf
, GetLastError());
360 goto finishCloseToken
;
363 sprintf(buf
, "\\\\.\\pipe\\" PIPE_NAME_OUT
, (unsigned int) pipe_nr
);
364 c
->pout
= CreateNamedPipe(buf
,
370 NMPWAIT_USE_DEFAULT_WAIT
,
372 if (c
->pout
== INVALID_HANDLE_VALUE
) {
373 hprintf(c
->pipe
, "error Cannot create out pipe(%s), error 0x%08X\n", buf
, GetLastError());
377 sprintf(buf
, "\\\\.\\pipe\\" PIPE_NAME_ERR
, (unsigned int) pipe_nr
);
378 c
->perr
= CreateNamedPipe(buf
,
384 NMPWAIT_USE_DEFAULT_WAIT
,
386 if (c
->perr
== INVALID_HANDLE_VALUE
) {
387 hprintf(c
->pipe
, "error Cannot create err pipe(%s), error 0x%08x\n", buf
, GetLastError());
388 goto finishClosePout
;
391 /* Send handle to client (it will use it to connect pipes) */
392 hprintf(c
->pipe
, CMD_STD_IO_ERR
" %08X\n", pipe_nr
);
394 HANDLE ph
[] = { c
->pin
, c
->pout
, c
->perr
};
397 for (i
= 0; i
< 3; ++i
) {
398 if (ConnectNamedPipe(ph
[i
], NULL
))
400 int err
= GetLastError();
401 if (err
!= ERROR_PIPE_CONNECTED
) {
402 hprintf(c
->pipe
, "error ConnectNamedPipe(pin) %d\n", err
);
404 DisconnectNamedPipe(ph
[i
]);
405 goto finishClosePerr
;
409 SetHandleInformation(c
->pin
, HANDLE_FLAG_INHERIT
, 1);
410 SetHandleInformation(c
->pout
, HANDLE_FLAG_INHERIT
, 1);
411 SetHandleInformation(c
->perr
, HANDLE_FLAG_INHERIT
, 1);
414 load_user_profile(c
);
416 PROCESS_INFORMATION pi
;
417 ZeroMemory(&pi
, sizeof(PROCESS_INFORMATION
));
420 ZeroMemory(&si
, sizeof(STARTUPINFO
));
421 si
.cb
= sizeof(STARTUPINFO
);
422 si
.hStdInput
= c
->pin
;
423 si
.hStdOutput
= c
->pout
;
424 si
.hStdError
= c
->perr
;
425 si
.dwFlags
|= STARTF_USESTDHANDLES
;
427 if (CreateProcessAsUser(
430 cmdline
, /* command line */
431 NULL
, /* process security attributes */
432 NULL
, /* primary thread security attributes */
433 TRUE
, /* handles are inherited */
434 0, /* creation flags */
435 NULL
, /* use parent's environment */
436 NULL
, /* use parent's current directory */
437 &si
, /* STARTUPINFO pointer */
438 &pi
) /* receives PROCESS_INFORMATION */
440 HANDLE hlist
[2] = {c
->pipe
->o
.hEvent
, pi
.hProcess
};
444 if (!ResetEvent(c
->pipe
->o
.hEvent
))
445 dbg("ResetEvent error - %lu\n", GetLastError());
446 if (!ReadFile(c
->pipe
->h
, str
, 1, NULL
, &c
->pipe
->o
) && GetLastError() != ERROR_IO_PENDING
)
447 dbg("ReadFile(control_pipe) error - %lu\n", GetLastError());
448 ec
= WaitForMultipleObjects(2, hlist
, FALSE
, INFINITE
);
449 dbg("WaitForMultipleObjects=%lu\n", ec
- WAIT_OBJECT_0
);
450 if (ec
!= WAIT_OBJECT_0
)
451 GetExitCodeProcess(pi
.hProcess
, &ec
);
453 TerminateProcess(pi
.hProcess
, ec
= 0x1234);
454 FlushFileBuffers(c
->pout
);
455 FlushFileBuffers(c
->perr
);
456 CloseHandle(pi
.hProcess
);
457 CloseHandle(pi
.hThread
);
458 hprintf(c
->pipe
, CMD_RETURN_CODE
" %08X\n", ec
);
460 hprintf(c
->pipe
, "error Creating process(%s) %d\n", cmdline
, GetLastError());
463 DisconnectNamedPipe(c
->perr
);
464 DisconnectNamedPipe(c
->pout
);
465 DisconnectNamedPipe(c
->pin
);
467 CloseHandle(c
->perr
);
469 CloseHandle(c
->pout
);
473 CloseHandle(c
->token
);
478 static CMD_ITEM cmd_table
[] = {
490 #define MAX_COMMAND_LENGTH (32768)
492 static VOID
handle_connection(connection_data
*data
)
496 connection_context _c
, *c
= &_c
;
497 cmd
= malloc(MAX_COMMAND_LENGTH
);
500 "error: unable to allocate buffer for command\n");
503 ZeroMemory(cmd
, MAX_COMMAND_LENGTH
);
504 ZeroMemory(c
, sizeof(connection_context
));
505 c
->pipe
= data
->pipe
;
507 c
->conn_number
= data
->conn_number
;
509 /* FIXME make wait for end of process or ctrl_pipe input */
511 res
= hgets(cmd
, MAX_COMMAND_LENGTH
, c
->pipe
);
513 dbg("Error reading from pipe(%p)\n", c
->pipe
->h
);
516 dbg("Retrieved line: \"%s\"\n", cmd
);
518 for (ci
= cmd_table
; ci
->name
; ++ci
) {
519 if (strstr(cmd
, ci
->name
) != cmd
)
521 char c
= cmd
[strlen(ci
->name
)];
522 if (!c
|| (c
== ' '))
529 hprintf(c
->pipe
, "error Ignoring unknown command (%s)\n", cmd
);
533 FlushFileBuffers(c
->pipe
->h
);
534 DisconnectNamedPipe(c
->pipe
->h
);
535 CloseHandle(c
->pipe
->h
);
536 CloseHandle(c
->pipe
->o
.hEvent
);
541 static int conn_number
= 0;
543 DWORD WINAPI
winexesvc_loop(LPVOID lpParameter
)
547 dbg("server_loop: alive\n");
548 if (!CreatePipesSA()) {
549 dbg("CreatePipesSA failed (%08lX)\n", GetLastError());
552 dbg("server_loop: CreatePipesSA done\n");
554 dbg("server_loop: Create Pipe\n");
556 pipe
= (OV_HANDLE
*)malloc(sizeof(OV_HANDLE
));
557 ZeroMemory(&pipe
->o
, sizeof(OVERLAPPED
));
558 pipe
->o
.hEvent
= CreateEvent(NULL
, TRUE
, TRUE
, NULL
);
559 pipe
->h
= CreateNamedPipe("\\\\.\\pipe\\" PIPE_NAME
,
560 PIPE_ACCESS_DUPLEX
| FILE_FLAG_OVERLAPPED
,
562 PIPE_UNLIMITED_INSTANCES
,
565 NMPWAIT_USE_DEFAULT_WAIT
,
567 if (pipe
->h
== INVALID_HANDLE_VALUE
) {
568 dbg("CreatePipe failed(%08lX)\n",
570 CloseHandle(pipe
->o
.hEvent
);
575 dbg("server_loop: Connect Pipe\n");
576 if (ConnectNamedPipe(pipe
->h
, &pipe
->o
)) {
577 dbg("server_loop: Connect Pipe err %08lX\n", GetLastError());
580 switch (GetLastError()) {
581 case ERROR_IO_PENDING
:
582 dbg("server_loop: Connect Pipe(0) pending\n");
584 res
= GetOverlappedResult(pipe
->h
, &pipe
->o
, &t
, TRUE
);
586 case ERROR_PIPE_CONNECTED
:
587 dbg("server_loop: Connect Pipe(0) connected\n");
591 dbg("server_loop: Connect Pipe(0) err %08lX\n", GetLastError());
597 connection_data
*cd
= malloc(sizeof(connection_data
));
599 cd
->conn_number
= ++conn_number
;
600 dbg("server_loop: CreateThread\n");
601 HANDLE th
= CreateThread(NULL
, /* no security attribute */
602 0, /* default stack size */
603 (LPTHREAD_START_ROUTINE
)
605 (LPVOID
) cd
, /* thread parameter */
606 0, /* not suspended */
607 NULL
); /* returns thread ID */
609 dbg("Cannot create thread\n");
610 CloseHandle(pipe
->h
);
611 CloseHandle(pipe
->o
.hEvent
);
615 dbg("server_loop: Thread created\n");
618 dbg("server_loop: Pipe not connected\n");
619 CloseHandle(pipe
->h
);
620 CloseHandle(pipe
->o
.hEvent
);
624 dbg("server_loop: STH wrong\n");
628 static SERVICE_STATUS winexesvcStatus
;
629 static SERVICE_STATUS_HANDLE winexesvcStatusHandle
;
631 static VOID WINAPI
winexesvcCtrlHandler(DWORD Opcode
)
634 case SERVICE_CONTROL_PAUSE
:
635 dbg(SERVICE_NAME
": winexesvcCtrlHandler: pause\n", 0);
636 winexesvcStatus
.dwCurrentState
= SERVICE_PAUSED
;
639 case SERVICE_CONTROL_CONTINUE
:
640 dbg(SERVICE_NAME
": winexesvcCtrlHandler: continue\n", 0);
641 winexesvcStatus
.dwCurrentState
= SERVICE_RUNNING
;
644 case SERVICE_CONTROL_STOP
:
645 dbg(SERVICE_NAME
": winexesvcCtrlHandler: stop\n", 0);
646 winexesvcStatus
.dwWin32ExitCode
= 0;
647 winexesvcStatus
.dwCurrentState
= SERVICE_STOPPED
;
648 winexesvcStatus
.dwCheckPoint
= 0;
649 winexesvcStatus
.dwWaitHint
= 0;
651 if (!SetServiceStatus (winexesvcStatusHandle
, &winexesvcStatus
))
652 dbg(SERVICE_NAME
": SetServiceStatus error %ld\n", GetLastError());
654 dbg(SERVICE_NAME
": Leaving winexesvc\n", 0);
657 case SERVICE_CONTROL_INTERROGATE
:
658 dbg(SERVICE_NAME
": winexesvcCtrlHandler: interrogate\n", 0);
662 dbg(SERVICE_NAME
": Unrecognized opcode %ld\n", Opcode
);
665 if (!SetServiceStatus(winexesvcStatusHandle
, &winexesvcStatus
))
666 dbg(SERVICE_NAME
": SetServiceStatus error 0x%08X\n", GetLastError());
671 static DWORD
winexesvcInitialization(DWORD argc
, LPTSTR
* argv
, DWORD
* specificError
)
673 HANDLE th
= CreateThread(NULL
, 0, winexesvc_loop
, NULL
, 0, NULL
);
681 static void WINAPI
winexesvcStart(DWORD argc
, LPTSTR
* argv
)
686 winexesvcStatus
.dwServiceType
= SERVICE_WIN32
;
687 winexesvcStatus
.dwCurrentState
= SERVICE_START_PENDING
;
688 winexesvcStatus
.dwControlsAccepted
= SERVICE_ACCEPT_STOP
| SERVICE_ACCEPT_PAUSE_CONTINUE
;
689 winexesvcStatus
.dwWin32ExitCode
= 0;
690 winexesvcStatus
.dwServiceSpecificExitCode
= 0;
691 winexesvcStatus
.dwCheckPoint
= 0;
692 winexesvcStatus
.dwWaitHint
= 0;
694 dbg(SERVICE_NAME
": RegisterServiceCtrlHandler\n", 0);
696 winexesvcStatusHandle
= RegisterServiceCtrlHandler(SERVICE_NAME
, winexesvcCtrlHandler
);
698 if (winexesvcStatusHandle
== (SERVICE_STATUS_HANDLE
) 0) {
700 ": RegisterServiceCtrlHandler failed %d\n",
704 status
= winexesvcInitialization(argc
, argv
, &specificError
);
706 if (status
!= NO_ERROR
) {
707 winexesvcStatus
.dwCurrentState
= SERVICE_STOPPED
;
708 winexesvcStatus
.dwCheckPoint
= 0;
709 winexesvcStatus
.dwWaitHint
= 0;
710 winexesvcStatus
.dwWin32ExitCode
= status
;
711 winexesvcStatus
.dwServiceSpecificExitCode
= specificError
;
713 SetServiceStatus(winexesvcStatusHandle
, &winexesvcStatus
);
717 winexesvcStatus
.dwCurrentState
= SERVICE_RUNNING
;
718 winexesvcStatus
.dwCheckPoint
= 0;
719 winexesvcStatus
.dwWaitHint
= 0;
721 if (!SetServiceStatus(winexesvcStatusHandle
, &winexesvcStatus
)) {
722 status
= GetLastError();
723 dbg(SERVICE_NAME
": SetServiceStatus error %ld\n", status
);
726 dbg(SERVICE_NAME
": Returning the Main Thread \n", 0);
731 int main(int argc
, char *argv
[])
733 SERVICE_TABLE_ENTRY DispatchTable
[] = {
734 {SERVICE_NAME
, winexesvcStart
},
738 dbg(SERVICE_NAME
": StartServiceCtrlDispatcher %d\n", GetLastError());
739 if (!StartServiceCtrlDispatcher(DispatchTable
)) {
741 ": StartServiceCtrlDispatcher (%d)\n",