Skip to content

Commit 45f7ee2

Browse files
committed
Don't loop forever if fscrypt unlock doesn't have a tty
This fixes the bug in two places: 1. Limit the number of maximum password attempts (I picked 3 to match sudo, but I'm happy to change it to something else). 2. Notice and error out when no input is available when reading a passphrase.
1 parent 827c136 commit 45f7ee2

4 files changed

Lines changed: 62 additions & 6 deletions

File tree

actions/callback.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ func getWrappingKey(info ProtectorInfo, keyFn KeyFunc, retry bool) (*crypto.Key,
8686
// callback returns an error.
8787
func unwrapProtectorKey(info ProtectorInfo, keyFn KeyFunc) (*crypto.Key, error) {
8888
retry := false
89-
for {
89+
for i := 0; i < 3; i++ {
9090
wrappingKey, err := getWrappingKey(info, keyFn, retry)
9191
if err != nil {
9292
return nil, err
@@ -108,6 +108,7 @@ func unwrapProtectorKey(info ProtectorInfo, keyFn KeyFunc) (*crypto.Key, error)
108108
return nil, err
109109
}
110110
}
111+
return nil, errors.New("giving up after 3 incorrect password attempts")
111112
}
112113

113114
// ProtectorOption is information about a protector relative to a Policy.

cli-tests/t_unlock.out

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,44 @@ Protected with 1 protector:
6262
PROTECTOR LINKED DESCRIPTION
6363
desc2 No custom protector "prot"
6464

65+
# Try to unlock with no stdin
66+
[ERROR] fscrypt unlock: empty password
67+
"MNT/dir" is encrypted with fscrypt.
68+
69+
Policy: desc1
70+
Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2
71+
Unlocked: No
72+
73+
Protected with 1 protector:
74+
PROTECTOR LINKED DESCRIPTION
75+
desc2 No custom protector "prot"
76+
77+
# Try to unlock with only a newline
78+
[ERROR] fscrypt unlock: empty password
79+
"MNT/dir" is encrypted with fscrypt.
80+
81+
Policy: desc1
82+
Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2
83+
Unlocked: No
84+
85+
Protected with 1 protector:
86+
PROTECTOR LINKED DESCRIPTION
87+
desc2 No custom protector "prot"
88+
89+
# Try infinitely many wrong passwords
90+
Enter custom passphrase for protector "prot": Incorrect Passphrase
91+
Enter custom passphrase for protector "prot": Incorrect Passphrase
92+
Enter custom passphrase for protector "prot": [ERROR] fscrypt unlock: giving up after 3 incorrect password attempts
93+
"MNT/dir" is encrypted with fscrypt.
94+
95+
Policy: desc1
96+
Options: padding:32 contents:AES_256_XTS filenames:AES_256_CTS policy_version:2
97+
Unlocked: No
98+
99+
Protected with 1 protector:
100+
PROTECTOR LINKED DESCRIPTION
101+
desc2 No custom protector "prot"
102+
65103
# Unlock directory
66104
Enter custom passphrase for protector "prot": "MNT/dir" is now unlocked and ready for use.
67105

@@ -90,7 +128,7 @@ desc1 Yes desc2
90128
the policy metadata for "MNT/dir".
91129
This directory has either been encrypted with another
92130
tool (such as e4crypt), or the file
93-
"MNT/.fscrypt/policies/desc20"
131+
"MNT/.fscrypt/policies/desc26"
94132
has been deleted.
95133

96134
# Try to unlock with missing protector metadata
@@ -103,14 +141,14 @@ information.
103141
[ERROR] fscrypt unlock: inconsistent metadata between encrypted directory
104142
"MNT/dir1" and its corresponding
105143
metadata file
106-
"MNT/.fscrypt/policies/desc21".
144+
"MNT/.fscrypt/policies/desc27".
107145

108146
Directory has
109-
descriptor:desc21 padding:32
147+
descriptor:desc27 padding:32
110148
contents:AES_256_XTS filenames:AES_256_CTS
111149
policy_version:2
112150

113151
Metadata file has
114-
descriptor:desc23 padding:32
152+
descriptor:desc29 padding:32
115153
contents:AES_256_XTS filenames:AES_256_CTS
116154
policy_version:2

cli-tests/t_unlock.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,18 @@ _print_header "Try to unlock with wrong passphrase"
3838
_expect_failure "echo bad | fscrypt unlock --quiet '$dir'"
3939
fscrypt status "$dir"
4040

41+
_print_header "Try to unlock with no stdin"
42+
_expect_failure "fscrypt unlock --quiet '$dir' </dev/null"
43+
fscrypt status "$dir"
44+
45+
_print_header "Try to unlock with only a newline"
46+
_expect_failure "echo | fscrypt unlock --quiet '$dir'"
47+
fscrypt status "$dir"
48+
49+
_print_header "Try infinitely many wrong passwords"
50+
_expect_failure "yes wrong | fscrypt unlock '$dir'"
51+
fscrypt status "$dir"
52+
4153
_print_header "Unlock directory"
4254
echo hunter2 | fscrypt unlock "$dir"
4355
_print_header "=> Check dir status"

crypto/key.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,12 @@ func NewKeyFromReader(reader io.Reader) (*Key, error) {
242242
}
243243
}
244244
case io.EOF:
245-
// Getting the EOF error means we are done
245+
// We got EOF without reading a password.
246+
// We don't support empty passwords, so something is wrong (likely we don't have a tty).
247+
if totalBytesRead == 0 {
248+
return nil, errors.New("empty password")
249+
}
250+
// Getting the EOF error means we are done.
246251
return key.resize(totalBytesRead)
247252
default:
248253
// Fail if Read() has a failure

0 commit comments

Comments
 (0)