RANGER-2183: Use INodeAttribute information to authorize HDFS access

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

Branch: refs/heads/ranger-0.7
Commit: 29801e0e51b752530be7ce36e786111459614387
Parents: 2d35834
Author: Abhay Kulkarni <akulka...@hortonworks.com>
Authored: Thu Aug 9 14:08:11 2018 -0700
Committer: Abhay Kulkarni <akulka...@hortonworks.com>
Committed: Wed Sep 26 21:46:42 2018 -0700

----------------------------------------------------------------------
 .../hadoop/RangerHdfsAuthorizer.java            | 181 +++++++++++++++----
 1 file changed, 145 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ranger/blob/29801e0e/hdfs-agent/src/main/java/org/apache/ranger/authorization/hadoop/RangerHdfsAuthorizer.java
----------------------------------------------------------------------
diff --git 
a/hdfs-agent/src/main/java/org/apache/ranger/authorization/hadoop/RangerHdfsAuthorizer.java
 
b/hdfs-agent/src/main/java/org/apache/ranger/authorization/hadoop/RangerHdfsAuthorizer.java
index b37d0ff..22de0e8 100644
--- 
a/hdfs-agent/src/main/java/org/apache/ranger/authorization/hadoop/RangerHdfsAuthorizer.java
+++ 
b/hdfs-agent/src/main/java/org/apache/ranger/authorization/hadoop/RangerHdfsAuthorizer.java
@@ -20,6 +20,7 @@
 package org.apache.ranger.authorization.hadoop;
 
 import static 
org.apache.ranger.authorization.hadoop.constants.RangerHadoopConstants.EXECUTE_ACCCESS_TYPE;
+import static 
org.apache.ranger.authorization.hadoop.constants.RangerHadoopConstants.HDFS_ROOT_FOLDER_PATH;
 import static 
org.apache.ranger.authorization.hadoop.constants.RangerHadoopConstants.READ_ACCCESS_TYPE;
 import static 
org.apache.ranger.authorization.hadoop.constants.RangerHadoopConstants.WRITE_ACCCESS_TYPE;
 
@@ -37,6 +38,7 @@ import org.apache.commons.lang.StringUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.apache.hadoop.fs.permission.FsAction;
+import org.apache.hadoop.hdfs.DFSUtil;
 import org.apache.hadoop.hdfs.server.namenode.INode;
 import org.apache.hadoop.hdfs.server.namenode.INodeAttributeProvider;
 import org.apache.hadoop.hdfs.server.namenode.INodeAttributes;
@@ -196,6 +198,16 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                        }
                }
 
