Coverage Report

Created: 2026-09-01 06:39

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