Skip to content

Commit 3f5e314

Browse files
authored
Ensure token cache files are created with 0600 (#6867)
* Ensure token cache files are created with 0600 * Add fallback when atomic move not supported * Add changelogs
1 parent 64276ec commit 3f5e314

4 files changed

Lines changed: 94 additions & 5 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS SSO OIDC",
4+
"contributor": "",
5+
"description": "Add defensive checks to ensure token cache file is created with user read only where possible and use atomic write/copy."
6+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"type": "bugfix",
3+
"category": "AWS Signin",
4+
"contributor": "",
5+
"description": "Add defensive checks to ensure token cache file is writen with user read permissions only where possible."
6+
}

services/signin/src/main/java/software/amazon/awssdk/services/signin/internal/OnDiskTokenManager.java

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,21 @@
2020
import java.io.OutputStream;
2121
import java.io.UncheckedIOException;
2222
import java.nio.charset.StandardCharsets;
23+
import java.nio.file.AtomicMoveNotSupportedException;
2324
import java.nio.file.Files;
2425
import java.nio.file.Path;
2526
import java.nio.file.StandardCopyOption;
27+
import java.nio.file.attribute.FileAttribute;
28+
import java.nio.file.attribute.PosixFilePermission;
29+
import java.nio.file.attribute.PosixFilePermissions;
2630
import java.security.MessageDigest;
2731
import java.security.NoSuchAlgorithmException;
2832
import java.time.Instant;
2933
import java.time.format.DateTimeFormatter;
34+
import java.util.EnumSet;
3035
import java.util.Locale;
3136
import java.util.Optional;
37+
import java.util.Set;
3238
import software.amazon.awssdk.annotations.SdkInternalApi;
3339
import software.amazon.awssdk.auth.credentials.AwsSessionCredentials;
3440
import software.amazon.awssdk.core.exception.SdkClientException;
@@ -41,6 +47,9 @@
4147

4248
@SdkInternalApi
4349
public final class OnDiskTokenManager implements AccessTokenManager {
50+
private static final Set<PosixFilePermission> OWNER_ONLY_PERMISSIONS =
51+
EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE);
52+
4453
private final JsonNodeParser jsonParser = JsonNodeParser.builder().removeErrorLocations(true).build();
4554

