Compare commits

...

1 Commits

Author SHA1 Message Date
naren-scality 034436f13d bf: CLDSRV-12 bucket policy resource security fix
Adds resource check for bucket policies. S3 actions are modified to pass
request object for resource validation.
2021-08-13 03:05:15 -07:00
58 changed files with 94 additions and 44 deletions

View File

@ -1,4 +1,4 @@
const { evaluators, actionMaps } = require('arsenal').policies; const { evaluators, actionMaps, RequestContext } = require('arsenal').policies;
const constants = require('../../../../constants'); const constants = require('../../../../constants');
const { allAuthedUsersId, bucketOwnerActions, logId, publicId } = constants; const { allAuthedUsersId, bucketOwnerActions, logId, publicId } = constants;
@ -182,6 +182,17 @@ function _checkBucketPolicyActions(requestType, actions, log) {
return evaluators.isActionApplicable(mappedAction, actions, log); return evaluators.isActionApplicable(mappedAction, actions, log);
} }
function _checkBucketPolicyResources(request, resource, log) {
if (!request || (Array.isArray(resource) && resource.length === 0)) {
return true;
}
// build request context from the request!
const requestContext = new RequestContext(request.headers, request.query,
request.bucketName, request.objectKey, null,
request.connection.encrypted, request.resourceType, 's3');
return evaluators.isResourceApplicable(requestContext, resource, log);
}
function _getAccountId(arn) { function _getAccountId(arn) {
// account or user arn is of format 'arn:aws:iam::<12-digit-acct-id>:etc... // account or user arn is of format 'arn:aws:iam::<12-digit-acct-id>:etc...
return arn.substr(13, 12); return arn.substr(13, 12);
@ -226,7 +237,7 @@ function _checkPrincipals(canonicalID, arn, principal) {
return false; return false;
} }
function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, log) { function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, log, request) {
let permission = 'defaultDeny'; let permission = 'defaultDeny';
// if requester is user within bucket owner account, actions should be // if requester is user within bucket owner account, actions should be
// allowed unless explicitly denied (assumes allowed by IAM policy) // allowed unless explicitly denied (assumes allowed by IAM policy)
@ -238,12 +249,13 @@ function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, l
const s = copiedStatement[0]; const s = copiedStatement[0];
const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal); const principalMatch = _checkPrincipals(canonicalID, arn, s.Principal);
const actionMatch = _checkBucketPolicyActions(requestType, s.Action, log); const actionMatch = _checkBucketPolicyActions(requestType, s.Action, log);
const resourceMatch = _checkBucketPolicyResources(request, s.Resource, log);
if (principalMatch && actionMatch && s.Effect === 'Deny') { if (principalMatch && actionMatch && resourceMatch && s.Effect === 'Deny') {
// explicit deny trumps any allows, so return immediately // explicit deny trumps any allows, so return immediately
return 'explicitDeny'; return 'explicitDeny';
} }
if (principalMatch && actionMatch && s.Effect === 'Allow') { if (principalMatch && actionMatch && resourceMatch && s.Effect === 'Allow') {
permission = 'allow'; permission = 'allow';
} }
copiedStatement = copiedStatement.splice(1); copiedStatement = copiedStatement.splice(1);
@ -251,7 +263,7 @@ function checkBucketPolicy(policy, requestType, canonicalID, arn, bucketOwner, l
return permission; return permission;
} }
function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log) { function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request) {
// Check to see if user is authorized to perform a // Check to see if user is authorized to perform a
// particular action on bucket based on ACLs. // particular action on bucket based on ACLs.
// TODO: Add IAM checks // TODO: Add IAM checks
@ -271,14 +283,14 @@ function isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log) {
return aclPermission; return aclPermission;
} }
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType, const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
canonicalID, arn, bucket.getOwner(), log); canonicalID, arn, bucket.getOwner(), log, request);
if (bucketPolicyPermission === 'explicitDeny') { if (bucketPolicyPermission === 'explicitDeny') {
return false; return false;
} }
return (aclPermission || (bucketPolicyPermission === 'allow')); return (aclPermission || (bucketPolicyPermission === 'allow'));
} }
function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, log) { function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, log, request) {
const bucketOwner = bucket.getOwner(); const bucketOwner = bucket.getOwner();
if (!objectMD) { if (!objectMD) {
// User is already authorized on the bucket for FULL_CONTROL or WRITE or // User is already authorized on the bucket for FULL_CONTROL or WRITE or
@ -288,7 +300,7 @@ function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, l
} }
// check bucket has read access // check bucket has read access
// 'bucketGet' covers listObjects and listMultipartUploads, bucket read actions // 'bucketGet' covers listObjects and listMultipartUploads, bucket read actions
return isBucketAuthorized(bucket, 'bucketGet', canonicalID, authInfo, log); return isBucketAuthorized(bucket, 'bucketGet', canonicalID, authInfo, log, request);
} }
let requesterIsNotUser = true; let requesterIsNotUser = true;
let arn = null; let arn = null;
@ -315,7 +327,7 @@ function isObjAuthorized(bucket, objectMD, requestType, canonicalID, authInfo, l
return aclPermission; return aclPermission;
} }
const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType, const bucketPolicyPermission = checkBucketPolicy(bucketPolicy, requestType,
canonicalID, arn, bucket.getOwner(), log); canonicalID, arn, bucket.getOwner(), log, request);
if (bucketPolicyPermission === 'explicitDeny') { if (bucketPolicyPermission === 'explicitDeny') {
return false; return false;
} }

