FreeRDP
Loading...
Searching...
No Matches
rpc_bind.c
1
20#include <freerdp/config.h>
21
22#include "../settings.h"
23
24#include <winpr/crt.h>
25#include <winpr/assert.h>
26#include <winpr/cast.h>
27
28#include <freerdp/log.h>
29
30#include "rpc_client.h"
31
32#include "rts.h"
33
34#include "rpc_bind.h"
35#include "../utils.h"
36
37#define TAG FREERDP_TAG("core.gateway.rpc")
38
39#define AUTH_PKG NTLM_SSP_NAME
40
46/* Syntax UUIDs */
47
48const p_uuid_t TSGU_UUID = {
49 0x44E265DD, /* time_low */
50 0x7DAF, /* time_mid */
51 0x42CD, /* time_hi_and_version */
52 0x85, /* clock_seq_hi_and_reserved */
53 0x60, /* clock_seq_low */
54 { 0x3C, 0xDB, 0x6E, 0x7A, 0x27, 0x29 } /* node[6] */
55};
56
57const p_uuid_t NDR_UUID = {
58 0x8A885D04, /* time_low */
59 0x1CEB, /* time_mid */
60 0x11C9, /* time_hi_and_version */
61 0x9F, /* clock_seq_hi_and_reserved */
62 0xE8, /* clock_seq_low */
63 { 0x08, 0x00, 0x2B, 0x10, 0x48, 0x60 } /* node[6] */
64};
65
66const p_uuid_t BTFN_UUID = {
67 0x6CB71C2C, /* time_low */
68 0x9812, /* time_mid */
69 0x4540, /* time_hi_and_version */
70 0x03, /* clock_seq_hi_and_reserved */
71 0x00, /* clock_seq_low */
72 { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } /* node[6] */
73};
74
112static int rpc_bind_setup(rdpRpc* rpc)
113{
114 SEC_WINNT_AUTH_IDENTITY identity = WINPR_C_ARRAY_INIT;
115
116 WINPR_ASSERT(rpc);
117
118 rdpContext* context = transport_get_context(rpc->transport);
119 WINPR_ASSERT(context);
120
121 rdpSettings* settings = context->settings;
122 WINPR_ASSERT(settings);
123
124 freerdp* instance = context->instance;
125 WINPR_ASSERT(instance);
126
127 credssp_auth_free(rpc->auth);
128 rpc->auth = credssp_auth_new(context);
129 if (!rpc->auth)
130 return -1;
131
132 auth_status rc = utils_authenticate_gateway(instance, GW_AUTH_RPC);
133 switch (rc)
134 {
135 case AUTH_SUCCESS:
136 case AUTH_SKIP:
137 break;
138 case AUTH_CANCELLED:
139 freerdp_set_last_error_log(instance->context, FREERDP_ERROR_CONNECT_CANCELLED);
140 return -1;
141 case AUTH_NO_CREDENTIALS:
142 WLog_INFO(TAG, "No credentials provided - using nullptr identity");
143 break;
144 case AUTH_FAILED:
145 default:
146 return -1;
147 }
148
149 if (!credssp_auth_init(rpc->auth, AUTH_PKG, nullptr))
150 return -1;
151
152 if (!identity_set_from_settings(&identity, settings, FreeRDP_GatewayUsername,
153 FreeRDP_GatewayDomain, FreeRDP_GatewayPassword))
154 return -1;
155
156 const char* GatewayHostname = freerdp_settings_get_string(settings, FreeRDP_GatewayHostname);
157 const char* GatewayUsername = freerdp_settings_get_string(settings, FreeRDP_GatewayUsername);
158
159 SEC_WINNT_AUTH_IDENTITY* identityArg = (GatewayUsername ? &identity : nullptr);
160 if (!credssp_auth_setup_client(rpc->auth, nullptr, GatewayHostname, identityArg, nullptr))
161 {
162 sspi_FreeAuthIdentity(&identity);
163 return -1;
164 }
165 sspi_FreeAuthIdentity(&identity);
166
167 credssp_auth_set_flags(rpc->auth, ISC_REQ_USE_DCE_STYLE | ISC_REQ_DELEGATE |
168 ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT);
169
170 if (credssp_auth_authenticate(rpc->auth) < 0)
171 return -1;
172
173 return 1;
174}
175
176int rpc_send_bind_pdu(rdpRpc* rpc, BOOL initial)
177{
178 int status = -1;
179 wStream* buffer = nullptr;
180 UINT32 offset = 0;
181 RpcClientCall* clientCall = nullptr;
182 p_cont_elem_t* p_cont_elem = nullptr;
183 rpcconn_bind_hdr_t bind_pdu = WINPR_C_ARRAY_INIT;
184 RpcVirtualConnection* connection = nullptr;
185 RpcInChannel* inChannel = nullptr;
186 const SecBuffer* sbuffer = nullptr;
187
188 WINPR_ASSERT(rpc);
189
190 connection = rpc->VirtualConnection;
191
192 WINPR_ASSERT(connection);
193
194 inChannel = connection->DefaultInChannel;
195
196 if (initial && rpc_bind_setup(rpc) < 0)
197 return -1;
198
199 WLog_DBG(TAG, initial ? "Sending Bind PDU" : "Sending Alter Context PDU");
200
201 sbuffer = credssp_auth_get_output_buffer(rpc->auth);
202
203 if (!sbuffer)
204 goto fail;
205
206 bind_pdu.header = rpc_pdu_header_init(rpc);
207 bind_pdu.header.auth_length = (UINT16)sbuffer->cbBuffer;
208 bind_pdu.auth_verifier.auth_value = sbuffer->pvBuffer;
209 bind_pdu.header.ptype = initial ? PTYPE_BIND : PTYPE_ALTER_CONTEXT;
210 bind_pdu.header.pfc_flags =
211 PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_SUPPORT_HEADER_SIGN | PFC_CONC_MPX;
212 bind_pdu.header.call_id = 2;
213 bind_pdu.max_xmit_frag = rpc->max_xmit_frag;
214 bind_pdu.max_recv_frag = rpc->max_recv_frag;
215 bind_pdu.assoc_group_id = 0;
216 bind_pdu.p_context_elem.n_context_elem = 2;
217 bind_pdu.p_context_elem.reserved = 0;
218 bind_pdu.p_context_elem.reserved2 = 0;
219 bind_pdu.p_context_elem.p_cont_elem =
220 calloc(bind_pdu.p_context_elem.n_context_elem, sizeof(p_cont_elem_t));
221
222 if (!bind_pdu.p_context_elem.p_cont_elem)
223 goto fail;
224
225 p_cont_elem = &bind_pdu.p_context_elem.p_cont_elem[0];
226 p_cont_elem->p_cont_id = 0;
227 p_cont_elem->n_transfer_syn = 1;
228 p_cont_elem->reserved = 0;
229 CopyMemory(&(p_cont_elem->abstract_syntax.if_uuid), &TSGU_UUID, sizeof(p_uuid_t));
230 p_cont_elem->abstract_syntax.if_version = TSGU_SYNTAX_IF_VERSION;
231 p_cont_elem->transfer_syntaxes = malloc(sizeof(p_syntax_id_t));
232
233 if (!p_cont_elem->transfer_syntaxes)
234 goto fail;
235
236 CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &NDR_UUID, sizeof(p_uuid_t));
237 p_cont_elem->transfer_syntaxes[0].if_version = NDR_SYNTAX_IF_VERSION;
238 p_cont_elem = &bind_pdu.p_context_elem.p_cont_elem[1];
239 p_cont_elem->p_cont_id = 1;
240 p_cont_elem->n_transfer_syn = 1;
241 p_cont_elem->reserved = 0;
242 CopyMemory(&(p_cont_elem->abstract_syntax.if_uuid), &TSGU_UUID, sizeof(p_uuid_t));
243 p_cont_elem->abstract_syntax.if_version = TSGU_SYNTAX_IF_VERSION;
244 p_cont_elem->transfer_syntaxes = malloc(sizeof(p_syntax_id_t));
245
246 if (!p_cont_elem->transfer_syntaxes)
247 goto fail;
248
249 CopyMemory(&(p_cont_elem->transfer_syntaxes[0].if_uuid), &BTFN_UUID, sizeof(p_uuid_t));
250 p_cont_elem->transfer_syntaxes[0].if_version = BTFN_SYNTAX_IF_VERSION;
251 offset = 116;
252
253 bind_pdu.auth_verifier.auth_type =
254 rpc_auth_pkg_to_security_provider(credssp_auth_pkg_name(rpc->auth));
255 bind_pdu.auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
256 bind_pdu.auth_verifier.auth_reserved = 0x00;
257 bind_pdu.auth_verifier.auth_context_id = 0x00000000;
258 offset += (8 + bind_pdu.header.auth_length);
259
260 WINPR_ASSERT(offset <= UINT16_MAX);
261 bind_pdu.header.frag_length = (UINT16)offset;
262
263 buffer = Stream_New(nullptr, bind_pdu.header.frag_length);
264
265 if (!buffer)
266 goto fail;
267
268 if (!rts_write_pdu_bind(buffer, &bind_pdu))
269 goto fail;
270
271 clientCall = rpc_client_call_new(bind_pdu.header.call_id, 0);
272
273 if (!clientCall)
274 goto fail;
275
276 if (!ArrayList_Append(rpc->client->ClientCallList, clientCall))
277 {
278 rpc_client_call_free(clientCall);
279 goto fail;
280 }
281
282 Stream_SealLength(buffer);
283 status = rpc_in_channel_send_pdu(inChannel, Stream_Buffer(buffer), Stream_Length(buffer));
284fail:
285
286 if (bind_pdu.p_context_elem.p_cont_elem)
287 {
288 free(bind_pdu.p_context_elem.p_cont_elem[0].transfer_syntaxes);
289 free(bind_pdu.p_context_elem.p_cont_elem[1].transfer_syntaxes);
290 }
291
292 free(bind_pdu.p_context_elem.p_cont_elem);
293 bind_pdu.p_context_elem.p_cont_elem = nullptr;
294
295 Stream_Free(buffer, TRUE);
296 return (status > 0) ? 1 : -1;
297}
298
325BOOL rpc_recv_bind_ack_pdu(rdpRpc* rpc, wStream* s)
326{
327 BOOL rc = FALSE;
328 const BYTE* auth_data = nullptr;
329 size_t pos = 0;
330 size_t end = 0;
331 rpcconn_hdr_t header = WINPR_C_ARRAY_INIT;
332 SecBuffer buffer = WINPR_C_ARRAY_INIT;
333
334 WINPR_ASSERT(rpc);
335 WINPR_ASSERT(rpc->auth);
336 WINPR_ASSERT(s);
337
338 pos = Stream_GetPosition(s);
339 if (!rts_read_pdu_header(s, &header))
340 goto fail;
341
342 WLog_DBG(TAG, header.common.ptype == PTYPE_BIND_ACK ? "Receiving BindAck PDU"
343 : "Receiving AlterContextResp PDU");
344
345 const UINT16 MAX_VALID_FRAG = 0x0FF8;
346 if ((header.bind_ack.max_xmit_frag > MAX_VALID_FRAG) ||
347 (header.bind_ack.max_recv_frag > MAX_VALID_FRAG))
348 {
349 WLog_ERR(TAG,
350 "bind_ack: invalid fragment size: max_xmit_frag=%" PRIu16
351 ", max_recv_frag=%" PRIu16 ", maximum=%" PRIu16,
352 header.bind_ack.max_xmit_frag, header.bind_ack.max_recv_frag, MAX_VALID_FRAG);
353 goto fail;
354 }
355
356 rpc->max_recv_frag = header.bind_ack.max_xmit_frag;
357 rpc->max_xmit_frag = header.bind_ack.max_recv_frag;
358
359 /* Get the correct offset in the input data and pass that on as input buffer.
360 * rts_read_pdu_header did already do consistency checks */
361 end = Stream_GetPosition(s);
362 if (!Stream_SetPosition(s, pos + header.common.frag_length - header.common.auth_length))
363 goto fail;
364 auth_data = Stream_ConstPointer(s);
365 if (!Stream_SetPosition(s, end))
366 goto fail;
367
368 buffer.cbBuffer = header.common.auth_length;
369 buffer.pvBuffer = malloc(buffer.cbBuffer);
370 if (!buffer.pvBuffer)
371 goto fail;
372 memcpy(buffer.pvBuffer, auth_data, buffer.cbBuffer);
373 credssp_auth_take_input_buffer(rpc->auth, &buffer);
374
375 if (credssp_auth_authenticate(rpc->auth) < 0)
376 goto fail;
377
378 rc = TRUE;
379fail:
380 rts_free_pdu_header(&header, FALSE);
381 return rc;
382}
383
391int rpc_send_rpc_auth_3_pdu(rdpRpc* rpc)
392{
393 int status = -1;
394 wStream* buffer = nullptr;
395 size_t offset = 0;
396 const SecBuffer* sbuffer = nullptr;
397 RpcClientCall* clientCall = nullptr;
398 rpcconn_rpc_auth_3_hdr_t auth_3_pdu = WINPR_C_ARRAY_INIT;
399 RpcVirtualConnection* connection = nullptr;
400 RpcInChannel* inChannel = nullptr;
401
402 WINPR_ASSERT(rpc);
403
404 connection = rpc->VirtualConnection;
405 WINPR_ASSERT(connection);
406
407 inChannel = connection->DefaultInChannel;
408 WINPR_ASSERT(inChannel);
409
410 WLog_DBG(TAG, "Sending RpcAuth3 PDU");
411
412 sbuffer = credssp_auth_get_output_buffer(rpc->auth);
413
414 if (!sbuffer)
415 return -1;
416
417 auth_3_pdu.header = rpc_pdu_header_init(rpc);
418 auth_3_pdu.header.auth_length = (UINT16)sbuffer->cbBuffer;
419 auth_3_pdu.auth_verifier.auth_value = sbuffer->pvBuffer;
420 auth_3_pdu.header.ptype = PTYPE_RPC_AUTH_3;
421 auth_3_pdu.header.pfc_flags = PFC_FIRST_FRAG | PFC_LAST_FRAG | PFC_CONC_MPX;
422 auth_3_pdu.header.call_id = 2;
423 auth_3_pdu.max_xmit_frag = rpc->max_xmit_frag;
424 auth_3_pdu.max_recv_frag = rpc->max_recv_frag;
425 offset = 20;
426
427 const size_t align = rpc_offset_align(&offset, 4);
428 WINPR_ASSERT(align <= UINT8_MAX);
429 auth_3_pdu.auth_verifier.auth_pad_length = (BYTE)align;
430 auth_3_pdu.auth_verifier.auth_type =
431 rpc_auth_pkg_to_security_provider(credssp_auth_pkg_name(rpc->auth));
432 auth_3_pdu.auth_verifier.auth_level = RPC_C_AUTHN_LEVEL_PKT_INTEGRITY;
433 auth_3_pdu.auth_verifier.auth_reserved = 0x00;
434 auth_3_pdu.auth_verifier.auth_context_id = 0x00000000;
435 offset += (8 + auth_3_pdu.header.auth_length);
436
437 WINPR_ASSERT(offset <= UINT16_MAX);
438 auth_3_pdu.header.frag_length = (UINT16)offset;
439
440 buffer = Stream_New(nullptr, auth_3_pdu.header.frag_length);
441
442 if (!buffer)
443 return -1;
444
445 if (!rts_write_pdu_auth3(buffer, &auth_3_pdu))
446 goto fail;
447
448 clientCall = rpc_client_call_new(auth_3_pdu.header.call_id, 0);
449
450 if (ArrayList_Append(rpc->client->ClientCallList, clientCall))
451 {
452 Stream_SealLength(buffer);
453 status = rpc_in_channel_send_pdu(inChannel, Stream_Buffer(buffer), Stream_Length(buffer));
454 }
455
456fail:
457 Stream_Free(buffer, TRUE);
458 return (status > 0) ? 1 : -1;
459}
460
461enum RPC_BIND_STATE rpc_bind_state(rdpRpc* rpc)
462{
463 BOOL complete = 0;
464 BOOL have_token = 0;
465 WINPR_ASSERT(rpc);
466
467 complete = credssp_auth_is_complete(rpc->auth);
468 have_token = credssp_auth_have_output_token(rpc->auth);
469
470 return complete ? (have_token ? RPC_BIND_STATE_LAST_LEG : RPC_BIND_STATE_COMPLETE)
471 : RPC_BIND_STATE_INCOMPLETE;
472}
473
474BYTE rpc_auth_pkg_to_security_provider(const char* name)
475{
476 if (strcmp(name, CREDSSP_AUTH_PKG_SPNEGO) == 0)
477 return RPC_C_AUTHN_GSS_NEGOTIATE;
478 else if (strcmp(name, CREDSSP_AUTH_PKG_NTLM) == 0)
479 return RPC_C_AUTHN_WINNT;
480 else if (strcmp(name, CREDSSP_AUTH_PKG_KERBEROS) == 0)
481 return RPC_C_AUTHN_GSS_KERBEROS;
482 else if (strcmp(name, CREDSSP_AUTH_PKG_SCHANNEL) == 0)
483 return RPC_C_AUTHN_GSS_SCHANNEL;
484 else
485 return RPC_C_AUTHN_NONE;
486}
WINPR_ATTR_NODISCARD FREERDP_API const char * freerdp_settings_get_string(const rdpSettings *settings, FreeRDP_Settings_Keys_String id)
Returns a immutable string settings value.