Em sex., 13 de ago. de 2021 às 10:03, Andres Freund <and...@anarazel.de>
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;
}

Reply via email to