View File

@ -17,6 +17,7 @@ function abortMultipartUpload(authInfo, bucketName, objectKey, uploadId, log,
objectKey, objectKey,
uploadId, uploadId,
preciseRequestType: 'multipartDelete', preciseRequestType: 'multipartDelete',
request,
}; };
// For validating the request at the destinationBucket level // For validating the request at the destinationBucket level
// params are the same as validating at the MPU level // params are the same as validating at the MPU level

View File

@ -28,6 +28,7 @@ function bucketDelete(authInfo, request, log, cb) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDelete', requestType: 'bucketDelete',
request,
}; };
return metadataValidateBucket(metadataValParams, log, return metadataValidateBucket(metadataValParams, log,

View File

@ -33,8 +33,7 @@ function bucketDeleteCors(authInfo, request, log, callback) {
} }
log.trace('found bucket in metadata'); log.trace('found bucket in metadata');
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
log)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketDeleteCors', method: 'bucketDeleteCors',

View File

@ -22,6 +22,7 @@ function bucketDeleteEncryption(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeleteEncryption', requestType: 'bucketDeleteEncryption',
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -18,6 +18,7 @@ function bucketDeleteLifecycle(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeleteLifecycle', requestType: 'bucketDeleteLifecycle',
request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);

View File

@ -17,6 +17,7 @@ function bucketDeletePolicy(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeletePolicy', requestType: 'bucketDeletePolicy',
request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);

View File

@ -18,6 +18,7 @@ function bucketDeleteReplication(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketDeleteReplication', requestType: 'bucketDeleteReplication',
request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);

View File

@ -25,8 +25,7 @@ function bucketDeleteWebsite(authInfo, request, log, callback) {
} }
log.trace('found bucket in metadata'); log.trace('found bucket in metadata');
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
log)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketDeleteWebsite', method: 'bucketDeleteWebsite',

View File

