1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
//! `StsService` `caller` family — extracted from service.rs by audit-2026-05-19.
use super::*;
impl StsService {
pub(super) fn get_caller_identity(
&self,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
// Prefer the pre-resolved principal that dispatch attached via the
// credential resolver — avoids re-parsing the Authorization header
// and re-walking IamState. Falls back to the account-root identity
// when the caller didn't present resolvable credentials (e.g. the
// `test` root bypass, or no auth header at all).
if let Some(principal) = req.principal.as_ref() {
let xml = xml_responses::get_caller_identity_response(
&principal.account_id,
&principal.arn,
&principal.user_id,
&req.request_id,
);
return Ok(AwsResponse::xml(StatusCode::OK, xml));
}
// F4: real GetCallerIdentity rejects calls that arrive with
// neither a resolvable principal nor an Authorization header.
// AWS returns `MissingAuthenticationTokenException` (HTTP 403)
// in that case. We keep the unsigned-but-account-scoped path
// (the `test` root bypass and similar smoke probes) by falling
// back to root only when an Authorization header was present
// but didn't resolve to a stored principal.
// Accepting `x-amz-security-token` alone lets an unauthenticated
// request pass this guard — that header is metadata that
// accompanies sigv4 auth, not a substitute for it. Require an
// actual Authorization header.
let has_auth_header = req.headers.contains_key("authorization");
if !has_auth_header {
return Err(AwsServiceError::aws_error(
StatusCode::FORBIDDEN,
"MissingAuthenticationTokenException",
"Request is missing Authentication Token",
));
}
let accounts = self.state.read();
let account_id = accounts.default_account_id();
let partition = partition_for_region(&req.region);
let arn = format!("arn:{}:iam::{}:root", partition, account_id);
let user_id = "FKIAIOSFODNN7EXAMPLE";
let xml =
xml_responses::get_caller_identity_response(account_id, &arn, user_id, &req.request_id);
Ok(AwsResponse::xml(StatusCode::OK, xml))
}
pub(super) fn get_access_key_info(
&self,
req: &AwsRequest,
) -> Result<AwsResponse, AwsServiceError> {
let access_key_id = req.query_params.get("AccessKeyId").ok_or_else(|| {
AwsServiceError::aws_error(
StatusCode::BAD_REQUEST,
"MissingParameter",
"The request must contain the parameter AccessKeyId",
)
})?;
validate_string_length("accessKeyId", access_key_id, 16, 128)?;
// Try to resolve account from known access keys across all accounts
let accounts = self.state.read();
let mut resolved_account_id = None;
for (acct_id, acct_state) in accounts.iter() {
if acct_state
.access_keys
.values()
.flatten()
.any(|k| k.access_key_id == *access_key_id)
{
resolved_account_id = Some(acct_id.to_string());
break;
}
if let Some(ci) = acct_state.credential_identities.get(access_key_id.as_str()) {
resolved_account_id = Some(ci.account_id.clone());
break;
}
}
let account_id =
resolved_account_id.unwrap_or_else(|| accounts.default_account_id().to_string());
let xml = xml_responses::get_access_key_info_response(&account_id, &req.request_id);
Ok(AwsResponse::xml(StatusCode::OK, xml))
}
}