Skip to content

Commit 23658ee

Browse files
committed
Merge master
2 parents 46a4fc4 + 686af8c commit 23658ee

File tree

48 files changed

+1478
-727
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1478
-727
lines changed

Cargo.lock

Lines changed: 277 additions & 285 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@ lto = true
3030
strip = "symbols"
3131

3232
[patch.crates-io]
33-
libsql = { git = "https://github.com/allan2/libsql", rev = "9364e90" }
3433
tracing-appender = { git = "https://github.com/CBenoit/tracing.git", rev = "42097daf92e683cf18da7639ddccb056721a796c" }
3534

3635
[workspace.lints.rust]

crates/devolutions-pedm-shell-ext/src/lib_win.rs

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use devolutions_pedm_shared::client::{self};
1212
use devolutions_pedm_shared::desktop;
1313
use parking_lot::{Mutex, RwLock};
1414
use tokio::sync::mpsc::{self, Receiver, Sender};
15+
use win_api_wrappers::fs::get_system32_path;
1516
use win_api_wrappers::process::{Module, Process};
1617
use win_api_wrappers::raw::core::{
1718
implement, interface, Error, IUnknown, IUnknown_Vtbl, Interface, Result, GUID, HRESULT, PWSTR,
@@ -218,6 +219,32 @@ fn find_main_explorer(session: u32) -> Option<u32> {
218219
})
219220
}
220221

