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 <jeffhost@microsoft.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff Hostetler committed Sep 20, 2021 at 15:36 UTC 8750249053c6441e912a41a2feca8f22f73babc4
1 file changed +129 -11
compat/simple-ipc/ipc-win32.c
+129 -11
@@ -3,6 +3,8 @@
3 #include "strbuf.h"
4 #include "pkt-line.h"
5 #include "thread-utils.h"
6 +#include "accctrl.h"
7 +#include "aclapi.h"
8
9 #ifndef SUPPORTS_SIMPLE_IPC
10 /*
@@ -592,11 +594,132 @@ finished:
594 return NULL;
595 }
596
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 +
716 static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
717 {
718 HANDLE hPipe;
719 DWORD dwOpenMode, dwPipeMode;
599 - LPSECURITY_ATTRIBUTES lpsa = NULL;
720 + struct my_sa_data my_sa_data;
721 +
722 + init_sa(&my_sa_data);
723
724 dwOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND |
725 FILE_FLAG_OVERLAPPED;
@@ -612,20 +735,15 @@ static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
735 * set the ACL / Security Attributes on the named
736 * pipe; subsequent instances inherit and cannot
737 * 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.
738 */
739 + get_sa(&my_sa_data);
740 }
741
742 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);
747
748 return hPipe;
749 }