You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hive.apache.org by kg...@apache.org on 2020/03/18 06:02:10 UTC

[hive] 02/03: HIVE-22539: HiveServer2 SPNEGO authentication should skip if authorization header is empty (Kevin Risden via Zoltan Haindrich)

This is an automated email from the ASF dual-hosted git repository.

kgyrtkirk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hive.git

commit abc067abb9ace807f0106d896ce84e48967d3e9c
Author: Kevin Risden <kr...@apache.org>
AuthorDate: Tue Mar 17 17:01:03 2020 +0000

    HIVE-22539: HiveServer2 SPNEGO authentication should skip if authorization header is empty (Kevin Risden via Zoltan Haindrich)
    
    Signed-off-by: Zoltan Haindrich <ki...@rxd.hu>
---
 .../hive/service/cli/thrift/ThriftHttpServlet.java | 53 ++++++++--------
 .../service/cli/thrift/ThriftHttpServletTest.java  | 71 ++++++++++++++++++++++
 2 files changed, 98 insertions(+), 26 deletions(-)

diff --git a/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java b/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
index e2231c2..6eb2606 100644
--- a/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
+++ b/service/src/java/org/apache/hive/service/cli/thrift/ThriftHttpServlet.java
@@ -36,6 +36,7 @@ import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 import javax.ws.rs.core.NewCookie;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.io.ByteStreams;
 import org.apache.commons.codec.binary.Base64;
 import org.apache.commons.codec.binary.StringUtils;
@@ -45,7 +46,6 @@ import org.apache.hadoop.hive.shims.HadoopShims.KerberosNameShim;
 import org.apache.hadoop.hive.shims.ShimLoader;
 import org.apache.hadoop.hive.shims.Utils;
 import org.apache.hadoop.security.UserGroupInformation;
-import org.apache.hadoop.security.token.delegation.web.DelegationTokenAuthenticator;
 import org.apache.hive.service.CookieSigner;
 import org.apache.hive.service.auth.AuthenticationProviderFactory;
 import org.apache.hive.service.auth.AuthenticationProviderFactory.AuthMethods;
@@ -163,7 +163,7 @@ public class ThriftHttpServlet extends TServlet {
         List<String> forwardedAddresses = Arrays.asList(forwarded_for.split(","));
         SessionManager.setForwardedAddresses(forwardedAddresses);
       } else {
-        SessionManager.setForwardedAddresses(Collections.<String>emptyList());
+        SessionManager.setForwardedAddresses(Collections.emptyList());
       }
 
       // If the cookie based authentication is not enabled or the request does not have a valid
