2013-08-09 02:13:33 +02:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2013 The Android Open Source Project
|
|
|
|
*
|
|
|
|
* Licensed 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
|
|
|
|
#include <errno.h>
|
Implement some of the missing LFS64 support.
This gives us:
* <dirent.h>
struct dirent64
readdir64, readdir64_r, alphasort64, scandir64
* <fcntl.h>
creat64, openat64, open64.
* <sys/stat.h>
struct stat64
fstat64, fstatat64, lstat64, stat64.
* <sys/statvfs.h>
struct statvfs64
statvfs64, fstatvfs64.
* <sys/vfs.h>
struct statfs64
statfs64, fstatfs64.
This also removes some of the incorrect #define hacks we've had in the
past (for stat64, for example, which we promised to clean up way back
in bug 8472078).
Bug: 11865851
Bug: 8472078
Change-Id: Ia46443521918519f2dfa64d4621027dfd13ac566
2014-01-18 03:42:49 +01:00
|
|
|
#include <fcntl.h>
|
2013-08-09 02:13:33 +02:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
2013-10-22 19:54:11 +02:00
|
|
|
#include "TemporaryFile.h"
|
|
|
|
|
2013-08-09 02:13:33 +02:00
|
|
|
TEST(sys_stat, futimens) {
|
|
|
|
FILE* fp = tmpfile();
|
|
|
|
ASSERT_TRUE(fp != NULL);
|
|
|
|
|
|
|
|
int fd = fileno(fp);
|
|
|
|
ASSERT_NE(fd, -1);
|
|
|
|
|
|
|
|
timespec times[2];
|
|
|
|
times[0].tv_sec = 123;
|
|
|
|
times[0].tv_nsec = 0;
|
|
|
|
times[1].tv_sec = 456;
|
|
|
|
times[1].tv_nsec = 0;
|
|
|
|
ASSERT_EQ(0, futimens(fd, times)) << strerror(errno);
|
|
|
|
|
|
|
|
struct stat sb;
|
|
|
|
ASSERT_EQ(0, fstat(fd, &sb));
|
|
|
|
ASSERT_EQ(times[0].tv_sec, static_cast<long>(sb.st_atime));
|
|
|
|
ASSERT_EQ(times[1].tv_sec, static_cast<long>(sb.st_mtime));
|
|
|
|
|
|
|
|
fclose(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, futimens_EBADF) {
|
|
|
|
timespec times[2];
|
|
|
|
times[0].tv_sec = 123;
|
|
|
|
times[0].tv_nsec = 0;
|
|
|
|
times[1].tv_sec = 456;
|
|
|
|
times[1].tv_nsec = 0;
|
|
|
|
ASSERT_EQ(-1, futimens(-1, times));
|
|
|
|
ASSERT_EQ(EBADF, errno);
|
|
|
|
}
|
2013-10-22 19:54:11 +02:00
|
|
|
|
2014-10-24 04:10:23 +02:00
|
|
|
TEST(sys_stat, mkfifo_failure) {
|
|
|
|
errno = 0;
|
|
|
|
ASSERT_EQ(-1, mkfifo("/", 0666));
|
|
|
|
ASSERT_EQ(EEXIST, errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, mkfifoat_failure) {
|
|
|
|
errno = 0;
|
|
|
|
ASSERT_EQ(-1, mkfifoat(-2, "x", 0666));
|
|
|
|
ASSERT_EQ(EBADF, errno);
|
|
|
|
}
|
|
|
|
|
2013-10-22 19:54:11 +02:00
|
|
|
TEST(sys_stat, mkfifo) {
|
2014-09-25 01:01:18 +02:00
|
|
|
if (getuid() == 0) {
|
|
|
|
// Racy but probably sufficient way to get a suitable filename.
|
|
|
|
std::string path;
|
|
|
|
{
|
|
|
|
TemporaryFile tf;
|
|
|
|
path = tf.filename;
|
|
|
|
}
|
2013-10-22 19:54:11 +02:00
|
|
|
|
2014-09-25 01:01:18 +02:00
|
|
|
ASSERT_EQ(0, mkfifo(path.c_str(), 0666));
|
|
|
|
struct stat sb;
|
|
|
|
ASSERT_EQ(0, stat(path.c_str(), &sb));
|
|
|
|
ASSERT_TRUE(S_ISFIFO(sb.st_mode));
|
|
|
|
unlink(path.c_str());
|
|
|
|
} else {
|
2014-10-24 04:10:23 +02:00
|
|
|
// SELinux policy forbids us from creating FIFOs. http://b/17646702.
|
2014-09-25 01:01:18 +02:00
|
|
|
GTEST_LOG_(INFO) << "This test only performs a test when run as root.";
|
|
|
|
}
|
2013-10-22 19:54:11 +02:00
|
|
|
}
|
Implement some of the missing LFS64 support.
This gives us:
* <dirent.h>
struct dirent64
readdir64, readdir64_r, alphasort64, scandir64
* <fcntl.h>
creat64, openat64, open64.
* <sys/stat.h>
struct stat64
fstat64, fstatat64, lstat64, stat64.
* <sys/statvfs.h>
struct statvfs64
statvfs64, fstatvfs64.
* <sys/vfs.h>
struct statfs64
statfs64, fstatfs64.
This also removes some of the incorrect #define hacks we've had in the
past (for stat64, for example, which we promised to clean up way back
in bug 8472078).
Bug: 11865851
Bug: 8472078
Change-Id: Ia46443521918519f2dfa64d4621027dfd13ac566
2014-01-18 03:42:49 +01:00
|
|
|
|
|
|
|
TEST(sys_stat, stat64_lstat64_fstat64) {
|
|
|
|
struct stat64 sb;
|
|
|
|
ASSERT_EQ(0, stat64("/proc/version", &sb));
|
|
|
|
ASSERT_EQ(0, lstat64("/proc/version", &sb));
|
|
|
|
int fd = open("/proc/version", O_RDONLY);
|
|
|
|
ASSERT_EQ(0, fstat64(fd, &sb));
|
|
|
|
close(fd);
|
|
|
|
}
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_EFAULT_file) {
|
|
|
|
ASSERT_EQ(-1, fchmodat(AT_FDCWD, (char *) 0x1, 0751, 0));
|
|
|
|
ASSERT_EQ(EFAULT, errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_EFAULT_file) {
|
|
|
|
ASSERT_EQ(-1, fchmodat(AT_FDCWD, (char *) 0x1, 0751, AT_SYMLINK_NOFOLLOW));
|
|
|
|
#if defined(__BIONIC__)
|
|
|
|
ASSERT_EQ(EFAULT, errno);
|
|
|
|
#else
|
|
|
|
// glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
|
|
|
|
// returns ENOTSUP
|
|
|
|
ASSERT_EQ(ENOTSUP, errno);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_bad_flags) {
|
|
|
|
ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, ~AT_SYMLINK_NOFOLLOW));
|
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_bad_flags_ALL) {
|
|
|
|
ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, ~0));
|
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_nonexistant_file) {
|
|
|
|
ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, 0));
|
|
|
|
ASSERT_EQ(ENOENT, errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_nonexistant_file) {
|
|
|
|
ASSERT_EQ(-1, fchmodat(AT_FDCWD, "/blah", 0751, AT_SYMLINK_NOFOLLOW));
|
|
|
|
#if defined(__BIONIC__)
|
|
|
|
ASSERT_EQ(ENOENT, errno);
|
|
|
|
#else
|
|
|
|
// glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
|
|
|
|
// returns ENOTSUP
|
|
|
|
ASSERT_EQ(ENOTSUP, errno);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-03-09 21:55:18 +01:00
|
|
|
static void AssertFileModeEquals(mode_t expected_mode, const char* filename) {
|
|
|
|
struct stat sb;
|
|
|
|
ASSERT_EQ(0, stat(filename, &sb));
|
|
|
|
mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
|
|
|
|
ASSERT_EQ(expected_mode & mask, static_cast<mode_t>(sb.st_mode) & mask);
|
|
|
|
}
|
|
|
|
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
TEST(sys_stat, fchmodat_file) {
|
|
|
|
TemporaryFile tf;
|
|
|
|
|
|
|
|
ASSERT_EQ(0, fchmodat(AT_FDCWD, tf.filename, 0751, 0));
|
2015-03-09 21:55:18 +01:00
|
|
|
AssertFileModeEquals(0751, tf.filename);
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_file) {
|
|
|
|
TemporaryFile tf;
|
|
|
|
errno = 0;
|
|
|
|
int result = fchmodat(AT_FDCWD, tf.filename, 0751, AT_SYMLINK_NOFOLLOW);
|
|
|
|
|
|
|
|
#if defined(__BIONIC__)
|
|
|
|
ASSERT_EQ(0, result);
|
|
|
|
ASSERT_EQ(0, errno);
|
2015-03-09 21:55:18 +01:00
|
|
|
AssertFileModeEquals(0751, tf.filename);
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
#else
|
|
|
|
// glibc 2.19 does not implement AT_SYMLINK_NOFOLLOW and always
|
|
|
|
// returns ENOTSUP
|
|
|
|
ASSERT_EQ(-1, result);
|
|
|
|
ASSERT_EQ(ENOTSUP, errno);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_symlink) {
|
|
|
|
TemporaryFile tf;
|
|
|
|
char linkname[255];
|
|
|
|
|
|
|
|
snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
|
|
|
|
|
|
|
|
ASSERT_EQ(0, symlink(tf.filename, linkname));
|
|
|
|
ASSERT_EQ(0, fchmodat(AT_FDCWD, linkname, 0751, 0));
|
2015-03-09 21:55:18 +01:00
|
|
|
AssertFileModeEquals(0751, tf.filename);
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
unlink(linkname);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_dangling_symlink) {
|
|
|
|
TemporaryFile tf;
|
|
|
|
char linkname[255];
|
|
|
|
char target[255];
|
|
|
|
|
|
|
|
snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
|
|
|
|
snprintf(target, sizeof(target), "%s.doesnotexist", tf.filename);
|
|
|
|
|
|
|
|
ASSERT_EQ(0, symlink(target, linkname));
|
|
|
|
ASSERT_EQ(-1, fchmodat(AT_FDCWD, linkname, 0751, 0));
|
|
|
|
ASSERT_EQ(ENOENT, errno);
|
|
|
|
unlink(linkname);
|
|
|
|
}
|
|
|
|
|
2015-03-09 21:55:18 +01:00
|
|
|
static void AssertSymlinkModeEquals(mode_t expected_mode, const char* linkname) {
|
|
|
|
struct stat sb;
|
|
|
|
ASSERT_EQ(0, fstatat(AT_FDCWD, linkname, &sb, AT_SYMLINK_NOFOLLOW));
|
|
|
|
mode_t mask = S_IRWXU | S_IRWXG | S_IRWXO;
|
|
|
|
ASSERT_EQ(expected_mode & mask, static_cast<mode_t>(sb.st_mode) & mask);
|
|
|
|
}
|
|
|
|
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_with_symlink) {
|
|
|
|
TemporaryFile tf;
|
2015-03-09 21:55:18 +01:00
|
|
|
struct stat tf_sb;
|
|
|
|
ASSERT_EQ(0, stat(tf.filename, &tf_sb));
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
|
2015-03-09 21:55:18 +01:00
|
|
|
char linkname[255];
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
|
|
|
|
|
|
|
|
ASSERT_EQ(0, symlink(tf.filename, linkname));
|
2015-03-09 21:55:18 +01:00
|
|
|
int result = fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW);
|
|
|
|
// It depends on the kernel whether chmod operation on symlink is allowed.
|
|
|
|
if (result == 0) {
|
|
|
|
AssertSymlinkModeEquals(0751, linkname);
|
|
|
|
} else {
|
|
|
|
ASSERT_EQ(-1, result);
|
|
|
|
ASSERT_EQ(ENOTSUP, errno);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Target file mode shouldn't be modified.
|
|
|
|
AssertFileModeEquals(tf_sb.st_mode, tf.filename);
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
unlink(linkname);
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, fchmodat_AT_SYMLINK_NOFOLLOW_with_dangling_symlink) {
|
|
|
|
TemporaryFile tf;
|
2015-03-09 21:55:18 +01:00
|
|
|
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
char linkname[255];
|
|
|
|
char target[255];
|
|
|
|
snprintf(linkname, sizeof(linkname), "%s.link", tf.filename);
|
|
|
|
snprintf(target, sizeof(target), "%s.doesnotexist", tf.filename);
|
|
|
|
|
|
|
|
ASSERT_EQ(0, symlink(target, linkname));
|
2015-03-09 21:55:18 +01:00
|
|
|
int result = fchmodat(AT_FDCWD, linkname, 0751, AT_SYMLINK_NOFOLLOW);
|
|
|
|
// It depends on the kernel whether chmod operation on symlink is allowed.
|
|
|
|
if (result == 0) {
|
|
|
|
AssertSymlinkModeEquals(0751, linkname);
|
|
|
|
} else {
|
|
|
|
ASSERT_EQ(-1, result);
|
|
|
|
ASSERT_EQ(ENOTSUP, errno);
|
|
|
|
}
|
|
|
|
|
Add fchmodat(AT_SYMLINK_NOFOLLOW) and fchmod O_PATH support
Many libc functions have an option to not follow symbolic
links. This is useful to avoid security sensitive code
from inadvertantly following attacker supplied symlinks
and taking inappropriate action on files it shouldn't.
For example, open() has O_NOFOLLOW, chown() has
lchown(), stat() has lstat(), etc.
There is no such equivalent function for chmod(), such as lchmod().
To address this, POSIX introduced fchmodat(AT_SYMLINK_NOFOLLOW),
which is intended to provide a way to perform a chmod operation
which doesn't follow symlinks.
Currently, the Linux kernel doesn't implement AT_SYMLINK_NOFOLLOW.
In GLIBC, attempting to use the AT_SYMLINK_NOFOLLOW flag causes
fchmodat to return ENOTSUP. Details are in "man fchmodat".
Bionic currently differs from GLIBC in that AT_SYMLINK_NOFOLLOW
is silently ignored and treated as if the flag wasn't present.
This patch provides a userspace implementation of
AT_SYMLINK_NOFOLLOW for bionic. Using open(O_PATH | O_NOFOLLOW),
we can provide a way to atomically change the permissions on
files without worrying about race conditions.
As part of this change, we add support for fchmod on O_PATH
file descriptors, because it's relatively straight forward
and could be useful in the future.
The basic idea behind this implementation comes from
https://sourceware.org/bugzilla/show_bug.cgi?id=14578 , specifically
comment #10.
Change-Id: I1eba0cdb2c509d9193ceecf28f13118188a3cfa7
2015-02-01 04:57:46 +01:00
|
|
|
unlink(linkname);
|
|
|
|
}
|
Fix "faccessat ignores flags"
The kernel system call faccessat() does not have any flags arguments,
so passing flags to the kernel is currently ignored.
Fix the kernel system call so that no flags argument is passed in.
Ensure that we don't support AT_SYMLINK_NOFOLLOW. This non-POSIX
(http://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html)
flag is a glibc extension, and has non-intuitive, error prone behavior.
For example, consider the following code:
symlink("foo.is.dangling", "foo");
if (faccessat(AT_FDCWD, "foo", R_OK, AT_SYMLINK_NOFOLLOW) == 0) {
int fd = openat(AT_FDCWD, "foo", O_RDONLY | O_NOFOLLOW);
}
The faccessat() call in glibc will return true, but an attempt to
open the dangling symlink will end up failing. GLIBC documents this
as returning the access mode of the symlink itself, which will
always return true for any symlink on Linux.
Some further discussions of this are at:
* http://lists.landley.net/pipermail/toybox-landley.net/2014-September/003617.html
* http://permalink.gmane.org/gmane.linux.lib.musl.general/6952
AT_SYMLINK_NOFOLLOW seems broken by design. I suspect this is why this
function was never added to POSIX. (note that "access" is pretty much
broken by design too, since it introduces a race condition between
check and action). We shouldn't support this until it's clearly
documented by POSIX or we can have it produce intuitive results.
Don't support AT_EACCESS for now. Implementing it is complicated, and
pretty much useless on Android, since we don't have setuid binaries.
See http://git.musl-libc.org/cgit/musl/commit/?id=0a05eace163cee9b08571d2ff9d90f5e82d9c228
for how an implementation might look.
Bug: 18867827
Change-Id: I25b86c5020f3152ffa3ac3047f6c4152908d0e04
2015-02-24 22:40:43 +01:00
|
|
|
|
|
|
|
TEST(sys_stat, faccessat_EINVAL) {
|
|
|
|
ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", F_OK, ~AT_SYMLINK_NOFOLLOW));
|
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
|
#if defined(__BIONIC__)
|
|
|
|
ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), 0));
|
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
|
#else
|
|
|
|
ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), AT_SYMLINK_NOFOLLOW));
|
|
|
|
ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", ~(R_OK | W_OK | X_OK), 0));
|
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, faccessat_AT_SYMLINK_NOFOLLOW_EINVAL) {
|
|
|
|
#if defined(__BIONIC__)
|
|
|
|
// Android doesn't support AT_SYMLINK_NOFOLLOW
|
|
|
|
ASSERT_EQ(-1, faccessat(AT_FDCWD, "/dev/null", F_OK, AT_SYMLINK_NOFOLLOW));
|
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
|
#else
|
|
|
|
ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", F_OK, AT_SYMLINK_NOFOLLOW));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, faccessat_dev_null) {
|
|
|
|
ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", F_OK, 0));
|
|
|
|
ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", R_OK, 0));
|
|
|
|
ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", W_OK, 0));
|
|
|
|
ASSERT_EQ(0, faccessat(AT_FDCWD, "/dev/null", R_OK|W_OK, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
TEST(sys_stat, faccessat_nonexistant) {
|
|
|
|
ASSERT_EQ(-1, faccessat(AT_FDCWD, "/blah", F_OK, AT_SYMLINK_NOFOLLOW));
|
|
|
|
#if defined(__BIONIC__)
|
|
|
|
// Android doesn't support AT_SYMLINK_NOFOLLOW
|
|
|
|
ASSERT_EQ(EINVAL, errno);
|
|
|
|
#else
|
|
|
|
ASSERT_EQ(ENOENT, errno);
|
|
|
|
#endif
|
|
|
|
}
|