Skip to content

Commit 45821f1

Browse files
committed
Merge remote-tracking branch 'upstream/4.22' into nas-backport-upstream
# Conflicts: # plugins/hypervisors/kvm/src/main/java/com/cloud/hypervisor/kvm/resource/wrapper/LibvirtRestoreBackupCommandWrapper.java # scripts/vm/hypervisor/kvm/nasbackup.sh
2 parents 2511b24 + 7ea1dca commit 45821f1

91 files changed

Lines changed: 3665 additions & 621 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎api/src/main/java/org/apache/cloudstack/api/ApiArgValidator.java‎

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,18 @@
1717

1818
package org.apache.cloudstack.api;
1919

20+
import java.util.Locale;
21+
import java.util.regex.Pattern;
22+
23+
import org.apache.commons.lang3.StringUtils;
24+
25+
import com.cloud.exception.InvalidParameterValueException;
26+
import com.cloud.utils.UuidUtils;
27+
2028
public enum ApiArgValidator {
2129
/**
22-
* Validates if the parameter is null or empty with the method {@link Strings#isNullOrEmpty(String)}.
30+
* Validates if the parameter is null or empty with the method {@link StringUtils#isEmpty(CharSequence)}.
31+
* Validation is currently done in the method ParamProcessWorker#validateNonEmptyString(String, String).
2332
*/
2433
NotNullOrEmpty,
2534

@@ -29,12 +38,72 @@ public enum ApiArgValidator {
2938
PositiveNumber,
3039

3140
/**
32-
* Validates if the parameter is an UUID with the method {@link UuidUtils#isUuid(String)}.
41+
* Validates if the parameter is a UUID with the method {@link UuidUtils#isUuid(String)}.
42+
* Validation is currently done in the method ParamProcessWorker#validateUuidString(String, String).
3343
*/
3444
UuidString,
3545

3646
/**
3747
* Validates if the parameter is a valid RFC Compliance domain name.
3848
*/
3949
RFCComplianceDomainName,
50+
51+
/**
52+
* Validates command option strings to avoid unsafe/code-like content.
53+
*/
54+
SafeCommandOptions((param, annotation) -> {
55+
if (BaseCmd.CommandType.STRING.equals(annotation.type())) {
56+
validateSafeCommandOptions(param, annotation.name());
57+
}
58+
});
59+
60+
private static final Pattern SAFE_COMMAND_OPTIONS_PATTERN = Pattern.compile("^[A-Za-z0-9,._=:/+\\-\\s]*$");
61+
62+
private static final String[] UNSAFE_TOKENS = {
63+
"$(", "`", "&&", "||", ";", "|", ">", "<"
64+
};
65+
66+
private final ValidationRule rule;
67+
68+
ApiArgValidator() {
69+
this(null);
70+
}
71+
72+
ApiArgValidator(ValidationRule rule) {
73+
this.rule = rule;
74+
}
75+
76+
public void validate(final Object paramObj, final Parameter annotation) {
77+
if (rule != null) {
78+
rule.validate(paramObj, annotation);
79+
}
80+
}
81+
82+
private static void validateSafeCommandOptions(final Object param, final String argName) {
83+
final String value = String.valueOf(param);
84+
if (StringUtils.isBlank(value)) {
85+
return;
86+
}
87+
88+
if (!SAFE_COMMAND_OPTIONS_PATTERN.matcher(value).matches()) {
89+
throwInvalidParameterValueException(argName, "contains unsupported or unsafe characters");
90+
}
91+
92+
final String normalized = value.toLowerCase(Locale.ROOT);
93+
for (String token : UNSAFE_TOKENS) {
94+
if (normalized.contains(token)) {
95+
throwInvalidParameterValueException(argName, "contains code-like or unsafe content");
96+
}
97+
}
98+
}
99+
100+
private static void throwInvalidParameterValueException(final String argName, final String customMsg) {
101+
throw new InvalidParameterValueException(String.format("Invalid value provided for API arg: %s%s", argName,
102+
StringUtils.isBlank(customMsg) ? "" : " - " + customMsg));
103+
}
104+
105+
@FunctionalInterface
106+
interface ValidationRule {
107+
void validate(Object paramObj, Parameter annotation);
108+
}
40109
}

‎api/src/main/java/org/apache/cloudstack/api/command/admin/vm/ResetVMPasswordCmdByAdmin.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,5 +27,5 @@
2727
@APICommand(name = "resetPasswordForVirtualMachine", responseObject=UserVmResponse.class, description = "Resets the password for Instance. " +
2828
"The Instance must be in a \"Stopped\" state and the Template must already " +
2929
"support this feature for this command to take effect. [async]", responseView = ResponseView.Full, entityType = {VirtualMachine.class},
30-
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
30+
requestHasSensitiveInfo = true, responseHasSensitiveInfo = true)
3131
public class ResetVMPasswordCmdByAdmin extends ResetVMPasswordCmd implements AdminCmd {}

‎api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/CreateAutoScaleVmProfileCmd.java‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.commons.collections.MapUtils;
2323

2424
import org.apache.cloudstack.acl.RoleType;
25+
import org.apache.cloudstack.api.ACL;
2526
import org.apache.cloudstack.api.APICommand;
2627
import org.apache.cloudstack.api.ApiCommandResourceType;
2728
import org.apache.cloudstack.api.ApiConstants;
@@ -106,6 +107,7 @@ public class CreateAutoScaleVmProfileCmd extends BaseAsyncCreateCmd {
106107
since = "4.18.0")
107108
private String userData;
108109

110+
@ACL
109111
@Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18.1")
110112
private Long userDataId;
111113

‎api/src/main/java/org/apache/cloudstack/api/command/user/autoscale/UpdateAutoScaleVmProfileCmd.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ public class UpdateAutoScaleVmProfileCmd extends BaseAsyncCustomIdCmd {
101101
since = "4.18.0")
102102
private String userData;
103103

104+
@ACL
104105
@Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the userdata",
105106
since = "4.18.1")
106107
private Long userDataId;

