-
Notifications
You must be signed in to change notification settings - Fork 122
Server sample & mstsc related connection fixes #198
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
Conversation
@elmarco luckily, I have just the right guide and tooling for you to properly decode mstsc traffic in Wireshark! This should help you diagnose the issue quickly: https://github.com/awakecoding/wireshark-rdp |
thanks, unfortunately that didn't help me much :( I am trying to dump ironrdp, on server side (and sfreerdp-server). |
is there a particular reason why the technique where you'd try to dump the SChannel pre-master secrets out of the Windows client machine running mstsc would not be suitable in this case? It's either that, or SSLKEYLOGFILE in the IronRDP server, but as my colleague Benoit pointed out there's a small code modification to be made there, I don't think it's enabled in the server right now. |
(Converted the PR to an draft explicitly, given the current title) |
I can get the decrypted TLS now. However, it doesn't interpret it as RDP. Any idea? |
@elmarco hum... that's odd, normally the RDP dissector kicks in automatically when TLS gets decrypted. This doesn't look like the Windows version of Wireshark, maybe it's an older release on another OS? In any case, if you look at the last packets exchanged, can you spot anything interesting? It may be easier to discuss some of this in the FreeRDP chat, which we use for more than just FreeRDP these days: https://www.freerdp.com/ (see the matrix.org channel, I hang in there from time to time). I cloned your branch, and I'm unsure how to really get started running the sample server. Here's what I did so far:
I see there's a parameter for a server certificate which I didn't pass, but I don't see how I'd be supposed to pass server username+password that the server would use to authenticate the client. If I can reproduce the issue on my side, I could probably get the Wireshark part right |
Wireshark 4.0.8 (Git commit 81696bb74857) (wireshark-4.0.8-2.fc38.x86_64)
I am there too, ok.
There is no user/pass handling atm in ironrdp-server (I suppose it accepts all users): https://github.com/Devolutions/IronRDP/blob/master/crates/ironrdp-acceptor/src/connection.rs#L329 You need to pass cert/keys (I generated mine with something like |
For reference, if I launch it without a certificate, it'll fallback in pre-TLS mode which we're not really supposed to support (although it partially connected, which is surprising?). mstsc shows this warning which is indicative of a pre-TLS connection: I then generated a self-signed RDP server certificate:
Then launched the sample RDP server again:
This time, I got the TLS certificate warning (expected, but confirming we're doing TLS): I hit a protocol error in mstsc, and the IronRDP server shows the following errors:
I'll work on getting the traffic decrypted now. I'm still curious about where the credentials come into play, have you been working with RDP NLA, or TLS without NLA, which delegates the credentials later during the connection sequence? |
Yep, same error here. nice! :-) |
Hi @elmarco! |
7a4aa8b
to
175d7b7
Compare
@@ -262,6 +262,7 @@ pub enum ShareDataPdu { | |||
FrameAcknowledge(FrameAcknowledgePdu), | |||
ServerSetErrorInfo(ServerSetErrorInfoPdu), | |||
Input(InputEventPdu), | |||
ShutdownRequest, |
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.
Nice! You don’t have to address the client-side as part of this PR, but it’s also moving forward this other issue: #115
gcc_blocks_buffer_length as u16 + CONFERENCE_RESPONSE_CONNECT_PDU_SIZE, | ||
gcc_blocks_buffer_length as u16 + CONFERENCE_RESPONSE_CONNECT_PDU_SIZE + 1, |
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.
Good catch. I think this fix will break some tests. FYI a lot of tests in ironrdp-pdu are very old and I wouldn’t be surprised if some of them are snapshots generated using IronRDP itself (i.e.: not official samples from the documentation or extracted from an actual RDP session)
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 am not sure why WireShark complains about this field though.. I tried to read their code, but it's not obvious :) anyway the field is marked as being ignored in RDP spec, so I guess we can just add +1 to make WS happy.
175d7b7
to
8ff6bc6
Compare
FYI, the coverage workflow is now "fixed" for forks on master: a4fee87 |
8ff6bc6
to
21b8fa2
Compare
fe4ace9
to
ca4e3d3
Compare
Even when the server doesn't advertize its support, msctc sends this. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Even when the server doesn't advertize its support, msctc sends this. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
To ease debugging scenario. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
39e279a
to
d851dd5
Compare
Support for other channel messages is currently lacking. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Used by the next patch during server negotiation. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Only accept channels we have attached. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Those flags are set both by client and server. Even if the server doesn't send those commands yet, this should be supported by most clients. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
It's needed for bitmap & surface commands etc. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
mstsc does not display bitmap update, for some reason. Using SetSurfaceBits instead solves it, and is required for more advanced codecs. Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
d851dd5
to
cbc682e
Compare
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.
LGTM! No blocker, I’ll merge and help with the follow up in a subsequent PR.
Thank you a lot for moving forward this!!
} | ||
|
||
fn to_buffer(&self, mut stream: impl io::Write) -> Result<(), Self::Error> { | ||
stream.write_u8(self.areas_to_refresh.len() as u8)?; |
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.
Issue (non-blocking): cast_length!
macro could be used to gracefully handle self.areas_to_refresh.len()
being too big to be converted into a u8
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 was wondering. I think there are a couple of other places that could also use it then.
@@ -74,6 +75,8 @@ fn virtual_channel_capabilities() -> capability_sets::VirtualChannel { | |||
|
|||
fn multifragment_update() -> capability_sets::MultifragmentUpdate { | |||
capability_sets::MultifragmentUpdate { | |||
max_request_size: u32::MAX, | |||
// FIXME: use an acceptable value for msctc. |
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.
Nitpick: add the issue number for easier cross-referencing
// FIXME: use an acceptable value for msctc. | |
// FIXME(#318): use an acceptable value for msctc. |
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.
right
let mut buffer = vec![0u8; 4096]; | ||
let mut encoder = UpdateEncoder::new(); | ||
debug!("Starting client loop"); | ||
self.io_channel_id = Some(result.io_channel_id); |
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.
Issue (non-blocking): we never handle more than one client at a time, so there is no actual problem, but I feel mixed about this approach of updating a global state (as far as the server is concerned) for something which is only relevant for the current client. This could lead to confusion, especially since this state isn't reset after client disconnection, leaving a redundant "dummy" value.
Thought: that being said, we know it’s technically always the same value, so the "dummy" value will always be the "good" value.
Suggestion: two avenues:
- Remove the
io_channel_id
fromRdpServer
: it’s possible to just keep the value in the function of the body and to give it tohandle_x224
- Go the "constant" route completely. I directed otherwise in a previous comment, but on second thought it was not bad all things considered. The only request I have is to keep things consistent, i.e., use the constant in the connector itself too (previously the approach was mixed).
if Some(data.channel_id) == self.io_channel_id && self.handle_io_channel_data(data).await? { | ||
return Ok(true); | ||
} |
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.
Suggestion: a more idiomatic approach would be to call handle_io_channel_data
inside the if body and avoid nesting control flow inside the condition expression itself.
if Some(data.channel_id) == self.io_channel_id && self.handle_io_channel_data(data).await? { | |
return Ok(true); | |
} | |
if Some(data.channel_id) == self.io_channel_id { | |
return self.handle_io_channel_data(data).await; | |
} |
} | ||
|
||
#[derive(Debug)] | ||
struct InnerHandler {} |
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.
Issue (non-blocking): this appears to be dead code. Strangely enough, the lint is not fired.
thanks a lot, btw are you on #ironrdp on matrix? |
Just joined the room! |
This is WIP branch to add a simple server, so we can further debug issues more easily.
In particular mstsc fails to connect succesfully, and I am having trouble to debug the issue. Notably, not being able to setup wireshark to decode the protocol...
Help welcome!