Menu

Bind/search using Group Managed Service Account (gMSA)

2021-09-13
2022-11-15
  • Martin Jacobsen

    Martin Jacobsen - 2021-09-13

    Hi.

    I'm new with LDAP, and I'm having some problems binding/searching using a gMSA. I also can't find anything useful online about how to do this. We have some customers that want to change the account used for running services to a gMSA, so I have tried to create a connection only using the DN of the group managed service account. I've tried using LDAPConnectionOptions with setBindWithDNRequiresPassword=true, and then using a SimpleBindRequest with the gMSA DN and an empty password. The binding seems to go ok, as I receive a result code='0 (success)', but when I try to do a search, I get the following exception:

    Exception in thread "main" LDAPException(resultCode=1 (operations error), numEntries=0, numReferences=0, diagnosticMessage='000004DC: LdapErr: DSID-0C090A5C, comment: In order to perform this operation a successful bind must be completed on the connection., data 0, v4563 ', ldapSDKVersion=6.0.1, revision=ca9bd061ccfdbf76247f8d6e011f1bc7f53eb520')

    I've tried all sorts of stuff, but I can't seem to figure out how to do this.

     
    • Jim Willeke

      Jim Willeke - 2021-09-13

      Using the "gMSA DN and an empty password" will make a
      successful "Unauthenticated Bind". For Microsoft Active Directory in most
      cases the operation is not usable to perform most LDAP Operations.

      AFIK, Microsoft Active Directory does not distinguish between
      Unauthenticated and anonymous operations.

      By default, anonymous Lightweight Directory Access Protocol (LDAP)
      operations to Active Directory, other than rootDSE searches and binds, are
      not permitted in Microsoft Windows Server 2003 and later.

      https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/anonymous-ldap-operations-active-directory-disabled

      The operations for gMSA accounts as used within Microsoft Active Directory
      use API operations which are outside of LDAP.
      Although Microsoft Active Directory supports LDAP it also has many other
      non-LDAP APIs.

      You could, retrieve the password and then make the bind, however, the
      password is managed by Microsoft Active Directory and could be changed
      at any time.
      The details for Password are stored in the msDS-ManagedPassword and
      msDS-ManagedPasswordId attributes of the object, these could be returned in
      LDAP Search, however, it does require a specific permissions and a "Secure
      connection" for LDAP Search to return the details as they are protected
      attributes.

      --
      -jim
      Jim Willeke

      On Mon, Sep 13, 2021 at 5:14 AM Martin Jacobsen martin-jacobsen@users.sourceforge.net wrote:

      Hi.

      I'm new with LDAP, and I'm having some problems binding/searching using a
      gMSA. I also can't find anything useful online about how to do this. We
      have some customers that want to change the account used for running
      services to a gMSA, so I have tried to create a connection only using the
      DN of the group managed service account. I've tried using
      LDAPConnectionOptions with setBindWithDNRequiresPassword=true, and then
      using a SimpleBindRequest with the gMSA DN and an empty password. The
      binding seems to go ok, as I receive a result code='0 (success)', but
      when I try to do a search, I get the following exception:

      Exception in thread "main" LDAPException(resultCode=1 (operations error),
      numEntries=0, numReferences=0, diagnosticMessage='000004DC: LdapErr:
      DSID-0C090A5C, comment: In order to perform this operation a successful
      bind must be completed on the connection., data 0, v4563 ',
      ldapSDKVersion=6.0.1, revision=ca9bd061ccfdbf76247f8d6e011f1bc7f53eb520')

      I've tried all sorts of stuff, but I can't seem to figure out how to do
      this.


      Bind/search using Group Managed Service Account (gMSA)
      https://sourceforge.net/p/ldap-sdk/discussion/1001257/thread/bb0f55349f/?limit=25#c563


      Sent from sourceforge.net because you indicated interest in
      https://sourceforge.net/p/ldap-sdk/discussion/1001257/

      To unsubscribe from further messages, please visit
      https://sourceforge.net/auth/subscriptions/

       
      • Martin Jacobsen

        Martin Jacobsen - 2021-09-23

        Thx for your reply Jim. I've tried going down this path, using a C# console application to retrieve the contents of the msDS-ManagedPassword attribute, and then extract the password from the byte blob. I then call this C# console application from a Java application using Runtime.exec(). I can verify that the password is correct, as I am able to authenticate using the sAMAccountName and a copy/paste of the password directly in the MS Ldap application on the server. I am however not able to make a bind using UnboundId LDAP SDK. I've tried various things without success. I'm using a SimpleBindRequest with the DN of the gMSA and the password either as a String or a byte array, but the result is the same:

        BindResult: BindResult(resultCode=49 (invalid credentials), messageID=1, diagnosticMessage='80090308: LdapErr: DSID-0C090439, comment: AcceptSecurityContext error, data 52e, v4563 ', hasServerSASLCredentials=false)

        I initially thought that it had to be some kind of encoding problem, but I'm not convinced this is the problem anymore, since I can authenticate using copy/paste. I am aware that placing the text into the clipboard might affect the encoding, but it still works this way for some reason. I have tried everything I can think off, so any advice would be much appreciated!

         

        Last edit: Martin Jacobsen 2021-09-23
        • Jim Willeke

          Jim Willeke - 2021-09-23

          You should post your code.
          In Microsoft Active Directory the default password attribute is unicodePwd
          and I this requires special encoding as shown in this example:
          https://github.com/jwilleke/Examples-JNDI/blob/master/src/com/willeke/samples/ldap/jndi/ADConnection.java
          See (updateUserPassword(String username, String password))

          --
          -jim
          Jim Willeke

          On Thu, Sep 23, 2021 at 5:40 AM Martin Jacobsen martin-jacobsen@users.sourceforge.net wrote:

          Thx for your reply Jim. I've tried going down this path, using a C#
          console application to retrieve the contents of the msDS-ManagedPassword
          attribute, and then extract the password from the byte blob. I can verify
          that the password is correct, as I am able to authenticate using the
          sAMAccountName and a copy/paste of the password directly in the MS Ldap
          application on the server. I am however not able to make a bind using
          UnboundId LDAP SDK. I've tried various things without success. I'm using a
          SimpleBindRequest with the DN of the gMSA and the password either as a
          String or a byte array, but the result is the same:

          BindResult: BindResult(resultCode=49 (invalid credentials), messageID=1,
          diagnosticMessage='80090308: LdapErr: DSID-0C090439, comment:
          AcceptSecurityContext error, data 52e, v4563 ',
          hasServerSASLCredentials=false)

          I initially thought that it had to be some kind of encoding problem, but
          I'm not convinced this is the problem anymore, since I can authenticate
          using copy/paste. I am aware that placing the text into the clipboard might
          affect the encoding, but it still works this way for some reason. I have
          tried everything I can think off, so any advice would be much appreciated!


          Bind/search using Group Managed Service Account (gMSA)
          https://sourceforge.net/p/ldap-sdk/discussion/1001257/thread/bb0f55349f/?limit=25#c563/e9e9/3a9a


          Sent from sourceforge.net because you indicated interest in
          https://sourceforge.net/p/ldap-sdk/discussion/1001257/

          To unsubscribe from further messages, please visit
          https://sourceforge.net/auth/subscriptions/

           
          • Martin Jacobsen

            Martin Jacobsen - 2021-09-23

            Thx for your fast reply Jim! I have posted two classes here: https://github.com/nsismartin/gMSA_bind_java/tree/main

            This is only the java side of the project. It reads the B64 encoded password from the output stream from a C# console application, decodes the password and tries to bind. Everything starts in the main method of the RuntimeExecPOC.java.

            Please let me know if you have any problems getting access to the code, or similar.

            /Martin

             
  • Martin Jacobsen

    Martin Jacobsen - 2021-09-26

    Did you get a chance to look at the code yet Jim? I did an experiment the other day, setting the same password as the gMSA on a normal user, and then tried binding to the user using my RuntimeExecPOC application, and it worked just fine. I'm beginning to think that it might just be a problem when using a gMSA. That you simply can't bind to it the normal way. It would be nice to find a solution for Java applications though. It doesn't feel right that this is something that you simply can't do in Java through LDAP.

     
    • Jim Willeke

      Jim Willeke - 2021-09-26

      I have but:
      I am not sure what the problem is and
      I do not know what the "B64 encoded password" would be. Would you even be
      able to bind with that?

      Unfortunately, I also have no test environment for this and have not done
      Java stuff in a while.
      --
      -jim
      Jim Willeke

      On Sun, Sep 26, 2021 at 10:02 AM Martin Jacobsen martin-jacobsen@users.sourceforge.net wrote:

      Did you get a chance to look at the code yet Jim? I did an experiment the
      other day, setting the same password as the gMSA on a normal user, and then
      tried binding to the user using my RuntimeExecPOC application, and it
      worked just fine. I'm beginning to think that it might just be a problem
      when using a gMSA. That you simply can't bind to it the normal way. It
      would be nice to find a solution for Java applications though. It doesn't
      feel right that this is something that you simply can't do in Java through
      LDAP.


      Bind/search using Group Managed Service Account (gMSA)
      https://sourceforge.net/p/ldap-sdk/discussion/1001257/thread/bb0f55349f/?limit=25#d110


      Sent from sourceforge.net because you indicated interest in
      https://sourceforge.net/p/ldap-sdk/discussion/1001257/

      To unsubscribe from further messages, please visit
      https://sourceforge.net/auth/subscriptions/

       
      • Jim Willeke

        Jim Willeke - 2021-09-26

        Well, I just could not leave it alone.
        This is just from some searching, no word on the real hoto.

        The return value of msDS-ManagedPassword is a msaBlob of a lot of items.
        (sort of described here https://markgamache.blogspot.com/p/gmsa-magic.html)
        Looks like this guy got it to work but using signing and sealing in a .NET
        project.
        https://markgamache.blogspot.com/2016/12/any-sufficiently-advanced-active.html

        Good Luck.

        -jim
        Jim Willeke

        On Sun, Sep 26, 2021 at 3:56 PM Jim Willeke jwilleke@users.sourceforge.net
        wrote:

        I have but:
        I am not sure what the problem is and
        I do not know what the "B64 encoded password" would be. Would you even be
        able to bind with that?

        Unfortunately, I also have no test environment for this and have not done
        Java stuff in a while.
        --
        -jim
        Jim Willeke

        On Sun, Sep 26, 2021 at 10:02 AM Martin Jacobsen
        martin-jacobsen@users.sourceforge.net
        %0Dmartin-jacobsen@users.sourceforge.net wrote:

        Did you get a chance to look at the code yet Jim? I did an experiment the
        other day, setting the same password as the gMSA on a normal user, and then
        tried binding to the user using my RuntimeExecPOC application, and it
        worked just fine. I'm beginning to think that it might just be a problem
        when using a gMSA. That you simply can't bind to it the normal way. It
        would be nice to find a solution for Java applications though. It doesn't
        feel right that this is something that you simply can't do in Java through
        LDAP.


        Bind/search using Group Managed Service Account (gMSA)

        https://sourceforge.net/p/ldap-sdk/discussion/1001257/thread/bb0f55349f/?limit=25#d110

        Sent from sourceforge.net because you indicated interest in
        https://sourceforge.net/p/ldap-sdk/discussion/1001257/

        To unsubscribe from further messages, please visit
        https://sourceforge.net/auth/subscriptions/


        Bind/search using Group Managed Service Account (gMSA)
        https://sourceforge.net/p/ldap-sdk/discussion/1001257/thread/bb0f55349f/?limit=25#d110/a8f3


        Sent from sourceforge.net because you indicated interest in
        https://sourceforge.net/p/ldap-sdk/discussion/1001257/

        To unsubscribe from further messages, please visit
        https://sourceforge.net/auth/subscriptions/

         
        • Martin Jacobsen

          Martin Jacobsen - 2021-09-26

          That is exactly the code I'm using in my C# application to get a hold of the msDS-ManagedPassword blob. I then run it into some other code to extract the password in clear text. It just looks like gibberish, but it's the password. I use the DSInternals module mentioned in this blog post: https://www.dsinternals.com/en/retrieving-cleartext-gmsa-passwords-from-active-directory/#comment-119852

          Even though I have the actual password, I've not been able to perform a successful bind from a java application yet.

           
        • Martin Jacobsen

          Martin Jacobsen - 2021-09-28

          I've tried to bind with the gMSA for several weeks now, but without success. Even though I'm able to get a hold of the password of the gMSA, I just can't get the bind to work through UnboundID LDAP SDK or even JNDI for that matter. It works fine in C# both with or without password. It's like there's something denying access when it's not a MS technology trying to make contact with AD through a gMSA. At the same time, I don't quite understand why this isn't something that is easily supported through the UnboundID LDAP SDK.

           
      • Martin Jacobsen

        Martin Jacobsen - 2021-09-26

        The problem is this: I have retrieved the password of the gMSA that I'm trying to bind to, but every time I do a simple bind using the DN of the gMSA and the retrieved password, I get a error 49 - invalid credentials. I can verify that the password is correct, since I can c/p it and do a bind in MS LDP.

        The reason for base64 encoding the password was just to wrap it up in something when writing it to the std output stream from the C# application, and then of course I decode it after reading it from the std output stream in my java application.

        EDIT: I might have misunderstood what you meant when I wrote the above, but this pretty much wraps up what my problem is :)

         

        Last edit: Martin Jacobsen 2021-09-27
  • Neil Wilson

    Neil Wilson - 2021-09-28

    Unfortunately, I can’t be of much direct help in this issue, since it’s specific to Active Directory, and I don’t have a lot of experience with or access to an Active Directory Instance for testing. However, I might be able to provide a couple of tips to help point you in the right direction.

    First, I did look at the DSInternals article you posted above, and at the Microsoft MSDS-MANAGEDPASSWORD_BLOB article that it references. Even though that blob packs a lot of information into it, it should be a fairly straightforward process to get the clear-text password out of it. It looks like you should just need to start reading from the 17th byte (array offset 16) of the msDS-ManagedPassword value and keep going until you find a null terminator (a byte of 0x00).

    According to the DSInternals article, you should end up with a raw password that is 256 bytes long, which represents 128 UTF-16 characters (and I’m guessing that it’s UTF-16LE, since I know that Microsoft really likes that encoding for some reason). My guess is that so far, you’ve tried using those 256 bytes directly as the password, and that might be the problem. That’s because while Microsoft provides the password as a UTF-16 string, LDAP generally uses UTF-8 for its strings, and that’s definitely the case for the password in a bind request. RFC 4511 section 4.2 specifically states: “Textual passwords (consisting of a character sequence with a known character set and encoding) transferred to the server using the simple AuthenticationChoice SHALL be transferred as UTF-8 [RFC3629] encoded [Unicode].” So my guess is that you need to convert the UTF-16 password into UTF-8 before using it in the bind request. You can do that with the following code:

    String passwordString = new String(utf16LEPasswordBytes, StandardCharsets.UTF_16LE);
    byte[] utf8PasswordBytes = passwordString.getBytes(StandardCharsets.UTF_8);
    

    When creating the bind request, it should be sufficient to either use the password string obtained from the first step because the LDAP SDK will automatically encode the string to UTF-8 when creating the bind request, but you could also try creating the bind request directly from the UTF-8 bytes obtained from the second step.

    If that doesn’t work, and if you’re able to get it working with a C# application, then the best option would probably be to capture the traffic generated by the C# application and examine it to see how the bytes it’s sending to Active Directory might be different from the bytes that the LDAP SDK is sending. If you can send the C# bind request over an unencrypted connection, then a network packet capture tool like Wireshark might be sufficient, but my guess is that may not be an option in this case. So instead, you can use the ldap-debugger tool that we provide with the LDAP SDK (in the tools directory). It’s a very simple LDAP proxy server that you can put between the client and an LDAP server and it will dump the details of the requests and responses that pass through it to standard output or to a file. So if you run it and point it at the Active Directory server, and then if you point your C# application at the ldap-debugger tool rather than directly at AD, then that should allow us to see the requests that the C# application is sending.

    To do that, you can use a command like the following:

    ldap-debugger --hostname {ad-address} --port 636 --useSSL --trustAll --listenUsingSSL --generateSelfSignedCertificate --outputFile ldap-debugger.txt
    

    When you run it, it will automatically pick a free port on the system (or you can explicitly specify a port using the --listenPort argument). Then, point your C# application at the ldap-debugger tool (you might need to do something to convince it to trust the self-signed certificate that the tool generates for TLS-encrypted connections) and send the bind request as you normally would. When you do that, the decoded content of the bind request (and any other requests and responses passing through the tool) should be written to the ldap-debugger.txt file. If you can provide the captured details from that bind request, then it should let us see what the difference is and hopefully figure out how to do the same thing from an LDAP SDK request.

     
    • Martin Jacobsen

      Martin Jacobsen - 2021-09-29

      Thank you for the answer and ideas Neil. I have also thought about using a program to capture the traffic, but I actually didn't know there was these tools in the SDK. That's nice to know.

      I have tried the ldap-debugger a bit today, and while I did manage to capture some test traffic, using the admin login from a Java app on my dev machine, I'm still not there yet where I can use the C# application and capture the traffic. I had some problems with the auth mechanism used in the C# application (Microsoft Negotiate Authentication). I'm not really sure if it's a standard auth mechanism or not, but it's not supported in the ldap-debugger as far as I can tell.

      We decided to try it directly on the server and get it working using ldp.exe, a tool where we have had success binding as the gMSA, but we never quite got it working. Even though we tried using the admin account to bind we couldn't get it to work.

      We were also quite puzzled by the fact that you have to supply the ldap-debugger tool with distinguished name and password during setup when just running it. Is this another mode of operation for the tool than the one you supplied with the command arguments?

       
  • Neil Wilson

    Neil Wilson - 2021-09-29

    You don’t actually need to invoke the ldap-debugger tool with authentication information. By default, it will just create an unauthenticated connection to the target server and will directly forward any requests from the client to the server, and responses from the server to the client, including those that authenticate the client. The fact that those arguments are provided is primarily because it’s using a more general-purpose command-line framework that the LDAP SDK provides for creating command-line tools that can interact with LDAP servers, and you don’t need to use them if you don’t need the tool to authenticate the connections it creates.

    I’m not familiar with the Microsoft Negotiate Authentication mechanism. I’m guessing that it’s not LDAP simple authentication, so I assume that it’s some type of SASL mechanism. But even if that’s the case, then ldap-debugger shouldn’t need to have direct knowledge of that mechanism because it will still be able to parse it as a generic SASL request to see the mechanism name and any credentials that are included in it, and it should at least be able to dump that much. It will then just forward that request on to the target server exactly as it was provided by the client. If it’s using a Microsoft-proprietary SASL mechanism, then the LDAP SDK won’t support it out of the box, but if it’s documented somewhere, then you could potentially write your own support for it by extending SASLBindRequest and handling all of the mechanism-specific logic in your class.

    If for some reason it’s actually using something other than LDAP simple or SASL authentication, then it wouldn’t actually be using the LDAP protocol, because those are the only two types of authentication defined in LDAP, and the LDAP SDK will not support it. In that case, the ldap-debugger tool would fail to parse the request, and it wouldn’t be able to forward it to the target server.

     
  • Martin Jacobsen

    Martin Jacobsen - 2021-10-18

    It's been a while since I wrote about my gMSA adventure, so I thought it was time for an update. I have finally managed to get this to work. A lot of time has been spent trying to figure out why we couldn't get the login to work as the gMSA, but at least some of the issue seemed to be related to using a SimpleBind. Now we are using a Digest-MD5 bind instead and it works! Here's a quick rundown of the solution:

    Since there's to my knowledge no easy way to retrieve the gMSA password from Java, we are using some C# code residing in a console application. Namely the DSInternals common module, which can be installed as a nuget package, and some code for decoding the gMSA byte blob. Both links can be found below.

    A big thanks to Jim and Neil for helping out with this!

    https://github.com/MichaelGrafnetter/DSInternals
    https://markgamache.blogspot.com/p/gmsa-magic.html

     

Log in to post a comment.