Skip to content

Commit e869dd1

Browse files
committed
vmm: Prevent path traveral attack
1 parent 5b60e19 commit e869dd1

File tree

3 files changed

+48
-20
lines changed

3 files changed

+48
-20
lines changed

vmm/rpc/proto/vmm_rpc.proto

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -301,7 +301,7 @@ service Vmm {
301301
rpc BackupDisk(BackupDiskRequest) returns (google.protobuf.Empty);
302302

303303
// List backups for a VM
304-
rpc ListBackups(Id) returns (ListBackupsResponse);
304+
rpc ListBackups(BackupDiskRequest) returns (ListBackupsResponse);
305305

306306
// Delete a backup
307307
rpc DeleteBackup(DeleteBackupRequest) returns (google.protobuf.Empty);

vmm/src/app.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,6 @@ pub struct App {
115115
state: Arc<Mutex<AppState>>,
116116
}
117117

118-
fn validate_filename(s: &str) -> Result<()> {
119-
if s.contains("/") || s.contains("\\") {
120-
bail!("Invalid filename");
121-
}
122-
Ok(())
123-
}
124-
125118
impl App {
126119
fn lock(&self) -> MutexGuard<AppState> {
127120
self.state.lock().unwrap()
@@ -139,15 +132,12 @@ impl App {
139132
self.config.cvm.backup.path.join(id).join("backups")
140133
}
141134

142-
fn backup_dir(&self, id: &str, backup_id: &str) -> Result<PathBuf> {
143-
validate_filename(backup_id)?;
144-
let backup_dir = self.backups_dir(id).join(backup_id);
145-
Ok(backup_dir)
135+
fn backup_dir(&self, id: &str, backup_id: &str) -> PathBuf {
136+
self.backups_dir(id).join(backup_id)
146137
}
147138

148-
fn backup_file(&self, id: &str, backup_id: &str, snapshot_id: &str) -> Result<PathBuf> {
149-
validate_filename(snapshot_id)?;
150-
Ok(self.backup_dir(id, backup_id)?.join(snapshot_id))
139+
fn backup_file(&self, id: &str, backup_id: &str, snapshot_id: &str) -> PathBuf {
140+
self.backup_dir(id, backup_id).join(snapshot_id)
151141
}
152142

153143
pub fn new(config: Config, supervisor: SupervisorClient) -> Self {
@@ -831,7 +821,7 @@ impl App {
831821
if !self.config.cvm.backup.enabled {
832822
bail!("Backup is not enabled");
833823
}
834-
let backup_dir = self.backup_dir(vm_id, backup_id)?;
824+
let backup_dir = self.backup_dir(vm_id, backup_id);
835825
if !backup_dir.exists() {
836826
bail!("Backup does not exist");
837827
}
@@ -857,7 +847,7 @@ impl App {
857847
bail!("VM is not stopped: status={}", info.status);
858848
}
859849

860-
let backup_file = self.backup_file(vm_id, backup_id, snapshot_id)?;
850+
let backup_file = self.backup_file(vm_id, backup_id, snapshot_id);
861851
if !backup_file.exists() {
862852
bail!("Backup file not found");
863853
}
@@ -867,7 +857,7 @@ impl App {
867857
// Just copy the file
868858
tokio::fs::copy(&backup_file, &hda_img).await?;
869859
} else {
870-
let backup_dir = self.backup_dir(vm_id, backup_id)?;
860+
let backup_dir = self.backup_dir(vm_id, backup_id);
871861
let snapshot_id = snapshot_id.to_string();
872862
// Rename the current hda file to *.bak
873863
let bak_file = hda_img.display().to_string() + ".bak";

vmm/src/main_service.rs

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,30 @@ fn resolve_gpus(gpu_cfg: &rpc::GpuConfig) -> Result<GpuConfig> {
106106
}
107107
}
108108

109+
fn check_path_traversal(input: &str) -> Result<()> {
110+
if input.is_empty() {
111+
bail!("path is empty");
112+
}
113+
114+
if input.len() > 255 {
115+
bail!("path is too long");
116+
}
117+
118+
if input.contains('/') {
119+
bail!("path contains '/'");
120+
}
121+
122+
if input.contains('\0') {
123+
bail!("path contains '\0'");
124+
}
125+
126+
if input == "." || input == ".." {
127+
bail!("path is '.' or '..'");
128+
}
129+
130+
Ok(())
131+
}
132+
109133
impl RpcHandler {
110134
fn resolve_gpus(&self, gpu_cfg: &rpc::GpuConfig) -> Result<GpuConfig> {
111135
let gpus = resolve_gpus(gpu_cfg)?;
@@ -209,6 +233,7 @@ impl VmmRpc for RpcHandler {
209233
}
210234

211235
async fn start_vm(self, request: Id) -> Result<()> {
236+
check_path_traversal(&request.id)?;
212237
self.app
213238
.start_vm(&request.id)
214239
.await
@@ -217,6 +242,7 @@ impl VmmRpc for RpcHandler {
217242
}
218243

219244
async fn stop_vm(self, request: Id) -> Result<()> {
245+
check_path_traversal(&request.id)?;
220246
self.app
221247
.stop_vm(&request.id)
222248
.await
@@ -225,6 +251,7 @@ impl VmmRpc for RpcHandler {
225251
}
226252

227253
async fn remove_vm(self, request: Id) -> Result<()> {
254+
check_path_traversal(&request.id)?;
228255
self.app
229256
.remove_vm(&request.id)
230257
.await
@@ -253,6 +280,7 @@ impl VmmRpc for RpcHandler {
253280
}
254281

255282
async fn upgrade_app(self, request: UpgradeAppRequest) -> Result<Id> {
283+
check_path_traversal(&request.id)?;
256284
let new_id = if !request.compose_file.is_empty() {
257285
// check the compose file is valid
258286
let _app_compose: AppCompose =
@@ -322,6 +350,7 @@ impl VmmRpc for RpcHandler {
322350
}
323351

324352
async fn get_info(self, request: Id) -> Result<GetInfoResponse> {
353+
check_path_traversal(&request.id)?;
325354
if let Some(vm) = self.app.vm_info(&request.id).await? {
326355
Ok(GetInfoResponse {
327356
found: true,
@@ -337,6 +366,7 @@ impl VmmRpc for RpcHandler {
337366

338367
#[tracing::instrument(skip(self, request), fields(id = request.id))]
339368
async fn resize_vm(self, request: ResizeVmRequest) -> Result<()> {
369+
check_path_traversal(&request.id)?;
340370
info!("Resizing VM: {:?}", request);
341371
let vm = self
342372
.app
@@ -393,6 +423,7 @@ impl VmmRpc for RpcHandler {
393423
}
394424

395425
async fn shutdown_vm(self, request: Id) -> Result<()> {
426+
check_path_traversal(&request.id)?;
396427
self.guest_agent_client(&request.id)?.shutdown().await?;
397428
Ok(())
398429
}
@@ -458,21 +489,28 @@ impl VmmRpc for RpcHandler {
458489
}
459490

460491
async fn backup_disk(self, request: BackupDiskRequest) -> Result<()> {
492+
check_path_traversal(&request.vm_id)?;
461493
self.app.backup_disk(&request.vm_id, &request.level).await
462494
}
463495

464-
async fn list_backups(self, request: Id) -> Result<rpc::ListBackupsResponse> {
465-
let backups = self.app.list_backups(&request.id).await?;
496+
async fn list_backups(self, request: BackupDiskRequest) -> Result<rpc::ListBackupsResponse> {
497+
check_path_traversal(&request.vm_id)?;
498+
let backups = self.app.list_backups(&request.vm_id).await?;
466499
Ok(rpc::ListBackupsResponse { backups })
467500
}
468501

469502
async fn delete_backup(self, request: DeleteBackupRequest) -> Result<()> {
503+
check_path_traversal(&request.vm_id)?;
504+
check_path_traversal(&request.backup_id)?;
470505
self.app
471506
.delete_backup(&request.vm_id, &request.backup_id)
472507
.await
473508
}
474509

475510
async fn restore_backup(self, request: RestoreBackupRequest) -> Result<()> {
511+
check_path_traversal(&request.vm_id)?;
512+
check_path_traversal(&request.backup_id)?;
513+
check_path_traversal(&request.snapshot_id)?;
476514
self.app
477515
.restore_backup(&request.vm_id, &request.backup_id, &request.snapshot_id)
478516
.await

0 commit comments

Comments
 (0)