1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 package org.apache.hadoop.hdfs.server.namenode;
19 
20 import java.io.IOException;
21 import java.util.Arrays;
22 import java.util.Collections;
23 import java.util.HashSet;
24 import java.util.Set;
25 import java.util.Stack;
26 
27 import org.apache.commons.logging.Log;
28 import org.apache.commons.logging.LogFactory;
29 import org.apache.hadoop.fs.permission.FsAction;
30 import org.apache.hadoop.fs.permission.FsPermission;
31 import org.apache.hadoop.security.AccessControlException;
32 import org.apache.hadoop.security.UserGroupInformation;
33 
34 /**
35  * Class that helps in checking file system permission.
36  * The state of this class need not be synchronized as it has data structures that
37  * are read-only.
38  *
39  * Some of the helper methods are guarded by {@link FSNamesystem} intrinsic lock.
40  */
41 class FSPermissionChecker {
42   static final Log LOG = LogFactory.getLog(UserGroupInformation.class);
43 
44   private final UserGroupInformation ugi;
45   private final String user;
46   /** A set with group namess. Not synchronized since it is unmodifiable */
47   private final Set<String> groups;
48   private final boolean isSuper;
49 
FSPermissionChecker(String fsOwner, String supergroup )50   FSPermissionChecker(String fsOwner, String supergroup
51       ) throws AccessControlException{
52     try {
53       ugi = UserGroupInformation.getCurrentUser();
54     } catch (IOException e) {
55       throw new AccessControlException(e);
56     }
57 
58     HashSet<String> s = new HashSet<String>(Arrays.asList(ugi.getGroupNames()));
59     groups = Collections.unmodifiableSet(s);
60     user = ugi.getShortUserName();
61     isSuper = user.equals(fsOwner) || groups.contains(supergroup);
62   }
63 
64   /**
65    * Check if the callers group contains the required values.
66    * @param group group to check
67    */
containsGroup(String group)68   public boolean containsGroup(String group) {return groups.contains(group);}
69 
getUser()70   public String getUser() {
71     return user;
72   }
73 
isSuperUser()74   public boolean isSuperUser() {
75     return isSuper;
76   }
77 
78   /**
79    * Verify if the caller has the required permission. This will result into
80    * an exception if the caller is not allowed to access the resource.
81    */
checkSuperuserPrivilege()82   public void checkSuperuserPrivilege()
83       throws AccessControlException {
84     if (!isSuper) {
85       throw new AccessControlException("Access denied for user "
86           + user + ". Superuser privilege is required");
87     }
88   }
89 
90   /**
91    * Check whether current user have permissions to access the path.
92    * Traverse is always checked.
93    *
94    * Parent path means the parent directory for the path.
95    * Ancestor path means the last (the closest) existing ancestor directory
96    * of the path.
97    * Note that if the parent path exists,
98    * then the parent path and the ancestor path are the same.
99    *
100    * For example, suppose the path is "/foo/bar/baz".
101    * No matter baz is a file or a directory,
102    * the parent path is "/foo/bar".
103    * If bar exists, then the ancestor path is also "/foo/bar".
104    * If bar does not exist and foo exists,
105    * then the ancestor path is "/foo".
106    * Further, if both foo and bar do not exist,
107    * then the ancestor path is "/".
108    *
109    * @param doCheckOwner Require user to be the owner of the path?
110    * @param ancestorAccess The access required by the ancestor of the path.
111    * @param parentAccess The access required by the parent of the path.
112    * @param access The access required by the path.
113    * @param subAccess If path is a directory,
114    * it is the access required of the path and all the sub-directories.
115    * If path is not a directory, there is no effect.
116    * @throws AccessControlException
117    *
118    * Guarded by {@link FSNamesystem} intrinsic lock
119    * Caller of this method must hold that lock.
120    */
checkPermission(String path, INodeDirectory root, boolean doCheckOwner, FsAction ancestorAccess, FsAction parentAccess, FsAction access, FsAction subAccess)121   void checkPermission(String path, INodeDirectory root, boolean doCheckOwner,
122       FsAction ancestorAccess, FsAction parentAccess, FsAction access,
123       FsAction subAccess) throws AccessControlException {
124     if (LOG.isDebugEnabled()) {
125       LOG.debug("ACCESS CHECK: " + this
126           + ", doCheckOwner=" + doCheckOwner
127           + ", ancestorAccess=" + ancestorAccess
128           + ", parentAccess=" + parentAccess
129           + ", access=" + access
130           + ", subAccess=" + subAccess);
131     }
132 
133     synchronized(root) {
134       INode[] inodes = root.getExistingPathINodes(path);
135       int ancestorIndex = inodes.length - 2;
136       for(; ancestorIndex >= 0 && inodes[ancestorIndex] == null;
137           ancestorIndex--);
138       checkTraverse(inodes, ancestorIndex);
139 
140       if (ancestorAccess != null && inodes.length > 1) {
141         check(inodes, ancestorIndex, ancestorAccess);
142       }
143       if (parentAccess != null && inodes.length > 1) {
144         check(inodes, inodes.length - 2, parentAccess);
145       }
146       if (access != null) {
147         check(inodes[inodes.length - 1], access);
148       }
149       if (subAccess != null) {
150         checkSubAccess(inodes[inodes.length - 1], subAccess);
151       }
152       if (doCheckOwner) {
153         checkOwner(inodes[inodes.length - 1]);
154       }
155     }
156   }
157 
158   /** Guarded by {@link FSNamesystem} intrinsic lock */
checkOwner(INode inode)159   private void checkOwner(INode inode) throws AccessControlException {
160     if (inode != null && user.equals(inode.getUserName())) {
161       return;
162     }
163     throw new AccessControlException("Permission denied");
164   }
165 
166   /** Guarded by {@link FSNamesystem} intrinsic lock */
checkTraverse(INode[] inodes, int last )167   private void checkTraverse(INode[] inodes, int last
168       ) throws AccessControlException {
169     for(int j = 0; j <= last; j++) {
170       check(inodes[j], FsAction.EXECUTE);
171     }
172   }
173 
174   /** Guarded by {@link FSNamesystem} intrinsic lock */
checkSubAccess(INode inode, FsAction access )175   private void checkSubAccess(INode inode, FsAction access
176       ) throws AccessControlException {
177     if (inode == null || !inode.isDirectory()) {
178       return;
179     }
180 
181     Stack<INodeDirectory> directories = new Stack<INodeDirectory>();
182     for(directories.push((INodeDirectory)inode); !directories.isEmpty(); ) {
183       INodeDirectory d = directories.pop();
184       check(d, access);
185 
186       for(INode child : d.getChildren()) {
187         if (child.isDirectory()) {
188           directories.push((INodeDirectory)child);
189         }
190       }
191     }
192   }
193 
194   /** Guarded by {@link FSNamesystem} intrinsic lock */
check(INode[] inodes, int i, FsAction access )195   private void check(INode[] inodes, int i, FsAction access
196       ) throws AccessControlException {
197     check(i >= 0? inodes[i]: null, access);
198   }
199 
200   /** Guarded by {@link FSNamesystem} intrinsic lock */
check(INode inode, FsAction access )201   private void check(INode inode, FsAction access
202       ) throws AccessControlException {
203     if (inode == null) {
204       return;
205     }
206     FsPermission mode = inode.getFsPermission();
207 
208     if (user.equals(inode.getUserName())) { //user class
209       if (mode.getUserAction().implies(access)) { return; }
210     }
211     else if (groups.contains(inode.getGroupName())) { //group class
212       if (mode.getGroupAction().implies(access)) { return; }
213     }
214     else { //other class
215       if (mode.getOtherAction().implies(access)) { return; }
216     }
217     throw new AccessControlException("Permission denied: user=" + user
218         + ", access=" + access + ", inode=" + inode);
219   }
220 }
221