use strict;
use Errno qw(:POSIX);
use File::Basename;
my $progname = basename ($0);
chdir '/' or die "chdir: $!\n";
if ($< != 0) {
print 'Error: '. $progname ." needs to be run by root\n";
exit EPERM();
}
umask 022;
my ($source);
my $argc = scalar @ARGV;
my $i;
for ($i = 0; $i < $argc; ++$i) {
if ($ARGV[$i] eq '--source') {
die "Error: --source requires an argument\n" unless ++$i < $argc;
$source = $ARGV[$i];
} else {
die "Error: unknown argument $ARGV[$i]\n";
}
}
die "--source not defined" unless defined $source;
-d "$source/var/db/krb5kdc" or exit 0;
my $KDC_LOCAL;
my $kdc_local = '/var/db/dslocal/nodes/Default/config/KerberosKDC.plist';
if (not open (KDC_LOCAL, '/usr/libexec/PlistBuddy -c "Print :realname:" '. $kdc_local. '|')) {
print 'Error: '. $progname ." failed to find $kdc_local\n";
exit 1;
}
my $targetrealm = join ('', <KDC_LOCAL>);
close KDC_LOCAL;
$targetrealm =~ s/^.*(LKDC:SHA1\.[0-9A-F]{40}).*$/\1/s;
if ($targetrealm eq '') {
print 'Error: '. $progname ." cannot parse $kdc_local\n";
exit 1;
}
my $migrationkdcconf = "/var/db/krb5kdc/kdc.conf.old";
my $migrationdump = "/var/db/krb5kdc/lkdc-migration-dump";
my ($IN, $OUT, $configrealm, %sourceusers, %targetusers);
open IN, "$source/var/db/krb5kdc/kdc.conf" or die "open source kdc.conf: $!";
open OUT, ">$migrationkdcconf";
while(<IN>) {
s@/var/db/krb5kdc/@$source/var/db/krb5kdc/@;
$configrealm = $1 if (m/(LKDC:.*) = {/);
print OUT $_;
}
close IN;
close OUT;
die "no Local KDC realm in source kdc.conf" unless defined $configrealm;
print "-----> targetrealm: $targetrealm\n";
print "-----> configrealm: $configrealm\n";
$ENV{'KRB5_KDC_PROFILE'} = $migrationkdcconf;
open IN, "/usr/sbin/kdb5_util -r $configrealm ".
"dump ".
"-mkey_convert ".
"-new_mkey_file /var/db/krb5kdc/.k5.$targetrealm ".
"-new_mkey_principal K/M\@$targetrealm |";
open OUT, ">$migrationdump";
my ($user, $realm, $sourcerealm);
while(<IN>) {
if (m/^kdb5_util load_dump version/) {
print OUT $_;
next;
}
next if (not m/^princ\s+\d+\s+\d+\s+\d+\s+\d+\s+\d+\s+([^@]*)\@([^ \t]*)\s+/);
($user, $realm) = ($1, $2);
next if ($user eq "K/M");
next if ($user =~ "^kadmin/"); next if ($user =~ m@^krbtgt/.*@); next if ($user =~ m@^.*/LKDC:*@);
print OUT $_;
$sourceusers{$user} = 1;
if (defined $sourcerealm and ($sourcerealm ne $realm)) {
print "Error: $progname realm ($realm) changed ".
"source realm ($sourcerealm)\n";
exit EINVAL();
}
$sourcerealm = $realm;
}
close IN;
close OUT;
$ENV{'KRB5_KDC_PROFILE'} = '/var/db/krb5kdc/kdc.conf';
if ($configrealm ne $sourcerealm) {
die "missmatch between database and kdc.conf realm"
}
print "-----> migration kdc.conf: $migrationkdcconf\n";
print "-----> migration dump: $migrationdump\n";
system("/usr/sbin/kdb5_util load -update $migrationdump");
foreach my $a (keys %sourceusers) {
print "-----> renaming user: $a\n";
system("/usr/sbin/kadmin.local ".
"-r $targetrealm ".
"-p kadmin/local\@$targetrealm ".
"-q \"renprinc -force $a\@$sourcerealm $a\@$targetrealm\"");
system("/usr/sbin/kadmin.local ".
"-r $targetrealm ".
"-p kadmin/local\@$targetrealm ".
"-q \"modprinc -allow_svr $a\@$targetrealm\"");
}
unlink $migrationkdcconf;
unlink $migrationdump;
system("killall krb5kdc");
exit 0;