Skip to content

Commit ee1fd8d

Browse files
committed
use crypto/rand to generate random password on windows sandbox account
1 parent f49bf09 commit ee1fd8d

File tree

5 files changed

+51
-28
lines changed

5 files changed

+51
-28
lines changed

fuji/check.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,12 @@ func (i *instance) Check(consumer *state.Consumer) error {
3535
// Some Windows versions (10 for example) expire password automatically.
3636
// Thankfully, we can renew it without administrator access, simply by using the old one.
3737
consumer.Opf("Password has expired, setting new password...")
38-
newPassword := generatePassword()
38+
newPassword, err := generatePassword()
39+
if err != nil {
40+
return fmt.Errorf("generating new password: %w", err)
41+
}
3942

40-
err := syscallex.NetUserChangePassword(
43+
err = syscallex.NetUserChangePassword(
4144
nil, // domainname
4245
syscall.StringToUTF16Ptr(creds.Username),
4346
syscall.StringToUTF16Ptr(creds.Password),

fuji/password.go

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
package fuji
44

55
import (
6-
"math/rand"
6+
"crypto/rand"
7+
"fmt"
8+
"math/big"
79
"strings"
8-
"sync"
9-
"time"
1010
)
1111

1212
/** Letters used when generating a random password */
@@ -18,38 +18,47 @@ const kNumbers = "0123456789"
1818
/** Special characters used when generating a random password */
1919
const kSpecial = "!_?-.;+/()=&"
2020

21-
func randomCharFromSet(prng *rand.Rand, set string) string {
22-
index := prng.Intn(len(set))
23-
return set[index : index+1]
21+
func randomCharFromSet(set string) (string, error) {
22+
index, err := randomInt(len(set))
23+
if err != nil {
24+
return "", err
25+
}
26+
return set[index : index+1], nil
27+
}
28+
29+
func randomInt(max int) (int, error) {
30+
if max <= 0 {
31+
return 0, fmt.Errorf("max must be positive")
32+
}
33+
n, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
34+
if err != nil {
35+
return 0, fmt.Errorf("could not generate secure random integer: %w", err)
36+
}
37+
return int(n.Int64()), nil
2438
}
2539

26-
func generatePassword() string {
40+
func generatePassword() (string, error) {
2741
pwd := ""
28-
prng := getPrng()
2942

3043
for i := 0; i < 16; i++ {
3144
var token string
45+
var err error
3246
switch i % 4 {
3347
case 0:
34-
token = randomCharFromSet(prng, kLetters)
48+
token, err = randomCharFromSet(kLetters)
3549
case 1:
36-
token = randomCharFromSet(prng, kNumbers)
50+
token, err = randomCharFromSet(kNumbers)
3751
case 2:
38-
token = randomCharFromSet(prng, kSpecial)
52+
token, err = randomCharFromSet(kSpecial)
3953
case 3:
40-
token = strings.ToUpper(randomCharFromSet(prng, kLetters))
54+
var letter string
55+
letter, err = randomCharFromSet(kLetters)
56+
token = strings.ToUpper(letter)
57+
}
58+
if err != nil {
59+
return "", err
4160
}
4261
pwd += token
4362
}
44-
return pwd
45-
}
46-
47-
var _prng *rand.Rand
48-
var _initPrngOnce sync.Once
49-
50-
func getPrng() *rand.Rand {
51-
_initPrngOnce.Do(func() {
52-
_prng = rand.New(rand.NewSource(time.Now().UnixNano()))
53-
})
54-
return _prng
63+
return pwd, nil
5564
}

fuji/password_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ func Test_GeneratePassword(t *testing.T) {
1313
previousPasswords := make(map[string]bool)
1414

1515
for i := 0; i < 100; i++ {
16-
pass := generatePassword()
16+
pass, err := generatePassword()
17+
assert.NoError(t, err)
1718
assert.True(t, strings.ContainsAny(pass, kLetters), "password has letters")
1819
assert.True(t, strings.ContainsAny(pass, kNumbers), "password has numbers")
1920
assert.True(t, strings.ContainsAny(pass, kSpecial), "password has special characters")

fuji/setup.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ func (i *instance) Setup(consumer *state.Consumer) error {
4141
username = existingCreds.Username
4242
if username != "" {
4343
consumer.Opf("Trying to salvage existing account (%s)....", username)
44-
password = generatePassword()
44+
password, err = generatePassword()
45+
if err != nil {
46+
return fmt.Errorf("generating password: %w", err)
47+
}
4548
err = winox.ForceSetPassword(username, password)
4649
if err != nil {
4750
consumer.Warnf("Could not force password: %+v", err)
@@ -55,7 +58,10 @@ func (i *instance) Setup(consumer *state.Consumer) error {
5558
username = fmt.Sprintf("itch-player-%x", time.Now().Unix())
5659
consumer.Opf("Generated username (%s)", username)
5760

58-
password = generatePassword()
61+
password, err = generatePassword()
62+
if err != nil {
63+
return fmt.Errorf("generating password: %w", err)
64+
}
5965
consumer.Opf("Generated password (%s)", password)
6066

6167
comment := "itch.io sandbox user"

runner/fuji_windows.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ func (wr *fujiRunner) Prepare() error {
4848
if err != nil {
4949
consumer.Warnf("Sandbox check failed: %s", err.Error())
5050

51+
if wr.params.FujiParams.PerformElevatedSetup == nil {
52+
return fmt.Errorf("sandbox setup callback is not configured")
53+
}
54+
5155
err := wr.params.FujiParams.PerformElevatedSetup()
5256
if err != nil {
5357
return err

0 commit comments

Comments
 (0)