Redirect file access - c++

Is there a way to monitor and redirect file access of a thread or process?
For example a thread wants to read /etc/mysql/my.cnf and I want to change the access to ~/my.cnf or if I run touch /etc/test.config I want the file to be redirected to ~/somefolder/etc/test.config.
I am looking for a libary preferably for C, C++ which works for Linux/Unix.
Thanks in advance

You could write a shared object that gets pre-loaded when your program starts running. In the .so you'd redefine the libc function open(). When the caller (the program that's getting fooled) passes as argument the string /etc/mysql/my.cnf, you'd instead open ~/my.cnf and return the opened file descriptor. The caller wouldn't know the difference.
Your shared object would of course need to call the "real" open() to to open the file handle; you can get a pointer to the original libc function using dlsym().
This seems overly complicated, but in fact it isn't, it works like a charm. I've used it on several occasions where I had to fool a program that I didn't have the sources for; and it simply works like a clockwork.
If you want to see a proof of concept, check out my blog where I wrote it up. Happy coding!

In linux, you can use bind mounts to map directory or file to another path, and per-process mount namespaces to do it for specific application.
See this question.
Example 1: use proot
$ proot -b ~/alternate_hosts:/etc/hosts
# echo '1.2.3.4 google.com' > /etc/hosts
# resolveip google.com
# IP address of google.com is 1.2.3.4
Example 2: use unshare(1)
$ unshare -m
# touch foo bar
# mount -o bind foo bar
# echo hello > foo
# cat bar
hello
Example 3: use unshare(2)
See this post: http://glandium.org/blog/?p=217.

Related

Mounting ecryptfs using C++ mount function

I am trying to mount ecryptfs from within a C++ program. I can definitely mount it without it asking questions by issuing this command at the prompt:
sudo mount -t ecryptfs -o "rw,key=passphrase:passphrase_passwd=geoff,ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=n,ecryptfs_enable_filename_crypto=n,no_sig_cache" ~/source/ ~/target/
Note that in reality, I am passing a full canonical path in case that matters.
But from within the program I get failure with errno=EINVAL after trying by using the mount() function with the same arguments:
mount("~/source/", "~/target/", "ecryptfs", MS_NODEV, "rw,key=passphrase:passphrase_passwd=geoff,ecryptfs_cipher=aes,ecryptfs_key_bytes=32,ecryptfs_passthrough=n,ecryptfs_enable_filename_crypto=n,no_sig_cache")
The program does launch with root privileges and I have checked that I have CAP_SYS_ADMIN.
The mount() function returns -1 and sets errno to EINVAL.
Have I got the arguments correct? Is this maybe a privileges issue?
EDIT: I got it to work by executing mount externally via system(), but would still like to use the function because of reasons.
I believe this is because mount -t ecryptfs is actually calling the helper executable mount.ecryptfs, and it's processing some of the options (in particular, key=) itself. What's actually passed to the kernel is different (you can see this by looking at /proc/mounts afterward).
If you look closely at https://manpages.ubuntu.com/manpages/kinetic/en/man7/ecryptfs.7.html, key= and ecryptfs_enable_filename_crypto= are listed under "MOUNT HELPER OPTIONS" - the actual kernel module's options are ecryptfs_sig=(fekek_sig) and ecryptfs_fnek_sig=(fnek_sig).
So, if you want to bypass the helper and do the mount directly, you'd need to load the tokens into the kernel's keyring with https://man7.org/linux/man-pages/man2/keyctl.2.html and replace key= with the resulting token signatures, like mount.ecryptfs did.
It does appear that there is a libecrytpfs with functions in ecryptfs.h like ecryptfs_add_passphrase_key_to_keyring which you can (presumably, not tested) use to do this in a way matching the mount.ecryptfs

How to evaluate an expression to be used for a gdb monitor command?

