@@ -151,6 +151,10 @@ static void WINAPI pgwin32_ServiceHandler(DWORD);
151
151
static void WINAPI pgwin32_ServiceMain (DWORD , LPTSTR * );
152
152
static void pgwin32_doRunAsService (void );
153
153
static int CreateRestrictedProcess (char * cmd , PROCESS_INFORMATION * processInfo , bool as_service );
154
+ static bool pgwin32_get_dynamic_tokeninfo (HANDLE token ,
155
+ TOKEN_INFORMATION_CLASS class ,
156
+ char * * InfoBuffer , char * errbuf , int errsize );
157
+ static int pgwin32_is_service (void );
154
158
#endif
155
159
156
160
static pgpid_t get_pgpid (void );
@@ -216,7 +220,7 @@ write_stderr(const char *fmt,...)
216
220
* On Win32, we print to stderr if running on a console, or write to
217
221
* eventlog if running as a service
218
222
*/
219
- if (!isatty ( fileno ( stderr ) )) /* Running as a service */
223
+ if (!pgwin32_is_service ( )) /* Running as a service */
220
224
{
221
225
char errbuf [2048 ]; /* Arbitrary size? */
222
226
@@ -1604,6 +1608,160 @@ pgwin32_doRunAsService(void)
1604
1608
}
1605
1609
}
1606
1610
1611
+ /*
1612
+ * Call GetTokenInformation() on a token and return a dynamically sized
1613
+ * buffer with the information in it. This buffer must be free():d by
1614
+ * the calling function!
1615
+ */
1616
+ static bool
1617
+ pgwin32_get_dynamic_tokeninfo (HANDLE token , TOKEN_INFORMATION_CLASS class ,
1618
+ char * * InfoBuffer , char * errbuf , int errsize )
1619
+ {
1620
+ DWORD InfoBufferSize ;
1621
+
1622
+ if (GetTokenInformation (token , class , NULL , 0 , & InfoBufferSize ))
1623
+ {
1624
+ snprintf (errbuf , errsize , "could not get token information: got zero size\n" );
1625
+ return false;
1626
+ }
1627
+
1628
+ if (GetLastError () != ERROR_INSUFFICIENT_BUFFER )
1629
+ {
1630
+ snprintf (errbuf , errsize , "could not get token information: error code %lu\n" ,
1631
+ GetLastError ());
1632
+ return false;
1633
+ }
1634
+
1635
+ * InfoBuffer = malloc (InfoBufferSize );
1636
+ if (* InfoBuffer == NULL )
1637
+ {
1638
+ snprintf (errbuf , errsize , "could not allocate %d bytes for token information\n" ,
1639
+ (int ) InfoBufferSize );
1640
+ return false;
1641
+ }
1642
+
1643
+ if (!GetTokenInformation (token , class , * InfoBuffer ,
1644
+ InfoBufferSize , & InfoBufferSize ))
1645
+ {
1646
+ snprintf (errbuf , errsize , "could not get token information: error code %lu\n" ,
1647
+ GetLastError ());
1648
+ return false;
1649
+ }
1650
+
1651
+ return true;
1652
+ }
1653
+
1654
+ /*
1655
+ * We consider ourselves running as a service if one of the following is
1656
+ * true:
1657
+ *
1658
+ * 1) We are running as Local System (only used by services)
1659
+ * 2) Our token contains SECURITY_SERVICE_RID (automatically added to the
1660
+ * process token by the SCM when starting a service)
1661
+ *
1662
+ * Return values:
1663
+ * 0 = Not service
1664
+ * 1 = Service
1665
+ * -1 = Error
1666
+ *
1667
+ * Note: we can't report errors via write_stderr (because that calls this)
1668
+ * We are therefore reduced to writing directly on stderr, which sucks, but
1669
+ * we have few alternatives.
1670
+ */
1671
+ int
1672
+ pgwin32_is_service (void )
1673
+ {
1674
+ static int _is_service = -1 ;
1675
+ HANDLE AccessToken ;
1676
+ char * InfoBuffer = NULL ;
1677
+ char errbuf [256 ];
1678
+ PTOKEN_GROUPS Groups ;
1679
+ PTOKEN_USER User ;
1680
+ PSID ServiceSid ;
1681
+ PSID LocalSystemSid ;
1682
+ SID_IDENTIFIER_AUTHORITY NtAuthority = {SECURITY_NT_AUTHORITY };
1683
+ UINT x ;
1684
+
1685
+ /* Only check the first time */
1686
+ if (_is_service != -1 )
1687
+ return _is_service ;
1688
+
1689
+ if (!OpenProcessToken (GetCurrentProcess (), TOKEN_READ , & AccessToken ))
1690
+ {
1691
+ fprintf (stderr , "could not open process token: error code %lu\n" ,
1692
+ GetLastError ());
1693
+ return -1 ;
1694
+ }
1695
+
1696
+ /* First check for local system */
1697
+ if (!pgwin32_get_dynamic_tokeninfo (AccessToken , TokenUser , & InfoBuffer ,
1698
+ errbuf , sizeof (errbuf )))
1699
+ {
1700
+ fprintf (stderr , "%s" , errbuf );
1701
+ return -1 ;
1702
+ }
1703
+
1704
+ User = (PTOKEN_USER ) InfoBuffer ;
1705
+
1706
+ if (!AllocateAndInitializeSid (& NtAuthority , 1 ,
1707
+ SECURITY_LOCAL_SYSTEM_RID , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
1708
+ & LocalSystemSid ))
1709
+ {
1710
+ fprintf (stderr , "could not get SID for local system account\n" );
1711
+ CloseHandle (AccessToken );
1712
+ return -1 ;
1713
+ }
1714
+
1715
+ if (EqualSid (LocalSystemSid , User -> User .Sid ))
1716
+ {
1717
+ FreeSid (LocalSystemSid );
1718
+ free (InfoBuffer );
1719
+ CloseHandle (AccessToken );
1720
+ _is_service = 1 ;
1721
+ return _is_service ;
1722
+ }
1723
+
1724
+ FreeSid (LocalSystemSid );
1725
+ free (InfoBuffer );
1726
+
1727
+ /* Now check for group SID */
1728
+ if (!pgwin32_get_dynamic_tokeninfo (AccessToken , TokenGroups , & InfoBuffer ,
1729
+ errbuf , sizeof (errbuf )))
1730
+ {
1731
+ fprintf (stderr , "%s" , errbuf );
1732
+ return -1 ;
1733
+ }
1734
+
1735
+ Groups = (PTOKEN_GROUPS ) InfoBuffer ;
1736
+
1737
+ if (!AllocateAndInitializeSid (& NtAuthority , 1 ,
1738
+ SECURITY_SERVICE_RID , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,
1739
+ & ServiceSid ))
1740
+ {
1741
+ fprintf (stderr , "could not get SID for service group\n" );
1742
+ free (InfoBuffer );
1743
+ CloseHandle (AccessToken );
1744
+ return -1 ;
1745
+ }
1746
+
1747
+ _is_service = 0 ;
1748
+ for (x = 0 ; x < Groups -> GroupCount ; x ++ )
1749
+ {
1750
+ if (EqualSid (ServiceSid , Groups -> Groups [x ].Sid ))
1751
+ {
1752
+ _is_service = 1 ;
1753
+ break ;
1754
+ }
1755
+ }
1756
+
1757
+ free (InfoBuffer );
1758
+ FreeSid (ServiceSid );
1759
+
1760
+ CloseHandle (AccessToken );
1761
+
1762
+ return _is_service ;
1763
+ }
1764
+
1607
1765
1608
1766
/*
1609
1767
* Mingw headers are incomplete, and so are the libraries. So we have to load
0 commit comments