[pbs-devel] [PATCH proxmox-backup 2/2] api2/access: implement term ticket
Dominik Csapak
d.csapak at proxmox.com
Fri Jul 17 15:38:38 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>
---
src/api2/access.rs | 70 +++++++++++++++++++++++++++++++++++++++------
src/tools/ticket.rs | 18 ++++++++++++
2 files changed, 80 insertions(+), 8 deletions(-)
diff --git a/src/api2/access.rs b/src/api2/access.rs
index f5855ed..780fa9b 100644
--- a/src/api2/access.rs
+++ b/src/api2/access.rs
@@ -13,15 +13,21 @@ 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>,
+) -> Result<bool, Error> {
let user_info = CachedUserInfo::new()?;
if !user_info.is_active_user(&username) {
@@ -33,14 +39,42 @@ 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() {
+ bail!("cannot check termnal ticket without path and priv");
+ }
+
+ let path = path.unwrap();
+ let privilege_name = privs.unwrap();
+
+ if let Ok((_age, _data)) = tools::ticket::verify_term_ticket(public_auth_key(), &username, &path, 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 +86,16 @@ fn authenticate_user(username: &str, password: &str) -> Result<(), Error> {
password: {
schema: PASSWORD_SCHEMA,
},
+ path: {
+ type: String,
+ description: "path",
+ optional: true,
+ },
+ privs: {
+ type: String,
+ description: "privs",
+ optional: true,
+ },
},
},
returns: {
@@ -78,9 +122,14 @@ 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(_) => {
+fn create_ticket(
+ username: String,
+ password: String,
+ path: Option<String>,
+ privs: Option<String>,
+) -> Result<Value, Error> {
+ match authenticate_user(&username, &password, path, privs) {
+ Ok(true) => {
let ticket = assemble_rsa_ticket( private_auth_key(), "PBS", Some(&username), None)?;
@@ -94,6 +143,11 @@ 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..fc8750e 100644
--- a/src/tools/ticket.rs
+++ b/src/tools/ticket.rs
@@ -11,6 +11,24 @@ 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,
+) -> Result<String, Error> {
+ assemble_rsa_ticket(keypair, TERM_PREFIX, None, Some(&format!("{}{}", username, path)))
+}
+
+pub fn verify_term_ticket(
+ keypair: &PKey<Public>,
+ username: &str,
+ path: &str,
+ ticket: &str,
+) -> Result<(i64, Option<String>), Error> {
+ verify_rsa_ticket(keypair, TERM_PREFIX, ticket, Some(&format!("{}{}", username, path)), -300, TICKET_LIFETIME)
+}
pub fn assemble_rsa_ticket(
keypair: &PKey<Private>,
--
2.20.1
More information about the pbs-devel
mailing list