Switching groups with newgrp - how to get the new group with |GetTokenInformation()| ?

Corinna Vinschen corinna-cygwin@cygwin.com
Sat Feb 24 12:53:46 GMT 2024

On Feb 23 19:45, Roland Mainz via Cygwin wrote:
> On Fri, Feb 23, 2024 at 4:47 PM Corinna Vinschen via Cygwin
> <cygwin@cygwin.com> wrote:
> > On Feb 23 14:03, Roland Mainz via Cygwin wrote:
> > > Do you have any idea what is going wrong in this case ?
> >
> > Not sure about that.  I'm not familiar with driver development under
> > Windows.
> Me neither, I'm still new to this whole Windows kernel stuff (coming
> from SUN&Solaris engineering), but as we need a NFSv4 filesystem
> client at work I'm basically forced at knifepoint to learn as fast as
> I can... ;-/
> > I'd expect that you get the token of the calling thread or, in
> > this case, process as is.
> I think it's the calling thread which makes the Win32 syscall, then
> the MiniRedirector driver (nfs41_driver.sys) gets that security
> context, and uses that to set the impersonation stuff when making the
> upcall to the userland part (nfsd_debug.exe), so that daemon thread
> can impersonate the caller.
> > However, did you try this with a primary group SID being part of the
> > token's supplementary group list, or did you try this with some
> > arbitrary group SID?
> I tried it like this:
> 1. On the Windows machine I created these two new groups:
> ---- snip ----
> WINHOST1:~$ net localgroup cygwingrp1 /add
> WINHOST1:~$ net localgroup cygwingrp2 /add
> WINHOST1:~$ getent group cygwingrp1
> cygwingrp1:S-1-5-21-3286904461-661230000-4220857270-1003:197611:
> WINHOST1:~$ getent group cygwingrp2
> cygwingrp2:S-1-5-21-3286904461-661230000-4220857270-1004:197612:
> ---- snip ----
> On the Linux NFSv4 server side I added these groups too, and added
> group membership for the matching user:
> ---- snip ----
> root@DERFWNB4966:~# groupadd -g 197611 cygwingrp1
> root@DERFWNB4966:~# groupadd -g 197612 cygwingrp2
> root@DERFWNB4966:~# usermod -a -G cygwingrp1 roland_mainz
> root@DERFWNB4966:~# usermod -a -G cygwingrp2 roland_mainz
> ---- snip ----
> After that /usr/bin/chgrp on Cygwin works on the NFSv4.1 filesystem,
> but if I do a /usr/bin/newgrp+/usr/bin/touch it will not create files
> with that new group, because nfsd_debug.exe only sees the default
> primary group, not the new primary group set by /usr/bin/newgrp.
> Or is there a mistake - do I have to add the current user to the
> Windows localgroup first somehow (like usermod on Linux) ?

Yes, there's a mistake, but in some way it's not your fault.

You have to make sure that the user calling newgrp is member in the
group *and* it has to be in the current user token's TOKEN_GROUP list.

The fact that it (looks like it) works on Cygwin does not mean it
actually worked on the OS level.  See below.

> > I toyed around a bit with this in user space, and it seems I
> > misinterpreted the results when I added the newgrp(1) tool.  The primary
> > group in the token *must* be member of the token's supplementary group
> > list.
> Like on UNIX, right ?

No.  On UNIX it depends if you're a privileged process with the
CAP_SETGID capability.  If so, you can set the pgrp to any arbitrary
group without being asked for credentials.  If you're non-priv'ed,
you can change to any group from the supplementary group list without
being asked for credentials, *or* you're asked for credentials (group

> > The fact that it looks like it works in Cygwin to set the pgrp to
> > an arbitrary SID is apparently based on incorrect error handling.
> >
> > I will fix this in the next couple of days.
> Thanks :-)

In fact I can't fix it and here's why:

Windows only allows to set the new primary group to a group which is
already in the TOKEN_GROUP list of the manipulated user token.  Even
a privileged account can't do that.  Only creating a new token from
scratch will do this.

However, there's a problem.  Consider the OpenSSH server sshd.  It
switches the user account basically like this:

  if (setgid(newgid) != 0)
  if (setuid(newuid) !=0)

Only after the setuid(2) call, Cygwin has full information to create the
new token for the process of the user just logging in.  So during the
setgid(2) call, Cygwin can only operate on the token of the service
account running sshd.

Now, if setgid(2) would fail if the group is not in the user token
of the current effective account, sshd would practically always fail.

So what Cygwin does is to fake success in setgid(2) even if switching
the pgrp in the token failed.  It just stores the information in its
internal user account representation.  Then, when setuid(2) gets called,
it will try to rectify this in the new user's token.

That's why setgid(2) always succeeds if the group exists.  We just don't
have a better way to implement the user account switch.

I just pushed a patch which lets newgrp(1) fail if the group is not
in the supplementary group list, but that's all I can do without
breaking OpenSSH.

So, again, what you have to do is to make sure that the account is
already in the group you're switching to.

For instance:

  $ id
  uid=1049577(corinna) gid=1049089(Domain Users) groups=1049089(Domain Users),545(Users),14(REMOTE INTERACTIVE LOGON),4(INTERACTIVE),11(Authenticated Users),15(This Organization),4095(CurrentSession),66048(LOCAL),70145(Authentication authority asserted identity),1049148(Denied RODC Password Replication Group),401408(Medium Mandatory Level)

So that works:

  $ newgrp 'INTERACTIVE' id -gn

But this will not work anymore with the new newgrp(1) from Cygwin 3.5.1:

  $ newgrp 'PROXY' id -gn
  newgrp: can't switch primary group to 'PROXY'

while it still (looks like it) works with newgrp(1) from Cygwin 3.5.0,
because setgid(2) is faking success.


More information about the Cygwin mailing list