I am interested in /proc/sys and trying to understand the access control mechanism of files in this directory.
I am not sure if accessing /proc/sys is the same as accessing on-disk file systems, like ext4. So I just started with the open system call and tried to trace critical kernel functions called among the process.
And found that it first called do_sys_open(), which called do_filp_open() internally. In do_filp_open(), some path name resolution is done at first and then it called may_open() to do some permission checks, and finally vfs_open() is called to do the specific open function according to the file system.
From reading the source code, I suppose the permission check work is indeed done by generic_permission() which located in /fs/namei.c. The whole function looks like this:
int generic_permission(struct inode *inode, int mask)
{
int ret;
/*
* Do the basic permission checks.
*/
ret = acl_permission_check(inode, mask);
if (ret != -EACCES)
return ret;
if (S_ISDIR(inode->i_mode)) {
/* DACs are overridable for directories */
if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
return 0;
if (!(mask & MAY_WRITE))
if (capable_wrt_inode_uidgid(inode,
CAP_DAC_READ_SEARCH))
return 0;
return -EACCES;
}
/*
* Read/write DACs are always overridable.
* Executable DACs are overridable when there is
* at least one exec bit set.
*/
if (!(mask & MAY_EXEC) || (inode->i_mode & S_IXUGO))
if (capable_wrt_inode_uidgid(inode, CAP_DAC_OVERRIDE))
return 0;
/*
* Searching includes executable on directories, else just read.
*/
mask &= MAY_READ | MAY_WRITE | MAY_EXEC;
if (mask == MAY_READ)
if (capable_wrt_inode_uidgid(inode, CAP_DAC_READ_SEARCH))
return 0;
return -EACCES;
}
So, it seems like UGO check comes at first, if UGO failed kernel will check if you have special Capability. But this process is not consistent with the experiment result I saw when I tried access files under /proc/sys. Take /proc/sys/kernel/usermodehelper/bset as an example:
$ ll /proc/sys/kernel/usermodehelper/bset
-rw------- 1 root root 0 Nov 6 12:15 /proc/sys/kernel/usermodehelper/bset
This file is owned by root and can’t be read from others. From the logic in generic_permission(), non-root could read this file if he has CAP_DAC_OVERRIDE. So I give /bin/cat CAP_DAC_OVERRIDE, but got an “Permission denied” and still cannot read the file.However, I could read /etc/shadow after grand CAP_DAC_OVERRIDE to cat, which is also a root file and can't be read by a normal user.
Why did this happen?What is the permission check process like when accessing files under /proc/sys? Doesn’t it go through generic_permission()? Is there some other check in kernel when accessing /proc/sys?
Related
I would like to copy a file using pure Go, emulating the behavior of cp -p.
My copy function currently looks like:
// copy creates a copy of the file located at `dst` at `src`.
func copyFile(src, dst string) error {
in, err := os.Open(src)
if err != nil {
return err
}
defer in.Close()
out, err := os.Create(dst)
if err != nil {
return err
}
_, err = io.Copy(out, in)
if err != nil {
out.Close()
return err
}
return out.Close()
}
which will create dst owned by whoever is running the process. Instead, I would like to keep the owner and permissions of src, i.e. what I get from:
// copy creates a copy of the file located at `dst` at `src`.
func copyFile(src, dst string) error {
cmd := exec.Command("cp", "-p", src, dst)
return cmd.Run()
}
but without having to call through to system commands (for portability). Everything I tried ended up calling through to something else. Is this possible to do in Go?
It's definitely possible in Go but not in a system-independent manner (because different OS kernels have different ideas about what "permissions" are, and how they are implemented).
Also consider that the identity used by the process copying the file might have insufficient permissions to set permissions on the target file (for instance, on Linux, a non-root user cannot change the owning group of a file to a group the user is not a member of, and it obviously cannot set the file owner to anyone other than theirselves — in other words, mere mortals cannot transfer ownerwhip, only share files within their own "circles" defined by group membership).
Basically, to do what you'r after, you have to Stat the source file and then os.Chmod (and os.Chown, if needed) the destination file after it is created.
Also please note that Linux-native filesystems support POSIX ACLs on files, and each file might or might not have them.
Whether you include this in what you define "permissions" are, is an open question.
I am working on a testing tool for nvme-cli(written in c and can run on linux).
For SSD validation purpose, i was actually looking for a custom command(For e.g. I/O command, write and then read the same and finally compare if both the data are same)
For read the ioctl() function is used as shown in the below code.
struct nvme_user_io io = {
.opcode = opcode,
.flags = 0,
.control = control,
.nblocks = nblocks,
.rsvd = 0,
.metadata = (__u64)(uintptr_t) metadata,
.addr = (__u64)(uintptr_t) data,
.slba = slba,
.dsmgmt = dsmgmt,
.reftag = reftag,
.appmask = appmask,
.apptag = apptag,
};
err = ioctl(fd, NVME_IOCTL_SUBMIT_IO, &io);
Can I to where exactly the control of execution goes in order to understand the read.
Also I want to have another command that looks like
err = ioctl(fd,NVME_IOCTL_WRITE_AND_COMPARE_IO, &io);
so that I can internally do a write, then read the same location and finally compare the both data to ensure that the disk contains only the data that I wanted to write.
Since I am new to this nvme/ioctl(), if there is any mistakes please correct me.
nvme_io() is a main command handler that accepts as a parameter the NVMe opcode that you want to send to your device. According to the standard, you have separate commands (opcodes) for read, write and compare. You could either send those commands separately, or add a vendor specific command to calculate what you need.
I am developing a program under Linux.
For debugging purposes I want to trace all calls from my program to a certain (preferably shared) library. (I do not want to trace calls happening inside the library.)
For syscalls there is strace. Is there any instrument to trace calls to a shared library?
The tool you are looking for is called ltrace. It allows to trace any call from the program to all (or a set of given) libraries.
For example, the following call will list any call to an external function loaded by a shared library:
$> ltrace ls /
__libc_start_main(0x4028c0, 2, 0x7fff1f4e72d8, 0x411e60 <unfinished ...>
strrchr("ls", '/') = nil
setlocale(LC_ALL, "") = "en_US.UTF-8"
bindtextdomain("coreutils", "/usr/share/locale") = "/usr/share/locale"
textdomain("coreutils") = "coreutils"
__cxa_atexit(0x40a200, 0, 0, 0x736c6974756572) = 0
isatty(1) = 0
getenv("QUOTING_STYLE") = nil
getenv("COLUMNS") = nil
ioctl(1, 21523, 0x7fff1f4e6e80) = -1
getenv("TABSIZE") = nil
getopt_long(2, 0x7fff1, "abcdfghiklmnopqrstuvw:xABCDFGHI:"..., 0x413080, -1) = -1
...
+++ exited (status 0) +++
If you want to focus on a particular library, then you should use the --library=pattern option:
-l, --library library_pattern
Display only calls to functions implemented by libraries that match
library_pattern. Multiple library patterns can be specified with several
instances of this option. Syntax of library_pattern is described in
section FILTER EXPRESSIONS.
Note that while this option selects calls that might be directed to the
selected libraries, there's no actual guarantee that the call won't be
directed elsewhere due to e.g. LD_PRELOAD or simply dependency ordering.
If you want to make sure that symbols in given library are actually called,
use -x #library_pattern instead.
So, for example, getting the list of calls to libselinux.so.1 is done like this:
$ ltrace -l libselinux.so.1 ls /
ls->freecon(0, 0xffffffff, 0x7f78c4eee628, 0) = 0
bin dev media root sbin sys usr boot etc home lib lost+found proc run tmp
+++ exited (status 0) +++
Only one call to the function freecon() is taken out this run.
In the EXT2 file.c the open file operation (.open) is being pointed to dquot_file_open which furthur points to generic_file_open which is present in fs/open.c.
The generic_file_open looks like it just has the below code
int generic_file_open(struct inode * inode, struct file * filp)
{
if (!(filp->f_flags & O_LARGEFILE) && i_size_read(inode) > MAX_NON_LFS)
return -EOVERFLOW;
return 0;
}
Where are the ACL permissions being checked when a file is about to be opened?
When is I googled and went through the code using LXR I found the below path.
do_sys_open -> do_filp_open -> path_openat -> do_last -> may_open -> inode_permission -> do_inode_permission -> generic_permission -> acl_permission_check -> check_acl -> posix_acl_permission
but I could not understand how the .open of EXT2 is linked to do_sys_open.
Any help in letting me know the path to checking the acl permissions during a file open would be greatly appreciated.
You're looking at it from the wrong end: names like do_sys_open are system call entry points, and will ultimately go through the VFS layer to find the ext2 open routine after validating permissions.
I think you got sidetracked by the ACL code; once the permission has been granted, follow nameidata_to_filp to __dentry_open:
Within __dentry_open():
f->f_op = fops_get(inode->i_fop);
/* ... */
if (!open && f->f_op)
open = f->f_op->open;
if (open) {
error = open(inode, f);
if (error)
goto cleanup_all;
}
This saves the inode->i_fop->open function pointer to the autovariable open, then proceeds to call it on the inode and f.
I am having some serious trouble getting a Python 2 based C++ engine to work in Python3. I know the whole IO stack has changed, but everything I seem to try just ends up in failure. Below is the pre-code (Python2) and post code (Python3). I am hoping someone can help me figure out what I'm doing wrong.I am also using boost::python to control the references.
The program is supposed to load a Python Object into memory via a map and then upon using the run function it then finds the file loaded in memory and runs it. I based my code off an example from the delta3d python manager, where they load in a file and run it immediately. I have not seen anything equivalent in Python3.
Python2 Code Begins here:
// what this does is first calls the Python C-API to load the file, then pass the returned
// PyObject* into handle, which takes reference and sets it as a boost::python::object.
// this takes care of all future referencing and dereferencing.
try{
bp::object file_object(bp::handle<>(PyFile_FromString(fullPath(filename), "r" )));
loaded_files_.insert(std::make_pair(std::string(fullPath(filename)), file_object));
}
catch(...)
{
getExceptionFromPy();
}
Next I load the file from the std::map and attempt to execute it:
bp::object loaded_file = getLoadedFile(filename);
try
{
PyRun_SimpleFile( PyFile_AsFile( loaded_file.ptr()), fullPath(filename) );
}
catch(...)
{
getExceptionFromPy();
}
Python3 Code Begins here: This is what I have so far based off some suggestions here... SO Question
Load:
PyObject *ioMod, *opened_file, *fd_obj;
ioMod = PyImport_ImportModule("io");
opened_file = PyObject_CallMethod(ioMod, "open", "ss", fullPath(filename), "r");
bp::handle<> h_open(opened_file);
bp::object file_obj(h_open);
loaded_files_.insert(std::make_pair(std::string(fullPath(filename)), file_obj));
Run:
bp::object loaded_file = getLoadedFile(filename);
int fd = PyObject_AsFileDescriptor(loaded_file.ptr());
PyObject* fileObj = PyFile_FromFd(fd,fullPath(filename),"r",-1,"", "\n","", 0);
FILE* f_open = _fdopen(fd,"r");
PyRun_SimpleFile( f_open, fullPath(filename) );
Lastly, the general state of the program at this point is the file gets loaded in as TextIOWrapper and in the Run: section the fd that is returned is always 3 and for some reason _fdopen can never open the FILE which means I can't do something like PyRun_SimpleFile. The error itself is a debug ASSERTION on _fdopen. Is there a better way to do all this I really appreciate any help.
If you want to see the full program of the Python2 version it's on Github
So this question was pretty hard to understand and I'm sorry, but I found out my old code wasn't quite working as I expected. Here's what I wanted the code to do. Load the python file into memory, store it into a map and then at a later date execute that code in memory. I accomplished this a bit differently than I expected, but it makes a lot of sense now.
Open the file using ifstream, see the code below
Convert the char into a boost::python::str
Execute the boost::python::str with boost::python::exec
Profit ???
Step 1)
vector<char> input;
ifstream file(fullPath(filename), ios::in);
if (!file.is_open())
{
// set our error message here
setCantFindFileError();
input.push_back('\0');
return input;
}
file >> std::noskipws;
copy(istream_iterator<char>(file), istream_iterator<char>(), back_inserter(input));
input.push_back('\n');
input.push_back('\0');
Step 2)
bp::str file_str(string(&input[0]));
loaded_files_.insert(std::make_pair(std::string(fullPath(filename)), file_str));
Step 3)
bp::str loaded_file = getLoadedFile(filename);
// Retrieve the main module
bp::object main = bp::import("__main__");
// Retrieve the main module's namespace
bp::object global(main.attr("__dict__"));
bp::exec(loaded_file, global, global);
Full Code is located on github: