Why is a file with 400 permissions seen writable by root but read-only by user?

7,159

Solution 1

test -w aka [ -w doesn't check the file mode. It checks if it's writable. For root, it is.

$ help test | grep '\-w'
  -w FILE        True if the file is writable by you.

The way I would test would be to do a bitwise comparison against the output of stat(1) ("%a Access rights in octal").

(( 0$(stat -c %a somefile) & 0200 )) && echo rw || echo ro

Note the subshell $(...) needs a 0 prefixed so that the output of stat is interpreted as octal by (( ... )).

Solution 2

I think you have have misunderstood what -w does. It does not check to see if the file has "Write permissions", it checks to see if the file is writable by the invoking user.

More specifically, it calls access(2) or similar.

eg if a script has if [ -w /etc/shadow ] then if you run strace on the script you may see a line similar to

faccessat(AT_FDCWD, "/etc/shadow", W_OK)

Since root can write to the file then it returns 0.

eg as a normal user:

faccessat(AT_FDCWD, "/etc/shadow", W_OK) = -1 EACCES (Permission denied)

As root

faccessat(AT_FDCWD, "/etc/shadow", W_OK) = 0

This, despite the fact that /etc/shadow has permission 000 on my machine.

---------- 1 root root 4599 Jan 29 20:08 /etc/shadow

Now what you want to do gets interesting and isn't so simple.

If you want to check the simple permissions then check the ls output, or call stat or similar. But realize that ACLs can over-ride these permissions. Just because a file is permission 400 doesn't stop it from being writable...

Solution 3

The root user can do as she pleases, "normal" file permissions are no limitation. It won't directly execute a plain file without any eXecute permissions, just for a bit of insurance against foot target practice.

Share:
7,159

Related videos on Youtube

Rich
Author by

Rich

Trying to be more right.

Updated on September 18, 2022

Comments

  • Rich
    Rich almost 2 years

    If I create a file as an unprivileged user, and change the permissions mode to 400, it's seen by that user as read-only, correctly:

    $ touch somefile
    $ chmod 400 somefile
    $ [ -w somefile ] && echo rw || echo ro
    ro
    

    All is well.

    But then root comes along:

    # [ -w somefile ] && echo rw || echo ro
    rw
    

    What the heck? Sure, root can write to read-only files, but it shouldn't make a habit of it: Best Practice would tend to dictate that I should be able to test for the write permission bit, and if it's not, then it was set that way for a reason.

    I guess I want to understand both why this is happening, and how can I get a false return code when testing a file that doesn't have the write bit set?

    • Rich
      Rich about 6 years
      btw I'm using both RHEL6 (4.1.2(1)-release) and RHEL7 (4.2.46(2)-release).
    • Kevin
      Kevin about 6 years
      "Best Practice would tend to dictate that I should be able to test for the write permission bit, and if it's not, then it was set that way for a reason." - Actually, best practice is "don't run stuff as root." If you're running as root, you've already decided to bypass permission checks. Manually re-implementing those permission checks in userspace is a recipe for disaster.
    • Rich
      Rich about 6 years
      @Kevin Good for you if you can run stuff unprivileged. This is for manipulating /etc/dhcp/dhcpd.conf, which is owned by root. I'm using the vendor-supplied dhcpd. Total disaster, huh? The file is checked into RCS, I'm automating use of rcsdiff, ci and co because we have operators that need to ... operate. The permission bit check (-w, as detailed by test(1)) was going to be a first line of failure, working on the basis that ci -u leaves a file read-only. I'm ditching that and going straight to rcsdiff -q and checking $?. Undisastrous dhcpd? It would be owned by dhcpd.
    • Kevin
      Kevin about 6 years
      It's a potential disaster because you now have two different implementations of permissions checks: one in the kernel and one in userspace. Worse, those implementations are not even intended to produce identical results, so you can't just fuzz test them against each other. So now you have two paths to access which have to be locked down and secured independently of each other.
    • Rich
      Rich about 6 years
      @Kevin Sure, they don't produce identical results and weren't intended to (despite the paucity of detail in manpages), but I explicitly want to check the write-permission bit; and the manpages for bash and test led me to believe that's what [ -w is for.
    • Tobias Kienzler
      Tobias Kienzler about 6 years
      If you want to prevent even root from accidentally modifying a file, try chattr +i. See also stackoverflow.com/q/48578108/321973
  • Rich
    Rich about 6 years
    No, "misunderstanding" would be reading the manpages wrong for -w: test(1) is explicit: "FILE exists and write permission is granted", not "file may be written by current user". Nothing about overriding permissions, nor ACLs. bash(1) is cagey: "True if file exists and is writable." ksh(1) makes a subtle hint toward shenanigans: "-w file // True, if file exists and is writable by current process." zsh(1) defers to test(1), zshmisc(1) is worded as ksh(1), and zshexpn(1) details some interesting permissions-based globbing. csh(1) is hilariously brief: "Write access".
  • Rich
    Rich about 6 years
    btw: I accepted Patrick's answer, 1. because you didn't adequately answer the lead-on question: how can I get a false return code when testing a file that doesn't have the write bit set? and 2. because of the snarky lead-in presuming I misunderstood the manpages.
  • Rich
    Rich about 6 years
    Thanks for being concise. Good use of (( ... & ... )). One typo corrected :-)
  • Rich
    Rich about 6 years
    Make that 3... No if required, octal permissions output is %a not %d, and (( ... )) needs a prefixed 0 to interpret the output of stat as octal.
  • Jerb
    Jerb about 6 years
    @Rich The way I read this, I think your comments are coming across as a bit snarky as well. I'm not saying that anything you wrote was wrong, just that it would probably go over better if expressed in a more polite way.
  • user2948306
    user2948306 about 6 years
    @Patrick my man stat says "%d device number in decimal", but what we want are the "access rights", no? Your point about needing the 0 prefix is well-made, but I guess we just have to bodge it in there :).
  • user2948306
    user2948306 about 6 years
    stat -c 0%a ...
  • phemmer
    phemmer about 6 years
    @sourcejedi ack, you're right. For some reason I was thinking %d was access rights in decimal. Restored the edit. thanks :-)
  • barbecue
    barbecue about 6 years
    "I think you have have misunderstood..." is not snarky. Your response however, 100% snarky.
  • Sebi
    Sebi about 6 years
    @Rich Also, while the manpages might not have been crystal clear, Stephen has stated that you may have "misunderstood what -w does", and not what manpages say, where the former looks to be the case, hence the question