Hi,
I have fixed the code and tested. It's working fine. Please review the
changes.
Web review link: http://cr.openjdk.java.net/~dbuck/8075773/webrev.02/
Regards,
Cheleswer
On 5/15/2015 12:26 PM, cheleswer sahu wrote:
Dear Dan & Dmitry,
Thanks for pointing out the security vulnerability in the fix and for
your precise review. I am also agree with Dmitry's fix. I will fix the
code and resend the review request.
Regards,
Cheleswer
On 5/14/2015 9:46 PM, Gerald Thornbrugh wrote:
Hi Dan,
When Cheleswer and I discussed this fix my interpretation had a
slightly different goal:
Prior to the initial security fix any user could execute "jps" and
get the user names associated
with other user's perf data (i.e. the call to get_user_name_slow()
would succeed.). My initial
thought was that this was a regression for all users not just "root"
and this goal led to this fix.
At the time I did not see this as a security vulnerability, your
review has changed my mind.
I agree that Dmitry's fix is a more secure fix for the issue and I
think we should use it.
Let me know if you have any questions.
Thanks!
Jerry
Hi,
Please review the code changes for
https://bugs.openjdk.java.net/browse/JDK-8075773. I have built and
tested JDK9 with fix successfully. As I do not have an account for
OpenJDK,
David Buck will push the fix into jdk9/hs-rt/.
Web review link: http://cr.openjdk.java.net/~dbuck/8075773/webrev.01/
Regards,
Cheleswer
Cheleswer,
Sorry for the lengthy review, but since this is security related,
I have to be complete.
The goal: Add a policy by-pass for the 'root' user in order to
fix the regression in jps(1) behavior.
The core of this policy by-pass is the change to this function:
205 static bool is_statbuf_secure(struct stat *statp, int mode) {
206 if (S_ISLNK(statp->st_mode) || !S_ISDIR(statp->st_mode)) {
207 // The path represents a link or some non-directory file
type,
208 // which is not what we expected. Declare it insecure.
209 //
210 return false;
211 }
212 // If the directory is going to be opened readonly, we consider
this as secure operation
213 // we do not need to do any more checks.
214 //
215 if ((mode & O_ACCMODE) == O_RDONLY) {
216 return true;
217 }
218 // We have an existing directory, check if the permissions
are safe.
219 //
220 if ((statp->st_mode & (S_IWGRP|S_IWOTH)) != 0) {
221 // The directory is open for writing and could be subjected
222 // to a symlink or a hard link attack. Declare it insecure.
223 //
224 return false;
225 }
226 // See if the uid of the directory matches the effective uid of
the process.
227 //
228 if (statp->st_uid != geteuid()) {
229 // The directory was not created by this user, declare it
insecure.
230 //
231 return false;
232 }
233 return true;
234 }
Lines 212-217 are added which allows a caller that passes in O_RDONLY
to by-pass the security checks on lines 220-225 and 228-232. This
implementation is using an attribute of _how_ the data is accessed
to override security policy instead of an attribute of _who_ is
accessing the data.
Here are the code paths that access the modified policy code:
is_statbuf_secure() is called by:
- is_directory_secure()
- is_dirfd_secure()
is_directory_secure() is called by:
- get_user_name_slow() with O_RDONLY
- make_user_tmp_dir() with O_RDWR
- mmap_attach_shared() with (O_RDONLY | O_NOFOLLOW)
is_dirfd_secure() is called by:
- open_directory_secure() with a mode parameter
open_directory_secure() is called by:
- open_directory_secure_cwd() with O_RDWR
- get_user_name_slow() with O_RDONLY
Only the code paths that specify O_RDWR make use of
the new policy by-pass code so it looks like only
- get_user_name_slow() with O_RDONLY
- mmap_attach_shared() with (O_RDONLY | O_NOFOLLOW)
are interesting.
The new security policy by-pass will allow get_user_name_slow():
- to process directory entries in a directory that is writable
which makes this use subject to a symlink or hard link attack.
- to process directory entries in a directory that the calling
user does not own; the intent of the policy by-pass is to
allow this for the 'root' user, but this implementation
allows the by-pass for any user.
It looks like the get_user_name_slow() code is written safely
enough such that any symlink or hard link attack should not
cause any issues.
The new policy by-pass will allow any user to determine the
user name associated with VMs owned by another user. This is
a broader policy by-pass than was intended.
The new security policy by-pass will allow mmap_attach_shared():
- to process directory entries in a directory that is writable
which makes this use subject to a symlink or hard link attack.
- to process directory entries in a directory that the calling
user does not own; the intent of the policy by-pass is to
allow this for the 'root' user, but this implementation allows
the by-pass for any user.
The mmap_attach_shared() code protects itself from a symlink
attack by including the 'O_NOFOLLOW' flag when opening the
PerfData file and it protects itself from a hardlink attack by
checking the hard link count after opening the file. It does
not protect itself against being handed a corrupted file or
even a very large file that would cause an OOM when the VM
tries to map what is supposed to be a PerfData file.
The new policy by-pass will allow any user to access the
PerfData file associated with VMs owned by another user. This
is a broader policy by-pass than was intended.
Summary:
This implementation of the new security policy by-pass is using
an attribute of _how_ the data is accessed to override security
policy instead of an attribute of _who_ is accessing the data.
This allows the VM to be exposed to some of the attacks that
the following fix was designed to prevent:
JDK-8050807 Better performing performance data handling
Dmitry's response to the code review provides a solution that
is based on who is accessing the data and that solution or
one like it should be considered.
Again, sorry for the lengthy review.
Dan