Inside a scripted gdb session I want to use monitor <cmd> where cmd should contain the address of a symbol. For example:
monitor foobar &myVariable
should become to:
monitor foobar 0x00004711
Because the remote side cannot evaluate the expression. Whatever I tried, the string "&myVariable" gets sent instead of the address.
I played around with convenience variables and stuff, but this is the only workaround I found:
# write the command into a file and execute this file
# didn't find a direct way to include the address into the monitor command
set logging overwrite on
set logging on command.tmp
printf "monitor foobar 0x%08x\n", &myVariable
set logging off
source command.tmp
Any ideas to solve this in a more elegant way?
The simplest way to do this is to use the gdb eval command, which was introduced for exactly this purpose. It works a bit like printf:
(gdb) eval "monitor foobar %p", &myVariable
(I didn't actually try this, so caution.)
If your gdb doesn't have eval, then it is on the old side. I would suggest upgrading.
If you can't upgrade, then you can also script this using Python.
If your gdb doesn't have Python, then it is either very old (upgrade!) or compiled without Python support (recompile!).
If you can't manage to get either of these features, then I am afraid the "write out a script and source it" approach is all that is left.

c++ . when can a system call to move fail?

Read some where that one should use WEXITSTATUS to check for return status of system calls .
However I dont think that a call like system("mv /a/b/c /a/b/d") needs a check if it fails .
What are the conditions when this call may fail ?
Some possibilities:
/a/b/c does not exist
/a/b does not exist
You have insufficient access to /a/b/c
You have insufficient access to /a/b/d
/a/b/d already exists
/a/b/c isn't moveable
There is no shell
mv does not exist
You have insufficient access to mv
You have no file system mounted
You have no storage available at all
And many, many more...
system("mv /a/b/c /a/b/d") : very probably both /a/b/c and /a/b/d lie on the same mounted file system. I am guessing you have a Posix system, perhaps Linux.
Then it is much more simpler to use the rename(2) syscall (which is called by /bin/mv when relevant!) and directly code:
if (rename("/a/b/c", "/a/b/d")) {
perror("rename failed");
exit(EXIT_FAILURE);
}
and you would have thru errno(3) i.e. thru perror(3) the error code explaining why the rename failed. All the error conditions in rename(2) are relevant failure cases of mv, see also mv(1)!
Read Advanced Linux Programming.
If (for some strange reason, e.g. bind mounts, symlinks, ...) /a/b/c and /a/b/d don't lie in the same filesystem, you would get errno == EXDEV (and you might handle that case by a copy followed by an unlink of the source).
In general, using system("mv ...") should be avoided. See this answer and this one explaining why (and also this and this). And your user may have a different mv in his PATH (or some alias), so mv should at least be /bin/mv ... If . (or even worse /tmp !) is early in his PATH and has a symlink mv ➙ /bin/rm your user won't be happy!
BTW, you generally don't call system with a compile-time constant string starting with mv. That string is generally built. And if you don't quote correctly things (imagine one argument being ; rm -rf $HOME) havoc can happen.
Also, system(3) can fail e.g. because fork(2) failed (too many user processes, reaching the RLIMIT_NPROC limit of setrlimit(2)...).

zmq ventilator/worker/sink paradigm not working w/ subprocess

I am trying to replicate the ventilator/workers/sink paradigm described in the ZMQ guide. I have the same Python Ventilator, the same C++ worker as, and the same Python Sink as was described in the ZMQ examples. I want to launch the ventilator, workers, and sink from one main python script, so I created "class" wrappers around the ventilator & sink, and both of those classes subclass the Python module "multiprocessing.Process." Since the C++ is a binary, I launch it with Python's subprocess.Popen call.
The order of starting all of this up is as follows:
h = subprocess.Popen('test') # test is the name of the binary
time.sleep(1)
s = sinkObj.start()
time.sleep(1)
v = ventObj.start()
What I am finding is that no data is getting through the system when I start up the components like this. However, if I start the C++ binary in its own shell, and only start the sinkObj and ventObj from the main python script, it works fine.
I apologize in advance if this is more of a Python question than a ZMQ question, but I haven't run into issues like this w/ Python's subprocess. I have also tried using os.system() instead of the subprocess... but same issue. I put all the code on this website: https://github.com/kkarrancsu/zmqtest if anybody is curious to test it out. There is a readme on that git which tells you what the files are.
Any ideas on why this could be happening?
------------------------- UPDATE --------------------
I found that if I create a shell script which simply launches the C binary, and call that shell script w/ os.system('run_the_shell_script') it works! So this means that there is something wrong with the way that I am using subprocess.Popen(...), but can't seem to pinpoint what the issue is. I tried w/ the shell=True flag, but it still hangs with that...
It's the name of the worker binary file that causes the problem.
There two solutions:
Chang the name of the binary file test to test_new and do the same in your All.py file, and then it will work as you desire.
Substitute subprocess.Popen('./test', shell=True) for subprocess.Popen('test', shell=True).
test is Linux command. If you type the following in your shell
$ echo $PATH
You may see that . is at the last position. It means that until shell couldn't find the binary file to be executed in the directories that $PATH indicates, it will try to search for it in the current directory .
When you execute subprocess.Popen('test', shell=True), it could find it before trying the . directory and so it won't execute the workers.
As I see, the ventilator and sink bind() to ports 6557 and 6558, and the C++ app connect() to these ports. In this case, if you start the cpp app first, it will try to connect() to the endpoints, but as nothing is bound there, it will drop silently.
In ZeroMQ the basic principle is "First Bind, then Connect". So you should not connect() before you bind() something on the socket. Imagine bind() is the 'Server', and connect() is the client. You cannot connect client to non-existing server. Also, in ZeroMQ every socket can be 'Server', but you SHOULD HAVE only 1 bind()-ing socket per URL. And you can have multiple 'connect()'s.

vimrc to detect remote connection

At the moment I have to hard code the names of servers on my vimrc in order to either make it different on the remote machine. This is done by conditional statement using hostname() function in vim. I want to make the conditional to be based on the status of remote connection and not on the hostname. So...
The first possible solution I found was using the following bash command in system():
cat /proc/$PPID/status | head -1 | cut -f2
This does not work because I use GNU screen and this will not detect my connection status properly.
The second possible solution I am exploring right now is using who am i This reliably shows whether or not remote connection has been made from which client, but I have trouble getting it working with system()
if substitute(system('who am i'), "theclient", ????, "") == ""
...
How could I get ???? to extract my client name somehow??
Even if the second solution works, allowing me to use .vimrc for many different remote machines, it is still tied to one client. I want the conditional to work in all remote session, regardless of the client name. So I am wondering, is this possible?
The following line allows me to create a variable that detects the remote connection status:
let g:remoteSession = ($STY == "")
Now you can surround the lines that you want to be ignored in the remote connection via:
if g:remoteSession
...
endif
On a side note, I do not know how expensive it is look up the environment variable compared to the global variable, but I am guessing the difference is negligible. The system call in an environment like cygwin where fork() is inefficient, it is worth doing the optimization.
Instead of adding conditional logic to a shared ~/.vimrc, you could alternatively source system-local settings. I use the following:
" Source system-specific .vimrc first.
if filereadable(expand('~/local/.vimrc'))
source ~/local/.vimrc
endif
" Stop sourcing if inclusion guard exists.
if exists('g:loaded_vimrc')
finish
endif
" Common settings of .vimrc here...
I find this more scalable than trying to maintain an ever-changing list of hostnames in a central location.