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"


Strings

Numbers

Date and Time

Arrays

Hashes

Files and Directories

Code Blocks and Iteration

Objects and Classes8

Modules and Namespaces

Reflection and Metaprogramming

XML and HTML

Graphics and Other File Formats

Databases and Persistence

Internet Services

Web Development Ruby on Rails

Web Services and Distributed Programming

Testing, Debugging, Optimizing, and Documenting

Packaging and Distributing Software

Automating Tasks with Rake

Multitasking and Multithreading

User Interface

Extending Ruby with Other Languages

System Administration



Ruby Cookbook
Ruby Cookbook (Cookbooks (OReilly))
ISBN: 0596523696
EAN: 2147483647
Year: N/A
Pages: 399

Flylib.com © 2008-2020.
If you may any questions please contact us: flylib@qtcs.net