Check other users permissions (with proposed solution)
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I think it is a good approach, but there are other ways to work with: you can check the number of arguments, $# will help you. Also you do not need double check if $# is good enough you can skip the check in the function permission.
__________________________________
Happy with solution ... mark as SOLVED
If someone helps you, or you approve of what's posted, click the "Add to Reputation" button, on the left of the post.
I think the best you can do is to check if that user is capable to do that. Therefore sudo -u $user <test> is the real way to check. I do not know why do you want another solution.
__________________________________
Happy with solution ... mark as SOLVED
If someone helps you, or you approve of what's posted, click the "Add to Reputation" button, on the left of the post.
It's such a common task to check (non-ACL) permissions, before performing some action.
I feel there should be some simple, direct, builtin (api-calling) compliant command to do it.
Perhaps an enhancement to bash or sh-POSIX.
Sudo is so heavy handed to use to perform such a simple test, which outght to be exposed at the filesystem-api or shell level. I feel using sudo for this is like driving a nail with a sledgehammer. It's certainly indirect, and also has security risks, but was the only quick solution I could find.
I can think of worse alternatives, I just can't think of any better.
It's such a common task to check (non-ACL) permissions, before performing some action.
Common maybe, but erroneous. It is unreliable, just plain wrong.
The correct way to do it is to simply do the action. Error handling can trivially find out why some part of the sequence could not be done; insufficient access permissions are just one possible reason. Checking beforehand is no substitute, since the permissions may change between checking and the actual access.
When you realize this, it suddenly becomes obvious why there is no API for this.
The only issue for programmers is that you need to have full error handling, preferably one that can gracefully fail with informative error messages. From experience, it is not hard; it becomes second nature very quickly when you always do it (always do it and do it right!). Learning how to do it was a lot of work.
Yes, there's no substitute for good exception handling.
But, in this case I need to provide sanity checks before any action is attempted. I cannot allow the script to run halfway through and fail. Exception handling not withstanding, because even if an exception is caught, there is no 'rollback' concept. So, the intention of this script is to ensure a minimum level of sanity and verify dependencies are satisfied before cascading actions commence.
Here is my simplified version, using runuser instead of sudo,
not sure if it is an improvement... And still need to clean the parameters...
Code:
#!/bin/bash
#
# @(#)$Id: permission,v 1.4 2012/06/15 22:07:35 rilliams final root $
# @(#)$Purpose: Determine if <user> has <permission> on <file|directory>$
# @(#)$Comment: this script is intended to be sourced, from a parent shell$
# @(#)$Usage: permission <user> <permission> <file|directory>$
# @(#)$Return: 0 TRUE, 1 FALSE, 99 NOT PROCESSED$
#
set -a
permission() {
[ ${#@} -eq 3 ] || return 99;
runuser $1 -c "[ -$2 $3 ] || exit 1;"; }
permission $1 $2 $3 && exit $?;
But, in this case I need to provide sanity checks before any action is attempted. I cannot allow the script to run halfway through and fail.
Then your design is faulty. That is exactly what a robust tool must be able to do: gracefully exit if an error occurs midway through.
I may sound harsh, but that is not my intention. I know that the world is full of examples and designs where you first do some checks to see whether the task ahead might fail, and then just do the work assuming it cannot fail -- or if it fails, the tool fails catastrophically. That design is just wrong. A good programmer can do better.
I personally prefer to use C (GNU C99 where available, C99 elsewhere), so I'm definitely not talking about exceptions either. Just robust error handling, where any function call can fail.
Quote:
Originally Posted by ph0n3s
there is no 'rollback' concept.
That means there will be no guarantees anyway about whether your tool works or not. The extra checks, then, are there only to catch the most common errors.
Let me repeat: that design is not robust. It is prone to failure. The correct solution is to do the work in a way which allows you to abort cleanly if an error occurs. I have personally not yet encountered a case where such a solution does not exist. In some cases they might be prohibitively expensive or slow, but I have not encountered such.
Quote:
Originally Posted by ph0n3s
So, the intention of this script is to ensure a minimum level of sanity and verify dependencies are satisfied before cascading actions commence.
All right. If your tool is run with elevated privileges, you can use simply
Code:
sudo -u "$USER" -- test -r "$FILE" && echo "$USER can read $FILE"
sudo -u "$USER" -- test -w "$FILE" && echo "$USER can write to $FILE"
sudo -u "$USER" -- test -x "$FILE" && echo "$USER can execute $FILE"
sudo -u "$USER" -- cd "$DIR" &>/dev/null && echo "$USER can enter $DIR"
and so on.
In the general case, I would use a separate helper script. Save
Code:
#!/bin/bash
[ "$(id -u)" = "0" ] || exec sudo -n -- "$0" "$@"
User="$1"
shift 1
if ! id -u "$User" &>/dev/null ; then
printf '%s: No such user.\n' "$User" >&2
exit 1
fi
exec sudo -n -u "$User" -- /usr/bin/test "$@"
as say /usr/local/bin/test-user and add the necessary ALL ALL = NOPASSWD: /usr/local/bin/test-user line to e.g. /etc/sudoers.d/test-user or /etc/sudoers file. Note that the script should NOT be setuid; mode 0755 (-rwxr-xr-x) and owned by root:root is best.
The end result is that the above script can be used just like test, except with the target username added as the first parameter. Examples:
Code:
test-user "$user" -d "$dir" -a -w "$dir" && echo "$user can create files in directory $dir"
test-user "$user" -w "$file" && echo "$user can write to $file"
The details: The second line in the script makes the script re-execute itself as root when run by a non-privileged user.
The script then checks using the id utility if the user exists. This check is for courtesy only: the following sudo will catch any invalid usernames anyway. (It helps catch installation problems, if you forget to add the line to sudoers.)
The final line uses sudo to switch to the desired user. Remember, the script is running privileged (as root) at this point. All other parameters -- except for the user name -- are given to the explicitly executed /usr/bin/test utility as-is. The above script is therefore just as secure as test is; there is no need to try to filter or modify any of the parameters. (If I used the shell built-in test or conditional operators, it would be a different matter.)
The main difference between our approaches is that you first look for something that works, then try to make it safe. I have the opposite approach; for me, security is an integral, inseparable part of my tools. They're not always safe, of course -- I err way too often --, but usually I manage to warn my users about the risks, and inform them about any deficiencies of the approach. I do not see security features as something that can be added on later.
I hope you find this useful. I apologise if I sound harsh, but your approach to the issue is something that I see almost constantly causing trouble. I wish programmers did better. The fact that you are aware of the issues, and discuss them here, tells me you have the skill -- or if not the skills yet, then definitely the capability the learn them! -- to handle this stuff right. Otherwise I would not have brought it up at all.
The details: The second line in the script makes the script re-execute itself as root when run by a non-privileged user.
...
and add the necessary ALL ALL = NOPASSWD: /usr/local/bin/test-user line to e.g. /etc/sudoers.d/test-user or /etc/sudoers file.
Why does the script need to run as root? Wouldn't it make more sense to add ALL ALL = NOPASSWD: /usr/bin/test to the sudoers file?
Why does the script need to run as root? Wouldn't it make more sense to add ALL ALL = NOPASSWD: /usr/bin/test to the sudoers file?
If you use it unmodified, it does not, and indeed the script could be simplified by using your suggestion.
However, I assume at some point the OP wishes to test if a user can enter a directory, which is best tested using cd, as I showed in my post. In that case test alone is insufficient. I was being sneaky
However, I assume at some point the OP wishes to test if a user can enter a directory, which is best tested using cd, as I showed in my post. In that case test alone is insufficient. I was being sneaky
Ah, hadn't noticed that.
Quote:
Originally Posted by Nominal Animal
All right. If your tool is run with elevated privileges, you can use simply
Code:
...
sudo -u "$USER" -- cd "$DIR" &>/dev/null && echo "$USER can enter $DIR"
However, cd is a builtin shell command so:
Code:
~$ USER=root
~$ DIR=/
~$ sudo -u "$USER" -- cd "$DIR" &>/dev/null && echo "$USER can enter $DIR" || echo "$USER can't enter $DIR"
root can't enter /
~$ sudo -u "$USER" -- cd "$DIR"
sudo: cd: command not found
Read and execute commands from string after processing the options, then exit. Any remaining arguments are assigned to the positional parameters, starting with $0.
That is very, very neat. The "$0" feels very weird to me, but it does work, just like the documentation says. I just had to check for myself! It even works in dash the exact same way.
Let's say I needed this sort of a tool to let my users check if they have set their file and directory accesses correctly. I prefer to use group-based access controls, so this is not that far fetched. Here is the skeleton of the script I'd use. Note: this one is intended to be used by humans interactively, not so much as a scripting tool.
I'm using [ instead of [[ (as well as avoiding certain other Bashisms) because this is the sort of thing I might wish to run using dash instead, in certain situations. Also, this is untested, so it probably contains bugs.
Code:
#!/bin/bash
# Make sure we have root privileges.
[ "$(id -u)" != "0" ] || exec sudo -n -- "$0" "$@"
# Usage requested?
if [ $# -lt 1 ] || [ "$1" = "-h" ] || [ "$1" = "--help" ]; then
exec >&2
echo "Usage: $0 [ -h | --help ]"
echo " $0 USER -drw DIR(s)..."
echo " $0 USER -frwx FILE(s)..."
echo "Where:"
echo " -d DIR USER can enter DIR"
echo " -w ITEM USER can write to ITEM"
echo " -r ITEM USER can read ITEM"
echo " -f FILE FILE is a regular file"
echo " -x FILE USER can execute FILE"
echo ""
echo "If all tests are successful, this will return success (exit status 0)."
echo "If any of the tests fail, this will return failure (exit status 1)."
echo "If USER is not valid user, this will return failure (exit status 2)."
echo "In case of other errors, this will return failure (exit status 3)."
echo ""
exit 3
fi
# Use POSIX/C locale to avoid problems with non-UTF-8 file names in UTF-8 locales.
export LANG=C
export LC_ALL=C
User="$1"
shift 1
# Courtesy user check
if ! id -u "$User" &>/dev/null ; then
echo "$User: No such user." >&2
exit 2
fi
while [ $# -gt 0 ]; do
Opts="${1#-}"
if [ "$Opts" = "$1" ]; then
echo "$1: Unsupported test." >&2
exit 3
fi
shift 1
if [ -z "$1" ]; then
echo "No files or directories specified for $Opts." >&2
exit 3
fi
case "$Opts" in
d) sudo -n -u "$User" -- bash -c 'cd "$0"' "$1" &>/dev/null || exit 1
shift 1
;;
f) sudo -n -u "$User" -- test -f "$1" &>/dev/null || exit 1
shift 1
;;
r) sudo -n -u "$User" -- test -r "$1" &>/dev/null || exit 1
shift 1
;;
w) sudo -n -u "$User" -- test -w "$1" &>/dev/null || exit 1
shift 1
;;
x) sudo -n -u "$User" -- test -x "$1" &>/dev/null || exit 1
shift 1
;;
*) echo "-$Opts: Unsupported test." >&2
exit 3
;;
esac
# Opts may contain more than one test. We handled the first one, so remove it.
Opts="${Opts#?}"
done
# Success.
exit 0
This should allow you to test e.g. USER -dw DIR to check if USER can enter and create new files in DIR . It should be easy to add new tests, too. I did not design it to support logical operators (-o, -a) or grouping, though; I think it is better if the caller does those themselves.
Hmm.. it might be useful to add echo or printf to the tests, to let the user know the detailed result of each test?
Last edited by Nominal Animal; 06-16-2012 at 02:12 PM.
This is great!
Thank you for the thorough discussion and recommendations!
I'll update my code.
[OT]
An aside , regarding the exception handling, et al:
Consider that we in the software-engineering field further our trade.
For mission and life-critical systems we are (more and more) mandated to incorporate 'formal (verification) methods' (esp. in Europe, getting better in the Americas).
That is, We MUST (formally, mathematically) show that a system or subsystem, yes even each and every script, must satisfy it's logical pre-conditions and logical post-conditions, WITHOUT execution (in the case of the verification), and also DURING execution. (the pre-conditions: I was calling sanity-checking-before-operation).
It's not 1970's, where formal systems and CASE tools were big and clunky, it's now, where flight systems and medical devices can kill, if we don't check that what we are about to do is safe -
... just a thought, you know what i'm sayin, and i see what your sayin, too.
[/OT]
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.