FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
rdpear_main.c
1
19#include <krb5.h>
20#include <errno.h>
21
22#include <winpr/assert.h>
23#include <winpr/wtypes.h>
24
25#include <winpr/crt.h>
26#include <winpr/wlog.h>
27#include <winpr/print.h>
28#include <winpr/asn1.h>
29#include <winpr/sspi.h>
30#include <winpr/collections.h>
31
32#include <rdpear-common/ndr.h>
33#include <rdpear-common/rdpear_common.h>
34#include <rdpear-common/rdpear_asn1.h>
35
36#include <freerdp/config.h>
37#include <freerdp/freerdp.h>
38#include <freerdp/addin.h>
39#include <freerdp/client/channels.h>
40#include <freerdp/channels/log.h>
41#include <freerdp/channels/rdpear.h>
42
43#define TAG CHANNELS_TAG("rdpear.client")
44
45#ifndef MAX_KEYTAB_NAME_LEN
46#define MAX_KEYTAB_NAME_LEN 1100 /* Defined in MIT krb5.h */
47#endif
48
49/* defined in libkrb5 */
50krb5_error_code encode_krb5_authenticator(const krb5_authenticator* rep, krb5_data** code_out);
51krb5_error_code encode_krb5_ap_rep(const krb5_ap_rep* rep, krb5_data** code_out);
52
53typedef struct
54{
56 rdpContext* rdp_context;
57 krb5_context krbContext;
58} RDPEAR_PLUGIN;
59
60static const BYTE payloadHeader[16] = { 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
62
63static krb5_error_code RPC_ENCRYPTION_KEY_to_keyblock(krb5_context ctx,
64 const KERB_RPC_ENCRYPTION_KEY* key,
65 krb5_keyblock** pkeyblock)
66{
67 WINPR_ASSERT(ctx);
68 WINPR_ASSERT(key);
69 WINPR_ASSERT(pkeyblock);
70
71 if (!key->reserved3.length)
72 return KRB5KDC_ERR_NULL_KEY;
73
74 krb5_error_code rv =
75 krb5_init_keyblock(ctx, (krb5_enctype)key->reserved2, key->reserved3.length, pkeyblock);
76 if (rv)
77 return rv;
78
79 krb5_keyblock* keyblock = *pkeyblock;
80 memcpy(keyblock->contents, key->reserved3.value, key->reserved3.length);
81 return rv;
82}
83
84static krb5_error_code kerb_do_checksum(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
85 krb5_keyusage kusage, krb5_cksumtype cksumtype,
86 const KERB_ASN1_DATA* plain, krb5_checksum* out)
87{
88 WINPR_ASSERT(ctx);
89 WINPR_ASSERT(key);
90 WINPR_ASSERT(plain);
91 WINPR_ASSERT(out);
92
93 krb5_keyblock* keyblock = NULL;
94 krb5_data data = { 0 };
95
96 krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
97 if (rv)
98 return rv;
99
100 data.data = (char*)plain->Asn1Buffer;
101 data.length = plain->Asn1BufferHints.count;
102
103 rv = krb5_c_make_checksum(ctx, cksumtype, keyblock, kusage, &data, out);
104
105 krb5_free_keyblock(ctx, keyblock);
106 return rv;
107}
108
109static krb5_error_code kerb_do_encrypt(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
110 krb5_keyusage kusage, const KERB_ASN1_DATA* plain,
111 krb5_data* out)
112{
113 WINPR_ASSERT(ctx);
114 WINPR_ASSERT(key);
115 WINPR_ASSERT(plain);
116 WINPR_ASSERT(out);
117
118 krb5_keyblock* keyblock = NULL;
119 krb5_data data = { 0 };
120 krb5_enc_data enc = { 0 };
121 size_t elen = 0;
122
123 krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
124 if (rv)
125 return rv;
126
127 data.data = (char*)plain->Asn1Buffer;
128 data.length = plain->Asn1BufferHints.count;
129
130 rv = krb5_c_encrypt_length(ctx, keyblock->enctype, data.length, &elen);
131 if (rv)
132 goto out;
133 if (!elen || (elen > UINT32_MAX))
134 {
135 rv = KRB5_PARSE_MALFORMED;
136 goto out;
137 }
138 enc.ciphertext.length = (unsigned int)elen;
139 enc.ciphertext.data = malloc(elen);
140 if (!enc.ciphertext.data)
141 {
142 rv = ENOMEM;
143 goto out;
144 }
145
146 rv = krb5_c_encrypt(ctx, keyblock, kusage, NULL, &data, &enc);
147
148 out->data = enc.ciphertext.data;
149 out->length = enc.ciphertext.length;
150out:
151 krb5_free_keyblock(ctx, keyblock);
152 return rv;
153}
154
155static krb5_error_code kerb_do_decrypt(krb5_context ctx, const KERB_RPC_ENCRYPTION_KEY* key,
156 krb5_keyusage kusage, const krb5_data* cipher,
157 KERB_ASN1_DATA* plain)
158{
159 WINPR_ASSERT(ctx);
160 WINPR_ASSERT(key);
161 WINPR_ASSERT(cipher);
162 WINPR_ASSERT(cipher->length);
163 WINPR_ASSERT(plain);
164
165 krb5_keyblock* keyblock = NULL;
166 krb5_data data = { 0 };
167 krb5_enc_data enc = { 0 };
168
169 krb5_error_code rv = RPC_ENCRYPTION_KEY_to_keyblock(ctx, key, &keyblock);
170 if (rv)
171 return rv;
172
173 enc.kvno = KRB5_PVNO;
174 enc.enctype = (krb5_enctype)key->reserved2;
175 enc.ciphertext.length = cipher->length;
176 enc.ciphertext.data = cipher->data;
177
178 data.length = cipher->length;
179 data.data = (char*)malloc(cipher->length);
180 if (!data.data)
181 {
182 rv = ENOMEM;
183 goto out;
184 }
185
186 rv = krb5_c_decrypt(ctx, keyblock, kusage, NULL, &enc, &data);
187
188 plain->Asn1Buffer = (BYTE*)data.data;
189 plain->Asn1BufferHints.count = data.length;
190out:
191 krb5_free_keyblock(ctx, keyblock);
192 return rv;
193}
194
195static BOOL rdpear_send_payload(RDPEAR_PLUGIN* rdpear, IWTSVirtualChannelCallback* pChannelCallback,
196 RdpEarPackageType packageType, wStream* payload)
197{
198 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
199 BOOL ret = FALSE;
200 wStream* finalStream = NULL;
201 SecBuffer cryptedBuffer = { 0 };
202 wStream* unencodedContent = rdpear_encodePayload(packageType, payload);
203 if (!unencodedContent)
204 goto out;
205
206 const size_t unencodedLen = Stream_GetPosition(unencodedContent);
207
208#if UINT32_MAX < SIZE_MAX
209 if (unencodedLen > UINT32_MAX)
210 goto out;
211#endif
212
213 SecBuffer inBuffer = { (ULONG)unencodedLen, SECBUFFER_DATA, Stream_Buffer(unencodedContent) };
214
215 if (!freerdp_nla_encrypt(rdpear->rdp_context, &inBuffer, &cryptedBuffer))
216 goto out;
217
218 finalStream = Stream_New(NULL, 200);
219 if (!finalStream)
220 goto out;
221 Stream_Write_UINT32(finalStream, 0x4EACC3C8); /* ProtocolMagic (4 bytes) */
222 Stream_Write_UINT32(finalStream, cryptedBuffer.cbBuffer); /* Length (4 bytes) */
223 Stream_Write_UINT32(finalStream, 0x00000000); /* Version (4 bytes) */
224 Stream_Write_UINT32(finalStream, 0x00000000); /* Reserved (4 bytes) */
225 Stream_Write_UINT64(finalStream, 0); /* TsPkgContext (8 bytes) */
226
227 /* payload */
228 if (!Stream_EnsureRemainingCapacity(finalStream, cryptedBuffer.cbBuffer))
229 goto out;
230
231 Stream_Write(finalStream, cryptedBuffer.pvBuffer, cryptedBuffer.cbBuffer);
232
233 const size_t pos = Stream_GetPosition(finalStream);
234#if UINT32_MAX < SIZE_MAX
235 if (pos > UINT32_MAX)
236 goto out;
237#endif
238
239 UINT status =
240 callback->channel->Write(callback->channel, (ULONG)pos, Stream_Buffer(finalStream), NULL);
241 ret = (status == CHANNEL_RC_OK);
242 if (!ret)
243 WLog_DBG(TAG, "rdpear_send_payload=0x%x", status);
244out:
245 sspi_SecBufferFree(&cryptedBuffer);
246 Stream_Free(unencodedContent, TRUE);
247 Stream_Free(finalStream, TRUE);
248 return ret;
249}
250
251static BOOL rdpear_prepare_response(NdrContext* rcontext, UINT16 callId, UINT32 status,
252 NdrContext** pwcontext, wStream* retStream)
253{
254 WINPR_ASSERT(rcontext);
255 WINPR_ASSERT(pwcontext);
256
257 BOOL ret = FALSE;
258 *pwcontext = NULL;
259 NdrContext* wcontext = ndr_context_copy(rcontext);
260 if (!wcontext)
261 return FALSE;
262
263 if (!Stream_EnsureRemainingCapacity(retStream, sizeof(payloadHeader)))
264 goto out;
265
266 Stream_Write(retStream, payloadHeader, sizeof(payloadHeader));
267
268 if (!ndr_write_header(wcontext, retStream) || !ndr_start_constructed(wcontext, retStream) ||
269 !ndr_write_pickle(wcontext, retStream) || /* pickle header */
270 !ndr_write_uint16(wcontext, retStream, callId) || /* callId */
271 !ndr_write_uint16(wcontext, retStream, 0x0000) || /* align padding */
272 !ndr_write_uint32(wcontext, retStream, status) || /* status */
273 !ndr_write_uint16(wcontext, retStream, callId) || /* callId */
274 !ndr_write_uint16(wcontext, retStream, 0x0000)) /* align padding */
275 goto out;
276
277 *pwcontext = wcontext;
278 ret = TRUE;
279out:
280 if (!ret)
281 ndr_context_destroy(&wcontext);
282 return ret;
283}
284
285static BOOL rdpear_kerb_version(NdrContext* rcontext, wStream* s, UINT32* pstatus, UINT32* pversion)
286{
287 *pstatus = ERROR_INVALID_DATA;
288
289 if (!ndr_read_uint32(rcontext, s, pversion))
290 return TRUE;
291
292 WLog_DBG(TAG, "-> KerbNegotiateVersion(v=0x%x)", *pversion);
293 *pstatus = 0;
294
295 return TRUE;
296}
297
298static BOOL rdpear_kerb_ComputeTgsChecksum(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
299 UINT32* pstatus, KERB_ASN1_DATA* resp)
300{
301 ComputeTgsChecksumReq req = { 0 };
302 krb5_checksum checksum = { 0 };
303 wStream* asn1Payload = NULL;
304
305 *pstatus = ERROR_INVALID_DATA;
306 WLog_DBG(TAG, "-> ComputeTgsChecksum");
307
308 if (!ndr_read_ComputeTgsChecksumReq(rcontext, s, NULL, &req) ||
309 !ndr_treat_deferred_read(rcontext, s))
310 goto out;
311 // ComputeTgsChecksumReq_dump(WLog_Get(""), WLOG_DEBUG, &req);
312
313 krb5_error_code rv =
314 kerb_do_checksum(rdpear->krbContext, req.Key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
315 (krb5_cksumtype)req.ChecksumType, req.requestBody, &checksum);
316 if (rv)
317 goto out;
318
319 asn1Payload = rdpear_enc_Checksum(req.ChecksumType, &checksum);
320 if (!asn1Payload)
321 goto out;
322
323 resp->Pdu = 8;
324 resp->Asn1Buffer = Stream_Buffer(asn1Payload);
325 const size_t pos = Stream_GetPosition(asn1Payload);
326 if (pos > UINT32_MAX)
327 goto out;
328 resp->Asn1BufferHints.count = (UINT32)pos;
329 *pstatus = 0;
330
331out:
332 ndr_destroy_ComputeTgsChecksumReq(rcontext, NULL, &req);
333 krb5_free_checksum_contents(rdpear->krbContext, &checksum);
334 Stream_Free(asn1Payload, FALSE);
335 return TRUE;
336}
337
338static BOOL rdpear_kerb_BuildEncryptedAuthData(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext,
339 wStream* s, UINT32* pstatus, KERB_ASN1_DATA* asn1)
340{
341 BuildEncryptedAuthDataReq req = { 0 };
342 krb5_data encrypted = { 0 };
343 wStream* asn1Payload = NULL;
344 krb5_error_code rv = 0;
345
346 *pstatus = ERROR_INVALID_DATA;
347 WLog_DBG(TAG, "-> BuildEncryptedAuthData");
348
349 if (!ndr_read_BuildEncryptedAuthDataReq(rcontext, s, NULL, &req) ||
350 !ndr_treat_deferred_read(rcontext, s))
351 goto out;
352
353 rv = kerb_do_encrypt(rdpear->krbContext, req.Key, (krb5_keyusage)req.KeyUsage,
354 req.PlainAuthData, &encrypted);
355 if (rv)
356 goto out;
357
358 /* do the encoding */
359 asn1Payload = rdpear_enc_EncryptedData(req.Key->reserved2, &encrypted);
360 if (!asn1Payload)
361 goto out;
362
363 // WLog_DBG(TAG, "rdpear_kerb_BuildEncryptedAuthData resp=");
364 // winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(asn1Payload), Stream_GetPosition(asn1Payload));
365 asn1->Pdu = 6;
366 asn1->Asn1Buffer = Stream_Buffer(asn1Payload);
367 const size_t pos = Stream_GetPosition(asn1Payload);
368 if (pos > UINT32_MAX)
369 goto out;
370 asn1->Asn1BufferHints.count = (UINT32)pos;
371 *pstatus = 0;
372
373out:
374 krb5_free_data_contents(rdpear->krbContext, &encrypted);
375 ndr_destroy_BuildEncryptedAuthDataReq(rcontext, NULL, &req);
376 Stream_Free(asn1Payload, FALSE);
377 return TRUE;
378}
379
380static char* KERB_RPC_UNICODESTR_to_charptr(const RPC_UNICODE_STRING* src)
381{
382 WINPR_ASSERT(src);
383 return ConvertWCharNToUtf8Alloc(src->Buffer, src->strLength, NULL);
384}
385
386static BOOL extractAuthData(const KERB_ASN1_DATA* src, krb5_authdata* authData, BOOL* haveData)
387{
388 WinPrAsn1Decoder dec = { 0 };
389 WinPrAsn1Decoder dec2 = { 0 };
390 WinPrAsn1Decoder dec3 = { 0 };
391 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
392 BOOL error = FALSE;
393 WinPrAsn1_INTEGER adType = 0;
394 WinPrAsn1_OctetString os = { 0 };
395
396 *haveData = FALSE;
397 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
398 return FALSE;
399
400 wStream subStream = WinPrAsn1DecGetStream(&dec2);
401 if (!Stream_GetRemainingLength(&subStream))
402 return TRUE;
403
404 if (!WinPrAsn1DecReadSequence(&dec2, &dec3))
405 return FALSE;
406
407 if (!WinPrAsn1DecReadContextualInteger(&dec3, 0, &error, &adType) ||
408 !WinPrAsn1DecReadContextualOctetString(&dec3, 1, &error, &os, FALSE))
409 return FALSE;
410
411 if (os.len > UINT32_MAX)
412 return FALSE;
413
414 authData->ad_type = adType;
415 authData->length = (unsigned int)os.len;
416 authData->contents = os.data;
417 *haveData = TRUE;
418 return TRUE;
419}
420
421static BOOL extractChecksum(const KERB_ASN1_DATA* src, krb5_checksum* dst)
422{
423 WinPrAsn1Decoder dec = { 0 };
424 WinPrAsn1Decoder dec2 = { 0 };
425 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
426 BOOL error = FALSE;
428
429 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
430 return FALSE;
431
432 WinPrAsn1_INTEGER cksumtype = 0;
433 if (!WinPrAsn1DecReadContextualInteger(&dec2, 0, &error, &cksumtype) ||
434 !WinPrAsn1DecReadContextualOctetString(&dec2, 1, &error, &os, FALSE))
435 return FALSE;
436
437 if (os.len > UINT32_MAX)
438 return FALSE;
439 dst->checksum_type = cksumtype;
440 dst->length = (unsigned int)os.len;
441 dst->contents = os.data;
442 return TRUE;
443}
444
445#define FILETIME_TO_UNIX_OFFSET_S 11644473600LL
446
447static LONGLONG krb5_time_to_FILETIME(const krb5_timestamp* ts, krb5_int32 usec)
448{
449 WINPR_ASSERT(ts);
450 return (((*ts + FILETIME_TO_UNIX_OFFSET_S) * (1000LL * 1000LL) + usec) * 10LL);
451}
452
453static void krb5_free_principal_contents(krb5_context ctx, krb5_principal principal)
454{
455 WINPR_ASSERT(principal);
456 krb5_free_data_contents(ctx, &principal->realm);
457 krb5_free_data(ctx, principal->data);
458}
459
460static BOOL rdpear_kerb_CreateApReqAuthenticator(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext,
461 wStream* s, UINT32* pstatus,
463{
464 krb5_error_code rv = 0;
465 wStream* asn1EncodedAuth = NULL;
466 CreateApReqAuthenticatorReq req = { 0 };
467 krb5_data authenticator = { 0 };
468 krb5_data* der = NULL;
469 krb5_keyblock* subkey = NULL;
470 krb5_principal_data client = { 0 };
471
472 *pstatus = ERROR_INVALID_DATA;
473 WLog_DBG(TAG, "-> CreateApReqAuthenticator");
474
475 if (!ndr_read_CreateApReqAuthenticatorReq(rcontext, s, NULL, &req) ||
476 !ndr_treat_deferred_read(rcontext, s))
477 goto out;
478
479 krb5_authdata authdata = { 0 };
480 krb5_authdata* authDataPtr[2] = { &authdata, NULL };
481 BOOL haveData = 0;
482
483 if (!extractAuthData(req.AuthData, &authdata, &haveData))
484 {
485 WLog_ERR(TAG, "error retrieving auth data");
486 winpr_HexDump(TAG, WLOG_DEBUG, req.AuthData->Asn1Buffer,
487 req.AuthData->Asn1BufferHints.count);
488 goto out;
489 }
490
491 if (req.SkewTime->QuadPart)
492 {
493 WLog_ERR(TAG, "!!!!! should handle SkewTime !!!!!");
494 }
495
496 if (req.SubKey)
497 {
498 rv = RPC_ENCRYPTION_KEY_to_keyblock(rdpear->krbContext, req.SubKey, &subkey);
499 if (rv)
500 {
501 WLog_ERR(TAG, "error importing subkey");
502 goto out;
503 }
504 }
505
506 krb5_authenticator authent = { .checksum = NULL,
507 .subkey = NULL,
508 .seq_number = req.SequenceNumber,
509 .authorization_data = haveData ? authDataPtr : NULL };
510
511 client.type = req.ClientName->NameType;
512 if (req.ClientName->nameHints.count > INT32_MAX)
513 goto out;
514
515 client.length = (krb5_int32)req.ClientName->nameHints.count;
516 client.data = calloc(req.ClientName->nameHints.count, sizeof(krb5_data));
517 if (!client.data)
518 goto out;
519
520 for (int i = 0; i < client.length; i++)
521 {
522 krb5_data* cur = &client.data[i];
523 cur->data = KERB_RPC_UNICODESTR_to_charptr(&req.ClientName->Names[i]);
524 if (!cur->data)
525 goto out;
526 const size_t len = strnlen(cur->data, MAX_KEYTAB_NAME_LEN + 1);
527 if (len > MAX_KEYTAB_NAME_LEN)
528 {
529 WLog_ERR(TAG,
530 "Invalid ClientName length %" PRIuz
531 ", limited to %u characters. ClientName: (%s)",
532 MAX_KEYTAB_NAME_LEN, len, cur->data);
533 goto out;
534 }
535 cur->length = (unsigned int)len;
536 }
537 client.realm.data = KERB_RPC_UNICODESTR_to_charptr(req.ClientRealm);
538 if (!client.realm.data)
539 goto out;
540
541 const size_t len = strnlen(client.realm.data, MAX_KEYTAB_NAME_LEN + 1);
542 if (len > MAX_KEYTAB_NAME_LEN)
543 {
544 WLog_ERR(TAG, "Invalid realm length %" PRIuz ", limited to %u characters. Realm: (%s)",
545 MAX_KEYTAB_NAME_LEN, len, client.realm.data);
546 goto out;
547 }
548 client.realm.length = (unsigned int)len;
549 authent.client = &client;
550
551 krb5_checksum checksum;
552 krb5_checksum* pchecksum = NULL;
553 if (req.GssChecksum)
554 {
555 if (!extractChecksum(req.GssChecksum, &checksum))
556 {
557 WLog_ERR(TAG, "Error getting the checksum");
558 goto out;
559 }
560 pchecksum = &checksum;
561 }
562 authent.checksum = pchecksum;
563
564 krb5_us_timeofday(rdpear->krbContext, &authent.ctime, &authent.cusec);
565
566 rv = encode_krb5_authenticator(&authent, &der);
567 if (rv)
568 {
569 WLog_ERR(TAG, "error encoding authenticator");
570 goto out;
571 }
572
573 KERB_ASN1_DATA plain_authent = { .Pdu = 0,
574 .Asn1Buffer = (BYTE*)der->data,
575 .Asn1BufferHints = { .count = der->length } };
576
577 rv = kerb_do_encrypt(rdpear->krbContext, req.EncryptionKey, (krb5_keyusage)req.KeyUsage,
578 &plain_authent, &authenticator);
579 if (rv)
580 {
581 WLog_ERR(TAG, "error encrypting authenticator");
582 goto out;
583 }
584
585 asn1EncodedAuth = rdpear_enc_EncryptedData(req.EncryptionKey->reserved2, &authenticator);
586 if (!asn1EncodedAuth)
587 {
588 WLog_ERR(TAG, "error encoding to ASN1");
589 rv = ENOMEM;
590 goto out;
591 }
592
593 // WLog_DBG(TAG, "authenticator=");
594 // winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(asn1EncodedAuth),
595 // Stream_GetPosition(asn1EncodedAuth));
596
597 const size_t size = Stream_GetPosition(asn1EncodedAuth);
598 if (size > UINT32_MAX)
599 goto out;
600 resp->Authenticator.Asn1BufferHints.count = (UINT32)size;
601 resp->Authenticator.Asn1Buffer = Stream_Buffer(asn1EncodedAuth);
602 resp->AuthenticatorTime.QuadPart = krb5_time_to_FILETIME(&authent.ctime, authent.cusec);
603 *pstatus = 0;
604
605out:
606 resp->Authenticator.Pdu = 6;
607 resp->KerbProtocolError = rv;
608 krb5_free_principal_contents(rdpear->krbContext, &client);
609 krb5_free_data(rdpear->krbContext, der);
610 krb5_free_data_contents(rdpear->krbContext, &authenticator);
611 if (subkey)
612 krb5_free_keyblock(rdpear->krbContext, subkey);
613 ndr_destroy_CreateApReqAuthenticatorReq(rcontext, NULL, &req);
614 Stream_Free(asn1EncodedAuth, FALSE);
615 return TRUE;
616}
617
618static BOOL rdpear_findEncryptedData(const KERB_ASN1_DATA* src, int* penctype, krb5_data* data)
619{
620 WinPrAsn1Decoder dec = { 0 };
621 WinPrAsn1Decoder dec2 = { 0 };
622 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
623 BOOL error = FALSE;
624 WinPrAsn1_INTEGER encType = 0;
625 WinPrAsn1_OctetString os = { 0 };
626
627 if (!WinPrAsn1DecReadSequence(&dec, &dec2) ||
628 !WinPrAsn1DecReadContextualInteger(&dec2, 0, &error, &encType) ||
629 !WinPrAsn1DecReadContextualOctetString(&dec2, 2, &error, &os, FALSE))
630 return FALSE;
631
632 if (os.len > UINT32_MAX)
633 return FALSE;
634 data->data = (char*)os.data;
635 data->length = (unsigned int)os.len;
636 *penctype = encType;
637 return TRUE;
638}
639
640static BOOL rdpear_kerb_UnpackKdcReplyBody(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
641 UINT32* pstatus, UnpackKdcReplyBodyResp* resp)
642{
643 UnpackKdcReplyBodyReq req = { 0 };
644
645 *pstatus = ERROR_INVALID_DATA;
646
647 if (!ndr_read_UnpackKdcReplyBodyReq(rcontext, s, NULL, &req) ||
648 !ndr_treat_deferred_read(rcontext, s))
649 goto out;
650
651 if (req.StrengthenKey)
652 {
653 WLog_ERR(TAG, "StrengthenKey not supported yet");
654 goto out;
655 }
656
657 WLog_DBG(TAG, "-> UnpackKdcReplyBody: KeyUsage=0x%x PDU=0x%x", req.KeyUsage, req.Pdu);
658 // WLog_DBG(TAG, "encryptedPayload=");
659 // winpr_HexDump(TAG, WLOG_DEBUG, req.EncryptedData->Asn1Buffer,
660 // req.EncryptedData->Asn1BufferHints.count);
661
662 krb5_data asn1Data = { 0 };
663 int encType = 0;
664 if (!rdpear_findEncryptedData(req.EncryptedData, &encType, &asn1Data) || !asn1Data.length)
665 goto out;
666
667 resp->KerbProtocolError = kerb_do_decrypt(
668 rdpear->krbContext, req.Key, (krb5_keyusage)req.KeyUsage, &asn1Data, &resp->ReplyBody);
669 resp->ReplyBody.Pdu = req.Pdu;
670
671 *pstatus = 0;
672
673out:
674 ndr_destroy_UnpackKdcReplyBodyReq(rcontext, NULL, &req);
675 return TRUE;
676}
677
678static BOOL rdpear_kerb_DecryptApReply(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
679 UINT32* pstatus, KERB_ASN1_DATA* resp)
680{
681 DecryptApReplyReq req = { 0 };
682
683 *pstatus = ERROR_INVALID_DATA;
684 if (!ndr_read_DecryptApReplyReq(rcontext, s, NULL, &req) ||
685 !ndr_treat_deferred_read(rcontext, s))
686 goto out;
687
688 WLog_DBG(TAG, "-> DecryptApReply");
689 // winpr_HexDump(TAG, WLOG_DEBUG, req.EncryptedReply->Asn1Buffer,
690 // req.EncryptedReply->Asn1BufferHints.count);
691
692 krb5_data asn1Data = { 0 };
693 int encType = 0;
694 if (!rdpear_findEncryptedData(req.EncryptedReply, &encType, &asn1Data) || !asn1Data.length)
695 goto out;
696
697 resp->Pdu = 0x31;
698 krb5_error_code rv =
699 kerb_do_decrypt(rdpear->krbContext, req.Key, KRB5_KEYUSAGE_AP_REP_ENCPART, &asn1Data, resp);
700 if (rv != 0)
701 {
702 WLog_ERR(TAG, "error decrypting");
703 goto out;
704 }
705
706 // WLog_DBG(TAG, "response=");
707 // winpr_HexDump(TAG, WLOG_DEBUG, resp->Asn1Buffer, resp->Asn1BufferHints.count);
708 *pstatus = 0;
709out:
710 ndr_destroy_DecryptApReplyReq(rcontext, NULL, &req);
711 return TRUE;
712}
713
714static BOOL rdpear_kerb_PackApReply(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
715 UINT32* pstatus, PackApReplyResp* resp)
716{
717 PackApReplyReq req = { 0 };
718 krb5_data asn1Data = { 0 };
719 krb5_data* out = NULL;
720
721 WLog_DBG(TAG, "-> PackApReply");
722 *pstatus = ERROR_INVALID_DATA;
723 if (!ndr_read_PackApReplyReq(rcontext, s, NULL, &req) || !ndr_treat_deferred_read(rcontext, s))
724 goto out;
725
726 krb5_error_code rv = kerb_do_encrypt(rdpear->krbContext, req.SessionKey,
727 KRB5_KEYUSAGE_AP_REP_ENCPART, req.ReplyBody, &asn1Data);
728 if (rv)
729 goto out;
730
731 krb5_ap_rep reply;
732 reply.enc_part.kvno = KRB5_PVNO;
733 reply.enc_part.enctype = (krb5_enctype)req.SessionKey->reserved2;
734 reply.enc_part.ciphertext.length = asn1Data.length;
735 reply.enc_part.ciphertext.data = asn1Data.data;
736
737 rv = encode_krb5_ap_rep(&reply, &out);
738 if (rv)
739 goto out;
740
741 resp->PackedReply = (BYTE*)out->data;
742 resp->PackedReplyHints.count = out->length;
743 *pstatus = 0;
744out:
745 free(out);
746 krb5_free_data_contents(rdpear->krbContext, &asn1Data);
747 ndr_destroy_PackApReplyReq(rcontext, NULL, &req);
748 return TRUE;
749}
750
751static UINT rdpear_decode_payload(RDPEAR_PLUGIN* rdpear,
752 IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
753{
754 UINT ret = ERROR_INVALID_DATA;
755 NdrContext* context = NULL;
756 NdrContext* wcontext = NULL;
757 UINT32 status = 0;
758
759 UINT32 uint32Resp = 0;
760 KERB_ASN1_DATA asn1Data = { 0 };
761 CreateApReqAuthenticatorResp createApReqAuthenticatorResp = { 0 };
762 UnpackKdcReplyBodyResp unpackKdcReplyBodyResp = { 0 };
763 PackApReplyResp packApReplyResp = { 0 };
764
765 void* resp = NULL;
766 NdrMessageType respDescr = NULL;
767
768 wStream* respStream = Stream_New(NULL, 500);
769 if (!respStream)
770 goto out;
771
772 Stream_Seek(s, 16); /* skip first 16 bytes */
773
774 wStream commandStream = { 0 };
775 UINT16 callId = 0;
776 UINT16 callId2 = 0;
777
778 context = ndr_read_header(s);
779 if (!context || !ndr_read_constructed(context, s, &commandStream) ||
780 !ndr_read_pickle(context, &commandStream) ||
781 !ndr_read_uint16(context, &commandStream, &callId) ||
782 !ndr_read_uint16(context, &commandStream, &callId2) || (callId != callId2))
783 goto out;
784
785 ret = CHANNEL_RC_NOT_OPEN;
786 switch (callId)
787 {
788 case RemoteCallKerbNegotiateVersion:
789 resp = &uint32Resp;
790 respDescr = ndr_uint32_descr();
791
792 if (rdpear_kerb_version(context, &commandStream, &status, &uint32Resp))
793 ret = CHANNEL_RC_OK;
794 break;
795 case RemoteCallKerbCreateApReqAuthenticator:
796 resp = &createApReqAuthenticatorResp;
797 respDescr = ndr_CreateApReqAuthenticatorResp_descr();
798
799 if (rdpear_kerb_CreateApReqAuthenticator(rdpear, context, &commandStream, &status,
800 &createApReqAuthenticatorResp))
801 ret = CHANNEL_RC_OK;
802 break;
803 case RemoteCallKerbDecryptApReply:
804 resp = &asn1Data;
805 respDescr = ndr_KERB_ASN1_DATA_descr();
806
807 if (rdpear_kerb_DecryptApReply(rdpear, context, &commandStream, &status, &asn1Data))
808 ret = CHANNEL_RC_OK;
809 break;
810 case RemoteCallKerbComputeTgsChecksum:
811 resp = &asn1Data;
812 respDescr = ndr_KERB_ASN1_DATA_descr();
813
814 if (rdpear_kerb_ComputeTgsChecksum(rdpear, context, &commandStream, &status, &asn1Data))
815 ret = CHANNEL_RC_OK;
816 break;
817 case RemoteCallKerbBuildEncryptedAuthData:
818 resp = &asn1Data;
819 respDescr = ndr_KERB_ASN1_DATA_descr();
820
821 if (rdpear_kerb_BuildEncryptedAuthData(rdpear, context, &commandStream, &status,
822 &asn1Data))
823 ret = CHANNEL_RC_OK;
824 break;
825 case RemoteCallKerbUnpackKdcReplyBody:
826 resp = &unpackKdcReplyBodyResp;
827 respDescr = ndr_UnpackKdcReplyBodyResp_descr();
828
829 if (rdpear_kerb_UnpackKdcReplyBody(rdpear, context, &commandStream, &status,
830 &unpackKdcReplyBodyResp))
831 ret = CHANNEL_RC_OK;
832 break;
833 case RemoteCallKerbPackApReply:
834 resp = &packApReplyResp;
835 respDescr = ndr_PackApReplyResp_descr();
836
837 if (rdpear_kerb_PackApReply(rdpear, context, &commandStream, &status, &packApReplyResp))
838 ret = CHANNEL_RC_OK;
839 break;
840
841 case RemoteCallNtlmNegotiateVersion:
842 WLog_ERR(TAG, "don't wanna support NTLM");
843 break;
844
845 default:
846 WLog_DBG(TAG, "Unhandled callId=0x%x", callId);
847 winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(&commandStream, BYTE),
848 Stream_GetRemainingLength(&commandStream));
849 break;
850 }
851
852 if (!rdpear_prepare_response(context, callId, status, &wcontext, respStream))
853 goto out;
854
855 if (resp && respDescr)
856 {
857 WINPR_ASSERT(respDescr->writeFn);
858
859 BOOL r = respDescr->writeFn(wcontext, respStream, NULL, resp) &&
860 ndr_treat_deferred_write(wcontext, respStream);
861
862 if (respDescr->destroyFn)
863 respDescr->destroyFn(wcontext, NULL, resp);
864
865 if (!r)
866 {
867 WLog_DBG(TAG, "!writeFn || !ndr_treat_deferred_write");
868 goto out;
869 }
870 }
871
872 if (!ndr_end_constructed(wcontext, respStream) ||
873 !rdpear_send_payload(rdpear, pChannelCallback, RDPEAR_PACKAGE_KERBEROS, respStream))
874 {
875 WLog_DBG(TAG, "rdpear_send_payload !!!!!!!!");
876 goto out;
877 }
878out:
879 if (context)
880 ndr_context_destroy(&context);
881
882 if (wcontext)
883 ndr_context_destroy(&wcontext);
884
885 if (respStream)
886 Stream_Free(respStream, TRUE);
887 return ret;
888}
889
890static UINT rdpear_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
891{
892 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
893 WINPR_ASSERT(callback);
894 UINT ret = ERROR_INVALID_DATA;
895
896 // winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(s, BYTE), Stream_GetRemainingLength(s));
897
898 if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
899 return ERROR_INVALID_DATA;
900
901 UINT32 protocolMagic = 0;
902 UINT32 Length = 0;
903 UINT32 Version = 0;
904 Stream_Read_UINT32(s, protocolMagic);
905 if (protocolMagic != 0x4EACC3C8)
906 return ERROR_INVALID_DATA;
907
908 Stream_Read_UINT32(s, Length);
909
910 Stream_Read_UINT32(s, Version);
911 if (Version != 0x00000000)
912 return ERROR_INVALID_DATA;
913
914 Stream_Seek(s, 4); /* Reserved (4 bytes) */
915 Stream_Seek(s, 8); /* TsPkgContext (8 bytes) */
916
917 if (!Stream_CheckAndLogRequiredLength(TAG, s, Length))
918 return ERROR_INVALID_DATA;
919
920 SecBuffer inBuffer = { Length, SECBUFFER_TOKEN, Stream_PointerAs(s, void) };
921 SecBuffer decrypted = { 0 };
922
923 RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)callback->plugin;
924 WINPR_ASSERT(rdpear);
925 if (!freerdp_nla_decrypt(rdpear->rdp_context, &inBuffer, &decrypted))
926 goto out;
927
928 WinPrAsn1Decoder dec = { 0 };
929 WinPrAsn1Decoder dec2 = { 0 };
930 wStream decodedStream = { 0 };
931 Stream_StaticInit(&decodedStream, decrypted.pvBuffer, decrypted.cbBuffer);
932 WinPrAsn1Decoder_Init(&dec, WINPR_ASN1_DER, &decodedStream);
933
934 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
935 goto out;
936
937 WinPrAsn1_OctetString packageName = { 0 };
938 WinPrAsn1_OctetString payload = { 0 };
939 BOOL error = 0;
940 if (!WinPrAsn1DecReadContextualOctetString(&dec2, 1, &error, &packageName, FALSE))
941 goto out;
942
943 if (!WinPrAsn1DecReadContextualOctetString(&dec2, 2, &error, &payload, FALSE))
944 goto out;
945
946 wStream payloadStream = { 0 };
947 Stream_StaticInit(&payloadStream, payload.data, payload.len);
948
949 ret = rdpear_decode_payload(rdpear, pChannelCallback, &payloadStream);
950out:
951 sspi_SecBufferFree(&decrypted);
952 return ret;
953}
954
960static UINT rdpear_on_open(IWTSVirtualChannelCallback* pChannelCallback)
961{
962 WINPR_UNUSED(pChannelCallback);
963 return CHANNEL_RC_OK;
964}
965
971static UINT rdpear_on_close(IWTSVirtualChannelCallback* pChannelCallback)
972{
973 WINPR_UNUSED(pChannelCallback);
974 return CHANNEL_RC_OK;
975}
976
977static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
978{
979 WINPR_ASSERT(base);
980
981 RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)base;
982 krb5_free_context(rdpear->krbContext);
983}
984
985static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
986{
987 WINPR_ASSERT(base);
988 WINPR_UNUSED(settings);
989
990 RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)base;
991 rdpear->rdp_context = rcontext;
992 if (krb5_init_context(&rdpear->krbContext))
993 return CHANNEL_RC_INITIALIZATION_ERROR;
994 return CHANNEL_RC_OK;
995}
996
997static const IWTSVirtualChannelCallback rdpear_callbacks = { rdpear_on_data_received,
998 rdpear_on_open, rdpear_on_close,
999 NULL };
1000
1006FREERDP_ENTRY_POINT(UINT rdpear_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
1007{
1008 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, RDPEAR_DVC_CHANNEL_NAME,
1009 sizeof(RDPEAR_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
1010 &rdpear_callbacks, init_plugin_cb, terminate_plugin_cb);
1011}
2.2.2.1.8 BuildEncryptedAuthData
2.2.2.1.7 ComputeTgsChecksum
2.2.2.1.4 CreateApReqAuthenticator
2.2.2.1.4 CreateApReqAuthenticator
2.2.1.2.1 KERB_ASN1_DATA
2.2.1.2.8 KERB_RPC_ENCRYPTION_KEY
message descriptor
Definition ndr.h:76
2.3.10 RPC_UNICODE_STRING (MS-DTYP)
2.2.2.1.6 UnpackKdcReplyBody
2.2.2.1.6 UnpackKdcReplyBody