Skip to content

Set permissions on TTY. #2508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Feb 11, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 87 additions & 9 deletions lib/srv/term.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"io"
"os"
"os/exec"
"os/user"
"strconv"
"sync"
"syscall"

Expand All @@ -36,6 +38,12 @@ import (
log "github.com/sirupsen/logrus"
)

// LookupUser is used to mock the value returned by user.Lookup(string).
type LookupUser func(string) (*user.User, error)

// LookupGroup is used to mock the value returned by user.LookupGroup(string).
type LookupGroup func(string) (*user.Group, error)

// Terminal defines an interface of handy functions for managing a (local or
// remote) PTY, such as resizing windows, executing commands with a PTY, and
// cleaning up.
Expand Down Expand Up @@ -118,19 +126,30 @@ type terminal struct {

// NewLocalTerminal creates and returns a local PTY.
func newLocalTerminal(ctx *ServerContext) (*terminal, error) {
pty, tty, err := pty.Open()
if err != nil {
log.Warnf("Could not start PTY %v", err)
return nil, err
}
return &terminal{
var err error

t := &terminal{
log: log.WithFields(log.Fields{
trace.Component: teleport.ComponentLocalTerm,
}),
ctx: ctx,
pty: pty,
tty: tty,
}, nil
}

// Open PTY and corresponding TTY.
t.pty, t.tty, err = pty.Open()
if err != nil {
log.Warnf("Could not start PTY %v", err)
return nil, err
}

// Set the TTY owner. Failure is not fatal, for example Teleport is running
// on a read-only filesystem, but logging is useful for diagnostic purposes.
err = t.setOwner()
if err != nil {
log.Debugf("Unable to set TTY owner: %v.\n", err)
}

return t, nil
}

// AddParty adds another participant to this terminal. We will keep the
Expand Down Expand Up @@ -294,6 +313,65 @@ func (t *terminal) SetTerminalModes(termModes ssh.TerminalModes) {
return
}

// getOwner determines the uid, gid, and mode of the TTY similar to OpenSSH:
// https://github.com/openssh/openssh-portable/blob/ddc0f38/sshpty.c#L164-L215
func getOwner(login string, lookupUser LookupUser, lookupGroup LookupGroup) (int, int, os.FileMode, error) {
var err error
var uid int
var gid int
var mode os.FileMode

// Lookup the Unix login for the UID and fallback GID.
u, err := lookupUser(login)
if err != nil {
return 0, 0, 0, trace.Wrap(err)
}
uid, err = strconv.Atoi(u.Uid)
if err != nil {
return 0, 0, 0, trace.Wrap(err)
}

// If the tty group exists, use that as the gid of the TTY and set mode to
// be u+rw. Otherwise use the group of the user with mode u+rw g+w.
group, err := lookupGroup("tty")
if err != nil {
gid, err = strconv.Atoi(u.Gid)
if err != nil {
return 0, 0, 0, trace.Wrap(err)
}
mode = 0620
} else {
gid, err = strconv.Atoi(group.Gid)
if err != nil {
return 0, 0, 0, trace.Wrap(err)
}
mode = 0600
}

return uid, gid, mode, nil
}

// setOwner changes the owner and mode of the TTY.
func (t *terminal) setOwner() error {
uid, gid, mode, err := getOwner(t.ctx.Identity.Login, user.Lookup, user.LookupGroup)
if err != nil {
return trace.Wrap(err)
}

err = os.Chown(t.tty.Name(), uid, gid)
if err != nil {
return trace.Wrap(err)
}
err = os.Chmod(t.tty.Name(), mode)
if err != nil {
return trace.Wrap(err)
}

log.Debugf("Set permissions on %v to %v:%v with mode %v.", t.tty.Name(), uid, gid, mode)

return nil
}

type remoteTerminal struct {
wg sync.WaitGroup
mu sync.Mutex
Expand Down
95 changes: 95 additions & 0 deletions lib/srv/term_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*
Copyright 2019 Gravitational, Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package srv

import (
"fmt"
"os"
"os/user"
"testing"

"github.com/gravitational/teleport/lib/utils"

"github.com/gravitational/trace"

"gopkg.in/check.v1"
)

type TermSuite struct {
}

var _ = check.Suite(&TermSuite{})
var _ = fmt.Printf

func (s *TermSuite) SetUpSuite(c *check.C) {
utils.InitLoggerForTests(testing.Verbose())
}
func (s *TermSuite) TearDownSuite(c *check.C) {}
func (s *TermSuite) SetUpTest(c *check.C) {}
func (s *TermSuite) TearDownTest(c *check.C) {}

func (s *TermSuite) TestGetOwner(c *check.C) {
tests := []struct {
inUserLookup LookupUser
inGroupLookup LookupGroup
outUid int
outGid int
outMode os.FileMode
}{
// Group "tty" exists.
{
inUserLookup: func(s string) (*user.User, error) {
return &user.User{
Uid: "1000",
Gid: "1000",
}, nil
},
inGroupLookup: func(s string) (*user.Group, error) {
return &user.Group{
Gid: "5",
}, nil
},
outUid: 1000,
outGid: 5,
outMode: 0600,
},
// Group "tty" does not exist.
{
inUserLookup: func(s string) (*user.User, error) {
return &user.User{
Uid: "1000",
Gid: "1000",
}, nil
},
inGroupLookup: func(s string) (*user.Group, error) {
return &user.Group{}, trace.BadParameter("")
},
outUid: 1000,
outGid: 1000,
outMode: 0620,
},
}

for _, tt := range tests {
uid, gid, mode, err := getOwner("", tt.inUserLookup, tt.inGroupLookup)
c.Assert(err, check.IsNil)

c.Assert(uid, check.Equals, tt.outUid)
c.Assert(gid, check.Equals, tt.outGid)
c.Assert(mode, check.Equals, tt.outMode)
}
}