-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathclient.rs
180 lines (131 loc) · 6.11 KB
/
client.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
// Based on the SSPI client example from MSDN: https://docs.microsoft.com/en-us/windows/win32/secauthn/using-sspi-with-a-windows-sockets-client
// This example works with the server example using SSPI. The client and server examples are designed to work together.
// This example demonstrates initializing an authenticating SSPI session with the NTLM, connecting with a server, establishing
// a secure communication session, receiving and decrypting a message from the server within the secure session.
use std::io;
use std::net::TcpStream;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
use sspi::{
AuthIdentity, BufferType, ClientRequestFlags, CredentialUse, DataRepresentation, Ntlm, SecurityBuffer,
SecurityBufferRef, SecurityStatus, Sspi, SspiImpl, Username,
};
const IP: &str = "127.0.0.1:8080";
fn main() -> Result<(), io::Error> {
let mut stream = TcpStream::connect(IP).expect("Failed to connect to the server");
println!("Connected to the server.");
let mut ntlm = Ntlm::new();
let account_name = whoami::username();
let computer_name = whoami::fallible::hostname().unwrap();
let username =
Username::new(&account_name, Some(&computer_name)).map_err(|e| io::Error::new(io::ErrorKind::Other, e))?;
let identity = AuthIdentity {
username,
password: String::from("password").into(),
};
do_authentication(&mut ntlm, &identity, &mut stream).expect("Failed to authenticate connection");
println!("Authenticated!");
let mut trailer = Vec::new();
// By agreement, the server encrypted the message and set the size
// of the trailer block to be just what it needed. decrypt_message
// needs the size of the trailer block.
//
// By agreement, the server placed the trailer at the beginning
// of the message, and the data comes after the trailer.
println!("Receiving the trailer...");
read_message(&mut stream, &mut trailer)?;
let mut data = Vec::new();
println!("Receiving the data...");
read_message(&mut stream, &mut data)?;
println!("Encrypted message: {:?}", data);
let mut msg_buffer = vec![
SecurityBufferRef::token_buf(&mut trailer),
SecurityBufferRef::data_buf(&mut data),
];
let _decryption_flags = ntlm.decrypt_message(&mut msg_buffer, 0)?;
println!("Decrypting...");
println!(
"Decrypted message: [{}]",
std::str::from_utf8(msg_buffer[1].data()).unwrap()
);
println!("Communication successfully finished.");
Ok(())
}
fn do_authentication(ntlm: &mut Ntlm, identity: &AuthIdentity, mut stream: &mut TcpStream) -> Result<(), io::Error> {
let mut acq_cred_result = ntlm
.acquire_credentials_handle()
.with_credential_use(CredentialUse::Outbound)
.with_auth_data(identity)
.execute(ntlm)?;
let mut output_buffer = vec![SecurityBuffer::new(Vec::new(), BufferType::Token)];
let username = whoami::username();
let mut builder = ntlm
.initialize_security_context()
.with_credentials_handle(&mut acq_cred_result.credentials_handle)
.with_context_requirements(ClientRequestFlags::CONFIDENTIALITY | ClientRequestFlags::ALLOCATE_MEMORY)
.with_target_data_representation(DataRepresentation::Native)
.with_target_name(username.as_str())
.with_output(&mut output_buffer);
let _result = ntlm
.initialize_security_context_impl(&mut builder)?
.resolve_to_result()?;
write_message(&mut stream, &output_buffer[0].buffer)?;
let mut input_buffer = vec![SecurityBuffer::new(Vec::new(), BufferType::Token)];
loop {
output_buffer[0].buffer.clear();
read_message(&mut stream, &mut input_buffer[0].buffer)?;
let mut builder = ntlm
.initialize_security_context()
.with_credentials_handle(&mut acq_cred_result.credentials_handle)
.with_context_requirements(ClientRequestFlags::CONFIDENTIALITY | ClientRequestFlags::ALLOCATE_MEMORY)
.with_target_data_representation(DataRepresentation::Native)
.with_target_name(username.as_str())
.with_input(&mut input_buffer)
.with_output(&mut output_buffer);
let result = ntlm
.initialize_security_context_impl(&mut builder)?
.resolve_to_result()?;
if [SecurityStatus::CompleteAndContinue, SecurityStatus::CompleteNeeded].contains(&result.status) {
println!("Completing the token...");
ntlm.complete_auth_token(&mut output_buffer)?;
}
write_message(&mut stream, &output_buffer[0].buffer)?;
input_buffer[0].buffer.clear();
if ![SecurityStatus::CompleteAndContinue, SecurityStatus::ContinueNeeded].contains(&result.status) {
break;
}
}
Ok(())
}
// By agreement, the message length is read from the first 2 bytes of the message in little-endian order.
pub fn read_message<T: io::Read, A: io::Write>(stream: &mut T, output_buffer: &mut A) -> Result<(), io::Error> {
let msg_len = stream.read_u16::<LittleEndian>()?;
let mut buff = vec![0u8; msg_len as usize];
stream.read_exact(&mut buff)?;
output_buffer.write_all(&buff)?;
println!("Received the buffer [{} bytes]: {:?}", buff.len(), buff);
Ok(())
}
// By agreement, the message length is written in the first 2 bytes of the message in little-endian order.
pub fn write_message<T: io::Write>(stream: &mut T, input_buffer: &[u8]) -> Result<(), io::Error> {
if !input_buffer.is_empty() {
println!("Sending the buffer [{} bytes]: {:?}", input_buffer.len(), input_buffer);
stream.write_u16::<LittleEndian>(input_buffer.len() as u16)?;
stream.write_all(input_buffer)?;
}
Ok(())
}
#[test]
fn buffer_read_correctly() {
let mut msg = vec![0x3, 0x0];
msg.append(&mut b"abc".to_vec());
let mut buffer = Vec::new();
read_message(&mut msg.as_slice(), &mut buffer).unwrap();
assert_eq!(buffer, b"abc".to_vec());
}
#[test]
fn buffer_written_correctly() {
let mut msg = vec![9, 8, 7];
let mut stream = Vec::new();
write_message(&mut stream, &mut msg).unwrap();
assert_eq!(stream, vec![3, 0, 9, 8, 7]);
}