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