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