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 $source_version = '10.5';
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];
} elsif ($ARGV[$i] eq '--source-version') {
die "Error: --source-version requires an argument\n" unless ++$i < $argc;
$source_version = $ARGV[$i]
} else {
die "Error: unknown argument $ARGV[$i]\n";
}
}
die "--source not defined" unless defined $source;
$source_version >= 10.5 or exit 0;
-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 $source_kdc_local = $source.$kdc_local;
if (not open (KDC_LOCAL, '/usr/libexec/PlistBuddy -c "Print :realname:" '. $source_kdc_local. '|')) {
print 'Error: '. $progname ." failed to find $kdc_local\n";
exit 1;
}
my $configrealm = join ('', <KDC_LOCAL>);
close KDC_LOCAL;
$configrealm =~ s/^.*(LKDC:SHA1\.[0-9A-F]{40}).*$/\1/s;
if ($configrealm eq '') {
print 'Error: '. $progname ." cannot parse $source_kdc_local\n";
exit 1;
}
my $migrationkdcconf = "/var/db/krb5kdc/migration.conf";
my $migrationdump = "/var/db/krb5kdc/lkdc-migration-dump";
my $CONF;
open CONF, ">$migrationkdcconf";
print CONF "[kdc]\n";
print CONF "database = {\n";
if ($source_version < 10.7) {
print CONF "\tdbname = mit-db:$source/var/db/krb5kdc/principal.$configrealm\n";
print CONF "\tmkey_file = FILE:$source/var/db/krb5kdc/.k5.$configrealm\n";
print CONF "\tlog_file = /var/db/krb5kdc/log-$configrealm\n";
} else {
print CONF "\tdbname = od:/Local/Default&$source/var/db/dslocal/nodes/Default\n";
print CONF "\tmkey_file = $source/var/db/krb5kdc/m-key\n";
print CONF "\tlog_file = /var/db/krb5kdc/log-$configrealm\n";
}
print CONF "}\n";
close CONF;
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/kadmin -c $migrationkdcconf -l -r $configrealm dump -d |";
open OUT, ">$migrationdump";
my (%sourceusers, %targetusers, $user, $realm, $sourcerealm);
while(<IN>) {
next if (not m/^([^@]*)\@([^ \t]*)\s+/);
($user, $realm) = ($1, $2);
next if ($user eq "K/M");
next if ($user eq "_krbtgt");
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/kadmin -l -r $targetrealm merge --fix-salts /var/db/krb5kdc/lkdc-migration-dump");
foreach my $a (keys %sourceusers) {
print "-----> fixing attributes: $a\n";
system("/usr/sbin/kadmin -l -r $targetrealm ".
"modify -a -disallow-tgt-based $a\@$targetrealm");
system("/usr/sbin/kadmin -l -r $targetrealm ".
"modify -a -disallow-forwardable $a\@$targetrealm");
system "/System/Library/PrivateFrameworks/Heimdal.framework/Helpers/hod-admin . ".
"principal-setflags $a RequireStrongPreAuth";
}
unlink $migrationkdcconf;
unlink $migrationdump;
system("killall krb5kdc");
exit 0;