FreeRDP
Loading...
Searching...
No Matches
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 = nullptr;
94 krb5_data data = WINPR_C_ARRAY_INIT;
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 = nullptr;
119 krb5_data data = WINPR_C_ARRAY_INIT;
120 krb5_enc_data enc = WINPR_C_ARRAY_INIT;
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, nullptr, &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 = nullptr;
166 krb5_data data = WINPR_C_ARRAY_INIT;
167 krb5_enc_data enc = WINPR_C_ARRAY_INIT;
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, nullptr, &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 BOOL isKerb, wStream* payload)
197{
198 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
199 BOOL ret = FALSE;
200 wStream* finalStream = nullptr;
201 SecBuffer cryptedBuffer = WINPR_C_ARRAY_INIT;
202 wStream* unencodedContent = rdpear_encodePayload(isKerb, 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(nullptr, 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 = callback->channel->Write(callback->channel, (ULONG)pos,
240 Stream_Buffer(finalStream), nullptr);
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, BOOL isKerb, 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 = nullptr;
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 goto out;
271 if (isKerb)
272 {
273 /* for some reason there's 4 zero undocumented bytes here after the pickle record
274 * in the kerberos package packets */
275 UINT32 v = 0;
276 if (!ndr_write_uint32(wcontext, retStream, v))
277 goto out;
278 }
279 if (!ndr_write_uint16(wcontext, retStream, callId) || /* callId */
280 !ndr_write_uint16(wcontext, retStream, 0x0000) || /* align padding */
281 !ndr_write_uint32(wcontext, retStream, status) || /* status */
282 !ndr_write_uint16(wcontext, retStream, callId) || /* callId */
283 !ndr_write_uint16(wcontext, retStream, 0x0000)) /* align padding */
284 goto out;
285
286 *pwcontext = wcontext;
287 ret = TRUE;
288out:
289 if (!ret)
290 ndr_context_destroy(&wcontext);
291 return ret;
292}
293
294static BOOL rdpear_kerb_version(NdrContext* rcontext, wStream* s, UINT32* pstatus, UINT32* pversion)
295{
296 *pstatus = ERROR_INVALID_DATA;
297
298 if (!ndr_read_uint32(rcontext, s, pversion))
299 return TRUE;
300
301 WLog_DBG(TAG, "-> KerbNegotiateVersion(v=0x%x)", *pversion);
302 *pstatus = 0;
303
304 return TRUE;
305}
306
307static BOOL rdpear_kerb_ComputeTgsChecksum(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
308 UINT32* pstatus, KERB_ASN1_DATA* resp)
309{
310 ComputeTgsChecksumReq req = WINPR_C_ARRAY_INIT;
311 krb5_checksum checksum = WINPR_C_ARRAY_INIT;
312 wStream* asn1Payload = nullptr;
313
314 *pstatus = ERROR_INVALID_DATA;
315 WLog_DBG(TAG, "-> ComputeTgsChecksum");
316
317 if (!ndr_read_ComputeTgsChecksumReq(rcontext, s, nullptr, &req) ||
318 !ndr_treat_deferred_read(rcontext, s))
319 goto out;
320 // ComputeTgsChecksumReq_dump(WLog_Get(""), WLOG_DEBUG, &req);
321
322 krb5_error_code rv =
323 kerb_do_checksum(rdpear->krbContext, req.Key, KRB5_KEYUSAGE_TGS_REQ_AUTH_CKSUM,
324 (krb5_cksumtype)req.ChecksumType, req.requestBody, &checksum);
325 if (rv)
326 goto out;
327
328 asn1Payload = rdpear_enc_Checksum(req.ChecksumType, &checksum);
329 if (!asn1Payload)
330 goto out;
331
332 resp->Pdu = 8;
333 resp->Asn1Buffer = Stream_Buffer(asn1Payload);
334 const size_t pos = Stream_GetPosition(asn1Payload);
335 if (pos > UINT32_MAX)
336 goto out;
337 resp->Asn1BufferHints.count = (UINT32)pos;
338 *pstatus = 0;
339
340out:
341 ndr_destroy_ComputeTgsChecksumReq(rcontext, nullptr, &req);
342 krb5_free_checksum_contents(rdpear->krbContext, &checksum);
343 Stream_Free(asn1Payload, FALSE);
344 return TRUE;
345}
346
347static BOOL rdpear_kerb_BuildEncryptedAuthData(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext,
348 wStream* s, UINT32* pstatus, KERB_ASN1_DATA* asn1)
349{
350 BuildEncryptedAuthDataReq req = WINPR_C_ARRAY_INIT;
351 krb5_data encrypted = WINPR_C_ARRAY_INIT;
352 wStream* asn1Payload = nullptr;
353 krb5_error_code rv = 0;
354
355 *pstatus = ERROR_INVALID_DATA;
356 WLog_DBG(TAG, "-> BuildEncryptedAuthData");
357
358 if (!ndr_read_BuildEncryptedAuthDataReq(rcontext, s, nullptr, &req) ||
359 !ndr_treat_deferred_read(rcontext, s))
360 goto out;
361
362 rv = kerb_do_encrypt(rdpear->krbContext, req.Key, (krb5_keyusage)req.KeyUsage,
363 req.PlainAuthData, &encrypted);
364 if (rv)
365 goto out;
366
367 /* do the encoding */
368 asn1Payload = rdpear_enc_EncryptedData(req.Key->reserved2, &encrypted);
369 if (!asn1Payload)
370 goto out;
371
372 // WLog_DBG(TAG, "rdpear_kerb_BuildEncryptedAuthData resp=");
373 // winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(asn1Payload), Stream_GetPosition(asn1Payload));
374 asn1->Pdu = 6;
375 asn1->Asn1Buffer = Stream_Buffer(asn1Payload);
376 const size_t pos = Stream_GetPosition(asn1Payload);
377 if (pos > UINT32_MAX)
378 goto out;
379 asn1->Asn1BufferHints.count = (UINT32)pos;
380 *pstatus = 0;
381
382out:
383 krb5_free_data_contents(rdpear->krbContext, &encrypted);
384 ndr_destroy_BuildEncryptedAuthDataReq(rcontext, nullptr, &req);
385 Stream_Free(asn1Payload, FALSE);
386 return TRUE;
387}
388
389static char* KERB_RPC_UNICODESTR_to_charptr(const RPC_UNICODE_STRING* src)
390{
391 WINPR_ASSERT(src);
392 return ConvertWCharNToUtf8Alloc(src->Buffer, src->strLength, nullptr);
393}
394
395static BOOL extractAuthData(const KERB_ASN1_DATA* src, krb5_authdata* authData, BOOL* haveData)
396{
397 WinPrAsn1Decoder dec = WinPrAsn1Decoder_init();
398 WinPrAsn1Decoder dec2 = WinPrAsn1Decoder_init();
399 WinPrAsn1Decoder dec3 = WinPrAsn1Decoder_init();
400 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
401 BOOL error = FALSE;
402 WinPrAsn1_INTEGER adType = 0;
403 WinPrAsn1_OctetString os = WINPR_C_ARRAY_INIT;
404
405 *haveData = FALSE;
406 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
407 return FALSE;
408
409 wStream subStream = WinPrAsn1DecGetStream(&dec2);
410 if (!Stream_GetRemainingLength(&subStream))
411 return TRUE;
412
413 if (!WinPrAsn1DecReadSequence(&dec2, &dec3))
414 return FALSE;
415
416 if (!WinPrAsn1DecReadContextualInteger(&dec3, 0, &error, &adType) ||
417 !WinPrAsn1DecReadContextualOctetString(&dec3, 1, &error, &os, FALSE))
418 return FALSE;
419
420 if (os.len > UINT32_MAX)
421 return FALSE;
422
423 authData->ad_type = adType;
424 authData->length = (unsigned int)os.len;
425 authData->contents = os.data;
426 *haveData = TRUE;
427 return TRUE;
428}
429
430static BOOL extractChecksum(const KERB_ASN1_DATA* src, krb5_checksum* dst)
431{
432 WinPrAsn1Decoder dec = WinPrAsn1Decoder_init();
433 WinPrAsn1Decoder dec2 = WinPrAsn1Decoder_init();
434 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
435 BOOL error = FALSE;
437
438 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
439 return FALSE;
440
441 WinPrAsn1_INTEGER cksumtype = 0;
442 if (!WinPrAsn1DecReadContextualInteger(&dec2, 0, &error, &cksumtype) ||
443 !WinPrAsn1DecReadContextualOctetString(&dec2, 1, &error, &os, FALSE))
444 return FALSE;
445
446 if (os.len > UINT32_MAX)
447 return FALSE;
448 dst->checksum_type = cksumtype;
449 dst->length = (unsigned int)os.len;
450 dst->contents = os.data;
451 return TRUE;
452}
453
454#define FILETIME_TO_UNIX_OFFSET_S 11644473600LL
455
456static LONGLONG krb5_time_to_FILETIME(const krb5_timestamp* ts, krb5_int32 usec)
457{
458 WINPR_ASSERT(ts);
459 return (((*ts + FILETIME_TO_UNIX_OFFSET_S) * (1000LL * 1000LL) + usec) * 10LL);
460}
461
462static void krb5_free_principal_contents(krb5_context ctx, krb5_principal principal)
463{
464 WINPR_ASSERT(principal);
465 krb5_free_data_contents(ctx, &principal->realm);
466 krb5_free_data(ctx, principal->data);
467}
468
469static BOOL rdpear_kerb_CreateApReqAuthenticator(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext,
470 wStream* s, UINT32* pstatus,
472{
473 krb5_error_code rv = 0;
474 wStream* asn1EncodedAuth = nullptr;
475 CreateApReqAuthenticatorReq req = WINPR_C_ARRAY_INIT;
476 krb5_data authenticator = WINPR_C_ARRAY_INIT;
477 krb5_data* der = nullptr;
478 krb5_keyblock* subkey = nullptr;
479 krb5_principal_data client = WINPR_C_ARRAY_INIT;
480
481 *pstatus = ERROR_INVALID_DATA;
482 WLog_DBG(TAG, "-> CreateApReqAuthenticator");
483
484 if (!ndr_read_CreateApReqAuthenticatorReq(rcontext, s, nullptr, &req) ||
485 !ndr_treat_deferred_read(rcontext, s))
486 goto out;
487
488 krb5_authdata authdata = WINPR_C_ARRAY_INIT;
489 krb5_authdata* authDataPtr[2] = { &authdata, nullptr };
490 BOOL haveData = 0;
491
492 if (!extractAuthData(req.AuthData, &authdata, &haveData))
493 {
494 WLog_ERR(TAG, "error retrieving auth data");
495 winpr_HexDump(TAG, WLOG_DEBUG, req.AuthData->Asn1Buffer,
496 req.AuthData->Asn1BufferHints.count);
497 goto out;
498 }
499
500 if (req.SkewTime->QuadPart)
501 {
502 WLog_ERR(TAG, "!!!!! should handle SkewTime !!!!!");
503 }
504
505 if (req.SubKey)
506 {
507 rv = RPC_ENCRYPTION_KEY_to_keyblock(rdpear->krbContext, req.SubKey, &subkey);
508 if (rv)
509 {
510 WLog_ERR(TAG, "error importing subkey");
511 goto out;
512 }
513 }
514
515 krb5_authenticator authent = { .checksum = nullptr,
516 .subkey = nullptr,
517 .seq_number = req.SequenceNumber,
518 .authorization_data = haveData ? authDataPtr : nullptr };
519
520 client.type = req.ClientName->NameType;
521 if (req.ClientName->nameHints.count > INT32_MAX)
522 goto out;
523
524 client.length = (krb5_int32)req.ClientName->nameHints.count;
525 client.data = calloc(req.ClientName->nameHints.count, sizeof(krb5_data));
526 if (!client.data)
527 goto out;
528
529 for (int i = 0; i < client.length; i++)
530 {
531 krb5_data* cur = &client.data[i];
532 cur->data = KERB_RPC_UNICODESTR_to_charptr(&req.ClientName->Names[i]);
533 if (!cur->data)
534 goto out;
535 const size_t len = strnlen(cur->data, MAX_KEYTAB_NAME_LEN + 1);
536 if (len > MAX_KEYTAB_NAME_LEN)
537 {
538 WLog_ERR(TAG,
539 "Invalid ClientName length %d, limited to %" PRIuz
540 " characters. ClientName: (%s)",
541 MAX_KEYTAB_NAME_LEN, len, cur->data);
542 goto out;
543 }
544 cur->length = (unsigned int)len;
545 }
546 client.realm.data = KERB_RPC_UNICODESTR_to_charptr(req.ClientRealm);
547 if (!client.realm.data)
548 goto out;
549
550 const size_t len = strnlen(client.realm.data, MAX_KEYTAB_NAME_LEN + 1);
551 if (len > MAX_KEYTAB_NAME_LEN)
552 {
553 WLog_ERR(TAG, "Invalid realm length %d, limited to %" PRIuz " characters. Realm: (%s)",
554 MAX_KEYTAB_NAME_LEN, len, client.realm.data);
555 goto out;
556 }
557 client.realm.length = (unsigned int)len;
558 authent.client = &client;
559
560 krb5_checksum checksum;
561 krb5_checksum* pchecksum = nullptr;
562 if (req.GssChecksum)
563 {
564 if (!extractChecksum(req.GssChecksum, &checksum))
565 {
566 WLog_ERR(TAG, "Error getting the checksum");
567 goto out;
568 }
569 pchecksum = &checksum;
570 }
571 authent.checksum = pchecksum;
572
573 krb5_us_timeofday(rdpear->krbContext, &authent.ctime, &authent.cusec);
574
575 rv = encode_krb5_authenticator(&authent, &der);
576 if (rv)
577 {
578 WLog_ERR(TAG, "error encoding authenticator");
579 goto out;
580 }
581
582 KERB_ASN1_DATA plain_authent = { .Pdu = 0,
583 .Asn1Buffer = (BYTE*)der->data,
584 .Asn1BufferHints = { .count = der->length } };
585
586 rv = kerb_do_encrypt(rdpear->krbContext, req.EncryptionKey, (krb5_keyusage)req.KeyUsage,
587 &plain_authent, &authenticator);
588 if (rv)
589 {
590 WLog_ERR(TAG, "error encrypting authenticator");
591 goto out;
592 }
593
594 asn1EncodedAuth = rdpear_enc_EncryptedData(req.EncryptionKey->reserved2, &authenticator);
595 if (!asn1EncodedAuth)
596 {
597 WLog_ERR(TAG, "error encoding to ASN1");
598 rv = ENOMEM;
599 goto out;
600 }
601
602 // WLog_DBG(TAG, "authenticator=");
603 // winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(asn1EncodedAuth),
604 // Stream_GetPosition(asn1EncodedAuth));
605
606 const size_t size = Stream_GetPosition(asn1EncodedAuth);
607 if (size > UINT32_MAX)
608 goto out;
609 resp->Authenticator.Asn1BufferHints.count = (UINT32)size;
610 resp->Authenticator.Asn1Buffer = Stream_Buffer(asn1EncodedAuth);
611 resp->AuthenticatorTime.QuadPart = krb5_time_to_FILETIME(&authent.ctime, authent.cusec);
612 *pstatus = 0;
613
614out:
615 resp->Authenticator.Pdu = 6;
616 resp->KerbProtocolError = rv;
617 krb5_free_principal_contents(rdpear->krbContext, &client);
618 krb5_free_data(rdpear->krbContext, der);
619 krb5_free_data_contents(rdpear->krbContext, &authenticator);
620 if (subkey)
621 krb5_free_keyblock(rdpear->krbContext, subkey);
622 ndr_destroy_CreateApReqAuthenticatorReq(rcontext, nullptr, &req);
623 Stream_Free(asn1EncodedAuth, FALSE);
624 return TRUE;
625}
626
627static BOOL rdpear_findEncryptedData(const KERB_ASN1_DATA* src, int* penctype, krb5_data* data)
628{
629 WinPrAsn1Decoder dec = WinPrAsn1Decoder_init();
630 WinPrAsn1Decoder dec2 = WinPrAsn1Decoder_init();
631 WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, src->Asn1Buffer, src->Asn1BufferHints.count);
632 BOOL error = FALSE;
633 WinPrAsn1_INTEGER encType = 0;
634 WinPrAsn1_OctetString os = WINPR_C_ARRAY_INIT;
635
636 if (!WinPrAsn1DecReadSequence(&dec, &dec2) ||
637 !WinPrAsn1DecReadContextualInteger(&dec2, 0, &error, &encType) ||
638 !WinPrAsn1DecReadContextualOctetString(&dec2, 2, &error, &os, FALSE))
639 return FALSE;
640
641 if (os.len > UINT32_MAX)
642 return FALSE;
643 data->data = (char*)os.data;
644 data->length = (unsigned int)os.len;
645 *penctype = encType;
646 return TRUE;
647}
648
649static BOOL rdpear_kerb_UnpackKdcReplyBody(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
650 UINT32* pstatus, UnpackKdcReplyBodyResp* resp)
651{
652 UnpackKdcReplyBodyReq req = WINPR_C_ARRAY_INIT;
653
654 *pstatus = ERROR_INVALID_DATA;
655
656 if (!ndr_read_UnpackKdcReplyBodyReq(rcontext, s, nullptr, &req) ||
657 !ndr_treat_deferred_read(rcontext, s))
658 goto out;
659
660 if (req.StrengthenKey)
661 {
662 WLog_ERR(TAG, "StrengthenKey not supported yet");
663 goto out;
664 }
665
666 WLog_DBG(TAG, "-> UnpackKdcReplyBody: KeyUsage=0x%x PDU=0x%x", req.KeyUsage, req.Pdu);
667 // WLog_DBG(TAG, "encryptedPayload=");
668 // winpr_HexDump(TAG, WLOG_DEBUG, req.EncryptedData->Asn1Buffer,
669 // req.EncryptedData->Asn1BufferHints.count);
670
671 krb5_data asn1Data = WINPR_C_ARRAY_INIT;
672 int encType = 0;
673 if (!rdpear_findEncryptedData(req.EncryptedData, &encType, &asn1Data) || !asn1Data.length)
674 goto out;
675
676 resp->KerbProtocolError = kerb_do_decrypt(
677 rdpear->krbContext, req.Key, (krb5_keyusage)req.KeyUsage, &asn1Data, &resp->ReplyBody);
678 resp->ReplyBody.Pdu = req.Pdu;
679
680 *pstatus = 0;
681
682out:
683 ndr_destroy_UnpackKdcReplyBodyReq(rcontext, nullptr, &req);
684 return TRUE;
685}
686
687static BOOL rdpear_kerb_DecryptApReply(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
688 UINT32* pstatus, KERB_ASN1_DATA* resp)
689{
690 DecryptApReplyReq req = WINPR_C_ARRAY_INIT;
691
692 *pstatus = ERROR_INVALID_DATA;
693 if (!ndr_read_DecryptApReplyReq(rcontext, s, nullptr, &req) ||
694 !ndr_treat_deferred_read(rcontext, s))
695 goto out;
696
697 WLog_DBG(TAG, "-> DecryptApReply");
698 // winpr_HexDump(TAG, WLOG_DEBUG, req.EncryptedReply->Asn1Buffer,
699 // req.EncryptedReply->Asn1BufferHints.count);
700
701 krb5_data asn1Data = WINPR_C_ARRAY_INIT;
702 int encType = 0;
703 if (!rdpear_findEncryptedData(req.EncryptedReply, &encType, &asn1Data) || !asn1Data.length)
704 goto out;
705
706 resp->Pdu = 0x31;
707 krb5_error_code rv =
708 kerb_do_decrypt(rdpear->krbContext, req.Key, KRB5_KEYUSAGE_AP_REP_ENCPART, &asn1Data, resp);
709 if (rv != 0)
710 {
711 WLog_ERR(TAG, "error decrypting");
712 goto out;
713 }
714
715 // WLog_DBG(TAG, "response=");
716 // winpr_HexDump(TAG, WLOG_DEBUG, resp->Asn1Buffer, resp->Asn1BufferHints.count);
717 *pstatus = 0;
718out:
719 ndr_destroy_DecryptApReplyReq(rcontext, nullptr, &req);
720 return TRUE;
721}
722
723static BOOL rdpear_kerb_PackApReply(RDPEAR_PLUGIN* rdpear, NdrContext* rcontext, wStream* s,
724 UINT32* pstatus, PackApReplyResp* resp)
725{
726 PackApReplyReq req = WINPR_C_ARRAY_INIT;
727 krb5_data asn1Data = WINPR_C_ARRAY_INIT;
728 krb5_data* out = nullptr;
729
730 WLog_DBG(TAG, "-> PackApReply");
731 *pstatus = ERROR_INVALID_DATA;
732 if (!ndr_read_PackApReplyReq(rcontext, s, nullptr, &req) ||
733 !ndr_treat_deferred_read(rcontext, s))
734 goto out;
735
736 krb5_error_code rv = kerb_do_encrypt(rdpear->krbContext, req.SessionKey,
737 KRB5_KEYUSAGE_AP_REP_ENCPART, req.ReplyBody, &asn1Data);
738 if (rv)
739 goto out;
740
741 krb5_ap_rep reply;
742 reply.enc_part.kvno = KRB5_PVNO;
743 reply.enc_part.enctype = (krb5_enctype)req.SessionKey->reserved2;
744 reply.enc_part.ciphertext.length = asn1Data.length;
745 reply.enc_part.ciphertext.data = asn1Data.data;
746
747 rv = encode_krb5_ap_rep(&reply, &out);
748 if (rv)
749 goto out;
750
751 resp->PackedReply = (BYTE*)out->data;
752 resp->PackedReplyHints.count = out->length;
753 *pstatus = 0;
754out:
755 free(out);
756 krb5_free_data_contents(rdpear->krbContext, &asn1Data);
757 ndr_destroy_PackApReplyReq(rcontext, nullptr, &req);
758 return TRUE;
759}
760
761static BOOL rdpear_ntlm_version(NdrContext* rcontext, wStream* s, UINT32* pstatus, UINT32* pversion)
762{
763 *pstatus = ERROR_INVALID_DATA;
764
765 if (!ndr_read_uint32(rcontext, s, pversion))
766 return TRUE;
767
768 WLog_DBG(TAG, "-> NtlmNegotiateVersion(v=0x%x)", *pversion);
769 *pstatus = 0;
770
771 return TRUE;
772}
773
774static UINT rdpear_decode_payload(RDPEAR_PLUGIN* rdpear,
775 IWTSVirtualChannelCallback* pChannelCallback,
776 const WinPrAsn1_OctetString* packageName, wStream* s)
777{
778 UINT ret = ERROR_INVALID_DATA;
779 NdrContext* context = nullptr;
780 NdrContext* wcontext = nullptr;
781 UINT32 status = 0;
782
783 UINT32 uint32Resp = 0;
784 KERB_ASN1_DATA asn1Data = WINPR_C_ARRAY_INIT;
785 CreateApReqAuthenticatorResp createApReqAuthenticatorResp = WINPR_C_ARRAY_INIT;
786 UnpackKdcReplyBodyResp unpackKdcReplyBodyResp = WINPR_C_ARRAY_INIT;
787 PackApReplyResp packApReplyResp = WINPR_C_ARRAY_INIT;
788 void* resp = nullptr;
789 NdrMessageType respDescr = nullptr;
790
791 wStream* respStream = Stream_New(nullptr, 500);
792 if (!respStream)
793 goto out;
794
795 BOOL isKerb = FALSE;
796 switch (rdpear_packageType_from_name(packageName))
797 {
798 case RDPEAR_PACKAGE_KERBEROS:
799 isKerb = TRUE;
800 break;
801 case RDPEAR_PACKAGE_NTLM:
802 isKerb = FALSE;
803 break;
804 default:
805 WLog_ERR(TAG, "unknown package type");
806 goto out;
807 }
808
809 Stream_Seek(s, 16); /* skip first 16 bytes */
810 wStream commandStream = WINPR_C_ARRAY_INIT;
811 UINT16 callId = 0;
812 UINT16 callId2 = 0;
813
814 context = ndr_read_header(s);
815 if (!context || !ndr_read_constructed(context, s, &commandStream) ||
816 !ndr_read_pickle(context, &commandStream))
817 goto out;
818
819 if (isKerb)
820 {
821 /* for some reason there's 4 zero undocumented bytes here after the pickle record
822 * in the kerberos package packets */
823 UINT32 v = 0;
824 if (!ndr_read_uint32(context, &commandStream, &v))
825 goto out;
826 }
827
828 if (!ndr_read_uint16(context, &commandStream, &callId) ||
829 !ndr_read_uint16(context, &commandStream, &callId2) || (callId != callId2))
830 goto out;
831
832 ret = CHANNEL_RC_NOT_OPEN;
833 switch (callId)
834 {
835 case RemoteCallKerbNegotiateVersion:
836 resp = &uint32Resp;
837 respDescr = ndr_uint32_descr();
838
839 if (rdpear_kerb_version(context, &commandStream, &status, &uint32Resp))
840 ret = CHANNEL_RC_OK;
841 break;
842 case RemoteCallKerbCreateApReqAuthenticator:
843 resp = &createApReqAuthenticatorResp;
844 respDescr = ndr_CreateApReqAuthenticatorResp_descr();
845
846 if (rdpear_kerb_CreateApReqAuthenticator(rdpear, context, &commandStream, &status,
847 &createApReqAuthenticatorResp))
848 ret = CHANNEL_RC_OK;
849 break;
850 case RemoteCallKerbDecryptApReply:
851 resp = &asn1Data;
852 respDescr = ndr_KERB_ASN1_DATA_descr();
853
854 if (rdpear_kerb_DecryptApReply(rdpear, context, &commandStream, &status, &asn1Data))
855 ret = CHANNEL_RC_OK;
856 break;
857 case RemoteCallKerbComputeTgsChecksum:
858 resp = &asn1Data;
859 respDescr = ndr_KERB_ASN1_DATA_descr();
860
861 if (rdpear_kerb_ComputeTgsChecksum(rdpear, context, &commandStream, &status, &asn1Data))
862 ret = CHANNEL_RC_OK;
863 break;
864 case RemoteCallKerbBuildEncryptedAuthData:
865 resp = &asn1Data;
866 respDescr = ndr_KERB_ASN1_DATA_descr();
867
868 if (rdpear_kerb_BuildEncryptedAuthData(rdpear, context, &commandStream, &status,
869 &asn1Data))
870 ret = CHANNEL_RC_OK;
871 break;
872 case RemoteCallKerbUnpackKdcReplyBody:
873 resp = &unpackKdcReplyBodyResp;
874 respDescr = ndr_UnpackKdcReplyBodyResp_descr();
875
876 if (rdpear_kerb_UnpackKdcReplyBody(rdpear, context, &commandStream, &status,
877 &unpackKdcReplyBodyResp))
878 ret = CHANNEL_RC_OK;
879 break;
880 case RemoteCallKerbPackApReply:
881 resp = &packApReplyResp;
882 respDescr = ndr_PackApReplyResp_descr();
883
884 if (rdpear_kerb_PackApReply(rdpear, context, &commandStream, &status, &packApReplyResp))
885 ret = CHANNEL_RC_OK;
886 break;
887 case RemoteCallNtlmNegotiateVersion:
888 resp = &uint32Resp;
889 respDescr = ndr_uint32_descr();
890
891 if (rdpear_ntlm_version(context, &commandStream, &status, &uint32Resp))
892 ret = CHANNEL_RC_OK;
893 break;
894
895 default:
896 WLog_DBG(TAG, "Unhandled callId=0x%x", callId);
897 winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(&commandStream, BYTE),
898 Stream_GetRemainingLength(&commandStream));
899 break;
900 }
901
902 if (!rdpear_prepare_response(context, isKerb, callId, status, &wcontext, respStream))
903 goto out;
904
905 if (resp && respDescr)
906 {
907 WINPR_ASSERT(respDescr->writeFn);
908
909 BOOL r = respDescr->writeFn(wcontext, respStream, nullptr, resp) &&
910 ndr_treat_deferred_write(wcontext, respStream);
911
912 if (respDescr->destroyFn)
913 respDescr->destroyFn(wcontext, nullptr, resp);
914
915 if (!r)
916 {
917 WLog_DBG(TAG, "!writeFn || !ndr_treat_deferred_write");
918 goto out;
919 }
920 }
921
922 if (!ndr_end_constructed(wcontext, respStream) ||
923 !rdpear_send_payload(rdpear, pChannelCallback, isKerb, respStream))
924 {
925 WLog_DBG(TAG, "rdpear_send_payload !!!!!!!!");
926 goto out;
927 }
928out:
929 if (context)
930 ndr_context_destroy(&context);
931
932 if (wcontext)
933 ndr_context_destroy(&wcontext);
934
935 if (respStream)
936 Stream_Free(respStream, TRUE);
937 return ret;
938}
939
940static UINT rdpear_on_data_received(IWTSVirtualChannelCallback* pChannelCallback, wStream* s)
941{
942 GENERIC_CHANNEL_CALLBACK* callback = (GENERIC_CHANNEL_CALLBACK*)pChannelCallback;
943 WINPR_ASSERT(callback);
944 UINT ret = ERROR_INVALID_DATA;
945
946 // winpr_HexDump(TAG, WLOG_DEBUG, Stream_PointerAs(s, BYTE), Stream_GetRemainingLength(s));
947
948 if (!Stream_CheckAndLogRequiredLength(TAG, s, 24))
949 return ERROR_INVALID_DATA;
950
951 UINT32 protocolMagic = 0;
952 UINT32 Length = 0;
953 UINT32 Version = 0;
954 Stream_Read_UINT32(s, protocolMagic);
955 if (protocolMagic != 0x4EACC3C8)
956 return ERROR_INVALID_DATA;
957
958 Stream_Read_UINT32(s, Length);
959
960 Stream_Read_UINT32(s, Version);
961 if (Version != 0x00000000)
962 return ERROR_INVALID_DATA;
963
964 Stream_Seek(s, 4); /* Reserved (4 bytes) */
965 Stream_Seek(s, 8); /* TsPkgContext (8 bytes) */
966
967 if (!Stream_CheckAndLogRequiredLength(TAG, s, Length))
968 return ERROR_INVALID_DATA;
969
970 SecBuffer inBuffer = { Length, SECBUFFER_TOKEN, Stream_PointerAs(s, void) };
971 SecBuffer decrypted = WINPR_C_ARRAY_INIT;
972
973 RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)callback->plugin;
974 WINPR_ASSERT(rdpear);
975 if (!freerdp_nla_decrypt(rdpear->rdp_context, &inBuffer, &decrypted))
976 goto out;
977
978 WinPrAsn1Decoder dec = WinPrAsn1Decoder_init();
979 WinPrAsn1Decoder dec2 = WinPrAsn1Decoder_init();
980 wStream decodedStream = WINPR_C_ARRAY_INIT;
981 Stream_StaticInit(&decodedStream, decrypted.pvBuffer, decrypted.cbBuffer);
982 WinPrAsn1Decoder_Init(&dec, WINPR_ASN1_DER, &decodedStream);
983
984 if (!WinPrAsn1DecReadSequence(&dec, &dec2))
985 goto out;
986
987 WinPrAsn1_OctetString packageName = WINPR_C_ARRAY_INIT;
988 WinPrAsn1_OctetString payload = WINPR_C_ARRAY_INIT;
989 BOOL error = 0;
990 if (!WinPrAsn1DecReadContextualOctetString(&dec2, 1, &error, &packageName, FALSE))
991 goto out;
992
993 if (!WinPrAsn1DecReadContextualOctetString(&dec2, 2, &error, &payload, FALSE))
994 goto out;
995
996 wStream payloadStream = WINPR_C_ARRAY_INIT;
997 Stream_StaticInit(&payloadStream, payload.data, payload.len);
998
999 ret = rdpear_decode_payload(rdpear, pChannelCallback, &packageName, &payloadStream);
1000out:
1001 sspi_SecBufferFree(&decrypted);
1002 return ret;
1003}
1004
1010static UINT rdpear_on_open(IWTSVirtualChannelCallback* pChannelCallback)
1011{
1012 WINPR_UNUSED(pChannelCallback);
1013 return CHANNEL_RC_OK;
1014}
1015
1021static UINT rdpear_on_close(IWTSVirtualChannelCallback* pChannelCallback)
1022{
1023 WINPR_UNUSED(pChannelCallback);
1024 return CHANNEL_RC_OK;
1025}
1026
1027static void terminate_plugin_cb(GENERIC_DYNVC_PLUGIN* base)
1028{
1029 WINPR_ASSERT(base);
1030
1031 RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)base;
1032 krb5_free_context(rdpear->krbContext);
1033}
1034
1035static UINT init_plugin_cb(GENERIC_DYNVC_PLUGIN* base, rdpContext* rcontext, rdpSettings* settings)
1036{
1037 WINPR_ASSERT(base);
1038 WINPR_UNUSED(settings);
1039
1040 RDPEAR_PLUGIN* rdpear = (RDPEAR_PLUGIN*)base;
1041 rdpear->rdp_context = rcontext;
1042 if (krb5_init_context(&rdpear->krbContext))
1043 return CHANNEL_RC_INITIALIZATION_ERROR;
1044 return CHANNEL_RC_OK;
1045}
1046
1047static const IWTSVirtualChannelCallback rdpear_callbacks = { rdpear_on_data_received,
1048 rdpear_on_open, rdpear_on_close,
1049 nullptr };
1050
1056FREERDP_ENTRY_POINT(UINT rdpear_DVCPluginEntry(IDRDYNVC_ENTRY_POINTS* pEntryPoints))
1057{
1058 return freerdp_generic_DVCPluginEntry(pEntryPoints, TAG, RDPEAR_DVC_CHANNEL_NAME,
1059 sizeof(RDPEAR_PLUGIN), sizeof(GENERIC_CHANNEL_CALLBACK),
1060 &rdpear_callbacks, init_plugin_cb, terminate_plugin_cb);
1061}
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:84
2.3.10 RPC_UNICODE_STRING (MS-DTYP)
2.2.2.1.6 UnpackKdcReplyBody
2.2.2.1.6 UnpackKdcReplyBody