#!/usr/bin/perl # # Copyright (c) 2009 Apple Inc. All rights reserved. # # @APPLE_LICENSE_HEADER_START@ # # This file contains Original Code and/or Modifications of Original Code # as defined in and that are subject to the Apple Public Source License # Version 2.0 (the 'License'). You may not use this file except in # compliance with the License. Please obtain a copy of the License at # http://www.opensource.apple.com/apsl/ and read it before using this # file. # # The Original Code and all software distributed under the License are # distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER # EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, # INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. # Please see the License for the specific language governing rights and # limitations under the License. # # @APPLE_LICENSE_HEADER_END@ # # This script migrates a LocalKDC database 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'; # if not given, assume Leopard 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; # # For pre-Leopard, skip migration # $source_version >= 10.5 or exit 0; # # If there is no database, skip migration # -d "$source/var/db/krb5kdc" or exit 0; # # Read in realm names, target and source realm names # 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 ('', ); 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 ('', ); 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"; # # Load in the database and transfor the dumpfile to the new masterkey # $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); # # Iterate over the data base and strip out all principals that doesn't # seem to be users and save them in the output dump file # while() { next if (not m/^([^@]*)\@([^ \t]*)\s+/); ($user, $realm) = ($1, $2); # skip admin/meta principals next if ($user eq "K/M"); next if ($user eq "_krbtgt"); next if ($user =~ "^kadmin/"); # ignore kadmin foo next if ($user =~ m@^krbtgt/.*@); # ignore tgt and cross realm next if ($user =~ m@^.*/LKDC:*@); # ignore LKDC services print OUT $_; $sourceusers{$user} = 1; # # Verify all principals are from the same realm # 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; # # Switch over to the new database # $ENV{'KRB5_KDC_PROFILE'} = '/var/db/krb5kdc/kdc.conf'; # # check that the dump contained what we expected # if ($configrealm ne $sourcerealm) { die "missmatch between database and kdc.conf realm" } print "-----> migration kdc.conf: $migrationkdcconf\n"; print "-----> migration dump: $migrationdump\n"; # Load the migration dump into the database system("/usr/sbin/kadmin -l -r $targetrealm merge --fix-salts /var/db/krb5kdc/lkdc-migration-dump"); # Fix the attributes 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; # # Restart KDC since to might have cached part of the database # and the it will have the wrong data # system("killall krb5kdc"); exit 0;