Takacs Istvan wrote:> Hi,
>
> I want to switch from our Windows NT server
> ( which works as our PDC ) to Linux - Samba.
>
> Could you advice a step-by-step guide about
> this process, or is that possible, at all?
I've bene working on this for a while - it is possible, with a few
caveats. Here's a HOWTO I started putting together months ago - it
remains unfinished due to lack of time, but it's a start.
I was trying to get this to a "finished" state, with a docbook
version,
and then submit it to the Samba team, but at this stage I think it's
better to throw it out there now to all interested parties - then even
if I don't finish it, it'll hopefully be useful to people.
> Can I use Samba as a BDC, convert the user
> DB and than promote it to PDC?
No. Samba does not currently have BDC support.
To make the migration process easier, I've written a perl script which
will take the smbpasswd created from the NT SAM, the unix passwd, group,
and shadow file, and remap all the UID's as necessary. It also generates
a shell script that can be used to change file ownerships to the new
UID's. I've used it successfully, but it's provided as is, and I
take no
resposibility for anyone who hoses their system with it. Screwing around
with UID's without fully understanding what you're doing can damage your
system's health.
It is my hope that these things are useful to people, and that they can
be improved further - I'd like to see a point and shoot tranparent
domain migration tool out there at some point.
Mike.
P.S. There is one notable error in my little HOWTO - newer versions of
Samba do not pur the domain SID in MACHINE.SID. However, they will still
*read* a MACHINE.SID file, and import it into secrets.tdb, so the
procedure outlines still works. Thanks to Simo Sorce for pointing this
out to me.
-------------- next part --------------
Samba PDC Transparent Migration HOWTO
General Information
==================a/ Who is this document for
This HOWTO is aimed at administrators working on a network controlled by a
Windows NT 4 Primary Domain Controller. Readers may already be using Samba to
provide services on their network, or may be looking to implement a Samba PDC to
reduce costs, or to increase reliability or ease of management.
b/ Issues to consider
The work involved in moving an existing NT controlled infrastructure to one
where some or all of the network services are provided by Samba is very
different to the job of setting up a new Samba controlled network from scratch.
Emphasis must be placed on transparency of migration - movement to the new
system should be accomplished with the absolute minimum of interference to the
working habits of users, and preferably without those users even noticing that
it has happened.
Sites that make heavy use of NT and NT features would be well advised to
consider a gradual migration path. A Samba server should be joined to the
existing NT domain, and networked printing and file sharing moved to this
machine before PDC migration is even attempted. Sites that make heavy use of NT
groups, ACL's, logon hours, and similar features will want to replicate
these capabilities on Samba one feature at a time, rather than all at once.
Samba will control a simple domain well at the current time. However,
inter-domain trust relationships, BDC support, and the NT GUI user
administration tools are not supported at this time.
c/ Software
The release of Samba 2.2 provides users with a stable platform that provides
enough features to replace an existing NT Domain Controller. This HOWTO will
focus on that software, though there are several other packages that can
significantly ease the job of migration.
Environment
==========This HOWTO was developed using a test environment, and in places,
machines are referred to. They are
VICTORIA - an NT4 server configured as a Primary Domain Controller, for the
LONDON domain.
MARKAB - a RedHat Linux 7.1 machine running Samba 2.2.0a
ALNIYAT - an NT4 workstation configured as a member of the LONDON domain.
Administrator - The LONDON domain Administrator account
admin1 - A second domain Administrator
user1 - A user.
user2 - "
user3 - "
user4 - "
Installing your Samba PDC
========================Start by installing a Samba 2.2 machine configured as a
member server. Migrate file storage and printers first.
Extracting the domain SID
========================When Samba is set up as a member server, it must be
allocated a machine trust account in the domain. This is done by using the
Server Manager program to create the account on the PDC, and then using the
smbpasswd program to negotiate the initial password change for this machine
account.
This process causes three changes to occur:-
* the machine trust account password in the NT PDC's SAM database is
changed
* a file called MACHINE.SID is created that contains the machine SID for the
newly joined samba server
* a file called secrets.tdb is created that contains the machine trust account
password for the samba server
The secrets.tdb file will be unneccessary when the samba server takes over the
role of domain controller. The MACHINE.SID file will also require changing to
the domain SID at that stage.
At this stage, you should extract the domain SID for the domain you wish to take
over. The domain SID for an NT domain is simply the machine SID of the PDC. You
need to extract this from the existing NT PDC, so that the Samba server can be
reconfigured with this SID when it takes over control of the NT domain.
The domain SID can be extracted with the rpcclient
utility, provided with Samba:-
[root@markab filestore]# rpcclient VICTORIA -U administrator
Enter Password:
session setup ok
Domain=[LONDON] OS=[Windows NT 4.0] Server=[NT LAN Manager 4.0]
rpcclient $> lsaquery
domain LONDON has sid S-1-5-21-1363377815-237862100-1307212239
rpcclient $> quit
Migrating users, passwords and machine trust accounts
====================================================To transparently migrate
users, all user information must be migrated from the NT PDC to the Samba
machine as seamlessly as possible. The information to be migrated is:-
User account details - the username, real name and various flags associated with
the user.
User passwords - necessary for any degree of transparency in the migration
Machine trust accounts - these must be migrated in order for the machine to
remain a member of the domain. If they are not migrated correctly, the Samba
server will not recognize the workstations as domain members.
All the above information is stored in the NT SAM database, on the PDC. To
extract this information from the SAM, and translate it into a format that Samba
can read, use the pwdump2 utility from http://www.webspan.net/~tas/pwdump2/ .
Running this on the PDC will dump the necessary information from the SAM in the
correct format for use as an smbpasswd file. On the test system described, the
output of this was the following file.
admin1:1007:11d6cfe00976602e1b9643e5b4ea1793:64520100893d15a3fbc53534f627522d:::
Administrator:500:05ba5dfe13b27dfa25ad3b83fa6627c7:b531ee2ba55d6e54f04e394754d4f687:::
ALNIYAT$:1004:aad3b435b51404eeaad3b435b51404ee:6829f8e013136fed4b61faf0c20a4a65:::
MARKAB$:1006:aad3b435b51404eeaad3b435b51404ee:ef0cf14884d933c02f544236adac6995:::
user1:1008:0f20048efc645d0a179b4d5d6690bdf3:1120acb74670c7dd46f1d3f5038a5ce8:::
user2:1009:0f20048efc645d0af7b8da23b20d7ffe:4f597a08786530135e227ac1a579a54c:::
user3:1010:0f20048efc645d0a468aa0df9e2394c4:4bce2b1108fbf76264778e2d20f8cbad:::
user4:1011:0f20048efc645d0a95464a043990e5ec:74fe7144e2b0b349a63515159c45b1d3:::
VICTORIA$:1000:240ca6d9c2b917a8b0b245f2c4d5309f:f28d473cd9d64f7c8fe6833528708586:::
Particularly bored readers may enjoy cracking the passwords.
It is worth noting that the format of the smbpasswd file has changed between
version 2.0 of Samba and version 2.2. The file produced by pwdump2 is in the
"old" format, but Samba 2.2 will still happily use the older format.
Migrating Roaming Profiles
=========================Create a profiles share on Samba, where roaming profile
for users will be stored:-
[profiles]
comment = Profiles Store
path = /usr/local/filestore/profiles
writeable = yes
valid users = @users
admin users = @admins
create mask = 0755
force create mode = 020
directory mask = 02755
force directory mode = 02070
map system = yes
map hidden = yes
It is important to note here that simply moving the NT profile alone will not
work. In an NT domain, each user has a RID, or relative identifier. These
RID's are used by NT to set ACL's on system objects. The registry file
in the roaming profile (ntuser.dat) contains ACL permissions that refer to the
profile's user by RID, and the method which Samba uses to generate RID's
will *not* result in the same RID. Simply copying the profile will result in a
profile that cannot be used due to permission problems.
Find an NT machine which has an up to date local copy of the profile for each
and every use whose profile needs migrating. It's likely that you will need
to arrange this by logging in as each user in turn. Having already dumped the
password hashes from the SAM earlier, it's now safe for the adminstrator to
reset all user passwords to a known value, and then log in as each user in turn.
Larger sites may wish to automate this process - the following perl snippet may
help, though I have made no attempt to use it, and have personally never even
used Perl on Win32:-
Will need Win32 perl.
foreach $USER in Users do
login $USER;
GOTO 'My Computer' -> 'Properties' -> Profiles;
cp $User_Profile $SMB_Profile_Deposits;
'Permit use' -> user from domain users;
done
To migrate the profiles manually, from the NT machine you have set up, go to
control panel -> system -> user profiles, and copy each profile to the
appropriate subdirectory on the Samba share. In the process of copying, set the
valid users to "Everyone".
Configuring Samba as a PDC
=========================After the above steps have been taken, it is possible
to transfer control of the domain over to the Samba server. Shut down Samba, and
edit the smb.conf file, making the following changes:-
Add
os level = 64
preferred master = yes
domain master = yes
local master = yes
domain logons = yes
logon path = \\%N\profiles\%u
logon drive = M:
logon home = \\%N\home
logon script = logon.cmd
Ensure that password encryption is set to "on", and that security is
changed from "domain" to "user". The logon path, logon drive
and logon home should be changed appropraitely for your setup.
Add a share called "netlogon", as shown:-
[netlogon]
path = /usr/local/filestore/netlogon
writeable = no
write list = ntadmin, admin1
Make backup copies of, and then delete the secrets.tdb file (probably in
/usr/local/private) that was created when you joined the NT domain, and the
MACHINE.SID file from the same directory. Replace the MACHINE.SID file with one
containing the domain SID that was extracted from the Windows PDC. Use the
output from pwdump as your smbpasswd file - store this in the
"private" directory along with the MACHINE.SID.
Ensure that all the accounts present in the smbpasswd file are present in
/etc/passwd, both machine trust accounts (all end with a "$"), and
user accounts. It is also important that the UID in /etc/passwd is the same as
that in smbpasswd for each account.
If Samba was configured with PAM support, ensure that an appropriate
/etc/pam.d/samba file exists.
Finally, shutdown the Windows PDC, and restart the Samba daemons from the new
configuration file.
You should now be able to log on to the Samba PDC from any of the Windows
workstations that are members of the domain.
Replacing your NT BDC
====================PDC to BDC replication is not supported in the current
releases of Samba 2.2, so setting up a BDC directly is not possible. It is,
however, possible to provide the redundancy offered by a BDC fairly simply.
-------
Some documentation on using rsync to maintain SAM/account details on two
machines, and provide failover in the event of one going down needed.
-------
Troubleshooting
==============-----
need lists of what can go wrong.
-----
Miscellaneous
Authentication and Single Sign on
Using pam_smb
Using pam_ntdom
Using winbind
Caveats/outstanding questions
Machine name length - if netbios name longer than 8 characters, will the machine
account die?
-------------- next part --------------
#!/usr/bin/perl
#
# Author: Mike Brodbelt
# Creation date: 21/11/01
# Last updated: 03/12/01
#
# Small script to read the contents of system account files, and an smbpasswd
file, and
# create new /etc/passwd and /etc/group files suitable for basing a Samba
controlled
# NT domain on. Also, generate scripts to change file ownership appropriately,
where
# a users UID changes.
# Set a few global variables to influence the script's operation
our $unix_pwd_field = "x"; # New Unix accounts will have their
password field set to this.
our $unix_shell = "/bin/bash"; # New Unix accounts will have their
shell set to this.
our $system_account_base = "105"; # Accounts in passwd file with UID
<= this will be preserved
our $system_group_base = "249"; # Accounts in group file with GID
<= this will be preserved
our $output_passwd_file = "new_passwd"; # Name of new passwd file for
output
our $output_group_file = "new_group"; # Name of new group file for
output
our $output_smbpasswd_file = "new_smbpasswd"; # Name of new smbpasswd
file for output
our $output_shadow_file = "new_shadow"; # Name of new shadow file for
output
our $shell_script = "ownership.sh"; # Name of shell script to change
file ownerships
(@ARGV == 4) || die "Usage: pdc_conv.pl <unix_passwd_file>
<unix_group_file> <smbpasswd_file> <shadow_file>\n";
($passwd, $group, $smbpasswd, $shadow) = @ARGV;
# Parse the supplied passwd, group, and smbpasswd files, building
# tables for them in memory.
$user_hashref = hash_unix_users();
$group_hashref = hash_unix_groups();
$smbpasswd_hashref = hash_smbpasswd();
$shadow_hashref = hash_shadow_file();
# Now, we need to create a new Unix /etc/passwd file. We go through the
existing accounts
# that have been pulled from the passwd file, and leave any that fall below the
base UID
# untouched - this preserves system accounts without any changes.
$newuser_hashref = add_reserved_accounts($user_hashref);
# For accounts present in the smbpasswd file, we need to add a Unix system
account. Where there is no
# corresponding UID in the Unix passwd file, we simply create the account,
using the appropriate
# account information. Where there is an existing UID in the Unix passwd file,
we store the details
# of the account for later matching.
add_smbpasswd_accounts($newuser_hashref, $user_hashref, $smbpasswd_hashref);
# At this point, we have an in memory passwd file that contains the contents of
the
# NT SAM from the smbpasswd file, and the accounts with a UID <= the system
base.
# Now, we need to go through the accounts in the passwd file, and where we have
# migrated an account with an identical username, we assume that this is the
same
# user, and bring across information from the passwd file (shell, home dir,
gecos).
add_finger_inf($newuser_hashref, $user_hashref, $smbpasswd_hashref);
# Finally, we need to migrate any accounts that exist in the system passwd
# file, that have no matching entry in the smbpasswd file. This will catch Unix
# user logins that are not in the NT SAM. Where these collide with a previously
# migrated user from the smbpasswd file, we *overwrite* the user from
smbpasswd.
# This is done on the principle that it's better to have a user account
fail to
# migrate than to trash a unix login that could be vital.
add_nonreserved_accounts($newuser_hashref, $user_hashref, $smbpasswd_hashref);
# Now we create a new Unix /etc/group file. As with the password file, first
add all
# the group accounts that fall below a user defined base GID.
$newgroup_hashref = add_reserved_groups($group_hashref);
# Now, add the other groups. Go through the group file, and add groups
unchanged
# unless the groupname matches the username of a user from the passwd file. If
# this occurs, and the group has no member list, assume this is a "user
private
# group", and change the GID to match the GID we're going to write out
in the new
# system passwd file.
add_all_groups($newgroup_hashref, $user_hashref, $group_hashref,
$smbpasswd_hashref);
# Now we need to generate new user private groups for user accounts with a
# primary group that has no corresponding entry in the new group list.
add_new_groups($newgroup_hashref, $newuser_hashref);
# Write out a new Unix passwd file
write_passwd_file($newuser_hashref);
# Write out a shadow file that corresponds to this passwd file
write_shadow_file($newuser_hashref, $shadow_hashref);
# Write out a new smbpasswd file, reformatting for Samba version 2.x
write_smbpasswd_file($smbpasswd_hashref);
# Write out a new Unix group file
write_group_file($newgroup_hashref);
# Now we need to generate a shell script that will change all the UIDs
# and GIDs on the filesystem as appropriate. To do this, we need to
# build maps linking the old UIDs and GIDs to the new ones.
$uid_map_hashref = build_uid_map($user_hashref, $newuser_hashref);
$gid_map_hashref = build_gid_map($group_hashref, $newgroup_hashref);
# Now, write a shell script out that'll do the changes.
write_shell_script($uid_map_hashref, $gid_map_hashref);
#################################################################################
sub build_gid_map {
my (%groups, %gid_map);
my ($group_hashref, $newgroup_hashref) = @_;
foreach (keys %$group_hashref) {
$groups{ $$group_hashref{$_}->{GROUPNAME} } = $_;
}
foreach (sort {$a <=> $b} keys %$newgroup_hashref) {
if (exists $groups{ $$newgroup_hashref{$_}->{GROUPNAME} }) {
unless ($groups{ $$newgroup_hashref{$_}->{GROUPNAME} } == $_) {
$gid_map{ $groups{ $$newgroup_hashref{$_}->{GROUPNAME} } } = $_;
}
}
}
return \%gid_map;
}
sub build_uid_map {
my (%users, %uid_map);
my ($user_hashref, $newuser_hashref) = @_;
foreach (keys %$user_hashref) {
$users{ $$user_hashref{$_}->{USERNAME} } = $_;
}
foreach (sort {$a <=> $b} keys %$newuser_hashref) {
if (exists $users{ $$newuser_hashref{$_}->{USERNAME} }) {
unless ($users{ $$newuser_hashref{$_}->{USERNAME} } == $_) {
$uid_map{ $users{ $$newuser_hashref{$_}->{USERNAME} } } = $_;
}
}
}
return \%uid_map;
}
sub add_all_groups {
my (%users);
my ($newgroup_hashref, $user_hashref, $group_hashref, $smbpasswd_hashref) = @_;
# Hash Unix usernames from passwd file against UID's
foreach (keys %$user_hashref) {
$users{ $$user_hashref{$_}->{USERNAME} } = $_;
}
foreach (keys %$group_hashref) {
if ($_ > $system_group_base) {
if ((exists $users{ $$group_hashref{$_}->{GROUPNAME} }) &&
(!scalar @{$$group_hashref{$_}->{MEMBERS}})) {
if (exists $$smbpasswd_hashref{ $$group_hashref{$_}->{GROUPNAME} }) {
# User private group with a corresponding smbpasswd entry
$$newgroup_hashref{
$$smbpasswd_hashref{$$group_hashref{$_}->{GROUPNAME}}->{UID} } = {
GROUPNAME => $$group_hashref{$_}->{GROUPNAME},
PASSWD => $$group_hashref{$_}->{PASSWD},
GID =>
$$smbpasswd_hashref{$$group_hashref{$_}->{GROUPNAME}}->{UID},
MEMBERS => $$group_hashref{$_}->{MEMBERS}
};
} else {
# User private group with no corresponding smbpasswd entry
$$newgroup_hashref{$_} = {
GROUPNAME => $$group_hashref{$_}->{GROUPNAME},
PASSWD => $$group_hashref{$_}->{PASSWD},
GID => $_,
MEMBERS => $$group_hashref{$_}->{MEMBERS}
};
}
} elsif (!scalar @{$$group_hashref{$_}->{MEMBERS}}) {
print "Discarding empty group
\"$$group_hashref{$_}->{GROUPNAME}\" from new group file\n";
} else {
$$newgroup_hashref{$_} = {
GROUPNAME => $$group_hashref{$_}->{GROUPNAME},
PASSWD => $$group_hashref{$_}->{PASSWD},
GID => $_,
MEMBERS => $$group_hashref{$_}->{MEMBERS}
};
}
}
}
}
sub add_new_groups {
my ($newgroup_hashref, $newuser_hashref) = @_;
foreach (sort {$a <=> $b} keys %$newuser_hashref) {
unless (exists $$newgroup_hashref{ $$newuser_hashref{$_}->{GID} }) {
$$newgroup_hashref{$_} = {
GROUPNAME => $$newuser_hashref{$_}->{USERNAME},
PASSWD => $unix_pwd_field,
GID => $$newuser_hashref{$_}->{GID},
MEMBERS => ""
};
}
}
}
sub add_reserved_groups {
my (%new_unix_groups);
my ($group_hashref) = @_;
foreach (sort keys %$group_hashref) {
if ($_ <= $system_group_base) {
$new_unix_groups{$_} = $$group_hashref{$_};
}
}
return \%new_unix_groups;
}
sub add_finger_inf {
my ($newuser_hashref, $user_hashref, $smbpasswd_hashref) = @_;
foreach (sort keys %$user_hashref) {
if ($_ > $system_account_base) {
if (exists $$smbpasswd_hashref{ $$user_hashref{$_}->{USERNAME} }) {
$$newuser_hashref{ $$smbpasswd_hashref{ $$user_hashref{$_}->{USERNAME}
}->{UID} }->{PASSWD} $$user_hashref{$_}->{PASSWD};
$$newuser_hashref{ $$smbpasswd_hashref{ $$user_hashref{$_}->{USERNAME}
}->{UID} }->{GECOS} $$user_hashref{$_}->{GECOS};
$$newuser_hashref{ $$smbpasswd_hashref{ $$user_hashref{$_}->{USERNAME}
}->{UID} }->{HOMEDIR} $$user_hashref{$_}->{HOMEDIR};
$$newuser_hashref{ $$smbpasswd_hashref{ $$user_hashref{$_}->{USERNAME}
}->{UID} }->{SHELL} $$user_hashref{$_}->{SHELL};
}
}
}
}
sub add_nonreserved_accounts {
my ($newuser_hashref, $user_hashref, $smbpasswd_hashref) = @_;
foreach (sort keys %$user_hashref) {
if ($_ > $system_account_base) {
# if the username matches a username in smbpasswd, skip it - already migrated
if (exists $$smbpasswd_hashref{ $$user_hashref{$_}->{USERNAME} }) {
next;
} else {
# Have we already got an account in the newuser hash with this UID?
if (exists $$newuser_hashref{$_}) {
print "Warning: SMB user $$newuser_hashref{$_}->{USERNAME} will not
be migrated, " .
"collides with Unix user
$$user_hashref{$_}->{USERNAME}!\n";
}
$$newuser_hashref{$_} = $$user_hashref{$_};
}
}
}
}
sub add_smbpasswd_accounts {
my ($newuser_hashref, $user_hashref, $smbpasswd_hashref) = @_;
foreach (keys %$smbpasswd_hashref) {
# Does the UID collide with one already in the new users list - i.e. a
corrupted smbpasswd file or a clash between
# an smbpasswd entry and a pre-existing Unix account with UID < system
reserved base.
if (exists $$newuser_hashref{ $$smbpasswd_hashref{$_}->{UID} }) {
print "Account with UID \"$$smbpasswd_hashref{$_}->{UID}\"
from smbpasswd collides with reserved account\n";
print "This account \($$smbpasswd_hashref{$_}->{USERNAME}\) account
will not be added to the new passwd file\n";
} else {
if ($$smbpasswd_hashref{$_}->{USERNAME} =~ /\$$/) {
$record = {
USERNAME => $$smbpasswd_hashref{$_}->{USERNAME},
PASSWD => $unix_pwd_field,
UID => $$smbpasswd_hashref{$_}->{UID},
GID => $$smbpasswd_hashref{$_}->{UID},
GECOS => "NT workstation trust account",
HOMEDIR => "/dev/null",
SHELL => "/bin/false"
};
} else {
$record = {
USERNAME => $$smbpasswd_hashref{$_}->{USERNAME},
PASSWD => $unix_pwd_field,
UID => $$smbpasswd_hashref{$_}->{UID},
GID => $$smbpasswd_hashref{$_}->{UID},
GECOS => "Account migrated from NT SAM database",
HOMEDIR => "/home/$$smbpasswd_hashref{$_}->{USERNAME}",
SHELL => $unix_shell
};
}
}
$$newuser_hashref{ $record->{UID} } = $record;
}
}
sub write_smbpasswd_file {
my ($flags, $rec);
my ($smbpasswd_hashref) = @_;
open OUTFILE, '>', $output_smbpasswd_file || die "Unable to
open $output_smbpasswd_file for writing\n";
foreach (sort {uc($a) cmp uc($b)} keys %$smbpasswd_hashref) {
if ($_ =~ /\$$/) {
$flags = "[ W ]";
} else {
$flags = "[U ]";
}
$rec = join(':',
$$smbpasswd_hashref{$_}->{USERNAME},
$$smbpasswd_hashref{$_}->{UID},
$$smbpasswd_hashref{$_}->{LMHASH},
$$smbpasswd_hashref{$_}->{NTHASH},
$flags,
"LCT-363F96AD",
""
);
print OUTFILE "$rec\n";
}
close(OUTFILE);
}
sub write_group_file {
my ($rec);
my ($newgroup_hashref) = @_;
open OUTFILE, '>', $output_group_file || die "Unable to open
$output_group_file for writing\n";
foreach (sort {$a <=> $b} keys %$newgroup_hashref) {
$rec = join(':',
$$newgroup_hashref{$_}->{GROUPNAME},
$$newgroup_hashref{$_}->{PASSWD},
$$newgroup_hashref{$_}->{GID},
(join(',', @{$$newgroup_hashref{$_}->{MEMBERS}}))
);
print OUTFILE "$rec\n";
}
close(OUTFILE);
}
sub write_passwd_file {
my ($rec);
my ($newuser_hashref) = @_;
open OUTFILE, '>', $output_passwd_file || die "Unable to open
$output_passwd_file for writing\n";
foreach (sort {$a <=> $b} keys %$newuser_hashref) {
$rec = join(':',
$$newuser_hashref{$_}->{USERNAME},
$$newuser_hashref{$_}->{PASSWD},
$$newuser_hashref{$_}->{UID},
$$newuser_hashref{$_}->{GID},
$$newuser_hashref{$_}->{GECOS},
$$newuser_hashref{$_}->{HOMEDIR},
$$newuser_hashref{$_}->{SHELL}
);
print OUTFILE "$rec\n";
}
close(OUTFILE);
}
sub write_shadow_file {
my ($newuser_hashref, $shadow_hashref)= @_;
open OUTFILE, '>', $output_shadow_file || die "Unable to open
$output_shadow_file for writing\n";
foreach (sort {$a <=> $b} keys %$newuser_hashref) {
if (exists $$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }) {
$rec = join(':',
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{USERNAME},
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{PASSWD},
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{LASTDAY},
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{MINDAY},
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{MAXDAY},
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{WARNDAY},
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{EXPIREDATE},
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{DISABLED},
$$shadow_hashref{ $$newuser_hashref{$_}->{USERNAME} }->{RESERVED}
);
} else {
$rec = join(':',
$$newuser_hashref{$_}->{USERNAME},
"*",
"10438",
"0",
"99999",
"7",
"",
"",
""
);
}
print OUTFILE "$rec\n";
}
close(OUTFILE);
}
sub write_shell_script {
my ($uid_map_hashref, $gid_map_hashref) = @_;
open OUTFILE, '>', $shell_script || die "Unable to open
$shell_script for writing\n";
print OUTFILE "#!/bin/sh\n";
foreach (sort {$a <=> $b} keys %$uid_map_hashref) {
print OUTFILE "find / -uid $_ -exec chown $$uid_map_hashref{$_} {}
\\;\n";
}
foreach (sort {$a <=> $b} keys %$gid_map_hashref) {
print OUTFILE "find / -gid $_ -exec chgrp $$gid_map_hashref{$_} {}
\\;\n";
}
close(OUTFILE);
chmod 0755,($shell_script);
}
sub add_reserved_accounts {
my (%new_unix_users);
my ($user_hashref) = @_;
foreach (sort keys %$user_hashref) {
if ($_ <= $system_account_base) {
$new_unix_users{$_} = $$user_hashref{$_};
}
}
return \%new_unix_users;
}
sub hash_smbpasswd {
# Open the smbpasswd file, and read all entries.
my (@fields, $record, %users);
open SMBPASSWD, $smbpasswd || die "Unable to open $smbpasswd for
reading\n";
while (<SMBPASSWD>) {
chomp;
(@fields) = split(/:/, $_);
$record = {
USERNAME => $fields[0],
UID => $fields[1],
LMHASH => $fields[2],
NTHASH => $fields[3]
};
# Now store the record we've just created, keying the hash by username.
$users{ $record->{USERNAME} } = $record;
}
close(SMBPASSWD);
# Return a reference to the hash
return \%users;
}
sub hash_unix_groups {
# Open the Unix group file, and build a list of groups, and
# their memberships. We want to isolate groups with no members,
# which are set to the primary group of some users, so we can
# chgrp as well as chown their files
my (@fields, $record, %groups);
open GROUP, $group || die "Unable to open $group for reading\n";
while (<GROUP>) {
chomp;
@fields = split(/:/, $_);
$record = {
GROUPNAME => $fields[0],
PASSWD => $fields[1],
GID => $fields[2],
MEMBERS => [ split(/\s*,\s*/, $fields[3]) ]
};
# Now store the group record created, keying the hash by GID
$groups{ $record->{GID} } = $record;
}
close(GROUP);
# Return a reference to the hash
return \%groups;
}
sub hash_unix_users {
# Open the unix passwd file, and read a list of all users.
# Then, store a 2 dimensional array of usernames, full names, and
# home directories.
my (@fields, $record, %users);
open PASSWD, $passwd || die "Unable to open $passwd for reading\n";
while (<PASSWD>) {
chomp;
(@fields) = split(/:/, $_);
$record = {
USERNAME => $fields[0],
PASSWD => $fields[1],
UID => $fields[2],
GID => $fields[3],
GECOS => $fields[4],
HOMEDIR => $fields[5],
SHELL => $fields[6],
};
# Now store the record we've just created, keying the hash by UID.
$users{ $record->{UID} } = $record;
}
close(PASSWD);
# Return a reference to the hash
return \%users;
}
sub hash_shadow_file {
my (@fields, $record, %shadow);
open SHADOW, $shadow || die "Unable to open $shadow for reading\n";
while (<SHADOW>) {
chomp;
(@fields) = split(/:/, $_);
$record = {
USERNAME => $fields[0],
PASSWD => $fields[1],
LASTDAY => $fields[2],
MINDAY => $fields[3],
MAXDAY => $fields[4],
WARNDAY => $fields[5],
EXPIREDATE => $fields[6],
DISABLED => $fields[7],
RESERVED => $fields[8],
};
# Now store the record we've just created, keying the hash by UID.
$shadow{ $record->{USERNAME} } = $record;
}
close(SHADOW);
# Return a reference to the hash
return \%shadow;
}