Skip to content

Commit af3fb71

Browse files
jeffhostetlerdscho
authored andcommitted
simple-ipc/ipc-win32: add Windows ACL to named pipe
Set an ACL on the named pipe to allow the well-known group EVERYONE to read and write to the IPC server's named pipe. In the event that the daemon was started with elevation, allow non-elevated clients to communicate with the daemon. Signed-off-by: Jeff Hostetler <[email protected]>
1 parent 8535c36 commit af3fb71

File tree

1 file changed

+129
-11
lines changed

1 file changed

+129
-11
lines changed

compat/simple-ipc/ipc-win32.c

Lines changed: 129 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
#include "strbuf.h"
44
#include "pkt-line.h"
55
#include "thread-utils.h"
6+
#include "accctrl.h"
7+
#include "aclapi.h"
68

79
#ifndef SUPPORTS_SIMPLE_IPC
810
/*
@@ -592,11 +594,132 @@ static void *server_thread_proc(void *_server_thread_data)
592594
return NULL;
593595
}
594596

597+
/*
598+
* We need to build a Windows "SECURITY_ATTRIBUTES" object and use it
599+
* to apply an ACL when we create the initial instance of the Named
600+
* Pipe. The construction is somewhat involved and consists of
601+
* several sequential steps and intermediate objects.
602+
*
603+
* We use this structure to hold these intermediate pointers so that
604+
* we can free them as a group. (It is unclear from the docs whether
605+
* some of these intermediate pointers can be freed before we are
606+
* finished using the "lpSA" member.)
607+
*/
608+
struct my_sa_data
609+
{
610+
PSID pEveryoneSID;
611+
PACL pACL;
612+
PSECURITY_DESCRIPTOR pSD;
613+
LPSECURITY_ATTRIBUTES lpSA;
614+
};
615+
616+
static void init_sa(struct my_sa_data *d)
617+
{
618+
memset(d, 0, sizeof(*d));
619+
}
620+
621+
static void release_sa(struct my_sa_data *d)
622+
{
623+
if (d->pEveryoneSID)
624+
FreeSid(d->pEveryoneSID);
625+
if (d->pACL)
626+
LocalFree(d->pACL);
627+
if (d->pSD)
628+
LocalFree(d->pSD);
629+
if (d->lpSA)
630+
LocalFree(d->lpSA);
631+
632+
memset(d, 0, sizeof(*d));
633+
}
634+
635+
/*
636+
* Create SECURITY_ATTRIBUTES to apply to the initial named pipe. The
637+
* creator of the first server instance gets to set the ACLs on it.
638+
*
639+
* We allow the well-known group `EVERYONE` to have read+write access
640+
* to the named pipe so that clients can send queries to the daemon
641+
* and receive the response.
642+
*
643+
* Normally, this is not necessary since the daemon is usually
644+
* automatically started by a foreground command like `git status`,
645+
* but in those cases where an elevated Git command started the daemon
646+
* (such that the daemon itself runs with elevation), we need to add
647+
* the ACL so that non-elevated commands can write to it.
648+
*
649+
* The following document was helpful:
650+
* https://docs.microsoft.com/en-us/windows/win32/secauthz/creating-a-security-descriptor-for-a-new-object-in-c--
651+
*
652+
* Returns d->lpSA set to a SA or NULL.
653+
*/
654+
static LPSECURITY_ATTRIBUTES get_sa(struct my_sa_data *d)
655+
{
656+
SID_IDENTIFIER_AUTHORITY sid_auth_world = SECURITY_WORLD_SID_AUTHORITY;
657+
#define NR_EA (1)
658+
EXPLICIT_ACCESS ea[NR_EA];
659+
DWORD dwResult;
660+
661+
if (!AllocateAndInitializeSid(&sid_auth_world, 1,
662+
SECURITY_WORLD_RID, 0,0,0,0,0,0,0,
663+
&d->pEveryoneSID)) {
664+
DWORD gle = GetLastError();
665+
trace2_data_intmax("ipc-debug", NULL, "alloc-world-sid/gle",
666+
(intmax_t)gle);
667+
goto fail;
668+
}
669+
670+
memset(ea, 0, NR_EA * sizeof(EXPLICIT_ACCESS));
671+
672+
ea[0].grfAccessPermissions = GENERIC_READ | GENERIC_WRITE;
673+
ea[0].grfAccessMode = SET_ACCESS;
674+
ea[0].grfInheritance = NO_INHERITANCE;
675+
ea[0].Trustee.MultipleTrusteeOperation = NO_MULTIPLE_TRUSTEE;
676+
ea[0].Trustee.TrusteeForm = TRUSTEE_IS_SID;
677+
ea[0].Trustee.TrusteeType = TRUSTEE_IS_WELL_KNOWN_GROUP;
678+
ea[0].Trustee.ptstrName = (LPTSTR)d->pEveryoneSID;
679+
680+
dwResult = SetEntriesInAcl(NR_EA, ea, NULL, &d->pACL);
681+
if (dwResult != ERROR_SUCCESS) {
682+
DWORD gle = GetLastError();
683+
trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/gle",
684+
(intmax_t)gle);
685+
trace2_data_intmax("ipc-debug", NULL, "set-acl-entry/dw",
686+
(intmax_t)dwResult);
687+
goto fail;
688+
}
689+
690+
d->pSD = (PSECURITY_DESCRIPTOR)LocalAlloc(
691+
LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH);
692+
if (!InitializeSecurityDescriptor(d->pSD, SECURITY_DESCRIPTOR_REVISION)) {
693+
DWORD gle = GetLastError();
694+
trace2_data_intmax("ipc-debug", NULL, "init-sd/gle", (intmax_t)gle);
695+
goto fail;
696+
}
697+
698+
if (!SetSecurityDescriptorDacl(d->pSD, TRUE, d->pACL, FALSE)) {
699+
DWORD gle = GetLastError();
700+
trace2_data_intmax("ipc-debug", NULL, "set-sd-dacl/gle", (intmax_t)gle);
701+
goto fail;
702+
}
703+
704+
d->lpSA = (LPSECURITY_ATTRIBUTES)LocalAlloc(LPTR, sizeof(SECURITY_ATTRIBUTES));
705+
d->lpSA->nLength = sizeof(SECURITY_ATTRIBUTES);
706+
d->lpSA->lpSecurityDescriptor = d->pSD;
707+
d->lpSA->bInheritHandle = FALSE;
708+
709+
return d->lpSA;
710+
711+
fail:
712+
release_sa(d);
713+
return NULL;
714+
}
715+
595716
static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
596717
{
597718
HANDLE hPipe;
598719
DWORD dwOpenMode, dwPipeMode;
599-
LPSECURITY_ATTRIBUTES lpsa = NULL;
720+
struct my_sa_data my_sa_data;
721+
722+
init_sa(&my_sa_data);
600723

601724
dwOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND |
602725
FILE_FLAG_OVERLAPPED;
@@ -612,20 +735,15 @@ static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
612735
* set the ACL / Security Attributes on the named
613736
* pipe; subsequent instances inherit and cannot
614737
* change them.
615-
*
616-
* TODO Should we allow the application layer to
617-
* specify security attributes, such as `LocalService`
618-
* or `LocalSystem`, when we create the named pipe?
619-
* This question is probably not important when the
620-
* daemon is started by a foreground user process and
621-
* only needs to talk to the current user, but may be
622-
* if the daemon is run via the Control Panel as a
623-
* System Service.
624738
*/
739+
get_sa(&my_sa_data);
625740
}
626741

627742
hPipe = CreateNamedPipeW(wpath, dwOpenMode, dwPipeMode,
628-
PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0, lpsa);
743+
PIPE_UNLIMITED_INSTANCES, 1024, 1024, 0,
744+
my_sa_data.lpSA);
745+
746+
release_sa(&my_sa_data);
629747

630748
return hPipe;
631749
}

0 commit comments

Comments
 (0)