1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openeuler-stratovirt

Присоединиться к Gitlife
Откройте для себя и примите участие в публичных проектах с открытым исходным кодом с участием более 10 миллионов разработчиков. Приватные репозитории также полностью бесплатны :)
Присоединиться бесплатно
Это зеркальный репозиторий, синхронизируется ежедневно с исходного репозитория.
В этом репозитории не указан файл с открытой лицензией (LICENSE). При использовании обратитесь к конкретному описанию проекта и его зависимостям в коде.
Клонировать/Скачать
auth_sasl.rs 19 КБ
Копировать Редактировать Web IDE Исходные данные Просмотреть построчно История
zhanghan64 Отправлено год назад 5ca8414
// Copyright (c) 2022 Huawei Technologies Co.,Ltd. All rights reserved.
//
// StratoVirt is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan
// PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
// http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY
// KIND, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
// NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
use std::ffi::{CStr, CString};
use std::ptr;
use anyhow::{anyhow, Result};
use libc::{c_char, c_int, c_uint, c_void};
use log::info;
use sasl2_sys::prelude::{
sasl_conn_t, sasl_dispose, sasl_getprop, sasl_listmech, sasl_security_properties_t,
sasl_server_init, sasl_server_new, sasl_server_start, sasl_server_step, sasl_setprop,
sasl_ssf_t, SASL_CONTINUE, SASL_OK, SASL_SEC_PROPS, SASL_SSF, SASL_SSF_EXTERNAL,
SASL_SUCCESS_DATA,
};
use sasl2_sys::sasl::SASL_USERNAME;
use crate::{
error::VncError,
vnc::client_io::{vnc_flush, vnc_write, ClientIoHandler, APP_NAME},
};
use util::byte_code::ByteCode;
/// Vnc Service.
const SERVICE: &str = "vnc";
const MECHNAME_MAX_LEN: u32 = 100;
const MECHNAME_MIN_LEN: u32 = 1;
const SASL_DATA_MAX_LEN: u32 = 1024 * 1024;
/// Minimum supported encryption length of ssf layer in sasl.
const MIN_SSF_LENGTH: usize = 56;
/// Authentication and encryption method.
#[derive(Debug, Clone, Copy)]
pub enum SubAuthState {
/// Send plain Message + no auth.
VncAuthVencryptPlain = 256,
/// Tls vencry with anon + no auth.
VncAuthVencryptTlNone = 257,
/// Tls vencrypt with x509 + no auth.
VncAuthVencryptX509None = 260,
/// Tls vencrypt with x509 + sasl.
VncAuthVencryptX509Sasl = 263,
/// Tls vencrypt + sasl.
VncAuthVencryptTlssasl = 264,
}
/// Configuration for authentication.
/// Identity: authentication user.
#[derive(Debug, Clone)]
pub struct SaslAuth {
pub identity: String,
}
impl SaslAuth {
pub fn new(identity: String) -> Self {
SaslAuth { identity }
}
}
/// Struct of sasl authentication.
#[derive(Debug, Clone)]
pub struct SaslConfig {
/// State of sasl connection .
sasl_conn: *mut sasl_conn_t,
/// Mech list server support.
mech_list: String,
/// Authentication mechanism currently in use.
mech_name: String,
/// State of auth.
sasl_stage: SaslStage,
/// Security layer in sasl.
want_ssf: bool,
/// Strength of ssf.
run_ssf: u32,
}
impl Default for SaslConfig {
fn default() -> Self {
SaslConfig {
sasl_conn: ptr::null_mut() as *mut sasl_conn_t,
mech_list: String::new(),
mech_name: String::new(),
sasl_stage: SaslStage::SaslServerStart,
want_ssf: false,
run_ssf: 0,
}
}
}
/// Authentication stage.
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum SaslStage {
SaslServerStart,
SaslServerStep,
}
impl ClientIoHandler {
/// Get length of mechname send form client.
pub fn get_mechname_length(&mut self) -> Result<()> {
let buf = self.read_incoming_msg();
let len = u32::from_be_bytes([buf[0], buf[1], buf[2], buf[3]]);
trace::vnc_client_get_mechname_length(&len);
if !(MECHNAME_MIN_LEN..MECHNAME_MAX_LEN).contains(&len) {
return Err(anyhow!(VncError::AuthFailed(
"get_mechname_length".to_string(),
"SASL mechname too short or too long".to_string()
)));
}
self.update_event_handler(len as usize, ClientIoHandler::get_sasl_mechname);
Ok(())
}
/// Start sasl authentication.
/// 1. Sals server init.
/// 2. Get the mechlist support by Sasl server.
/// 3. Send the mechlist to client.
pub fn start_sasl_auth(&mut self) -> Result<()> {
self.sasl_server_init()?;
self.set_ssf_for_sasl()?;
self.send_mech_list()?;
Ok(())
}
/// Get authentication mechanism supported by client.
pub fn get_sasl_mechname(&mut self) -> Result<()> {
let buf = self.read_incoming_msg();
let mech_name = String::from_utf8_lossy(&buf).to_string();
trace::vnc_client_get_mechname(&mech_name);
let mut security = self.server.security_type.borrow_mut();
let mech_list: Vec<&str> = security.saslconfig.mech_list.split(',').collect();
for mech in mech_list {
if mech_name == *mech {
security.saslconfig.mech_name = mech_name;
break;
}
}
// Unsupported mechanism.
if security.saslconfig.mech_name.is_empty() {
return Err(anyhow!(VncError::AuthFailed(
"get_sasl_mechname".to_string(),
"Unsupported mechanism".to_string()
)));
}
drop(security);
self.update_event_handler(4, ClientIoHandler::get_authmessage_length);
Ok(())
}
/// Length of client authentication message.
fn get_authmessage_length(&mut self) -> Result<()> {
let buf = self.read_incoming_msg();
let buf = [buf[0], buf[1], buf[2], buf[3]];
let len = u32::from_be_bytes(buf);
trace::vnc_client_get_authmessage_length(&len);
if len > SASL_DATA_MAX_LEN {
return Err(anyhow!(VncError::AuthFailed(
"get_authmessage_length".to_string(),
"SASL start len too large".to_string()
)));
}
if len == 0 {
return self.client_sasl_auth();
}
self.update_event_handler(len as usize, ClientIoHandler::client_sasl_auth);
Ok(())
}
/// Receive the authentication information from client and return the result.
fn client_sasl_auth(&mut self) -> Result<()> {
let buf = self.read_incoming_msg();
let mut client_data = buf.to_vec();
let mut client_len: c_uint = 0;
if self.expect > 0 {
client_len = (self.expect - 1) as c_uint;
client_data[self.expect - 1] = 0_u8;
}
let server = self.server.clone();
let client = self.client.clone();
let mut security = server.security_type.borrow_mut();
let mut serverout: *const c_char = ptr::null_mut();
let mut serverout_len: c_uint = 0;
let mech_name = CString::new(security.saslconfig.mech_name.as_str())?;
// Start authentication.
let err: c_int = match security.saslconfig.sasl_stage {
// SAFETY: sasl_server_start() and sasl_server_step() is C function. All parameters
// passed of the function have been checked. Memory will be allocated for the incoming
// pointer inside the function.
SaslStage::SaslServerStart => unsafe {
sasl_server_start(
security.saslconfig.sasl_conn,
mech_name.as_ptr(),
client_data.as_ptr() as *const c_char,
client_len,
&mut serverout,
&mut serverout_len,
)
},
// SAFETY: The reason is same as above.
SaslStage::SaslServerStep => unsafe {
sasl_server_step(
security.saslconfig.sasl_conn,
client_data.as_ptr() as *const c_char,
client_len,
&mut serverout,
&mut serverout_len,
)
},
};
trace::vnc_client_sasl_auth(&err, &serverout_len);
if err != SASL_OK && err != SASL_CONTINUE {
// SAFETY: sasl_dispose() is C function. All parameters passed of the
// function have been checked.
unsafe { sasl_dispose(&mut security.saslconfig.sasl_conn) }
return Err(anyhow!(VncError::AuthFailed(
"client_sasl_auth".to_string(),
"Auth failed!".to_string()
)));
}
if serverout_len > SASL_DATA_MAX_LEN {
// SAFETY: The reason is same as above.
unsafe { sasl_dispose(&mut security.saslconfig.sasl_conn) }
return Err(anyhow!(VncError::AuthFailed(
"client_sasl_auth".to_string(),
"SASL data too long".to_string()
)));
}
let mut buf = Vec::new();
if serverout_len > 0 {
// Authentication related information.
// SAFETY: pointer of serverout can be guaranteed not null.
let serverout = unsafe { CStr::from_ptr(serverout as *const c_char) };
let auth_message = String::from(serverout.to_str().unwrap_or(""));
buf.append(&mut (serverout_len + 1).to_be_bytes().to_vec());
buf.append(&mut auth_message.as_bytes().to_vec());
} else {
buf.append(&mut (0_u32).to_be_bytes().to_vec());
}
if err == SASL_OK {
buf.append(&mut (1_u8).as_bytes().to_vec());
} else if err == SASL_CONTINUE {
buf.append(&mut (0_u8).as_bytes().to_vec());
}
drop(security);
if err == SASL_CONTINUE {
// Authentication continue.
let mut security = server.security_type.borrow_mut();
security.saslconfig.sasl_stage = SaslStage::SaslServerStep;
self.update_event_handler(4, ClientIoHandler::get_authmessage_length);
drop(security);
return Ok(());
} else {
if let Err(err) = self.sasl_check_ssf() {
// Reject auth: the strength of ssf is too weak.
auth_reject(&mut buf);
vnc_write(&client, buf);
vnc_flush(&client);
return Err(err);
}
if let Err(err) = self.sasl_check_authz() {
// Reject auth: wrong sasl username.
auth_reject(&mut buf);
vnc_write(&client, buf);
vnc_flush(&client);
return Err(err);
}
// Accept auth.
buf.append(&mut (0_u32).as_bytes().to_vec());
}
vnc_write(&client, buf);
vnc_flush(&client);
self.update_event_handler(1, ClientIoHandler::handle_client_init);
Ok(())
}
/// Sasl server init.
fn sasl_server_init(&mut self) -> Result<()> {
let mut err: c_int;
let service = CString::new(SERVICE)?;
let appname = CString::new(APP_NAME)?;
let local_addr = self.stream.local_addr()?.to_string().replace(':', ";");
let remote_addr = self.stream.peer_addr()?.to_string().replace(':', ";");
info!("local_addr: {} remote_addr: {}", local_addr, remote_addr);
let local_addr = CString::new(local_addr)?;
let remote_addr = CString::new(remote_addr)?;
// SAFETY: sasl_server_init() and sasl_server_new() is C function. All parameters passed of
// the function have been checked. Memory will be allocated for the incoming pointer inside
// the function. Sasl server init.
unsafe {
err = sasl_server_init(ptr::null_mut(), appname.as_ptr());
}
if err != SASL_OK {
return Err(anyhow!(VncError::AuthFailed(
"sasl_server_init".to_string(),
format!("SASL_FAIL error code {}", err)
)));
}
let mut saslconfig = SaslConfig::default();
// SAFETY: The reason is same as above.
unsafe {
err = sasl_server_new(
service.as_ptr(),
ptr::null_mut(),
ptr::null_mut(),
local_addr.as_ptr(),
remote_addr.as_ptr(),
ptr::null_mut(),
SASL_SUCCESS_DATA,
&mut saslconfig.sasl_conn,
);
}
if err != SASL_OK {
return Err(anyhow!(VncError::AuthFailed(
"sasl_server_init".to_string(),
format!("SASL_FAIL error code {}", err)
)));
}
self.server.security_type.borrow_mut().saslconfig = saslconfig;
Ok(())
}
/// Set properties for sasl.
fn set_ssf_for_sasl(&mut self) -> Result<()> {
// Set the relevant properties of sasl.
let mut err: c_int;
let ssf: sasl_ssf_t = 256;
let ssf = &ssf as *const sasl_ssf_t;
let security = self.server.security_type.borrow_mut();
// SAFETY: sasl_setprop() and sasl_server_new() is C function. It can be ensure
// that security.saslconfig.sasl_conn is not null.
unsafe {
err = sasl_setprop(
security.saslconfig.sasl_conn,
SASL_SSF_EXTERNAL as i32,
ssf as *const c_void,
);
}
if err != SASL_OK {
return Err(anyhow!(VncError::AuthFailed(
"set_ssf_for_sasl".to_string(),
format!("SASL_FAIL error code {}", err)
)));
}
// Already using tls, disable ssf in sasl.
let props_name = ptr::null_mut() as *mut *const c_char;
let props_value = ptr::null_mut() as *mut *const c_char;
let saslprops = sasl_security_properties_t {
min_ssf: 0,
max_ssf: 0,
maxbufsize: 8192,
security_flags: 0,
property_names: props_name,
property_values: props_value,
};
let props = &saslprops as *const sasl_security_properties_t;
// SAFETY: sasl_setprop() and sasl_server_new() is C function. It can be ensure
// that security.saslconfig.sasl_conn is not null.
unsafe {
err = sasl_setprop(
security.saslconfig.sasl_conn,
SASL_SEC_PROPS as i32,
props as *const c_void,
);
}
if err != SASL_OK {
return Err(anyhow!(VncError::AuthFailed(
"set_ssf_for_sasl".to_string(),
format!("SASL_FAIL error code {}", err)
)));
}
Ok(())
}
/// Get the mechlist support by Sasl server.
/// Send the mechlist to client.
fn send_mech_list(&mut self) -> Result<()> {
let err: c_int;
let prefix = CString::new("")?;
let sep = CString::new(",")?;
let suffix = CString::new("")?;
let mut mechlist: *const c_char = ptr::null_mut();
let mut security = self.server.security_type.borrow_mut();
let client = self.client.clone();
// SAFETY: sasl_listmech() is C function. It can be ensure
// that security.saslconfig.sasl_conn is not null.
unsafe {
err = sasl_listmech(
security.saslconfig.sasl_conn,
ptr::null_mut(),
prefix.as_ptr(),
sep.as_ptr(),
suffix.as_ptr(),
&mut mechlist,
ptr::null_mut(),
ptr::null_mut(),
);
}
if err != SASL_OK || mechlist.is_null() {
return Err(anyhow!(VncError::AuthFailed(
"send_mech_list".to_string(),
"SASL_FAIL: no support sasl mechlist".to_string()
)));
}
// SAFETY: It can be ensure that the pointer of mechlist is not null.
let mech_list = unsafe { CStr::from_ptr(mechlist as *const c_char) };
trace::vnc_server_send_mech_list(&mech_list);
security.saslconfig.mech_list = String::from(mech_list.to_str()?);
let mut buf = Vec::new();
let len = security.saslconfig.mech_list.len();
buf.append(&mut (len as u32).to_be_bytes().to_vec());
buf.append(&mut security.saslconfig.mech_list.as_bytes().to_vec());
drop(security);
vnc_write(&client, buf);
vnc_flush(&client);
Ok(())
}
/// Check whether the ssf layer of sasl meets the strength requirements.
fn sasl_check_ssf(&mut self) -> Result<()> {
let server = self.server.clone();
let mut security = server.security_type.borrow_mut();
if !security.saslconfig.want_ssf {
return Ok(());
}
let err: c_int;
let mut val: *const c_void = ptr::null_mut();
// SAFETY: sasl_getprop() is C function. It can be ensure
// that security.saslconfig.sasl_conn is not null.
unsafe { err = sasl_getprop(security.saslconfig.sasl_conn, SASL_SSF as c_int, &mut val) }
if err != SASL_OK {
return Err(anyhow!(VncError::AuthFailed(
"sasl_check_ssf".to_string(),
"sasl_getprop: internal error".to_string()
)));
}
// SAFETY: It can be ensure that the ptr of val is not null.
let ssf: usize = unsafe { *(val as *const usize) };
if ssf < MIN_SSF_LENGTH {
return Err(anyhow!(VncError::AuthFailed(
"sasl_check_ssf".to_string(),
"SASL SSF too weak".to_string()
)));
}
security.saslconfig.run_ssf = 1;
drop(security);
Ok(())
}
/// Check username.
fn sasl_check_authz(&mut self) -> Result<()> {
let security = self.server.security_type.borrow_mut();
let mut val: *const c_void = ptr::null_mut();
// SAFETY: sasl_getprop() is C function. It can be ensure
// that security.saslconfig.sasl_conn is not null.
let err = unsafe {
sasl_getprop(
security.saslconfig.sasl_conn,
SASL_USERNAME as c_int,
&mut val,
)
};
drop(security);
if err != SASL_OK {
return Err(anyhow!(VncError::AuthFailed(
"sasl_check_authz".to_string(),
"Cannot fetch SASL username".to_string()
)));
}
if val.is_null() {
return Err(anyhow!(VncError::AuthFailed(
"sasl_check_authz".to_string(),
"No SASL username set".to_string()
)));
}
// SAFETY: It can ensure that the pointer val is not null.
let username = unsafe { CStr::from_ptr(val as *const c_char) };
let username = String::from(username.to_str()?);
let server = self.server.clone();
let security = server.security_type.borrow_mut();
match &security.saslauth {
Some(saslauth) if saslauth.identity == username => Ok(()),
_ => Err(anyhow!(VncError::AuthFailed(
"sasl_check_authz".to_string(),
"No SASL username set".to_string()
))),
}
}
}
/// Auth reject.
fn auth_reject(buf: &mut Vec<u8>) {
let reason = String::from("Authentication failed");
buf.append(&mut (1_u32).to_be_bytes().to_vec());
buf.append(&mut (reason.len() as u32).to_be_bytes().to_vec());
buf.append(&mut reason.as_bytes().to_vec());
}

Комментарий ( 0 )

Вы можете оставить комментарий после Вход в систему

1
https://gitlife.ru/oschina-mirror/openeuler-stratovirt.git
git@gitlife.ru:oschina-mirror/openeuler-stratovirt.git
oschina-mirror
openeuler-stratovirt
openeuler-stratovirt
master