15

I created a Bash script which echoes "Hello World". I also created a test user, bob, using adduser.

Nobody has permission to execute that file as denoted by ls:

$ ls -l hello.sh 
-rw-r--r-- 1 george george 19 Mai 29 13:06 hello.sh

As we can see from the above the file's owner is george where he has only read and write access but no execute access. But logged in as george I am able to execute the script directly:

$ . hello.sh 
Hello World

To make matters worse, I log in as bob, where I have only read permission, but I am still able to execute the file:

$ su bob
Password: 
$ . /home/george/testdir/hello.sh 
Hello World

What's going on?

Boann
  • 630
Themelis
  • 411

3 Answers3

31

In your examples, you are not executing the files, but sourcing them.

Executing would be via

$ ./hello.sh

and for that, execution permission is necessary. In this case a sub-shell is opened in which the commands of the script file are executed.

Sourcing, i.e.

$ . hello.sh

(with a space in between) only reads the file, and the shell from which you have called the . hello.sh command then executes the commands directly as read, i.e. without opening a sub-shell. As the file is only read, the read permission is sufficient for the operation. (Also note that stating the script filename like that invokes a PATH search, so if there is another hello.sh in your PATH that will be sourced! Use explicit paths, as in . ./hello.sh to ensure you source "the right one".)

If you want to prevent that from happening, you have to remove the read permission, too, for any user who is not supposed to be using the script. This is reasonable anyway if you are really concerned about unauthorized use of the script, since

  • non-authorizeded users could easily bypass the missing execution permission by simply copy-and-pasting the script content into a new file to which they could give themselves execute permissions, and
  • as noted by Kusalananda, otherwise an unauthorized user could still comfortably use the script by calling it via
    sh ./hello.sh
    
    instead of
    ./hello.sh
    

because this also only requires read permissions on the script file (see this answer e.g.).

As a general note, keep in mind that there are subtle differences between sourcing and executing a script (see this question e.g.).

AdminBee
  • 22,803
14

The execute permission simply means that this file can be executed. However, when you source it (. hello.sh or source hello.sh) or when you pass it as an argument to a shell interpreter (sh hello.sh), you aren't executing the file, you're executing another command (. or sh) and passing the file as an argument to that command.

So to answer your question, the reason you can "execute" the file with . hello.sh is the same reason why you can run cat hello.sh: you are just reading the file, not executing it.

To illustrate:

$ ls -l
total 4.0K
-r--r--r-- 1 terdon terdon 21 May 29 12:29 foo.sh

$ cat ./foo.sh #!/bin/sh echo Hello

$ ./foo.sh bash: ./foo.sh: Permission denied

$ sh ./foo.sh Hello

As you can see, I cannot actually execute the script, but I can read it—either with cat or with sh—perfectly well.

terdon
  • 242,166
  • That means that terdon does not have the execute permission in your example but the cat programm does? And if yes that's because of the read permission? – Themelis May 29 '20 at 11:34
  • 1
    @Themelis I think you misunderstood. The cat call actually outputs only the content, without executing it. – AdminBee May 29 '20 at 11:35
  • 1
    @Themeli, yes pretty much (although "terdon" is my user, the script is foo.sh). The script doesn't have execute permission, but the program cat does. And it is cat that is being executed, not the script. – terdon May 29 '20 at 11:35
  • 1
    I've also removed read permission and I can see now that cat is not able to read the script thus cannot function. Thanks for the clarifications @AdminBee, @Terdon. – Themelis May 29 '20 at 11:41
  • 5
    Also, note that if the user has read access to the script, they can copy it, and then since they own the copy they can set whatever permissions they want (including execute access) on the copy. – Gordon Davisson May 29 '20 at 21:11
  • Last sentence should say sh not . – user253751 May 30 '20 at 20:12
8

The very short version is: you are not executing the file. You are reading it into the shell, which then executes it.

Note that some language interpreters check the execute permissions of a file they are being asked to execute and will refuse to do so if the user doesn't have appropriate permissions. But that is a completely discretionary check by the author of that particular interpreter, and is not enforced by the operating system.

Let's make a little thought experiment.

I think we can both agree, that if a program has executable permissions, I should be allowed to execute this program. I think we can also agree that this program should be allowed to print the string "Hello World" to the console.

Further, we can probably also agree that a program should be able to read a file as long as that file has read permissions. And it doesn't really matter what the content of that file is, as long as the file has the read permission bit set, the program should be allowed to read it.

Okay, so, since we have agreed that the program should be allowed to print "Hello World" to the console and we have agreed that the program should be allowed to read the file, you must logically also agree that the program should be allowed to read the file and print "Hello World" to the console. And we have also said that whether or not the program is allowed to read the file depends on the permissions, and not on the content, so it should also be allowed to read the file and print "Hello World" to the console if the content of the file is echo Hello World.

Now, can you also agree that since the program is allowed to read the file and since the program is allowed to print "Hello World" to the console, that it is also allowed to check the content of the file and only print "Hello World" to the console if the content of the file is echo Hello World?

Well, but then it is "interpreting", in other words executing the file, and you have just agreed with me on every step of the way that it should be allowed to do that!

And that is exactly what is happening here. The shell is simply reading a text file, which it is allowed to do because the text file has read permissions. And the shell is executing the instructions in the text file, which it is allowed to do because the shell has execute permissions.

  • Good example, thanks! – Themelis May 29 '20 at 22:14
  • 1
    Another point: even if the language interpreter enforces a file's execute permissions, if you can read the file, you can copy it and set the execute permissions on your own copy. In other words, the execute bit is really just a kind of convenience based on various conventions. There are always ways to execute the contents of a file which can be read. – jrw32982 Jun 03 '20 at 17:20
  • @jrw32982supportsMonica: Agreed. In this instance, it's more a protection against your own stupidity, by e.g. making sure to mount untrusted filesystems with noexec. – Jörg W Mittag Jun 03 '20 at 17:34