4655
private final Path tokenLocation;
@@ -82,18 +91,45 @@ public Optional<LoginAccessToken> loadToken() {
8291

8392
@Override
8493
public void storeToken(LoginAccessToken token) {
85-
// atomic write (write to a temp file and then move/replace the destination location).
94+
// Write to a temp file first, then move to the destination to avoid partial reads.
8695
try {
87-
Path temp = Files.createTempFile(tokenLocation.getParent(), "token-", ".tmp");
96+
Path temp = createOwnerOnlyTempFile(tokenLocation.getParent(), "token-", ".tmp");
8897
try (OutputStream os = Files.newOutputStream(temp)) {
8998
os.write(marshalToken(token));
9099
}
91-
Files.move(temp, tokenLocation, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
100+
atomicOrFallbackMove(temp, tokenLocation);
92101
} catch (IOException | UncheckedIOException e) {
93102
throw SdkClientException.create("Unable to write token to location " + tokenLocation, e);
94103
}
95104
}
96105

106+
/**
107+
* Creates a temp file with owner-only read/write permissions (0600) on POSIX-compatible file systems.
108+
* On non-POSIX file systems (e.g., Windows), falls back to default permissions.
109+
*/
110+
private static Path createOwnerOnlyTempFile(Path dir, String prefix, String suffix) throws IOException {
111+
try {
112+
FileAttribute<Set<PosixFilePermission>> attr =
113+
PosixFilePermissions.asFileAttribute(OWNER_ONLY_PERMISSIONS);
114+
return Files.createTempFile(dir, prefix, suffix, attr);
115+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
116+
// File system does not support POSIX permissions (e.g., Windows, or in-memory file systems);
117+
// fall back to default permissions.
118+
return Files.createTempFile(dir, prefix, suffix);
119+
}
120+
}
121+
122+
/**
123+
* Attempts an atomic move, falling back to a non-atomic replace if the file system does not support it.
124+
*/
125+
private static void atomicOrFallbackMove(Path source, Path target) throws IOException {
126+
try {
127+
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
128+
} catch (AtomicMoveNotSupportedException e) {
129+
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
130+
}
131+
}
132+
97133
@Override
98134
public void close() {
99135

services/ssooidc/src/main/java/software/amazon/awssdk/services/ssooidc/internal/OnDiskTokenManager.java

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,22 @@
2222
import java.io.InputStream;
2323
import java.io.OutputStream;
2424
import java.nio.charset.StandardCharsets;
25+
import java.nio.file.AtomicMoveNotSupportedException;
2526
import java.nio.file.Files;
2627
import java.nio.file.Path;
2728
import java.nio.file.Paths;
29+
import java.nio.file.StandardCopyOption;
30+
import java.nio.file.attribute.FileAttribute;
31+
import java.nio.file.attribute.PosixFilePermission;
32+
import java.nio.file.attribute.PosixFilePermissions;
2833
import java.security.MessageDigest;
2934
import java.security.NoSuchAlgorithmException;
3035
import java.time.Instant;
3136
import java.time.format.DateTimeFormatter;
37+
import java.util.EnumSet;
3238
import java.util.Locale;
3339
import java.util.Optional;
40+
import java.util.Set;
3441
import software.amazon.awssdk.annotations.SdkInternalApi;
3542
import software.amazon.awssdk.awscore.internal.token.TokenManager;
3643
import software.amazon.awssdk.core.exception.SdkClientException;
@@ -48,6 +55,8 @@
4855
@SdkInternalApi
4956
public final class OnDiskTokenManager implements TokenManager<SsoOidcToken> {
5057
private static final Path DEFAULT_TOKEN_LOCATION = Paths.get(userHomeDirectory(), ".aws", "sso", "cache");
58+
private static final Set<PosixFilePermission> OWNER_ONLY_PERMISSIONS =
59+
EnumSet.of(PosixFilePermission.OWNER_READ, PosixFilePermission.OWNER_WRITE);
5160

5261
private final JsonNodeParser jsonParser = JsonNodeParser.builder().removeErrorLocations(true).build();
5362

@@ -77,13 +86,45 @@ public Optional<SsoOidcToken> loadToken() {
7786

7887
@Override
7988
public void storeToken(SsoOidcToken token) {
80-
try (OutputStream os = Files.newOutputStream(tokenLocation)) {
81-
os.write(marshalToken(token));
89+
// Write to a temp file first, then move to the destination to avoid partial reads.
90+
try {
91+
Path temp = createOwnerOnlyTempFile(tokenLocation.getParent(), "token-", ".tmp");
92+
try (OutputStream os = Files.newOutputStream(temp)) {
93+
os.write(marshalToken(token));
94+
}
95+
atomicOrFallbackMove(temp, tokenLocation);
8296
} catch (IOException e) {
8397
throw SdkClientException.create("Unable to write token to location " + tokenLocation, e);
8498
}
8599
}
86100

101+
/**
102+
* Creates a temp file with owner-only read/write permissions (0600) on POSIX-compatible file systems.
103+
* On non-POSIX file systems (e.g., Windows), falls back to default permissions.
104+
*/
105+
private static Path createOwnerOnlyTempFile(Path dir, String prefix, String suffix) throws IOException {
106+
try {
107+
FileAttribute<Set<PosixFilePermission>> attr =
108+
PosixFilePermissions.asFileAttribute(OWNER_ONLY_PERMISSIONS);
109+
return Files.createTempFile(dir, prefix, suffix, attr);
110+
} catch (UnsupportedOperationException | IllegalArgumentException e) {
111+
// File system does not support POSIX permissions (e.g., Windows, or in-memory file systems);
112+
// fall back to default permissions.
113+
return Files.createTempFile(dir, prefix, suffix);
114+
}
115+
}
116+
117+
/**
118+
* Attempts an atomic move, falling back to a non-atomic replace if the file system does not support it.
119+
*/
120+
private static void atomicOrFallbackMove(Path source, Path target) throws IOException {
121+
try {
122+
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
123+
} catch (AtomicMoveNotSupportedException e) {
124+
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);
125+
}
126+
}
127+
87128
@Override
88129
public void close() {
89130
}

0 commit comments

Comments
 (0)