‎api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/AddBackupRepositoryCmd.java‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.cloud.utils.StringUtils;
2121
import org.apache.cloudstack.acl.RoleType;
2222
import org.apache.cloudstack.api.APICommand;
23+
import org.apache.cloudstack.api.ApiArgValidator;
2324
import org.apache.cloudstack.api.ApiConstants;
2425
import org.apache.cloudstack.api.ApiErrorCode;
2526
import org.apache.cloudstack.api.BaseCmd;
@@ -57,7 +58,8 @@ public class AddBackupRepositoryCmd extends BaseCmd {
5758
@Parameter(name = ApiConstants.PROVIDER, type = CommandType.STRING, description = "backup repository provider")
5859
private String provider;
5960

60-
@Parameter(name = ApiConstants.MOUNT_OPTIONS, type = CommandType.STRING, description = "shared storage mount options")
61+
@Parameter(name = ApiConstants.MOUNT_OPTIONS, type = CommandType.STRING, description = "shared storage mount options",
62+
validations = {ApiArgValidator.SafeCommandOptions})
6163
private String mountOptions;
6264

6365
@Parameter(name = ApiConstants.ZONE_ID,

‎api/src/main/java/org/apache/cloudstack/api/command/user/backup/repository/UpdateBackupRepositoryCmd.java‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@
1717

1818
package org.apache.cloudstack.api.command.user.backup.repository;
1919

20+
import javax.inject.Inject;
21+
2022
import org.apache.cloudstack.acl.RoleType;
2123
import org.apache.cloudstack.api.APICommand;
24+
import org.apache.cloudstack.api.ApiArgValidator;
2225
import org.apache.cloudstack.api.ApiConstants;
2326
import org.apache.cloudstack.api.ApiErrorCode;
2427
import org.apache.cloudstack.api.BaseCmd;
@@ -29,8 +32,6 @@
2932
import org.apache.cloudstack.backup.BackupRepositoryService;
3033
import org.apache.cloudstack.context.CallContext;
3134

32-
import javax.inject.Inject;
33-
3435
@APICommand(name = "updateBackupRepository",
3536
description = "Update a backup repository",
3637
responseObject = BackupRepositoryResponse.class, since = "4.22.0",
@@ -53,7 +54,8 @@ public class UpdateBackupRepositoryCmd extends BaseCmd {
5354
@Parameter(name = ApiConstants.ADDRESS, type = CommandType.STRING, description = "address of the backup repository")
5455
private String address;
5556

56-
@Parameter(name = ApiConstants.MOUNT_OPTIONS, type = CommandType.STRING, description = "shared storage mount options")
57+
@Parameter(name = ApiConstants.MOUNT_OPTIONS, type = CommandType.STRING, description = "shared storage mount options",
58+
validations = {ApiArgValidator.SafeCommandOptions})
5759
private String mountOptions;
5860

5961
@Parameter(name = ApiConstants.CROSS_ZONE_INSTANCE_CREATION, type = CommandType.BOOLEAN, description = "backups in this repository can be used to create Instances on all Zones")

‎api/src/main/java/org/apache/cloudstack/api/command/user/userdata/DeleteUserDataCmd.java‎

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
package org.apache.cloudstack.api.command.user.userdata;
1818

1919
import org.apache.cloudstack.acl.RoleType;
20+
import org.apache.cloudstack.acl.SecurityChecker;
21+
import org.apache.cloudstack.api.ACL;
2022
import org.apache.cloudstack.api.APICommand;
2123
import org.apache.cloudstack.api.ApiConstants;
2224
import org.apache.cloudstack.api.ApiErrorCode;
@@ -27,7 +29,6 @@
2729
import org.apache.cloudstack.api.response.ProjectResponse;
2830
import org.apache.cloudstack.api.response.SuccessResponse;
2931
import org.apache.cloudstack.api.response.UserDataResponse;
30-
import org.apache.cloudstack.context.CallContext;
3132

3233
import com.cloud.user.Account;
3334
import com.cloud.user.UserData;
@@ -43,6 +44,7 @@ public class DeleteUserDataCmd extends BaseCmd {
4344
//////////////// API parameters /////////////////////
4445
/////////////////////////////////////////////////////
4546

47+
@ACL(accessType = SecurityChecker.AccessType.OperateEntry)
4648
@Parameter(name = ApiConstants.ID, type = CommandType.UUID, required = true, entityType = UserDataResponse.class, description = "The ID of the Userdata")
4749
private Long id;
4850

@@ -97,18 +99,13 @@ public void execute() {
9799

98100
@Override
99101
public long getEntityOwnerId() {
100-
Account account = CallContext.current().getCallingAccount();
101-
if ((account == null || _accountService.isAdmin(account.getId())) && (domainId != null && accountName != null)) {
102-
Account userAccount = _responseGenerator.findAccountByNameDomain(accountName, domainId);
103-
if (userAccount != null) {
104-
return userAccount.getId();
102+
if (id != null) {
103+
UserData userData = _entityMgr.findById(UserData.class, id);
104+
if (userData != null) {
105+
return userData.getAccountId();
105106
}
106107
}
107108

108-
if (account != null) {
109-
return account.getId();
110-
}
111-
112109
return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked
113110
}
114111
}

‎api/src/main/java/org/apache/cloudstack/api/command/user/userdata/LinkUserDataToTemplateCmd.java‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
package org.apache.cloudstack.api.command.user.userdata;
1919

2020
import org.apache.cloudstack.acl.RoleType;
21+
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
22+
import org.apache.cloudstack.api.ACL;
2123
import org.apache.cloudstack.api.APICommand;
2224
import org.apache.cloudstack.api.ApiConstants;
2325
import org.apache.cloudstack.api.ApiErrorCode;
@@ -56,6 +58,7 @@ public class LinkUserDataToTemplateCmd extends BaseCmd implements AdminCmd {
5658
description = "The ID of the ISO for the Instance")
5759
private Long isoId;
5860

61+
@ACL(accessType = AccessType.OperateEntry)
5962
@Parameter(name = ApiConstants.USER_DATA_ID,
6063
type = CommandType.UUID,
6164
entityType = UserDataResponse.class,

‎api/src/main/java/org/apache/cloudstack/api/command/user/vm/BaseDeployVMCmd.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ public abstract class BaseDeployVMCmd extends BaseAsyncCreateCustomIdCmd impleme
149149
length = 1048576)
150150
private String userData;
151151

152+
@ACL
152153
@Parameter(name = ApiConstants.USER_DATA_ID, type = CommandType.UUID, entityType = UserDataResponse.class, description = "the ID of the Userdata", since = "4.18")
153154
private Long userdataId;
154155

‎api/src/main/java/org/apache/cloudstack/api/command/user/vm/ResetVMPasswordCmd.java‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
@APICommand(name = "resetPasswordForVirtualMachine", responseObject=UserVmResponse.class, description = "Resets the password for the Instance. " +
4343
"The Instance must be in a \"Stopped\" state and the Template must already " +
4444
"support this feature for this command to take effect. [async]", responseView = ResponseView.Restricted, entityType = {VirtualMachine.class},
45-
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
45+
requestHasSensitiveInfo = true, responseHasSensitiveInfo = true)
4646
public class ResetVMPasswordCmd extends BaseAsyncCmd implements UserCmd {
4747

4848
private static final String s_name = "resetpasswordforvirtualmachineresponse";

0 commit comments

Comments
 (0)