Saturday, November 21, 2015

What can you do when you can't chmod chmod?

http://www.itworld.com/article/3002286/operating-systems/what-can-you-do-when-you-cant-chmod-chmod.html

An interesting question was posed recently in a discussion group. What would you do if the chmod command on a system that you manage lost its execute permissions? Well, you wouldn't exactly just chmod it back to its original state. So, what could you do? The answers offered at the time were clever, ranging from grabbing a copy of the command from a similar system to using some system tools to resolve the problem on the spot. And, as I pondered the question, even more answers appeared -- some that made me think more deeply about how our wonderful Unix systems work and some that I'd never have thought of without some prodding.
Here are some of the many ways that you could make chmod executable again. How many of these would have popped into your head and how many provide insights that you might not have considered?

Copy the file from another system

If you have compatible systems, you can always just grab a copy of chmod from another server using scp or rsync.
# cd /bin
# mv chmod chmod.orig
# scp twin:/bin/chmod .
# diff chmod chmod.orig
In the example above, we copy chmod after making a backup and then run a diff command to convince ourselves that it really is the same file. Once the new chmod is in place, we can get rid of the duplicate.

Use rsync to restore permissions without involving a remote system

The rsync command is almost always used to move files between systems, but it isn't restricted to that use and can copy files locally. And, as you can see from the example below, rsync also allows you to assign permissions to the new file.
In the example below, we use rsync to copy chmod to /tmp while giving the copy execute permissions. We then check the copy to make sure it looks right and use it to overwrite the original file.
# ls -l /bin/chmod
-rw-r--r-- 1 root root 52992 Oct  7 2014 chmod
# rsync chmod /tmp/chmod --chmod=ugo+x
# ls -l /tmp/chmod
-rwxr-xr-x 1 root root 52992 Nov  6 20:51 /tmp/chmod
# mv /tmp/chmod /bin

Fix permissions using setfacl

Using the setfacl (set file ACL) command, we set owner permissions. Then we can use the chmod command to set the rest of the permission bits.
# cd /bin
# ls -l chmod
-rw-r--r-- 1 root root 52992 Nov  6 18:05 chmod
# setfacl -m u::rx chmod
# ls -l chmod
-r-xr--r-- 1 root root 52992 Nov  6 18:05 chmod
# chmod 755 chmod
# ls -l chmod
-rwxr-xr-x 1 root root 52992 Nov  6 18:05 chmod
You can also use setfacl to copy permissions from another file.
In this command, we use a combination of getfacl and setfacl commands to copy the permissions from another file.
# getfacl /bin/ls | setfacl --set-file=- /bin/chmod

Change permissions with a C program

You can write a fairly simple C program to fix permissions problems. After all, even the chmod command just uses system calls to change permissions. Compile the program shown below and use it to set any file's permissions to 755.
I call this program fixperms, but feel free to use any name that doesn't conflict with a system executable. If you name the file "fixperms.c" and compile with "make fixperms", you should be good to go.
Notice all the "S_IRUSR" type commands in the "modify, each of which is setting a particular bit in the file's permissions matrix. The "R" in S_IRUSR meand "read" and the "USR" means the file's owner.
#include 
#include 

int main ( int argc, char *argv[] )
{
    if ( argc != 2 ) /* need file name */
    {
        printf( "usage: %s filename\n", argv[0] );
    }

    struct stat st;
    mode_t mode;
    const char *path = argv[1];
    stat(argv[1], &st);

    mode = st.st_mode & 07777;

    // modify permissions to rwxr-xr-x
    mode |= S_IRUSR;       /* Set owner read bit */
    mode |= S_IWUSR;       /* Set owner write bit */
    mode |= S_IXUSR;       /* Set owner execute bit */
    mode |= S_IRGRP;       /* Set group read bit */
    mode |= S_IXGRP;       /* Set group execute bit */
    mode |= S_IROTH;       /* Set other read */
    mode |= S_IXOTH;       /* Set other execute */

    chmod(path, mode);

    return 0;
}
In the commands shown below, we compile and then run the program.
# make fixperms
# ls -l fixperms
-rwxrwxr-x 1 root root 7224 Nov  7 01:20 fixperms
$ ls -l /bin/chmod
-r--r--r-- 1 root root 38852 Nov  6 16:26 chmod
$ ./fixperms /bin/chmod
$ ls -l /bin/chmod
-rwxr-xr-x 1 root root 38852 Nov  6 16:26 chmod

Use the dynamic linker/loader to run chmod

Another clever trick is to run chmod (yes, even without it having execute permission) by supplying it as an argument to the dynamic linker/loader. This is similar to running a script that doesn't have execute permission set by supplying it as an argument to bash.
# ls -l chmod
-r--r--r-- 1 root root 52992 Nov  6 18:05 chmod
# /lib64/ld-2.17.so ./chmod +x ./chmod
# ls -l chmod
-r-xr-xr-x 1 root root 52992 Nov  6 18:05 chmod

Fix chmod with perl

You can also restore execute permission to a file using a perl command like that shown below.
# ls -l /bin/chmod
-r--r--r-- 1 root root 52992 Nov  6 18:05 /bin/chmod
# perl -e 'chmod 0755, "/bin/chmod"'
# ls -l /bin/chmod
-rwxr-xr-x 1 root root 52992 Nov  6 18:05 chmod

Wrap Up

I used to spend a portion of my time every week thinking about what could go wrong and what I would do if it did. This question was a good one for getting me back into that way of thinking.
Even if your first reaction to a problem is "Oh, no!", there may be a number of ways to fix whatever is wrong. I sometimes even get around to welcoming problems, especially when I have enough breathing room to deal with them (i.e., no one is breathing down my neck). Problems often force us sysadmins to think a little harder, explore some options we might not have previously considered, and sometimes learn some new tricks -- often tricks that will come in handy many times over. In fact, quite a few of my best insights over the last 30 years came about because I ran into a problem that initially stumped me, but slowly led to me getting insights that never would have come about if I hadn't first slammed into what felt like a brick wall.

No comments:

Post a Comment