+               class SubAccessData {
+                       final INodeDirectory    dir;
+                       final String            resourcePath;
+
+                       SubAccessData(INodeDirectory dir, String resourcePath) {
+                               this.dir            = dir;
+                               this.resourcePath   = resourcePath;
+                       }
+               }
+
                @Override
                public void checkPermission(String fsOwner, String superGroup, 
UserGroupInformation ugi,
                                                                        
INodeAttributes[] inodeAttrs, INode[] inodes, byte[][] pathByNameArr,
@@ -207,11 +219,12 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                        RangerHdfsAuditHandler auditHandler  = null;
                        String                 user          = ugi != null ? 
ugi.getShortUserName() : null;
                        Set<String>            groups        = ugi != null ? 
Sets.newHashSet(ugi.getGroupNames()) : null;
+                       String                 resourcePath  = path;
 
                        if(LOG.isDebugEnabled()) {
                                LOG.debug("==> 
RangerAccessControlEnforcer.checkPermission("
                                                + "fsOwner=" + fsOwner + "; 
superGroup=" + superGroup + ", inodesCount=" + (inodes != null ? inodes.length 
: 0)
-                                               + ", snapshotId=" + snapshotId 
+ ", user=" + user + ", path=" + path + ", ancestorIndex=" + ancestorIndex
+                                               + ", snapshotId=" + snapshotId 
+ ", user=" + user + ", provided-path=" + path + ", ancestorIndex=" + 
ancestorIndex
                                                + ", doCheckOwner="+ 
doCheckOwner + ", ancestorAccess=" + ancestorAccess + ", parentAccess=" + 
parentAccess
                                                + ", access=" + access + ", 
subAccess=" + subAccess + ", ignoreEmptyDir=" + ignoreEmptyDir + ")");
                        }
@@ -219,7 +232,7 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                        RangerPerfTracer perf = null;
 
                        
if(RangerPerfTracer.isPerfTraceEnabled(PERF_HDFSAUTH_REQUEST_LOG)) {
-                               perf = 
RangerPerfTracer.getPerfTracer(PERF_HDFSAUTH_REQUEST_LOG, 
"RangerHdfsAuthorizer.checkPermission(path=" + path + ")");
+                               perf = 
RangerPerfTracer.getPerfTracer(PERF_HDFSAUTH_REQUEST_LOG, 
"RangerHdfsAuthorizer.checkPermission(provided-path=" + path + ")");
                        }
 
                        try {
@@ -229,6 +242,29 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                                INode   inode               = null;
 
                                if(plugin != null && 
!ArrayUtils.isEmpty(inodes)) {
+                                       int sz = inodeAttrs.length;
+                                       if (LOG.isDebugEnabled()) {
+                                               LOG.debug("Size of INodeAttrs 
array:[" + sz + "]");
+                                       }
+                                       byte[][] components = new byte[sz][];
+
+                                       int i = 0;
+                                       for (; i < sz; i++) {
+                                               if (inodeAttrs[i] != null) {
+                                                       components[i] = 
inodeAttrs[i].getLocalNameBytes();
+                                               } else {
+                                                       break;
+                                               }
+                                       }
+                                       if (i != sz) {
+                                               if (LOG.isDebugEnabled()) {
+                                                       LOG.debug("Input 
INodeAttributes array contains null at position " + i);
+                                                       LOG.debug("Will use 
only first [" + i + "] components to build resourcePath");
+                                               }
+                                       }
+
+                                       resourcePath = 
DFSUtil.byteArray2PathString(components, 0, i);
+
                                        if(ancestorIndex >= inodes.length) {
                                                ancestorIndex = inodes.length - 
1;
                                        }
@@ -241,25 +277,10 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                                        parent   = inodes.length > 1 ? 
inodes[inodes.length - 2] : null;
                                        inode    = inodes[inodes.length - 1]; 
// could be null while creating a new file
 
-                                       auditHandler = new 
RangerHdfsAuditHandler(path, isTraverseOnlyCheck);
+                                       auditHandler = new 
RangerHdfsAuditHandler(resourcePath, isTraverseOnlyCheck);
 
                                        if(isTraverseOnlyCheck) {
-                                               INode           nodeToCheck = 
inode;
-                                               INodeAttributes nodeAttribs = 
inodeAttrs.length > 0 ? inodeAttrs[inodeAttrs.length - 1] : null;
-
-                                               if(nodeToCheck == null || 
nodeToCheck.isFile()) {
-                                                       if(parent != null) {
-                                                               nodeToCheck = 
parent;
-                                                               nodeAttribs = 
inodeAttrs.length > 1 ? inodeAttrs[inodeAttrs.length - 2] : null;
-                                                       } else if(ancestor != 
null) {
-                                                               nodeToCheck = 
ancestor;
-                                                               nodeAttribs = 
inodeAttrs.length > ancestorIndex ? inodeAttrs[ancestorIndex] : null;
-                                                       }
-                                               }
-
-                                               if(nodeToCheck != null) {
-                                                       authzStatus = 
isAccessAllowed(nodeToCheck, nodeAttribs, FsAction.EXECUTE, user, groups, 
plugin, auditHandler);
-                                               }
+                                               authzStatus = 
traverseOnlyCheck(inode, inodeAttrs, resourcePath, components, parent, 
ancestor, ancestorIndex, user, groups, plugin, auditHandler);
                                        }
 
                                        // checkStickyBit
@@ -273,8 +294,9 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                                        // checkAncestorAccess
                                        if(authzStatus == AuthzStatus.ALLOW && 
ancestorAccess != null && ancestor != null) {
                                                INodeAttributes ancestorAttribs 
= inodeAttrs.length > ancestorIndex ? inodeAttrs[ancestorIndex] : null;
+                                               String ancestorPath = 
ancestorAttribs != null ? DFSUtil.byteArray2PathString(components, 0, 
ancestorIndex + 1) : null;
 
-                                               authzStatus = 
isAccessAllowed(ancestor, ancestorAttribs, ancestorAccess, user, groups, 
plugin, auditHandler);
+                                               authzStatus = 
isAccessAllowed(ancestor, ancestorAttribs, ancestorPath, ancestorAccess, user, 
groups, plugin, auditHandler);
                                                if (authzStatus == 
AuthzStatus.NOT_DETERMINED) {
                                                        authzStatus = 
checkDefaultEnforcer(fsOwner, superGroup, ugi, inodeAttrs, inodes,
                                                                                
        pathByNameArr, snapshotId, path, ancestorIndex, doCheckOwner,
@@ -286,8 +308,9 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                                        // checkParentAccess
                                        if(authzStatus == AuthzStatus.ALLOW && 
parentAccess != null && parent != null) {
                                                INodeAttributes parentAttribs = 
inodeAttrs.length > 1 ? inodeAttrs[inodeAttrs.length - 2] : null;
+                                               String parentPath = 
parentAttribs != null ? DFSUtil.byteArray2PathString(components, 0, 
inodeAttrs.length - 1) : null;
 
-                                               authzStatus = 
isAccessAllowed(parent, parentAttribs, parentAccess, user, groups, plugin, 
auditHandler);
+                                               authzStatus = 
isAccessAllowed(parent, parentAttribs, parentPath, parentAccess, user, groups, 
plugin, auditHandler);
                                                if (authzStatus == 
AuthzStatus.NOT_DETERMINED) {
                                                        authzStatus = 
checkDefaultEnforcer(fsOwner, superGroup, ugi, inodeAttrs, inodes,
                                                                                
        pathByNameArr, snapshotId, path, ancestorIndex, doCheckOwner,
@@ -300,7 +323,7 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                                        if(authzStatus == AuthzStatus.ALLOW && 
access != null && inode != null) {
                                                INodeAttributes inodeAttribs = 
inodeAttrs.length > 0 ? inodeAttrs[inodeAttrs.length - 1] : null;
 
-                                               authzStatus = 
isAccessAllowed(inode, inodeAttribs, access, user, groups, plugin, 
auditHandler);
+                                               authzStatus = 
isAccessAllowed(inode, inodeAttribs, resourcePath, access, user, groups, 
plugin, auditHandler);
                                                if (authzStatus == 
AuthzStatus.NOT_DETERMINED) {
                                                        authzStatus = 
checkDefaultEnforcer(fsOwner, superGroup, ugi, inodeAttrs, inodes,
                                                                                
        pathByNameArr, snapshotId, path, ancestorIndex, doCheckOwner,
@@ -311,16 +334,16 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
 
                                        // checkSubAccess
                                        if(authzStatus == AuthzStatus.ALLOW && 
subAccess != null && inode != null && inode.isDirectory()) {
-                                               Stack<INodeDirectory> 
directories = new Stack<INodeDirectory>();
+                                               Stack<SubAccessData> 
directories = new Stack<>();
 
-                                               
for(directories.push(inode.asDirectory()); !directories.isEmpty(); ) {
-                                                       INodeDirectory      dir 
  = directories.pop();
-                                                       ReadOnlyList<INode> 
cList = dir.getChildrenList(snapshotId);
+                                               for(directories.push(new 
SubAccessData(inode.asDirectory(), resourcePath)); !directories.isEmpty(); ) {
+                                                       SubAccessData data = 
directories.pop();
+                                                       ReadOnlyList<INode> 
cList = data.dir.getChildrenList(snapshotId);
 
                                                        if (!(cList.isEmpty() 
&& ignoreEmptyDir)) {
-                                                               INodeAttributes 
dirAttribs = dir.getSnapshotINode(snapshotId);
+                                                               INodeAttributes 
dirAttribs = data.dir.getSnapshotINode(snapshotId);
 
-                                                               authzStatus = 
isAccessAllowed(dir, dirAttribs, subAccess, user, groups, plugin, auditHandler);
+                                                               authzStatus = 
isAccessAllowed(data.dir, dirAttribs, data.resourcePath, subAccess, user, 
groups, plugin, auditHandler);
 
                                                                if(authzStatus 
!= AuthzStatus.ALLOW) {
                                                                        break;
@@ -331,13 +354,13 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                                                                boolean 
optimizeSubAccessAuthEnabled = 
RangerHdfsPlugin.isOptimizeSubAccessAuthEnabled();
 
                                                                if 
(optimizeSubAccessAuthEnabled) {
-                                                                       
subDirAuthStatus = isAccessAllowedForHierarchy(dir, dirAttribs, subAccess, 
user, groups, plugin);
+                                                                       
subDirAuthStatus = isAccessAllowedForHierarchy(data.dir, dirAttribs, 
data.resourcePath, subAccess, user, groups, plugin);
                                                                }
 
                                                                if 
(subDirAuthStatus != AuthzStatus.ALLOW) {
                                                                        
for(INode child : cList) {
                                                                                
if (child.isDirectory()) {
-                                                                               
        directories.push(child.asDirectory());
+                                                                               
        directories.push(new SubAccessData(child.asDirectory(), resourcePath + 
org.apache.hadoop.fs.Path.SEPARATOR_CHAR + child.getLocalName()));
                                                                                
}
                                                                        }
                                                                }
@@ -382,7 +405,7 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                                                }
                                        }
 
-                                       throw new 
RangerAccessControlException("Permission denied: user=" + user + ", access=" + 
action + ", inode=\"" + path + "\"");
+                                       throw new 
RangerAccessControlException("Permission denied: user=" + user + ", access=" + 
action + ", inode=\"" + resourcePath + "\"");
                                }
                        } finally {
                                if(auditHandler != null) {
@@ -392,9 +415,97 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                                RangerPerfTracer.log(perf);
 
                                if(LOG.isDebugEnabled()) {
-                                       LOG.debug("<== 
RangerAccessControlEnforcer.checkPermission(" + path + ", " + access + ", 
user=" + user + ") : " + authzStatus);
+                                       LOG.debug("<== 
RangerAccessControlEnforcer.checkPermission(" + resourcePath + ", " + access + 
", user=" + user + ") : " + authzStatus);
+                               }
+                       }
+               }
+
+               /*
+                   Check if parent or ancestor of the file being accessed is 
denied EXECUTE permission. If not, assume that Ranger-acls
+                   allowed EXECUTE access. Do not audit this authorization 
check if resource is a file unless access is explicitly denied
+                */
+               private AuthzStatus traverseOnlyCheck(INode inode, 
INodeAttributes[] inodeAttrs, String path, byte[][] components, INode parent, 
INode ancestor, int ancestorIndex,
+                                                                               
          String user, Set<String> groups, RangerHdfsPlugin plugin, 
RangerHdfsAuditHandler auditHandler) {
+
+                       if (LOG.isDebugEnabled()) {
+                               LOG.debug("==> 
RangerAccessControlEnforcer.traverseOnlyCheck("
+                                               + "path=" + path + ", user=" + 
user + ", groups=" + groups + ")");
+                       }
+                       final AuthzStatus ret;
+
+                       INode nodeToCheck = inode;
+                       INodeAttributes nodeAttribs = inodeAttrs.length > 0 ? 
inodeAttrs[inodeAttrs.length - 1] : null;
+                       boolean skipAuditOnAllow = false;
+
+                       String resourcePath = path;
+                       if (nodeToCheck == null || nodeToCheck.isFile()) {
+                               skipAuditOnAllow = true;
+                               if (parent != null) {
+                                       nodeToCheck = parent;
+                                       nodeAttribs = inodeAttrs.length > 1 ? 
inodeAttrs[inodeAttrs.length - 2] : null;
+                                       resourcePath = inodeAttrs.length > 0 ? 
DFSUtil.byteArray2PathString(components, 0, inodeAttrs.length - 1) : 
HDFS_ROOT_FOLDER_PATH;
+                               } else if (ancestor != null) {
+                                       nodeToCheck = ancestor;
+                                       nodeAttribs = inodeAttrs.length > 
ancestorIndex ? inodeAttrs[ancestorIndex] : null;
+                                       resourcePath = nodeAttribs != null ? 
DFSUtil.byteArray2PathString(components, 0, ancestorIndex+1) : 
HDFS_ROOT_FOLDER_PATH;
+                               }
+                       }
+
+                       if (nodeToCheck != null) {
+                               if (resourcePath.length() > 1) {
+                                       if 
(resourcePath.endsWith(HDFS_ROOT_FOLDER_PATH)) {
+                                               resourcePath = 
resourcePath.substring(0, resourcePath.length()-1);
+                                       }
                                }
+                               ret = isAccessAllowedForTraversal(nodeToCheck, 
nodeAttribs, resourcePath, user, groups, plugin, auditHandler, 
skipAuditOnAllow);
+                       } else {
+                               ret = AuthzStatus.ALLOW;
                        }
+                       if (LOG.isDebugEnabled()) {
+                               LOG.debug("<== 
RangerAccessControlEnforcer.traverseOnlyCheck("
+                                               + "path=" + path + ", 
resourcePath=" + resourcePath + ", user=" + user + ", groups=" + groups + ") : 
" + ret);
+                       }
+                       return ret;
+               }
+
+               private AuthzStatus isAccessAllowedForTraversal(INode inode, 
INodeAttributes inodeAttribs, String path, String user, Set<String> groups, 
RangerHdfsPlugin plugin, RangerHdfsAuditHandler auditHandler, boolean 
skipAuditOnAllow) {
+                       final AuthzStatus ret;
+                       String pathOwner = inodeAttribs != null ? 
inodeAttribs.getUserName() : null;
+                       String clusterName = plugin.getClusterName();
+                       FsAction access = FsAction.EXECUTE;
+
+
+                       if (pathOwner == null) {
+                               pathOwner = inode.getUserName();
+                       }
+
+                       if 
(RangerHadoopConstants.HDFS_ROOT_FOLDER_PATH_ALT.equals(path)) {
+                               path = 
RangerHadoopConstants.HDFS_ROOT_FOLDER_PATH;
+                       }
+
+                       if (LOG.isDebugEnabled()) {
+                               LOG.debug("==> 
RangerAccessControlEnforcer.isAccessAllowedForTraversal(" + path + ", " + 
access + ", " + user + ", " + skipAuditOnAllow + ")");
+                       }
+
+                       RangerHdfsAccessRequest request = new 
RangerHdfsAccessRequest(inode, path, pathOwner, access, EXECUTE_ACCCESS_TYPE, 
user, groups, clusterName);
+
+                       RangerAccessResult result = 
plugin.isAccessAllowed(request, null);
+
+                       if (result != null && result.getIsAccessDetermined() && 
!result.getIsAllowed()) {
+                               ret = AuthzStatus.DENY;
+                       } else {
+                               ret = AuthzStatus.ALLOW;
+                       }
+
+                       if (ret == AuthzStatus.DENY || (!skipAuditOnAllow && 
result != null && result.getIsAccessDetermined())) {
+                               auditHandler.processResult(result);
+                       }
+
+                       if (LOG.isDebugEnabled()) {
+                               LOG.debug("<== 
RangerAccessControlEnforcer.isAccessAllowedForTraversal(" + path + ", " + 
access + ", " + user + ", " + skipAuditOnAllow + "): " + ret);
+                       }
+
+                       return ret;
                }
 
                private AuthzStatus checkDefaultEnforcer(String fsOwner, String 
superGroup, UserGroupInformation ugi,
@@ -476,9 +587,8 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                        return authzStatus;
                }
 
-               private AuthzStatus isAccessAllowed(INode inode, 
INodeAttributes inodeAttribs, FsAction access, String user, Set<String> groups, 
RangerHdfsPlugin plugin, RangerHdfsAuditHandler auditHandler) {
+               private AuthzStatus isAccessAllowed(INode inode, 
INodeAttributes inodeAttribs, String path, FsAction access, String user, 
Set<String> groups, RangerHdfsPlugin plugin, RangerHdfsAuditHandler 
auditHandler) {
                        AuthzStatus ret       = null;
-                       String      path      = inode != null ? 
inode.getFullPathName() : null;
                        String      pathOwner = inodeAttribs != null ? 
inodeAttribs.getUserName() : null;
                        String          clusterName = plugin.getClusterName();
 
@@ -531,9 +641,8 @@ public class RangerHdfsAuthorizer extends 
INodeAttributeProvider {
                        return ret;
                }
 
-               private AuthzStatus isAccessAllowedForHierarchy(INode inode, 
INodeAttributes inodeAttribs, FsAction access, String user, Set<String> groups, 
RangerHdfsPlugin plugin) {
+               private AuthzStatus isAccessAllowedForHierarchy(INode inode, 
INodeAttributes inodeAttribs, String path, FsAction access, String user, 
Set<String> groups, RangerHdfsPlugin plugin) {
                        AuthzStatus ret   = null;
-                       String  path      = inode != null ? 
inode.getFullPathName() : null;
                        String  pathOwner = inodeAttribs != null ? 
inodeAttribs.getUserName() : null;
                        String          clusterName = plugin.getClusterName();
 

Reply via email to