Search code examples
c#unicodeactive-directoryhpcgmsa

XmlBinaryNodeWriter failing to serialize unicode Group Managed Service Account password for web service transmission


Backstory: We are submitting an HPC job using the microsoft HPC pack 2019 SP3 SDK. HPC Doesn't natively support Active Directory gMSA accounts, so we obtain the gMSA account password via AD. The MSA password is a 256 byte password, expressed in unicode (UTF16? - .NET displays the password as 128 length)

Issue: When we attempt to submit the job this password, via the HPC webservice, the XmlBinaryNodeWriter fails to serialize with the following error:

Exception thrown: 'System.Text.EncoderFallbackException' in mscorlib.dll Unable to translate Unicode character \uDC33 at index 43 to specified code page.

Here is our gMSA password (some parts redacted for security):

enter image description here

Can anyone explain exactly why the serialization is failing? I guess it could be due to non displayable characters or something?

Edit: Lookling at the callstack, it appears like an MSA password is attempted to be serialized as UTF8 when it's infact UTF16. Anyone know how to tell the XmlObjectSerializer that a string is UTF16 and not 8?

enter image description here


Solution

  • Got to the bottom of it: This wasn't an issue with HPC/the serializer.

    Rather, a potential bug in the DSInternals.Common.dll library, where it pulls the gMSA password out of active directory/extracts it from the SecureString object, and returns it as a string.

    Comparing the correctly produced string [top] vs the incorrectly produced string [bottom] we can see some corruption.

    enter image description here

    Old code:

    var passwordBlob    = (byte[])result.Properties["msDS-ManagedPassword"][0];
    var managedPassword = new ManagedPassword(passwordBlob);
    var password        = managedPassword.CurrentPassword; 
    

    New code:

    var passwordBlob    = (byte[])result.Properties["msDS-ManagedPassword"][0];
    var managedPassword = new ManagedPassword(passwordBlob);
    var password = System.Text.Encoding.Unicode.GetString(managedPassword.SecureCurrentPassword.ToByteArray());
    

    Hopefully this helps someone else out who is manually extracting passwords from gMSA accounts.