GSSException: Потокът от съобщения е променен (41)

Работя с LDAP в горска архитектура (всички сървъри и моят сървър са windows). Свързвам се с AD чрез NTLM удостоверяване.

Имам JAVA код, който изпълнява операциите срещу LDAP сървъра.

Кодът е опакован като сървлет tomcat.

При директно изпълнение на JAVA кода (просто изпълнение на кода за удостоверяване на LDAP като приложение), свързването работи както срещу локалния домейн (локален домейн = влязох в Windows и стартирах този процес с потребител на този домейн), така и срещу чужди домейни .

Когато изпълнявате JAVA кода като сървлет, обвързването работи и удостоверява потребители от един домейн, но не работи, ако се опитвам да удостоверявам потребители от друг домейн, няма да работи (ще работи само ако рестартирам tomcat ).

Получавам изключение:

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

Ще спомена, че това е същият код, със същите конфигурации и същия krb5 файл.

Редактиране: Повече информация:

Това е моят код:

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();
        }
    }
}

И моят файл jaas_ntlm_configuration.txt съдържа:

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

Моят файл krb5.conf е:

# 
# 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
    }

Добавих следното като java параметри:

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

Ако извикам func("SUB*.DOMAIN.COM", "DDC.SUB*.DOMAIN.COM") винаги с един и същ поддомейн - ще работи, но ако извикам с един поддомейн и след това с друг, второто ще се провали.

Повече информация:

Ето резултата с 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

Какво мога да направя? Правя ли нещо нередно?

Благодаря.


person Matan    schedule 10.04.2013    source източник


Отговори (2)


Благодаря за това! Само за справка, главните букви на областта (т.е. сферата трябва да е 100% правилна и с главни букви) са много важни, за да се избегне "Изключение: krb_error 41 Потокът от съобщения е променен (41) ".

Ето пример за правилна нотация:

[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

За разбирането,

Ника.

person Nika Gerson Lohman    schedule 17.05.2013
comment
Навременна корекция - точно това беше грешното в моята конфигурация. Изобщо не е интуитивна грешка, но поставянето на домейна в главни букви реши проблема ми. - person Justin; 24.09.2013

NTLM != Kerberos. Java SASL не поддържа NTLM. Конфигурирайте правилно Kerberos и ще работи.

person Michael-O    schedule 15.04.2013
comment
Мисля, че го конфигурирах правилно. Публикувах своя код и моите конфигурации, ще се радвам, ако можете да помогнете да разберете какво не е наред. Благодаря. - person Matan; 17.04.2013
comment
Какво очаквате да използвате? NTLM или Kerberos? - person Michael-O; 18.04.2013
comment
1. Премахнете всички System.setProperty от приложението си и им дайте -D в началния момент. 2. Предоставете примерни имена на хостове (SPN) и UPN, където се опитвате да се свържете. Трябва да проверим целия път. Кръстосаното царство в гора не е проблем с JGSS. Това работи тук с чар. Може да искате да облекчите болката от бойното решение, като използвате тази библиотека. - person Michael-O; 18.04.2013
comment
Не мога да премахна System.setProperty, защото в момента ги използвам, за да работя с различни сфери. Без тях няма да мога да работя с различни поддомейни, дори ако рестартирам приложението си. За съжаление не мога да извърша големи промени, като например да използвам различна библиотека... - person Matan; 18.04.2013
comment
Защо? Всички области са посочени в krb.conf. Няма нужда да ги дублираме. SPN: тук. - person Michael-O; 18.04.2013
comment
Опитано без setProperty: Приложението изобщо не работи, когато работи с чуждия домейн (не домейна, към който е свързан bind потребителят). Ако посоча домейна по подразбиране във файла krb5, приложението ще работи само с този домейн и не с други. Предполагам, че трябва да използвам операторите setPropery. - person Matan; 21.04.2013
comment
Не, не знаете, че вашето krb5.conf е неправилно. Все още не сте предоставили информацията, която поисках. В противен случай не мога да ви помогна. - person Michael-O; 21.04.2013
comment
Няма нужда да ги публикувате 1:1. Напълно съм доволен, ако ги объркате, стига структурата да остане същата. - person Michael-O; 26.04.2013
comment
Но аз не използвам SPN. Моят код и моята конфигурация са написани по-горе заедно с изхода с krb.debug=true - аз просто използвам realm и kdc... - person Matan; 30.04.2013
comment
Да, вие ги използвате, защото SPN, DNS имената и областите са тясно свързани и вие ИМАТЕ проблем с областта. - person Michael-O; 30.04.2013
comment
Да, наистина изглежда като проблем на царството. Затова, моля, обяснете ми как да получа тези SPN от моя код/конфигурации и аз ще го осигуря. - person Matan; 02.05.2013
comment
Успях да възпроизведа този проблем с Windows 7 и Windows Server 2003 и 2008 KDC. Липсват ви няколко [domain_realm] записа. Тук е решено. Трябва да изглежда така. - person Michael-O; 12.05.2013