Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,8 @@ const constants = {
'objectDeleteTagging',
'objectGetTagging',
'objectPutTagging',
'objectPutLegalHold',
'objectPutRetention',
],
// response header to be sent when there are invalid
// user metadata in the object's metadata
Expand Down Expand Up @@ -184,6 +186,16 @@ const constants = {
assumedRoleArnResourceType: 'assumed-role',
// Session name of the backbeat lifecycle assumed role session.
backbeatLifecycleSessionName: 'backbeat-lifecycle',
unsupportedSignatureChecksums: new Set([
'STREAMING-UNSIGNED-PAYLOAD-TRAILER',
'STREAMING-AWS4-HMAC-SHA256-PAYLOAD-TRAILER',
'STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD',
'STREAMING-AWS4-ECDSA-P256-SHA256-PAYLOAD-TRAILER',
]),
supportedSignatureChecksums: new Set([
'UNSIGNED-PAYLOAD',
'STREAMING-AWS4-HMAC-SHA256-PAYLOAD',
]),
};

module.exports = constants;
32 changes: 32 additions & 0 deletions lib/api/apiUtils/object/validateChecksumHeaders.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
const { errors } = require('arsenal');

const { unsupportedSignatureChecksums, supportedSignatureChecksums } = require('../../../../constants');

function validateChecksumHeaders(headers) {
// If the x-amz-trailer header is present the request is using one of the
// trailing checksum algorithms, which are not supported.
if (headers['x-amz-trailer'] !== undefined) {
return errors.BadRequest.customizeDescription('trailing checksum is not supported');
}

const signatureChecksum = headers['x-amz-content-sha256'];
if (signatureChecksum === undefined) {
return null;
}

if (supportedSignatureChecksums.has(signatureChecksum)) {
return null;
}

// If the value is not one of the possible checksum algorithms
// the only other valid value is the actual sha256 checksum of the payload.
// Do a simple sanity check of the length to guard against future algos.
// If the value is an unknown algo, then it will fail checksum validation.
if (!unsupportedSignatureChecksums.has(signatureChecksum) && signatureChecksum.length === 64) {
return null;
}

return errors.BadRequest.customizeDescription('unsupported checksum algorithm');
}

module.exports = validateChecksumHeaders;
110 changes: 53 additions & 57 deletions lib/api/bucketPutACL.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const aclUtils = require('../utilities/aclUtils');
const { cleanUpBucket } = require('./apiUtils/bucket/bucketCreation');
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const constants = require('../../constants');
const { metadataValidateBucket } = require('../metadata/metadataUtils');
const { standardMetadataValidateBucket } = require('../metadata/metadataUtils');
const vault = require('../auth/vault');
const { pushMetric } = require('../utapi/utilities');