@@ -196,7 +196,7 @@ public class ThriftHttpServlet extends TServlet {
             String delegationToken = request.getHeader(HIVE_DELEGATION_TOKEN_HEADER);
             // Each http request must have an Authorization header
             if ((delegationToken != null) && (!delegationToken.isEmpty())) {
-              clientUserName = doTokenAuth(request, response);
+              clientUserName = doTokenAuth(request);
             } else {
               clientUserName = doKerberosAuth(request);
             }
@@ -319,12 +319,12 @@ public class ThriftHttpServlet extends TServlet {
    * Each cookie is of the format [key]=[value]
    */
   private String toCookieStr(Cookie[] cookies) {
-	String cookieStr = "";
+    StringBuilder cookieStr = new StringBuilder();
 
-	for (Cookie c : cookies) {
-     cookieStr += c.getName() + "=" + c.getValue() + " ;\n";
+    for (Cookie c : cookies) {
+      cookieStr.append(c.getName()).append('=').append(c.getValue()).append(" ;\n");
     }
-    return cookieStr;
+    return cookieStr.toString();
   }
 
   /**
@@ -386,9 +386,9 @@ public class ThriftHttpServlet extends TServlet {
 
   /**
    * Do the LDAP/PAM authentication
-   * @param request
-   * @param authType
-   * @throws HttpAuthenticationException
+   * @param request request to authenticate
+   * @param authType type of authentication
+   * @throws HttpAuthenticationException on error authenticating end user
    */
   private String doPasswdAuth(HttpServletRequest request, String authType)
       throws HttpAuthenticationException {
@@ -408,7 +408,7 @@ public class ThriftHttpServlet extends TServlet {
     return userName;
   }
 
-  private String doTokenAuth(HttpServletRequest request, HttpServletResponse response)
+  private String doTokenAuth(HttpServletRequest request)
       throws HttpAuthenticationException {
     String tokenStr = request.getHeader(HIVE_DELEGATION_TOKEN_HEADER);
     try {
@@ -424,18 +424,23 @@ public class ThriftHttpServlet extends TServlet {
    * 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
+   * @param request Request to act on
+   * @return client principal name
+   * @throws HttpAuthenticationException on error authenticating the user
    */
-  private String doKerberosAuth(HttpServletRequest request)
+  @VisibleForTesting
+  String doKerberosAuth(HttpServletRequest request)
       throws HttpAuthenticationException {
-    // Try authenticating with the http/_HOST principal
+    // Each http request must have an Authorization header
+    // Check before trying to do kerberos authentication twice
+    getAuthHeader(request, authType);
+
+    // 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, " +
+        LOG.info("Failed to authenticate with HTTP/_HOST kerberos principal, " +
             "trying with hive/_HOST kerberos principal");
       }
     }
@@ -443,13 +448,9 @@ public class ThriftHttpServlet extends TServlet {
     try {
       return serviceUGI.doAs(new HttpKerberosServerAction(request, serviceUGI));
     } catch (Exception e) {
-      if (e.getCause() instanceof HttpEmptyAuthenticationException) {
-        throw (HttpEmptyAuthenticationException)e.getCause();
-      }
       LOG.error("Failed to authenticate with hive/_HOST kerberos principal");
       throw new HttpAuthenticationException(e);
     }
-
   }
 
   class HttpKerberosServerAction implements PrivilegedExceptionAction<String> {
@@ -578,10 +579,10 @@ public class ThriftHttpServlet extends TServlet {
 
   /**
    * Returns the base64 encoded auth header payload
-   * @param request
-   * @param authType
-   * @return
-   * @throws HttpAuthenticationException
+   * @param request request to interrogate
+   * @param authType Either BASIC or NEGOTIATE
+   * @return base64 encoded auth header payload
+   * @throws HttpAuthenticationException exception if header is missing or empty
    */
   private String getAuthHeader(HttpServletRequest request, String authType)
       throws HttpAuthenticationException {
@@ -602,7 +603,7 @@ public class ThriftHttpServlet extends TServlet {
     }
     authHeaderBase64String = authHeader.substring(beginIndex);
     // Authorization header must have a payload
-    if (authHeaderBase64String == null || authHeaderBase64String.isEmpty()) {
+    if (authHeaderBase64String.isEmpty()) {
       throw new HttpAuthenticationException("Authorization header received " +
           "from the client does not contain any data.");
     }
diff --git a/service/src/test/org/apache/hive/service/cli/thrift/ThriftHttpServletTest.java b/service/src/test/org/apache/hive/service/cli/thrift/ThriftHttpServletTest.java
new file mode 100644
index 0000000..948e97f
--- /dev/null
+++ b/service/src/test/org/apache/hive/service/cli/thrift/ThriftHttpServletTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hive.service.cli.thrift;
+
+import org.apache.hive.service.auth.HiveAuthConstants;
+import org.apache.hive.service.auth.HttpAuthUtils;
+import org.apache.hive.service.auth.ldap.HttpEmptyAuthenticationException;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import org.junit.runner.RunWith;
+import org.mockito.Mockito;
+import org.mockito.runners.MockitoJUnitRunner;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * ThriftHttpServletTest.
+ */
+@RunWith(MockitoJUnitRunner.class)
+public class ThriftHttpServletTest {
+  @Rule
+  public ExpectedException exceptionRule = ExpectedException.none();
+
+  private ThriftHttpServlet thriftHttpServlet;
+
+  @Before
+  public void setUp() {
+    String authType = HiveAuthConstants.AuthTypes.KERBEROS.toString();
+    thriftHttpServlet = new ThriftHttpServlet(null, null, authType, null, null, null);
+  }
+
+  @Test
+  public void testMissingAuthorizatonHeader() throws Exception {
+    HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class);
+    Mockito.when(httpServletRequest.getHeader(HttpAuthUtils.AUTHORIZATION)).thenReturn(null);
+
+    exceptionRule.expect(HttpEmptyAuthenticationException.class);
+    exceptionRule.expectMessage("Authorization header received " +
+            "from the client is empty.");
+    thriftHttpServlet.doKerberosAuth(httpServletRequest);
+  }
+
+  @Test
+  public void testEmptyAuthorizatonHeader() throws Exception {
+    HttpServletRequest httpServletRequest = Mockito.mock(HttpServletRequest.class);
+    Mockito.when(httpServletRequest.getHeader(HttpAuthUtils.AUTHORIZATION)).thenReturn("");
+
+    exceptionRule.expect(HttpEmptyAuthenticationException.class);
+    exceptionRule.expectMessage("Authorization header received " +
+        "from the client is empty.");
+    thriftHttpServlet.doKerberosAuth(httpServletRequest);
+  }
+}