#!/usr/bin/perl use Data::Dumper; # this script creates users on FreeBSD/OpenBSD, Linux or Solaris, # it's been tested on Solaris 8, many distros of Linux, FreeBSD 4 and 5 # and OpenBSD 3.x # it handles creating a group and password entry, setting up the # homedir if it doesn't exist, and creating ~/.ssh/authorized_keys # or ~/.ssh/authorized_keys2 and setting the modes on .ssh and auth*keys # send: userinfo.pl require "userinfo.pl"; # FreeBSD # /etc/master.passwd # foo:$1$vJ/1lFTb$PblahblabhlahlkjlkO0Qu11:1094:1094::0:0:foo:/home/foo:/bin/sh # /etc/group # foo:*:1094: # Linux # /etc/passwd # foo:x:1094:1094::/home/foo:/bin/zsh # /etc/group # foo:x:1094: # /etc/shadow # foo:$1$xM9Fv4Iv$blahblabhblah.dU0:11298:0:99999:7:-1:-1:134539588 # Login name # Encrypted password # Days since Jan 1, 1970 that password was last changed: int(time()/86400) # Days before password may be changed: 0 # Days after which password must be changed: 99999 # Days before password is to expire that user is warned: 7 # Days after password expires that account is disabled: -1 # Days since Jan 1, 1970 that account is disabled: -1 # A reserved field # Solaris # /etc/passwd # foo:x:1094:1094::/home/foo:/bin/zsh # /etc/group # foo::1094: # /etc/shadow # foo:SO38blahblah:11350:::::: # username:password:lastchg:min:max:warn:inactive:expire:flag # if passwd = '*LK*' then you can't login (if sshd doesn't have # "UseLogin yes" then you can still ssh in with RSA # if passwd = '' then login will ask you for a new password on # first login # if passwd = 'NP' then you can't login with a password, but you # can login with ssh & RSA auth @add = split /,/, join ',',@ARGV; # find possible home dirs $home{root} = "/"; for (qw(/home/ /export/home/)) { -d && do {$home{home} = $_; last; }; } for (qw(/export/home/ /home/)) { -d && do {$home{export} = $_; last; }; } if (!$home{export}) { $home{export} = $home{home}; } unless ($home{home}) { die "/home (or equivalent) must exist"; } print "Home:\n"; for (keys(%home)) { print "$_: $home{$_}\n"; } #if (-d "/homeland/home") { # $home{nfs} = "/homeland/home/"; #} elsif (-d "/omd/home") { # $home{nfs} = "/omd/home/"; # print "Warning: home is on /omd/home"; #} else { # $home{nfs} = $home{home}; #} # find shells and alternates incase a preferred shell isn't installed %shells = ( tcsh => [qw(csh bash sh)], csh => [qw(tcsh bash sh)], zsh => [qw(bash sh)], bash => [qw(zsh sh)], sh => undef, false => undef ); # find shells on this machine for $dir (split /:/, $ENV{PATH}) { for (keys(%shells)) { $shell{$_} = "$dir/$_" if -x "$dir/$_"; } } for (keys(%shells)) { altshell($_,$shells{$_}); } print "Shells: \n"; for (keys(%shells)) { print "$_: $shell{$_}\n"; } sub altshell { my ($sh, $altsh) = @_; my $try = shift @{$altsh}; unless ($shell{$sh}) { die "no altshell for $sh\n" unless $try; $shell{$sh} = altshell($try,$shells{$try}); } } # check that each user has all the required fields for my $name (@add) { for my $field (qw(md5passwd despasswd uid gid shell key)) { die "$name is missing $field" unless defined $users{$name}{$field}; } } # check which OS we're running on $os = `uname`; chomp($os); print "os: $os\n"; # slurp info in from /etc files # update the info with the stuff from $users with names in @add # save the /etc files back out for ($os) { (/OpenBSD/ || /FreeBSD/) && do { my ($master, $morder) = slurp("/etc/master.passwd"); my ($group, $gorder) = slurp("/etc/group"); for my $name (@add) { print "Adding $name\n"; setupfiles($name); push @$morder, $name unless $master->{$name}; $master->{$name} = qq($name:$users{$name}{md5passwd}:$users{$name}{uid}:$users{$name}{gid}\::0:0:$name:$home{$users{$name}{home}}$name:$shell{$users{$name}{shell}}); addgroup($name,$group,$gorder); } save("/etc/group",$group,$gorder); save("/etc/master.passwd",$master,$morder); system("/usr/sbin/pwd_mkdb -p /etc/master.passwd"); }; /Linux/ && do { my ($passwd, $porder) = slurp("/etc/passwd"); my ($shadow, $sorder) = slurp("/etc/shadow"); my ($group, $gorder) = slurp("/etc/group"); for my $name (@add) { print "Adding $name\n"; setupfiles($name); push @$porder, $name unless $passwd->{$name}; push @$sorder, $name unless $shadow->{$name}; $passwd->{$name} = qq($name:x:$users{$name}{uid}:$users{$name}{gid}\::$home{$users{$name}{home}}$name:$shell{$users{$name}{shell}}); my $lastchg = int(time()/86400); $shadow->{$name} = qq($name:$users{$name}{md5passwd}:$lastchg:0:99999:7:-1:-1:); addgroup($name,$group,$gorder); } save("/etc/group",$group,$gorder); save("/etc/passwd",$passwd,$porder); save("/etc/shadow",$shadow,$sorder); }; /SunOS/ && do { my ($passwd, $porder) = slurp("/etc/passwd"); my ($shadow, $sorder) = slurp("/etc/shadow"); my ($group, $gorder) = slurp("/etc/group"); for my $name (@add) { print "Adding $name\n"; setupfiles($name); push @$porder, $name unless $passwd->{$name}; push @$sorder, $name unless $shadow->{$name}; $passwd->{$name} = qq($name:x:$users{$name}{uid}:$users{$name}{gid}\::$home{$users{$name}{home}}$name:$shell{$users{$name}{shell}}); my $lastchg = int(time()/86400); $shadow->{$name} = qq($name:$users{$name}{despasswd}:$lastchg\::::::); addgroup($name,$group,$gorder); } save("/etc/group",$group,$gorder); save("/etc/passwd",$passwd,$porder); save("/etc/shadow",$shadow,$sorder); }; } sub setupfiles { my ($name) = @_; die "setupfiles called without a name" unless $name; if (-d "$home{$users{$name}{home}}$name") { warn "$home{$users{$name}{home}}$name already exists"; } else { mkdir "$home{$users{$name}{home}}$name", 0700 or die $!; } if (-d "$home{$users{$name}{home}}$name/.ssh") { warn "$home{$users{$name}{home}}$name/.ssh already exists"; } else { mkdir "$home{$users{$name}{home}}$name/.ssh", 0700 or die $!; } if (-f "$home{$users{$name}{home}}$name/.ssh/authorized_keys") { warn "authorized_keys already exists, overwriting"; } if (-f "$home{$users{$name}{home}}$name/.ssh/authorized_keys2") { warn "authorized_keys2 already exists, overwriting"; } if (defined $users{$name}{key2}) { open FILE, ">$home{$users{$name}{home}}$name/.ssh/authorized_keys2" || die "open failed $home{$users{$name}{home}}$name/.ssh/authorized_keys2: $!"; print FILE $users{$name}{key2}; close FILE; # } else { # die "no ssh2 key for $name"; } if (defined $users{$name}{key}) { open FILE, ">$home{$users{$name}{home}}$name/.ssh/authorized_keys" || die "open failed $home{$users{$name}{home}}$name/.ssh/authorized_keys: $!"; print FILE $users{$name}{key}; close FILE; # } else { # die "no ssh1 key for $name"; } if (defined $users{$name}{uid} && defined $users{$name}{gid}) { chmod 0600, "$home{$users{$name}{home}}$name/.ssh/authorized_keys"; chmod 0600, "$home{$users{$name}{home}}$name/.ssh/authorized_keys2"; chown $users{$name}{uid},$users{$name}{gid},"$home{$users{$name}{home}}$name", "$home{$users{$name}{home}}$name/.ssh", "$home{$users{$name}{home}}$name/.ssh/authorized_keys", "$home{$users{$name}{home}}$name/.ssh/authorized_keys2"; system('chown','-R',"$users{$name}{uid}:$users{$name}{gid}","$home{$users{$name}{home}}$name"); } else { die "no uid or gid for $name"; } } sub slurp { my ($filename) = @_; my (%h, @ord); open FILE, $filename || die "slurp() open failed $filename: $!"; while () { my ($name) = split /:/, $_; chomp($_); $h{$name} = $_; push @ord, $name; } close FILE; return (\%h, \@ord); } sub save { my ($filename, $h, $order) = @_; open FILE, ">$filename" || die "save() open failed $filename: $!"; for (@$order) { print FILE $h->{$_}."\n"; } close FILE; } sub addgroup { my ($name, $group, $gorder) = @_; push @$gorder, $name unless $group->{$name}; $group->{$name} = qq($name\::$users{$name}{gid}:); } #($name, $passwd, $uid, $gid, $class, $change, #$expire, $gecos, $homedir, $shell) = split /:/, $_;