Normalizing Ownership and Permissions in User Directories
Problem
You want to make make sure your users home directories don contain world-writable directories, directories owned by other users, or other potential security problems.
Solution
Use the etc library to look up a users home directory and UID from the username. Then use Find.find to walk the directory trees, and File methods to check and modify access to each file.
We are looking out for any case where one users home directory can be modified by some other user. Whenever we find such a case, we fix it with a File.chmod or File.chown call. In this program, the actual calls are commented out, so that you don accidentally change your permissions when you just want to test out the program.
#!/usr/bin/ruby -w # normalize_homes.rb require etc require find require optparse def normalize_home(pwd_entry, maximum_perms=0775, dry_run=true) uid, home = pwd_entry.uid, pwd_entry.dir username = pwd_entry.name puts "Scanning #{username}s home of #{home}." Find.find(home) do |f| next unless File.exists? f stat = File.stat(f) file_uid, file_gid, mode = stat.uid, stat.gid, stat.mode
The most obvious thing we want to check is whether the user owns every file in their home directory. With occasional exceptions (such as files owned by the web server), a user should own the files in his or her home directory:
# Does the user own the file? if file_uid != uid begin current_owner = Etc.getpwuid(file_uid).name rescue ArgumentError # No such user; just use UID current_owner = "uid #{file_uid}" end puts " CHOWN #{f}" puts " Current owner is #{current_owner}, should be #{username}" # File.chown(uid, nil, f) unless dry_run end
A less obvious check involves the Unix group that owns the file. A user can let other people work on a file in their home directory by giving ownership to a user group. But you can only give ownership to a group if you e a member of that group. If a users home directory contains a file owned by a group the user doesn belong to, something fishy is probably going on.
# Does the user belong to the group that owns the file? begin group = Etc.getgrgid(file_gid) group_name = group.name rescue ArgumentError # No such group group_name = "gid #{file_gid}" end unless group && (group.mem.member?(username) || group.name == username) puts " CHGRP #{f}" puts " Current group is #{group_name}, and #{username} doesn belong." # File.chown(nil, uid, f) unless dry_run end
Finally, well check each files permissions and make sure they are no more permissive than the value passed in as maximum_perms. The default value of 0775 allows any kind of file except a world-writable file. If normalize_home finds a world-writable file, it will flip the world-writable bit and leave the rest of the permissions alone:
# Does the file have more than the maximum allowed permissions? perms = mode & 0777 # Drop non-permission bits should_be = perms & maximum_perms if perms != should_be puts " CHMOD #{f}" puts " Current perms are #{perms.to_s(8)}, " + "should be #{should_be.to_s(8)}" # File.chmod(perms & maximum_perms, f) unless dry_run end end end
All thats left to do is a simple command-line interface to the normalize_home method:
dry_run = false opts = OptionParser.new do |opts| opts.on("-D", "--dry-run", "Display changes to be made, don make them.") do dry_run = true end opts.on_tail("-h", "--help", "display this help and exit") do puts opts exit end end opts.banner = "Usage: #{__FILE__} [--dry-run] username [username2, …]" opts.parse!(ARGV) # Make sure all the users exist. pwd_entries = ARGV.collect { |username| Etc.getpwnam(username) } # Normalize all given home directories. pwd_entries.each { |p| normalize_home(p, 0775, dry_run ) }
Discussion
Running this script on my home directory shows over 2,500 problems. These are mostly files owned by root, files owned by UIDs that don exist on my system (these come from tarballs), and world-writable files. Below I give a sample of the embarrassment:
$ ruby -D normalize_homes.rb leonardr Scanning leonardrs home of /home/leonardr. CHOWN /home/leonardr/writing/Ruby Cookbook/sys-proctable-0.7.3/proctable.so Current owner is root, should be leonardr CHGRP /home/leonardr/writing/Ruby Cookbook/sys-proctable-0.7.3/proctable.so Current group is root, and leonardr doesn belong. … CHOWN /home/leonardr/writing/Ruby Cookbook/rubygems-0.8.4/lib/rubygems.rb Current owner is uid 501, should be leonardr CHGRP /home/leonardr/writing/Ruby Cookbook/rubygems-0.8.4/lib/rubygems.rb Current group is gid 501, and leonardr doesn belong. … CHMOD /home/leonardr/SORT/gogol-home-2002/mail Current perms are 722, should be 720 …
Running the script as root (and with the File.chmod and File.chown calls uncommented) fixes all the problems.
You can run the script as yourself to check your own home directory, and itll fix permission problems on files you own. But if a file is owned by someone else, you can take it back just because its in your home directorythats part of the problem with having a file owned by someone else in your home directory.
As usual with system administration scripts, normalize.homes.rb is only a starting point. Youll probably need to adapt this program to your specific purposes. For instance, you may want to leave certain files alone, especially files owned by root (who can modify anyones home directory anyway) or by system processes such as the web server (usually user apache, httpd, or nobody).
See Also
- Recipe 2.6, "Converting Between Numeric Bases"
- Recipe 6.2, "Checking Your Access to a File"
- Recipe 6.3, "Changing the Permissions on a File"
- Recipe 6.12, "Walking a Directory Tree"
Категории