#! /usr/bin/perl $ID = q($Id: krsh 3213 2007-06-06 07:19:21Z rra $ ); $VERSION = '1.14'; # # krsh -- Forward tickets when running rsh. # # Copyright 1996, 2001, 2002, 2003, 2005, 2007 # Board of Trustees, Leland Stanford Jr. University ############################################################################## # Site configuration ############################################################################## # Find locations of Kerberos programs. These directories are searched before # the user path for programs run on the local host. For programs run on the # remote host, the user's path is always used. /usr/pubsw/bin is the standard # Stanford location, /usr/local/bin is used by stow packages, and # /etc/leland/bin was used by Kerberos kits. @PATH = qw(/usr/pubsw/bin /usr/local/bin /etc/leland/bin); ############################################################################## # Modules and declarations ############################################################################## require 5.004; use Getopt::Long qw(GetOptions); use strict; use vars qw(%CONFIG $ID @PATH %PATH $VERSION); ############################################################################## # Option parsing ############################################################################## # Print out a brief help message and exit. (Used for -h or --help on the # command line). sub usage { print <<"EOM"; Usage: krsh [-hvfqV] [-l username] host command krsh optionally forwards your Kerberos TGT to the remove host and then performs a Kerberized rsh to that system. If your ticket is forwarded, it wraps the supplied command with aklog before the command and kdestroy and unlog after the command, to obtain an AFS token and to clean it and the ticket up after the command has executed. -h Print this message and exit. -v Print the version number and exit. -f Do not forward your ticket. -l user Log in as user (defaults to the local username). -q Tell kftgt to be quiet. -V Display each command before executing it. -x Encrypt the connection. EOM exit 0; } # Get the fully qualified name for a host. We do this because the real name # may vary from the name given on the command line if they're going to a # load-balanced address and because we want to trim off the domain if it's the # same as our domain. sub qualify { my ($host) = @_; my ($type, $realhost) = (gethostbyname $host)[2,4]; $realhost = gethostbyaddr ($realhost, $type); die "$0: unknown host $host\n" unless $realhost; eval { require Net::Domain }; unless ($@) { my $domain = Net::Domain::hostdomain (); $realhost =~ s/\Q.$domain\E$//i; } $realhost; } # Find a programs that we care about. We search @PATH and then the user's # PATH. It would be better to search the user's PATH first, but that makes it # too likely that we'll find a non-Kerberos rsh. If we can't find the # executable, we return the first path in @PATH (which will probably then fail # later). sub search_path { my $program = shift; for (@PATH, split (':', $ENV{PATH})) { return "$_/$program" if -x "$_/$program"; } return "$PATH[0]/$program"; } # Set up the %PATH hash with the locations of the programs we care about, # honoring environment variables for each one. sub find_programs { $PATH{kftgt} = $ENV{'KLOGIN_KFTGT_PROGRAM'} || search_path 'kftgt'; $PATH{rsh} = $ENV{'KLOGIN_RSH_PROGRAM'} || search_path 'rsh'; } # Parse command-line options, putting the results into the global %CONFIG # hash. Note that krsh *reverses* the default meaning of -f, but does *not* # reverse the default meaning of -x (unlike klogin, which reverses both of # them). Note that it also doesn't support all of the various rsh flags. # Returns the host and command. sub parse_options { my ($host, $command); Getopt::Long::config ('bundling_override'); GetOptions (\%CONFIG,'help|h', 'version|v', 'noforward|f', 'login|user|l=s', 'quiet|q', 'verbose|V', 'encrypt|x') or exit 1; # If they requested usage or version, give them that and exit. if ($CONFIG{help}) { usage } if ($CONFIG{version}) { my @version = split (' ', $ID); shift @version if $ID =~ /^\$Id/; $version[1] = $VERSION; my $version = join (' ', @version[0..2]); $version =~ s/,v\b//; $version =~ s/(\S+)$/($1)/; $version =~ tr%/%-%; print $version, "\n"; exit; } # I'd really prefer not to support this, but we're stuck with it. Which # means I should go back and add in support for all of the flags we # support. And some way to turn off on the command line something that's # turned on in the environment.... local $_; if ($_ = $ENV{'KLOGIN_OPTS'}) { $CONFIG{noforward} ||= /f/; $CONFIG{verbose} ||= /V/; } # Check the arguments. $host = shift @ARGV; die "$0: no host specified (-h for help)\n" unless defined $host; $command = join (' ', @ARGV) if @ARGV; # Do a forward and then reverse name lookup on the hostname. This is # required because of our load-balancing system; otherwise, we may forward # our ticket to one host and then try to rsh to another. We also want to # pass rsh the correct hostname so that it knows which principal to use. $host = qualify ($host); return ($host, $command); } ############################################################################## # Running programs ############################################################################## # Forward the user's tgt to the remote host using kftgt. We have to look for # a -l command line option and be sure to pass that along to kftgt as well, # and also honor the -q option. sub forward_ticket { my ($host) = @_; my @command = $PATH{kftgt}; push (@command, '-l', $CONFIG{login}) if $CONFIG{login}; push (@command, '-q') if $CONFIG{quiet}; push (@command, $host); print "@command\n" if $CONFIG{verbose}; my $status = system (@command); $status >>= 8; if ($status != 0 && $status != 3 && $status != 4 && $status != 5) { die "$0: $command[0] failed with exit status $status\n"; } } # Run rsh to the remote system. sub rsh { my ($host, $command) = @_; my @command = $PATH{rsh}; push (@command, '-l', $CONFIG{login}) if $CONFIG{login}; push (@command, '-f') unless $CONFIG{noforward}; push (@command, '-x') if $CONFIG{encrypt}; push (@command, $host); if ($command) { $command = "aklog; $command; sh -c 'kdestroy > /dev/null 2>&1'; unlog" unless $CONFIG{noforward}; push (@command, $command); } if ($CONFIG{verbose}) { my @print = @command; $print[-1] = "'$print[-1]'"; print "@print\n"; } system (@command) == 0 or die "$0: $command[0] failed with exit status ", ($? >> 8), "\n"; } ############################################################################## # Main routine ############################################################################## # Trim extraneous path garbage from the program name. $0 =~ s%.*/%%; # Find the programs we care about. find_programs; # Parse the command line options to determine what to do. my ($host, $command) = parse_options; # Optionally forward our ticket. forward_ticket ($host) unless $CONFIG{noforward}; # Do the actual rsh. rsh ($host, $command); ############################################################################## # Documentation ############################################################################## =head1 NAME krsh - Kerberos rsh with v4 ticket forwarding =head1 SYNOPSIS krsh [B<-fhqvVx>] [B<-l> I] I I ... =head1 DESCRIPTION B forwards your Kerberos ticket-granting ticket to the machine I using B and then executes I on that system using Kerberos B. It assumes that the remote system has B, B, and B installed on your default path on the remote system. If B fails with an error indicating that the remote system isn't running B, the user has no Kerberos v4 ticket cache, or the user has no F<.klogin> file on the remote system, B continues anyway (in case Kerberos v5 will work fine). Otherwise, B fails if B fails. Normally it's not necessary to forward one's ticket to execute commands remotely with B. The reason for the existence of this wrapper is to facilitate running programs that need to write to disk on a remote system that uses AFS. The commands given to B will be wrapped by an execution of B before the command to obtain an AFS token from the forwarded ticket and B and B after the command to destroy the forwarded ticket and obtained token. The host given to B is put through a forward and then reverse DNS lookup before being used, to resolve any CNAMEs to their canonical hosts and to handle load-balanced hosts or hosts with multiple A records. Please note that Kerberos B generally will not put your new session in a PAG, which means that any tokens that you get on the remote system will become available to all processes running outside a PAG with that same UID, and the B command added to B will similarly remove all tokens for all processes outside a PAG with that same UID. This means this command is often not what you want when running commands remotely as root and it may be better to use Kerberos B directly. =head1 OPTIONS =over 4 =item B<-f>, B<--noforward> Don't forward tickets to the remote host. This tells B not to run B and not pass the B<-f> flag to B for K5 ticket forwarding. This option essentially makes B function exactly like B except with the DNS resolution described above. =item B<-h>, B<--help> Print a summary of options and exit. =item B<-l> I, B<--login>=I Set the username on the remote system to I. This is the user to log in as as well as user to which to forward tickets. If this option is not given, the default will be the username on the local host. This option will often be necessary if the local username differs from the Kerberos principal name, since B and B differ on the default otherwise. =item B<-q>, B<--quiet> Tell B to not print out its initial message about forwarding your ticket. Some programs that use rsh are confused by the initial output. =item B<-V>, B<--verbose> Print out each command and the arguments used before it's executed. =item B<-v>, B<--version> Print the version number of B and exit. =item B<-x>, B<--encrypt> Encrypt the connection to the remote host. This is not the default because it only works with Kerberos v5 rsh. If the remote system does not have a Kerberos v5 keytab, or if you do not have a Kerberos v5 TGT, this will cause B to fail. =back =head1 BUGS Regular rsh options are not accepted and passed to rsh. =head1 AUTHORS B was originally written by Larry Schwimmer . Questions and bug reports may be sent to Russ Allbery , but please be aware that we only support Stanford affiliates and may not be able to help with problems at other sites. =head1 LICENSE Copyright 1996, 2001, 2002, 2003, 2005 Board of Trustees, Leland Stanford Jr. University All rights reserved. Export of this software from the United States of America may require a specific license from the United States Government. It is the responsibility of any person or organization contemplating export to obtain such a license before exporting. WITHIN THAT CONSTRAINT, permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Stanford University not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. Stanford University makes no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. =head1 SEE ALSO aklog(1), kdestroy(1), kftgt(1), klogin(1), rsh(1), unlog(1) =cut