1 2 Previous Next 15 Replies Latest reply: Feb 28, 2010 12:42 PM by 843810 RSS

    Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)

    843810
      Hi,

      I'm running JBoss that has:
      1. NegotiateAuthenticator - converses with the browser via NEGOTIATE authentication protocol to obtain a GSSAPI Token (containing a Kerberos Service Ticket).
      2. KerberosLoginModule - which uses Sun's GSSAPI implementation for Kerberos 5, to validate the Service Ticket.

      Currently JBoss is registered as a service in our domain, and everything is working properly.

      The problem arises when I wish to authenticate a user from another DOMAIN (on another Active Directory).

      Does anyone have a clue on how to this?

      Asaf
        • 1. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
          843810
          Sorry, a bug.

          HTTP/SPNEGO in JDK doesn't realize the service belongs to another realm.
          • 2. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
            843810
            Can you please be a little more specific?
            • 3. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
              843810
              To start the negotiation, Java needs to generate a service principal name from the full qualified hostname of the web server. For example, if a client in domain X.COM wants to access http://www.y.com, the service principal name for it should be something like http/www.y.com@Y.COM. Unfortunately, currently Java mistakenly uses the name http/www.y.com@X.COM, where the realm name is still that of the client. The result is that if you access a web site in your own realm (domain), everything is OK, otherwise, fail.
              • 4. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                843810
                But the process that creates the SPN for the web server (hostname) is the browser: IE or Firefox, not Java.

                Krb5LoginModule.java code in the JDK, is used in my web server, to open the the Kerberos Service Ticket, and validate it.

                My question is basically: How do Internet Explorer builds the SPN for a given URL?
                • 5. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                  843810
                  Oh sorry, I was talking about HHTP/SPNEGO on the client side.

                  I have no idea how IE builds the SPN. I guess you can use a network sniffer (say, Wireshark) to inspect the Kerberos traffic between IE. KDC and your server. Pay attention to all the TGS-REQ packets, they should show what services tickets IE has acquired.
                  • 6. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                    843810
                    Hi,,

                    I'm really interested in your SSO solution with Kerberos and SPNEGO LoginModule. I'm currently working on a solution in order to use SSO beetwen a KDC and an existing JBoss J2EE Architecture. What I have understood for now :

                    - We have to add a Service Account into KDC
                    - We have to generate a KeyTab file (using ktpass) with the Service Account Credentials and put it on the application Server
                    - We have to set a kind of NegotiateLoginModule on Jboss in order to handle the SPNEGO Protocol between JAAS mechanism and Internet. We have to configure the LoginModule for using the KeyTab file
                    - We have to set a specific tomcat valve in order to put the correct Principal in each request (so as the silent login process is not done each time i suppose ?)

                    Do you confirm that process ?

                    I'd like to know more about the specific LoginModule that can be used ? Is it the NegotiateLoginModule from this wiki page : http://wiki.jboss.org/wiki/Wiki.jsp?page=NegotiateKerberos ?

                    Have you customized the LoginModule code ?

                    Can you discribe your solution ?

                    It would be very nice of you, you would save my life ... and my brain to ... Sorry for my english 'cause i'm french :-)

                    Best Regards,
                    • 7. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                      843810
                      Hi mAsaf,

                      i'm writing a tool that does what u already have running.

                      1. NegotiateAuthenticator - converses with the browser via NEGOTIATE authentication protocol to obtain a GSSAPI Token (containing a Kerberos Service Ticket).
                      2. KerberosLoginModule - which uses Sun's GSSAPI implementation for Kerberos 5, to validate the Service Ticket.

                      i want to implement this to a webapp that have embedded Tomcat running. Can u post some code examples on how to aquire the service ticket with the browser (NEGOTIATE authentication )?
                      and how to validate it ?

                      any help will be high regarded
                      • 8. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                        843810
                        Well, I haven't written most of it, but used a third party library that does most of the work: http://www.taglab.com/developers/index.html

                        I've downloaded their library and wrote an Authenticator and a Login Module which simply uses them.

                        Tell me if you need some guidance with that.

                        Asaf
                        • 9. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                          843810
                          Thank Asaf for ur reply,

                          i have a webapp that is written in Java 1.4 and should stay and have an embedded Tomcat running and use no web server only that embedded aplication server. I understand all pieces seperated and have a problem getting them together.

                          My scenario should work as follow:
                          A user use browser to acess the webapp and the browser should use SPNEGO to make the authentication between webapp & kerberos and the user. A configured Kerberos exists.

                          So JGSSAPI talking the hole time about Client & Server implementaion. And i can't get around.
                          - Where should i write the code that aquire the service ticket? Because my Client is a Browser (and the browser should do this - redirection or ?).
                          - and where to write the one who unpack and validate it. (as a valve in the app server filter or a normal servlet binded to the webapp ?)
                          - Is the unpack mechanism for the service ticket involved in SPNEGO or is it seperate?

                          Is there then any Incompatibility of the Java versions because SPNEGO implementaion von sun exists only since Java 6.
                          • 10. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                            843810
                            The user access a secured resource on your web application.
                            You protect a resource in tomcat by including a <login-config> element in the web.xml and by specifying in the servlet mapping that certain resources are protected. The <login-config> has an element inside it, called <auth-method>. This is where you specify the authentication method you wish to use, when tomcat negotiates with the browser for credentials. Tomcat vanilla version comes prebundled with a few authenticators (which are java classes that implements authentication methods) as they required to by the Java Servlet Specifications (you can read it in Sun site):
                            BASIC, FORM, DIGEST, and one more I forgot.
                            So, in the web.xml, you will have:
                            <login-config>
                                 <auth-method>FORM</auth-method>
                                ...
                            </login-config>
                            You want to use a different authentication mechanism, called Negotiate (by Microsoft), which is a limited implementation of SPNEGO. In order to support it, you need to write your own Authenticator class, which implements this method.
                            After you write such class, you register it in tomcat ( I know how to do it if its an embedded tomcat inside JBoss): You add your class to the jboss-service.xml file in jboss/server/default/deploy/jbossweb-tomcat55.sar/META-INF directory.

                            Lucky for you, some company released an open source project (Apache License i think) called TagLab which implements Negotiate protocol.
                            All you have to do is, write a class that extends AuthenticatorBase (it's in tomcat's jars, needed only for compilation), which uses TagLab code.
                            If you are using JBoss, then the story gets more complicated, since you need to move the actual authentication part of the authenticator into a JBoss login module.

                            Regarding the ticketing flow:
                            1. Browser access a secured resource in your web app.
                            2. Your authenticator responds with a 401 response, and a special header:
                            WWW-Authenticate: NEGOTIATE
                            This tells the browser: We'll use the NEGOTIATE authentication protocol.
                            3. The browser realizes it needs to acquire a service ticket.
                            The service name is the url you've typed in (well, only part of it): http://fully-qualified-host-name
                            It sends a request to the Active Directory, for a service ticket, to that service name.
                            (that means you need to register your tomcat/jboss as a service in the AD, with that exact name)
                            4. The browser tries to access the same resource url as before, this time, it adds a header to the HTTP request;
                            WWW-authorization: NEGOTIATE encoded-spnego-token
                            encoded-spnego-token is the SPNEGO token encoded in base64. The spnego token is basically a wrapper for the service ticket.
                            5. The authenticator unwraps the spnego token, and aquires the service ticket. Using the secret key (shared only by tomcat and AD) it decodes the service ticket and makes sure the timestamp is close enough to the current time. Those two steps are the validation that this is the user that sent the ticket. If all passes, the user is valid to aquire the resource.
                            6. The authenticator returns true, and passes control to the servlet container which sends back the requested resource. Otherwise, a 403 response is sent back to the browser.

                            Feel free to ask some more questions, since I took me a month of digging through the net to make this work and understand all the flow.
                            I should start writing a white paper on this.

                            Edited by: mAsaf on Jun 19, 2008 9:04 AM
                            • 11. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                              843810
                              Good explanation ! Many Thanks.

                              there are a little bit that not clear yet.
                              1- I read overall that i need JAAS & JGSSAPI, in your discription you didn't mention any of them? Do i need one of them, both or none of them?
                              2- In the forth point regarding the ticketing flow, Does the browser do the second try in order to access the app automaticly or has the user to do the URL request twice? (automaticly means intern the user doesn't notice anything. Just type the URL once and he/she access the app)
                              3- when the browser couldn't aquire the service ticket from the KDC (cause KDC isn't configured right or whatever) what does browser do then?
                              4- where is the secret key be saved, that shared only between KDC and Tomcat and how to create it? (KDC admin by registration of the service principal?)

                              i found a valve that does almost exactlly what u described on this Link: http://devel.it.su.se/pub/jsp/polopoly.jsp?d=1047&a=3780
                              i unpacked the jar to take a look at the class:

                              public class GSSAPIValve extends ValveBase
                              {
                                   public GSSAPIValve()
                                   {     
                                        setOverrideMech("BASIC");
                                   }
                                   
                                   public String getOverrideMech()
                                   {
                                        return overrideMech;
                                   }
                                        
                                   public void setOverrideMech(String overrideMech)
                                   {
                                        this.overrideMech = overrideMech.trim();
                                   }
                                        
                                   public void invoke(Request request, Response response) throws IOException, ServletException
                                   {
                                        HttpServletRequest hreq = request.getRequest();
                                        HttpServletResponse hres = response.getResponse();
                                        log.debug("GSSAPI valve entered");
                                        boolean headerPresent = false;
                                        String header = hreq.getHeader("Authorization");
                                        log.debug("Authorization: " + header);
                                        
                                        if(header != null && header.startsWith("Negotiate ") && header.length() > 10)
                                        {
                                             log.debug("GSSAPI Authorization header found with " + (header.length() - 10) + " bytes");
                                             GSSContext gcontext = null;
                                             String outToken = null;
                                             try
                                             {
                                                  byte in[] = Base64.decodeBase64(header.substring(10).getBytes());
                                                  gcontext = getContext(in);
                                                  outToken = getToken(in, gcontext);
                                             }
                                             catch(GSSException gsse)
                                             {
                                                  throw new ServletException(gsse);
                                             }
                                             if(outToken != null)
                                                  hres.setHeader("WWW-Authenticate", "Negotiate " + outToken.getBytes());
                                             else
                                                  hres.setHeader("WWW-Authenticate", "Negotiate");
                                   
                                             headerPresent = true;
                                   
                                             if(gcontext != null && gcontext.isEstablished())
                                             {
                                                  try
                                   
                                                  {
                                                       Subject subject = new Subject();
                                                       org.ietf.jgss.GSSName sname = gcontext.getSrcName();
                                                       java.security.Principal identity = new SimpleGSSPrincipal(sname);
                                                       request.setUserPrincipal(identity);
                                                       request.setAuthType("NEGOTIATE");
                                                       subject.getPrincipals().add(identity);
                                                       log.debug("Authorized name: " + sname);
                                                       GSSCredential dc = null;
                                                       if(gcontext.getCredDelegState())
                                                       {
                                                            dc = gcontext.getDelegCred();
                                                            org.ietf.jgss.GSSName dname = dc.getName();
                                                            subject.getPrincipals().add(new SimpleGSSPrincipal(dname));
                                                            subject.getPrivateCredentials().add(gcontext.getDelegCred());
                                                            log.debug("Delegated name: " + dname);
                                                       }
                                                  
                                                       SecurityAssociations.setPrincipalInfo(identity, dc, subject);
                                                  }
                                                  catch(Exception ex)
                                                  {
                                                       log.info(ex);
                                                       hres.addHeader("Client-Warning", ex.getMessage());
                                                       hres.setStatus(401);
                                                  }
                                   
                                             }
                                        else
                                        {
                                             hres.setStatus(401);
                                             log.debug("GSS-KERBEROS5: Unauthorized");
                                        }
                                   }
                                   
                                        getNext().invoke(request, response);
                                        log.debug("GSSAPI valve ending");
                                        
                                        if(response.getHeader("WWW-Authenticate") != null && !headerPresent && response.getHeader("WWW-Authenticate").toUpperCase().startsWith(getOverrideMech().toUpperCase()))
                                        {
                                             log.debug("Replacing " + getOverrideMech() + " with Negotiate in WWW-Authenticate header");
                                             hres.setHeader("WWW-Authenticate", "Negotiate");
                                        }
                                   }
                                   
                                   protected GSSContext getContext(byte in[]) throws GSSException
                                   {
                                        GSSManager manager;
                                        if(in == null)
                                        break MISSING_BLOCK_LABEL_15;
                                        manager = GSSManager.getInstance();
                                        return manager.createContext(in);
                                        GSSException gsse;
                                        gsse;
                                        
                                        return null;
                                   }
                                   
                                   protected String getToken(byte in[], GSSContext context) throws GSSException
                                   {
                                        if(in == null || context == null)
                                        break MISSING_BLOCK_LABEL_124;
                                        byte out[] = context.acceptSecContext(in, 0, in.length);
                                        return new String(Base64.encodeBase64(out));
                                        GSSException gsse;
                                        gsse;
                                        log.error("GSSException: " + gsse.getMessage());
                                        log.error("GSSException major: " + gsse.getMajorString());
                                        log.error("GSSException minor: " + gsse.getMinorString());
                                        throw gsse;
                                        return null;
                                   }
                                   
                                   static Class _mthclass$(String x0)
                                   {
                                        return Class.forName(x0);
                                        ClassNotFoundException x1;
                                        x1;
                                        throw new NoClassDefFoundError(x1.getMessage());
                                   }
                                   
                                   private static final Log log;
                                   
                                   private String overrideMech;
                                   
                                   static
                                   {
                                        log = LogFactory.getLog(se.su.it.gss.valve.GSSAPIValve.class);
                                   }
                              }

                              i know how to and where should i place the authenticator in my embedded Tomcat. Can u post some comments on this code if it does what u mean (speak the unpack and time verfication to validate the user). Is this Validation secure enough?
                              • 12. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                                843810
                                Hi,

                                1. JAAS is an authentication and authorization model, created by Sun. JBoss implemented their security model using JAAS model. This enables any person to plug their own authentication and authorization modules into JBoss security module.

                                JGSSAPI is something you are using, but indirectly. GSS API is an standard API / Protocol for authentication and authorization. JGSSAPI is the JAVA side/implementation of this API. Indirectly, because the taglab library I mentioned, and also the code you've pasted are using this API. It comes bundled (API + implementation) in the JDK (Starting from 1.4 I think).

                                JGSS Sun's implementation is using JAAS model for accessing Active Directory. This requires you to supply a JAAS Configuration file (I'll explain later)

                                2. It's automatic. It's part of the Negotiate protocol specification. The user doesn't know a thing about it.

                                3. When the browser can't aquire the service ticket, it doesn't access the website again, but displays the original response send by the web server: 401.

                                4. You create a secret key by registering the service principal in Active Directory. This is done using the utility KTPASS.EXE. One of its arguments is the keytab file location to save the secret key to. This keytab contains the service principal name and the secret key. You should place this in a secure / non-public-access location. In JBoss you define an application-policy element named "com.sun.security.jgss.accept" in the login-config.xml file. In that element, you define the location of the the keytab. Your authenticator call taglab library, which calls Sun's JGSS implementation. It reads the Keytab and then opens up the service ticket received using that secret key.
                                In tomcat, since you don't have a login-config.xml, you need to define this in a JAAS Configuration file. Look for syntax on the internet (Sun site is the first source). You should supply a path to this file in the JVM command line arguments or it should be in the classpath.

                                Regarding your valve - In a quick glance it seems quite similar to the Taglab library code. Since it's open source you can compare them both, and learn about the process while doing that.
                                One of the things which are mostly missing when you learn it is Sun's JGSS code. I had to read Sun's Open JVM source code, which is a bit different from the source in JDK 1.5, to understand it. It's better then nothing.

                                I hope this helps. This is a complex issue you're getting into, filled with many areas that needs to be learned to get a good understanding of it.

                                Asaf
                                • 13. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                                  843810
                                  Hi,

                                  it did help and answered the most important questions i had. Thanks & i'm looking foraward to your white paper on the topic. It would be nice if u notice me where to find it when it done on alim27@web.de. All best.

                                  Alim
                                  • 14. Re: Cross Realm Authentication using NEGOTIATE protocol (SPNEGO)
                                    843810
                                    Hi Asaf, currently I have JBoss in SunSolaris and Active Directory (AD) in windows. I want to implement single sign on (SSO). I am not going to use any login form to enter the username/Password, just I am going to enter a URL requesting a JSP page, the server needs to authenticate using my network id (which is a windows userid) against the AD and take me to the requested JSP page. Can you please suggest whether to use Kerbrose/SPNEGO or if any. And also can you please guide me through a step by step process?

                                    Thank you in advance
                                    1 2 Previous Next