LinuxQuestions.org
Latest LQ Deal: Latest LQ Deals
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 06-15-2012, 12:54 PM   #1
ph0n3s
LQ Newbie
 
Registered: Jun 2012
Location: Atlanta
Distribution: Centos
Posts: 6

Rep: Reputation: Disabled
Question Check other users permissions (with proposed solution)


So, in bash, as root, I want to check if <user> has <permission> on <file|directory>.

In bash, of course there is 'test' to determine if the current <user> (EUID) has execute permissions on <file>:

Code:
[ -x /pathto/file ]
But its not the current user I am curious about, I want to know if a different user has the permissions.

The best I could come up with was this:

Code:
#!/bin/bash
#
# @(#)$Id: permission,v 1.3 2012/06/15 03:20:13 rwilliams 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$
#

# Automatically export defined functions
set -a

permission() {
  { [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ]; } || return 99;
  chk="{ [ -${2} ${3} ] && exit 0; } || exit 1;"
  sudo -u ${1} bash -c "${chk}"
  return $?
}

# Skip testing if no command-line parameters were given
{ [ -n "$1" ] && [ -n "$2" ] && [ -n "$3" ]; } \
&& permission $1 $2 $3 && exit $?
Of course, the permission function needs to CLEAN it's parameters.
It works, but I was wondering, is there an alternative/better way??

Thanks!
- Rand
 
Old 06-15-2012, 01:31 PM   #2
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,846

Rep: Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309
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.
 
Old 06-15-2012, 02:01 PM   #3
ph0n3s
LQ Newbie
 
Registered: Jun 2012
Location: Atlanta
Distribution: Centos
Posts: 6

Original Poster
Rep: Reputation: Disabled
${#@} got it, thanks!

Still wondering if there other ways to check for another users permissions on a file/dir.
 
Old 06-15-2012, 02:33 PM   #4
pan64
LQ Addict
 
Registered: Mar 2012
Location: Hungary
Distribution: debian/ubuntu/suse ...
Posts: 21,846

Rep: Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309Reputation: 7309
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.
 
Old 06-15-2012, 04:14 PM   #5
ph0n3s
LQ Newbie
 
Registered: Jun 2012
Location: Atlanta
Distribution: Centos
Posts: 6

Original Poster
Rep: Reputation: Disabled
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.
 
Old 06-15-2012, 04:41 PM   #6
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by ph0n3s View Post
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.
 
Old 06-15-2012, 05:40 PM   #7
ph0n3s
LQ Newbie
 
Registered: Jun 2012
Location: Atlanta
Distribution: Centos
Posts: 6

Original Poster
Rep: Reputation: Disabled
Cool

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 $?;
 
Old 06-15-2012, 06:39 PM   #8
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by ph0n3s View Post
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 View Post
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 View Post
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.
 
Old 06-15-2012, 06:54 PM   #9
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,781

Rep: Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081
Quote:
Originally Posted by Nominal Animal View Post
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?
 
Old 06-16-2012, 10:31 AM   #10
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by ntubski View Post
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
 
Old 06-16-2012, 10:48 AM   #11
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,781

Rep: Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081
Quote:
Originally Posted by Nominal Animal View Post
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
 
Old 06-16-2012, 11:01 AM   #12
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by ntubski View Post
Ah, hadn't noticed that. However, cd is a builtin shell command
Ouch. Teaches me right for not testing my snippets.

The correct solution is of course
Code:
sudo -n -u "$USER" -- bash -c "cd '$DIRSQ'" &>/dev/null
where DIRSQ is a non-empty string with all single quotes escaped using '"'"' . In other words,
Code:
SQ="'"
ESQ="'\"'\"'"
[ -n "$DIR" ] || exit 1
sudo -n -u "$USER" -- bash -c "cd '${DIR//$SQ/$ESQ}'" &>/dev/null || exit 1
 
Old 06-16-2012, 11:29 AM   #13
ntubski
Senior Member
 
Registered: Nov 2005
Distribution: Debian, Arch
Posts: 3,781

Rep: Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081Reputation: 2081
Quote:
Originally Posted by Nominal Animal View Post
Ouch. Teaches me right for not testing my snippets.
Yup, I've noticed that when I post untested code it's nearly always wrong.

Quote:
where DIRSQ is a non-empty string with all single quotes escaped using '"'"' .
We can avoid some escaping, I think:
Code:
sudo -n -u "$USER" -- bash -c 'cd "$0"' "$DIR" &>/dev/null
Quote:
-c string
Read and execute commands from string after processing the options, then exit. Any remaining arguments are assigned to the positional parameters, starting with $0.
From the Bash Reference Manual - Invoking Bash
 
Old 06-16-2012, 02:09 PM   #14
Nominal Animal
Senior Member
 
Registered: Dec 2010
Location: Finland
Distribution: Xubuntu, CentOS, LFS
Posts: 1,723
Blog Entries: 3

Rep: Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948Reputation: 948
Quote:
Originally Posted by ntubski View Post
We can avoid some escaping, I think:
Code:
sudo -n -u "$USER" -- bash -c 'cd "$0"' "$DIR" &>/dev/null
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.
 
Old 06-18-2012, 11:19 AM   #15
ph0n3s
LQ Newbie
 
Registered: Jun 2012
Location: Atlanta
Distribution: Centos
Posts: 6

Original Poster
Rep: Reputation: Disabled
Hey Nominal, pan64, Ntubski,

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]
 
  


Reply

Tags
bash scripting, conditionals, permissions



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
Solution to have svn managed file-permissions and ownership nobody25145147 Linux - Software 0 01-25-2009 07:36 AM
Need permission solution for sharing media among users computercolin Linux - Newbie 2 01-21-2007 11:47 AM
Proposed solution for "status" (aka "problem solved") indicator demerson3 LQ Suggestions & Feedback 12 04-08-2006 02:15 PM
Users Locked Out, Gdm Gone Beserk,14 hours @ work. No Solution yet. fragmented_user Linux - Newbie 17 09-02-2005 06:07 AM
The perfect Solution Windoze users switching to Linux: koolkat Linux - Distributions 1 09-28-2003 12:45 PM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 01:11 AM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration