Fix missing big-endian conversions for multi-byte NTLM wire fields#125039
Fix missing big-endian conversions for multi-byte NTLM wire fields#125039
Conversation
Co-authored-by: rzikm <32671551+rzikm@users.noreply.github.com>
src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs
Outdated
Show resolved
Hide resolved
src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs
Outdated
Show resolved
Hide resolved
Co-authored-by: rzikm <32671551+rzikm@users.noreply.github.com>
…esses Co-authored-by: rzikm <32671551+rzikm@users.noreply.github.com>
|
@ShreyaLaxminarayan Can you test with this change? I don't have a Big-Endian machine available to me at the moment. |
|
Tagging subscribers to this area: @dotnet/ncl, @bartonjs, @vcsjones |
There was a problem hiding this comment.
Pull request overview
Fixes remaining big-endian issues in Managed NTLM wire-format overlay structs by making several multi-byte fields endianness-aware, aligning them with the existing pattern used for other fields.
Changes:
- Update
MessageField.Length/MaximumLengthto use private backing fields + endianness-aware properties; simplifyGetField/SetFieldaccordingly. - Update
ChallengeMessage.FlagsandAuthenticateMessage.Flagsto use private backing fields + endianness-aware properties (removing call-site byte swapping). - Update
NtChallengeResponse.Timeto use a private backing field + endianness-aware property.
src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.cs
Show resolved
Hide resolved
|
@Dotnet-s390x build |
|
Build Queued.. To cancel the current build, please comment: |
|
Build Failed Build Error Summary |
|
Failures are un-related to the changes in the PR |
|
@copilot invoke the code-review skill and post the analysis/results as a comment on this PR |
🤖 Copilot Code Review — PR #125039Holistic AssessmentMotivation: Justified. PR #124598 added big-endian support to ManagedNtlm but inconsistently applied the pattern — Approach: Correct. The fix consistently applies the established Summary: ✅ LGTM. The change is correct, complete, and consistent with the existing codebase pattern. All multi-byte fields in NTLM wire-format overlay structs are now endianness-aware. Struct layouts are preserved (private backing fields occupy the same positions as the former public fields). The code simplifications ( Detailed Findings✅ Struct layout preservation verifiedAll ✅ Endianness coverage is completeAudited all struct fields in the file:
✅
|
PR #124598 added big-endian support for ManagedNtlm but missed several multi-byte fields. On big-endian architectures,
MessageField.Length/MaximumLength,ChallengeMessage.Flags,AuthenticateMessage.Flags, andNtChallengeResponse.Timewere still read/written without byte-swap, corrupting the NTLM wire format.Description
All changes follow the endianness-aware property pattern already established by
_payloadOffset,_productBuild, andNegotiateMessage.Flags:MessageFieldLengthandMaximumLength(ushort) converted from public fields to private backing fields with endianness-aware propertiesunsaferemoved from struct (no fixed arrays, no longer needed)GetFieldLengthandGetFieldOffsethelpers removed;GetFieldnow accessesfield.Lengthandfield.PayloadOffsetdirectlySetField→ direct property assignments (wasMemoryMarshal.AsBytes+WriteInt16/32LittleEndian)ChallengeMessage.Flags(uint)_flags+ property; removed the inlineBitConverter.IsLittleEndianconversion at the call siteAuthenticateMessage.Flags(uint)ChallengeMessage.FlagsNtChallengeResponse.Time(long)_time+ endianness-aware propertyAll
[StructLayout(LayoutKind.Sequential)]struct layouts are unchanged — backing fields remain in identical declaration positions.Original prompt
Background
PR #124598 added big-endian support for ManagedNtlm by introducing endianness-aware getters/setters on some struct fields. However, several multi-byte fields were missed and still need conversion. The NTLM wire protocol is little-endian, so all multi-byte fields in the overlay structs must be stored in little-endian format. On big-endian architectures, the fields that are accessed directly (without
BinaryPrimitivesconversion) will have the wrong byte order.The file to modify is:
src/libraries/System.Net.Security/src/System/Net/NegotiateAuthenticationPal.ManagedNtlm.csWhat PR #124598 already fixed
The following fields already have proper endianness-aware getters/setters:
MessageField._payloadOffset(int) — hasPayloadOffsetproperty withReverseEndiannessVersion._productBuild(ushort) — hasProductBuildproperty withReverseEndiannessNegotiateMessage._flags(Flags/uint) — hasFlagsproperty withReverseEndiannessAdditionally,
ChallengeMessage.Flagsis read with an inline endianness conversion at the call site (line 592).What still needs to be fixed
The following multi-byte fields are still directly accessed without endianness conversion and need the same getter/setter treatment:
1.
MessageField.LengthandMessageField.MaximumLength(bothushort)These are currently public fields (
public ushort Length; public ushort MaximumLength;) accessed directly. They should be made private backing fields with endianness-aware property getters/setters, like_payloadOffsetalready is.Note: After adding properties, the helper functions
GetFieldLength,GetFieldOffset, andSetFieldthat currently useBinaryPrimitives.ReadInt16LittleEndian/WriteInt16LittleEndian/WriteInt32LittleEndianto read/write the raw bytes of MessageField should be refactored to simply use the new properties directly. This eliminates the redundant byte-level endianness handling since the properties now handle it. Similarly,GetFieldOffsetalready reads the offset viaReadInt16LittleEndianon raw bytes, but thePayloadOffsetproperty getter already handles this — soGetFieldOffsetshould just usefield.PayloadOffset. After this refactoring, theunsafemodifier can likely be removed fromGetFieldLengthandGetFieldOffset.2.
ChallengeMessage.Flags(Flags/uint)Currently a public field. The conversion is done inline at the call site (line 592) with:
This should be converted to a private backing field with an endianness-aware property (like
NegotiateMessage.Flagsalready is), and the call site should simply readchallengeMessage.Flagswithout the inline conversion.3.
AuthenticateMessage.Flags(Flags/uint)Currently a public field that is written to directly on line 646:
This should be converted to a private backing field with an endianness-aware property getter/setter, like
NegotiateMessage.Flagsalready is.4.
NtChallengeResponse.Time(long)Currently a public field written on line 424:
This needs to be stored as little-endian on the wire. Should be converted to a private backing field with an endianness-aware property.
5.
NtChallengeResponse._reserved3(int) andNtChallengeResponse._reserved4(int)These are private
intfields. Although they are reserved (always zero-initialized viaClear()), they should still have endianness conversion for correctness and consistency. Since they're always zero, the conversion is a no-op in practice, but it's good form. However, since they are private and only ever zero, these can be left as-is if the team prefers — the key point isTimeabove.Summary of changes needed
MessageField: MakeLengthandMaximumLengthprivate with endianness-aware properties. SimplifyGetFieldLength,GetFieldOffset, andSetFieldto use the new properties.ChallengeMessage: MakeFlagsa private backing field with endianness-aware property (same pattern asNegotiateMessage.Flags). Remove inline conversion at call site.AuthenticateMessage: MakeFlagsa private backing field with endianness-aware property.NtChallengeResponse: MakeTimea private backing field with endianness-aware property.The pattern for all should be the same as the existing conversions in the file:
This pull request was created from Copilot chat.
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.