Coverage Report

Created: 2025-07-01 06:46

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