Author: thejas
Date: Fri Mar 28 18:11:20 2014
New Revision: 1582848

URL: http://svn.apache.org/r1582848
Log:
HIVE-6697 : HiveServer2 secure thrift/http authentication needs to support 
SPNego  (Dilli Arumugam via Thejas Nair)

Modified:
    hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
    hive/trunk/conf/hive-default.xml.template
    
hive/trunk/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java
    hive/trunk/service/src/java/org/apache/hive/service/cli/CLIService.java
    
hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java
    
hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
    
hive/trunk/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java
    
hive/trunk/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java
    
hive/trunk/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java

Modified: hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java
URL: 
http://svn.apache.org/viewvc/hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java 
(original)
+++ hive/trunk/common/src/java/org/apache/hadoop/hive/conf/HiveConf.java Fri 
Mar 28 18:11:20 2014
@@ -887,6 +887,8 @@ public class HiveConf extends Configurat
     
HIVE_SERVER2_ALLOW_USER_SUBSTITUTION("hive.server2.allow.user.substitution", 
true),
     
HIVE_SERVER2_KERBEROS_KEYTAB("hive.server2.authentication.kerberos.keytab", ""),
     
HIVE_SERVER2_KERBEROS_PRINCIPAL("hive.server2.authentication.kerberos.principal",
 ""),
+    HIVE_SERVER2_SPNEGO_KEYTAB("hive.server2.authentication.spnego.keytab", 
""),
+    
HIVE_SERVER2_SPNEGO_PRINCIPAL("hive.server2.authentication.spnego.principal", 
""),
     HIVE_SERVER2_PLAIN_LDAP_URL("hive.server2.authentication.ldap.url", null),
     HIVE_SERVER2_PLAIN_LDAP_BASEDN("hive.server2.authentication.ldap.baseDN", 
null),
     HIVE_SERVER2_PLAIN_LDAP_DOMAIN("hive.server2.authentication.ldap.Domain", 
null),

Modified: hive/trunk/conf/hive-default.xml.template
URL: 
http://svn.apache.org/viewvc/hive/trunk/conf/hive-default.xml.template?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- hive/trunk/conf/hive-default.xml.template (original)
+++ hive/trunk/conf/hive-default.xml.template Fri Mar 28 18:11:20 2014
@@ -2218,7 +2218,36 @@
     Kerberos keytab file for server principal
   </description>
 </property>
- 
+
+<property>
+  <name>hive.server2.authentication.spnego.principal</name>
+  <value></value>
+  <description>
+    SPNego service principal, optional,
+    typical value would look like HTTP/[email protected]
+    SPNego service principal would be used by hiveserver2 when kerberos 
security is enabled
+    and HTTP transport mode is used.
+    This needs to be set only if SPNEGO is to be used in authentication.
+  </description>
+</property>
+
+<property>
+  <name>hive.server2.authentication.spnego.keytab</name>
+  <value></value>
+  <description>
+    keytab file for SPNego principal, optional,
+    typical value would look like /etc/security/keytabs/spnego.service.keytab,
+    This keytab would be used by hiveserver2 when kerberos security is enabled
+    and HTTP transport mode is used.
+    This needs to be set only if SPNEGO is to be used in authentication.
+    SPNego authentication would be honored only if valid
+    hive.server2.authentication.spnego.principal
+    and
+    hive.server2.authentication.spnego.keytab
+    are specified
+  </description>
+</property>
+
 <property>
   <name>hive.server2.authentication.ldap.url</name>
   <value></value>

