You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ranger.apache.org by sn...@apache.org on 2016/03/24 03:43:54 UTC

incubator-ranger git commit: RANGER-746: Addressing suggestions from Review - Add wildcard, multiple CN & SAN support when validating plugins' SSL certs

Repository: incubator-ranger
Updated Branches:
  refs/heads/master 147ff4b11 -> 31c2b030c


RANGER-746: Addressing suggestions from Review - Add wildcard, multiple CN & SAN support when validating plugins' SSL certs

Signed-off-by: sneethiraj <sn...@apache.org>


Project: http://git-wip-us.apache.org/repos/asf/incubator-ranger/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-ranger/commit/31c2b030
Tree: http://git-wip-us.apache.org/repos/asf/incubator-ranger/tree/31c2b030
Diff: http://git-wip-us.apache.org/repos/asf/incubator-ranger/diff/31c2b030

Branch: refs/heads/master
Commit: 31c2b030c50ff473d29d12897a03828071618615
Parents: 147ff4b
Author: Velmurugan Periasamy <ve...@apache.org>
Authored: Wed Mar 23 18:42:11 2016 -0400
Committer: sneethiraj <sn...@apache.org>
Committed: Wed Mar 23 22:43:03 2016 -0400

----------------------------------------------------------------------
 .../org/apache/ranger/common/ServiceUtil.java   | 169 ++++++++++++++-----
 1 file changed, 130 insertions(+), 39 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-ranger/blob/31c2b030/security-admin/src/main/java/org/apache/ranger/common/ServiceUtil.java
