Files
teleport/lib/sshutils/x11/auth_test.go
T
2022-02-04 23:47:03 +00:00

158 lines
5.0 KiB
Go

// Copyright 2022 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 x11
import (
"bytes"
"context"
"encoding/binary"
"encoding/hex"
"os"
"path/filepath"
"testing"
"github.com/gravitational/trace"
"github.com/stretchr/testify/require"
)
func TestXAuthCommands(t *testing.T) {
if os.Getenv("TELEPORT_XAUTH_TEST") == "" {
t.Skip("Skipping test as xauth is not enabled")
}
ctx := context.Background()
tmpDir := t.TempDir()
xauthFile := filepath.Join(tmpDir, ".Xauthority")
display, err := ParseDisplay("unix:10")
require.NoError(t, err)
// New xauth file should have no entries
xauth := NewXAuthCommand(ctx, xauthFile)
xauthEntry, err := xauth.ReadEntry(display)
require.Error(t, err)
require.True(t, trace.IsNotFound(err))
require.Nil(t, xauthEntry)
// Add trusted xauth entry
trustedXauthEntry, err := NewFakeXAuthEntry(display)
require.NoError(t, err)
xauth = NewXAuthCommand(ctx, xauthFile)
err = xauth.AddEntry(*trustedXauthEntry)
require.NoError(t, err)
// Read back the xauth entry
xauth = NewXAuthCommand(ctx, xauthFile)
xauthEntry, err = xauth.ReadEntry(display)
require.NoError(t, err)
require.Equal(t, trustedXauthEntry, xauthEntry)
// Remove xauth entries
xauth = NewXAuthCommand(ctx, xauthFile)
err = xauth.RemoveEntries(xauthEntry.Display)
require.NoError(t, err)
xauth = NewXAuthCommand(ctx, xauthFile)
xauthEntry, err = xauth.ReadEntry(display)
require.Error(t, err)
require.True(t, trace.IsNotFound(err))
require.Nil(t, xauthEntry)
// TODO(Joerger): Currently this test will only run/pass locally if you have $DISPLAY
// set. We need to add Xorg to the buildbox and start an XServer in order to run
// this test in CI. Additionally, Xorg and x11-apps can be used to add more tests
// and rework current tests which depend on fake display listeners instead.
localDisplay, err := GetXDisplay()
if trace.IsBadParameter(err) {
t.Skip("skipping xauth generate test, DISPLAY isn't set")
}
xauth = NewXAuthCommand(ctx, xauthFile)
err = xauth.GenerateUntrustedCookie(localDisplay, 0)
require.NoError(t, err)
xauth = NewXAuthCommand(ctx, xauthFile)
xauthEntry, err = xauth.ReadEntry(localDisplay)
require.NoError(t, err)
require.NotNil(t, xauthEntry)
}
func TestReadAndRewriteXAuthPacket(t *testing.T) {
t.Parallel()
realXAuthEntry, err := NewFakeXAuthEntry(Display{})
require.NoError(t, err)
realXAuthPacket := mockXAuthPacket(t, realXAuthEntry)
spoofedXAuthEntry, err := realXAuthEntry.SpoofXAuthEntry()
require.NoError(t, err)
spoofedXAuthPacket := mockXAuthPacket(t, spoofedXAuthEntry)
otherXAuthEntry, err := NewFakeXAuthEntry(Display{})
require.NoError(t, err)
otherXAuthPacket := mockXAuthPacket(t, otherXAuthEntry)
t.Run("match and replace xauth packet", func(t *testing.T) {
in := bytes.NewBuffer(spoofedXAuthPacket)
out, err := ReadAndRewriteXAuthPacket(in, spoofedXAuthEntry, realXAuthEntry)
require.NoError(t, err)
require.Equal(t, realXAuthPacket, out)
})
t.Run("xauth packet doesn't match", func(t *testing.T) {
in := bytes.NewBuffer(otherXAuthPacket)
out, err := ReadAndRewriteXAuthPacket(in, spoofedXAuthEntry, realXAuthEntry)
require.True(t, trace.IsAccessDenied(err))
require.Empty(t, out)
})
t.Run("xauth packet missing xauth data", func(t *testing.T) {
in := bytes.NewBuffer(mockXAuthPacketInitial(len(mitMagicCookieProto), mitMagicCookieSize))
out, err := ReadAndRewriteXAuthPacket(in, spoofedXAuthEntry, realXAuthEntry)
require.Error(t, err)
require.Empty(t, out)
})
t.Run("xauth packet empty", func(t *testing.T) {
out, err := ReadAndRewriteXAuthPacket(bytes.NewBuffer([]byte{}), spoofedXAuthEntry, realXAuthEntry)
require.Error(t, err)
require.Empty(t, out)
})
}
// mockXAuthPacket creates an xauth packet for the given xauth entry.
func mockXAuthPacket(t *testing.T, entry *XAuthEntry) []byte {
authData, err := hex.DecodeString(entry.Cookie)
require.NoError(t, err)
var xauthPacket []byte
initPacket := mockXAuthPacketInitial(len(entry.Proto), len(authData))
xauthPacket = append(xauthPacket, initPacket...)
xauthPacket = append(xauthPacket, []byte(entry.Proto)...)
xauthPacket = append(xauthPacket, 0, 0)
xauthPacket = append(xauthPacket, authData...)
return xauthPacket
}
// mockXAuthPacketInitial creates the fixed size initial
// portion of an xauth packet, with little endian encoding.
func mockXAuthPacketInitial(authProtoLen, authDataLen int) []byte {
initData := make([]byte, 12)
initData[0] = 'l'
e := binary.LittleEndian
e.PutUint16(initData[6:8], uint16(authProtoLen))
e.PutUint16(initData[8:10], uint16(authDataLen))
return initData
}