@ -315,6 +315,7 @@ function bucketGet(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGet', requestType: 'bucketGet',
request,
}; };
const listParams = { const listParams = {
listingType: 'DelimiterMaster', listingType: 'DelimiterMaster',

View File

@ -45,6 +45,7 @@ function bucketGetACL(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetACL', requestType: 'bucketGetACL',
request,
}; };
const grantInfo = { const grantInfo = {
grants: [], grants: [],

View File

@ -34,8 +34,7 @@ function bucketGetCors(authInfo, request, log, callback) {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
log)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketGetCors', method: 'bucketGetCors',

View File

@ -23,6 +23,7 @@ function bucketGetEncryption(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetEncryption', requestType: 'bucketGetEncryption',
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -21,6 +21,7 @@ function bucketGetLifecycle(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetLifecycle', requestType: 'bucketGetLifecycle',
request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);

View File

@ -36,8 +36,7 @@ function bucketGetLocation(authInfo, request, log, callback) {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
log)) {
log.debug('access denied for account on bucket', { log.debug('access denied for account on bucket', {
requestType, requestType,
method: 'bucketGetLocation', method: 'bucketGetLocation',

View File

@ -38,6 +38,7 @@ function bucketGetNotification(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetNotification', requestType: 'bucketGetNotification',
request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, log, (err, bucket) => {

View File

@ -34,6 +34,7 @@ function bucketGetObjectLock(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetObjectLock', requestType: 'bucketGetObjectLock',
request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);

View File

@ -18,6 +18,7 @@ function bucketGetPolicy(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetPolicy', requestType: 'bucketGetPolicy',
request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, log, (err, bucket) => {

View File

@ -21,6 +21,7 @@ function bucketGetReplication(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetReplication', requestType: 'bucketGetReplication',
request,
}; };
return metadataValidateBucket(metadataValParams, log, (err, bucket) => { return metadataValidateBucket(metadataValParams, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(headers.origin, method, bucket); const corsHeaders = collectCorsHeaders(headers.origin, method, bucket);

View File

@ -54,6 +54,7 @@ function bucketGetVersioning(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketGetVersioning', requestType: 'bucketGetVersioning',
request,
}; };
metadataValidateBucket(metadataValParams, log, (err, bucket) => { metadataValidateBucket(metadataValParams, log, (err, bucket) => {

View File

@ -34,8 +34,7 @@ function bucketGetWebsite(authInfo, request, log, callback) {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,
request.method, bucket); request.method, bucket);
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
log)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketGetWebsite', method: 'bucketGetWebsite',

View File

@ -19,6 +19,7 @@ function bucketHead(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketHead', requestType: 'bucketHead',
request,
}; };
metadataValidateBucket(metadataValParams, log, (err, bucket) => { metadataValidateBucket(metadataValParams, log, (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin, const corsHeaders = collectCorsHeaders(request.headers.origin,

View File

@ -72,6 +72,7 @@ function bucketPutACL(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutACL', requestType: 'bucketPutACL',
request,
}; };
const possibleGrants = ['FULL_CONTROL', 'WRITE', const possibleGrants = ['FULL_CONTROL', 'WRITE',
'WRITE_ACP', 'READ', 'READ_ACP']; 'WRITE_ACP', 'READ', 'READ_ACP'];

View File

@ -66,8 +66,7 @@ function bucketPutCors(authInfo, request, log, callback) {
}); });
}, },
function validateBucketAuthorization(bucket, rules, corsHeaders, next) { function validateBucketAuthorization(bucket, rules, corsHeaders, next) {
if (!isBucketAuthorized(bucket, requestType, canonicalID, if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
authInfo, log)) {
log.debug('access denied for account on bucket', { log.debug('access denied for account on bucket', {
requestType, requestType,
}); });

View File

@ -24,6 +24,7 @@ function bucketPutEncryption(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutEncryption', requestType: 'bucketPutEncryption',
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -26,6 +26,7 @@ function bucketPutLifecycle(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutLifecycle', requestType: 'bucketPutLifecycle',
request,
}; };
return waterfall([ return waterfall([
next => parseXML(request.post, log, next), next => parseXML(request.post, log, next),

View File

@ -24,6 +24,7 @@ function bucketPutNotification(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutNotification', requestType: 'bucketPutNotification',
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -27,6 +27,7 @@ function bucketPutObjectLock(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutObjectLock', requestType: 'bucketPutObjectLock',
request,
}; };
return waterfall([ return waterfall([
next => parseXML(request.post, log, next), next => parseXML(request.post, log, next),

View File

@ -38,6 +38,7 @@ function bucketPutPolicy(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutPolicy', requestType: 'bucketPutPolicy',
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -28,6 +28,7 @@ function bucketPutReplication(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutReplication', requestType: 'bucketPutReplication',
request,
}; };
return waterfall([ return waterfall([
// Validate the request XML and return the replication configuration. // Validate the request XML and return the replication configuration.

View File

@ -88,6 +88,7 @@ function bucketPutVersioning(authInfo, request, log, callback) {
authInfo, authInfo,
bucketName, bucketName,
requestType: 'bucketPutVersioning', requestType: 'bucketPutVersioning',
request,
}; };
return waterfall([ return waterfall([

View File

@ -46,8 +46,7 @@ function bucketPutWebsite(authInfo, request, log, callback) {
}); });
}, },
function validateBucketAuthorization(bucket, config, next) { function validateBucketAuthorization(bucket, config, next) {
if (!isBucketAuthorized(bucket, requestType, canonicalID, if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
authInfo, log)) {
log.debug('access denied for user on bucket', { log.debug('access denied for user on bucket', {
requestType, requestType,
method: 'bucketPutWebsite', method: 'bucketPutWebsite',

View File

@ -84,6 +84,7 @@ function completeMultipartUpload(authInfo, request, log, callback) {
// same as putting a part. // same as putting a part.
requestType: 'putPart or complete', requestType: 'putPart or complete',
log, log,
request,
}; };
const xmlParams = { const xmlParams = {
bucketName, bucketName,

View File

@ -88,6 +88,7 @@ function initiateMultipartUpload(authInfo, request, log, callback) {
bucketName, bucketName,
// Required permissions for this action are same as objectPut // Required permissions for this action are same as objectPut
requestType: 'objectPut', requestType: 'objectPut',
request,
}; };
const accountCanonicalID = authInfo.getCanonicalID(); const accountCanonicalID = authInfo.getCanonicalID();
let initiatorID = accountCanonicalID; let initiatorID = accountCanonicalID;

View File

@ -97,6 +97,7 @@ function listMultipartUploads(authInfo, request, log, callback) {
// as listing objects in a bucket. // as listing objects in a bucket.
requestType: 'bucketGet', requestType: 'bucketGet',
preciseRequestType: 'listMultipartUploads', preciseRequestType: 'listMultipartUploads',
request,
}; };
async.waterfall([ async.waterfall([

View File

@ -96,6 +96,7 @@ function listParts(authInfo, request, log, callback) {
objectKey, objectKey,
uploadId, uploadId,
preciseRequestType: 'listParts', preciseRequestType: 'listParts',
request,
}; };
// For validating the request at the destinationBucket level // For validating the request at the destinationBucket level
// params are the same as validating at the MPU level // params are the same as validating at the MPU level

View File

@ -460,8 +460,7 @@ function multiObjectDelete(authInfo, request, log, callback) {
return next(null, quietSetting, errorResults, inPlay, return next(null, quietSetting, errorResults, inPlay,
bucketMD); bucketMD);
} }
if (!isBucketAuthorized(bucketMD, 'objectDelete', if (!isBucketAuthorized(bucketMD, 'objectDelete', canonicalID, authInfo, log, request)) {
canonicalID, authInfo, log)) {
log.trace("access denied due to bucket acl's"); log.trace("access denied due to bucket acl's");
// if access denied at the bucket level, no access for // if access denied at the bucket level, no access for
// any of the objects so all results will be error results // any of the objects so all results will be error results

View File

@ -213,12 +213,14 @@ function objectCopy(authInfo, request, sourceBucket,
objectKey: sourceObject, objectKey: sourceObject,
versionId: sourceVersionId, versionId: sourceVersionId,
requestType: 'objectGet', requestType: 'objectGet',
request,
}; };
const valPutParams = { const valPutParams = {
authInfo, authInfo,
bucketName: destBucketName, bucketName: destBucketName,
objectKey: destObjectKey, objectKey: destObjectKey,
requestType: 'objectPut', requestType: 'objectPut',
request,
}; };
const dataStoreContext = { const dataStoreContext = {
bucketName: destBucketName, bucketName: destBucketName,

View File

@ -48,6 +48,7 @@ function objectDelete(authInfo, request, log, cb) {
objectKey, objectKey,
versionId: reqVersionId, versionId: reqVersionId,
requestType: 'objectDelete', requestType: 'objectDelete',
request,
}; };
const canonicalID = authInfo.getCanonicalID(); const canonicalID = authInfo.getCanonicalID();

View File

@ -43,6 +43,7 @@ function objectDeleteTagging(authInfo, request, log, callback) {
objectKey, objectKey,
requestType: 'objectDeleteTagging', requestType: 'objectDeleteTagging',
versionId: reqVersionId, versionId: reqVersionId,
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -44,6 +44,7 @@ function objectGet(authInfo, request, returnTagCount, log, callback) {
objectKey, objectKey,
versionId, versionId,
requestType: 'objectGet', requestType: 'objectGet',
request,
}; };
return metadataValidateBucketAndObj(mdValParams, log, return metadataValidateBucketAndObj(mdValParams, log,

View File

@ -60,6 +60,7 @@ function objectGetACL(authInfo, request, log, callback) {
objectKey, objectKey,
versionId, versionId,
requestType: 'objectGetACL', requestType: 'objectGetACL',
request,
}; };
const grantInfo = { const grantInfo = {
grants: [], grants: [],

View File

@ -39,6 +39,7 @@ function objectGetLegalHold(authInfo, request, log, callback) {
objectKey, objectKey,
requestType: 'objectGetLegalHold', requestType: 'objectGetLegalHold',
versionId, versionId,
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -39,6 +39,7 @@ function objectGetRetention(authInfo, request, log, callback) {
objectKey, objectKey,
requestType: 'objectGetRetention', requestType: 'objectGetRetention',
versionId: reqVersionId, versionId: reqVersionId,
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -39,6 +39,7 @@ function objectGetTagging(authInfo, request, log, callback) {
objectKey, objectKey,
requestType: 'objectGetTagging', requestType: 'objectGetTagging',
versionId: reqVersionId, versionId: reqVersionId,
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -44,6 +44,7 @@ function objectHead(authInfo, request, log, callback) {
objectKey, objectKey,
versionId, versionId,
requestType: 'objectHead', requestType: 'objectHead',
request,
}; };
return metadataValidateBucketAndObj(mdValParams, log, return metadataValidateBucketAndObj(mdValParams, log,

View File

@ -56,7 +56,7 @@ function objectPut(authInfo, request, streamingV4Params, log, callback) {
const invalidSSEError = errors.InvalidArgument.customizeDescription( const invalidSSEError = errors.InvalidArgument.customizeDescription(
'The encryption method specified is not supported'); 'The encryption method specified is not supported');
const requestType = 'objectPut'; const requestType = 'objectPut';
const valParams = { authInfo, bucketName, objectKey, requestType }; const valParams = { authInfo, bucketName, objectKey, requestType, request };
const canonicalID = authInfo.getCanonicalID(); const canonicalID = authInfo.getCanonicalID();
log.trace('owner canonicalID to send to data', { canonicalID }); log.trace('owner canonicalID to send to data', { canonicalID });

View File

@ -42,6 +42,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
objectKey: sourceObject, objectKey: sourceObject,
versionId: reqVersionId, versionId: reqVersionId,
requestType: 'objectGet', requestType: 'objectGet',
request,
}; };
const partNumber = Number.parseInt(request.query.partNumber, 10); const partNumber = Number.parseInt(request.query.partNumber, 10);
@ -62,6 +63,7 @@ function objectPutCopyPart(authInfo, request, sourceBucket,
bucketName: destBucketName, bucketName: destBucketName,
objectKey: destObjectKey, objectKey: destObjectKey,
requestType: 'objectPut', requestType: 'objectPut',
request,
}; };
// For validating the request at the MPU, the params are the same // For validating the request at the MPU, the params are the same

View File

@ -42,6 +42,7 @@ function objectPutLegalHold(authInfo, request, log, callback) {
objectKey, objectKey,
requestType: 'objectPutLegalHold', requestType: 'objectPutLegalHold',
versionId, versionId,
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -110,8 +110,7 @@ function objectPutPart(authInfo, request, streamingV4Params, log,
// For validating the request at the destinationBucket level the // For validating the request at the destinationBucket level the
// `requestType` is the general 'objectPut'. // `requestType` is the general 'objectPut'.
const requestType = 'objectPut'; const requestType = 'objectPut';
if (!isBucketAuthorized(destinationBucket, requestType, if (!isBucketAuthorized(destinationBucket, requestType, canonicalID, authInfo, log, request)) {
canonicalID, authInfo, log)) {
log.debug('access denied for user on bucket', { requestType }); log.debug('access denied for user on bucket', { requestType });
return next(errors.AccessDenied, destinationBucket); return next(errors.AccessDenied, destinationBucket);
} }

View File

@ -45,6 +45,7 @@ function objectPutRetention(authInfo, request, log, callback) {
objectKey, objectKey,
requestType: 'objectPutRetention', requestType: 'objectPutRetention',
versionId: reqVersionId, versionId: reqVersionId,
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -44,6 +44,7 @@ function objectPutTagging(authInfo, request, log, callback) {
objectKey, objectKey,
requestType: 'objectPutTagging', requestType: 'objectPutTagging',
versionId: reqVersionId, versionId: reqVersionId,
request,
}; };
return async.waterfall([ return async.waterfall([

View File

@ -144,7 +144,7 @@ function websiteGet(request, log, callback) {
{ error: err }); { error: err });
let returnErr = err; let returnErr = err;
const bucketAuthorized = isBucketAuthorized(bucket, const bucketAuthorized = isBucketAuthorized(bucket,
'bucketGet', constants.publicId, null, log); 'bucketGet', constants.publicId, null, log, request);
// if index object does not exist and bucket is private AWS // if index object does not exist and bucket is private AWS
// returns 403 - AccessDenied error. // returns 403 - AccessDenied error.
if (err === errors.NoSuchKey && !bucketAuthorized) { if (err === errors.NoSuchKey && !bucketAuthorized) {
@ -156,7 +156,7 @@ function websiteGet(request, log, callback) {
callback); callback);
} }
if (!isObjAuthorized(bucket, objMD, 'objectGet', if (!isObjAuthorized(bucket, objMD, 'objectGet',
constants.publicId, null, log)) { constants.publicId, null, log, request)) {
const err = errors.AccessDenied; const err = errors.AccessDenied;
log.trace('request not authorized', { error: err }); log.trace('request not authorized', { error: err });
return _errorActions(err, websiteConfig.getErrorDocument(), return _errorActions(err, websiteConfig.getErrorDocument(),

View File

@ -104,7 +104,7 @@ function websiteHead(request, log, callback) {
{ error: err }); { error: err });
let returnErr = err; let returnErr = err;
const bucketAuthorized = isBucketAuthorized(bucket, const bucketAuthorized = isBucketAuthorized(bucket,
'bucketGet', constants.publicId, null, log); 'bucketGet', constants.publicId, null, log, request);
// if index object does not exist and bucket is private AWS // if index object does not exist and bucket is private AWS
// returns 403 - AccessDenied error. // returns 403 - AccessDenied error.
if (err === errors.NoSuchKey && !bucketAuthorized) { if (err === errors.NoSuchKey && !bucketAuthorized) {
@ -114,7 +114,7 @@ function websiteHead(request, log, callback) {
reqObjectKey, corsHeaders, log, callback); reqObjectKey, corsHeaders, log, callback);
} }
if (!isObjAuthorized(bucket, objMD, 'objectGet', if (!isObjAuthorized(bucket, objMD, 'objectGet',
constants.publicId, null, log)) { constants.publicId, null, log, request)) {
const err = errors.AccessDenied; const err = errors.AccessDenied;
log.trace('request not authorized', { error: err }); log.trace('request not authorized', { error: err });
return _errorActions(err, routingRules, reqObjectKey, return _errorActions(err, routingRules, reqObjectKey,

View File

@ -169,12 +169,13 @@ function metadataGetObject(bucketName, objectKey, versionId, log, cb) {
* @param {string} params.objectKey - name of object * @param {string} params.objectKey - name of object
* @param {string} [params.versionId] - version id if getting specific version * @param {string} [params.versionId] - version id if getting specific version
* @param {string} params.requestType - type of request * @param {string} params.requestType - type of request
* @param {object} params.request - http request object
* @param {RequestLogger} log - request logger * @param {RequestLogger} log - request logger
* @param {function} callback - callback * @param {function} callback - callback
* @return {undefined} - and call callback with params err, bucket md * @return {undefined} - and call callback with params err, bucket md
*/ */
function metadataValidateBucketAndObj(params, log, callback) { function metadataValidateBucketAndObj(params, log, callback) {
const { authInfo, bucketName, objectKey, versionId, requestType, preciseRequestType } = params; const { authInfo, bucketName, objectKey, versionId, requestType, preciseRequestType, request } = params;
const canonicalID = authInfo.getCanonicalID(); const canonicalID = authInfo.getCanonicalID();
async.waterfall([ async.waterfall([
function getBucketAndObjectMD(next) { function getBucketAndObjectMD(next) {
@ -189,7 +190,7 @@ function metadataValidateBucketAndObj(params, log, callback) {
return next(errors.MethodNotAllowed, bucket); return next(errors.MethodNotAllowed, bucket);
} }
if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID, if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID,
authInfo, log)) { authInfo, log, request)) {
log.debug('access denied for user on bucket', { requestType }); log.debug('access denied for user on bucket', { requestType });
return next(errors.AccessDenied, bucket); return next(errors.AccessDenied, bucket);
} }
@ -203,8 +204,7 @@ function metadataValidateBucketAndObj(params, log, callback) {
return next(null, bucket, objMD); return next(null, bucket, objMD);
}, },
function checkObjectAuth(bucket, objMD, next) { function checkObjectAuth(bucket, objMD, next) {
if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, if (!isObjAuthorized(bucket, objMD, requestType, canonicalID, authInfo, log, request)) {
authInfo, log)) {
log.debug('access denied for user on object', { requestType }); log.debug('access denied for user on object', { requestType });
return next(errors.AccessDenied, bucket); return next(errors.AccessDenied, bucket);
} }
@ -251,12 +251,13 @@ function metadataGetBucket(requestType, bucketName, log, cb) {
* @param {AuthInfo} params.authInfo - AuthInfo class instance, requester's info * @param {AuthInfo} params.authInfo - AuthInfo class instance, requester's info
* @param {string} params.bucketName - name of bucket * @param {string} params.bucketName - name of bucket
* @param {string} params.requestType - type of request * @param {string} params.requestType - type of request
* @param {string} params.request - http request object
* @param {RequestLogger} log - request logger * @param {RequestLogger} log - request logger
* @param {function} callback - callback * @param {function} callback - callback
* @return {undefined} - and call callback with params err, bucket md * @return {undefined} - and call callback with params err, bucket md
*/ */
function metadataValidateBucket(params, log, callback) { function metadataValidateBucket(params, log, callback) {
const { authInfo, bucketName, requestType, preciseRequestType } = params; const { authInfo, bucketName, requestType, preciseRequestType, request } = params;
const canonicalID = authInfo.getCanonicalID(); const canonicalID = authInfo.getCanonicalID();
return metadataGetBucket(requestType, bucketName, log, (err, bucket) => { return metadataGetBucket(requestType, bucketName, log, (err, bucket) => {
if (err) { if (err) {
@ -269,7 +270,7 @@ function metadataValidateBucket(params, log, callback) {
return callback(errors.MethodNotAllowed, bucket); return callback(errors.MethodNotAllowed, bucket);
} }
// still return bucket for cors headers // still return bucket for cors headers
if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID, authInfo, log)) { if (!isBucketAuthorized(bucket, (preciseRequestType || requestType), canonicalID, authInfo, log, request)) {
log.debug('access denied for user on bucket', { requestType }); log.debug('access denied for user on bucket', { requestType });
return callback(errors.AccessDenied, bucket); return callback(errors.AccessDenied, bucket);
} }

View File

@ -808,11 +808,14 @@ function routeBackbeat(clientIP, request, response, log) {
// Bucket and object do not exist in metadata. // Bucket and object do not exist in metadata.
return next(null, null, null); return next(null, null, null);
} }
const mdValParams = { bucketName: request.bucketName, const mdValParams = {
bucketName: request.bucketName,
objectKey: request.objectKey, objectKey: request.objectKey,
authInfo: userInfo, authInfo: userInfo,
versionId, versionId,
requestType: 'ReplicateObject' }; requestType: 'ReplicateObject',
request,
};
return metadataValidateBucketAndObj(mdValParams, log, next); return metadataValidateBucketAndObj(mdValParams, log, next);
}, },
(bucketInfo, objMd, next) => { (bucketInfo, objMd, next) => {

View File

@ -20,7 +20,7 @@
"homepage": "https://github.com/scality/S3#readme", "homepage": "https://github.com/scality/S3#readme",
"dependencies": { "dependencies": {
"@hapi/joi": "^17.1.0", "@hapi/joi": "^17.1.0",
"arsenal": "github:scality/Arsenal#9aa8710", "arsenal": "github:scality/Arsenal#db7d8b0",
"async": "~2.5.0", "async": "~2.5.0",
"aws-sdk": "2.905.0", "aws-sdk": "2.905.0",
"azure-storage": "^2.1.0", "azure-storage": "^2.1.0",
@ -77,7 +77,7 @@
"install_ft_deps": "yarn install aws-sdk@2.28.0 bluebird@3.3.1 mocha@2.3.4 mocha-junit-reporter@1.23.1 tv4@1.2.7", "install_ft_deps": "yarn install aws-sdk@2.28.0 bluebird@3.3.1 mocha@2.3.4 mocha-junit-reporter@1.23.1 tv4@1.2.7",
"lint": "eslint $(git ls-files '*.js')", "lint": "eslint $(git ls-files '*.js')",
"lint_md": "mdlint $(git ls-files '*.md')", "lint_md": "mdlint $(git ls-files '*.md')",
"mem_backend": "S3BACKEND=mem node index.js", "mem_backend": "S3BACKEND=mem node --inspect index.js",
"start": "npm-run-all --parallel start_dmd start_s3server", "start": "npm-run-all --parallel start_dmd start_s3server",
"start_mdserver": "node mdserver.js", "start_mdserver": "node mdserver.js",
"start_dataserver": "node dataserver.js", "start_dataserver": "node dataserver.js",

View File

@ -650,9 +650,9 @@ arraybuffer.slice@~0.0.7:
resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675" resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog== integrity sha512-wGUIVQXuehL5TCqQun8OW81jGzAWycqzFF8lFp+GOM5BXLYj3bKNsYC4daB7n6XjCqxQA/qgTJ+8ANR3acjrog==
"arsenal@github:scality/Arsenal#9aa8710": "arsenal@github:scality/Arsenal#db7d8b0":
version "7.7.2" version "7.7.2"
resolved "https://codeload.github.com/scality/Arsenal/tar.gz/9aa8710a57b037685ad2851543808df3a41476b2" resolved "https://codeload.github.com/scality/Arsenal/tar.gz/db7d8b0b4536653361d8e22092c09b87563d6901"
dependencies: dependencies:
"@hapi/joi" "^15.1.0" "@hapi/joi" "^15.1.0"
JSONStream "^1.0.0" JSONStream "^1.0.0"