----------------------------------------------------------------------
diff --git a/security-admin/src/main/java/org/apache/ranger/common/ServiceUtil.java b/security-admin/src/main/java/org/apache/ranger/common/ServiceUtil.java
index c6f5258..0706041 100644
--- a/security-admin/src/main/java/org/apache/ranger/common/ServiceUtil.java
+++ b/security-admin/src/main/java/org/apache/ranger/common/ServiceUtil.java
@@ -21,6 +21,7 @@ package org.apache.ranger.common;
 
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
@@ -65,6 +66,8 @@ import org.springframework.stereotype.Component;
 @Component
 public class ServiceUtil {
 	static final Logger LOG = Logger.getLogger(ServiceUtil.class);
+	private static final String REGEX_PREFIX_STR 	 = "regex:" ;
+	private static final int REGEX_PREFIX_STR_LENGTH = REGEX_PREFIX_STR.length() ;
 
 	static Map<String, Integer> mapServiceTypeToAssetType = new HashMap<String, Integer>();
 	static Map<String, Integer> mapAccessTypeToPermType   = new HashMap<String, Integer>();
@@ -1313,10 +1316,8 @@ public class ServiceUtil {
 	}
 	
    
-   public boolean isValidateHttpsAuthentication( String serviceName, HttpServletRequest request) {
-		  
+	public boolean isValidateHttpsAuthentication( String serviceName, HttpServletRequest request) {		  
 		boolean isValidAuthentication=false;
-//		boolean httpEnabled = PropertiesUtil.getBooleanProperty("http.enabled",true);
 		boolean httpEnabled = PropertiesUtil.getBooleanProperty("ranger.service.http.enabled",true);
 		X509Certificate[] certchain = (X509Certificate[]) request.getAttribute("javax.servlet.request.X509Certificate");
 		String ipAddress = request.getHeader("X-FORWARDED-FOR");  
@@ -1324,13 +1325,13 @@ public class ServiceUtil {
 			ipAddress = request.getRemoteAddr();
 		}
 		boolean isSecure = request.isSecure();
-		
+
 		if (serviceName == null || serviceName.isEmpty()) {			
 			LOG.error("ServiceName not provided");
 			throw restErrorUtil.createRESTException("Unauthorized access.",
 					MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);
 		}
-		
+
 		RangerService service = null;
 		try {
 			service = svcStore.getServiceByName(serviceName);
@@ -1362,50 +1363,140 @@ public class ServiceUtil {
 						+ " unable to get client certificate",
 						MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);
 			}
-		}		
-		String commonName = null;
-		if (certchain != null) {
-			X509Certificate clientCert = certchain[0];
-			String dn = clientCert.getSubjectX500Principal().getName();
+
+			// Check if common name is found in service config
+			Map<String, String> configMap = service.getConfigs();
+			String cnFromConfig = configMap.get("commonNameForCertificate");
+			if (cnFromConfig == null || "".equals(cnFromConfig.trim())) {
+				LOG.error("Unauthorized access. No common name for certificate set. Please check your service config");
+				throw restErrorUtil.createRESTException("Unauthorized access. No common name for certificate set. Please check your service config",
+						MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);		
+			}
+
+
+			String cnFromConfigForTest = cnFromConfig ;
+			boolean isRegEx = cnFromConfig.toLowerCase().startsWith(REGEX_PREFIX_STR) ;
+			if (isRegEx) {
+				cnFromConfigForTest = cnFromConfig.substring(REGEX_PREFIX_STR_LENGTH) ;
+			}
+
+			// Perform SAN validation
 			try {
-				LdapName ln = new LdapName(dn);
-				for (Rdn rdn : ln.getRdns()) {
-					if (rdn.getType().equalsIgnoreCase("CN")) {
-						commonName = rdn.getValue() + "";
-						break;
+			    Collection<List<?>> subjectAltNames = certchain[0].getSubjectAlternativeNames();
+				if (subjectAltNames != null) {
+					for (List<?> sanItem : subjectAltNames) {
+						if (sanItem.size() == 2) {
+							Integer sanType = (Integer) sanItem.get(0);
+							String sanValue = (String) sanItem.get(1);
+							if ( (sanType == 2 || sanType == 7) && (matchNames(sanValue, cnFromConfigForTest,isRegEx)) ) {
+								if (LOG.isDebugEnabled()) LOG.debug("Client Cert verification successful, matched SAN:" + sanValue);
+								isValidAuthentication=true;
+								break;
+							}
+						}
 					}
 				}
-				if (commonName == null) {
-					LOG.error("Unauthorized access. CName is null. serviceName=" + serviceName);
-					throw restErrorUtil.createRESTException(
-							"Unauthorized access - Unable to find Common Name from ["
-									+ dn + "]",
-							MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);
-				}
-			} catch (InvalidNameException e) {
-				LOG.error("Invalid Common Name. CName=" + commonName + ", serviceName=" + serviceName, e);
-				throw restErrorUtil.createRESTException(
-						"Unauthorized access - Invalid Common Name",
-						MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);
+			} catch (Throwable e) {
+			    LOG.error("Unauthorized access. Error getting SAN from certificate", e);
+				throw restErrorUtil.createRESTException("Unauthorized access - Error getting SAN from client certificate", MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);						
 			}
-		}		
-		if (commonName != null) {
 
-			Map<String, String> configMap = service.getConfigs();
-			String cnFromConfig = configMap.get("commonNameForCertificate");
-			if (cnFromConfig == null
-					|| !commonName.equalsIgnoreCase(cnFromConfig)) {
-				LOG.error("Unauthorized access. expected [" + cnFromConfig + "], found [" 
-					+ commonName + "], serviceName=" + serviceName);
-				throw restErrorUtil.createRESTException(
-						"Unauthorized access. expected [" + cnFromConfig
+			// Perform common name validation only if SAN validation did not succeed
+			if (!isValidAuthentication) {
+				String commonName = null;
+				if (certchain != null) {
+					X509Certificate clientCert = certchain[0];
+					String dn = clientCert.getSubjectX500Principal().getName();
+					try {
+						LdapName ln = new LdapName(dn);
+						for (Rdn rdn : ln.getRdns()) {
+							if (rdn.getType().equalsIgnoreCase("CN")) {
+								commonName = rdn.getValue() + "";
+								break;
+							}
+						}
+						if (commonName == null) {
+							LOG.error("Unauthorized access. CName is null. serviceName=" + serviceName);
+							throw restErrorUtil.createRESTException(
+									"Unauthorized access - Unable to find Common Name from ["
+											+ dn + "]",
+											MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);
+						}
+					} catch (InvalidNameException e) {
+						LOG.error("Invalid Common Name. CName=" + commonName + ", serviceName=" + serviceName, e);
+						throw restErrorUtil.createRESTException(
+								"Unauthorized access - Invalid Common Name",
+								MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);
+					}
+				}		
+				if (commonName != null) {
+					if (matchNames(commonName, cnFromConfigForTest,isRegEx)) {
+						if (LOG.isDebugEnabled()) LOG.debug("Client Cert verification successful, matched CN " + commonName + " with " + cnFromConfigForTest + ", wildcard match = " + isRegEx);
+						isValidAuthentication=true;
+					}
+
+					if (!isValidAuthentication) {
+						LOG.error("Unauthorized access. expected [" + cnFromConfigForTest + "], found [" 
+								+ commonName + "], serviceName=" + serviceName);
+						throw restErrorUtil.createRESTException(
+								"Unauthorized access. expected [" + cnFromConfigForTest
 								+ "], found [" + commonName + "]",
-						MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);
+								MessageEnums.OPER_NOT_ALLOWED_FOR_ENTITY);
+					}
+				}
 			}
+		} else {
+			isValidAuthentication = true;  
 		}
-		isValidAuthentication=true;
 		return isValidAuthentication;
 	}
+   
+   private boolean matchNames(String target, String source, boolean wildcardMatch) {
+       boolean matched = false;
+       if(target != null && source != null) {
+           String names[] = (wildcardMatch ? new String[] { source } : source.split(","));
+           for (String n:names) {
+               
+               if (wildcardMatch) {
+                   if(LOG.isDebugEnabled()) LOG.debug("Wildcard Matching [" + target + "] with [" + n + "]");
+            	   if (wildcardMatch(target,n)) {
+            		   if(LOG.isDebugEnabled()) LOG.debug("Matched target:" + target + " with " + n);
+            		   matched = true;
+            		   break;
+            	   }            	               	   
+               } else {
+                   if(LOG.isDebugEnabled()) LOG.debug("Matching [" + target + "] with [" + n + "]");
+            	   if (target.equalsIgnoreCase(n)) {
+            		   if(LOG.isDebugEnabled()) LOG.debug("Matched target:" + target + " with " + n);
+            		   matched = true;
+            		   break;
+            	   }            	   
+               }
+           }
+       } else {
+    	   if(LOG.isDebugEnabled()) LOG.debug("source=[" + source + "],target=[" + target +"], returning false.");
+       }
+       return matched;
+   }
+   
+   private boolean matchNames(String target, String source) {
+	   return matchNames(target,source,false);
+   }   
+   
+   private boolean wildcardMatch(String target, String source) {
+	   boolean matched = false;
+	   if(target != null && source != null) {
+		   try {
+			   matched = target.matches(source);
+		   } catch (Throwable e) {
+			   LOG.error("Error doing wildcard match..", e);
+		   }
+	   } else {
+    	   if(LOG.isDebugEnabled()) LOG.debug("source=[" + source + "],target=[" + target +"], returning false.");
+       }
+	   return matched;
+   }
+   
 	
 	private Boolean toBooleanReplacePerm(boolean isReplacePermission) {