71

I accidently ran

sudo chmod 755 -R /

instead of

sudo chmod 755 -R ./

I stopped it after a few seconds, but now I'm starting to have problems. For example, sudo isn't working anymore:

sudo: must be setuid root

How can I revert permissions back?

fl00r
  • 1,383

12 Answers12

72

In short: you can't, reinstall your system.

I mean, Posix permissions are used and relied on heavily; there's a multitude of places in the filesystem where wrong permissions would break the OS (SUID flags) or even worse, make it exposed security-wise (/etc/ssh/ssh_host_rsa_key) while it appears to be working OK.

Hence, such a recovery is hard to do properly. Miss one thing — and you screw it up. You already screwed up your sudo chmod command (if that's your friend rather than you, she might as well learn some Linux lesson, too) — and that's a very simple of a command. Proper recovery would demand way more commands and way more vigilance. Even if you use someone's script.

So trust me, just reinstall. It's a safe bet and guaranteed to keep you out of trouble.


Finally, some tips relevant here.

First: reinstalls will be less painful if you setup your /home on a separate partition next time. Actually, they will be a breeze.

Second: consider doing crazy Linux science in a virtual machine like the VirtualBox, and do your snapshots.

Third: chmod -R . works. A dot by itself . is valid directory name. There's no real need to append that slash. You could've avoided the catastrophic risk of skipping the dot entrirely;
mere chmod: missing operand after ‘755’ VS a ruined system.

ulidtko
  • 5,988
29

I wrote and have been using for several years a couple of Ruby scripts to rsync permissions and ownership. Script get-filesystem-acl collects all the information by recursively traversing all the files and puts it all into the file .acl. Script .acl-restore will read .acl and apply all the chown's and chmod's.

You can run get-filesystem-acl on a similar Ubuntu installation and then copy over the .acl file to your chmod-damaged box, put .acl and .acl-restore in /, and run .acl-restore.

You will need to have root so fix your sudo as Marco Ceppi suggested.

I can generate and give you the .acl file for my Ubuntu.

get-filesystem-acl

#!/usr/bin/ruby

RM   = "/bin/rm"
SORT = "/usr/bin/sort"
TMP  = "/tmp/get_acl_#{Time.now.to_i}_#{rand * 899 + 100}"

require 'find'

IGNORE = [".git"]

def numeric2human(m)
  return sprintf("%c%c%c%c%c%c%c%c%c",
            (m & 0400 == 0 ? ?- : ?r),
            (m & 0200 == 0 ? ?- : ?w),
            (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
                             (m & 04000 == 0 ? ?x : ?s)),
            (m & 0040 == 0 ? ?- : ?r),
            (m & 0020 == 0 ? ?- : ?w),
            (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
                             (m & 02000 == 0 ? ?x : ?s)),
            (m & 0004 == 0 ? ?- : ?r),
            (m & 0002 == 0 ? ?- : ?w),
            (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
                             (m & 01000 == 0 ? ?x : ?t)))
end


File.open(TMP, "w") do |acl_file|

  # TODO: Instead of the current dir, find the .git dir, which could be
  #       the same or outside of the current dir
  Find.find(".") do |path|

    next if IGNORE.collect {|ig| !!(path[2..-1] =~ /\A#{ig}/)}.include? true
    next if File.symlink?(path)

    stat = File.lstat(path)
    group_id = stat.gid
    rules    = "#{type}#{numeric2human(stat.mode)}" 

    acl_file.puts "#{path} #{rules} #{owner_id} #{group_id}"
  end
end

`#{SORT} #{TMP} > .acl`
`#{RM}   #{TMP}`

.acl-restore

#!/usr/bin/ruby

# This script will only work with .acl_ids

# Restore from...
FROM  = ".acl"

MKDIR = "/bin/mkdir"
CHMOD = "/bin/chmod"
CHOWN = "/bin/chown"
known_content_missing = false


def numeric2human(m)
  return sprintf("%c%c%c%c%c%c%c%c%c",
            (m & 0400 == 0 ? ?- : ?r),
            (m & 0200 == 0 ? ?- : ?w),
            (m & 0100 == 0 ? (m & 04000 == 0 ? ?- : ?S) :
                             (m & 04000 == 0 ? ?x : ?s)),
            (m & 0040 == 0 ? ?- : ?r),
            (m & 0020 == 0 ? ?- : ?w),
            (m & 0010 == 0 ? (m & 02000 == 0 ? ?- : ?S) :
                             (m & 02000 == 0 ? ?x : ?s)),
            (m & 0004 == 0 ? ?- : ?r),
            (m & 0002 == 0 ? ?- : ?w),
            (m & 0001 == 0 ? (m & 01000 == 0 ? ?- : ?T) :
                             (m & 01000 == 0 ? ?x : ?t)))
end

def human2chmod(mode)
  raise unless mode =~ /([r-][w-][xtsTS-])([r-][w-][xtsTS-])([r-][w-][xtsTS-])/
  triple = [$1, $2, $3]
  u,g,o = triple.collect do |i|
    i.sub('s', 'sx').sub('t', 'tx').downcase.gsub('-', '')
  end

  return "u=#{u},g=#{g},o=#{o}" 
end



File.open(FROM).each do |acl|
  raise unless acl =~ /\A(([^ ]*? )+)([^ ]+) ([^ ]+) ([^ ]+)\Z/
  path, rules, owner_id, group_id = $1, $3, $4, $5
  path = path.strip
  owner_id = owner_id.to_i
  group_id = group_id.to_i

  if !File.exists?(path) and !File.symlink?(path)
    if rules =~ /\Ad/
      STDERR.puts "Restoring a missing directory: #{path}"
      STDERR.puts "Probably it was an empty directory. Git goes not track them."
      `#{MKDIR} -p '#{path}'` # Creating the any parents
    else
      known_content_missing = true
      STDERR.puts "ERROR: ACL is listed but the file is missing: #{path}"
      next
    end
  end

  s = File.lstat(path)
  t = s.ftype[0..0].sub('f', '-') # Single character for the file type
                                  # But a "-" istead of "f"

  # Actual, but not neccesarely Desired 
  actual_rules    = "#{t}#{numeric2human(s.mode)}"
  actual_owner_id = s.uid 
  actual_group_id = s.gid 

  unless [actual_rules, actual_owner_id, actual_group_id] ==
    [rules, owner_id, group_id]

    chmod_argument = human2chmod(rules)

    # Debug
    #p chmod_argument
    #p s.mode

    ## Verbose
    puts path
    puts "Wrong: #{[actual_rules, actual_owner_id, actual_group_id].inspect}"
    puts "Fixed: #{[rules, owner_id, group_id].inspect}"
    `#{CHMOD} #{chmod_argument} '#{path}'`

    #puts
  end

end

if known_content_missing
  STDERR.puts "-" * 80 
  STDERR.puts "Some files that are listed in #{FROM.inspect} are missing in " +
              "the current directory."
  STDERR.puts
  STDERR.puts "Is #{FROM.inspect} outdated?"
  STDERR.puts "(Try retrograding the current directory to an earlier version)"
  STDERR.puts
  STDERR.puts "Or is the current directory incomplete?"
  STDERR.puts "(Try to recover the current directory)"
  STDERR.puts "-" * 80 
end
muru
  • 207,228
14

In long: you can. You'll need to mount the the file system from the a Live CD and begin reverting the permissions in the appropriate places. At a minimum to get sudo back you'll want to run sudo chmod u+s /usr/bin/sudo while in the LiveCD session - that will fix the must be setuid root.

However, it would likely be easier to simply reinstall the system.

Marco Ceppi
  • 48,827
5
How to prevent future accidents

All answers here focus (predictably) on restoring the system to a working state. But no answer focuses on how to better prevent a similar accident in the future.

To completely prevent accidents like this in the future, add the following to your ~/.bashrc (or ~/.bash_aliases) file:

# set Safetynets
alias sudo='sudo '    # This allows sudo to run aliases
alias chmod='chmod --preserve-root'
alias chown='chown --preserve-root'
alias chgrp='chgrp --preserve-root'

Also, make sure the same contents are inside the ~/.bashrc (or ~/.bash_aliases) file of the root user as well (else it doesn't matter!).

In this way, you CAN NOT use these commands (even as root) recursively on the root filesystem.

Adding this to ~/.bashrc

Add the following line to your own ~/.bashrc:

echo "alias sudo='sudo '    # This allows sudo to run aliases" >> ~/.bashrc

An easy way to add lines to roots ~/.bashrc is to use a "heredoc". First switch to the root user:

sudo -s

Then run the following text block:

cat << EOT >> ~/.bashrc

set Safetynets

alias sudo='sudo ' # This allows sudo to run aliases alias chmod='chmod --preserve-root' alias chown='chown --preserve-root' alias chgrp='chgrp --preserve-root' EOT

Artur Meinild
  • 31,035
4

I would try to reinstall all packages with apt-get install --reinstall, possibly using the output of dpkg --get-selections | grep install to get a list of them.

Adam Byrtek
  • 9,861
4

Alright, I haven't tested this (so use at your own risk), but it still might work. I Will test this in a virtual machine when I get the chance to:

First, in a still working system, I did the following to get all file permissions in a list, skipping the /home/ directory:

sudo find / -not -path /home -printf "%m:%p\0" > /tmp/fileper.log

This will print the permissions and file name for each file or directory on the system, followed by a \0 character (this is needed later on to deal with weird file names such as those containing newlines).

Then, on a system where the file permissions have been compromised:

while IFS=: read -r -d '' perm file; do  
    chmod "$perm" "$file"
done < /tmp/fileper.log 

This will read each line of fileper.log, saving the permissions as$perm and the file name as $file and then will set the file (or directory's) permissions to whatever was listed in the fileper.log


A few things to note here:

  • While outputting to the file: /tmp/fileper.log, you might be listing custom settings, and proc, etc.
  • you might not be able to boot, or run commands,

What I would suggest is boot up a LiveCD with the Linux version you have on your disk, run the command, modify the path to where you have the local disk mounted, and run the second command!


I have tested that when booted from an Ubuntu CD/USB, I can choose not to format disk, meaning it will replace everything in the / directory, BUT skip the /home/ directory. Meaning your users will have the configuration of apps/DATA(Music,Video,Documents) still intact. And by replacing the system files, the chmod is set to there proper number.

blade19899
  • 26,994
4

You can try restoring permissions with apt-get.

If you can not run these commands with sudo you may need to boot to recovery mode and run them as root.

For booting to recovery mode see https://wiki.ubuntu.com/RecoveryMode.

From http://hyperlogos.org/page/Restoring-Permissions-Debian-System

Note: This was originally posted on the Ubuntu Forums but I can not find the original post.

Try, in order,

sudo apt-get --reinstall install `dpkg --get-selections | grep install | grep -v deinstall | cut -f1`

If that fails:

sudo apt-get --reinstall install `dpkg --get-selections | grep install | grep -v deinstall | cut -f1 | egrep -v '(package1|package2)'`

And finally, as a last resort,

sudo dpkg --get-selections | grep install | grep -v deinstall | cut -f1 | xargs apt-get --reinstall -y --force-yes install

Using apt-get

Here's the relevant snip, EDITED FOR CORRECTNESS and reformatted:

sudo apt-get --reinstall install `dpkg --get-selections | grep install | grep -v deinstall | cut -f1`

Let's say you get messages about some packages that can't be reinstalled, and the command fails. Here's one way to fix it by skipping the packages in question:

sudo apt-get --reinstall install `dpkg --get-selections | grep install | grep -v deinstall | cut -f1 | egrep -v '(package1|package2)'`

And finally, if you should somehow have so many things installed that the above command fails saying your argument list is too long, here's the fix, which will run apt-get many more times than you might like:

sudo dpkg --get-selections | grep install | grep -v deinstall | cut -f1 | xargs apt-get --reinstall -y --force-yes install

Note the -y and --force-yes options, which will stop apt-get from prompting you over and over again. These are always fun options, if you're sure you know what you're doing.

Eliah Kagan
  • 119,640
Panther
  • 104,528
3

There is a closed question (with a great answer) as duplicate of this one from:

As such I will give Terdon the credit but here is my own answer based his.

During development of a new script, the variable $TMP_DIRECTORY/ had unexpected result of /. This was followed by chmod 700 and chown -R $UDO_USER pointing to $TMP_DIRECTORY which was really /.

System slowly crashed and burned. After that it would not reboot. Everyone screams "YOU MUST REINSTALL" but that means loosing years of work. This script is a variation of Terdon's answer:

#!/bin/bash

if [[ $(id -u) != 0 ]]; then # root powers needed to call this script
    echo >&2 "$0 must be called with sudo powers"
    exit 1
fi

# Parameters blank?
[[ $1 == "" ]] && echo "Parm 1 must be source directory" && exit 1
[[ $2 == "" ]] && echo "Parm 2 must be target directory" && exit 1

# Parameters valid directories?
[[ ! -d "$1" ]] && echo "Parm 1 must be a directory" && exit 1
[[ ! -d "$2" ]] && echo "Parm 2 must be a directory" && exit 1
# [[ ! "$2" == "/mnt/"* ]] && echo "Parm 2 must start with /mnt/..." && exit 1

# shopt -s globstar     # From original
shopt -s dotglob
liveSystem="$1"
deadSystem="$2"

# Parameters must end in same subdirectory
[[ "${liveSystem##*/}" != "${deadSystem##*/}" ]] && \
    echo "subdirectory ${liveSystem##*/} not same as ${deadSystem##*/}" && \
    exit 1

cd "$deadsystem"
Count=0
NoRef=0
one() {
    path="$1"
    MountlessPath="${path##*$deadSystem/}"
    if [ -e "$liveSystem/$MountlessPath" ] ; then
        echo chown --reference "$liveSystem/$MountlessPath" "$path"
        echo chmod --reference "$liveSystem/$MountlessPath" "$path"
        (( Count++ ))
    else
        echo "No reference for: $path"
        (( NoRef++ ))
    fi
}

# From: https://stackoverflow.com/questions/51654041/loop-through-all-files-in-a-directory-and-subdirectories-using-bash
# Note typo "If" instead of "if" in above link
recurse() {
  path="$1"
  if [ -d "$path" ] ; then
#     echo "Path: $path"
     one "$path"            # Enhancement: chown of directories too
     for i in "$path/"* # "$path/."* # Enhancement: do hidden directories
     do
        # Skip over .. which gives endless loop
        [[ "$path" == *".cache/speech-dispatcher"* ]] && continue
        [[ "$path" == *"/usr/bin/X11"* ]] && continue
        BaseName=${path##*/}
        [[ "${#BaseName}" -gt 300 ]] && continue
#        [[ ${#BaseName[@]} -gt 1 ]] && continue
#        echo BaseName: $BaseName
#        [[ $BaseName == ".." || $BaseName == "*" ]] && continue
        recurse "$i"
     done
# Original code selected only files but we want links too
#  elif [ -f "$path" ] ; then
  else
    [ ! -f "$path" ] && printf "Not a file -> "
    one "$path"
  fi
}

recurse "$deadSystem"

echo "========================================================================"
echo "$Count references found for chown command to process"
echo "$NoRef files, directories and links, etc. could not be processed"
# From: https://unix.stackexchange.com/questions/193368/can-scp-create-a-directory-if-it-doesnt-exist/193372?noredirect=1#comment1111576_193372
# Original didn't have much luck?
#for file in **/*; do 
#    [ -e "$liveSystem/$file" ] &&
#        echo sudo chown --reference "$liveSystem/$file" "$file"
#done

When running the script with $ sudo reset-owner /mnt/old/var /var | grep "No reference" the tail end says this:

Not a file -> No reference for: /var/tmp/systemd-private-fdbc599820b645f4a461404675af3b5d-rtkit-daemon.service-jvV8tG/tmp/*
No reference for: /var/tmp/systemd-private-fdbc599820b645f4a461404675af3b5d-systemd-timesyncd.service-vmgqTh
No reference for: /var/tmp/systemd-private-fdbc599820b645f4a461404675af3b5d-systemd-timesyncd.service-vmgqTh/tmp
Not a file -> No reference for: /var/tmp/systemd-private-fdbc599820b645f4a461404675af3b5d-systemd-timesyncd.service-vmgqTh/tmp/*
No reference for: /var/tmp/uefi.dat
No reference for: /var/tmp/uefi.dsl
========================================================================
12369 references found for chown command to process
7676 files, directories and links, etc. could not be processed

As daunting as it seems it only took an hour to work it all out. By the time I was done I was left with this:

$ sudo reset-owner /mnt/old/var /var | grep "No reference" | grep -v /var/tmp/ | grep -v /var/spool/ | grep -v '\/\*' | grep -v /var/run | grep -v /unattended | grep -v /upstart | grep -v /log/journal | grep -v /log/pm | grep -v /log/cron | grep -v /log/dpkg | grep -v /log/apt | grep -v /log/altern | grep -v /lib/u | grep -v /lib/systemd | grep -v /lib/snapd | grep -v /lib/shim | grep -v /lib/NetworkManager | grep -v /lib/lightdm | grep -v /lib/initram | grep -v /lib/dpkg | grep -v /lib/doc-base | grep -v /var/lib/dkms | grep -v /lib/blue | grep -v /lib/binfmts | grep -v /lib/apt | grep -v /lib/app-info | grep -v /lib/Accounts | grep -v /var/crash | grep -v /var/cache
No reference for: /var
No reference for: /var/lib/flashplugin-installer
No reference for: /var/lib/gems
No reference for: /var/lib/gems/2.3.0
No reference for: /var/lib/git
No reference for: /var/lib/tlp/rfkill-saved
3

(I know I shouldn't comment in an answer, but not enough reputation to comment.)

blade19899's answer worked for me except for symlinks. E.g. it applied 755 to /bin/bash, but then applied 777 to the symlink /bin/rbash, effectively 777-ing /bin/bash.

As I already had the fileper.log file, I just modified the destination-end command:

while IFS=: read -r -d '' perm file; do  
    if [[ ! -L "$file" ]]; then    
        chmod "$perm" "$file"
    fi
done < /tmp/fileper.log 
Marjan
  • 139
0

For packages, the easiest, fastest, and more stable option that I know of is this Aptitude command below.

In summary, this command automatically restore a package permissions to their default. Including both all its default folder(s) & file(s). The end result is as if the package was freshly installed for the first time with its default permission. While at the same time keeping any already existing configuration file(s).

Three Steps

Step 1

Using Terminal/Console, connect into the appropriate Linux user account. This is important because in both steps 2 & 3 below all commands will be executed as this user.

Step 2

If not already done, using Terminal execute this command to install Sudo

apt install sudo

Note: For those not familiar with Sudo, allow a Linux user(s) limited super user privileges

If not already done, using Terminal execute this command to install Aptitude

sudo apt install aptitude

Note: For those not familiar with Aptitude, it is a package manager. Which is similar to Apt. Aptitude has more functionality than Apt. Aptitude includes both an interactive UI and a text-only interface/CLI. In comparison, Apt lacks an interactive UI. Aptitude is a high-level package manager while Apt is lower-level package manager.

Step 3

Choose one of the four options below. Which will reset the package permissions to their default. And keep the package(s) configuration:

Option 1: ONE package. WITHOUT Backport.

  • Format: sudo aptitude reinstall <PACKAGE NAME>

  • Example: sudo aptitude reinstall jami

Option 2: ALL packages. WITHOUT Backport.

  • Example: sudo aptitude reinstall '~i'

Note

  • This option above will both reinstall ALL already installed packages and reset their permissions to their default. Including your operating system (OS). Before executing this command, it is suggested to complete the appropriate actions. Such as, but not limited to, create a backup, check that you have enough available locale storage. Time needed for this command is similar to installing a new OS.

Option 3: ONE package. WITH Backport.

  • Format: sudo aptitude -t <CODE NAME DEBIAN>-backports reinstall <PACKAGE NAME>

  • Example: sudo aptitude -t buster-backports reinstall jami

Note

Option 4: ALL packages. WITH Backport.

  • Format: sudo aptitude -t <CODE NAME DEBIAN>-backports reinstall '~i'

  • Example: sudo aptitude -t buster-backports reinstall '~i'

Note

  • This option above is for Debian 10 Buster. If you use Ubuntu, you would need to adapt this command. Related documentation at https://help.ubuntu.com/community/UbuntuBackports Note

  • This option above will both reinstall ALL already installed packages and reset their permissions to their default. Including your operating system (OS). Before executing this command, it is suggested to complete the appropriate actions. Such as, but not limited to, create a backup, check that you have enough available locale storage. Time needed for this command is similar to installing a new OS.

Notes about all four options above

  • Depending on the package, most of the time, those commands above will reset the permissions only on the package folder(s) & file(s). Other files, such as configuration file(s) may not be affected. In other words, the configuration file will remains unaffected. If needed, and if appropriate you could manually reset the permissions on the configuration file(s) only. Often those configurations file(s) are stored at a different location. For example, but not limited to, somewhere into the user home folder. If unsure, find each package documentation.

Attribution

Thanks to this Debian wiki for those options above at https://wiki.debian.org/Permissions/ResetPackagePermissions

-1

I did the same mistake by accident and with these steps my problem was solved.

I was logged out from root and I couldn't run commands with root permissions, and I was working on debian 10.

1-Start or re-start your computer and press ESC(in some machines, press and hold Shift key) to show the Grub boot menu.

2-Choose "Advanced Options for..." from menu and boot the kernel entry with(recovery mode)

3-It gives you a root shell if you have your root password, so you can run these commands

chmod -R 755 /bin /boot /dev /etc/ /home /lib /lib64 /media /mnt /opt /run /sbin /srv /usr /var

chmod -R 777 /initrd.img /vmlinuz

chmod -R 1777 /tmp

chmod -R 555 /sys

chmod -R 555 /proc

chmod -R 700 /root

4-If you get an error when you want to log in as root user, you can run chmod +s /bin/su and check the result with ls -lt /bin/su
the result should be like this -rwsr-sr-x instead of -rwxr-sr-x

I hope it helps.

-1

I did the same thing. In short. I did not reinstall my system. When I noticed that it is running not under all files under the current directory I immediately canceled my command. Then I saw I CANNOT RUN sudo!

Whenever I try to run sudo I get below error message.

sudo: /etc/sudoers is world writable 
sudo: no valid sudoers sources found, quitting
sudo: unable to initialize policy plugin

When I googled I found https://www.thegeekdiary.com/sudo-etc-sudoers-is-world-writable-how-to-correct-the-permissions-of-sudoers-file/

basically, it says to execute chmod 440 /etc/sudoers as root. How can I be root if I can't type sudo?

To do that I go into recovery mode in Ubuntu like https://askubuntu.com/a/92558/488768. From there I log in as root then I executed chmod 440 /etc/sudoers Then I can execute sudo. When I execute a command with sudo I get another warning saying sudo: /etc/sudoers.d is world writable I executed chmod 440 /etc/sudoers.d to fix that.

After that, I see many weird things such as I cannot do SSH on my machine. I deleted the "OpenSSHD" program and then reinstalled it. When I delete and reinstall I see many warnings

WARN: /etc/default/ufw is group writable!
WARN: /etc/default is group writable!
WARN: /etc is group writable!
WARN: / is group writable!
WARN: /lib/ufw/user.rules is group writable!
WARN: /lib/ufw is group writable!
WARN: /lib is group writable!
WARN: /etc/ufw/applications.d is group writable!
WARN: /etc/ufw is group writable!
WARN: /etc/ufw/after6.rules is group writable!
WARN: /lib/ufw/ufw-init is group writable!
WARN: /etc/ufw/ufw.conf is group writable!
WARN: /lib/ufw/user6.rules is group writable!
WARN: /etc/ufw/before6.rules is group writable!
WARN: /etc/ufw/after.rules is group writable!
WARN: /etc/ufw/before.rules is group writable!
WARN: /usr/sbin/ufw is group writable!
WARN: /usr/sbin is group writable!
WARN: /usr is group writable!

Then I followed the suggestions of How to fix permissions warnings in the root folder?

Then I still see my server name is changed. So set the name in "/etc/hosts" Then I still cannot SSH to the given name of my server. Then we changed the IP address back to the previous one. I don't know how IP address changed.

canbax
  • 99