I use snprintf to write formatted data to disk, but I have one problem, how do I save it to the user's home directory?
snprintf(filename, sizeof(filename), "%s_%s.sho", client, id);
The user controls his environment - so the HOME environment variable may not be correct or it may not even be set.
Use getuid() and getpwuid() to get the user's home directory as specified by your system:
#include <unistd.h>
#include <pwd.h>
struct passwd *pwd = getpwuid( getuid() );
/* need to duplicate the string - we don't know
what pw_dir points to */
const char *homeDir = strdup( pwd->pw_dir );
Error checking is left as an exercise...
On Linux and POSIX systems the home directory is often from the HOME environment variable. So you might code
snprintf(filename, sizeof(filename), "%s/%s_%s.sho",
getenv("HOME"), client, id);
Pedantically the getenv(3) could fail (or be wrong). But that rarely happens. See environ(7).
(You might check, and or use getpwuid(3) with getuid(2)...)
With setuid executables things could become interestingly complex. You would need to define more precisely what the home is, and code appropriately (this is left as an exercise).
Use getenv(3) to query the value of the HOME environment variable. For example, this prints my home directory:
#include <stdlib.h>
#include <stdio.h>
int main(void) {
printf("%s\n", getenv("HOME"));
return 0;
}
You can set your variable filename to the return value, and then write whatever data you want there.
This should work on any Unix-like system, including Linux, macOS, BSD, and probably many more.
Related
I have a sandboxed Cocoa app that, during an export process, needs to run a third party command-line tool. This tool appears to be hardcoded to use /tmp for its temporary files; sandboxing doesn't permit access to this folder, so the export fails.
How can I get this tool to run? I don't have access to its source code, so I can't modify it to use NSTemporaryDirectory(), and it doesn't appear to respect the TMP or TEMPDIR environment variables. For reasons I don't understand, giving myself a com.apple.security.temporary-exception.files.absolute-path.read-write entitlement doesn't seem to work, either.
Is there some way to re-map folders within my sandbox? Is there some obscure trick I can use? Should I try to patch the tool's binary somehow? I'm at my wit's end here.
I was able to get user3159253's DYLD_INSERT_LIBRARIES approach to work. I'm hoping they will write an answer describing how that works, so I'll leave the details of that out and explain the parts that ended up being specific to this case.
Thanks to LLDB, elbow grease, and not a little help from Hopper, I was able to determine that the third-party tool used mkstemp() to generate its temporary file names, and some calls (not all) used a fixed template starting with /tmp. I then wrote a libtmphack.dylib that intercepted calls to mkstemp() and modified the parameters before calling the standard library version.
Since mkstemp() takes a pointer to a preallocated buffer, I didn't feel like I could rewrite a path starting with a short string like "/tmp" to the very long string needed to get to the Caches folder inside the sandbox. Instead, I opted to create a symlink to it called "$tmp" in the current working directory. This could break if the tool chdir()'d at an inopportune time, but fortunately it doesn't seem to do that.
Here's my code:
//
// libtmphack.c
// Typesetter
//
// Created by Brent Royal-Gordon on 8/27/14.
// Copyright (c) 2014 Groundbreaking Software. This file is MIT licensed.
//
#include "libtmphack.h"
#include <dlfcn.h>
#include <stdlib.h>
#include <unistd.h>
//#include <errno.h>
#include <string.h>
static int gbs_has_prefix(char * needle, char * haystack) {
return strncmp(needle, haystack, strlen(needle)) == 0;
}
int mkstemp(char *template) {
static int (*original_mkstemp)(char * template) = NULL;
if(!original_mkstemp) {
original_mkstemp = dlsym(RTLD_NEXT, "mkstemp");
}
if(gbs_has_prefix("/tmp", template)) {
printf("libtmphack: rewrote mkstemp(\"%s\") ", template);
template[0] = '$';
printf("to mkstemp(\"%s\")\n", template);
// If this isn't successful, we'll presume it's because it's already been made
symlink(getenv("TEMP"), "$tmp");
int ret = original_mkstemp(template);
// Can't do this, the caller needs to be able to open the file
// int retErrno = errno;
// unlink("$tmp");
// errno = retErrno;
return ret;
}
else {
printf("libtmphack: OK with mkstemp(\"%s\")\n", template);
return original_mkstemp(template);
}
}
Very quick and dirty, but it works like a charm.
Since #BrentRoyal-Gordon has already published a working solution I'm simply duplicating my comment which inspired him to produce the solution:
In order to fix a program behavior, I would intercept and override some system calls with the help of DYLD_INSERT_LIBRARIES and a custom shared library with a custom implementation of the given system calls.
The exact list of the syscalls which need to be overridden depends on nature of the application and can be studied with a number of tools built upon MacOS DTrace kernel facility. E.g. dtruss or Hopper. #BrentRoyal-Gordon has investigated that the app can be fixed solely with an /appropriate/ implementation of mkstemp.
That's it. I'm still not sure that I've deserved the bounty :)
Another solution would be to use chroot within the child process (or posix_spawn options) to change its root directory to a directory that is within your sandbox. Its “/tmp” will then be a “tmp” directory within that directory.
I wanted a function or a system call similar to mountpoint command Linux.
(this command will check if given directory is a mount point or not)
I'm not aware of an actual syscall that would do that, but what you can do is compare the device numbers of the directory that you want to check and its parent. That can be done with stat. Example code (error checking omitted) :
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
int main(void) {
struct stat mp, mp_parent;
stat("/boot", &mp);
stat("/boot/..", &mp_parent);
if (mp.st_dev != mp_parent.st_dev)
printf("/boot is a mount point.\n");
else
printf("/boot is not a mount point.\n");
return 0;
}
The need for functions like this is usually (though not always) a red flag that you're not embracing the abstractions of the system...and you should reconsider whatever-it-is-you're-doing. If there is some choice you're making in your software based on whether something is a mountpoint...consider making that alternative behavior a parameter which can be controlled via scripting. People can parameterize your program to adopt that behavior via using the native mountpoint command, or whatever else.
With that disclaimer aside, here's an implementation of mountpoint.c:
https://fossies.org/linux/sysvinit/src/mountpoint.c
...and a reference on "testing the type of a file"
http://www.aquaphoenix.com/ref/gnu_c_library/libc_160.html
Write your own app to parse /proc/mounts. Then compare your path with paths from /proc/mounts. If they are equal then path is a mount point.
here is my problem: In C, I create the copy of a file (with some changes) This is done trivially via fopen(), getchar and putchar.
Copying the file is fine and the outputfile itself is what I want it to be.
My problem is: I assume that I will use this program often as sudo and then the resulting file has both another owner (root) as well as different permissions (execute rights are gone).
My question is: How can I copy the owner and permissions of the original file and then write them into the new one?
Use the fstat(2) system call to obtain the details about the owner and the permissions, and the fchmod(2) and fchown(2) system calls to set them. See an example in the setfile function of the *BSD cp(1) source code.
since you use fopen():
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
//fp is your source filepointer, fdest is your dest filepointer
//fn is the new filename you're copying to
struct stat fst;
//let's say this wont fail since you already worked OK on that fp
fstat(fileno(fp),&fst);
//update to the same uid/gid
fchown(fileno(fdest),fst.st_uid,fst.st_gid);
//update the permissions
fchmod(fileno(fdest),fst.st_mode);
as a quick note, you may want to fread() and fwrite() instead of f*char()'ing
Under linux use the libc fchmod and fchown
Manpages can be found here:
http://linux.die.net/man/2/fchmod
http://linux.die.net/man/2/fchown
I have a question regarding the getlogin() function (). I tried to get the login name of my account from the c program using this function. But the function returns a NULL. Using perror shows that the error is "No such file or directory".
I don't get what is the problem. Is there a way to get user login name in a program.
Here is a sample code:
#include <stdio.h>
#include <unistd.h>
int main()
{
char *name;
name = getlogin();
perror("getlogin() error");
//printf("This is the login info: %s\n", name);
return 0;
}
And this is the output: getlogin() error: No such file or directory
Please let me know how to get this right.
Thanks.
getlogin is an unsafe and deprecated way of determining the logged-in user. It's probably trying to open a record of logged-in users, perhaps utmp or something. The correct way to determine the user you're running as (which might not be the same as the logged-in user, but is almost always better to use anyway) is getpwuid(getuid()).
Here is a good link I found explaining that it may not work: getlogin
Here is a quote from it:
Unfortunately, it is often rather easy to fool getlogin(). Sometimes it does not work at all, because some program messed up the utmp file
It works fine for me if I comment perror call.
From man:
getlogin() returns a pointer to a string containing the name of the user logged in on the controlling terminal of the process, or a null pointer if this information cannot be determined.'
So you should do:
#include <stdio.h>
#include <unistd.h>
int main()
{
char *name;
name = getlogin();
if (!name)
perror("getlogin() error");
else
printf("This is the login info: %s\n", name);
return 0;
}
According to the man page the error (ENOENT) means:
There was no corresponding entry in the utmp-file.
I typically use getpwent() along with a call to geteuid() and getegid(). This gives me all of the information that I might possibly need to know (at least as far as /etc/passwd has to offer) and tells me if I'm running as setuid / setgid, which is helpful when programming defensively.
I have written several programs for my company that outright refuse to work if someone tries to setuid them and change ownership to root, or refuse to run as root if being called by a system user (www-data, nobody, etc).
As others have said, reading from utmp is a very bad idea for this purpose.
I'm writing a program that will be monitoring select files and directories for changes. Some of the files are world writeable, some owner, some group.
What I need to do is be able to figure out the last person to modify (not just access) a file. Somehow I thought this would be simple, given that we know the inode of the file .. however I can not seem to find any way of obtaining this. I thought there was a practical way of correlating any given inode to the uid last accessing it.
I think I've squeezed google for all its going to give me on the topic.
Any help is appreciated. I'm writing the program in C.
Edit:
I need to be able to do this after the PID of whatever program modified the file is long gone.
If you are on a 2.6 kernel, you can take advantage of kernel's auditd daemon. Check this URL out. It might give you some hint on how to accomplish what you are trying to. I'm sure there is an API you could use in C.
To my knowledge, this information is not stored by any of the common filesystems, but you should by able to hook into inotify and keep an audit trail of which processes touch which files.
Okay, using straight old standard Linux with normal file systems, you're not going to be able to do it. That information isn't stored anywhere (see man lstat for what is stored.)
As #pablo suggests, you can do this with security auditing turned on. The link he notes is a good start, but the gist of it is this:
you turn on the audit daemon, which enables auditing form the kernel
you configure the rules file to capture what you want
you search the audit files for the events you want.
The difficulty here is that if you start auditing all file operations for all files, the audit is going to get big.
So what is the actual need you want to fil?
very basic , but it works:
you can easily write a little c-program that does what you want
this example retrieves the UID of file or directory or link,
just try to find the properties that you want.
compile with:
gcc -x c my-prog.c -o my-prog
then:
./my-prog /etc
a lot of other information can be obtained like this
it's not robust. but whatever, i know how to use it,
and do the checking in a bash shell :-)
[ -x /etc ] && my-prog /etc
source code:
# retrieve the uid of a file
# source code: my-prog.c
#
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, char **argv) {
struct stat buffer;
int status;
char *fname;
fname=argv[1];
status = stat(fname, &buffer);
printf("%i",buffer.st_uid);
return 0;
}