Commit 41ccf312 authored by Amos Wenger's avatar Amos Wenger

Fix some bugs, remove some butler dependencies

parent 3ad6bddb
Pipeline #10373 failed with stage
in 50 seconds
package firejail
type Settings struct {
FirejailBinary string
}
......@@ -13,7 +13,7 @@ import (
func (i *instance) Check(consumer *state.Consumer) error {
consumer.Opf("Retrieving player data from registry...")
creds, err := i.getCredentials()
creds, err := i.GetCredentials()
if err != nil {
return errors.WithStack(err)
}
......
......@@ -12,7 +12,7 @@ type Credentials struct {
Password string
}
func (i *instance) getCredentials() (*Credentials, error) {
func (i *instance) GetCredentials() (*Credentials, error) {
username, err := getRegistryString(i.settings, "username")
if err != nil {
return nil, errors.WithStack(err)
......
......@@ -23,7 +23,7 @@ func Test_Credentials(t *testing.T) {
})
assert.NoError(t, err)
creds, err := i.getCredentials()
creds, err := i.GetCredentials()
assert.NoError(t, err)
assert.EqualValues(t, "gecko", creds.Username)
assert.EqualValues(t, "jesus", creds.Password)
......
package fuji
import "github.com/itchio/wharf/state"
type PerformElevatedSetupFunc func() error
type Settings struct {
// CredentialsRegistryKey is the path of a key under HKEY_CURRENT_USER
// itch uses `SOFTWARE\itch\Sandbox`.
CredentialsRegistryKey string
PerformElevatedSetup PerformElevatedSetupFunc
}
type Instance interface {
Settings() *Settings
Check(consumer *state.Consumer) error
Setup(consumer *state.Consumer) error
GetCredentials() (*Credentials, error)
}
......@@ -5,6 +5,7 @@ package fuji
import (
"math/rand"
"strings"
"sync"
"time"
)
......@@ -24,7 +25,7 @@ func randomCharFromSet(prng *rand.Rand, set string) string {
func generatePassword() string {
pwd := ""
prng := rand.New(rand.NewSource(time.Now().UnixNano()))
prng := getPrng()
for i := 0; i < 16; i++ {
var token string
......@@ -42,3 +43,13 @@ func generatePassword() string {
}
return pwd
}
var _prng *rand.Rand
var _initPrngOnce sync.Once
func getPrng() *rand.Rand {
_initPrngOnce.Do(func() {
_prng = rand.New(rand.NewSource(time.Now().UnixNano()))
})
return _prng
}
......@@ -96,7 +96,7 @@ type CheckAccessParams struct {
}
func (i *instance) CheckAccess(consumer *state.Consumer, params *CheckAccessParams) error {
creds, err := i.getCredentials()
creds, err := i.GetCredentials()
if err != nil {
return errors.WithStack(err)
}
......
......@@ -6,7 +6,6 @@ import (
"fmt"
"time"
"github.com/itchio/butler/comm"
"github.com/itchio/ox/winox"
"github.com/itchio/wharf/state"
"github.com/pkg/errors"
......@@ -36,33 +35,33 @@ func (i *instance) Setup(consumer *state.Consumer) error {
var username string
var password string
existingCreds, err := i.getCredentials()
existingCreds, err := i.GetCredentials()
if err != nil {
return errors.WithStack(err)
}
username = existingCreds.Username
if username != "" {
comm.Opf("Trying to salvage existing account (%s)....", username)
consumer.Opf("Trying to salvage existing account (%s)....", username)
password = generatePassword()
err = winox.ForceSetPassword(username, password)
if err != nil {
consumer.Warnf("Could not force password: %+v", err)
username = ""
} else {
comm.Statf("Forced password successfully")
consumer.Statf("Forced password successfully")
}
}
if username == "" {
username = fmt.Sprintf("itch-player-%x", time.Now().Unix())
comm.Opf("Generated username (%s)", username)
consumer.Opf("Generated username (%s)", username)
password = generatePassword()
comm.Opf("Generated password (%s)", password)
consumer.Opf("Generated password (%s)", password)
comment := "itch.io sandbox user"
comm.Opf("Adding user...")
consumer.Opf("Adding user...")
err = winox.AddUser(username, password, comment)
if err != nil {
......@@ -70,21 +69,21 @@ func (i *instance) Setup(consumer *state.Consumer) error {
}
}
comm.Opf("Removing from Users group (so it doesn't show up as a login option)...")
consumer.Opf("Removing from Users group (so it doesn't show up as a login option)...")
err = winox.RemoveUserFromUsersGroup(username)
if err != nil {
return errors.WithStack(err)
}
comm.Opf("Loading profile for the first time (to create some directories)...")
consumer.Opf("Loading profile for the first time (to create some directories)...")
err = winox.LoadProfileOnce(username, ".", password)
if err != nil {
return errors.WithStack(err)
}
comm.Opf("Saving to credentials registry...")
consumer.Opf("Saving to credentials registry...")
creds := &Credentials{
Username: username,
......@@ -95,7 +94,7 @@ func (i *instance) Setup(consumer *state.Consumer) error {
return errors.WithStack(err)
}
comm.Statf("All done! (in %s)", time.Since(startTime))
consumer.Statf("All done! (in %s)", time.Since(startTime))
return nil
}
......@@ -10,9 +10,6 @@ import (
"github.com/itchio/ox/syscallex"
"github.com/itchio/ox/winox"
"github.com/pkg/errors"
"github.com/itchio/butler/butlerd"
"github.com/itchio/butler/butlerd/messages"
)
func getAttachRunner(params *RunnerParams) (Runner, error) {
......@@ -94,8 +91,12 @@ type attachRunner struct {
var _ Runner = (*attachRunner)(nil)
func (ar *attachRunner) Prepare() error {
rc := ar.params.RequestContext
consumer := rc.Consumer
ar.bringWindowsToForeground()
return nil
}
func (ar *attachRunner) bringWindowsToForeground() {
consumer := ar.params.Consumer
// Note: using EnumThreadWindows sounds better at first glance,
// but then remember that this means using CreateToolhelp32Snapshot with
......@@ -103,33 +104,47 @@ func (ar *attachRunner) Prepare() error {
// to avoid looping through a few windows.
// EnumWindows sounds just fine in comparison.
var numWindowsBroughtToForeground int
var referenceLparam uintptr = 999
var hwnds []syscall.Handle
var cbErr error
cb := syscall.NewCallback(func(hwnd syscall.Handle, lparam uintptr) uintptr {
var procId uint32
syscallex.GetWindowThreadProcessId(hwnd, &procId)
if procId == ar.pid {
consumer.Infof("Found window (%x)", hwnd)
// ignore error on purpose - chances are we don't have permissions
// to set foreground anyway
_ = syscallex.SetForegroundWindow(hwnd)
numWindowsBroughtToForeground++
// ignore error - it's not really essential
_ = messages.LaunchWindowShouldBeForeground.Notify(rc, &butlerd.LaunchWindowShouldBeForegroundNotification{
Hwnd: int64(hwnd),
})
if lparam != referenceLparam {
cbErr = errors.Errorf("Internal error: expected lparam %d, but got %d", referenceLparam, lparam)
return 0
}
var procID uint32
syscallex.GetWindowThreadProcessId(hwnd, &procID)
if procID == ar.pid {
hwnds = append(hwnds, hwnd)
}
return 1 // continue enumeration
})
err := syscallex.EnumWindows(cb, 999)
err := syscallex.EnumWindows(cb, referenceLparam)
if err != nil {
consumer.Warnf("Could not enumerate windows: %v", err)
}
consumer.Infof("Brought %d windows to foreground", numWindowsBroughtToForeground)
if cbErr != nil {
consumer.Warnf("While enumerating windows: %v", err)
}
return nil
if len(hwnds) == 0 {
consumer.Warnf("Did not find any windows belonging to (%s), that's unexpected.", ar.params.FullTargetPath)
return
}
bwtf := ar.params.AttachParams.BringWindowToForeground
if bwtf == nil {
consumer.Warnf("Not bringing %d windows to foreground (null BringWindowToForeground)", len(hwnds))
return
}
for _, hwnd := range hwnds {
bwtf(int64(hwnd))
}
consumer.Infof("Brought %d windows to front", len(hwnds))
}
func (ar *attachRunner) Run() error {
......
......@@ -7,29 +7,26 @@ import (
"path/filepath"
"strings"
"github.com/itchio/butler/butlerd/messages"
"github.com/itchio/butler/installer"
"github.com/itchio/butler/butlerd"
"github.com/itchio/butler/cmd/elevate"
"github.com/itchio/ox/syscallex"
"github.com/itchio/ox/winox"
"github.com/itchio/ox/winox/execas"
"github.com/itchio/smaug/fuji"
"github.com/itchio/wharf/state"
"github.com/pkg/errors"
)
type fujiRunner struct {
params *RunnerParams
params *RunnerParams
Credentials *fuji.Credentials
}
var _ Runner = (*fujiRunner)(nil)
func newFujiRunner(params *RunnerParams) (Runner, error) {
if params.FujiParams.Instance == nil {
return nil, errors.Errorf("FujiParams.Instance should be set")
}
wr := &fujiRunner{
params: params,
}
......@@ -38,53 +35,26 @@ func newFujiRunner(params *RunnerParams) (Runner, error) {
func (wr *fujiRunner) Prepare() error {
consumer := wr.params.Consumer
fi := wr.params.FujiParams.Instance
nullConsumer := &state.Consumer{}
err := fuji.Check(nullConsumer)
err := fi.Check(nullConsumer)
if err != nil {
consumer.Warnf("Sandbox check failed: %s", err.Error())
r, err := messages.AllowSandboxSetup.Call(wr.params.RequestContext, &butlerd.AllowSandboxSetupParams{})
err := fi.Settings().PerformElevatedSetup()
if err != nil {
return errors.WithStack(err)
}
if !r.Allow {
return errors.WithStack(butlerd.CodeOperationAborted)
}
consumer.Infof("Proceeding with sandbox setup...")
res, err := installer.RunSelf(&installer.RunSelfParams{
Consumer: consumer,
Args: []string{
"--elevate",
"fuji",
"setup",
},
})
if err != nil {
return errors.WithStack(err)
}
if res.ExitCode != 0 {
if res.ExitCode == elevate.ExitCodeAccessDenied {
return errors.WithStack(butlerd.CodeOperationAborted)
}
}
err = installer.CheckExitCode(res.ExitCode, err)
if err != nil {
return errors.WithStack(err)
return err
}
consumer.Infof("Sandbox setup done, checking again...")
err = fuji.Check(nullConsumer)
err = fi.Check(nullConsumer)
if err != nil {
return errors.WithStack(err)
}
}
Credentials, err := fuji.GetCredentials()
Credentials, err := fi.GetCredentials()
if err != nil {
return errors.WithStack(err)
}
......
......@@ -7,8 +7,8 @@ import (
"syscall"
"unsafe"
"github.com/itchio/butler/runner/execas"
"github.com/itchio/ox/syscallex"
"github.com/itchio/ox/winox/execas"
"github.com/itchio/wharf/state"
"github.com/pkg/errors"
)
......
......@@ -7,7 +7,6 @@ import (
"runtime"
"github.com/itchio/butler/manager"
"github.com/itchio/smaug/firejail"
"github.com/itchio/smaug/fuji"
"github.com/itchio/wharf/state"
)
......@@ -30,8 +29,23 @@ type RunnerParams struct {
InstallFolder string
Runtime *manager.Runtime
FujiSettings *fuji.Settings
FirejailSettings *firejail.Settings
// runner-specific params
FirejailParams FirejailParams
FujiParams FujiParams
AttachParams AttachParams
}
type FirejailParams struct {
BinaryPath string
}
type FujiParams struct {
Instance fuji.Instance
}
type AttachParams struct {
BringWindowToForeground func(hwnd int64)
}
type Runner interface {
......@@ -47,7 +61,7 @@ func GetRunner(params *RunnerParams) (Runner, error) {
return attachRunner, nil
}
if err != nil {
consumer.Warnf("Could not determine if app is aslready running: %s", err.Error())
consumer.Warnf("Could not determine if app is already running: %s", err.Error())
}
switch runtime.GOOS {
......
......@@ -3,8 +3,8 @@
package runner
import (
"github.com/itchio/butler/runner/execas"
"github.com/itchio/ox/syscallex"
"github.com/itchio/ox/winox/execas"
"github.com/pkg/errors"
)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment