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+
595716static 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