Developer

Check home ownership

Perl is powerful and versatile. Take it beyond traditional Web CGI scripting to check the ownership of user home directories.


In this Daily Feature, I’m going to take a look at a Perl script to check the ownership of user home directories. The script itself is pretty basic, but it illustrates some of the power and the useful nature of Perl for many small applications, beyond the traditional Web CGI scripting that many people associate with Perl.

Why check home ownership?
You may be asking yourself what kind of use this script would have for you, be it at home or work. To be quite honest, for the average home user, this script will be next to useless. However, for any system administrator with a larger than average number of users on the system, this may come in quite handy. Imagine, for a moment, this scenario: You were upgrading your server to the latest version of your favorite Linux distribution, and you’ve caused some damage to your system that can only be rectified with a fresh install. If you’re lucky, you’ve remembered to back up your entire /etc/ directory, primarily those files dealing with the users on your system, like /etc/passwd, /etc/groups, /etc/shadow, and so on. But, if you haven’t, you’re stuck in a situation where you have to re-create your users.

Unless you remember the uid and gid values of your users prior to your re-install, you may have a problem. I’ve learned from bitter experience that sometimes re-creating your user base will result in differing uid and gid values from your previous install. This can result in directories being owned by the wrong user or wrong group. The consequences? When logging in, a user may be denied access to his or her own home directory, or refused access to his or her POP3 mailbox. This can make users quite upset.

That may be a rather drastic (and probably far-fetched) example, but it can happen to people who aren’t careful. Maybe this scenario is far more realistic: You were simply hacking away at your system and either removed a user’s home directory or changed the ownership by accident. Or maybe your system suffered a root compromise and someone has maliciously decided to re-assign ownership to a handful of directories. Whatever the reason, having a simple and effective means to check the ownership of user home directories is handy. This is what the following Perl script will accomplish. Let’s simply call the program check.pl. In your favorite editor, create the following Perl script (without the line numbers).

check.pl1: #!/usr/bin/perl
2:
3: require 'stat.pl';
4:
5: while(($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) =
6: getpwent()) {
7: $uids{$uid} = $name;
8: }
9: endpwent();
10:
11: while(($name, $passwd, $uid, $gid, $quota, $comment, $gcos, $dir, $shell) =
12: getpwent()) {
13: $string = "$name\'s home directory is $dir: ";
14: if (!_e $dir) {
15:    $exist = "non_existent\n";
16:    next;
17: } else {
18:    $exist = "exists\n";
19: }
20: $~ = "DIREXIST";
21: write;
22: &Stat($dir);
23: if ($uid != $st_uid) {
24: $string = "$dir is not owned by $name (uid $uid)";
25: $ownuid = "owner: $uids{$st_uid} (uid $st_uid)";
26: $~ = "UIDCHECK";
27: write;
28:    next;
29: }
30: }
31: endpwent();
32:
33: format DIREXIST =
34: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>
35: $string, $exist
36: .
37:
38: format UIDCHECK =
39: @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>>>>>>>>>>>>>>
40: $string, $ownuid
41: .


What it does
Let’s take a closer look at this script and see what it all does. The first line points to your Perl interpreter, in this case it is /usr/bin/perl. The third line uses functions from the stat.pl file, which is a part of your standard Perl kit, so we need to make it available to the program by using the require function.

The fifth line sets up the call to getpwent(), a function that will read a line from the /etc/passwd file and pull out the values for each line. By using a while() statement, we ensure that we go through the entire /etc/passwd file and pull out the entries for each user listed. We assign the values found in the /etc/passwd file to a number of standard variables. The syntax for getpwent() is:
(username, password, userid, groupid, quota, comment, infofield, homedir, shell) = getpwent();

In this case, we assigned the $name variable to the username, the $passwd variable to the password, and so forth. On line 7, we take the value of the $uid and $name variables and create an associative array element with the subscript of the array element being the uid and the value being the username. After we have finished constructing this associative array, we call the endpwent() function, which closes the /etc/passwd file, as seen on line 9.

On lines 11 and 12, we once again call getpwent() to go through the password file, but this time we will perform the actual directory checking. On line 13, we create a string stored in the variable $string which basically says the user’s name and his or her home directory.

On line 14, we check to see whether or not the user’s home directory exists. If it doesn’t exist, we assign the string “non-existent” to the variable $exist. If it does exist, we assign “exists” to the variable $exist. Lines 20 and 21 are an interesting piece, and I will explain them in a moment. Suffice it to say, they print the results of the check.

On line 22, we call the Stat() function on the user’s home directory. This function is a part of the stat.pl file that we required at the beginning of the script. It returns information on the directory. In this case, we are interested in the owner’s uid of that directory, and the Stat() function returns this as the variable $st_uid.

Line 23 compares the uid found in the password file to the uid returned by the Stat() function. If they do not match, we then proceed to line 24, which creates a new $string variable. Line 25 creates a $ownuid variable that contains the name and uid of the owner of the user’s home directory. Remember, the only way this will be called is if the owner’s uid (according to /etc/passwd), and the real owner’s uid (according to the home directory itself) do not match. In a normal scenario, these will match (and they should match for users, whereas they may not match for system accounts).

Lines 26 and 27 are similar to lines 20 and 21 in that they present the results of the test and print it to the screen. Finally, on line 31, we close the password file once again.

Now, we get to find out what lines 20 and 21 are referencing. On line 33, we encounter the format directive. This directive assigns to a name (in this case DIREXIST) a formatting style. Line 34 contains two different strings. The string beginning with the @< characters tells Perl to format the first specified string (on line 35) to the number of < characters following the @ character, and to left-justify it. We also then see a string beginning with the @> characters, which likewise tells Perl to format the second string specified (on line 35), to the number of > characters following the @ character, and to right-justify it. Line 35 simply contains the strings to format, separated by a comma. In this case it is the variables $string and $exist, which we defined on lines 13 and 15 (or 17, depending on whether the home directory existed or not). Line 36 contains a single period, which indicates to Perl that the format definition is complete.

Lines 38 to 41 contain another format definition, this one being UIDCHECK, which is called on lines 26 and 27. Line 26 calls the UIDCHECK format with the defined variables, and line 27 calls the write command, which does the actual printing of the formatted strings to the screen.

Conclusion
This is a relatively simple Perl script, but it illustrates some of the things that can be done with the programming language. Many people have been using Perl for years because of its strength and versatility. Its power is obvious with the very small script we have written, since it performs an important function and does so quickly. The script could be further enhanced to report only missing directories or directories with mismatched ownerships. You could even turn this into a weekly or monthly report that would be automatically e-mailed to the administrator of the server with very little extra work.

Vincent Danen, a native Canadian in Edmonton, Alberta, is an avid Linux "revolutionary" and a firm believer in the Open Source philosophy. He attempts to contribute to the Linux cause in as many ways as possible, from his Freezer Burn Web site to local advocacy in his hometown. Owner of a Linux consulting firm, Vincent is also the security updates manager for MandrakeSoft, creators of the Linux-Mandrake operating system. Vincent is a certified Linux Administrator by Tekmetrics.com.

The authors and editors have taken care in preparation of the content contained herein, but make no expressed or implied warranty of any kind and assume no responsibility for errors or omissions. No liability is assumed for any damages. Always have a verified backup before making any changes.

About Vincent Danen

Vincent Danen works on the Red Hat Security Response Team and lives in Canada. He has been writing about and developing on Linux for over 10 years and is a veteran Mac user.

Editor's Picks