use strict;
use File::Basename;
my $configured = "/var/db/.configureLocalKDC";
my $progname = basename ($0);
chdir '/' or die "chdir: $!\n";
if ($< != 0) {
print 'Error: '. $progname ." needs to be run by root\n";
exit 1;
}
umask 022;
close STDOUT;
open STDOUT, ">>/Library/Logs/LKDC-setup.log" || die "Failed to open STDOUT";
open STDERR, ">&STDOUT" || die "Failed to open STDERR";
system("date");
my $system_keychain = '/Library/Keychains/System.keychain';
if (! -f $system_keychain) {
system '/usr/sbin/systemkeychain', '-C';
}
my $kdc_dir = '/var/db/krb5kdc';
my $kdc_conf = $kdc_dir .'/kdc.conf';
my $kdc_local = '/var/db/dslocal/nodes/Default/config/KerberosKDC.plist';
my $plist_set = 0;
my $make_keytab_flag = 0;
my $update_plists_flag = 0;
my @kdcsetup_args;
my $krb5kdc_plist = '/System/Library/LaunchDaemons/edu.mit.Kerberos.krb5kdc.plist';
my $source_version = '10.5'; my $restore_directory;
my $fixup_disallow_service = 0;
my $create_keychain = 1;
my $argc = scalar @ARGV;
my $i;
for ($i = 0; $i < $argc; ++$i) {
if ($ARGV[$i] eq '--plist') {
$plist_set = 1;
$make_keytab_flag = 0;
$update_plists_flag = 1;
} elsif ($ARGV[$i] eq '--target') {
die "Error: --target requires an argument\n" unless ++$i < $argc;
push @kdcsetup_args, '-T', $ARGV[$i];
} elsif ($ARGV[$i] eq '--source') {
die "Error: --source requires an argument\n" unless ++$i < $argc;
$restore_directory = $ARGV[$i]
} elsif ($ARGV[$i] eq '--source-version') {
die "Error: --source-version requires an argument\n" unless ++$i < $argc;
$source_version = $ARGV[$i]
} elsif ($ARGV[$i] eq '--mode') {
die "Error: --mode requires an argument\n" unless ++$i < $argc;
} else {
die "Error: unknown argument $ARGV[$i]\n";
}
}
if (defined $restore_directory) {
printf("Doing a migration\n");
if ($source_version lt '10.5') {
push @kdcsetup_args, '-L';
} else {
push @kdcsetup_args, '-R', $restore_directory;
$fixup_disallow_service = 1;
$create_keychain = 0;
printf "Creating log directory\n";
mkdir '/var/log/krb5kdc', 0700;
}
} else {
push @kdcsetup_args, '-L';
}
if (! -d $kdc_dir or ! -f $kdc_local) {
my $res;
if ($create_keychain) {
printf("creating system keychain entries\n");
$res = system '/usr/bin/certtool', 'C', 'com.apple.systemdefault', 'u', 'P', 'v';
if ($res != 0) {
unlink $configured;
die "cert tool failed for com.apple.systemdefault";
}
$res = system '/usr/bin/certtool', 'C', 'com.apple.kerberos.kdc', 'u', 'P', 'v';
if ($res != 0) {
unlink $configured;
die "cert tool failed for com.apple.kerberos.kdc";
}
} else {
printf("system chain already migrated\n");
}
$res = system '/usr/sbin/kdcsetup', '-n', @kdcsetup_args;
if ($res != 0) {
unlink $configured;
die "kdcsetup failed";
}
system '/bin/launchctl', 'load', $krb5kdc_plist;
unless ($plist_set) {
$make_keytab_flag = 1;
$update_plists_flag = 1;
}
}
system '/System/Library/PrivateFrameworks/KerberosHelper.framework/Resources/lkdc_acl',
'-s', 'com.apple.kerberos.kdc', '-a', '/usr/sbin/krb5kdc';
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 $LKDC_realm = join ('', <KDC_LOCAL>);
$LKDC_realm =~ s/^.*(LKDC:SHA1\.[0-9A-F]{40}).*$/\1/s;
if ($LKDC_realm eq '') {
print 'Error: '. $progname ." cannot parse $kdc_local\n";
exit 1;
}
close KDC_LOCAL;
if (not open (KDC_CONF, '<', $kdc_conf)) {
print 'Error: '. $progname ." failed to find $kdc_conf\n";
exit 1;
}
my @default_realm = grep /default_realm/, <KDC_CONF>;
if (1 != scalar (@default_realm)) {
print 'Error: '. $progname ." cannot parse $kdc_conf\n";
exit 1;
}
my $default_realm = (split (/\s+/, $default_realm[0], 4))[3];
chomp $default_realm;
close KDC_CONF;
if ($default_realm !~ /^LKDC:/) {
$update_plists_flag = 0;
}
if ($make_keytab_flag or $update_plists_flag) {
my %afp_config = (service => 'afpserver', realm => $LKDC_realm,
prefs => '/Library/Preferences/com.apple.AppleFileServer',
key => 'kerberosPrincipal', format => '%s/%s@%2$s');
my %smb_config = (service => 'cifs', realm => $LKDC_realm,
prefs => '/Library/Preferences/SystemConfiguration/com.apple.smb.server',
key => 'LocalKerberosRealm', format => '%2$s');
my %ss_config = (service => 'vnc', realm => $LKDC_realm);
configure_service (\%afp_config, $make_keytab_flag, $update_plists_flag);
configure_service (\%smb_config, $make_keytab_flag, $update_plists_flag);
configure_service (\%ss_config, $make_keytab_flag, $update_plists_flag);
}
print $LKDC_realm . "\n";
if ($fixup_disallow_service) {
my $IN;
my @users;
open IN, "/usr/sbin/kadmin.local -r ${LKDC_realm} -q 'listprincs *' -r ${LKDC_realm}|";
while (<IN>) {
next if (not m%^([^/ ]+)\@${LKDC_realm}%);
push @users, $1;
}
close IN;
foreach my $user (@users) {
printf "fixing disable server for user: $user\n";
system("/usr/sbin/kadmin.local ".
"-r ${LKDC_realm} ".
"-q \"modprinc -allow_svr +requires_preauth $user\@${LKDC_realm}\"");
}
}
system "touch", $configured;
sub configure_service {
my $config = shift;
my $do_keytab = shift;
my $do_plist = shift;
my $realm = $config->{realm};
my $principal = sprintf "%s/%s@%s", $config->{service}, $realm, $realm;
if ($do_keytab) {
my $kadmin_local = '/usr/sbin/kadmin.local';
my @args = ('add_principal', '-randkey', $principal);
print $kadmin_local . join (' ', '-q', @args). "\n";
system $kadmin_local, '-r', $LKDC_realm,
'-q', join (' ', @args);
@args = ('ktadd', $principal);
print $kadmin_local . join (' ', '-q', @args). "\n";
system $kadmin_local, '-r', $LKDC_realm,
'-q', join (' ', @args);
}
if ($do_plist and defined ($config->{prefs})) {
my $value = sprintf $config->{format}, $config->{service}, $realm;
my @args = ('write', $config->{prefs}, $config->{key}, $value);
print '/usr/bin/defaults '. join (' ', @args). "\n";
system '/usr/bin/defaults', @args;
}
}