Test if a directory is writable by a given UID?

后端 未结 8 1573
一生所求
一生所求 2020-12-14 14:31

We can test if a directory is writable by the uid of the current process:

if [ -w $directory ] ; then echo \'Eureka!\' ; fi

But can anyone

相关标签:
8条回答
  • 2020-12-14 15:09

    That could do the test:

    if read -a dirVals < <(stat -Lc "%U %G %A" $directory) && (
        ( [ "$dirVals" == "$wantedUser" ] && [ "${dirVals[2]:2:1}" == "w" ] ) ||
        ( [ "${dirVals[2]:8:1}" == "w" ] ) ||
        ( [ "${dirVals[2]:5:1}" == "w" ] && (
            gMember=($(groups $wantedUser)) &&
            [[ "${gMember[*]:2}" =~ ^(.* |)${dirVals[1]}( .*|)$ ]]
        ) ) )
      then
        echo 'Happy new year!!!'
      fi
    

    Explanations:

    There is only one test (if), no loop and no fork.

    + Nota: as I'v used stat -Lc instead of stat -c, this will work for symlinks too!

    So condition is if,

    • I could successfully read stats of $directory and assign them to dirVals,
    • And (
      • ( Owner match And Flag UserWriteable is present )
      • or flag Other Writeable is present
      • or ( Flag GroupWriteabe is present AND
        • I could successfully assing member list of $wantedUser to gMember AND
        • A string built by merging fields 2 to last of $gMember will match beginOfSting-Or-something-followed-by-a-space, immediately followed by target's group (${dirVals[1]}), immediately followed by a-space-followed-by-something-Or-endOfString. )

    then echo Happy new year!

    As the group's test implie a second fork (And I love to reduce as possible such calls), this is the last test to be done.

    Old:

    Simply:

    su - mysql -c "test -w '$directory'" && echo yes
    yes
    

    or:

    if su - mysql -s /bin/sh -c "test -w '$directory'" ; then 
        echo 'Eureka!'
      fi
    

    Nota: Warn to enclose first with double-quotes for having $directory developped!

    0 讨论(0)
  • 2020-12-14 15:09

    Why not just do something simple like TRY a mkdir on the folder in question. It's more reliable....

        mkdir your_directory/
        [[ $? -ne 0 ]] && echo "fatal" || echo "winner winner chicken dinner.."
    

    OR ?

        # -- run the following commands as the_User_ID
        sudo su - the_User_ID << BASH
    
        mkdir your_directory/
        [[ $? -ne 0 ]] && echo "fatal" || echo "winner winner chicken dinner.."
    
        BASH
    
    0 讨论(0)
  • 2020-12-14 15:14

    You can use sudo to execute the test in your script. For instance:

    sudo -u mysql -H sh -c "if [ -w $directory ] ; then echo 'Eureka' ; fi"
    

    To do this, the user executing the script will need sudo privileges of course.

    If you explicitly need the uid instead of the username, you can also use:

    sudo -u \#42 -H sh -c "if [ -w $directory ] ; then echo 'Eureka' ; fi"
    

    In this case, 42 is the uid of the mysql user. Substitute your own value if needed.

    UPDATE (to support non-sudo-priviledged users)
    To get a bash script to change-users without sudu would be to require the ability to suid ("switch user id"). This, as pointed out by this answer, is a security restriction that requires a hack to work around. Check this blog for an example of "how to" work around it (I haven't tested/tried it, so I can't confirm it's success).

    My recommendation, if possible, would be to write a script in C that is given permission to suid (try chmod 4755 file-name). Then, you can call setuid(#) from the C script to set the current user's id and either continue code-execution from the C application, or have it execute a separate bash script that runs whatever commands you need/want. This is also a pretty hacky method, but as far as non-sudo alternatives it's probably one of the easiest (in my opinion).

    0 讨论(0)
  • 2020-12-14 15:14

    alias wbyu='_(){ local -i FND=0; if [[ $# -eq 2 ]]; then for each in $(groups "$1" | awk "{\$1=\"\";\$2=\"\"; print \$0}"); do (($(find "${2}" \( -perm /220 -o -group "$each" -a -perm /g+w \) 2>/dev/null | wc -l))) && FND=1; done; else echo "Usage: wbyu <user> <file|dir>"; fi; (($FND)) && echo "Eureka!"; }; _'

    I put it into an alias it takes two arguments, the first is the user and the second is the directory to check. It looks for permissions writable by anyone and also loops over the groups of the specified user to check if the directory is in the user group and writable - if either gets a hit it sets a found flag and prints Eureka! at the end.

    IOW:

    FND=0
    USER=user1
    DIR=/tmp/test
    for each in $(groups "$USER" | awk '{$1="";$2=""; print $0}'); do 
    (($(find "$DIR" \( -perm /220 -o -group "$each" -a -perm /g+w \)\ 
       2>/dev/null | wc -l))) && FND=1 
    done
    (($FND)) && echo 'Eureka!'
    
    0 讨论(0)
  • 2020-12-14 15:17

    Because I had to make some changes to @chepner's answer in order to get it to work, I'm posting my ad-hoc script here for easy copy & paste. It's a minor refactoring only, and I have upvoted chepner's answer. I'll delete mine if the accepted answer is updated with these fixes. I have already left comments on that answer pointing out the things I had trouble with.

    I wanted to do away with the Bashisms so that's why I'm not using arrays at all. The ((arithmetic evaluation)) is still a Bash-only feature, so I'm stuck on Bash after all.

    for f; do
        set -- $(stat -Lc "0%a %G %U" "$f")
        (("$1" & 0002)) && continue
        if (("$1" & 0020)); then
            case " "$(groups "$USER")" " in *" "$2" "*) continue ;; esac
        elif (("$1" & 0200)); then
            [ "$3" = "$USER" ] && continue
        fi
        echo "$0: Wrong permissions" "$@" "$f" >&2
    done
    

    Without the comments, this is even fairly compact.

    0 讨论(0)
  • 2020-12-14 15:19

    I've written a function can_user_write_to_file which will return 1 if the user passed to it either is the owner of the file/directory, or is member of a group which has write access to that file/directory. If not, the method returns 0.

    ## Method which returns 1 if the user can write to the file or
    ## directory.
    ##
    ## $1 :: user name
    ## $2 :: file
    function can_user_write_to_file() {
      if [[ $# -lt 2 || ! -r $2 ]]; then
        echo 0
        return
      fi
    
      local user_id=$(id -u ${1} 2>/dev/null)
      local file_owner_id=$(stat -c "%u" $2)
      if [[ ${user_id} == ${file_owner_id} ]]; then
        echo 1
        return
      fi
    
      local file_access=$(stat -c "%a" $2)
      local file_group_access=${file_access:1:1}
      local file_group_name=$(stat -c "%G" $2)
      local user_group_list=$(groups $1 2>/dev/null)
    
      if [ ${file_group_access} -ge 6 ]; then
        for el in ${user_group_list-nop}; do
          if [[ "${el}" == ${file_group_name} ]]; then
            echo 1
            return
          fi
        done
      fi
    
      echo 0
    }
    

    To test it, I wrote a wee test function:

    function test_can_user_write_to_file() {
      echo "The file is: $(ls -l $2)"
      echo "User is:" $(groups $1 2>/dev/null)
      echo "User" $1 "can write to" $2 ":" $(can_user_write_to_file $1 $2)
      echo ""
    }
    
    test_can_user_write_to_file root /etc/fstab
    test_can_user_write_to_file invaliduser /etc/motd
    test_can_user_write_to_file torstein /home/torstein/.xsession
    test_can_user_write_to_file torstein /tmp/file-with-only-group-write-access
    

    At least from these tests, the method works as intended considering file ownership and group write access :-)

    0 讨论(0)
提交回复
热议问题