Em sex., 13 de ago. de 2021 às 10:03, Andres Freund <[email protected]>
escreveu:
> Hi,
>
> Magnus, Michael, Anyone - I'd appreciate a look.
>
> On 2021-03-05 12:55:37 -0800, Andres Freund wrote:
> > > After fighting with a windows VM for a bit (ugh), it turns out that
> yes,
> > > there is stderr, but that fileno(stderr) returns -2, and
> > > GetStdHandle(STD_ERROR_HANDLE) returns NULL (not INVALID_HANDLE_VALUE).
> > >
> > > The complexity however is that while that's true for pg_ctl within
> > > pgwin32_ServiceMain:
> > > checking what stderr=00007FF8687DFCB0 is (handle: 0, fileno=-2)
> > > but not for postmaster or backends
> > > WARNING: 01000: checking what stderr=00007FF880F5FCB0 is (handle: 92,
> fileno=2)
> > >
> > > which makes sense in a way, because we don't tell CreateProcessAsUser()
> > > that it should pass stdin/out/err down (which then seems to magically
> > > get access to the "topmost" console applications output - damn, this
> > > stuff is weird).
> >
> > That part is not too hard to address - it seems we only need to do that
> > in pg_ctl pgwin32_doRunAsService(). It seems that the
> > stdin/stderr/stdout being set to invalid will then be passed down to
> > postmaster children.
> >
> > https://docs.microsoft.com/en-us/windows/console/getstdhandle
> > "If an application does not have associated standard handles, such as a
> > service running on an interactive desktop, and has not redirected them,
> > the return value is NULL."
> >
> > There does seem to be some difference between what services get as std*
> > - GetStdHandle() returns NULL, and what explicitly passing down invalid
> > handles to postmaster does - GetStdHandle() returns
> > INVALID_HANDLE_VALUE. But passing down NULL rather than
> > INVALID_HANDLE_VALUE to postmaster seems to lead to postmaster
> > re-opening console buffers.
> >
> > Patch attached.
>
> > I'd like to commit something to address this issue to master soon - it
> > allows us to run a lot more tests in cirrus-ci... But probably not
> > backpatch it [yet] - there've not really been field complaints, and who
> > knows if there end up being some unintentional side-effects...
>
> Because it'd allow us to run more tests as part of cfbot and other CI
> efforts, I'd like to push this forward. So I'm planning to commit this
> to master soon-ish, unless somebody wants to take this over? I'm really
> not a windows person...
>
Hi Andres,
I found this function on the web, from OpenSSL, but I haven't tested it.
I think that there is one more way to test if a service is running
(SECURITY_INTERACTIVE_RID).
Can you test on a Windows VM?
If this works I can elaborate a bit.
Attached.
regards,
Ranier Vilela
int
pgwin32_is_service(void)
{
static int _is_service = -1;
HANDLE hProcessToken = NULL;
DWORD groupLength = 64;
PTOKEN_GROUPS groupInfo;
SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY;
PSID InteractiveSid = NULL;
PSID ServiceSid = NULL;
DWORD i;
/* Only check the first time */
if (_is_service != -1)
return _is_service;
_is_service = 0;
groupInfo = (PTOKEN_GROUPS) LocalAlloc(0, groupLength);
if (groupInfo == NULL)
{
fprintf(stderr, "could not get SID for local system account\n");
goto clean;
}
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken))
{
fprintf(stderr, "could not get SID for local system account\n");
goto clean;
}
if (!GetTokenInformation(hProcessToken, TokenGroups, groupInfo,
groupLength, &groupLength))
{
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
fprintf(stderr, "could not get SID for local system
account\n");
goto clean;
}
LocalFree(groupInfo);
groupInfo = NULL;
groupInfo = (PTOKEN_GROUPS) LocalAlloc(0, groupLength);
if (groupInfo == NULL)
{
fprintf(stderr, "could not get SID for local system
account\n");
goto clean;
}
if (!GetTokenInformation(hProcessToken, TokenGroups, groupInfo,
groupLength, &groupLength))
{
fprintf(stderr, "could not get SID for local system
account\n");
goto clean;
}
}
//
// We now know the groups associated with this token. We want to look to
see if
// the interactive group is active in the token, and if so, we know that
// this is an interactive process.
//
// We also look for the "service" SID, and if it's present, we know we're
a service.
//
// The service SID will be present iff the service is running in a
// user account (and was invoked by the service controller).
//
if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_INTERACTIVE_RID, 0, 0,
0, 0, 0, 0, 0, &InteractiveSid))
{
fprintf(stderr, "could not get SID for local system account\n");
goto clean;
}
if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_SERVICE_RID, 0, 0, 0,
0, 0, 0, 0, &ServiceSid))
{
fprintf(stderr, "could not get SID for local system account\n");
goto clean;
}
for (i = 0; i < groupInfo->GroupCount; i += 1)
{
SID_AND_ATTRIBUTES sanda = groupInfo->Groups[i];
PSID Sid = sanda.Sid;
//
// Check to see if the group we're looking at is one of
// the 2 groups we're interested in.
//
if (EqualSid(Sid, InteractiveSid))
{
//
// This process has the Interactive SID in its
// token. This means that the process is running as
// an EXE.
//
goto clean;
}
else if (EqualSid(Sid, ServiceSid))
{
//
// This process has the Service SID in its
// token. This means that the process is running as
// a service running in a user account.
//
_is_service = 1;
goto clean;
}
}
//
// Neither Interactive or Service was present in the current users token,
// This implies that the process is running as a service, most likely
// running as LocalSystem.
//
_is_service = 1;
clean:
if (InteractiveSid)
FreeSid(InteractiveSid);
if (ServiceSid)
FreeSid(ServiceSid);
if (groupInfo)
LocalFree(groupInfo);
if (hProcessToken)
CloseHandle(hProcessToken);
return _is_service;
}