Skip to content

Commit 8c0b18a

Browse files
refactor(ffi): enable lints for unsafe code (#430)
1 parent 6f385b6 commit 8c0b18a

File tree

13 files changed

+1131
-446
lines changed

13 files changed

+1131
-446
lines changed

ffi/src/dpapi/api.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ mod inner {
2020
use dpapi::{CryptProtectSecretArgs, CryptUnprotectSecretArgs, Result};
2121
use dpapi_transport::{ProxyOptions, Transport};
2222
use ffi_types::{Dword, LpByte, LpCStr, LpCUuid, LpDword};
23-
use sspi::network_client::AsyncNetworkClient;
24-
use sspi::{KerberosConfig, Secret};
23+
use sspi::Secret;
2524
use url::Url;
2625
use uuid::Uuid;
2726

ffi/src/dpapi/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@ pub unsafe extern "system" fn DpapiProtectSecret(
182182
return NTE_INTERNAL_ERROR;
183183
}
184184

185-
// SAFETY: Memory allocation should be safe. Moreover, we check for the null value below.
185+
// SAFETY: Memory allocation is safe. Moreover, we check for the null value below.
186186
let blob_buf = unsafe { libc::malloc(blob_data.len()) as *mut u8 };
187187
if blob_buf.is_null() {
188188
error!("Failed to allocate memory for the output DPAPI blob: blob buf pointer is NULL");
@@ -327,7 +327,7 @@ pub unsafe extern "system" fn DpapiUnprotectSecret(
327327
return NTE_INTERNAL_ERROR;
328328
}
329329

330-
// SAFETY: Memory allocation should be safe. Moreover, we check for the null value below.
330+
// SAFETY: Memory allocation is safe. Moreover, we check for the null value below.
331331
let secret_buf = unsafe { libc::malloc(secret_data.as_ref().len()) as *mut u8 };
332332
if secret_buf.is_null() {
333333
error!("Failed to allocate memory for the output DPAPI blob: blob buf pointer is NULL.");

ffi/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#![allow(clippy::missing_safety_doc)]
22
#![allow(clippy::print_stdout)]
33
#![allow(non_snake_case)]
4+
#![deny(unsafe_op_in_unsafe_fn)]
5+
#![warn(clippy::undocumented_unsafe_blocks)]
46

57
#[macro_use]
68
extern crate tracing;
@@ -13,6 +15,4 @@ pub mod logging;
1315
pub mod sspi;
1416
mod utils;
1517
#[cfg(feature = "scard")]
16-
#[deny(unsafe_op_in_unsafe_fn)]
17-
#[warn(clippy::undocumented_unsafe_blocks)]
1818
pub mod winscard;

ffi/src/sspi/common.rs

Lines changed: 140 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ use std::slice::{from_raw_parts, from_raw_parts_mut};
33
use libc::{c_ulonglong, c_void};
44
use num_traits::cast::{FromPrimitive, ToPrimitive};
55
use sspi::{
6-
BufferType, DataRepresentation, DecryptionFlags, EncryptionFlags, ErrorKind, SecurityBuffer, SecurityBufferRef,
7-
SecurityBufferType, ServerRequestFlags, Sspi,
6+
BufferType, DataRepresentation, DecryptionFlags, EncryptionFlags, Error, ErrorKind, SecurityBuffer,
7+
SecurityBufferRef, SecurityBufferType, ServerRequestFlags, Sspi,
88
};
99
#[cfg(windows)]
1010
use symbol_rename_macro::rename_symbol;
@@ -25,10 +25,15 @@ use crate::utils::into_raw_ptr;
2525
pub unsafe extern "system" fn FreeCredentialsHandle(ph_credential: PCredHandle) -> SecurityStatus {
2626
check_null!(ph_credential);
2727

28-
let cred_handle = (*ph_credential).dw_lower as *mut CredentialsHandle;
28+
// SAFETY: `ph_credentials` is not null. We've checked for this above.
29+
let cred_handle = unsafe { (*ph_credential).dw_lower as *mut CredentialsHandle };
2930
check_null!(cred_handle);
3031

31-
let _cred_handle = Box::from_raw(cred_handle);
32+
// SAFETY: `cred_handle` is not null. We've checked for this above.
33+
// We create and allocate credentials handles using `Box::into_raw`. Thus,
34+
// it is safe to deallocate them using `Box::from_raw`.
35+
// The user have to ensure that the credentials handle was created by us.
36+
let _cred_handle = unsafe { Box::from_raw(cred_handle) };
3237

3338
0
3439
}
@@ -58,25 +63,40 @@ pub unsafe extern "system" fn AcceptSecurityContext(
5863
check_null!(p_output);
5964
check_null!(pf_context_attr);
6065

61-
let credentials_handle = (*ph_credential).dw_lower as *mut CredentialsHandle;
66+
// SAFETY: `ph_credentials` is not null. We've checked for this above.
67+
let credentials_handle = unsafe { (*ph_credential).dw_lower as *mut CredentialsHandle };
6268

63-
let (auth_data, security_package_name, attributes) = match transform_credentials_handle(credentials_handle) {
64-
Some(data) => data,
65-
None => return ErrorKind::InvalidHandle.to_u32().unwrap(),
69+
// SAFETY: It's safe to call the function, because it has internal null check and proper error handling.
70+
let (auth_data, security_package_name, attributes) = unsafe {
71+
match transform_credentials_handle(credentials_handle) {
72+
Some(data) => data,
73+
None => return ErrorKind::InvalidHandle.to_u32().unwrap(),
74+
}
6675
};
6776

68-
let sspi_context_ptr = try_execute!(p_ctxt_handle_to_sspi_context(
77+
// SAFETY: It's safe to call the function, because:
78+
// *`ph_context` can be null;
79+
// * the value behind `ph_context` must be initialized by ourself: the user does not have to create the [CtxHandle] values ​​themselves.
80+
// * other parameters are type checked.
81+
let mut sspi_context_ptr = try_execute!(unsafe { p_ctxt_handle_to_sspi_context(
6982
&mut ph_context,
7083
Some(security_package_name),
7184
attributes,
72-
));
73-
74-
let sspi_context = sspi_context_ptr
75-
.as_mut()
76-
.expect("security context pointer cannot be null");
77-
78-
let mut input_tokens =
79-
p_sec_buffers_to_security_buffers(from_raw_parts((*p_input).p_buffers, (*p_input).c_buffers as usize));
85+
)});
86+
87+
// SAFETY: It's safe to call the `as_mut` function, because `sspi_context_ptr` is a local pointer,
88+
// which is initialized by the `p_ctx_handle_to_sspi_context` function. Thus, the value behind this pointer is valid.
89+
let sspi_context = unsafe { sspi_context_ptr.as_mut() };
90+
91+
// SAFETY: `p_input` is not null. We've checked for this above. Additionally, we check `p_buffers` for null.
92+
// All other guarantees must be provided by user.
93+
let mut input_tokens = try_execute!(unsafe {
94+
if (*p_input).p_buffers.is_null() {
95+
Err(Error::new(ErrorKind::InvalidParameter, "p_buffers cannot be null"))
96+
} else {
97+
Ok(p_sec_buffers_to_security_buffers(from_raw_parts((*p_input).p_buffers, (*p_input).c_buffers as usize)))
98+
}
99+
});
80100

81101
let mut output_tokens = vec![SecurityBuffer::new(Vec::with_capacity(1024), BufferType::Token)];
82102

@@ -88,12 +108,18 @@ pub unsafe extern "system" fn AcceptSecurityContext(
88108
.with_output(&mut output_tokens)
89109
.execute(sspi_context);
90110

91-
copy_to_c_sec_buffer((*p_output).p_buffers, &output_tokens, false);
111+
// SAFETY: `p_output` is not null. We've checked this above.
112+
try_execute!(unsafe { copy_to_c_sec_buffer((*p_output).p_buffers, &output_tokens, false) });
92113

93-
(*ph_new_context).dw_lower = sspi_context_ptr as c_ulonglong;
94-
(*ph_new_context).dw_upper = into_raw_ptr(security_package_name.to_owned()) as c_ulonglong;
114+
// SAFETY: `ph_new_context` is not null. We've checked this above.
115+
let ph_new_context = unsafe { ph_new_context.as_mut() }.expect("ph_new_context should not be null");
95116

96-
*pf_context_attr = f_context_req;
117+
ph_new_context.dw_lower = sspi_context_ptr.as_ptr() as c_ulonglong;
118+
ph_new_context.dw_upper = into_raw_ptr(security_package_name.to_owned()) as c_ulonglong;
119+
// SAFETY: `pf_context_attr` is not null. We've checked this above.
120+
unsafe {
121+
*pf_context_attr = f_context_req;
122+
}
97123

98124
let result = try_execute!(result_status);
99125
result.status.to_u32().unwrap()
@@ -123,16 +149,27 @@ pub unsafe extern "system" fn CompleteAuthToken(
123149
check_null!(ph_context);
124150
check_null!(p_token);
125151

126-
let sspi_context = try_execute!(p_ctxt_handle_to_sspi_context(
152+
// SAFETY: `p_token` is not null. We've checked this above.
153+
unsafe { check_null!((*p_token).p_buffers); }
154+
155+
// SAFETY: It's safe to call the function, because:
156+
// *`ph_context` can be null;
157+
// * the value behind `ph_context` must be initialized by ourself: the user does not have to create the [CtxHandle] values ​​themselves.
158+
// * other parameters are type checked.
159+
let mut sspi_context_ptr = try_execute!(unsafe { p_ctxt_handle_to_sspi_context(
127160
&mut ph_context,
128161
None,
129162
&CredentialsAttributes::default()
130-
))
131-
.as_mut()
132-
.expect("security context pointer cannot be null");
163+
)});
133164

134-
let raw_buffers = from_raw_parts((*p_token).p_buffers, (*p_token).c_buffers as usize);
135-
let mut buffers = p_sec_buffers_to_security_buffers(raw_buffers);
165+
// SAFETY: It's safe to call the `as_mut` function, because `sspi_context_ptr` is a local pointer,
166+
// which is initialized by the `p_ctx_handle_to_sspi_context` function. Thus, the value behind this pointer is valid.
167+
let sspi_context = unsafe { sspi_context_ptr.as_mut() };
168+
169+
// SAFETY: This function is safe to call because `p_buffers` is not null. We've checked this above.
170+
let raw_buffers = unsafe { from_raw_parts((*p_token).p_buffers, (*p_token).c_buffers as usize) };
171+
// SAFETY: This function is safe to call because `raw_buffers` is type checked. All other guarantees must be provided by user.
172+
let mut buffers = unsafe { p_sec_buffers_to_security_buffers(raw_buffers) };
136173

137174
sspi_context.complete_auth_token(&mut buffers).map_or_else(
138175
|err| err.error_type.to_u32().unwrap(),
@@ -150,14 +187,29 @@ pub unsafe extern "system" fn DeleteSecurityContext(mut ph_context: PCtxtHandle)
150187
catch_panic!(
151188
check_null!(ph_context);
152189

153-
let _context: Box<SspiHandle> = Box::from_raw(try_execute!(p_ctxt_handle_to_sspi_context(
190+
// SAFETY: It's safe to call the function, because:
191+
// * the value behind `ph_context` must be initialized by ourself: the user does not have to create the [CtxHandle] values ​​themselves.
192+
// * other parameters are type checked.
193+
let mut sspi_context_ptr = try_execute!(unsafe { p_ctxt_handle_to_sspi_context(
154194
&mut ph_context,
155195
None,
156196
&CredentialsAttributes::default()
157-
)));
197+
)});
198+
199+
// SAFETY: It's safe to constructs a box from a raw pointer because:
200+
// * the `sspi_context_ptr` is not null;
201+
// * the value behind `sspi_context_ptr` must be initialized by ourself: the user does not have to create the [CtxHandle] values ​​themselves.
202+
let _context: Box<SspiHandle> = unsafe {
203+
Box::from_raw(sspi_context_ptr.as_mut())
204+
};
158205

159-
if (*ph_context).dw_upper != 0 {
160-
let _name: Box<String> = Box::from_raw((*ph_context).dw_upper as *mut String);
206+
// SAFETY: `ph_context` is not null. We've checked for it above.
207+
let dw_upper = unsafe { (*ph_context).dw_upper };
208+
if dw_upper != 0 {
209+
// SAFETY: It's safe to constructs a box from a raw pointer because:
210+
// * the `dw_upper` is not equal to zero;
211+
// * the value behind `dw_upper` pointer must be initialized by ourself: the user does not have to create the [CtxHandle] values ​​themselves.
212+
let _name: Box<String> = unsafe { Box::from_raw(dw_upper as *mut String) };
161213
}
162214

163215
0
@@ -226,7 +278,12 @@ pub type VerifySignatureFn = extern "system" fn(PCtxtHandle, PSecBufferDesc, u32
226278
#[no_mangle]
227279
pub unsafe extern "system" fn FreeContextBuffer(pv_context_buffer: *mut c_void) -> SecurityStatus {
228280
// NOTE: see https://github.com/Devolutions/sspi-rs/pull/141 for rationale behind libc usage.
229-
libc::free(pv_context_buffer);
281+
// SAFETY: Memory deallocation is safe.
282+
// The user must call this function to free buffers allocated by ourself. On our side, we always use `malloc`
283+
// to allocate buffers in in FFI.
284+
unsafe {
285+
libc::free(pv_context_buffer);
286+
}
230287

231288
0
232289
}
@@ -270,25 +327,43 @@ pub unsafe extern "system" fn EncryptMessage(
270327
check_null!(ph_context);
271328
check_null!(p_message);
272329

273-
let sspi_context = try_execute!(p_ctxt_handle_to_sspi_context(
330+
// SAFETY: `p_message` is not null. We've checked this above.
331+
unsafe { check_null!((*p_message).p_buffers); }
332+
333+
// SAFETY: It's safe to call the function, because:
334+
// *`ph_context` can be null;
335+
// * the value behind `ph_context` must be initialized by ourself: the user does not have to create the [CtxHandle] values ​​themselves.
336+
// * other parameters are type checked.
337+
let mut sspi_context_ptr = try_execute!(unsafe { p_ctxt_handle_to_sspi_context(
274338
&mut ph_context,
275339
None,
276340
&CredentialsAttributes::default()
277-
))
278-
.as_mut()
279-
.expect("security context pointer cannot be null");
341+
)});
342+
343+
// SAFETY: It's safe to call the `as_mut` function, because `sspi_context_ptr` is a local pointer,
344+
// which is initialized by the `p_ctx_handle_to_sspi_context` function. Thus, the value behind this pointer is valid.
345+
let sspi_context = unsafe { sspi_context_ptr.as_mut() };
280346

281-
let len = (*p_message).c_buffers as usize;
282-
let raw_buffers = from_raw_parts((*p_message).p_buffers, len);
283-
let mut message = try_execute!(p_sec_buffers_to_decrypt_buffers(raw_buffers));
347+
// SAFETY: `p_message` is not null. We've checked this above.
348+
let len = unsafe { (*p_message).c_buffers as usize };
349+
350+
// SAFETY: `p_message` is not null. We've checked this above. Moreover, we've checked `p_buffers` for null above.
351+
let raw_buffers = unsafe {
352+
from_raw_parts((*p_message).p_buffers, len)
353+
};
354+
355+
// SAFETY: The user must provide guarantees about the correctness of buffers in `raw_buffers'.
356+
let mut message = try_execute!(unsafe { p_sec_buffers_to_decrypt_buffers(raw_buffers)});
284357

285358
let result_status = sspi_context.encrypt_message(
286359
EncryptionFlags::from_bits(f_qop.try_into().unwrap()).unwrap(),
287360
&mut message,
288361
message_seq_no.try_into().unwrap(),
289362
);
290363

291-
try_execute!(copy_decrypted_buffers((*p_message).p_buffers, message));
364+
// SAFETY: `p_message` and `p_buffers` are not null. We've checked this above.
365+
// All other guarantees must be provided by user.
366+
try_execute!(unsafe { copy_decrypted_buffers((*p_message).p_buffers, message) });
292367

293368
let result = try_execute!(result_status);
294369
result.to_u32().unwrap()
@@ -311,28 +386,44 @@ pub unsafe extern "system" fn DecryptMessage(
311386
check_null!(ph_context);
312387
check_null!(p_message);
313388

314-
let sspi_context = try_execute!(p_ctxt_handle_to_sspi_context(
389+
// SAFETY: `p_message` is not null. We've checked this above.
390+
unsafe { check_null!((*p_message).p_buffers); }
391+
392+
// SAFETY: It's safe to call the function, because:
393+
// *`ph_context` can be null;
394+
// * the value behind `ph_context` must be initialized by ourself: the user does not have to create the [CtxHandle] values ​​themselves.
395+
// * other parameters are type checked.
396+
let mut sspi_context_ptr = try_execute!(unsafe { p_ctxt_handle_to_sspi_context(
315397
&mut ph_context,
316398
None,
317399
&CredentialsAttributes::default()
318-
))
319-
.as_mut()
320-
.expect("security context pointer cannot be null");
400+
)});
401+
402+
// SAFETY: It's safe to call the `as_mut` function, because `sspi_context_ptr` is a local pointer,
403+
// which is initialized by the `p_ctx_handle_to_sspi_context` function. Thus, the value behind this pointer is valid.
404+
let sspi_context = unsafe { sspi_context_ptr.as_mut() };
321405

322-
let len = (*p_message).c_buffers as usize;
323-
let raw_buffers = from_raw_parts((*p_message).p_buffers, len);
324-
let mut message = try_execute!(p_sec_buffers_to_decrypt_buffers(raw_buffers));
406+
// SAFETY: `p_message` is not null. We've checked this above.
407+
let len = unsafe { (*p_message).c_buffers as usize };
408+
// SAFETY: `p_message` and `p_buffers` is not null. We've checked this above.
409+
let raw_buffers = unsafe { from_raw_parts((*p_message).p_buffers, len) };
410+
// SAFETY: The user must provide guarantees about the correctness of buffers in `raw_buffers'.
411+
let mut message = try_execute!(unsafe { p_sec_buffers_to_decrypt_buffers(raw_buffers) });
325412

326413
let (decryption_flags, result_status) =
327414
match sspi_context.decrypt_message(&mut message, message_seq_no.try_into().unwrap()) {
328415
Ok(flags) => (flags, Ok(())),
329416
Err(error) => (DecryptionFlags::empty(), Err(error)),
330417
};
331418

332-
try_execute!(copy_decrypted_buffers((*p_message).p_buffers, message));
419+
// SAFETY: `p_message` and `p_buffers` is not null. We've checked this above.
420+
// All other guarantees must be provided by user.
421+
try_execute!(unsafe { copy_decrypted_buffers((*p_message).p_buffers, message) });
333422
// `pf_qop` can be null if this library is used as a CredSsp security package
334423
if !pf_qop.is_null() {
335-
*pf_qop = decryption_flags.bits().try_into().unwrap();
424+
let flags = try_execute!(decryption_flags.bits().try_into(), ErrorKind::InternalError);
425+
// SAFETY: `pf_qop` is not null. We've checked this above.
426+
unsafe { *pf_qop = flags };
336427
}
337428

338429
try_execute!(result_status);

0 commit comments

Comments
 (0)