Linux setuid bit [duplicate] - linux

I created this simple script to allow the user to remove files created by the web server in his home directory without giving him "su". Both scripts are set with "chmod 4750".
The craziest thing is that they DID work and now they don't. Here's the scripts:
#!/bin/bash
# Ask for directory to delete
echo "Enter the file or directory you would like to delete, the assumed path is /home/user"
read DIRECTORY
rm -rf /home/user/"$DIRECTORY"
echo "Deleting /home/user/$DIRECTORY ..."
exit 0
2:
#!/bin/bash
# Reset permissions
echo "Resetting the ownership of the contents of /home/user to user."
chown -R user /home/user
exit 0
I will make them a little more advanced and work for multiple users but right now I cannot even get the simple version to work. It works when run as root of course. It used to work when run as user 'user' but now it doesn't. I get this:
user#dev:/home/user$ delete.sh
Enter the file or directory you would like to delete, the assumed path is /home/user/[your input]
test-dir
rm: cannot remove ‘/home/user/test-dir/test-file’: Permission denied
Deleting /home/user/test-dir ...
and
chown: changing ownership of ‘/home/user/test-dir’: Operation not permitted
What can possibly be the problem?
-rwsr-x--- 1 root user 291 Nov 6 05:23 delete.sh
-rwsr-x--- 1 root user 177 Nov 6 05:45 perms.sh

There is a pretty comprehansive answer at https://unix.stackexchange.com/questions/364/allow-setuid-on-shell-scripts
Bottom line is that there are two main points against it:
A race condition between when the Kernel opens the file to find which interpreter it should execute and when the interpreter opens the file to read the script.
Shell scripts which execute many external programs without proper checks can be fooled into executing the wrong program (e.g. using malicious PATH), or expand variables in a broken way (e.g. having white space in variable values), and generally it has less control on how well the external programs it executes handle the input.
Historically, there was a famous bug in the original Bourne shell (at least on 4.2BSD, which is where I saw this in action) which allowed anyone to get interactive root shell by creating a symlink called -i to a suid shell script. That's possibly the original trigger for this being prohibited.
EDIT: To answer "How do I fix it" - configure sudo to allow users to execute only these scripts as user root, and perhaps use a trick like in https://stackoverflow.com/a/4598126/164137 to find the original user's name and force operation on their own home directory, instead of letting them pass in any arbitrary input (i.e. in their current state, nothing in the scripts you include in your question prevents user1 from executing the scripts and passing them users2's directory, or any directory for that matter)

Related

Execute a bash script without typing ./ [duplicate]