Expand Down Expand Up @@ -43,7 +43,7 @@ const { pushMetric } = require('../utapi/utilities');
function bucketPutACL(authInfo, request, log, callback) {
log.debug('processing request', { method: 'bucketPutACL' });

const bucketName = request.bucketName;
const { bucketName } = request;
const canonicalID = authInfo.getCanonicalID();
const newCannedACL = request.headers['x-amz-acl'];
const possibleCannedACL = [
Expand All @@ -53,25 +53,14 @@ function bucketPutACL(authInfo, request, log, callback) {
'authenticated-read',
'log-delivery-write',
];
if (newCannedACL && possibleCannedACL.indexOf(newCannedACL) === -1) {
log.trace('invalid canned acl argument', {
acl: newCannedACL,
method: 'bucketPutACL',
});
return callback(errors.InvalidArgument);
}
if (!aclUtils.checkGrantHeaderValidity(request.headers)) {
log.trace('invalid acl header');
return callback(errors.InvalidArgument);
}
const possibleGroups = [constants.allAuthedUsersId,
constants.publicId,
constants.logId,
];
const metadataValParams = {
authInfo,
bucketName,
requestType: 'bucketPutACL',
requestType: request.apiMethods || 'bucketPutACL',
request,
};
const possibleGrants = ['FULL_CONTROL', 'WRITE',
Expand All @@ -85,34 +74,41 @@ function bucketPutACL(authInfo, request, log, callback) {
READ_ACP: [],
};

const grantReadHeader =
aclUtils.parseGrant(request.headers[
'x-amz-grant-read'], 'READ');
const grantWriteHeader =
aclUtils.parseGrant(request.headers['x-amz-grant-write'], 'WRITE');
const grantReadACPHeader =
aclUtils.parseGrant(request.headers['x-amz-grant-read-acp'],
'READ_ACP');
const grantWriteACPHeader =
aclUtils.parseGrant(request.headers['x-amz-grant-write-acp'],
'WRITE_ACP');
const grantFullControlHeader =
aclUtils.parseGrant(request.headers['x-amz-grant-full-control'],
'FULL_CONTROL');
const grantReadHeader = aclUtils.parseGrant(request.headers[
'x-amz-grant-read'], 'READ');
const grantWriteHeader = aclUtils.parseGrant(request.headers['x-amz-grant-write'], 'WRITE');
const grantReadACPHeader = aclUtils.parseGrant(request.headers['x-amz-grant-read-acp'],
'READ_ACP');
const grantWriteACPHeader = aclUtils.parseGrant(request.headers['x-amz-grant-write-acp'],
'WRITE_ACP');
const grantFullControlHeader = aclUtils.parseGrant(request.headers['x-amz-grant-full-control'],
'FULL_CONTROL');

return async.waterfall([
function waterfall1(next) {
metadataValidateBucket(metadataValParams, log,
(err, bucket) => {
if (err) {
log.trace('request authorization failed', {
error: err,
method: 'metadataValidateBucket',
});
return next(err, bucket);
}
return next(null, bucket);
});
standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
(err, bucket) => {
if (err) {
log.trace('request authorization failed', {
error: err,
method: 'metadataValidateBucket',
});
return next(err, bucket);
}
// if the API call is allowed, ensure that the parameters are valid
if (newCannedACL && possibleCannedACL.indexOf(newCannedACL) === -1) {
log.trace('invalid canned acl argument', {
acl: newCannedACL,
method: 'bucketPutACL',
});
return next(errors.InvalidArgument);
}
if (!aclUtils.checkGrantHeaderValidity(request.headers)) {
log.trace('invalid acl header');
return next(errors.InvalidArgument);
}
return next(null, bucket);
});
},
function waterfall2(bucket, next) {
// If not setting acl through headers, parse body
Expand Down Expand Up @@ -179,7 +175,7 @@ function bucketPutACL(authInfo, request, log, callback) {
if (!skip && granteeType === 'Group') {
if (possibleGroups.indexOf(grantee.URI[0]) < 0) {
log.trace('invalid user group',
{ userGroup: grantee.URI[0] });
{ userGroup: grantee.URI[0] });
return next(errors.InvalidArgument, bucket);
}
return usersIdentifiedByGroup.push({
Expand All @@ -193,22 +189,23 @@ function bucketPutACL(authInfo, request, log, callback) {
} else {
// If no canned ACL and no parsed xml, loop
// through the access headers
const allGrantHeaders =
[].concat(grantReadHeader, grantWriteHeader,
const allGrantHeaders = [].concat(grantReadHeader, grantWriteHeader,
grantReadACPHeader, grantWriteACPHeader,
grantFullControlHeader);

usersIdentifiedByEmail = allGrantHeaders.filter(item =>
item && item.userIDType.toLowerCase() === 'emailaddress');
usersIdentifiedByEmail = allGrantHeaders.filter(item => item
&& item.userIDType.toLowerCase() === 'emailaddress');

usersIdentifiedByGroup = allGrantHeaders
.filter(itm => itm && itm.userIDType
.toLowerCase() === 'uri');
.toLowerCase() === 'uri');
for (let i = 0; i < usersIdentifiedByGroup.length; i++) {
const userGroup = usersIdentifiedByGroup[i].identifier;
if (possibleGroups.indexOf(userGroup) < 0) {
log.trace('invalid user group', { userGroup,
method: 'bucketPutACL' });
log.trace('invalid user group', {
userGroup,
method: 'bucketPutACL',
});
return next(errors.InvalidArgument, bucket);
}
}
Expand Down Expand Up @@ -241,8 +238,8 @@ function bucketPutACL(authInfo, request, log, callback) {
return vault.getCanonicalIds(justEmails, log,
(err, results) => {
if (err) {
log.trace('error looking up canonical ids', {
error: err, method: 'vault.getCanonicalIDs' });
log.trace('error looking up canonical ids',
{ error: err, method: 'vault.getCanonicalIDs' });
return next(err, bucket);
}
const reconstructedUsersIdentifiedByEmail = aclUtils
Expand All @@ -251,17 +248,18 @@ function bucketPutACL(authInfo, request, log, callback) {
const allUsers = [].concat(
reconstructedUsersIdentifiedByEmail,
usersIdentifiedByID,
usersIdentifiedByGroup);
usersIdentifiedByGroup,
);
const revisedAddACLParams = aclUtils
.sortHeaderGrants(allUsers, addACLParams);
return next(null, bucket, revisedAddACLParams);
});
}
const allUsers = [].concat(
usersIdentifiedByID,
usersIdentifiedByGroup);
const revisedAddACLParams =
aclUtils.sortHeaderGrants(allUsers, addACLParams);
usersIdentifiedByGroup,
);
const revisedAddACLParams = aclUtils.sortHeaderGrants(allUsers, addACLParams);
return next(null, bucket, revisedAddACLParams);
},
function waterfall4(bucket, addACLParams, next) {
Expand All @@ -272,12 +270,10 @@ function bucketPutACL(authInfo, request, log, callback) {
if (bucket.hasTransientFlag() || bucket.hasDeletedFlag()) {
log.trace('transient or deleted flag so cleaning up bucket');
bucket.setFullAcl(addACLParams);
return cleanUpBucket(bucket, canonicalID, log, err =>
next(err, bucket));
return cleanUpBucket(bucket, canonicalID, log, err => next(err, bucket));
}
// If no bucket flags, just add acl's to bucket metadata
return acl.addACL(bucket, addACLParams, log, err =>
next(err, bucket));
return acl.addACL(bucket, addACLParams, log, err => next(err, bucket));
},
], (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
Expand Down
11 changes: 5 additions & 6 deletions lib/api/bucketPutCors.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ const { errors } = require('arsenal');

const bucketShield = require('./apiUtils/bucket/bucketShield');
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const { isBucketAuthorized } =
require('./apiUtils/authorization/permissionChecks');
const { isBucketAuthorized } = require('./apiUtils/authorization/permissionChecks');
const metadata = require('../metadata/wrapper');
const { parseCorsXml } = require('./apiUtils/bucket/bucketCors');
const { pushMetric } = require('../utapi/utilities');
Expand All @@ -22,7 +21,7 @@ const requestType = 'bucketPutCors';
*/
function bucketPutCors(authInfo, request, log, callback) {
log.debug('processing request', { method: 'bucketPutCors' });
const bucketName = request.bucketName;
const { bucketName } = request;
const canonicalID = authInfo.getCanonicalID();

if (!request.post) {
Expand Down Expand Up @@ -66,7 +65,8 @@ function bucketPutCors(authInfo, request, log, callback) {
});
},
function validateBucketAuthorization(bucket, rules, corsHeaders, next) {
if (!isBucketAuthorized(bucket, requestType, canonicalID, authInfo, log, request)) {
if (!isBucketAuthorized(bucket, request.apiMethods || requestType, canonicalID, authInfo,
log, request, request.actionImplicitDenies)) {
log.debug('access denied for account on bucket', {
requestType,
});
Expand All @@ -77,8 +77,7 @@ function bucketPutCors(authInfo, request, log, callback) {
function updateBucketMetadata(bucket, rules, corsHeaders, next) {
log.trace('updating bucket cors rules in metadata');
bucket.setCors(rules);
metadata.updateBucket(bucketName, bucket, log, err =>
next(err, corsHeaders));
metadata.updateBucket(bucketName, bucket, log, err => next(err, corsHeaders));
},
], (err, corsHeaders) => {
if (err) {
Expand Down
8 changes: 4 additions & 4 deletions lib/api/bucketPutEncryption.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ const async = require('async');
const { parseEncryptionXml } = require('./apiUtils/bucket/bucketEncryption');
const { checkExpectedBucketOwner } = require('./apiUtils/authorization/bucketOwner');
const metadata = require('../metadata/wrapper');
const { metadataValidateBucket } = require('../metadata/metadataUtils');
const { standardMetadataValidateBucket } = require('../metadata/metadataUtils');
const kms = require('../kms/wrapper');
const { pushMetric } = require('../utapi/utilities');
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
Expand All @@ -18,17 +18,17 @@ const collectCorsHeaders = require('../utilities/collectCorsHeaders');
*/

function bucketPutEncryption(authInfo, request, log, callback) {
const bucketName = request.bucketName;
const { bucketName } = request;

const metadataValParams = {
authInfo,
bucketName,
requestType: 'bucketPutEncryption',
requestType: request.apiMethods || 'bucketPutEncryption',
request,
};

return async.waterfall([
next => metadataValidateBucket(metadataValParams, log, next),
next => standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log, next),
(bucket, next) => checkExpectedBucketOwner(request.headers, bucket, log, err => next(err, bucket)),
(bucket, next) => {
log.trace('parsing encryption config', { method: 'bucketPutEncryption' });
Expand Down
14 changes: 6 additions & 8 deletions lib/api/bucketPutLifecycle.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
const { waterfall } = require('async');
const uuid = require('uuid/v4');
const LifecycleConfiguration =
require('arsenal').models.LifecycleConfiguration;
const { LifecycleConfiguration } = require('arsenal').models;

const parseXML = require('../utilities/parseXML');
const collectCorsHeaders = require('../utilities/collectCorsHeaders');
const metadata = require('../metadata/wrapper');
const { metadataValidateBucket } = require('../metadata/metadataUtils');
const { standardMetadataValidateBucket } = require('../metadata/metadataUtils');
const { pushMetric } = require('../utapi/utilities');

/**
Expand All @@ -21,11 +20,11 @@ const { pushMetric } = require('../utapi/utilities');
function bucketPutLifecycle(authInfo, request, log, callback) {
log.debug('processing request', { method: 'bucketPutLifecycle' });

const bucketName = request.bucketName;
const { bucketName } = request;
const metadataValParams = {
authInfo,
bucketName,
requestType: 'bucketPutLifecycle',
requestType: request.apiMethods || 'bucketPutLifecycle',
request,
};
return waterfall([
Expand All @@ -42,7 +41,7 @@ function bucketPutLifecycle(authInfo, request, log, callback) {
return next(null, configObj);
});
},
(lcConfig, next) => metadataValidateBucket(metadataValParams, log,
(lcConfig, next) => standardMetadataValidateBucket(metadataValParams, request.actionImplicitDenies, log,
(err, bucket) => {
if (err) {
return next(err, bucket);
Expand All @@ -54,8 +53,7 @@ function bucketPutLifecycle(authInfo, request, log, callback) {
bucket.setUid(uuid());
}
bucket.setLifecycleConfiguration(lcConfig);
metadata.updateBucket(bucket.getName(), bucket, log, err =>
next(err, bucket));
metadata.updateBucket(bucket.getName(), bucket, log, err => next(err, bucket));
},
], (err, bucket) => {
const corsHeaders = collectCorsHeaders(request.headers.origin,
Expand Down
Loading