@@ -152,6 +152,10 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
152152static void WINAPI pgwin32_ServiceMain (DWORD , LPTSTR * );
153153static void pgwin32_doRunAsService (void );
154154static 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 );
155159#endif
156160
157161static pgpid_t get_pgpid (bool is_status_request );
@@ -218,7 +222,7 @@ write_stderr(const char *fmt,...)
218222 * On Win32, we print to stderr if running on a console, or write to
219223 * eventlog if running as a service
220224 */
221- if (!isatty ( fileno ( stderr ) )) /* Running as a service */
225+ if (!pgwin32_is_service ( )) /* Running as a service */
222226 {
223227 char errbuf [2048 ]; /* Arbitrary size? */
224228
@@ -1681,6 +1685,160 @@ pgwin32_doRunAsService(void)
16811685 }
16821686}
16831687
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+
16841842
16851843/*
16861844 * Mingw headers are incomplete, and so are the libraries. So we have to load
0 commit comments