Modified: 
hive/trunk/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java
URL: 
http://svn.apache.org/viewvc/hive/trunk/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- 
hive/trunk/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java 
(original)
+++ 
hive/trunk/service/src/java/org/apache/hive/service/auth/HiveAuthFactory.java 
Fri Mar 28 18:11:20 2014
@@ -186,7 +186,22 @@ public class HiveAuthFactory {
     if (!principal.isEmpty() && !keyTabFile.isEmpty()) {
       ShimLoader.getHadoopShims().loginUserFromKeytab(principal, keyTabFile);
     } else {
-      throw new IOException ("HiveServer2 kerberos principal or keytab is not 
correctly configured");
+      throw new IOException ("HiveServer2 kerberos principal or keytab " +
+          "is not correctly configured");
+    }
+  }
+
+  // Perform spnego login using the hadoop shim API if the configuration is 
available
+  public static UserGroupInformation loginFromSpnegoKeytabAndReturnUGI(
+      HiveConf hiveConf) throws IOException {
+    String principal = hiveConf.getVar(ConfVars.HIVE_SERVER2_SPNEGO_PRINCIPAL);
+    String keyTabFile = hiveConf.getVar(ConfVars.HIVE_SERVER2_SPNEGO_KEYTAB);
+    if (!principal.isEmpty() && !keyTabFile.isEmpty()) {
+      return ShimLoader.getHadoopShims().loginUserFromKeytabAndReturnUGI(
+          principal, keyTabFile);
+    } else {
+      throw new IOException ("HiveServer2 SPNego principal or keytab " +
+          "is not correctly configured");
     }
   }
 

Modified: 
hive/trunk/service/src/java/org/apache/hive/service/cli/CLIService.java
URL: 
http://svn.apache.org/viewvc/hive/trunk/service/src/java/org/apache/hive/service/cli/CLIService.java?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- hive/trunk/service/src/java/org/apache/hive/service/cli/CLIService.java 
(original)
+++ hive/trunk/service/src/java/org/apache/hive/service/cli/CLIService.java Fri 
Mar 28 18:11:20 2014
@@ -67,6 +67,7 @@ public class CLIService extends Composit
   private SessionManager sessionManager;
   private IMetaStoreClient metastoreClient;
   private UserGroupInformation serviceUGI;
+  private UserGroupInformation httpUGI;
 
   public CLIService() {
     super("CLIService");
@@ -90,6 +91,21 @@ public class CLIService extends Composit
       } catch (LoginException e) {
         throw new ServiceException("Unable to login to kerberos with given 
principal/keytab", e);
       }
+
+      // Also try creating a UGI object for the SPNego principal
+      String principal = 
hiveConf.getVar(ConfVars.HIVE_SERVER2_SPNEGO_PRINCIPAL);
+      String keyTabFile = hiveConf.getVar(ConfVars.HIVE_SERVER2_SPNEGO_KEYTAB);
+      if (principal.isEmpty() || keyTabFile.isEmpty()) {
+        LOG.info("SPNego httpUGI not created, spNegoPrincipal: " + principal +
+            ", ketabFile: " + keyTabFile);
+      } else {
+        try {
+          this.httpUGI = 
HiveAuthFactory.loginFromSpnegoKeytabAndReturnUGI(hiveConf);
+          LOG.info("SPNego httpUGI successfully created.");
+        } catch (IOException e) {
+          LOG.warn("SPNego httpUGI creation failed: ", e);
+        }
+      }
     }
     super.init(hiveConf);
   }
@@ -98,6 +114,10 @@ public class CLIService extends Composit
     return this.serviceUGI;
   }
 
