/** * FreeRDP: A Remote Desktop Protocol Implementation * * Copyright 2023 David Fort * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #define TAG CHANNELS_TAG("rdpear") static char kerberosPackageName[] = { 'K', 0, 'e', 0, 'r', 0, 'b', 0, 'e', 0, 'r', 0, 'o', 0, 's', 0 }; static char ntlmPackageName[] = { 'N', 0, 'T', 0, 'L', 0, 'M', 0 }; RdpEarPackageType rdpear_packageType_from_name(WinPrAsn1_OctetString* package) { if (package->len == sizeof(kerberosPackageName) && memcmp(package->data, kerberosPackageName, package->len) == 0) return RDPEAR_PACKAGE_KERBEROS; if (package->len == sizeof(ntlmPackageName) && memcmp(package->data, ntlmPackageName, package->len) == 0) return RDPEAR_PACKAGE_NTLM; return RDPEAR_PACKAGE_UNKNOWN; } wStream* rdpear_encodePayload(RdpEarPackageType packageType, wStream* payload) { wStream* ret = NULL; WinPrAsn1Encoder* enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER); if (!enc) return NULL; /* TSRemoteGuardInnerPacket ::= SEQUENCE { */ if (!WinPrAsn1EncSeqContainer(enc)) goto out; /* packageName [1] OCTET STRING */ WinPrAsn1_OctetString packageOctetString; switch (packageType) { case RDPEAR_PACKAGE_KERBEROS: packageOctetString.data = (BYTE*)kerberosPackageName; packageOctetString.len = sizeof(kerberosPackageName); break; case RDPEAR_PACKAGE_NTLM: packageOctetString.data = (BYTE*)ntlmPackageName; packageOctetString.len = sizeof(ntlmPackageName); break; default: goto out; } if (!WinPrAsn1EncContextualOctetString(enc, 1, &packageOctetString)) goto out; /* buffer [2] OCTET STRING*/ WinPrAsn1_OctetString payloadOctetString = { Stream_GetPosition(payload), Stream_Buffer(payload) }; if (!WinPrAsn1EncContextualOctetString(enc, 2, &payloadOctetString)) goto out; /* } */ if (!WinPrAsn1EncEndContainer(enc)) goto out; ret = Stream_New(NULL, 100); if (!ret) goto out; if (!WinPrAsn1EncToStream(enc, ret)) { Stream_Free(ret, TRUE); ret = NULL; goto out; } out: WinPrAsn1Encoder_Free(&enc); return ret; } #define RDPEAR_SIMPLE_MESSAGE_TYPE(V) \ BOOL ndr_read_##V(NdrContext* context, wStream* s, const void* hints, V* obj) \ { \ WINPR_UNUSED(hints); \ return ndr_struct_read_fromDescr(context, s, &V##_struct, obj); \ } \ BOOL ndr_write_##V(NdrContext* context, wStream* s, const void* hints, const V* obj) \ { \ WINPR_UNUSED(hints); \ return ndr_struct_write_fromDescr(context, s, &V##_struct, obj); \ } \ void ndr_destroy_##V(NdrContext* context, const void* hints, V* obj) \ { \ WINPR_UNUSED(hints); \ ndr_struct_destroy(context, &V##_struct, obj); \ } \ void ndr_dump_##V(wLog* logger, UINT32 lvl, size_t indentLevel, const V* obj) \ { \ ndr_struct_dump_fromDescr(logger, lvl, indentLevel, &V##_struct, obj); \ } \ \ static BOOL ndr_descr_read_##V(NdrContext* context, wStream* s, const void* hints, void* obj) \ { \ WINPR_UNUSED(hints); \ return ndr_struct_read_fromDescr(context, s, &V##_struct, obj); \ } \ static BOOL ndr_descr_write_##V(NdrContext* context, wStream* s, const void* hints, \ const void* obj) \ { \ WINPR_UNUSED(hints); \ return ndr_struct_write_fromDescr(context, s, &V##_struct, obj); \ } \ static void ndr_descr_destroy_##V(NdrContext* context, const void* hints, void* obj) \ { \ WINPR_UNUSED(hints); \ ndr_struct_destroy(context, &V##_struct, obj); \ } \ static void ndr_descr_dump_##V(wLog* logger, UINT32 lvl, size_t indentLevel, const void* obj) \ { \ ndr_struct_dump_fromDescr(logger, lvl, indentLevel, &V##_struct, obj); \ } \ \ static NdrMessageDescr ndr_##V##_descr_s = { \ NDR_ARITY_SIMPLE, sizeof(V), ndr_descr_read_##V, ndr_descr_write_##V, \ ndr_descr_destroy_##V, ndr_descr_dump_##V, \ }; \ \ NdrMessageType ndr_##V##_descr(void) \ { \ return &ndr_##V##_descr_s; \ } static const NdrFieldStruct KERB_RPC_OCTET_STRING_fields[] = { { "Length", offsetof(KERB_RPC_OCTET_STRING, length), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "value", offsetof(KERB_RPC_OCTET_STRING, value), NDR_POINTER_NON_NULL, 0, &ndr_uint8Array_descr_s } }; static const NdrStructDescr KERB_RPC_OCTET_STRING_struct = { "KERB_RPC_OCTET_STRING", 2, KERB_RPC_OCTET_STRING_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_RPC_OCTET_STRING) /* ============================= KERB_ASN1_DATA ============================== */ static const NdrFieldStruct KERB_ASN1_DATA_fields[] = { { "Pdu", offsetof(KERB_ASN1_DATA, Pdu), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "Count", offsetof(KERB_ASN1_DATA, Asn1BufferHints.count), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "Asn1Buffer", offsetof(KERB_ASN1_DATA, Asn1Buffer), NDR_POINTER_NON_NULL, 1, &ndr_uint8Array_descr_s } }; static const NdrStructDescr KERB_ASN1_DATA_struct = { "KERB_ASN1_DATA", ARRAYSIZE(KERB_ASN1_DATA_fields), KERB_ASN1_DATA_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_ASN1_DATA) /* ============================ RPC_UNICODE_STRING ========================== */ BOOL ndr_read_RPC_UNICODE_STRING(NdrContext* context, wStream* s, const void* hints, RPC_UNICODE_STRING* res) { NdrDeferredEntry bufferDesc = { NDR_PTR_NULL, "RPC_UNICODE_STRING.Buffer", &res->lenHints, (void*)&res->Buffer, ndr_uint16VaryingArray_descr() }; UINT16 Length = 0; UINT16 MaximumLength = 0; WINPR_UNUSED(hints); if (!ndr_read_uint16(context, s, &Length) || !ndr_read_uint16(context, s, &MaximumLength) || !ndr_read_refpointer(context, s, &bufferDesc.ptrId) || Length > MaximumLength) return FALSE; res->lenHints.length = Length; res->lenHints.maxLength = MaximumLength; res->strLength = Length / 2; return ndr_push_deferreds(context, &bufferDesc, 1); } static BOOL ndr_descr_read_RPC_UNICODE_STRING(NdrContext* context, wStream* s, const void* hints, void* res) { return ndr_read_RPC_UNICODE_STRING(context, s, hints, res); } #if 0 BOOL ndr_write_RPC_UNICODE_STRING(NdrContext* context, wStream* s, const void* hints, const RPC_UNICODE_STRING* res) { return ndr_write_uint32(context, s, res->lenHints.length) && ndr_write_uint32(context, s, res->lenHints.maxLength) /*&& ndr_write_BYTE_ptr(context, s, (BYTE*)res->Buffer, res->Length)*/ ; } #endif void ndr_dump_RPC_UNICODE_STRING(wLog* logger, UINT32 lvl, size_t indentLevel, const RPC_UNICODE_STRING* obj) { WINPR_UNUSED(indentLevel); WLog_Print(logger, lvl, "\tLength=%d MaximumLength=%d", obj->lenHints.length, obj->lenHints.maxLength); winpr_HexLogDump(logger, lvl, obj->Buffer, obj->lenHints.length); } static void ndr_descr_dump_RPC_UNICODE_STRING(wLog* logger, UINT32 lvl, size_t indentLevel, const void* obj) { ndr_dump_RPC_UNICODE_STRING(logger, lvl, indentLevel, obj); } void ndr_destroy_RPC_UNICODE_STRING(NdrContext* context, const void* hints, RPC_UNICODE_STRING* obj) { WINPR_UNUSED(context); WINPR_UNUSED(hints); if (!obj) return; free(obj->Buffer); obj->Buffer = NULL; } static void ndr_descr_destroy_RPC_UNICODE_STRING(NdrContext* context, const void* hints, void* obj) { ndr_destroy_RPC_UNICODE_STRING(context, hints, obj); } static const NdrMessageDescr RPC_UNICODE_STRING_descr_s = { NDR_ARITY_SIMPLE, sizeof(RPC_UNICODE_STRING), ndr_descr_read_RPC_UNICODE_STRING, /*ndr_write_RPC_UNICODE_STRING*/ NULL, ndr_descr_destroy_RPC_UNICODE_STRING, ndr_descr_dump_RPC_UNICODE_STRING }; NdrMessageType ndr_RPC_UNICODE_STRING_descr(void) { return &RPC_UNICODE_STRING_descr_s; } /* ========================= RPC_UNICODE_STRING_Array ======================== */ static BOOL ndr_read_RPC_UNICODE_STRING_Array(NdrContext* context, wStream* s, const void* hints, void* v) { WINPR_ASSERT(context); WINPR_ASSERT(s); WINPR_ASSERT(hints); return ndr_read_uconformant_array(context, s, hints, ndr_RPC_UNICODE_STRING_descr(), v); } static BOOL ndr_write_RPC_UNICODE_STRING_Array(NdrContext* context, wStream* s, const void* ghints, const void* v) { WINPR_ASSERT(context); WINPR_ASSERT(s); WINPR_ASSERT(ghints); const NdrArrayHints* hints = (const NdrArrayHints*)ghints; return ndr_write_uconformant_array(context, s, hints->count, ndr_RPC_UNICODE_STRING_descr(), v); } static const NdrMessageDescr RPC_UNICODE_STRING_Array_descr_s = { NDR_ARITY_ARRAYOF, sizeof(RPC_UNICODE_STRING), ndr_read_RPC_UNICODE_STRING_Array, ndr_write_RPC_UNICODE_STRING_Array, NULL, NULL }; static NdrMessageType ndr_RPC_UNICODE_STRING_Array_descr(void) { return &RPC_UNICODE_STRING_Array_descr_s; } /* ========================== KERB_RPC_INTERNAL_NAME ======================== */ BOOL ndr_read_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s, const void* hints, KERB_RPC_INTERNAL_NAME* res) { WINPR_ASSERT(res); union { RPC_UNICODE_STRING** ppstr; void* pv; } cnv; cnv.ppstr = &res->Names; NdrDeferredEntry names = { NDR_PTR_NULL, "KERB_RPC_INTERNAL_NAME.Names", &res->nameHints, cnv.pv, ndr_RPC_UNICODE_STRING_Array_descr() }; UINT16 nameCount = 0; WINPR_UNUSED(hints); if (!ndr_read_uint16(context, s, &res->NameType) || !ndr_read_uint16(context, s, &nameCount)) return FALSE; res->nameHints.count = nameCount; return ndr_read_refpointer(context, s, &names.ptrId) && ndr_push_deferreds(context, &names, 1); } static BOOL ndr_descr_read_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s, const void* hints, void* res) { return ndr_read_KERB_RPC_INTERNAL_NAME(context, s, hints, res); } BOOL ndr_write_KERB_RPC_INTERNAL_NAME(NdrContext* context, wStream* s, const void* hints, const KERB_RPC_INTERNAL_NAME* res) { WINPR_UNUSED(context); WINPR_UNUSED(s); WINPR_UNUSED(hints); WINPR_UNUSED(res); WLog_ERR(TAG, "TODO: implement this"); return FALSE; } void ndr_dump_KERB_RPC_INTERNAL_NAME(wLog* logger, UINT32 lvl, size_t indentLevel, const KERB_RPC_INTERNAL_NAME* obj) { WINPR_UNUSED(indentLevel); WINPR_UNUSED(obj); WLog_Print(logger, lvl, "TODO: implement this"); } static void ndr_descr_dump_KERB_RPC_INTERNAL_NAME(wLog* logger, UINT32 lvl, size_t indentLevel, const void* obj) { ndr_dump_KERB_RPC_INTERNAL_NAME(logger, lvl, indentLevel, obj); } void ndr_destroy_KERB_RPC_INTERNAL_NAME(NdrContext* context, const void* hints, KERB_RPC_INTERNAL_NAME* obj) { WINPR_UNUSED(hints); if (!obj) return; for (UINT32 i = 0; i < obj->nameHints.count; i++) ndr_destroy_RPC_UNICODE_STRING(context, NULL, &obj->Names[i]); free(obj->Names); obj->Names = NULL; } static void ndr_descr_destroy_KERB_RPC_INTERNAL_NAME(NdrContext* context, const void* hints, void* obj) { ndr_destroy_KERB_RPC_INTERNAL_NAME(context, hints, obj); } static NdrMessageDescr KERB_RPC_INTERNAL_NAME_descr_s = { NDR_ARITY_SIMPLE, sizeof(KERB_RPC_INTERNAL_NAME), ndr_descr_read_KERB_RPC_INTERNAL_NAME, NULL, ndr_descr_destroy_KERB_RPC_INTERNAL_NAME, ndr_descr_dump_KERB_RPC_INTERNAL_NAME }; NdrMessageType ndr_KERB_RPC_INTERNAL_NAME_descr(void) { return &KERB_RPC_INTERNAL_NAME_descr_s; } /* ========================== KERB_RPC_ENCRYPTION_KEY ======================== */ static const NdrFieldStruct KERB_RPC_ENCRYPTION_KEY_fields[] = { { "reserved1", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved1), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "reserved2", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved2), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "reserved3", offsetof(KERB_RPC_ENCRYPTION_KEY, reserved3), NDR_NOT_POINTER, -1, &ndr_KERB_RPC_OCTET_STRING_descr_s } }; static const NdrStructDescr KERB_RPC_ENCRYPTION_KEY_struct = { "KERB_RPC_ENCRYPTION_KEY", ARRAYSIZE(KERB_RPC_ENCRYPTION_KEY_fields), KERB_RPC_ENCRYPTION_KEY_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(KERB_RPC_ENCRYPTION_KEY) /* ========================== BuildEncryptedAuthDataReq ======================== */ static const NdrFieldStruct BuildEncryptedAuthDataReq_fields[] = { { "KeyUsage", offsetof(BuildEncryptedAuthDataReq, KeyUsage), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "key", offsetof(BuildEncryptedAuthDataReq, Key), NDR_POINTER_NON_NULL, -1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }, { "plainAuthData", offsetof(BuildEncryptedAuthDataReq, PlainAuthData), NDR_POINTER_NON_NULL, -1, &ndr_KERB_ASN1_DATA_descr_s } }; static const NdrStructDescr BuildEncryptedAuthDataReq_struct = { "BuildEncryptedAuthDataReq", ARRAYSIZE(BuildEncryptedAuthDataReq_fields), BuildEncryptedAuthDataReq_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(BuildEncryptedAuthDataReq) /* ========================== ComputeTgsChecksumReq ======================== */ static const NdrFieldStruct ComputeTgsChecksumReq_fields[] = { { "requestBody", offsetof(ComputeTgsChecksumReq, requestBody), NDR_POINTER_NON_NULL, -1, &ndr_KERB_ASN1_DATA_descr_s }, { "key", offsetof(ComputeTgsChecksumReq, Key), NDR_POINTER_NON_NULL, -1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }, { "ChecksumType", offsetof(ComputeTgsChecksumReq, ChecksumType), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s } }; static const NdrStructDescr ComputeTgsChecksumReq_struct = { "ComputeTgsChecksumReq", ARRAYSIZE(ComputeTgsChecksumReq_fields), ComputeTgsChecksumReq_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(ComputeTgsChecksumReq) /* ========================== CreateApReqAuthenticatorReq ======================== */ static const NdrFieldStruct CreateApReqAuthenticatorReq_fields[] = { { "EncryptionKey", offsetof(CreateApReqAuthenticatorReq, EncryptionKey), NDR_POINTER_NON_NULL, -1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }, { "SequenceNumber", offsetof(CreateApReqAuthenticatorReq, SequenceNumber), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "ClientName", offsetof(CreateApReqAuthenticatorReq, ClientName), NDR_POINTER_NON_NULL, -1, &KERB_RPC_INTERNAL_NAME_descr_s }, { "ClientRealm", offsetof(CreateApReqAuthenticatorReq, ClientRealm), NDR_POINTER_NON_NULL, -1, &RPC_UNICODE_STRING_descr_s }, { "SkewTime", offsetof(CreateApReqAuthenticatorReq, SkewTime), NDR_POINTER_NON_NULL, -1, &ndr_uint64_descr_s }, { "SubKey", offsetof(CreateApReqAuthenticatorReq, SubKey), NDR_POINTER, -1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }, { "AuthData", offsetof(CreateApReqAuthenticatorReq, AuthData), NDR_POINTER_NON_NULL, -1, &ndr_KERB_ASN1_DATA_descr_s }, { "GssChecksum", offsetof(CreateApReqAuthenticatorReq, GssChecksum), NDR_POINTER, -1, &ndr_KERB_ASN1_DATA_descr_s }, { "KeyUsage", offsetof(CreateApReqAuthenticatorReq, KeyUsage), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, }; static const NdrStructDescr CreateApReqAuthenticatorReq_struct = { "CreateApReqAuthenticatorReq", ARRAYSIZE(CreateApReqAuthenticatorReq_fields), CreateApReqAuthenticatorReq_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(CreateApReqAuthenticatorReq) /* ========================== CreateApReqAuthenticatorResp ======================== */ static const NdrFieldStruct CreateApReqAuthenticatorResp_fields[] = { { "AuthenticatorTime", offsetof(CreateApReqAuthenticatorResp, AuthenticatorTime), NDR_NOT_POINTER, -1, &ndr_uint64_descr_s }, { "Authenticator", offsetof(CreateApReqAuthenticatorResp, Authenticator), NDR_NOT_POINTER, -1, &ndr_KERB_ASN1_DATA_descr_s }, { "KerbProtocolError", offsetof(CreateApReqAuthenticatorResp, KerbProtocolError), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, }; static const NdrStructDescr CreateApReqAuthenticatorResp_struct = { "CreateApReqAuthenticatorResp", ARRAYSIZE(CreateApReqAuthenticatorResp_fields), CreateApReqAuthenticatorResp_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(CreateApReqAuthenticatorResp) /* ========================== UnpackKdcReplyBodyReq ======================== */ static const NdrFieldStruct UnpackKdcReplyBodyReq_fields[] = { { "EncryptedData", offsetof(UnpackKdcReplyBodyReq, EncryptedData), NDR_POINTER_NON_NULL, -1, &ndr_KERB_ASN1_DATA_descr_s }, { "Key", offsetof(UnpackKdcReplyBodyReq, Key), NDR_POINTER_NON_NULL, -1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }, { "StrenghtenKey", offsetof(UnpackKdcReplyBodyReq, StrengthenKey), NDR_POINTER, -1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s }, { "Pdu", offsetof(UnpackKdcReplyBodyReq, Pdu), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "KeyUsage", offsetof(UnpackKdcReplyBodyReq, KeyUsage), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, }; static const NdrStructDescr UnpackKdcReplyBodyReq_struct = { "UnpackKdcReplyBodyReq", ARRAYSIZE(UnpackKdcReplyBodyReq_fields), UnpackKdcReplyBodyReq_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(UnpackKdcReplyBodyReq) /* ========================== UnpackKdcReplyBodyResp ======================== */ static const NdrFieldStruct UnpackKdcReplyBodyResp_fields[] = { { "KerbProtocolError", offsetof(UnpackKdcReplyBodyResp, KerbProtocolError), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "ReplyBody", offsetof(UnpackKdcReplyBodyResp, ReplyBody), NDR_NOT_POINTER, -1, &ndr_KERB_ASN1_DATA_descr_s } }; static const NdrStructDescr UnpackKdcReplyBodyResp_struct = { "UnpackKdcReplyBodyResp", ARRAYSIZE(UnpackKdcReplyBodyResp_fields), UnpackKdcReplyBodyResp_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(UnpackKdcReplyBodyResp) /* ========================== DecryptApReplyReq ======================== */ static const NdrFieldStruct DecryptApReplyReq_fields[] = { { "EncryptedReply", offsetof(DecryptApReplyReq, EncryptedReply), NDR_POINTER_NON_NULL, -1, &ndr_KERB_ASN1_DATA_descr_s }, { "Key", offsetof(DecryptApReplyReq, Key), NDR_POINTER_NON_NULL, -1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s } }; static const NdrStructDescr DecryptApReplyReq_struct = { "DecryptApReplyReq", ARRAYSIZE(DecryptApReplyReq_fields), DecryptApReplyReq_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(DecryptApReplyReq) /* ========================== PackApReplyReq ======================== */ static const NdrFieldStruct PackApReplyReq_fields[] = { { "Reply", offsetof(PackApReplyReq, Reply), NDR_POINTER_NON_NULL, -1, &ndr_KERB_ASN1_DATA_descr_s }, { "ReplyBody", offsetof(PackApReplyReq, ReplyBody), NDR_POINTER_NON_NULL, -1, &ndr_KERB_ASN1_DATA_descr_s }, { "SessionKey", offsetof(PackApReplyReq, SessionKey), NDR_POINTER_NON_NULL, -1, &ndr_KERB_RPC_ENCRYPTION_KEY_descr_s } }; static const NdrStructDescr PackApReplyReq_struct = { "PackApReplyReq", ARRAYSIZE(PackApReplyReq_fields), PackApReplyReq_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(PackApReplyReq) /* ========================== PackApReplyResp ======================== */ static const NdrFieldStruct PackApReplyResp_fields[] = { { "PackedReplySize", offsetof(PackApReplyResp, PackedReplyHints), NDR_NOT_POINTER, -1, &ndr_uint32_descr_s }, { "PackedReply", offsetof(PackApReplyResp, PackedReply), NDR_POINTER_NON_NULL, 0, &ndr_uint8Array_descr_s }, }; static const NdrStructDescr PackApReplyResp_struct = { "PackApReplyResp", ARRAYSIZE(PackApReplyResp_fields), PackApReplyResp_fields }; RDPEAR_SIMPLE_MESSAGE_TYPE(PackApReplyResp)