#!/usr/sww/bin/perl5 -w # # Install Kerberos on a local system. # the base directory for all the Kerberos administrative files # $KRB_BASE = "/etc"; # your Kerberos realm (should be upper case) # $KRB_REALM = "EECS.BERKELEY.EDU"; # location of the NFS-mounted Kerberos binaries # $KRB_REMOTE_PATH = '/usr/sww/kerberosIV'; # location of the local Kerberos binaries # $KRB_LOCAL_PATH = '/usr/kerberos'; # location of installation instructions # $KRB_INSTRUCTIONS = "/usr/sww/doc/kerberos/krb-admin"; # hash table mapping Kerberos realm to an index # %REALM2HASH = ( 'EECS.BERKELEY.EDU', 0, 'CSUA.BERKELEY.EDU', 1, 'XCF.BERKELEY.EDU', 2, ); # admin servers we know about # $KRB_ADMIN[$REALM2HASH{'EECS.BERKELEY.EDU'}] = 'kerberos1.CS.Berkeley.EDU'; $KRB_ADMIN[$REALM2HASH{'CSUA.BERKELEY.EDU'}] = 'kerberos.CSUA.Berkeley.EDU'; $KRB_ADMIN[$REALM2HASH{'XCF.BERKELEY.EDU'}] = 'kerberos.XCF.Berkeley.EDU'; # Key Distribution Centers we know about # %KDC2REALM = ( 'kerberos1.CS.Berkeley.EDU', 'EECS.BERKELEY.EDU', 'kerberos2.EECS.Berkeley.EDU', 'EECS.BERKELEY.EDU', 'kerberos3.CS.Berkeley.EDU', 'EECS.BERKELEY.EDU', 'kerberos4.EECS.Berkeley.EDU', 'EECS.BERKELEY.EDU', 'kerberos5.CS.Berkeley.EDU', 'EECS.BERKELEY.EDU', 'kerberos6.EECS.Berkeley.EDU', 'EECS.BERKELEY.EDU', 'kerberos7.CS.Berkeley.EDU', 'EECS.BERKELEY.EDU', 'kerberos8.EECS.Berkeley.EDU', 'EECS.BERKELEY.EDU', 'kerberos.CSUA.Berkeley.EDU', 'CSUA.BERKELEY.EDU', 'kerberos.XCF.Berkeley.EDU', 'XCF.BERKELEY.EDU', 'kerberos.MIT.EDU', 'ATHENA.MIT.EDU', 'kerberos-1.MIT.EDU', 'ATHENA.MIT.EDU', 'kerberos-2.MIT.EDU', 'ATHENA.MIT.EDU', 'kerberos-3.MIT.EDU', 'ATHENA.MIT.EDU', 'kerberos.LCS.MIT.EDU', 'LCS.MIT.EDU', 'bitsy.MIT.EDU', 'TELECOM.MIT.EDU', ); # list of known realms (for krb.realms) # %KRB_REALMS = ( '.CS.Berkeley.EDU', 'EECS.BERKELEY.EDU', '.EECS.Berkeley.EDU', 'EECS.BERKELEY.EDU', '.Berkeley.EDU', 'EECS.BERKELEY.EDU', '.CSUA.Berkeley.EDU', 'CSUA.BERKELEY.EDU', '.XCF.Berkeley.EDU', 'XCF.BERKELEY.EDU', '.MIT.EDU', 'ATHENA.MIT.EDU', '.LCS.MIT.EDU', 'LCS.MIT.EDU', ); # list of Kerberos daemons (for /etc/inetd.conf) # # (don't currently support # kpop stream tcp nowait root $KRB_LOCAL_PATH/libexec/popper popper # ) # $KRB_DAEMON_LIST = <<"+++++EOF+++++"; klogin stream tcp nowait root $KRB_LOCAL_PATH/libexec/rlogind rlogind -k eklogin stream tcp nowait root $KRB_LOCAL_PATH/libexec/rlogind rlogind -k -x kshell stream tcp nowait root $KRB_LOCAL_PATH/libexec/rshd rshd -k -L ekshell stream tcp nowait root $KRB_LOCAL_PATH/libexec/rshd rshd -k -L -x rkinit stream tcp nowait root $KRB_LOCAL_PATH/libexec/rkinitd rkinitd telnet stream tcp nowait root $KRB_LOCAL_PATH/libexec/telnetd telnetd +++++EOF+++++ # list of Kerberos services (for /etc/services) # $KRB_SERVICE_LIST = <<'+++++EOF+++++'; klogin 543/tcp # Kerberos authenticated rlogin kerberos 750/udp kdc # Kerberos authentication--udp kerberos 750/tcp kdc # Kerberos authentication--tcp kerberos_master 751/udp # Kerberos authentication kerberos_master 751/tcp # Kerberos authentication passwd_server 752/udp # Kerberos passwd server userreg_server 753/udp # Kerberos userreg server krb_prop 754/tcp # Kerberos slave propagation krbupdate 760/tcp kreg # Kerberos registration kpasswd 761/tcp kpwd # Kerberos "passwd" kpop 1109/tcp # Pop with Kerberos knetd 2053/tcp # Kerberos de-multiplexor kshell 544/tcp cmd # and remote shell eklogin 2105/tcp # Kerberos encrypted rlogin ekshell 2106/tcp cmd # and encrypted remote shell erlogin 888/tcp # Login and environment passing rkinit 2108/tcp # Kerberos remote initialization +++++EOF+++++ ########################################################### # Don't change anything past this point # ########################################################### # save program name for future use # $PROGRAM = $0; $PROGRAM =~ s|^.*/||; # initialize a bunch of global variables # $ETC_SERVICES = 0; $ETC_INETD_CONF = 0; $ETC_KRB_CONF = 0; $ETC_KRB_REALMS = 0; # If this is non-zero, go into debugging mode. # This is normally set with the '-d' flag. # $DEBUG = 0; # set the umask to something reasonable # umask(022); # check command line options # ($expert) = &getopts(); # make sure they have access to Kerberos binaries # if ( ! -d "$KRB_REMOTE_PATH/bin" ) { print STDERR "$PROGRAM: Sorry, but Kerberos is not yet available"; print STDERR " for this architecture.\n"; exit 1; } # Make sure they read the instructions first # if ( -r "$KRB_INSTRUCTIONS" && !$expert) { $question = "Would you like to see the instructions for installing Kerberos? "; if (&ask_yes_or_no($question) eq 'y') { $PAGER = (defined($ENV{'PAGER'}) ? $ENV{'PAGER'} : 'more'); `$PAGER $KRB_INSTRUCTIONS /dev/tty`; } print "\n\n\tThe instructions for installing Kerberos can be found in\n"; print "\t$KRB_INSTRUCTIONS\n\n\n"; } # This must run as root. # if ($< != 0 && !$DEBUG) { # see if they really want to do this # $question = "You aren't running as root. Don't you think you should be? "; if (&ask_yes_or_no($question) eq 'y') { print "I thought not. Toodles.\n"; exit 0; } print "OK, it's your choice.\n"; } # set KRB_ADMIN to the admin server for this realm # $KRB_ADMIN = $KRB_ADMIN[$REALM2HASH{$KRB_REALM}]; if (!$expert || $expert == -1) { $COPY_BINARIES = 0; } else { $COPY_BINARIES = &ask_about_local_path(); if ($COPY_BINARIES == 1) { print "OK, the Kerberos binaries will be copied to local disk.\n"; } elsif ($COPY_BINARIES == 0) { print "OK, I'll make a symbolic link to the remote filesystem.\n"; } } if (!$expert || $expert == -1 || &nfs_mounted_path('/usr')) { $UPDATE_UCB = 0; } else { print "\n(Until everyone in EECS is Kerberized, you should answer 'no')\n"; $question = 'Should rsh and rlogin be replaced' . ' with the Kerberized versions? '; $UPDATE_UCB = (&ask_yes_or_no($question) eq 'y'); if ($UPDATE_UCB) { print "The old versions will have a '.pre-krb' extension added.\n"; } else { print "OK, I'll leave them alone.\n"; } } if (!$expert || $expert == -1) { $DISABLE_RDAEMONS = 0; } else { print "\n(Until everyone in EECS is Kerberized, you should answer 'no')\n"; $question = 'Should the unKerberized rshd and rlogind daemons be disabled? '; $DISABLE_RDAEMONS = (&ask_yes_or_no($question) eq 'y'); if ($DISABLE_RDAEMONS) { print "Great! I'll update /etc/inetd.conf and move the old daemons.\n"; } else { print "OK, unauthorized connections will be permitted.\n"; } } if (!$expert || $expert == -1 || &nfs_mounted_path('/usr/bin')) { $USR_BIN_SYMLINKS = 0; } else { print "\nNOTE: the following option doesn't affect the currently installed rsh or rlogin.\n"; $question = "Do you want symlinks for kinit, klist, krlogin and krsh made in /usr/bin? "; $USR_BIN_SYMLINKS = (&ask_yes_or_no($question) eq 'y'); if ($USR_BIN_SYMLINKS) { print "Great! You'll be able to access kinit, klist, krlogin and krsh.\n"; } else { print "OK, you'll have to access the Kerberos binaries via /usr/kerberos/bin.\n"; } } &check_krb_srvtab(); &update_services(); &update_inetd(); &update_krb_conf(); &update_krb_realms(); if ($COPY_BINARIES > 0) { ©_binaries(); } elsif ($COPY_BINARIES == 0) { &link_binaries(); } else { &verify_local_path(); } &update_ucb() if ($UPDATE_UCB); &disable_rdaemons() if ($DISABLE_RDAEMONS); &usr_bin_symlinks() if ($USR_BIN_SYMLINKS); &finalize(); exit 0; ###################################################################### # # Ask $question, return an answer from @response # sub ask_question { local($question, $case_sensitive, @response) = @_; local($_, $r); while (1) { # ask a question, get an answer # print STDOUT $question; chop($_ = ); # clean up the answer # s/^\s+//; s/\s$//; tr/A-Z/a-z/ if (!$case_sensitive); # find answer in list of valid responses # foreach $r (@response) { return $r if ($r eq $_); } # print list of valid responses # print STDERR 'Please answer '; for ($r = 0; $r < @response; $r++) { print STDERR ($r == $#response ? ' or ' : ', ') if ($r > 0); print STDERR "'$response[$r]'"; } print STDERR "\n"; } } ###################################################################### # # Ask $question, return 'y' or 'n' # sub ask_yes_or_no { local($question) = @_; return substr(&ask_question($question, 0, 'y', 'n', 'yes', 'no'), 0, 1); } ###################################################################### # # Get options, return ($expert) which is either # undefined (if false) or set to 1 (if true) # sub getopts { local($expert); local($i, $error); $expert=undef; while ($i = shift(@ARGV)) { if ($i =~ /^-x/) { $expert = 1; } elsif ($i =~ /^-q/) { $expert = -1 } elsif ($i eq 'server') { print STDERR "Ignoring 'server' option\n"; } elsif ($i eq 'client') { print STDERR "Ignoring 'client' option\n"; } elsif ($i eq 'all') { print STDERR "Ignoring 'all' option\n"; } elsif ($i =~ /^-[dn]/) { $DEBUG = 1; } elsif ($i =~ /^-b/) { $KRB_BASE = shift(@ARGV); if ( ! -d $KRB_BASE) { print STDERR "$PROGRAM: Kerberos base directory \"$KRB_BASE\""; print STDERR " does not exist\n"; $error = 1; } } elsif ($i =~ /^-r/) { $KRB_REALM = shift(@ARGV); if (!defined($REALM2HASH{$KRB_REALM})) { print STDERR "$PROGRAM: Don't know about Kerberos realm \"$KRB_REALM\""; $error = 1; } } else { print STDERR "$PROGRAM: Bad argument '$i'\n"; $error = 1; } } # catch errors # if ($error) { print STDERR "Usage: $PROGRAM [-x(pert-mode)]\n"; exit 1; } # let 'em know if they're debugging # print "Now in debugging mode. No changes will be made.\n" if ($DEBUG); ($expert); } ###################################################################### # # Return 1 if $path is an NFS-mounted filesystem, 0 otherwise # NOTE: This assumes that stat() returns a negative number in # 'st_dev' for an NFS-mounted filesystem # sub nfs_mounted_path { local($path) = @_; local($d,$_); while ($path) { return($d < 0) if (-e $path && (($d) = stat(_))); $path =~ s/[^\/]+\/*$//; } # something's hosed # return -1; } ###################################################################### # # Ask user what to do about Kerberos binaries directory # -1 = do nothing, 0 = symlink, 1 = copy files # sub ask_about_local_path { local($question, $response); # don't do anything if KRB_LOCAL_PATH is on an NFS filesystem # return -1 if (&nfs_mounted_path($KRB_LOCAL_PATH)); # see how KRB_LOCAL_PATH is handled currently # if ( -e $KRB_LOCAL_PATH ) { if ( -l $KRB_LOCAL_PATH ) { # see if we should replace the symlink with a directory # print STDOUT "$KRB_LOCAL_PATH is currently a symbolic link.\n"; $question = "Should this be changed to a local directory? "; return 1 if (&ask_yes_or_no($question) eq 'y'); # make sure the symlink is what we think it should be # return (readlink($KRB_LOCAL_PATH) eq $KRB_REMOTE_PATH ? -1 : 0); } elsif ( -d $KRB_LOCAL_PATH) { # see if we should update the directory # print STDOUT "$KRB_LOCAL_PATH is currently a local directory.\n"; $question = "Should the binaries be updated? "; return (&ask_yes_or_no($question) eq 'y' ? 1 : -1); } } # ask about KRB_LOCAL_PATH # print STDOUT "$KRB_LOCAL_PATH doesn't exist.\n"; $question = "Should $KRB_LOCAL_PATH be a copy of $KRB_REMOTE_PATH\n"; $question .= "or just a symbolic link to it? "; $response = &ask_question($question, 0, 'c', 's', 'l', 'copy', 'link'); $response = substr($response, 0, 1); return($response eq 'c'); } ###################################################################### # # Fill $string with enough tabs & spaces to reach $column # # NOTE: calling tabpad() with the result of a previous tabpad() # probably won't work, because length("\t") is 1 not 8 # sub tabpad { local($string, $column) = @_; local($l) = (length($string)); local($spaces, $needed) = ($column & 0x7, $column - $l); # make sure we don't add too many spaces # $spaces = $needed if ($needed < $spaces); # add tabs # $needed -= $spaces; while ($needed > 0) { $string .= "\t"; $needed -= 8; } # add spaces # while ($spaces-- > 0) { $string .= ' '; } return $string; } ###################################################################### # # Make sure krb.srvtab exists # sub check_krb_srvtab { if ( ! -e $KRB_BASE . '/krb.srvtab' ) { print STDERR <<"+++++EOF+++++"; You do not have a services table installed in $KRB_BASE/krb.srvtab. +++++EOF+++++ print STDERR <<'+++++EOF+++++'; This file is different for every host, and must match the registration in the Kerberos server. You can get a copy from Keith Sklower (sklower@cs, 2-9587) or Michael Short (mshort@cs, 2-5836). +++++EOF+++++ } } ###################################################################### # # Build the list of Kerberos services # sub build_kservices { local($_, $name, $port, $alias, $comment, $number, $proto, $key); # clear global variable and arrays # %KSRVPORT = %KSRVALIAS = %KSRVCOMMENT = (); $KSRV_SEARCH = ''; # fill global variable and arrays # foreach $_ (split(/\n/, $KRB_SERVICE_LIST)) { if (/^(\S*)\s*(\d*\/\S*)\s*([^#\s]*)\s*#*\s*(.*)$/) { ($name, $port, $alias, $comment) = ($1, $2, $3, $4); ($number, $proto) = split(/\//, $port); $key = "$name/$proto"; $KSRVPORT{$key} = $port; $KSRVALIAS{$key} = $alias; $KSRVCOMMENT{$key} = $comment; $KSRV_SEARCH = "$KSRV_SEARCH^$name|"; } else { print STDERR "$PROGRAM: Wierd Kerberos services line '$_'\n"; } } # lose final '|' in search string # chop($KSRV_SEARCH); } ###################################################################### # # Copy old services to new file, filtering out incorrect # Kerberos entries # sub copy_services { local(*ORIG, *NEW) = @_; local($name, $port, $alias, $comment, $number, $proto, $key); local($_); while () { if (! /$KSRV_SEARCH/) { print NEW $_ if (!$DEBUG); } else { if (/^(\S*)\s*(\d*\/\S*)\s*([^#\s]*)\s*#*\s*(.*)$/) { # break this line into usable pieces # ($name, $port, $alias, $comment) = ($1, $2, $3, $4); ($number, $proto) = split(/\//, $port); $key = "$name/$proto"; if ($KSRVPORT{$key} ne $2 || $KSRVALIAS{$key} ne $3 || $KSRVCOMMENT{$key} ne $4) { # let user know this line was changed # print STDERR "$PROGRAM: Changing /etc/services entry for '$1'\n"; } else { # don't move this line # print NEW $_ if (!$DEBUG); delete $KSRVPORT{$key}; $remainder = 1; } } } } close(ORIG); $remainder; } ###################################################################### # # compare two KSRVPORT keys # sub by_port { local($an,$ap,$bn,$bp,$rval); ($an,$ap) = split(/\//, $KSRVPORT{$a}); ($bn,$bp) = split(/\//, $KSRVPORT{$b}); $rval = $an <=> $bn; $rval = $bn cmp $an if (!$rval); $rval; } ###################################################################### # # if there are unlisted Kerberos lines, add them to the end of the file # sub add_kservices { local($remainder, *NEW) = @_; local($key, @keys); local($name, $port, $alias, $comment); @keys = keys(%KSRVPORT); if (@keys > 0) { # change comment if there are already Kerberos services in the file # if ($remainder == 0) { print NEW "#\n# Kerberos (Project Athena/MIT) services\n#\n"; } else { print NEW "#\n# Remainder of Kerberos (Project Athena/MIT) services\n#\n"; } # write Kerberos services # foreach $key (sort by_port @keys) { # print name # ($name) = split(/\//, $key); print NEW &tabpad($name, 16); # copy data into local variables # ($port, $alias, $comment) = ($KSRVPORT{$key}, $KSRVALIAS{$key}, $KSRVCOMMENT{$key}); if ($alias eq '' && $comment eq '') { # this is the end of the line # print NEW $port,"\n"; } else { # format port string, print port & alias # print NEW &tabpad($port,16); # print comment (if there is one) # if ($comment) { print NEW &tabpad($alias, 16),'# ',$comment; } else { print NEW $alias; } print NEW "\n"; } } # remember that we updated /etc/services # $ETC_SERVICES = 1; } print NEW "+++++EOF+++++\n" if ($DEBUG); close(NEW); # remove file if it's a copy of the original # if (! $ETC_SERVICES ) { if ($DEBUG) { print "\nrm /etc/services.krb\n"; print "echo Your /etc/services file is already correct\n\n"; } else { unlink '/etc/services.krb'; } } } ###################################################################### # # Add kerberos services to /etc/services # sub update_services { local($remainder) = (0); # break Kerberos services up into more usable pieces # &build_kservices(); # return if we can't read old services file # if (!open(ORIG, "/etc/services")) { print STDERR "$PROGRAM: Couldn't read '/etc/services'\n"; return; } # return if we can't write new services file # if ($DEBUG) { open(NEW, ">-") || die "$PROGRAM: Couldn't clone STDOUT\n"; print NEW "\f\ncat >/etc/services.krb << +++++EOF+++++\n"; } else { if (!open(NEW, ">/etc/services.krb")) { print STDERR "$PROGRAM: Couldn't create '/etc/services.krb'\n"; return; } } # write old services to new file # $remainder = ©_services(*ORIG, *NEW); # add Kerberos services to end of new file # &add_kservices($remainder, *NEW); # get rid of Kerberos services data # undef %KSRVPORT; undef %KSRVALIAS; undef %KSRVCOMMENT; } ###################################################################### # # Build the list of Kerberos daemons # sub build_kinetd { local($l); local($name, $socket, $proto, $flags, $user, $path, $args); # clear global variable and arrays # %KNETSOCKET = %KNETPROTO = %KNETFLAGS = %KNETUSER = %KNETPATH = %KNETARGS = (); $KNET_SEARCH = ''; # fill global variable and arrays # foreach $l (split(/\n/, $KRB_DAEMON_LIST)) { ($name, $socket, $proto, $flags, $user, $path, $args) = split(/\s+/, $l, 7); $KNETSOCKET{$name} = $socket; $KNETPROTO{$name} = $proto; $KNETFLAGS{$name} = $flags; $KNETUSER{$name} = $user; $KNETPATH{$name} = $path; $KNETARGS{$name} = $args; $KNET_SEARCH = "$KNET_SEARCH^$name|"; } # lose final '|' in search string # chop($KNET_SEARCH); } ###################################################################### # # Copy old daemons to new file, filtering out incorrect # Kerberos entries # sub copy_inetd { local(*ORIG, *NEW) = @_; local($name, $socket, $proto, $flags, $user, $path, $args); while () { if (! /$KNET_SEARCH/) { if ($DISABLE_RDAEMONS && /^login|^shell/) { print NEW '#',$_ if (!$DEBUG); # let user know that we changed /etc/inetd.conf # ($name) = split(' ', $_, 2); print STDERR "$PROGRAM: Disabled '$name' in /etc/inetd.conf\n"; $ETC_INETD_CONF = 1; } else { print NEW $_ if (!$DEBUG); } } else { # break this line into usable pieces # chop; ($name, $socket, $proto, $flags, $user, $path, $args) = split(' ', $_, 7); if ($KNETSOCKET{$name} ne $socket || $KNETPROTO{$name} ne $proto || $KNETFLAGS{$name} ne $flags || $KNETUSER{$name} ne $user || $KNETPATH{$name} ne $path || $KNETARGS{$name} ne $args) { # let user know this line was changed # print STDERR "$PROGRAM: Changing /etc/inetd.conf entry for '$name'\n"; } else { # don't move this line # print NEW $_,"\n" if (!$DEBUG); delete $KNETSOCKET{$name}; $remainder = 1; } } } close(ORIG); $remainder; } ###################################################################### # # if there are unlisted Kerberos lines, add them to the end of the file # sub add_kinetd { local($remainder, *NEW) = @_; local($name, @keys); local($len, $path); @keys = keys(%KNETSOCKET); if (@keys > 0) { # change comment if there are already Kerberos daemons in the file # if ($remainder == 0) { print NEW "#\n# Kerberos authenticated services\n#\n"; } else { print NEW "#\n# Remainder of Kerberos authenticated services\n#\n"; } # write Kerberos daemons # foreach $name (sort {$KNETPATH{$a} cmp $KNETPATH{$b};} @keys) { print NEW "$name\t$KNETSOCKET{$name}\t$KNETPROTO{$name}\t"; print NEW "$KNETFLAGS{$name}\t$KNETUSER{$name}\t"; # fill out path # print NEW &tabpad($KNETPATH{$name}, 32),' ',$KNETARGS{$name},"\n"; } # remember that we updated /etc/inetd.conf # $ETC_INETD_CONF = 1; } print NEW "+++++EOF+++++\n" if ($DEBUG); close(NEW); # remove file if it's a copy of the original # if (! $ETC_INETD_CONF ) { if ($DEBUG) { print "\nrm /etc/inetd.conf.krb\n"; print "echo Your /etc/inetd.conf file is already correct\n\n"; } else { unlink '/etc/inetd.conf.krb'; } } } ###################################################################### # # Add kerberos daemons to /etc/inetd.conf # sub update_inetd { local($remainder) = (0); # break Kerberos daemons up into more usable pieces # &build_kinetd(); # return if we can't read old inetd.conf # if (!open(ORIG, "/etc/inetd.conf")) { print STDERR "$PROGRAM: Couldn't read '/etc/inetd.conf'\n"; return; } # return if we can't write new inetd.conf # if ($DEBUG) { open(NEW, ">-") || die "$PROGRAM: Couldn't clone STDOUT\n"; print NEW "\f\ncat >/etc/inetd.conf.krb << +++++EOF+++++\n"; } else { if (!open(NEW, ">/etc/inetd.conf.krb")) { print STDERR "$PROGRAM: Couldn't create '/etc/inetd.conf.krb'\n"; return; } } # write old daemons to new file # $remainder = ©_inetd(*ORIG, *NEW); # add Kerberos daemons to end of new file # &add_kinetd($remainder, *NEW); # get rid of Kerberos daemon data # undef %KNETSOCKET; undef %KNETPROTO; undef %KNETFLAGS; undef %KNETUSER; undef %KNETPATH; undef %KNETARGS; } ###################################################################### # # Check KRB_BASE/krb.conf realm and KDC servers # sub check_krb_conf { local(*CONF); local($realm, $host, $type, $_, $line); local(%translation); local(%validated); # make sure KDC host names are lowercase # for $host (keys(%KDC2REALM)) { $_ = $host; tr/A-Z/a-z/; $translation{$_} = $host if ($host ne $_); } # return empty list if we can't open krb.conf file # if (!open(CONF, "$KRB_BASE/krb.conf")) { print STDERR "$PROGRAM: Couldn't validate $KRB_BASE/krb.conf!\n"; return (); } # validate realm # chop($realm = ); $realm =~ tr/a-z/A-Z/; return () if ($realm ne $KRB_REALM); # validate all KDC servers # while ($line = ) { chop($line); ($realm, $host, $type) = split(' ', $line, 3); $realm =~ tr/a-z/A-Z/; # convert hostname to something we recognize # $_ = $host; tr/A-Z/a-z/; $host = $translation{$_} if (defined($translation{$_})); # remember result of validation # if (defined($KDC2REALM{$host}) && (!defined($type) || ($type eq '') || ($type =~ /admin\s+server/))) { $validated{$host} = 1; } else { print STDERR "$KRB_BASE/krb.conf: Unrecognized line '$line'\n"; print STDERR "\t"; print STDERR ' realm=',defined($realm) ? '"'.$realm.'"' : ''; print STDERR ' host=',defined($host) ? '"'.$host.'"' : ''; print STDERR ' type=',defined($type) ? '"'.$type.'"' : ''; print STDERR "\n"; push(@NOT_OUR_REALM, $line); } } close(CONF); # return the list of validated hosts # %validated; } ###################################################################### # # Compare two host names # sub by_name { $a cmp $b; } ###################################################################### # # Compare two realm names # sub by_realm { local($rtnval); $rtnval = $KDC2REALM{$a} cmp $KDC2REALM{$b}; $rtnval = $a cmp $b if ($rtnval == 0); $rtnval; } ###################################################################### # # Write KRB_BASE/krb.conf file # sub write_krb_conf { local($confname) = @_; local(*CONF); local($host, $realm, $_); # fail if we can't write new krb.conf file # if ($DEBUG) { open(CONF, ">-") || die "$PROGRAM: Couldn't clone STDOUT\n"; print CONF "\f\ncat >$KRB_BASE/$confname << +++++EOF+++++\n"; } else { if (!open(CONF, ">$KRB_BASE/$confname")) { print STDERR "$PROGRAM: Couldn't create '$KRB_BASE/$confname'\n"; return -1; } } # write our realm and entries to krb.conf # print CONF "$KRB_REALM\n"; foreach $host (sort by_name keys(%KDC2REALM)) { if ($KDC2REALM{$host} eq $KRB_REALM) { print CONF "$KRB_REALM $host"; print CONF " admin server" if ($host eq $KRB_ADMIN); print CONF "\n"; } } foreach $host (sort by_realm keys(%KDC2REALM)) { $realm = $KDC2REALM{$host}; if ($realm ne $KRB_REALM) { print CONF "$realm $host"; print CONF " admin server" if (defined($REALM2HASH{$realm}) && $host eq $KRB_ADMIN[$REALM2HASH{$realm}]); print CONF "\n"; } } # write remaining entries to krb.conf # while ($_ = shift(@NOT_OUR_REALM)) { print CONF "$_\n"; } undef @NOT_OUR_REALM; # close new file if we opened it # print CONF "+++++EOF+++++\n" if ($DEBUG); close(CONF); } ###################################################################### # # Return 1 if the existing KRB_BASE/krb.conf file contains # all the correct information for our realm # sub valid_krb_conf { local(%validated); local($host); # check old file for valid hosts # %validated = &check_krb_conf(); # return if all entries were validated # $ETC_KRB_CONF = 0; foreach $host (keys(%KDC2REALM)) { if (!defined($validated{$host}) || $validated{$host} <= 0) { $ETC_KRB_CONF = 1; last; } } return 1 if (!$ETC_KRB_CONF); &write_krb_conf('krb.conf.krb'); # KRB_BASE/krb.conf is correct # return 1; } ###################################################################### # # Update KRB_BASE/krb.conf file (backing up the old version # if it's incorrect for our realm) # sub update_krb_conf { # skip it if they've already got a valid KRB_BASE/krb.conf file # return if ( -f "$KRB_BASE/krb.conf" && &valid_krb_conf()); undef @NOT_OUR_REALM; &write_krb_conf('krb.conf'); } ###################################################################### # # Write %realms to KRB_BASE/$realmname # sub write_krb_realms { local($realmname, %realms) = @_; local(*REALMS); local($domain, $realm); # create a krb.realms file # if ($DEBUG) { open(REALMS, ">-") || die "$PROGRAM: Couldn't clone STDOUT\n"; print REALMS "\f\ncat >$KRB_BASE/$realmname << +++++EOF+++++\n"; } else { if (!open(REALMS, ">$KRB_BASE/$realmname")) { print STDERR "$PROGRAM: Couldn't create '$KRB_BASE/$realmname'!\n"; return; } } # write realm info # while (($domain, $realm) = each(%realms)) { print REALMS &tabpad($domain, 24),$realm,"\n" if ($realm eq $KRB_REALM); } while (($domain, $realm) = each(%realms)) { print REALMS &tabpad($domain, 24),$realm,"\n" if ($realm ne $KRB_REALM); } print REALMS "+++++EOF+++++\n" if ($DEBUG); close(REALMS); } ###################################################################### # # Check KRB_BASE/krb.realms data # sub check_krb_realms { local(*REALMS); local($domain, $realm, $_); local(%translation); local(%validated); while (($domain, $realm) = each(%KRB_REALMS)) { # make sure realm names are uppercase # $_ = $realm; tr/a-z/A-Z/; $KRB_REALMS{$domain} = $_ if ($realm ne $_); # make sure host/domain names are lowercase # $_ = $domain; tr/A-Z/a-z/; $translation{$_} = $domain if ($domain ne $_ && !defined($KRB_REALMS{$_})); } # open realm file # if (!open(REALMS, "$KRB_BASE/krb.realms")) { print STDERR "$PROGRAM: Couldn't validate $KRB_BASE/krb.realms!\n"; return (); } # validate all host/domain-to-realm mappings # while () { chop; ($domain, $realm) = split(' ', $_, 2); # standardize host/domain and realm # if (!defined($KRB_REALMS{$domain})) { $domain =~ tr/A-Z/a-z/; $domain = $translation{$domain} if (defined($translation{$domain})); } $realm =~ tr/a-z/A-Z/; # this entry is wrong, fail validation # if (!defined($KRB_REALMS{$domain})) { print STDERR "$PROGRAM: Preserving domain '$domain' for realm '$realm'\n" if ($DEBUG); $NEW_REALMS{$domain} = $realm; } elsif ($KRB_REALMS{$domain} ne $realm) { print STDERR "$PROGRAM: Preserving domain '$domain' for realm"; print STDERR " '$KRB_REALMS{$domain}'\n\t\t(should be '$realm')\n"; delete $KRB_REALMS{$domain}; $NEW_REALMS{$domain} = $realm; } else { # remember that we validated this host # $validated{$domain} = 1; $OLD_REALMS{$domain} = $realm; } } close(REALMS); # return the list of validated host/domains # %validated; } ###################################################################### # # Return 1 if the existing KRB_BASE/krb.realms file contains # all the correct information for our realm # sub valid_krb_realms { local(*ORIG, *NEW); local(%validated); local(@rkeys); local($domain, $realm, $_); # check old file for valid hosts/domains # %validated = &check_krb_realms(); # make sure all lines were validated # foreach $domain (keys(%KRB_REALMS)) { # if this host wasn't in krb.realms... # $NEW_REALMS{$domain} = $KRB_REALMS{$domain} if (!defined($validated{$domain})); } # if there are extra realms in this system's krb.realms... # @rkeys = keys(%NEW_REALMS); if (@rkeys > 0) { # merge old and new realms # # while ($domain,$realm) = each(%OLD_REALMS)) { # $NEW_REALMS{$domain} = $realm; # } &write_krb_realms('krb.realms.krb', %OLD_REALMS, %NEW_REALMS); $ETC_KRB_REALMS = 1; } # krb.realms is correct # return 1; } ###################################################################### # # Update KRB_BASE/krb.realms file (backing up the old version # if it's incorrect for our realm) # sub update_krb_realms { local(*REALMS); local($domain, $realm, $l); # skip it if they've already got a valid krb.realms file # return if ( -f "$KRB_BASE/krb.realms" && &valid_krb_realms()); &write_krb_realms('krb.realms', %KRB_REALMS); } ###################################################################### # # Move old $KRB_LOCAL_PATH out of the way # sub backup_local_path { # move old version out of the way # if (-e $KRB_LOCAL_PATH) { if ($DEBUG) { print "mv $KRB_LOCAL_PATH $KRB_LOCAL_PATH.old\n"; } elsif (!rename($KRB_LOCAL_PATH, "$KRB_LOCAL_PATH.old")) { print STDERR "$PROGRAM: Couldn't rename $KRB_LOCAL_PATH"; print STDERR " to $KRB_LOCAL_PATH.old\n"; } else { print "Old $KRB_LOCAL_PATH moved to $KRB_LOCAL_PATH.old\n"; } } } ###################################################################### # # Copy $oldfile to $newfile # sub copy_file { local($oldfile, $newfile) = @_; local(*OLD, *NEW); local($len, $buf, $offset, $written); if ($DEBUG) { print "cp $oldfile $newfile\n"; return 1; } # open old file # if (!open(OLD, $oldfile)) { print STDERR "$PROGRAM: Couldn't read $oldfile: $!\n"; return 0; } # open new file # if (!open(NEW, ">$newfile")) { print STDERR "$PROGRAM: Couldn't write to $newfile: $!\n"; return 0; } # copy old file to new file # while ($len = sysread(OLD, $buf, 2048)) { # handle errors # if (!defined($len)) { next if $! =~ /^Interrupted/; close(OLD); close(NEW); print STDERR "$PROGRAM: read error: $!\n"; return 0; } # write this block # $offset = 0; while ($len) { $written = syswrite(NEW, $buf, $len, $offset); if (!defined($written)) { close(OLD); close(NEW); print STDERR "$PROGRAM: write error: $!\n"; return 0; } $len -= $written; $offset += $written; } } # clean up # close(OLD); close(NEW); return 1; } ###################################################################### # # Set mode/owner/times for $file # sub set_file_stats { local($file, $mode, $uid, $gid, $atime, $mtime) = @_; local($name); if ($DEBUG) { # look up username if we haven't yet # if (!defined($UIDNAME{$uid})) { if (($name) = getpwuid($uid)) { $UIDNAME{$uid} = $name; } else { $UIDNAME{$uid} = $uid; } } # look up groupname if we haven't yet # if (!defined($GIDNAME{$gid})) { if (($name) = getgrgid($gid)) { $GIDNAME{$gid} = $name; } else { $GIDNAME{$gid} = $uid; } } print "chown $UIDNAME{$uid}.$GIDNAME{$gid} $file\n"; printf "chmod %lo $file\n", $mode & 07777; ($sec,$min,$hour,$mday,$mon,$year) = localtime($atime); printf "touch -a %02d%02d%02d%02d%02d $file\n", $mon, $mday, $hour, $min, $year; ($sec,$min,$hour,$mday,$mon,$year) = localtime($mtime); printf "touch -m %02d%02d%02d%02d%02d $file\n", $mon, $mday, $hour, $min, $year; } else { chown $uid, $gid, $file; chmod $mode, $file; utime $atime, $mtime, $file; } } ###################################################################### # # Make a copy of directory $olddir at $newdir # sub copy_tree { local($olddir, $newdir) = @_; local(*DIR); local($file); local($x, $mode, $uid, $gid, $atime, $mtime); local($name); # open old directory # if (!opendir(DIR, $olddir)) { print STDERR "$PROGRAM: Couldn't read directory $olddir: $!\n"; return 0; } # create new directory # if ($DEBUG) { print "mkdir $newdir\n"; } elsif ( ! -d $newdir && !mkdir($newdir, 0755)) { print STDERR "$PROGRAM: Couldn't create new directory $newdir: $!\n"; return 0; } # copy all non-directories # for $file (readdir(DIR)) { # ignore special entries # if ($file ne '.' && $file ne '..') { # get info on this file # ($x,$x,$mode,$x,$uid,$gid,$x,$x,$x,$atime,$mtime) = stat("$olddir/$file"); if ( -d _) { ©_tree("$olddir/$file", "$newdir/$file"); } else { ©_file("$olddir/$file", "$newdir/$file"); } # update stats on file # &set_file_stats("$newdir/$file", $mode, $uid, $gid, $atime, $mtime); } } closedir(DIR); return 1; } ###################################################################### # # Copy binaries from $KRB_REMOTE_PATH to $KRB_LOCAL_PATH # sub copy_binaries { print "\f\n" if ($DEBUG); &backup_local_path(); # recursively copy remote to local # print "Copying $KRB_REMOTE_PATH to $KRB_LOCAL_PATH\n"; ©_tree($KRB_REMOTE_PATH, $KRB_LOCAL_PATH); } ###################################################################### # # Make a symlink from $KRB_REMOTE_PATH to $KRB_LOCAL_PATH # sub link_binaries { return if (-l $KRB_LOCAL_PATH && readlink($KRB_LOCAL_PATH) eq $KRB_REMOTE_PATH); print "\f\n" if ($DEBUG); &backup_local_path(); # make symlink to remote path # if ($DEBUG) { print "ln -s $KRB_REMOTE_PATH $KRB_LOCAL_PATH\n"; } elsif (!symlink($KRB_REMOTE_PATH, $KRB_LOCAL_PATH)) { print STDERR "$PROGRAM: Couldn't create a symlink to $KRB_REMOTE_PATH"; print STDERR " at $KRB_LOCAL_PATH\n"; } else { print "Linked $KRB_REMOTE_PATH to $KRB_LOCAL_PATH\n"; } } ###################################################################### # # Make sure $KRB_LOCAL_PATH exists # sub verify_local_path { if ($DEBUG && ! -e $KRB_LOCAL_PATH ) { print "\f\n"; print "Can't create $KRB_LOCAL_PATH, on NFS-mounted file system\n"; print "Make sure the appropriate person is notified\n"; } } ###################################################################### # # Move a new system file into place, # backing up old file if it exists # sub install_new_file { local($name, $oldext, $newext) = @_; # back up old file (unless it's already backed up) # if (-e $name && ! -e "$name.$oldext") { if ($DEBUG) { print "mv $name $name.$oldext\n"; } else { rename($name, "$name.$oldext"); print "Original $name file saved to $name.$oldext\n"; } } # install new file, destroying old file if it exists # if ($DEBUG) { print "mv $name.$newext $name\n"; } else { rename("$name.$newext", $name); } } ###################################################################### # # Replace rsh and rlogin with Kerberized versions # sub update_ucb { local($file, $d, $dir, $olddir); local($x,$mode,$uid,$gid,$atime,$mtime); # go through all possible incarnations of rsh/rlogin # foreach $file ('rsh', 'rlogin', 'remsh') { # check common directories # $dir = undef; foreach $d ('/usr/ucb', '/usr/bin') { # quit the loop if we found it # if ( -x "$d/$file" ) { $dir = $d; last; } } # if we found this file # if ($dir) { # make sure Kerberized version exists # if (-x "$KRB_LOCAL_PATH/bin/$file" ) { $olddir = $KRB_LOCAL_PATH; } elsif ( -x "$KRB_REMOTE_PATH/bin/$file") { $olddir = $KRB_REMOTE_PATH; } else { $olddir = undef; print STDERR "$PROGRAM: Kerberized version of $dir/$file not found\n"; } if (defined $olddir) { # copy Kerberized program to system directory # ©_file("$olddir/bin/$file", "$dir/$file.krb"); # install new version # &install_new_file("$dir/$file", 'pre-krb', 'krb'); # update stats on file # ($x,$x,$mode,$x,$uid,$gid,$x,$x,$x,$atime,$mtime) = stat("$olddir/bin/$file"); &set_file_stats("$dir/$file", $mode, $uid, $gid, $atime, $mtime); } } } } ###################################################################### # # Delete unKerberized rshd and rlogind # sub disable_rdaemons { local($file, $d, $dir); # go through all possible incarnations of rshd/rlogind # foreach $file ('rshd', 'in.rshd', 'remshd', 'rlogind', 'in.rlogind') { # check common directories # $dir = undef; foreach $d ('/usr/etc', '/etc') { # quit the loop if we found it # if ( -x "$d/$file" ) { $dir = $d; last; } } # if we found this daemon... # if ($dir) { # back it up (unless there's already an archival copy # if (! -e "$dir/$file.pre-krb") { if ($DEBUG) { print "mv $dir/$file $dir/$file.pre-krb\n"; } else { rename("$dir/$file", "$dir/$file.pre-krb"); print "Old $dir/$file file saved to $dir/$file.pre-krb\n"; } } else { if ($DEBUG) { print "rm $dir/$file\n"; } else { unlink("$dir/$file"); print "Old $dir/$file removed (because $dir/$file.pre-krb exists)\n"; } } } } } ###################################################################### # # Add links for Kerberos binaries to /usr/bin # sub usr_bin_symlinks { local($name, $source, $target); foreach $name ('rlogin', 'rsh', 'kinit', 'klist') { # build source and target names # $source = '/usr/kerberos/bin/' . $name; if ($name =~ /^k/) { $target = '/usr/bin/' . $name; } else { $target = '/usr/bin/k' . $name; } # skip this target if it's a symlink that points to the right place # next if (-l $target && readlink($target) eq $source); # create new symlink # if ($DEBUG) { print "rm -f $target.krb\n" if (-e $target . '.krb'); print "ln -s $source $target.krb\n"; } else { unlink($target . '.krb') if (-e $target . '.krb'); symlink($source, $target . '.krb'); } &install_new_file($target, 'pre-krb', 'krb'); } } ###################################################################### # # Let user know if NIS map needs to be updated # sub nis_map_notify { local($server, $service); local($_); # make sure the NIS server know about kerberos # chop($service = `/usr/bin/ypmatch 750/udp services 2>/dev/null`) if ( -x '/usr/bin/ypmatch'); # return if everything's groovy # return if ($service =~ /^kerberos/); # get NIS 'services' server # chop($server = `/usr/bin/ypwhich -m services 2>/dev/null`) if ( -x '/usr/bin/ypwhich'); # return if there's no NIS server # return if (!$server); # remind user to upgrade NIS server # print "\n"; print "*****************************************************************\n"; print "* *\n"; print "* You'll need to update and distribute the NIS 'services' map *\n"; $_ = "on $server before Kerberos will work "; print "* ",substr($_,0,59)," *\n"; print "* *\n"; print "*****************************************************************\n"; } ###################################################################### # # Rebuild the NIS files if necessary # sub rebuild_nis { local(*YPMAKE); local($domain); # don't need to do anything if /etc/services wasn't touched # return if (! $ETC_SERVICES); # get NIS domain # if ( -x '/bin/domainname') { chop($domain = `/bin/domainname`); } elsif ( -x '/usr/bin/domainname') { chop($domain = `/usr/bin/domainname`); } # make sure this is an NIS server # return &nis_map_notify() if ( ! -d "/var/yp/$domain"); # make sure they have an NIS services database # return if (! -e "/var/yp/services.time"); # let user know what's happening # if ($DEBUG) { print "\f\necho Need to rebuild NIS services database\n"; print "(cd /var/yp && make services)\n"; } else { if (!open(YPMAKE, "cd /var/yp && make services 2>&1 |")) { print STDERR "$PROGRAM: Couldn't rebuild the NIS services database!\n"; } else { print "Rebuilding NIS services database ...\n"; while () { print $_; } close(YPMAKE); print "NIS services database has been rebuilt.\n"; } } } ###################################################################### # # Send a SIGHUP to inetd to have it reload /etc/inetd.conf # sub reload_inetd_conf { local($pid) = (-1); local(*PSOUT, $junk, $_); # try to get process info # open(PSOUT, "ps ax 2>&1 |") || return; while () { if (/inetd/ && !/grep/) { s/^\s+//; ($pid) = split; last; } } close(PSOUT); # if we didn't get a PID... # if ($pid == -1) { # try again with SysVish options # open(PSOUT, "ps -ef 2>&1 |") || return; while () { if (/inetd/ && !/grep/) { s/^\s+//; ($junk,$pid) = split; last; } } close(PSOUT); } # send a HUP to inetd # if ($pid != -1) { if ($DEBUG) { print "\necho Reloading inetd.conf\nkill -HUP $pid\n"; } else { kill 'HUP', $pid; } } else { print STDERR "$PROGRAM: Couldn't reconfigure inetd!\n"; } } ###################################################################### # # Move new system files into place # sub finalize { print "\f\n" if ($DEBUG); &install_new_file('/etc/services', 'pre-krb', 'krb') if ($ETC_SERVICES); &install_new_file('/etc/inetd.conf', 'pre-krb', 'krb') if ($ETC_INETD_CONF); &install_new_file('/etc/krb.conf', 'pre-krb', 'krb') if ($ETC_KRB_CONF); &install_new_file('/etc/krb.realms', 'pre-krb', 'krb') if ($ETC_KRB_REALMS); &rebuild_nis(); &reload_inetd_conf() if ($ETC_INETD_CONF); if (! -e '/usr/kerberos' && &nfs_mounted_path('/usr')) { print "\n"; print "*****************************************************************\n"; print "* *\n"; print "* Remember to set up the /usr/kerberos symlink on *\n"; print "* the NFS server for /usr. *\n"; print "* *\n"; print "*****************************************************************\n"; } }