-
Notifications
You must be signed in to change notification settings - Fork 15
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Ntlm fixes for disabled sealing and signing #182
Conversation
pauldumais
commented
Oct 26, 2023
- NTLM flags now allow to disable signing and sealing
- MIC is now properly generated if key exchange is disabled (such as the case of disabled sealing)
- NTLM DecryptMessage now supports STREAM decoding
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don’t understand most of it, but I left a few comments FYI. Feel free to ignore the nits.
let session_key = OsRng.gen::<[u8; SESSION_KEY_SIZE]>(); | ||
let mut session_key = OsRng.gen::<[u8; SESSION_KEY_SIZE]>(); | ||
let encrypted_session_key_vec = Rc4::new(&key_exchange_key).process(session_key.as_ref()); | ||
let mut encrypted_session_key = [0x00; ENCRYPTED_RANDOM_SESSION_KEY_SIZE]; | ||
encrypted_session_key.clone_from_slice(encrypted_session_key_vec.as_ref()); | ||
|
||
context.flags = get_flags(context.flags, credentials); | ||
context.flags = get_flags(context, credentials); | ||
|
||
if !context.flags.contains(NegotiateFlags::NTLM_SSP_NEGOTIATE_KEY_EXCH) { | ||
session_key = key_exchange_key; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: I understand that we re-use key_exchange_key
unless the NTLM_SSP_NEGOTIATE_KEY_EXCH
flag is set, in which case we generate a different session key. Instead of generating a session key that we never use and use a mutable binding, you could do that:
let session_key = if context.flags.contains(NegotiateFlags::NTLM_SSP_NEGOTIATE_KEY_EXCH) {
OsRng.gen::<[u8; SESSION_KEY_SIZE]>()
} else {
key_exchange_key
};
(if blocks are expressions, and this is more idiomatic)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok
src/ntlm/mod.rs
Outdated
let signature = encrypted[0..16].to_vec(); | ||
let enc = encrypted[16..].to_vec(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: do you know if the "invariant" buffer.len() > 16
is checked somewhere? Inside SecurityBuffer
perhaps? Just being cautious, because in Rust, when the index is out of bounds, such indexing, instead of corrupting the memory, will stop the program by panicking (similar to C++ stack unwinding when an exception is raised). However, panicking (or unwinding) across an FFI boundary is undefined behavior. (There is a working group on that matter, but as of today it’s undefined.)
One avenue is to use the get
method for a checked access which returns Option<&[u8]>
:
let signature = encrypted.get(0..16).ok_or_else(|| /* construct error value */)?;
let enc = &encrypted[16..]; // the second one is fine, because of the above
Another avenue is to perform an upfront check like so:
// Upfront check
if encrypted.len() < 16 {
return Err(/* error value */);
}
// Split the slice in two using `split_at`
let (signature, enc) = encrypted.split_at(16);
Mostly a matter of preference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perfect!
src/ntlm/mod.rs
Outdated
let signature = encrypted[0..16].to_vec(); | ||
let enc = encrypted[16..].to_vec(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: as far as I can see in the rest of the code, there is no need to use .to_vec()
:
let signature = encrypted[0..16].to_vec(); | |
let enc = encrypted[16..].to_vec(); | |
let signature = &encrypted[0..16]; | |
let enc = &encrypted[16..]; |
(and remove the .as_slice()
below)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
let mut encrypted = if let Ok(buffer) = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token) { | ||
buffer | ||
} else { | ||
SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Stream)? | ||
} | ||
.buffer | ||
.clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: as far as I know, it’s more idiomatic to write this:
let mut encrypted = if let Ok(buffer) = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token) { | |
buffer | |
} else { | |
SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Stream)? | |
} | |
.buffer | |
.clone(); | |
let security_buffer = let Ok(buffer) = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token) { | |
buffer | |
} else { | |
SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Stream)? | |
}; | |
let encrypted = &security_buffer.buffer; |
Or even better:
let security_buffer = SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Token)
.or_else(|| SecurityBuffer::find_buffer_mut(message, SecurityBufferType::Stream))?;
let encrypted = &security_buffer.buffer;
If I understand correctly, it’s also not necessary to .clone()
the buffer at all.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code was copied from the kerberos code, and the changes proposed do not compile, I will leave it as is since it's only a nit.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Benoît has written all the comments. I have nothing to add
the changes don't look like they'd break other ways to call the same APIs, so I'm good with them. I'll leave the rest for @CBenoit to review in terms of Rust code style :) |