GSSException: изменен поток сообщений (41)

Я работаю с LDAP в лесной архитектуре (все серверы и мой сервер - окна). Я привязываюсь к 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 Message stream изменен (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 != Керберос. 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) и имен участников-пользователей, к которым вы пытаетесь подключиться. Нам нужно проверить весь путь. Кросс-реалм в лесу не проблема с 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 - я просто использую область и kdc... - person Matan; 30.04.2013
comment
Да, вы используете их, потому что имена участников-служб, 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