1 /*
   2  * Copyright 2005-2009 Sun Microsystems, Inc.  All Rights Reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
  22  * CA 95054 USA or visit www.sun.com if you need additional information or
  23  * have any questions.
  24  */
  25 
  26 package sun.net.www.protocol.http;
  27 
  28 import java.io.IOException;
  29 
  30 import org.ietf.jgss.GSSContext;
  31 import org.ietf.jgss.GSSException;
  32 import org.ietf.jgss.GSSName;
  33 import org.ietf.jgss.Oid;
  34 
  35 import sun.security.jgss.GSSManagerImpl;
  36 import sun.security.jgss.GSSUtil;
  37 import sun.security.jgss.HttpCaller;
  38 
  39 /**
  40  * This class encapsulates all JAAS and JGSS API calls in a separate class
  41  * outside NegotiateAuthentication.java so that J2SE build can go smoothly
  42  * without the presence of it.
  43  *
  44  * @author weijun.wang@sun.com
  45  * @since 1.6
  46  */
  47 public class NegotiatorImpl extends Negotiator {
  48 
  49     private static final boolean DEBUG =
  50         java.security.AccessController.doPrivileged(
  51               new sun.security.action.GetBooleanAction("sun.security.krb5.debug"));
  52 
  53     private GSSContext context;
  54     private byte[] oneToken;
  55 
  56     /**
  57      * Initialize the object, which includes:<ul>
  58      * <li>Find out what GSS mechanism to use from the system property
  59      * <code>http.negotiate.mechanism.oid</code>, defaults SPNEGO
  60      * <li>Creating the GSSName for the target host, "HTTP/"+hostname
  61      * <li>Creating GSSContext
  62      * <li>A first call to initSecContext</ul>
  63      */
  64     private void init(HttpCallerInfo hci) throws GSSException {
  65         final Oid oid;
  66 
  67         if (hci.scheme.equalsIgnoreCase("Kerberos")) {
  68             // we can only use Kerberos mech when the scheme is kerberos
  69             oid = GSSUtil.GSS_KRB5_MECH_OID;
  70         } else {
  71             String pref = java.security.AccessController.doPrivileged(
  72                     new java.security.PrivilegedAction<String>() {
  73                         public String run() {
  74                             return System.getProperty(
  75                                 "http.auth.preference",
  76                                 "spnego");
  77                         }
  78                     });
  79             if (pref.equalsIgnoreCase("kerberos")) {
  80                 oid = GSSUtil.GSS_KRB5_MECH_OID;
  81             } else {
  82                 // currently there is no 3rd mech we can use
  83                 oid = GSSUtil.GSS_SPNEGO_MECH_OID;
  84             }
  85         }
  86 
  87         GSSManagerImpl manager = new GSSManagerImpl(
  88                 new HttpCaller(hci));
  89 
  90         // RFC 4559 4.1 uses uppercase service name "HTTP".
  91         // RFC 4120 6.2.1 demands the host be lowercase
  92         String peerName = "HTTP@" + hci.host.toLowerCase();
  93 
  94         GSSName serverName = manager.createName(peerName,
  95                 GSSName.NT_HOSTBASED_SERVICE);
  96         context = manager.createContext(serverName,
  97                                         oid,
  98                                         null,
  99                                         GSSContext.DEFAULT_LIFETIME);
 100 
 101         // In order to support credential delegation in HTTP/SPNEGO,
 102         // we always request it before initSecContext. The current
 103         // implementation will check the OK-AS-DELEGATE flag inside
 104         // the service ticket of the web server, and only enable
 105         // delegation when this flag is set. This check is only
 106         // performed when the GSS caller is CALLER_HTTP_NEGOTIATE,
 107         // so all other normal GSS-API calls are not affected.
 108 
 109         context.requestCredDeleg(true);
 110         oneToken = context.initSecContext(new byte[0], 0, 0);
 111     }
 112 
 113     /**
 114      * Constructor
 115      * @throws java.io.IOException If negotiator cannot be constructed
 116      */
 117     public NegotiatorImpl(HttpCallerInfo hci) throws IOException {
 118         try {
 119             init(hci);
 120         } catch (GSSException e) {
 121             if (DEBUG) {
 122                 System.out.println("Negotiate support not initiated, will " +
 123                         "fallback to other scheme if allowed. Reason:");
 124                 e.printStackTrace();
 125             }
 126             IOException ioe = new IOException("Negotiate support not initiated");
 127             ioe.initCause(e);
 128             throw ioe;
 129         }
 130     }
 131 
 132     /**
 133      * Return the first token of GSS, in SPNEGO, it's called NegTokenInit
 134      * @return the first token
 135      */
 136     public byte[] firstToken() {
 137         return oneToken;
 138     }
 139 
 140     /**
 141      * Return the rest tokens of GSS, in SPNEGO, it's called NegTokenTarg
 142      * @param token the token received from server
 143      * @return the next token
 144      * @throws java.io.IOException if the token cannot be created successfully
 145      */
 146     public byte[] nextToken(byte[] token) throws IOException {
 147         try {
 148             return context.initSecContext(token, 0, token.length);
 149         } catch (GSSException e) {
 150             if (DEBUG) {
 151                 System.out.println("Negotiate support cannot continue. Reason:");
 152                 e.printStackTrace();
 153             }
 154             IOException ioe = new IOException("Negotiate support cannot continue");
 155             ioe.initCause(e);
 156             throw ioe;
 157         }
 158     }
 159 }