[pbs-devel] [PATCH proxmox-backup 2/5] api2/access: implement term ticket
Dominik Csapak
d.csapak at proxmox.com
Tue Jul 21 11:10:37 CEST 2020
modeled after pves/pmgs vncticket (i substituted the vnc with term)
by putting the path and username as secret data in the ticket
when sending the ticket to /access/ticket it only verifies it,
checks the privs on the path and does not generate a new ticket
Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
changes from last version:
* include the port in the termticket
* use rstfmt
src/api2/access.rs | 79 +++++++++++++++++++++++++++++++++++++++------
src/tools/ticket.rs | 32 ++++++++++++++++++
2 files changed, 101 insertions(+), 10 deletions(-)
diff --git a/src/api2/access.rs b/src/api2/access.rs
index f5855ed..46fbd99 100644
--- a/src/api2/access.rs
+++ b/src/api2/access.rs
@@ -13,15 +13,22 @@ use crate::auth_helpers::*;
use crate::api2::types::*;
use crate::config::cached_user_info::CachedUserInfo;
-use crate::config::acl::PRIV_PERMISSIONS_MODIFY;
+use crate::config::acl::{PRIVILEGES, PRIV_PERMISSIONS_MODIFY};
pub mod user;
pub mod domain;
pub mod acl;
pub mod role;
-fn authenticate_user(username: &str, password: &str) -> Result<(), Error> {
-
+/// returns Ok(true) if a ticket has to be created
+/// and Ok(false) if not
+fn authenticate_user(
+ username: &str,
+ password: &str,
+ path: Option<String>,
+ privs: Option<String>,
+ port: Option<u16>,
+) -> Result<bool, Error> {
let user_info = CachedUserInfo::new()?;
if !user_info.is_active_user(&username) {
@@ -33,14 +40,43 @@ fn authenticate_user(username: &str, password: &str) -> Result<(), Error> {
if password.starts_with("PBS:") {
if let Ok((_age, Some(ticket_username))) = tools::ticket::verify_rsa_ticket(public_auth_key(), "PBS", password, None, -300, ticket_lifetime) {
if ticket_username == username {
- return Ok(());
+ return Ok(true);
} else {
bail!("ticket login failed - wrong username");
}
}
+ } else if password.starts_with("PBSTERM:") {
+ if path.is_none() || privs.is_none() || port.is_none() {
+ bail!("cannot check termnal ticket without path, priv and port");
+ }
+
+ let path = path.unwrap();
+ let privilege_name = privs.unwrap();
+ let port = port.unwrap();
+
+ if let Ok((_age, _data)) =
+ tools::ticket::verify_term_ticket(public_auth_key(), &username, &path, port, password)
+ {
+ for (name, privilege) in PRIVILEGES {
+ if *name == privilege_name {
+ let mut path_vec = Vec::new();
+ for part in path.split('/') {
+ if part != "" {
+ path_vec.push(part);
+ }
+ }
+
+ user_info.check_privs(username, &path_vec, *privilege, false)?;
+ return Ok(false);
+ }
+ }
+
+ bail!("No such privilege");
+ }
}
- crate::auth::authenticate_user(username, password)
+ let _ = crate::auth::authenticate_user(username, password)?;
+ Ok(true)
}
#[api(
@@ -52,6 +88,21 @@ fn authenticate_user(username: &str, password: &str) -> Result<(), Error> {
password: {
schema: PASSWORD_SCHEMA,
},
+ path: {
+ type: String,
+ description: "Path for verifying terminal tickets.",
+ optional: true,
+ },
+ privs: {
+ type: String,
+ description: "Privilege for verifying terminal tickets.",
+ optional: true,
+ },
+ port: {
+ type: Integer,
+ description: "Port for verifying terminal tickets.",
+ optional: true,
+ },
},
},
returns: {
@@ -78,11 +129,16 @@ fn authenticate_user(username: &str, password: &str) -> Result<(), Error> {
/// Create or verify authentication ticket.
///
/// Returns: An authentication ticket with additional infos.
-fn create_ticket(username: String, password: String) -> Result<Value, Error> {
- match authenticate_user(&username, &password) {
- Ok(_) => {
-
- let ticket = assemble_rsa_ticket( private_auth_key(), "PBS", Some(&username), None)?;
+fn create_ticket(
+ username: String,
+ password: String,
+ path: Option<String>,
+ privs: Option<String>,
+ port: Option<u16>,
+) -> Result<Value, Error> {
+ match authenticate_user(&username, &password, path, privs, port) {
+ Ok(true) => {
+ let ticket = assemble_rsa_ticket(private_auth_key(), "PBS", Some(&username), None)?;
let token = assemble_csrf_prevention_token(csrf_secret(), &username);
@@ -94,6 +150,9 @@ fn create_ticket(username: String, password: String) -> Result<Value, Error> {
"CSRFPreventionToken": token,
}))
}
+ Ok(false) => Ok(json!({
+ "username": username,
+ })),
Err(err) => {
let client_ip = "unknown"; // $rpcenv->get_client_ip() || '';
log::error!("authentication failure; rhost={} user={} msg={}", client_ip, username, err.to_string());
diff --git a/src/tools/ticket.rs b/src/tools/ticket.rs
index 4727b1e..ff639ce 100644
--- a/src/tools/ticket.rs
+++ b/src/tools/ticket.rs
@@ -11,6 +11,38 @@ use crate::tools::epoch_now_u64;
pub const TICKET_LIFETIME: i64 = 3600*2; // 2 hours
+const TERM_PREFIX: &str = "PBSTERM";
+
+pub fn assemble_term_ticket(
+ keypair: &PKey<Private>,
+ username: &str,
+ path: &str,
+ port: u16,
+) -> Result<String, Error> {
+ assemble_rsa_ticket(
+ keypair,
+ TERM_PREFIX,
+ None,
+ Some(&format!("{}{}{}", username, path, port)),
+ )
+}
+
+pub fn verify_term_ticket(
+ keypair: &PKey<Public>,
+ username: &str,
+ path: &str,
+ port: u16,
+ ticket: &str,
+) -> Result<(i64, Option<String>), Error> {
+ verify_rsa_ticket(
+ keypair,
+ TERM_PREFIX,
+ ticket,
+ Some(&format!("{}{}{}", username, path, port)),
+ -300,
+ TICKET_LIFETIME,
+ )
+}
pub fn assemble_rsa_ticket(
keypair: &PKey<Private>,
--
2.20.1
More information about the pbs-devel
mailing list