+  public UserGroupInformation getHttpUGI() {
+    return this.httpUGI;
+  }
+
   @Override
   public synchronized void start() {
     super.start();

Modified: 
hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java
URL: 
http://svn.apache.org/viewvc/hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- 
hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java
 (original)
+++ 
hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpCLIService.java
 Fri Mar 28 18:11:20 2014
@@ -76,7 +76,10 @@ public class ThriftHttpCLIService extend
       String schemeName = useSsl ? "https" : "http";
       String authType = hiveConf.getVar(ConfVars.HIVE_SERVER2_AUTHENTICATION);
       // Set during the init phase of HiveServer2 if auth mode is kerberos
+      // UGI for the hive/_HOST (kerberos) principal
       UserGroupInformation serviceUGI = cliService.getServiceUGI();
+      // UGI for the http/_HOST (SPNego) principal
+      UserGroupInformation httpUGI = cliService.getHttpUGI();
 
       if (useSsl) {
         String keyStorePath = 
hiveConf.getVar(ConfVars.HIVE_SERVER2_SSL_KEYSTORE_PATH).trim();
@@ -101,8 +104,9 @@ public class ThriftHttpCLIService extend
       TProcessor processor = processorFactory.getProcessor(null);
 
       TProtocolFactory protocolFactory = new TBinaryProtocol.Factory();
+
       TServlet thriftHttpServlet = new ThriftHttpServlet(processor, 
protocolFactory,
-          authType, serviceUGI);
+          authType, serviceUGI, httpUGI);
 
       final ServletContextHandler context = new ServletContextHandler(
           ServletContextHandler.SESSIONS);

Modified: 
hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
URL: 
http://svn.apache.org/viewvc/hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- 
hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
 (original)
+++ 
hive/trunk/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
 Fri Mar 28 18:11:20 2014
@@ -58,12 +58,14 @@ public class ThriftHttpServlet extends T
   public static final Log LOG = 
LogFactory.getLog(ThriftHttpServlet.class.getName());
   private final String authType;
   private final UserGroupInformation serviceUGI;
+  private final UserGroupInformation httpUGI;
 
   public ThriftHttpServlet(TProcessor processor, TProtocolFactory 
protocolFactory,
-      String authType, UserGroupInformation serviceUGI) {
+      String authType, UserGroupInformation serviceUGI, UserGroupInformation 
httpUGI) {
     super(processor, protocolFactory);
     this.authType = authType;
     this.serviceUGI = serviceUGI;
+    this.httpUGI = httpUGI;
   }
 
   @Override
@@ -73,24 +75,25 @@ public class ThriftHttpServlet extends T
     try {
       // For a kerberos setup
       if(isKerberosAuthMode(authType)) {
-        clientUserName = doKerberosAuth(request, serviceUGI);
+        clientUserName = doKerberosAuth(request);
       }
       else {
         clientUserName = doPasswdAuth(request, authType);
       }
 
       LOG.info("Client username: " + clientUserName);
-      
+
       // Set the thread local username to be used for doAs if true
       SessionManager.setUserName(clientUserName);
       super.doPost(request, response);
     }
     catch (HttpAuthenticationException e) {
-      // Send a 403 to the client
       LOG.error("Error: ", e);
-      response.setContentType("application/x-thrift");
-      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
-      // Send the response back to the client
+      // Send a 401 to the client
+      response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
+      if(isKerberosAuthMode(authType)) {
+        response.addHeader(HttpAuthUtils.WWW_AUTHENTICATE, 
HttpAuthUtils.NEGOTIATE);
+      }
       response.getWriter().println("Authentication Error: " + e.getMessage());
     }
     finally {
@@ -127,24 +130,38 @@ public class ThriftHttpServlet extends T
    * Do the GSS-API kerberos authentication.
    * We already have a logged in subject in the form of serviceUGI,
    * which GSS-API will extract information from.
+   * In case of a SPNego request we use the httpUGI,
+   * for the authenticating service tickets.
    * @param request
    * @return
    * @throws HttpAuthenticationException
    */
-  private String doKerberosAuth(HttpServletRequest request, 
-      UserGroupInformation serviceUGI) throws HttpAuthenticationException {
+  private String doKerberosAuth(HttpServletRequest request)
+      throws HttpAuthenticationException {
+    // Try authenticating with the http/_HOST principal
+    if (httpUGI != null) {
+      try {
+        return httpUGI.doAs(new HttpKerberosServerAction(request, httpUGI));
+      } catch (Exception e) {
+        LOG.info("Failed to authenticate with http/_HOST kerberos principal, " 
+
+            "trying with hive/_HOST kerberos principal");
+      }
+    }
+    // Now try with hive/_HOST principal
     try {
       return serviceUGI.doAs(new HttpKerberosServerAction(request, 
serviceUGI));
     } catch (Exception e) {
+      LOG.error("Failed to authenticate with hive/_HOST kerberos principal");
       throw new HttpAuthenticationException(e);
     }
+
   }
 
   class HttpKerberosServerAction implements PrivilegedExceptionAction<String> {
     HttpServletRequest request;
     UserGroupInformation serviceUGI;
-    
-    HttpKerberosServerAction(HttpServletRequest request, 
+
+    HttpKerberosServerAction(HttpServletRequest request,
         UserGroupInformation serviceUGI) {
       this.request = request;
       this.serviceUGI = serviceUGI;
@@ -152,14 +169,16 @@ public class ThriftHttpServlet extends T
 
     @Override
     public String run() throws HttpAuthenticationException {
-   // Get own Kerberos credentials for accepting connection
+      // Get own Kerberos credentials for accepting connection
       GSSManager manager = GSSManager.getInstance();
       GSSContext gssContext = null;
       String serverPrincipal = getPrincipalWithoutRealm(
           serviceUGI.getUserName());
       try {
         // This Oid for Kerberos GSS-API mechanism.
-        Oid mechOid = new Oid("1.2.840.113554.1.2.2");
+        Oid kerberosMechOid = new Oid("1.2.840.113554.1.2.2");
+        // Oid for SPNego GSS-API mechanism.
+        Oid spnegoMechOid = new Oid("1.3.6.1.5.5.2");
         // Oid for kerberos principal name
         Oid krb5PrincipalOid = new Oid("1.2.840.113554.1.2.2.1");
 
@@ -168,7 +187,9 @@ public class ThriftHttpServlet extends T
 
         // GSS credentials for server
         GSSCredential serverCreds = manager.createCredential(serverName,
-            GSSCredential.DEFAULT_LIFETIME,  mechOid, 
GSSCredential.ACCEPT_ONLY);
+            GSSCredential.DEFAULT_LIFETIME,
+            new Oid[]{kerberosMechOid, spnegoMechOid},
+            GSSCredential.ACCEPT_ONLY);
 
         // Create a GSS context
         gssContext = manager.createContext(serverCreds);
@@ -275,6 +296,7 @@ public class ThriftHttpServlet extends T
   private boolean isKerberosAuthMode(String authType) {
     return 
authType.equalsIgnoreCase(HiveAuthFactory.AuthTypes.KERBEROS.toString());
   }
+
 }
 
 

Modified: 
hive/trunk/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java
URL: 
http://svn.apache.org/viewvc/hive/trunk/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- 
hive/trunk/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java
 (original)
+++ 
hive/trunk/shims/0.20/src/main/java/org/apache/hadoop/hive/shims/Hadoop20Shims.java
 Fri Mar 28 18:11:20 2014
@@ -599,6 +599,13 @@ public class Hadoop20Shims implements Ha
   }
 
   @Override
+  public UserGroupInformation loginUserFromKeytabAndReturnUGI(
+      String principal, String keytabFile) throws IOException {
+    throwKerberosUnsupportedError();
+    return null;
+  }
+
+  @Override
   public void reLoginUserFromKeytab() throws IOException{
     throwKerberosUnsupportedError();
   }

Modified: 
hive/trunk/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java
URL: 
http://svn.apache.org/viewvc/hive/trunk/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- 
hive/trunk/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java
 (original)
+++ 
hive/trunk/shims/common-secure/src/main/java/org/apache/hadoop/hive/shims/HadoopShimsSecure.java
 Fri Mar 28 18:11:20 2014
@@ -558,6 +558,13 @@ public abstract class HadoopShimsSecure 
   }
 
   @Override
+  public UserGroupInformation loginUserFromKeytabAndReturnUGI(
+      String principal, String keytabFile) throws IOException {
+    String hostPrincipal = SecurityUtil.getServerPrincipal(principal, 
"0.0.0.0");
+    return UserGroupInformation.loginUserFromKeytabAndReturnUGI(hostPrincipal, 
keytabFile);
+  }
+
+  @Override
   public String getTokenFileLocEnvName() {
     return UserGroupInformation.HADOOP_TOKEN_FILE_LOCATION;
   }

Modified: 
hive/trunk/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java
URL: 
http://svn.apache.org/viewvc/hive/trunk/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java?rev=1582848&r1=1582847&r2=1582848&view=diff
==============================================================================
--- 
hive/trunk/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java
 (original)
+++ 
hive/trunk/shims/common/src/main/java/org/apache/hadoop/hive/shims/HadoopShims.java
 Fri Mar 28 18:11:20 2014
@@ -311,6 +311,14 @@ public interface HadoopShims {
   public void loginUserFromKeytab(String principal, String keytabFile) throws 
IOException;
 
   /**
+   *  Perform kerberos login using the given principal and keytab,
+   *  and return the UGI object
+   * @throws IOException
+   */
+  public UserGroupInformation loginUserFromKeytabAndReturnUGI(String principal,
+      String keytabFile) throws IOException;
+
+  /**
    * Perform kerberos re-login using the given principal and keytab, to renew
    * the credentials
    * @throws IOException


Reply via email to