Skip to content

Commit 35ddc87

Browse files
Fix - list_buckets allowing unauthorized bucket access
Signed-off-by: Aayush Chouhan <achouhan@redhat.com>
1 parent ed6b491 commit 35ddc87

File tree

3 files changed

+88
-3
lines changed

3 files changed

+88
-3
lines changed

src/server/common_services/auth_server.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -536,7 +536,12 @@ function _get_auth_info(account, system, authorized_by, role, extra) {
536536
* @returns {Promise<boolean>} true if the account has permission to perform the action on the bucket
537537
*/
538538
async function has_bucket_action_permission(bucket, account, action, req_query, bucket_path = "") {
539+
if (!account || !account.email) {
540+
dbg.warn('has_bucket_action_permission: account or account.email is missing');
541+
return false;
542+
}
539543
dbg.log1('has_bucket_action_permission:', bucket.name, account.email, bucket.owner_account.email);
544+
540545
// If the system owner account wants to access the bucket, allow it
541546
if (bucket.system.owner.email.unwrap() === account.email.unwrap()) return true;
542547

@@ -558,7 +563,9 @@ async function has_bucket_action_permission(bucket, account, action, req_query,
558563
);
559564

560565
if (result === 'DENY') return false;
561-
return is_owner || result === 'ALLOW';
566+
if (result === 'ALLOW') return true;
567+
568+
return is_owner;
562569
}
563570

564571
/**

src/server/system_services/bucket_server.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1062,8 +1062,13 @@ async function list_buckets(req) {
10621062
let continuation_token = req.rpc_params?.continuation_token;
10631063
const max_buckets = req.rpc_params?.max_buckets;
10641064

1065-
const accessible_bucket_list = system_store.data.buckets.filter(
1066-
async bucket => await req.has_s3_bucket_permission(bucket, "s3:ListBucket", req) && !bucket.deleting
1065+
const accessible_bucket_list = [];
1066+
await Promise.all(
1067+
system_store.data.buckets.map(async bucket => {
1068+
if (bucket.deleting) return;
1069+
const has_permission = await req.has_s3_bucket_permission(bucket, "s3:ListBucket");
1070+
if (has_permission) accessible_bucket_list.push(bucket);
1071+
})
10671072
);
10681073

10691074
accessible_bucket_list.sort((a, b) => a.name.unwrap().localeCompare(b.name.unwrap()));

src/test/integration_tests/api/s3/test_s3_list_buckets.js

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,5 +127,78 @@ mocha.describe('s3_ops', function() {
127127
});
128128
});
129129

130+
mocha.describe('list_buckets permissions', function() {
131+
this.timeout(60000);
132+
let s3_user;
133+
const test_user = 'test-user';
134+
const user_bucket = 'user-buck';
135+
const admin_bucket = 'admin-buck';
136+
137+
mocha.before(async function() {
138+
const account = await rpc_client.account.create_account({
139+
name: test_user,
140+
email: test_user,
141+
has_login: false,
142+
s3_access: true,
143+
default_resource: coretest.POOL_LIST[0].name
144+
});
145+
146+
s3_user = new S3Client({
147+
...s3_client_params,
148+
credentials: {
149+
accessKeyId: account.access_keys[0].access_key.unwrap(),
150+
secretAccessKey: account.access_keys[0].secret_key.unwrap(),
151+
}
152+
});
153+
154+
await s3_user.send(new CreateBucketCommand({ Bucket: user_bucket }));
155+
await s3.send(new CreateBucketCommand({ Bucket: admin_bucket }));
156+
});
157+
158+
mocha.after(async function() {
159+
await s3_user.send(new DeleteBucketCommand({ Bucket: user_bucket }));
160+
await s3.send(new DeleteBucketCommand({ Bucket: admin_bucket }));
161+
await rpc_client.account.delete_account({ email: test_user });
162+
});
163+
164+
mocha.it('user should list only owned buckets', async function() {
165+
const buckets = (await s3_user.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
166+
assert(buckets.includes(user_bucket) && !buckets.includes(admin_bucket));
167+
});
168+
169+
mocha.it('admin should list all buckets', async function() {
170+
const buckets = (await s3.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
171+
assert(buckets.length >= 2);
172+
assert(buckets.includes(user_bucket));
173+
assert(buckets.includes(admin_bucket));
174+
});
175+
176+
mocha.it('bucket policy grants list access', async function() {
177+
let buckets = (await s3_user.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
178+
assert(!buckets.includes(admin_bucket));
179+
180+
await rpc_client.bucket.put_bucket_policy({
181+
name: admin_bucket,
182+
policy: {
183+
Version: '2012-10-17',
184+
Statement: [{
185+
Effect: 'Allow',
186+
Principal: { AWS: test_user },
187+
Action: ['s3:ListBucket'],
188+
Resource: [`arn:aws:s3:::${admin_bucket}`]
189+
}]
190+
}
191+
});
192+
193+
buckets = (await s3_user.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
194+
assert(buckets.includes(admin_bucket));
195+
196+
await rpc_client.bucket.delete_bucket_policy({ name: admin_bucket });
197+
198+
buckets = (await s3_user.send(new ListBucketsCommand())).Buckets.map(b => b.Name);
199+
assert(!buckets.includes(admin_bucket));
200+
});
201+
});
202+
130203
});
131204

0 commit comments

Comments
 (0)