@@ -152,6 +152,10 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
152
152
static void WINAPI pgwin32_ServiceMain (DWORD , LPTSTR * );
153
153
static void pgwin32_doRunAsService (void );
154
154
static int CreateRestrictedProcess (char * cmd , PROCESS_INFORMATION * processInfo , bool as_service );
155
+ static bool pgwin32_get_dynamic_tokeninfo (HANDLE token ,
156
+ TOKEN_INFORMATION_CLASS class ,
157
+ char * * InfoBuffer , char * errbuf , int errsize );
158
+ static int pgwin32_is_service (void );
155
159
#endif
156
160
157
161
static pgpid_t get_pgpid (bool is_status_request );
@@ -218,7 +222,7 @@ write_stderr(const char *fmt,...)
218
222
* On Win32, we print to stderr if running on a console, or write to
219
223
* eventlog if running as a service
220
224
*/
221
- if (!isatty ( fileno ( stderr ) )) /* Running as a service */
225
+ if (!pgwin32_is_service ( )) /* Running as a service */
222
226
{
223
227
char errbuf [2048 ]; /* Arbitrary size? */
224
228
@@ -1681,6 +1685,160 @@ pgwin32_doRunAsService(void)
1681
1685
}
1682
1686
}
1683
1687
1688
+ /*
1689
+ * Call GetTokenInformation() on a token and return a dynamically sized
1690
+ * buffer with the information in it. This buffer must be free():d by
1691
+ * the calling function!
1692
+ */
1693
+ static bool
1694
+ pgwin32_get_dynamic_tokeninfo (HANDLE token , TOKEN_INFORMATION_CLASS class ,
1695
+ char * * InfoBuffer , char * errbuf , int errsize )
1696
+ {
1697
+ DWORD InfoBufferSize ;
1698
+
1699
+ if (GetTokenInformation (token , class , NULL , 0 , & InfoBufferSize ))
1700
+ {
1701
+ snprintf (errbuf , errsize , "could not get token information: got zero size\n" );
1702
+ return false;
1703
+ }
1704
+
1705
+ if (GetLastError () != ERROR_INSUFFICIENT_BUFFER )
1706
+ {
1707
+ snprintf (errbuf , errsize , "could not get token information: error code %lu\n" ,
1708
+ GetLastError ());
1709
+ return false;
1710
+ }
1711
+
1712
+ * InfoBuffer = malloc (InfoBufferSize );
1713
+ if (* InfoBuffer == NULL )
1714
+ {
1715
+ snprintf (errbuf , errsize , "could not allocate %d bytes for token information\n" ,
1716
+ (int ) InfoBufferSize );
1717
+ return false;
1718
+ }
1719
+
1720
+ if (!GetTokenInformation (token , class , * InfoBuffer ,
1721
+ InfoBufferSize , & InfoBufferSize ))
1722
+ {
1723
+ snprintf (errbuf , errsize , "could not get token information: error code %lu\n" ,
1724
+ GetLastError ());
1725
+ return false;
1726
+ }
1727
+
1728
+ return true;
1729
+ }
1730
+
1731
+ /*
1732
+ * We consider ourselves running as a service if one of the following is
1733
+ * true:
1734
+ *
1735
+ * 1) We are running as Local System (only used by services)
1736
+ * 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
1737
+ * process token by the SCM when starting a service)
1738
+ *
1739
+ * Return values:
1740
+ * 0 = Not service
1741
+ * 1 = Service
1742
+ * -1 = Error
1743
+ *
1744
+ * Note: we can't report errors via write_stderr (because that calls this)
1745
+ * We are therefore reduced to writing directly on stderr, which sucks, but
1746
+ * we have few alternatives.
1747
+ */
1748
+ int
1749
+ pgwin32_is_service (void )
1750
+ {
1751
+ static int _is_service = -1 ;
1752
+ HANDLE AccessToken ;
1753
+ char * InfoBuffer = NULL ;
1754
+ char errbuf [256 ];
1755
+ PTOKEN_GROUPS Groups ;
1756
+ PTOKEN_USER User ;
1757
+ PSID ServiceSid ;
1758
+ PSID LocalSystemSid ;
1759
+ SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY };
1760
+ UINT x ;
1761
+
1762
+ /* Only check the first time */
1763
+ if (_is_service != -1 )
1764
+ return _is_service ;
1765
+
1766
+ if (!OpenProcessToken (GetCurrentProcess (), TOKEN_READ , & AccessToken ))
1767
+ {
1768
+ fprintf (stderr , "could not open process token: error code %lu\n" ,
1769
+ GetLastError ());
1770
+ return -1 ;
1771
+ }
1772
+
1773
+ /* First check for local system */
1774
+ if (!pgwin32_get_dynamic_tokeninfo (AccessToken , TokenUser , & InfoBuffer ,
1775
+ errbuf , sizeof (errbuf )))
1776
+ {
1777
+ fprintf (stderr , "%s" , errbuf );
1778
+ return -1 ;
1779
+ }
1780
+
1781
+ User = (PTOKEN_USER ) InfoBuffer ;
1782
+
1783
+ if (!AllocateAndInitializeSid (& NtAuthority , 1 ,
1784
+ SECURITY_LOCAL_SYSTEM_RID , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
1785
+ & LocalSystemSid ))
1786
+ {
1787
+ fprintf (stderr , "could not get SID for local system account\n" );
1788
+ CloseHandle (AccessToken );
1789
+ return -1 ;
1790
+ }
1791
+
1792
+ if (EqualSid (LocalSystemSid , User -> User .Sid ))
1793
+ {
1794
+ FreeSid (LocalSystemSid );
1795
+ free (InfoBuffer );
1796
+ CloseHandle (AccessToken );
1797
+ _is_service = 1 ;
1798
+ return _is_service ;
1799
+ }
1800
+
1801
+ FreeSid (LocalSystemSid );
1802
+ free (InfoBuffer );
1803
+
1804
+ /* Now check for group SID */
1805
+ if (!pgwin32_get_dynamic_tokeninfo (AccessToken , TokenGroups , & InfoBuffer ,
1806
+ errbuf , sizeof (errbuf )))
1807
+ {
1808
+ fprintf (stderr , "%s" , errbuf );
1809
+ return -1 ;
1810
+ }
1811
+
1812
+ Groups = (PTOKEN_GROUPS ) InfoBuffer ;
1813
+
1814
+ if (!AllocateAndInitializeSid (& NtAuthority , 1 ,
1815
+ SECURITY_SERVICE_RID , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
1816
+ & ServiceSid ))
1817
+ {
1818
+ fprintf (stderr , "could not get SID for service group\n" );
1819
+ free (InfoBuffer );
1820
+ CloseHandle (AccessToken );
1821
+ return -1 ;
1822
+ }
1823
+
1824
+ _is_service = 0 ;
1825
+ for (x = 0 ; x < Groups -> GroupCount ; x ++ )
1826
+ {
1827
+ if (EqualSid (ServiceSid , Groups -> Groups [x ].Sid ))
1828
+ {
1829
+ _is_service = 1 ;
1830
+ break ;
1831
+ }
1832
+ }
1833
+
1834
+ free (InfoBuffer );
1835
+ FreeSid (ServiceSid );
1836
+
1837
+ CloseHandle (AccessToken );
1838
+
1839
+ return _is_service ;
1840
+ }
1841
+
1684
1842
1685
1843
/*
1686
1844
* Mingw headers are incomplete, and so are the libraries. So we have to load
0 commit comments