I feel like I'm missing something very basic so apologies if this question is obtuse. I've been struggling with this problem for as long as I've been using the bash shell.
Say I have a structure like this:
├──bin
├──command (executable)
This will execute:
$ bin/command
then I symlink bin/command to the project root
$ ln -s bin/command c
like so
├──c (symlink to bin/command)
├──bin
├──command (executable)
I can't do the following (errors with -bash: c: command not found)
$ c
I must do?
$ ./c
What's going on here? — is it possible to execute a command from the current directory without preceding it with ./ and also without using a system wide alias? It would be very convenient for distributed executables and utility scripts to give them one letter folder specific shortcuts on a per project basis.
It's not a matter of bash not allowing execution from the current directory, but rather, you haven't added the current directory to your list of directories to execute from.
export PATH=".:$PATH"
$ c
$
This can be a security risk, however, because if the directory contains files which you don't trust or know where they came from, a file existing in the currently directory could be confused with a system command.
For example, say the current directory is called "foo" and your colleague asks you to go into "foo" and set the permissions of "bar" to 755. As root, you run "chmod foo 755"
You assume chmod really is chmod, but if there is a file named chmod in the current directory and your colleague put it there, chmod is really a program he wrote and you are running it as root. Perhaps "chmod" resets the root password on the box or something else dangerous.
Therefore, the standard is to limit command executions which don't specify a directory to a set of explicitly trusted directories.
Beware that the accepted answer introduces a serious vulnerability!
You might add the current directory to your PATH but not at the beginning of it. That would be a very risky setting.
There are still possible vulnerabilities when the current directory is at the end but far less so this is what I would suggest:
PATH="$PATH":.
Here, the current directory is only searched after every directory already present in the PATH is explored so the risk to have an existing command overloaded by an hostile one is no more present. There is still a risk for an uninstalled command or a typo to be exploited, but it is much lower. Just make sure the dot is always at the end of the PATH when you add new directories in it.
You could add . to your PATH. (See kamituel's answer for details)
Also there is ~/.local/bin for user specific binaries on many distros.
What you can do is add the current dir (.) to the $PATH:
export PATH=.:$PATH
But this can pose a security issue, so be aware of that. See this ServerFault answer on why it's not so good idea, especially for the root account.

Qmail/popuser Environment Permissions (Linux/CentOS)

I am trying to get a mail-receiving script working, but I am getting a permissions error when it tries to copy files to where I need them.
We're running a Linux/CentOS/Plesk webserver. I have qmail set to pipe messages that a certain address receives to a shell script. That script is supposed to write the messages to a file and copy them to one of the server's vhost subscriptions so that they can be accessed by code running via the webserver.
I can get the messages passed to the shell script no problem, and it can successfully write the received message to a file in the local directory (the recipient's folder in /var/qmail/mailnames/). When the script tries to copy the message file to the correct vhost directory, however, I get a 'permission denied' error.
--
.qmail (file piping mail to parse_mail.sh - included for clarity, this part is working as intended):
| true
| /bin/bash parse_mail.sh &> parse_mail_sh.log
--
parse_mail.sh:
echo "Start parse_mail $(date)";
u=$(/bin/id -u -n);
echo "running as ${u}";
umask 000;
# random tag name
templ='message';
rand=$(awk -vmin=100000000 -vmax=999999999 'BEGIN{srand(); print
int(min+rand()*(max-min+1))}');
tag=$templ$rand;
echo "Create dir ${tag}/";
/bin/mkdir $tag;
echo "Write message into ${tag}/";
/bin/cp /dev/stdin ${tag}/message.txt;
echo "Copy message to message_files/";
/bin/cp ${tag}/message.txt /var/www/vhosts/subscription/httpdocs/subfolder/message_files/${tag}_content.txt
echo "Remove ${tag}/";
/bin/rm -R ${tag};
echo "End parse_mail";
--
parse_mail_sh.log log shows:
Start parse_mail Thu Jul 18 11:07:06 EDT 2019
running as popuser
Create dir message494556566/
Write message into message494556566/
Copy message to message_files/
/bin/cp: failed to access '/var/www/vhosts/subscription/httpdocs/subfolder/message_files/message494556566_content.txt': Permission denied
Remove message494556566/
End parse_mail
--
Pretty straight-forward, right? 'popuser' just doesn't have permission to write to this other directory.
Except, here's the odd thing:
I'm not an expert, but I generally understand how unix/linux permissions work. I believe popuser DOES have the requisite permissions to access and write to the directory - the intervening directory structure is traversable, and I've added popuser to the groups necessary to write to that specific subscription folder.
I can su to popuser in the terminal and cd up and down the directory structure, and copy/create files in the correct places. I can even run the mail-receiving script from the terminal AS popuser, and it works perfectly. It only throws the error when triggered on receiving mail.
I assume this means that there is some difference in the environment popuser is operating in via qmail as opposed to the terminal - I just haven't been able to track down exactly what that might be (or find articles online about it).
Or perhaps I misunderstanding how something works? Like I said, not an expert...
So my question is:
Why can't the parse_mail.sh script successfully copy the created file to the indicated message_files/ directory when a message is received by qmail, given that that same script CAN do so when executed by the same system user (popuser) when run via the terminal?
After several days of fighting with this, I discovered the problem.
I had made all of the correct changes, but the system hadn't caught up.
I had restarted various services in PLESK - including the "POP/mail server"- but apparently postfix is not something you can control from there.
When I stopped and then restarted Postfix, my script suddenly worked.
My only theory is that perhaps 'popuser' was operating through postfix on an old session that didn't have my user group changes - which might explain why I could perform the operations via a popuser terminal, but the mail processor could not.

Using mkdir in my bash script and getting permission denied

i have script that is owned by root in a directory owned by root. part of the script is to make a directory that will hold the inputs/outputs of that script. i also have a sim link to that script so any user can run it from anywhere. i don't use the temp directory so this info can be used as logs later.
Problem: when a user tries to run the script they get an error that the directory cannot be created because of permission denied.
Questions: why won't the script make the directory so root owns it independent of what user runs it? how can the script make the directory so root owns it instead of the user that ran it? only the script needs this info, not the user.
Additional info:
the directory is: drws--s--x.
the script is: -rwxr-xr-x.
(if you need to know) the line in the script is simply: mkdir $tempdirname
i am matching the permissions of other scripts on the same server that output text files correctly, but since mine is a directory i'm getting permission errors.
i have tried adding the permissions for suid and sgid. suid sounded like the correct solution since it should make the script run as if it were run by the user that owns the script. (why isn't this the correct solution?)
i would like any user to be able to type in the sim link name, that will run the script that is owned by root in the directory that is owned by root, and the directories created by that script will stay in its own directory. and the end user has no knowledge or access to the inner workings of this process. (hence owned by root)
Scripts run as the user that runs them; the owner of the file and/or the directory it's in are irrelevant (except that the user needs read and execute permission to the file and directory). Binary executables can have their setuid bit set to make them always run as the file's owner. Old unixes allowed this for scripts as well but this caused a security hole, so setuid is ignored on scripts in modern unixes/Linuxes.
If you need to let regular users run a script as root, there are a couple of other ways to do this. One is to add the script to your /etc/sudoers file, so that users can use sudo to run it as root. WARNING: if you mess up your /etc/sudoers file, it can be hard to recover access to clean it up and get back to normal. Make a backup first, don't edit it with anything except visudo, and I recommend having a root shell open so if something goes wrong you'll have the root access you need to fix it without having to promote via sudo. The line you'll need to add will be something like this:
%everyone ALL=NOPASSWD: /path/to/script
If you want to make this automatic, so that users don't have to explicitly use sudo to run the script, you can start the script like this:
#!/bin/bash
if [[ $EUID -ne 0 ]];
then
exec sudo "$BASH_SOURCE" "$#"
fi
EDIT: A simpler version occurred to me; rather than having the script re-run itself under sudo, just replace the symlink with a stub script like this:
#!/bin/bash
exec sudo /path/to/real/script "$#"
Note that with this option, the /etc/sudoers entry must refer to the real script's path, not that of the symlink. Also, if the script doesn't take arguments, you can leave the "$#" off. Or use it, it won't do any harm either.
If messing with /etc/sudoers sounds too scary, there's another option: you could "compile" the script with shc (which actually just makes a binary executable wrapper around it), and make that setuid root (chmod 4755 /path/to/compiled-script; chown root /path/to/compiled-script). Since it's in a binary wrapper, setuid will work.

Owner cannot create file in folder with all permission Linux

I'm trying to create few files under folder with script, but not able to.
following is the folder privilege under which some files are to be created
su pnaid
The partial script content, responsible to create folder and create files in it
MKDIR_CMD="/bin/mkdir -p \"${PATH_TO_WRITE}\" > /dev/null 2>&1"
"${MKDIR_CMD}"
echo "Checking Dir Path exist"
if [ -d "${PATH_TO_WRITE}" ]; then
echo "Calling another script to create files under this folder"
"/createFiles.sh \"${PATH_TO_WRITE}\""
else
echo "WARNING: unable to create folder"
The parent folder to the $(PATH_TO_WRITE) has following privileges
drwxr-x---. 2 pnaid pnaid 4096 Dec 3 12:31 work_directory
Each time the statement "WARNING: unable to create folder" is displayed.
I tried creating a folder with pnaid user having 777 permission and feeding that in script instead of "${MKDIR_CMD}", in that case the statement "Calling another script to create files under this folder" is displayed but the other script is not able to write to this folder.
Also the echo statements from createFiles.sh when called from original script are not displayed, is there any way to view it.
If we perform the same commands on shell prompt instead of script, the commands work and desired output is obtained; i.e. folder is created with all the files in it.
Also the same script works if we run it with user root.
I believe this should work across Linux flavors, in this case I'm using CentOS
Please help me resolve this issue and let me know if I have missed mentioning any details.
Thanks
This line:
"${MKDIR_CMD}"
will not work. It treats the entire value of $MKDIR_COMMAND as the name of the program to run, it doesn't split it into the program and arguments, because you put quotes around it. Also, redirections are not processed when expanding a variable.
You need to use eval to re-parse the string:
eval "$MKDIR_CMD"
You have the same problem with:
"/createFiles.sh \"${PATH_TO_WRITE}\""
This should be:
/createFiles.sh "$PATH_TO_WRITE"
These problems don't depend on permissions, I doubt the script really works when run as root.
Here's a related question that shows how to store command parameters best in variables:
Setting an argument with bash
However, the solution there (using an array instead of a string) won't work if you're also storing shell operators like redirection.

Why can I 'touch' a write-protected file?

Why is it possible to touch a write-protected file?
Shouldn't the following give an error?
$ touch test.txt
$ chmod a-w test.txt
$ ls -l test.txt
-r--r--r-- 1 name group 0 Jun 13 09:14 test.txt
$ touch test.txt && echo OK
OK
$ ls -l test.txt
-r--r--r-- 1 name group 0 Jun 13 09:15 test.txt
Does touch change permissions, touch the file, and change permissions back? Why would it do that?
Given this behavior, if I really want to protect a file so that I (my user) will never (unintentionally) change, remove or change its timestamp in the future -- how can I do it?
(Sorry, not strictly programming-related, but slightly, and probably of interest to many programmers.)
From the touch (coreutils) documentation:
If changing both the access and
modification times to the current
time, `touch' can change the
timestamps for files that the user
running it does not own but has write
permission for. Otherwise, the user
must own the files.
The execution permissions of the directory that the file contains dictates the ability to delete or modify the inode information for the entry in the directory that is associated with the file.
As the comment below indicates I have glossed over the technical reason but instead offered a reasoning why the behavior might not be as expected. Since you can execute in the directory there are a number of things you can do to tinker with the file and I am going to leave it at that.
If you want to stop anyone but root from modifying a file the best method is to use the chattr +i filename on the file. Even root will not be able to perform any actions on it without running chattr -i on it. This applies to Linux so YMMV.
Here's the relevant output from : strace "touch test.txt"
open("test.txt", O_WRONLY|O_CREAT|O_NOCTTY|O_NONBLOCK|O_LARGEFILE, 0666) = -1 EACCES (Permission denied)
futimesat(AT_FDCWD, "test.txt", NULL) = 0
It indeed gets a "Permission denied error" on the open(2) system call regarding EACCES. See relevant section in utimes(2) man page.
However, it does succeed in updating the timestamp using the futimesat(2) system call.
As others have indicated, it looks like the directory permissions hold the rights to update access/moficiation timestamps.
You can, however change the attribute of a file to immutable using:
chattr +i test.txt
Note: Only root can do this, and it's a very harsh way to disable access to files. But in extreme cases, it can be useful. In addition, this is an ext2/3/4 feature, not available on other filesystems as far as I know.
You can update the modification time if you own the file, regardless of write permission. (It is not related to any permission on the directory.)
From POSIX.1-2008:
Only a process with the effective user ID equal to the user ID of the file, or with write access to the file, or with appropriate privileges may use futimens() or utimensat() with a null pointer as the times argument or with both tv_nsec fields set to the special value UTIME_NOW. Only a process with the effective user ID equal to the user ID of the file or with appropriate privileges may use futimens() or utimensat() with a non-null times argument that does not have both tv_nsec fields set to UTIME_NOW and does not have both tv_nsec fields set to UTIME_OMIT. If both tv_nsec fields are set to UTIME_OMIT, no ownership or permissions check shall be performed for the file, but other error conditions may still be detected (including [EACCES] errors related to the path prefix).
In layman's terms, using the touch command will update or create a file without editing/modifying its contents. Because the command doesn't(and can't) write or erase anything from the file, it can be used on write-protected files. See the wiki on the touch command for more information: http://en.wikipedia.org/wiki/Touch_(Unix)

Resources