So here's the setup. You've logged into one of your servers to update
permissions on a file only to see this:
server [0] /bin/chmod +x /opt/somefile
-ksh: /bin/chmod: cannot execute [Permission denied]
server [0] /bin/ls -ld /bin/chmod
-rw-r--r-- 1 root root 38564 Jul 18 22:24 /bin/chmod
Ouch, if we use 'chmod' to modify permissions on files, yet 'chmod'
is no longer executable, then we're up the proverbial creek without
a paddle. Right?
Those who know me will tell you that I'm particularly fond of this
seeming conundrum. The typical answers that I hear to this situation
are normally "Just copy it over from another box," or "Copy it over
from the boot media." I seem to recall once hearing "Restore it from
backup." While these recovery methods are fine, they are also unnecessary
since you can recover chmod's execute bit using only the resources on
the server.
HOST INFO
Hosts: snorkle, tux, beastie
Shell Prompt: host [0]
OSes: Solaris 10, CentOS 5.4, FreeBSD 8.1
CHMOD Default Permissions:
Solaris: 0555 (root:root)
Linux: 0755 (root:root
FreeBSD: 0555 (root:wheel)
Notes: The following is pretty generic and should
be usable on earlier versions of each OS.
DETAILS
All of the following will result in restoring the default permissions
to 'chmod'. To begin, since 'setfacl' can also modify permissions,
it could be used (see note 1 at end):
snorkle [0] /usr/bin/setfacl -s u::r-x,g::r-x,o:r-x /bin/chmod
tux [0] /usr/bin/setfacl --set u::rwx,g::r-x,o::r-x /bin/chmod
beastie [0] /bin/setfacl -bn -m u::r-x,g::r-x,o::r-x /bin/chmod
Of note, both the Solaris and Linux versions of 'setfacl' include a
deliberate 'set' function, whereas FreeBSD only allows 'modify'. As a
result, the first two options to FreeBSD's 'setfacl' seen above are
'-bn', which will remove all facls aside from the required user, group,
and other. I mention this because otherwise 'chmod' would end up with
a facl due to setting of a default mask, thus not exactly identical to
previous permissions.
There's always copying out another executable binary with the same
permissions and overwriting it. On all three OSes, 'ls' has the same
ownership / permissions as 'chmod'. Since the process is identical on
all three boxes, 'snorkle' will be used for the example:
snorkle [0] /bin/ls -ld /bin/ls
-r-xr-xr-x 1 root bin 18700 Jun 11 2008 /bin/ls
snorkle [0] /bin/cp /bin/ls /tmp/chmod
snorkle [0] /bin/cat /bin/chmod > /tmp/chmod
snorkle [0] /bin/mv /tmp/chmod /bin/chmod
For Solaris and Linux, one can always run 'ld*.so.X', the runtime linker,
which will execute a dynamic executable such as 'chmod'. The kernel
will map the file and pass off control to the appropriate interpreter
to handle, effectively allowing you to use 'chmod' to fix 'chmod':
snorkle [0] /lib/ld.so.1 /bin/chmod +x /bin/chmod
tux [0] /lib/ld-linux.so.2 /bin/chmod +x /bin/chmod
Anymore, while you may not find a C compiler on a production box, 'perl'
is quite often available. This is useful because 'perl' has its very own
'chmod' function:
snorkle [0] /usr/bin/perl -e 'chmod 0555, qw(/bin/chmod)'
tux [0] /usr/bin/perl -e 'chmod 0755, qw(/bin/chmod)'
beastie [0] /usr/bin/perl -e 'chmod 0555, qw(/bin/chmod)'
The last method to mention is 'install', familiar to those who compile
source packages (see note 2), and move it into place:
snorkle [0] /usr/ucb/install -o root -g root -m 0555 /bin/chmod /tmp/chmod
tux [0] /usr/bin/install -m 0755 /bin/chmod /tmp/chmod
beastie [0] /usr/bin/install -m 0555 /bin/chmod /tmp/chmod
snorkle [0] /bin/mv /tmp/chmod /bin/chmod
The result of any of the above methods is the restoration of the
executable bit on 'chmod', as seen below on snorkle:
snorkle [0] /bin/ls -ld /bin/chmod
-r-xr-xr-x 1 root root 14236 Oct 29 23:12 /bin/chmod
Hopefully one wouldn't have to deal with this type of situation, though
as UNIX administrators, we know accidents can happen. By remaining calm
and thinking creatively, it's easy to see that the situation really isn't
all that bad and is recoverable. This also goes to illustrate the time
honored UNIX saying, "there's more than one way to do it." If you have
an alternative means of handling a non-executable 'chmod' and are willing
to share it, drop a line in the comments.
Note 1:
On Linux and FreeBSD hosts, unless facls are enabled on the filesystem,
via either mount options or tune2fs / tunefs (respectively), attempting
to set a facl will result in the following:
tux [0] /usr/bin/setfacl --set u::rwx,g::r-x,o::r-x /bin/chmod
setfacl: chmod: Operation not supported
beastie [0] /bin/setfacl -m u::r-x,g::r-x,o::r-x /bin/chmod
setfacl: chmod: acl_get_file() failed: Operation not supported
A quick resolve is a remount of the filesystem with acl support:
tux [0] /bin/mount -o remount,rw,acl /
beastie [0] /sbin/mount -o rw,acls -u /
Additionally, upon using the illustrated 'setfacl' command on FreeBSD,
the following error may occur and can be safely ignored:
beastie [0] /bin/setfacl -bn -m u::r-x,g::r-x,o::r-x /bin/chmod
setfacl: /bin/chmod: warning: no mask entry
Note 2:
Solaris has two 'install' executables:
sbin: /usr/sbin/install
ucb: /usr/ucb/install
The first (sbin) is installed with package SUNWcsu (Core Solaris,
(Usr)). It is also unusable in this situation as it is only a shell
script which would otherwise call the non-functioning 'chmod' binary.
The second (ucb) is installed with package SUNWscpu (Source Compatibility,
(Usr)). This one is a binary executable and does not call the 'chmod'
executable to modify file permissions, which is why it works.
As a secondary, the additional options for the Solaris 'install' command,
specify the user:group. This is due to 'install' placing 'chmod' into
/tmp as user:group 'root:staff', rather than 'root:root' by default.