3
3
#include "strbuf.h"
4
4
#include "pkt-line.h"
5
5
#include "thread-utils.h"
6
+ #include "accctrl.h"
7
+ #include "aclapi.h"
6
8
7
9
#ifndef SUPPORTS_SIMPLE_IPC
8
10
/*
@@ -49,6 +51,9 @@ static enum ipc_active_state get_active_state(wchar_t *pipe_path)
49
51
if (GetLastError () == ERROR_FILE_NOT_FOUND )
50
52
return IPC_STATE__PATH_NOT_FOUND ;
51
53
54
+ trace2_data_intmax ("ipc-debug" , NULL , "getstate/waitpipe/gle" ,
55
+ (intmax_t )GetLastError ());
56
+
52
57
return IPC_STATE__OTHER_ERROR ;
53
58
}
54
59
@@ -109,9 +114,15 @@ static enum ipc_active_state connect_to_server(
109
114
t_start_ms = (DWORD )(getnanotime () / 1000000 );
110
115
111
116
if (!WaitNamedPipeW (wpath , timeout_ms )) {
112
- if (GetLastError () == ERROR_SEM_TIMEOUT )
117
+ DWORD gleWait = GetLastError ();
118
+
119
+ if (gleWait == ERROR_SEM_TIMEOUT )
113
120
return IPC_STATE__NOT_LISTENING ;
114
121
122
+ trace2_data_intmax ("ipc-debug" , NULL ,
123
+ "connect/waitpipe/gle" ,
124
+ (intmax_t )gleWait );
125
+
115
126
return IPC_STATE__OTHER_ERROR ;
116
127
}
117
128
@@ -133,17 +144,31 @@ static enum ipc_active_state connect_to_server(
133
144
break ; /* try again */
134
145
135
146
default :
147
+ trace2_data_intmax ("ipc-debug" , NULL ,
148
+ "connect/createfile/gle" ,
149
+ (intmax_t )gle );
150
+
136
151
return IPC_STATE__OTHER_ERROR ;
137
152
}
138
153
}
139
154
140
155
if (!SetNamedPipeHandleState (hPipe , & mode , NULL , NULL )) {
156
+ gle = GetLastError ();
157
+ trace2_data_intmax ("ipc-debug" , NULL ,
158
+ "connect/setpipestate/gle" ,
159
+ (intmax_t )gle );
160
+
141
161
CloseHandle (hPipe );
142
162
return IPC_STATE__OTHER_ERROR ;
143
163
}
144
164
145
165
* pfd = _open_osfhandle ((intptr_t )hPipe , O_RDWR |O_BINARY );
146
166
if (* pfd < 0 ) {
167
+ gle = GetLastError ();
168
+ trace2_data_intmax ("ipc-debug" , NULL ,
169
+ "connect/openosfhandle/gle" ,
170
+ (intmax_t )gle );
171
+
147
172
CloseHandle (hPipe );
148
173
return IPC_STATE__OTHER_ERROR ;
149
174
}
@@ -208,15 +233,16 @@ void ipc_client_close_connection(struct ipc_client_connection *connection)
208
233
209
234
int ipc_client_send_command_to_connection (
210
235
struct ipc_client_connection * connection ,
211
- const char * message , struct strbuf * answer )
236
+ const char * message , size_t message_len ,
237
+ struct strbuf * answer )
212
238
{
213
239
int ret = 0 ;
214
240
215
241
strbuf_setlen (answer , 0 );
216
242
217
243
trace2_region_enter ("ipc-client" , "send-command" , NULL );
218
244
219
- if (write_packetized_from_buf_no_flush (message , strlen ( message ) ,
245
+ if (write_packetized_from_buf_no_flush (message , message_len ,
220
246
connection -> fd ) < 0 ||
221
247
packet_flush_gently (connection -> fd ) < 0 ) {
222
248
ret = error (_ ("could not send IPC command" ));
@@ -239,7 +265,8 @@ int ipc_client_send_command_to_connection(
239
265
240
266
int ipc_client_send_command (const char * path ,
241
267
const struct ipc_client_connect_options * options ,
242
- const char * message , struct strbuf * response )
268
+ const char * message , size_t message_len ,
269
+ struct strbuf * response )
243
270
{
244
271
int ret = -1 ;
245
272
enum ipc_active_state state ;
@@ -250,7 +277,9 @@ int ipc_client_send_command(const char *path,
250
277
if (state != IPC_STATE__LISTENING )
251
278
return ret ;
252
279
253
- ret = ipc_client_send_command_to_connection (connection , message , response );
280
+ ret = ipc_client_send_command_to_connection (connection ,
281
+ message , message_len ,
282
+ response );
254
283
255
284
ipc_client_close_connection (connection );
256
285
@@ -458,7 +487,7 @@ static int do_io(struct ipc_server_thread_data *server_thread_data)
458
487
if (ret >= 0 ) {
459
488
ret = server_thread_data -> server_data -> application_cb (
460
489
server_thread_data -> server_data -> application_data ,
461
- buf .buf , do_io_reply_callback , & reply_data );
490
+ buf .buf , buf . len , do_io_reply_callback , & reply_data );
462
491
463
492
packet_flush_gently (reply_data .fd );
464
493
@@ -565,11 +594,132 @@ static void *server_thread_proc(void *_server_thread_data)
565
594
return NULL ;
566
595
}
567
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
+
568
716
static HANDLE create_new_pipe (wchar_t * wpath , int is_first )
569
717
{
570
718
HANDLE hPipe ;
571
719
DWORD dwOpenMode , dwPipeMode ;
572
- LPSECURITY_ATTRIBUTES lpsa = NULL ;
720
+ struct my_sa_data my_sa_data ;
721
+
722
+ init_sa (& my_sa_data );
573
723
574
724
dwOpenMode = PIPE_ACCESS_INBOUND | PIPE_ACCESS_OUTBOUND |
575
725
FILE_FLAG_OVERLAPPED ;
@@ -585,20 +735,15 @@ static HANDLE create_new_pipe(wchar_t *wpath, int is_first)
585
735
* set the ACL / Security Attributes on the named
586
736
* pipe; subsequent instances inherit and cannot
587
737
* change them.
588
- *
589
- * TODO Should we allow the application layer to
590
- * specify security attributes, such as `LocalService`
591
- * or `LocalSystem`, when we create the named pipe?
592
- * This question is probably not important when the
593
- * daemon is started by a foreground user process and
594
- * only needs to talk to the current user, but may be
595
- * if the daemon is run via the Control Panel as a
596
- * System Service.
597
738
*/
739
+ get_sa (& my_sa_data );
598
740
}
599
741
600
742
hPipe = CreateNamedPipeW (wpath , dwOpenMode , dwPipeMode ,
601
- 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 );
602
747
603
748
return hPipe ;
604
749
}
0 commit comments