#!/usr/bin/perl -w -T # # dirgroup - Bjarni R. Einarsson, http://bre.klaki.net/. # $Id: dirgroup,v 1.4 2001/03/22 00:44:14 bre Exp $ # # This script will read the named directory (e.g. /etc/group.d) and merge # any changes found there with the contents of /etc/group. The contents # of the directory are favored over those in the /etc/group file when # conflicts arise - as long as the group name, file name and group IDs all # match. # # Read "perldoc dirgroup" for more info. # I especially recommend the INSTALLATION chapter... # use strict; use Fcntl; ### Initialize global variables ### my $gdir = "/etc/group.d"; my $gfile = "/etc/group"; my %dirgroup; my %gn2gid; my %gid2gn; ### Load up contents of group file ### open (GFILE, "<$gfile") || die "Couldn't open group file $gfile: $!\n"; my $line = 0; while () { $line++; addgroup($_, undef, "$gfile, line $line"); } close (GFILE); ### Load up contents of group directory ### opendir (GDIR, $gdir) || die "Couldn't opendir $gdir: $!\n"; my @files = grep { /^[a-z0-9\._-]{1,16}$/ && -f $gdir."/".$_ } readdir(GDIR); closedir(GDIR); foreach my $grp (@files) { open(GFILE, "<$gdir/$grp") || die "Couldn't open group file $gdir/$grp: $!\n"; my $data = join('', grep(!/^(#.*|\s*)$/,)); addgroup($data, $grp, $gdir."/".$grp); close(GFILE); } ### Update group file ### sysopen (GFILE, "$gfile.new", O_RDWR|O_CREAT|O_EXCL) || die "Couldn't open and lock $gfile.new: $!\n"; foreach my $grp (keys(%dirgroup)) { print GFILE $dirgroup{$grp}, "\n"; open (GDFILE, ">$gdir/$grp") || die "Couldn't open $gdir/$grp"; print GDFILE $dirgroup{$grp}, "\n"; close(GDFILE); } # Overwrite old file... link $gfile, "$gfile-"; unlink $gfile; link "$gfile.new", $gfile; unlink "$gfile.new"; close (GFILE); ### Subroutines ### sub addgroup { my $data = shift; my $name = shift; my $source = shift; chomp $data; if (($data =~ /^([a-z0-9\._-]{1,16}):[A-Za-z0-9\*]*:(\d+):([a-z0-9\._-]{1,16}(,[a-z0-9\._-]{1,16})*)?$/) && ((!defined($name)) || ($name eq $1))) { my ($gn, $gid) = ($1, $2); if ( ((!$gid2gn{$gid}) || ($gid2gn{$gid} eq $gn)) && ((!$gn2gid{$gn}) || ($gn2gid{$gn} eq $gid))) { $gn2gid{$gn} = $gid; $gid2gn{$gid} = $gn; $dirgroup{$gn} = $data; } else { print STDERR "Group ID or name clash in $source. Skipped.\n"; } } else { print STDERR "Invalid group definition in $source. Skipped.\n"; } } =head1 NAME dirgroup - a script for merging the contents of /etc/group.d into /etc/group. =head1 SYNOPSIS dirgroup =head1 README This is a script for merging the contents of a directory, /etc/group.d, into the file /etc/group. Storing group information in a directory allows users other than root to own and manage Unix groups. This minor change to Unix permission semantics provides many of the advantages of much more complex ACL systems. =head1 DESCRIPTION Normal Unix permissions are fine for keeping people out of your private files - unfortunately things tend to get more complicated when you want to grant access to only one or two people. The only way to do this in traditional Unix is to have the system administrator create a group for you and your friends, and then assign group ownership of the file to that group. This isn't very efficient, since few system administrators will want to be bothered with creating little custom groups for everyone. In practise nobody bothers, and groups aren't really used very much. This script allows system administrators to safely give normal users permission to create and modify their own groups, thus making the whole group concept much more useful and usable. =head1 USAGE Create and modify groups in /etc/group.d using your favorite editor or a more user-friendly tool which has yet to be written. Don't forget to add yourself to the group! Run B to merge your modified group into /etc/group. Log out, log in, have fun with your new group. =head1 FILE FORMATS The format of a group entry is something like this: group-name:password:group-number:user1,user2,user3, ... See the group(5) man page for more information. Usually people don't use passwords on groups - but it is very important not to leave that field empty (it should contain an x or a *), since an empty password means anyone is allowed to join the group using the B(1) command. =head1 COMMUNITY All open source programs need user communities, so I've created a mailing list for B. If you have suggestions, bug reports or other contributions, please send mail to Edirgroup@molar.isE.. Archives and a subscription form are at http://www.molar.is/en/lists/dirgroup/ =head1 INSTALLATION To install B, I recommend simply copying the script to /usr/bin, /usr/local/bin or wherever you store third-party binaries. Next you create the /etc/group.d directory and populate it with the current contents of /etc/group. Finally you modify the permissions of /etc/group and the B script to allow normal users to create and modify their own groups. A typical installation sequence looks like this: $ cp dirgroup /usr/bin $ mkdir /etc/group.d $ dirgroup $ chmod ugo+rwxt /etc/group.d $ chmod u+s /usr/bin/dirgroup Optionally, you may prefer not to make B setuid root, but instead invoke it from cron every once in a while. Note to packagers: Running B to populate /etc/group.d B assigning granting global write access to /etc/group.d is very important to protect the current group structure from race-based attacks during installation. =head1 KNOWN BUGS Users will need to log in and out or use the newgrp command to see new or modified groups. =head1 SCRIPT CATEGORIES UNIX/System_administration =head1 AUTHOR AND COPYRIGHT B was written by Bjarni R. Einarsson Ebre@klaki.netE. This script is hereby released to the Public Domain. New versions will be put here: http://bre.klaki.net/programs/dirgroup/ =head1 SEE ALSO newgrp(1), group(5), groupadd(8), groupdel(8), groupmod(8) =cut __END__