tape: install new sg-tape-cmd setuid binary

This commit is contained in:
Dietmar Maurer 2020-12-28 11:10:25 +01:00
parent 76b15a035f
commit b27c32821c
3 changed files with 175 additions and 3 deletions

View File

@ -20,7 +20,7 @@ SERVICE_BIN := \
proxmox-backup-api \
proxmox-backup-banner \
proxmox-backup-proxy \
proxmox-daily-update \
proxmox-daily-update
ifeq ($(BUILD_MODE), release)
CARGO_BUILD_ARGS += --release
@ -141,6 +141,8 @@ install: $(COMPILED_BINS)
install -m755 $(COMPILEDIR)/$(i) $(DESTDIR)$(SBINDIR)/ ; \
install -m644 zsh-completions/_$(i) $(DESTDIR)$(ZSH_COMPL_DEST)/ ;)
install -dm755 $(DESTDIR)$(LIBEXECDIR)/proxmox-backup
# install sg-tape-cmd as setuid binary
install -m2755 -mu+s q$(COMPILEDIR)/sg-tape-cmd $(DESTDIR)$(LIBEXECDIR)/proxmox-backup/sg-tape-cmd
$(foreach i,$(SERVICE_BIN), \
install -m755 $(COMPILEDIR)/$(i) $(DESTDIR)$(LIBEXECDIR)/proxmox-backup/ ;)
$(MAKE) -C www install

155
src/bin/sg-tape-cmd.rs Normal file
View File

@ -0,0 +1,155 @@
/// Tape command implemented using scsi-generic raw commands
///
/// SCSI-generic command needs root priviledges, so this binary need
/// to be setuid root.
///
/// This command can use STDIN as tape device handle.
use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd};
use anyhow::{bail, Error};
use proxmox::{
api::{
api,
cli::*,
RpcEnvironment,
},
};
use proxmox_backup::{
api2::types::{
LINUX_DRIVE_PATH_SCHEMA,
LinuxDriveAndMediaStatus,
},
tape::{
mam_extract_media_usage,
linux_tape::{
LinuxTapeHandle,
open_linux_tape_device,
check_tape_is_linux_tape_device,
},
},
};
fn get_tape_handle(device: Option<String>) -> Result<LinuxTapeHandle, Error> {
let file = if let Some(device) = device {
open_linux_tape_device(&device)?
} else {
let fd = std::io::stdin().as_raw_fd();
let file = unsafe { File::from_raw_fd(fd) };
check_tape_is_linux_tape_device(&file)?;
file
};
Ok(LinuxTapeHandle::new(file))
}
#[api(
input: {
properties: {
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
},
},
)]
/// Tape/Media Status
fn status(
device: Option<String>,
) -> Result<(), Error> {
let result = proxmox::try_block!({
let mut handle = get_tape_handle(device)?;
let drive_status = handle.get_drive_status()?;
let mam = handle.cartridge_memory()?;
let usage = mam_extract_media_usage(&mam)?;
Ok(LinuxDriveAndMediaStatus {
blocksize: drive_status.blocksize,
density: drive_status.density,
status: format!("{:?}", drive_status.status),
file_number: drive_status.file_number,
block_number: drive_status.block_number,
manufactured: usage.manufactured,
bytes_read: usage.bytes_read,
bytes_written: usage.bytes_written,
})
}).map_err(|err: Error| err.to_string());
println!("{}", serde_json::to_string_pretty(&result)?);
Ok(())
}
#[api(
input: {
properties: {
device: {
schema: LINUX_DRIVE_PATH_SCHEMA,
optional: true,
},
},
},
)]
/// Read Cartridge Memory (Medium auxiliary memory attributes)
fn cartridge_memory(
device: Option<String>,
) -> Result<(), Error> {
let result = proxmox::try_block!({
let mut handle = get_tape_handle(device)?;
handle.cartridge_memory()
}).map_err(|err| err.to_string());
println!("{}", serde_json::to_string_pretty(&result)?);
Ok(())
}
fn main() -> Result<(), Error> {
// check if we are user root or backup
let backup_uid = proxmox_backup::backup::backup_user()?.uid;
let backup_gid = proxmox_backup::backup::backup_group()?.gid;
let running_uid = nix::unistd::Uid::current();
let running_gid = nix::unistd::Gid::current();
let effective_uid = nix::unistd::Uid::effective();
if !effective_uid.is_root() {
bail!("this program needs to be run with setuid root");
}
if !running_uid.is_root() {
if running_uid != backup_uid || running_gid != backup_gid {
bail!(
"Not running as backup user or group (got uid {} gid {})",
running_uid, running_gid,
);
}
}
let cmd_def = CliCommandMap::new()
.insert(
"status",
CliCommand::new(&API_METHOD_STATUS)
)
.insert(
"cartridge-memory",
CliCommand::new(&API_METHOD_CARTRIDGE_MEMORY)
)
;
let mut rpcenv = CliEnvironment::new();
rpcenv.set_auth_id(Some(String::from("root@pam")));
run_cli_command(cmd_def, rpcenv, None);
Ok(())
}

View File

@ -1,6 +1,6 @@
use std::fs::{OpenOptions, File};
use std::os::unix::fs::OpenOptionsExt;
use std::os::unix::io::AsRawFd;
use std::os::unix::io::{AsRawFd, FromRawFd};
use std::convert::TryFrom;
use anyhow::{bail, format_err, Error};
@ -9,6 +9,7 @@ use nix::fcntl::{fcntl, FcntlArg, OFlag};
use proxmox::sys::error::SysResult;
use crate::{
tools::run_command,
api2::types::{
TapeDensity,
MamAttribute,
@ -237,8 +238,22 @@ impl LinuxTapeHandle {
}
/// Read Cartridge Memory (MAM Attributes)
///
/// Note: Only 'root' user may run RAW SG commands, so we need to
/// spawn setuid binary 'sg-tape-cmd'.
pub fn cartridge_memory(&mut self) -> Result<Vec<MamAttribute>, Error> {
read_mam_attributes(&mut self.file)
if nix::unistd::Uid::effective().is_root() {
return read_mam_attributes(&mut self.file);
}
let mut command = std::process::Command::new(
"/usr/lib/x86_64-linux-gnu/proxmox-backup/sg-tape-cmd");
command.args(&["cartridge-memory"]);
command.stdin(unsafe { std::process::Stdio::from_raw_fd(self.file.as_raw_fd())});
let output = run_command(command, None)?;
let result: Result<Vec<MamAttribute>, String> = serde_json::from_str(&output)?;
result.map_err(|err| format_err!("{}", err))
}
}