222+
fn resolve_msi(path: &Path) -> Option<LaunchPayload> {
223+
if !matches!(path.extension().and_then(|e| e.to_str()), Some(ext) if ext.eq_ignore_ascii_case("msi")) {
224+
return None;
225+
}
226+
227+
let system32 = get_system32_path().ok()?;
228+
let msiexec_path = Path::new(&system32).join("msiexec.exe");
229+
230+
let environment = environment_block(None, false).ok()?;
231+
232+
let exe_path = expand_environment_path(&msiexec_path, &environment).ok()?;
233+
234+
// By inspecting elevated .msi files launched from Explorer, we see that Explorer invokes %systemroot%\system32\msiexec,
235+
// with the command line "%systemroot%\system32\msiexec" /i "{path-to-msi}".
236+
// We achieve the same in the PEDM module by using the same command if the file extension is .msi.
237+
// The .msi extension is already being trapped by the shell extension, but previously we would call
238+
// CreateProcess on the .msi causing "file is not a valid Win32 executable".
239+
Some(LaunchPayload {
240+
executable_path: exe_path.as_os_str().to_str().map(str::to_owned),
241+
command_line: Some(format!("\"{}\" /i \"{}\"", exe_path.display(), path.display())),
242+
working_directory: None,
243+
creation_flags: 0,
244+
startup_info: None,
245+
})
246+
}
247+
221248
fn resolve_lnk(path: &Path) -> Option<LaunchPayload> {
222249
let link = Link::new(path);
223250

@@ -253,13 +280,16 @@ fn start_listener() {
253280
match command {
254281
ChannelCommand::Exit => break,
255282
ChannelCommand::Elevate(path) => {
256-
let mut payload = resolve_lnk(&path).unwrap_or_else(|| LaunchPayload {
257-
executable_path: path.as_os_str().to_str().map(str::to_owned),
258-
command_line: None,
259-
working_directory: None,
260-
creation_flags: 0,
261-
startup_info: None,
262-
});
283+
let mut payload =
284+
resolve_lnk(&path)
285+
.or_else(|| resolve_msi(&path))
286+
.unwrap_or_else(|| LaunchPayload {
287+
executable_path: path.as_os_str().to_str().map(str::to_owned),
288+
command_line: None,
289+
working_directory: None,
290+
creation_flags: 0,
291+
startup_info: None,
292+
});
263293

264294
payload.startup_info = Some(StartupInfoDto {
265295
parent_pid: find_main_explorer(

crates/devolutions-pedm/src/api/launch.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ use win_api_wrappers::thread::{ThreadAttributeList, ThreadAttributeType};
1919
use win_api_wrappers::token::Token;
2020
use win_api_wrappers::utils::{environment_block, expand_environment_path, CommandLine, WideString};
2121

22+
use crate::api::state::AppState;
23+
use crate::db::DbHandle;
2224
use crate::elevator;
2325
use crate::error::Error;
2426
use crate::policy::Policy;
@@ -87,7 +89,7 @@ fn win_canonicalize(path: &Path, token: Option<&Token>) -> Result<PathBuf, Error
8789

8890
pub(crate) async fn post_launch(
8991
Extension(named_pipe_info): Extension<NamedPipeConnectInfo>,
90-
NoApi(State(policy)): NoApi<State<Arc<RwLock<Policy>>>>,
92+
NoApi(State(state)): NoApi<State<AppState>>,
9193
Json(mut payload): Json<LaunchPayload>,
9294
) -> Result<Json<LaunchResponse>, Error> {
9395
payload.executable_path = payload
@@ -133,7 +135,8 @@ pub(crate) async fn post_launch(
133135
startup_info.attribute_list = Some(Some(attributes.raw()));
134136

135137
let proc_info = elevator::try_start_elevated(
136-
&policy,
138+
&state.db_handle,
139+
&state.policy,
137140
&named_pipe_info.token,
138141
parent_pid,
139142
payload.executable_path.as_deref(),

crates/devolutions-pedm/src/api/mod.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use tower_http::timeout::TimeoutLayer;
2121
use tower_service::Service;
2222
use tracing::{error, info};
2323

24+
use devolutions_gateway_task::ShutdownSignal;
2425
use devolutions_pedm_shared::policy::User;
2526
use win_api_wrappers::handle::Handle;
2627
use win_api_wrappers::identity::sid::Sid;
@@ -34,7 +35,7 @@ use win_api_wrappers::utils::Pipe;
3435

3536
use crate::account::{diff_accounts, list_accounts, ListAccountsError};
3637
use crate::config::Config;
37-
use crate::db::{Db, DbError, InitSchemaError};
38+
use crate::db::{Db, DbAsyncBridgeTask, DbError, InitSchemaError};
3839
use crate::error::{Error, ErrorResponse};
3940
use crate::utils::AccountExt;
4041

@@ -173,10 +174,11 @@ async fn health_check() -> &'static str {
173174
}
174175

175176
/// Initializes the appliation and starts the named pipe server.
176-
pub async fn serve(config: Config) -> Result<(), ServeError> {
177+
pub async fn serve(config: Config, shutdown_signal: ShutdownSignal) -> Result<(), ServeError> {
177178
let db = Db::new(&config).await?;
178179
db.setup().await?;
179180

181+
<<<<<<< HEAD
180182
// Update the list of accounts in the database.
181183

182184
// SAFETY: uses `NetUserEnum` and `LookupAccountNameW` from `windows`
@@ -188,6 +190,12 @@ pub async fn serve(config: Config) -> Result<(), ServeError> {
188190
db.update_accounts(&diff).await?;
189191

190192
let state = AppState::new(db, &config.pipe_name).await?;
193+
=======
194+
let (db_handle, db_async_bridge_task) = DbAsyncBridgeTask::new(db.clone());
195+
let _db_async_bridge_task = devolutions_gateway_task::spawn_task(db_async_bridge_task, shutdown_signal);
196+
197+
let state = AppState::new(db, db_handle, &config.pipe_name).await?;
198+
>>>>>>> origin/master
191199

192200
// a plain Axum router
193201
let hello_router = Router::new().route("/health", axum::routing::get(health_check));

crates/devolutions-pedm/src/api/state.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use chrono::Utc;
88
use hyper::StatusCode;
99
use parking_lot::RwLock;
1010

11-
use crate::db::{Database, Db, DbError};
11+
use crate::db::{Database, Db, DbError, DbHandle};
1212
use crate::model::StartupInfo;
1313
use crate::policy::{LoadPolicyError, Policy};
1414

@@ -23,11 +23,12 @@ pub(crate) struct AppState {
2323
/// TODO: implement a check to ensure that there is only one PEDM instance running at any given time
2424
pub(crate) req_counter: Arc<AtomicI32>,
2525
pub(crate) db: Arc<dyn Database + Send + Sync>,
26+
pub(crate) db_handle: DbHandle,
2627
pub(crate) policy: Arc<RwLock<Policy>>,
2728
}
2829

2930
impl AppState {
30-
pub(crate) async fn new(db: Db, pipe_name: &str) -> Result<Self, AppStateError> {
31+
pub(crate) async fn new(db: Db, db_handle: DbHandle, pipe_name: &str) -> Result<Self, AppStateError> {
3132
let policy = Policy::load()?;
3233

3334
let last_req_id = db.get_last_request_id().await?;
@@ -44,6 +45,7 @@ impl AppState {
4445
startup_info,
4546
req_counter: Arc::new(AtomicI32::new(last_req_id)),
4647
db: db.0,
48+
db_handle,
4749
policy: Arc::new(RwLock::new(policy)),
4850
})
4951
}

crates/devolutions-pedm/src/db/libsql.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,16 @@ use std::ops::Deref;
88

99
use async_trait::async_trait;
1010
use chrono::{DateTime, Utc};
11+
<<<<<<< HEAD
1112
use futures_util::{StreamExt, TryStreamExt};
1213
use libsql::params::IntoParams;
1314
use libsql::{params, Row, Value};
1415
use tracing::info;
16+
=======
17+
use devolutions_pedm_shared::policy::ElevationResult;
18+
use libsql::params::IntoParams;
19+
use libsql::{params, Row};
20+
>>>>>>> origin/master
1521

1622
use crate::account::{AccountWithId, AccountsDiff, DomainId, Sid};
1723

@@ -314,6 +320,11 @@ ORDER BY n.name";
314320
.await?;
315321
Ok(())
316322
}
323+
324+
async fn insert_jit_elevation_result(&self, result: &ElevationResult) -> Result<(), DbError> {
325+
// TODO: execute the SQL query.
326+
Ok(())
327+
}
317328
}
318329

319330
/// Converts a timestamp in microseconds to a `DateTime<Utc>`.

crates/devolutions-pedm/src/db/mod.rs

Lines changed: 109 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ use std::sync::Arc;
88

99
use async_trait::async_trait;
1010
use chrono::{DateTime, Utc};
11-
use tracing::info;
11+
use devolutions_gateway_task::{ShutdownSignal, Task};
12+
use devolutions_pedm_shared::policy::ElevationResult;
13+
use tracing::{info, warn};
1214

1315
mod err;
1416
mod util;
@@ -209,4 +211,110 @@ pub(crate) trait Database: Send + Sync {
209211
async fn update_accounts(&self, diff: &AccountsDiff) -> Result<(), DbError>;
210212

211213
async fn insert_elevate_tmp_request(&self, req_id: i32, seconds: i32) -> Result<(), DbError>;
214+
215+
async fn insert_jit_elevation_result(&self, result: &ElevationResult) -> Result<(), DbError>;
216+
}
217+
218+
// Bridge for DB operations from synchronous functions.
219+
// This may or may not be a temporary workaround.
220+
221+
pub(crate) struct DbHandleError<T> {
222+
pub(crate) db_error: Option<DbError>,
223+
pub(crate) value: T,
224+
}
225+
226+
#[derive(Clone)]
227+
pub(crate) struct DbHandle {
228+
tx: tokio::sync::mpsc::Sender<DbRequest>,
229+
}
230+
231+
impl DbHandle {
232+
pub(crate) fn insert_jit_elevation_result(
233+
&self,
234+
result: ElevationResult,
235+
) -> Result<(), DbHandleError<ElevationResult>> {
236+
let (tx, rx) = tokio::sync::oneshot::channel();
237+
238+
match self
239+
.tx
240+
.blocking_send(DbRequest::InsertJitElevationResult { result, tx })
241+
{
242+
Ok(()) => match rx.blocking_recv() {
243+
Ok(db_result) => db_result,
244+
Err(_) => {
245+
warn!("Did not receive the response from the async bridge task");
246+
Ok(())
247+
}
248+
},
249+
Err(error) => {
250+
let DbRequest::InsertJitElevationResult { result, .. } = error.0 else {
251+
unreachable!()
252+
};
253+
254+
Err(DbHandleError {
255+
db_error: None,
256+
value: result,
257+
})
258+
}
259+
}
260+
}
261+
}
262+
263+
pub(crate) enum DbRequest {
264+
InsertJitElevationResult {
265+
result: ElevationResult,
266+
tx: tokio::sync::oneshot::Sender<Result<(), DbHandleError<ElevationResult>>>,
267+
},
268+
}
269+
270+
pub(crate) struct DbAsyncBridgeTask {
271+
db: Db,
272+
rx: tokio::sync::mpsc::Receiver<DbRequest>,
273+
}
274+
275+
impl DbAsyncBridgeTask {
276+
pub fn new(db: Db) -> (DbHandle, Self) {
277+
let (tx, rx) = tokio::sync::mpsc::channel(8);
278+
(DbHandle { tx }, Self { db, rx })
279+
}
280+
}
281+
282+
#[async_trait]
283+
impl Task for DbAsyncBridgeTask {
284+
type Output = anyhow::Result<()>;
285+
286+
const NAME: &'static str = "db-async-bridge";
287+
288+
async fn run(mut self, mut shutdown_signal: ShutdownSignal) -> anyhow::Result<()> {
289+
loop {
290+
tokio::select! {
291+
req = self.rx.recv() => {
292+
let Some(req) = req else {
293+
break;
294+
};
295+
296+
match req {
297+
DbRequest::InsertJitElevationResult { result, tx } => {
298+
match self.db.insert_jit_elevation_result(&result).await {
299+
Ok(()) => {
300+
let _ = tx.send(Ok(()));
301+
}
302+
Err(error) => {
303+
let _ = tx.send(Err(DbHandleError {
304+
db_error: Some(error),
305+
value: result,
306+
}));
307+
}
308+
}
309+
}
310+
}
311+
}
312+
_ = shutdown_signal.wait() => {
313+
break;
314+
}
315+
}
316+
}
317+
318+
Ok(())
319+
}
212320
}

crates/devolutions-pedm/src/db/pg.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ use async_trait::async_trait;
99
use bb8::Pool;
1010
use bb8_postgres::PostgresConnectionManager;
1111
use chrono::{DateTime, Utc};
12+
<<<<<<< HEAD
1213
use futures_util::try_join;
1314
use tokio_postgres::types::ToSql;
15+
=======
16+
use devolutions_pedm_shared::policy::ElevationResult;
17+
>>>>>>> origin/master
1418
use tokio_postgres::NoTls;
1519

1620
use crate::account::{AccountWithId, AccountsDiff, DomainId, Sid};
@@ -328,6 +332,10 @@ ORDER BY n.name",
328332
.await?;
329333
Ok(())
330334
}
335+
336+
async fn insert_jit_elevation_result(&self, result: &ElevationResult) -> Result<(), DbError> {
337+
unimplemented!()
338+
}
331339
}
332340

333341
/// Constructs query args like `($1), ($2), ($3)`.

crates/devolutions-pedm/src/elevator/mod.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use win_api_wrappers::Error;
2222
use local_admin_elevator::LocalAdminElevator;
2323
use virtual_account_elevator::VirtualAccountElevator;
2424

25+
use crate::db::DbHandle;
2526
use crate::log;
2627
use crate::policy::{self, application_from_path, Policy};
2728
use crate::utils::{start_process, AccountExt};
@@ -66,6 +67,7 @@ fn elevate_token(policy: &RwLock<Policy>, token: &Token) -> Result<Token> {
6667
}
6768

6869
fn validate_elevation(
70+
db_handle: &DbHandle,
6971
policy: &RwLock<Policy>,
7072
client_token: &Token,
7173
client_pid: u32,
@@ -104,15 +106,18 @@ fn validate_elevation(
104106

105107
let validation = policy.read().validate(client_token.session_id()?, &req);
106108

107-
log::log_elevation(&ElevationResult {
109+
let elevation_result = ElevationResult {
108110
request: req,
109111
successful: validation.is_ok(),
110-
})?;
112+
};
113+
114+
log::log_elevation(db_handle, elevation_result);
111115

112116
validation
113117
}
114118

115119
pub(crate) fn try_start_elevated(
120+
db_handle: &DbHandle,
116121
policy: &RwLock<Policy>,
117122
client_token: &Token,
118123
client_pid: u32,
@@ -123,6 +128,7 @@ pub(crate) fn try_start_elevated(
123128
startup_info: &mut StartupInfo,
124129
) -> Result<ProcessInformation> {
125130
validate_elevation(
131+
db_handle,
126132
policy,
127133
client_token,
128134
client_pid,

0 commit comments

Comments
 (0)