11

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?

  • Regarding the broader conceptual issue: Is there any way to stop a user from creating executables and running them? – Eliah Kagan May 30 at 20:34
  • General rule: if the user has (r) permission on a file that isn't suid or sgid, the (x) permission is advisory. Ways can be found to make it run. – Joshua May 31 at 16:49
  • Yep, I agree @Joshua – Themelis May 31 at 17:04
  • This question might be better renamed something like "why can bash source files that do not have the execute bit set" – ThorSummoner Jun 2 at 18:28
  • 1
    @Themelis I think you are right about the current title being easier to find. – AdminBee Jun 4 at 11:31
27

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.).

| improve this answer | |
  • 4
    Your dot/source examples are not equivalent. . hello.sh will first look for hello.sh in PATH, . ./hello.sh wouldn't. – pizdelect May 29 at 12:01
  • 1
    @pizdelect You are right, I will edit to make that clear. – AdminBee May 29 at 12:15
  • Does bash do a PATH search for sourcing files? I know zsh does (and source and . are slightly different in zsh about what’s is searched first, I think)... – D. Ben Knoble May 29 at 12:53
  • @D.BenKnoble Yes, I tried it, and it does indeed. The other question I linked to also mentions this, albeit only in a comment. – AdminBee May 29 at 12:58
  • 1
    @D.BenKnoble: Surprised me, too. I tested with . man which produces bash: .: /usr/bin/man: cannot execute binary file with Bash 5.0.16 on Arch GNU/Linux. – Peter Cordes May 30 at 8:09
12

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 .—perfectly well.

| improve this answer | |
  • 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 at 11:34
  • 1
    @Themelis I think you misunderstood. The cat call actually outputs only the content, without executing it. – AdminBee May 29 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 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 at 11:41
  • 3
    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 at 21:11
5

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.

| improve this answer | |
  • Good example, thanks! – Themelis May 29 at 22:14
  • 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 supports Monica Jun 3 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 3 at 17:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.