GSSException: Message stream modified (41)

12,584

Thanks for this! Just for reference, uppercase of the realm (ie. the realm should be 100% correct and in uppercase) is very important to avoid "Exception: krb_error 41 Message stream modified (41) ".

Here's an example of correct notation:

[libdefaults] 
default_realm = EXAMPLE.COM

[realms] 
EXAMPLE.COM = { 
kdc = domaincontroller.example.com
admin_server = domaincontroller.example.com
default_domain = EXAMPLE.COM
} 

[domain_realm] 
.example.com = EXAMPLE.COM
example.com = EXAMPLE.COM

Regards,

Nika.

Share:
12,584
Matan
Author by

Matan

Updated on June 11, 2022

Comments

  • Matan
    Matan about 2 years

    I'm working with an LDAP in forest architecture (all servers and my server are windows). I'm binding to the AD using NTLM authentication.

    I have a JAVA code that perform the operations against the LDAP server.

    The code is wrapped as a tomcat servlet.

    When running the JAVA code directly (just executing the LDAP authentication code as an application), the bind works both against the local domain (local domain = I logged in to windows, and ran this process with a user of this domain) and foreign domains.

    When running the JAVA code as a servlet, the bind works and authenticates users from one domain but does not work if I'm trying to authenticate users from other domain, it won't work (it will work only if I'll restart tomcat).

    I'm getting an exception:

    GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Message stream modified (41))]]
    

    I'll mention that it is the same code, with the same configurations and the same krb5 file.

    Edit: More information:

    This is my code:

    public void func(String realm, String kdc) {
        try {
          URL configURL = getClass().getResource("jaas_ntlm_configuration.txt");
            System.setProperty("java.security.auth.login.config", configURL.toString());
    
                System.setProperty("java.security.krb5.realm", realm);
                System.setProperty("java.security.krb5.kdc",kdc);
    
            // If the application is run on NT rather than Unix, use this name
            String loginAppName = "MyConfig";
    
            // Create login context
            LoginContext lc = new LoginContext(loginAppName, new SampleCallbackHandler());
    
            // Retrieve the information on the logged-in user
            lc.login();
    
            // Get the authenticated subject
            Subject subject = lc.getSubject();
    
            System.out.println(subject.toString());
    
            Subject.doAs(subject, new JndiAction(new String[] { "" }));
        }
        catch (LoginException e) {
          e.printStackTrace();
        }
    }
    
    class JndiAction implements java.security.PrivilegedAction {
        private String[] args;
    
        public JndiAction(String[] origArgs) {
            this.args = (String[])origArgs.clone();
        }
    
        public Object run() {
            performJndiOperation(args);
            return null;
        }
    
        private static void performJndiOperation(String[] args) {
    
            // Set up environment for creating initial context
            Hashtable env = new Hashtable(11);
    
            env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    
            // Must use fully qualified hostname
            env.put(Context.PROVIDER_URL, "ldap://server:389");
    
            // Request the use of the "GSSAPI" SASL mechanism
            // Authenticate by using already established Kerberos credentials
            env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
    
            try {
                // Create the initial context
                DirContext ctx = new InitialLdapContext(env, null);
    
    
                // Close the context when we're done
                ctx.close();
            } catch (NamingException e) {
                e.printStackTrace();
            }
        }
    }
    

    And my jaas_ntlm_configuration.txt file contains:

    MyConfig { com.sun.security.auth.module.Krb5LoginModule required
    useTicketCache=true
    doNotPrompt=false;
    };
    

    My krb5.conf file is:

    # 
    # All rights reserved.
    #
    #pragma ident   @(#)krb5.conf   1.1 00/12/08
    
    [libdefaults]
        default_tkt_enctypes = des3-cbc-sha1 des-cbc-md5 des-cbc-crc
        default_tgs_enctypes = des3-cbc-sha1 des-cbc-md5 des-cbc-crc
        forwardable  = true
        renewable  = true
        noaddresses = true
        clockskew  = 300
    
    [realms]
            SUB1.DOMAIN.COM = {
                    kdc = DDC.SUB1.DOMAIN.COM
            default_domain=DOMAIN.COM
            }
        SUB2.DOMAIN.COM = {
                    kdc = DDC.SUB.DOMAIN.COM
            default_domain=DOMAIN.COM
            }
        SUB3.DOMAIN.COM = {
                    kdc = DDC.SUB3.DOMAIN.COM
            default_domain=DOMAIN.COM
            }
    
    [domain_realm]
        .DOMAIN.COM = SUB1.DOMAIN.COM
        .DOMAIN.COM = SUB2.DOMAIN.COM
        .DOMAIN.COM = SUB3.DOMAIN.COM
    
    [logging]
            default = FILE:/var/krb5/kdc.log
            kdc = FILE:/var/krb5/kdc.log
        kdc_rotate = {
    
    # How often to rotate kdc.log. Logs will get rotated no more
    # often than the period, and less often if the KDC is not used
    # frequently.
    
            period = 1d
    
    # how many versions of kdc.log to keep around (kdc.log.0, kdc.log.1, ...)
    
            versions = 10
        }
    
    [appdefaults]   
        kinit = {
            renewable = true
            forwardable= true
        }
        rlogin = {
            forwardable= true
        }
        rsh = {
            forwardable= true
        }
        telnet = {
                autologin = true 
            forwardable= true
        }
    

    I added the following as java parameters:

    -Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.krb5.conf="krb5.conf" -Dsun.security.krb5.debug=true
    

    If I call func("SUB*.DOMAIN.COM", "DDC.SUB*.DOMAIN.COM") always with the same subdomain - it will work, but if I'll call with one subdomain and then with another, the second will fail.

    More information:

    Here is the output with krb5.debug=true:

    java -Xmx100m -cp gssapi_test.jar -Djavax.security.auth.useSubjectCredsOnly=false -Djava.security.krb5.conf="krb5.conf" -Dsun.security.krb5.debug=true  gssapitest.myTest my_config.txt
    2 users provided. Performing authentication #1
    Reading configuration file my_config.txt
    kdc: DDC.SUB1.DOMAIN.COM, realm: SUB1.DOMAIN.COM
    >>>KinitOptions cache name is C:\Users\user1\krb5cc_user1
    >> Acquire default native Credentials
    >>> Obtained TGT from LSA: Credentials:
    [email protected]
    server=krbtgt/[email protected]
    authTime=20130422075139Z
    startTime=20130422075139Z
    endTime=20130422175139Z
    renewTill=20130429075139Z
    flags: FORWARDABLE;RENEWABLE;INITIAL;PRE-AUTHENT
    EType (int): 23
    Subject:
        Principal: [email protected]
        Private Credential: Ticket (hex) = 
    .....
    
    Client Principal = [email protected]
    Server Principal = krbtgt/[email protected]
    Session Key = EncryptionKey: keyType=23 keyBytes (hex dump)=
    0000: 2B 8C 97 3C 8E 83 66 F1   6D 58 6C 37 20 0E 1F 53  +..<..f.mXl7 ..S
    
    
    Forwardable Ticket true
    Forwarded Ticket false
    Proxiable Ticket false
    Proxy Ticket false
    Postdated Ticket false
    Renewable Ticket true
    Initial Ticket true
    Auth Time = Mon Apr 22 15:51:39 2013
    Start Time = Mon Apr 22 15:51:39 2013
    End Time = Tue Apr 23 01:51:39 2013
    Renew Till = Mon Apr 29 15:51:39 2013
    Client Addresses  Null 
    
    Connecting to LDAP
    Config name: krb5.conf
    Found ticket for [email protected] to go to krbtgt/[email protected] expiring on Tue Apr 23 01:51:39 2013
    Entered Krb5Context.initSecContext with state=STATE_NEW
    Service ticket not found in the subject
    >>> Credentials acquireServiceCreds: same realm
    default etypes for default_tgs_enctypes: 16 3 1.
    >>> CksumType: sun.security.krb5.internal.crypto.RsaMd5CksumType
    >>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
    >>> KdcAccessibility: reset
    >>> KrbKdcReq send: kdc=DDC.SUB1.DOMAIN.COM UDP:88, timeout=30000, number of retries =3, #bytes=1554
    >>> KDCCommunication: kdc=DDC.SUB1.DOMAIN.COM UDP:88, timeout=30000,Attempt =1, #bytes=1554
    >>> KrbKdcReq send: #bytes read=107
    >>> KrbKdcReq send: kdc=DDC.SUB1.DOMAIN.COM TCP:88, timeout=30000, number of retries =3, #bytes=1554
    >>> KDCCommunication: kdc=DDC.SUB1.DOMAIN.COM TCP:88, timeout=30000,Attempt =1, #bytes=1554
    >>>DEBUG: TCPClient reading 1497 bytes
    >>> KrbKdcReq send: #bytes read=1497
    >>> KdcAccessibility: remove DDC.SUB1.DOMAIN.COM
    >>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
    >>> KrbApReq: APOptions are 00000000 00000000 00000000 00000000
    >>> EType: sun.security.krb5.internal.crypto.DesCbcMd5EType
    Krb5Context setting mySeqNumber to: 1005735013
    Krb5Context setting peerSeqNumber to: 0
    Created InitSecContextToken:
    .....
    
    Krb5Context.unwrap: token=[60 33 06 09 2a 86 48 86 f7 12 01 02 02 02 01 00 00 ff ff ff ff 94 52 14 5b f6 02 28 1c a4 3c c5 8f 03 9c a2 d6 e5 f6 f1 18 ed 6f 16 ab 07 a0 00 00 04 04 04 04 ]
    Krb5Context.unwrap: data=[07 a0 00 00 ]
    Krb5Context.wrap: data=[01 01 00 00 ]
    Krb5Context.wrap: token=[60 33 06 09 2a 86 48 86 f7 12 01 02 02 02 01 00 00 ff ff ff ff 2d b6 92 0d d9 51 da aa ef 41 67 33 5c de b3 e6 ce 9a 46 31 a0 a8 0e 27 01 01 00 00 04 04 04 04 ]
    Connected
    Disconnected
    #1: Done
    Performing authentication #2
    Reading configuration file my_config.txt
    kdc: DDC.SUB2.DOMAIN.COM, realm: SUB2.DOMAIN.COM
    >>>KinitOptions cache name is C:\Users\user1\krb5cc_user1
    >> Acquire default native Credentials
    >>> Obtained TGT from LSA: Credentials:
    [email protected]
    server=krbtgt/[email protected]
    authTime=20130422075139Z
    startTime=20130422075139Z
    endTime=20130422175139Z
    renewTill=20130429075139Z
    flags: FORWARDABLE;RENEWABLE;INITIAL;PRE-AUTHENT
    EType (int): 23
    Subject:
        Principal: [email protected]
        Private Credential: Ticket (hex) = 
    .....
    
    Client Principal = [email protected]
    Server Principal = krbtgt/[email protected]
    Session Key = EncryptionKey: keyType=23 keyBytes (hex dump)=
    0000: 2B 8C 97 3C 8E 83 66 F1   6D 58 6C 37 20 0E 1F 53  +..<..f.mXl7 ..S
    
    
    Forwardable Ticket true
    Forwarded Ticket false
    Proxiable Ticket false
    Proxy Ticket false
    Postdated Ticket false
    Renewable Ticket true
    Initial Ticket true
    Auth Time = Mon Apr 22 15:51:39 2013
    Start Time = Mon Apr 22 15:51:39 2013
    End Time = Tue Apr 23 01:51:39 2013
    Renew Till = Mon Apr 29 15:51:39 2013
    Client Addresses  Null 
    
    Connecting to LDAP
    Found ticket for [email protected] to go to krbtgt/[email protected] expiring on Tue Apr 23 01:51:39 2013
    Entered Krb5Context.initSecContext with state=STATE_NEW
    Service ticket not found in the subject
    >>> Credentials acquireServiceCreds: same realm
    default etypes for default_tgs_enctypes: 16 3 1.
    >>> CksumType: sun.security.krb5.internal.crypto.RsaMd5CksumType
    >>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
    >>> KrbKdcReq send: kdc=DDC.SUB1.DOMAIN.COM UDP:88, timeout=30000, number of retries =3, #bytes=1554
    >>> KDCCommunication: kdc=DDC.SUB1.DOMAIN.COM UDP:88, timeout=30000,Attempt =1, #bytes=1554
    >>> KrbKdcReq send: #bytes read=107
    >>> KrbKdcReq send: kdc=DDC.SUB1.DOMAIN.COM TCP:88, timeout=30000, number of retries =3, #bytes=1554
    >>> KDCCommunication: kdc=DDC.SUB1.DOMAIN.COM TCP:88, timeout=30000,Attempt =1, #bytes=1554
    >>>DEBUG: TCPClient reading 1482 bytes
    >>> KrbKdcReq send: #bytes read=1482
    >>> KdcAccessibility: remove DDC.SUB1.DOMAIN.COM
    >>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
    KrbException: Message stream modified (41)
        at sun.security.krb5.KrbKdcRep.check(Unknown Source)
        at sun.security.krb5.KrbTgsRep.<init>(Unknown Source)
        at sun.security.krb5.KrbTgsReq.getReply(Unknown Source)
        at sun.security.krb5.KrbTgsReq.sendAndGetCreds(Unknown Source)
        at sun.security.krb5.internal.CredentialsUtil.serviceCreds(Unknown Source)
        at sun.security.krb5.internal.CredentialsUtil.acquireServiceCreds(Unknown Source)
        at sun.security.krb5.Credentials.acquireServiceCreds(Unknown Source)
        at sun.security.jgss.krb5.Krb5Context.initSecContext(Unknown Source)
        at sun.security.jgss.GSSContextImpl.initSecContext(Unknown Source)
        at sun.security.jgss.GSSContextImpl.initSecContext(Unknown Source)
        at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(Unknown Source)
        at com.sun.jndi.ldap.sasl.LdapSasl.saslBind(Unknown Source)
        at com.sun.jndi.ldap.LdapClient.authenticate(Unknown Source)
        at com.sun.jndi.ldap.LdapCtx.connect(Unknown Source)
        at com.sun.jndi.ldap.LdapCtx.<init>(Unknown Source)
        at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(Unknown Source)
        at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(Unknown Source)
        at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(Unknown Source)
        at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(Unknown Source)
        at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
        at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
        at javax.naming.InitialContext.init(Unknown Source)
        at javax.naming.ldap.InitialLdapContext.<init>(Unknown Source)
        at gssapitest.JndiAction.performJndiOperation(myTest.java:603)
        at gssapitest.JndiAction.run(myTest.java:577)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Unknown Source)
        at gssapitest.myTest.Do(myTest.java:59)
        at gssapitest.myTest.main(myTest.java:513)
    javax.naming.AuthenticationException: GSSAPI [Root exception is javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Message stream modified (41))]]
        at com.sun.jndi.ldap.sasl.LdapSasl.saslBind(Unknown Source)
        at com.sun.jndi.ldap.LdapClient.authenticate(Unknown Source)
        at com.sun.jndi.ldap.LdapCtx.connect(Unknown Source)
        at com.sun.jndi.ldap.LdapCtx.<init>(Unknown Source)
        at com.sun.jndi.ldap.LdapCtxFactory.getUsingURL(Unknown Source)
        at com.sun.jndi.ldap.LdapCtxFactory.getUsingURLs(Unknown Source)
        at com.sun.jndi.ldap.LdapCtxFactory.getLdapCtxInstance(Unknown Source)
        at com.sun.jndi.ldap.LdapCtxFactory.getInitialContext(Unknown Source)
        at javax.naming.spi.NamingManager.getInitialContext(Unknown Source)
        at javax.naming.InitialContext.getDefaultInitCtx(Unknown Source)
        at javax.naming.InitialContext.init(Unknown Source)
        at javax.naming.ldap.InitialLdapContext.<init>(Unknown Source)
        at gssapitest.JndiAction.performJndiOperation(myTest.java:603)
        at gssapitest.JndiAction.run(myTest.java:577)
        at java.security.AccessController.doPrivileged(Native Method)
        at javax.security.auth.Subject.doAs(Unknown Source)
        at gssapitest.myTest.Do(myTest.java:59)
        at gssapitest.myTest.main(myTest.java:513)
    Caused by: javax.security.sasl.SaslException: GSS initiate failed [Caused by GSSException: No valid credentials provided (Mechanism level: Message stream modified (41))]
        at com.sun.security.sasl.gsskerb.GssKrb5Client.evaluateChallenge(Unknown Source)
        ... 18 more
    Caused by: GSSException: No valid credentials provided (Mechanism level: Message stream modified (41))
        at sun.security.jgss.krb5.Krb5Context.initSecContext(Unknown Source)
        at sun.security.jgss.GSSContextImpl.initSecContext(Unknown Source)
        at sun.security.jgss.GSSContextImpl.initSecContext(Unknown Source)
        ... 19 more
    Caused by: KrbException: Message stream modified (41)
        at sun.security.krb5.KrbKdcRep.check(Unknown Source)
        at sun.security.krb5.KrbTgsRep.<init>(Unknown Source)
        at sun.security.krb5.KrbTgsReq.getReply(Unknown Source)
        at sun.security.krb5.KrbTgsReq.sendAndGetCreds(Unknown Source)
        at sun.security.krb5.internal.CredentialsUtil.serviceCreds(Unknown Source)
        at sun.security.krb5.internal.CredentialsUtil.acquireServiceCreds(Unknown Source)
        at sun.security.krb5.Credentials.acquireServiceCreds(Unknown Source)
        ... 22 more
    FAILED
    

    What can I do? Am I doings something wrongs?

    Thanks.

  • Matan
    Matan about 11 years
    I think I configured it properly. I posted my code and my configurations, I'll be happy if you can help understanding what's wrong. Thanks.
  • Michael-O
    Michael-O about 11 years
    What do you expect to use? NTLM or Kerberos?
  • Michael-O
    Michael-O about 11 years
    1. Remove all System.setProperty from your app and supply them with -D at start time. 2. Provide sample hostnames (SPNs) and UPNs where your are trying to connect. We need to check the entire path. Cross-realm in a forest not a problem with JGSS. This works here with a charm. You might want to ease the pain for boiler-plate from by using this library.
  • Matan
    Matan about 11 years
    I cannot remove the System.setProperty because currently I'm using these in order to work with different realms. Without those, I will not be able to work with different subdomains even if I'll restart my application. Unfortunately, I cannot perform major changes like using a different library...
  • Michael-O
    Michael-O about 11 years
    Why? All realms are specified in the krb.conf. These is no need to duplicate those. SPNs: here.
  • Matan
    Matan about 11 years
    Tried without the setProperty: The application does not work at all when working with the forign domain (not the domain the bind user is connected to). If I'll specify the default domain in the krb5 file, the application will work only with that domain and no with others. I guess I have to use the setPropery statements.
  • Michael-O
    Michael-O about 11 years
    No, you don't your krb5.conf is incorrect. You still have failed to provide the information I have asked for. Otherwise I am not able to help you.
  • Michael-O
    Michael-O about 11 years
    There is no need to publish them 1:1. I am fully satisfied if you obfuscate them as long as the struncture remains the same.
  • Matan
    Matan about 11 years
    But I'm not using the SPN's. My code and my config is written above together with the output with krb.debug=true - I'm just using the realm and kdc...
  • Michael-O
    Michael-O about 11 years
    Yes, you are using them because SPNs, DNS names and realms are closely related and you DO have a realm problem.
  • Matan
    Matan about 11 years
    Yes, it does seem like a realm problem. So please, explain me how to get those SPN's from my code / configurations and I'll provide it.
  • Michael-O
    Michael-O about 11 years
    I was able to reprocude this issue with Windows 7 and Windows Server 2003 and 2008 KDCs. You are missing a few [domain_realm] entries. At it resolved here. It should look like this.
  • Justin
    Justin almost 11 years
    Timely fix - this was exactly what was wrong about my config. Not an intuitive error at all, but putting the domain to upper case fixed my issue.