Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Tailscale SSH doesn't drop privileges correctly on FreeBSD when running a non-shell command #7616

Description

@andrew-d

What is the issue?

On FreeBSD, we don't use the login command when running a command without a shell/TTY:

if !ia.isShell || !ia.hasTTY {
// We can only use login command if a shell was requested with a TTY. If
// there is no TTY, login exits immediately, which breaks things likes
// mosh and VSCode.
return nil
}

Additionally, FreeBSD (and Darwin) exhibit non-POSIX-compliant behaviour in their getgroups() and setgroups() system calls: the first entry in the list is returned and used to set the process's egid, as if it were returned by getegid and changed by setegid.

As a result, our existing behaviour for dropping privileges has a bug:

if err := setGroups(groupIDs); err != nil {
return err
}
if egid := os.Getegid(); egid != ia.gid {
if err := syscall.Setgid(int(ia.gid)); err != nil {
logf(err.Error())
os.Exit(1)
}
}
if euid != ia.uid {
// Switch users if required before starting the desired process.
if err := syscall.Setuid(int(ia.uid)); err != nil {
logf(err.Error())
os.Exit(1)
}
}

For example, if we call our incubator with --uid=1002 --gid=1002 --groups=1002, it will:

  • Set the new supplemental groups to []int{1002} with setGroups, which changes the egid to 1002
  • Read the current now-changed egid, which is 1002, and compare against the expected egid of 1002; nothing changes, so it does not call setgid.
  • Check the current UID and call setuid(1002).

As a result, the egid has changed to 1002, but the gid remains 0 (assuming Tailscale is running as root).

This doesn't affect other OSes because:

  • On Darwin, we always use the login command, which resets groups for us
  • On non-FreeBSD platforms, the setgroups command doesn't change the egid, which means that the comparison against egid succeeds and we call setgid to reset the GID.

See also:

Steps to reproduce

This could be observed by running "id -p" in two contexts. The expected output, and the output returned when running from a Tailscale SSH shell, is:

andrew@freebsd:~ $ id -p
uid         andrew
groups      andrew

However, when run via "ssh andrew@freebsd id -p", the output would be:

$ ssh andrew@freebsd id -p
login       root
uid         andrew
rgid        wheel
groups      andrew

Are there any recent changes that introduced the issue?

n/a

OS

Other

OS version

FreeBSD

Tailscale version

1.38.1

Other software

No response

Bug report

No response

Metadata

Metadata

Assignees

Labels

L1 Very fewLikelihoodOS-freebsdP5 Halts deploymentPriority levelT6 Major usabilityIssue typebugBugsecurityIssues involving security in the Tailscale product or infrastructuresshRelating to Tailscale SSH https://tailscale.com/blog/tailscale-ssh/

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions