1#!/usr/bin/perl 2 3use strict; 4use warnings; 5 6use Getopt::Long; 7 8use IO::Socket::INET; 9 10use GSSAPI; 11use MIME::Base64; 12 13my %opt; 14 15unless(GetOptions(\%opt, qw(prodid=s hostname=s port=s mutual))) { 16 print "$0 needs arguments, provide at least -prodid and -hostname, optionally -port (defauly 10000) or -mutual (for two sided authentication)\n"; 17 exit(1); 18} 19 20if(! $opt{hostname}) { 21 die "$0: must specify -hostname\n"; 22} 23 24if(! $opt{prodid}) { 25 die "$0: must specify -prodid\n"; 26} 27 28if(! $opt{port}) { 29 warn "$0: -port not specified, defaulting to 10000\n"; 30 $opt{port} = 10000; 31} 32 33if(! $opt{prodid}) { 34 $opt{prodid} = "host"; 35} 36 37warn "$0: using [$opt{prodid}\@$opt{hostname}:$opt{port}]\n"; 38 39 40# 41# GSSAPI::Name->import produces $gss_server_name 42# which is then passed in to GSSAPI::Context::init 43# $gss_server_name represents the principcal name 44# of the app server to which we are authenticating 45# 46 47my $server_name = "$opt{prodid}\@$opt{hostname}"; 48my $status = GSSAPI::Name->import(my $gss_server_name, $server_name, gss_nt_service_name); 49$status || gss_exit("CLIENT::Unable to import server name: $server_name", $status); 50 51$status = $gss_server_name->display(my $display_name, my $type); 52print "CLIENT::principal [$server_name] means going to communicate with server name [$display_name]\n"; 53 54my $gss_input_token = q{}; 55 56my $socket = IO::Socket::INET->new 57 ( 58 PeerAddr => $opt{hostname}, 59 PeerPort => $opt{port}, 60 Proto => 'tcp', 61 Type => SOCK_STREAM, 62 ); 63 64die "socket/connect: $!\n" unless ($socket); 65 66# 67# The main purpose of GSSAPI::Context::init is to produce 68# an authentication token ($gss_output_token) which will 69# be sent by the app client to app server. Note the output 70# is binary data. 71# 72 73my $gss_auth_flags; 74if($opt{mutual}) { 75 $gss_auth_flags = GSS_C_MUTUAL_FLAG; 76} else { 77 $gss_auth_flags = 0; 78} 79 80my $client_context; 81 82my $counter = 0; 83my $error = 0; 84 85do { 86 $counter++; 87 88 $status = GSSAPI::Context::init($client_context, # output context 89 GSS_C_NO_CREDENTIAL, 90 $gss_server_name, # authenticate to this name 91 GSS_C_NO_OID, # use default mechanism (krb5) 92 $gss_auth_flags, # input flags 93 0, # input time 94 GSS_C_NO_CHANNEL_BINDINGS, # no channel binding 95 $gss_input_token, # input token 96 my $out_mech, 97 my $gss_output_token, 98 my $out_flags, 99 my $out_time); 100 101 $status || gss_exit("CLIENT::Unable to initialize security context", $status); 102 103 print "CLIENT::gss_init_sec_context success\n"; 104 105 # The GSS protocol can do mutual authentication. If this is requested, the token 106 # that we generate in the first pass will indicate this to the server. The major 107 # status will have the GSS_S_CONTINUE_NEEDED bit set to indicate that we are 108 # expecting a reply with a server identity token. This loop will continue until 109 # that bit is no longer set. It should go through only once (non-mutual) or twice 110 # (mutual). 111 112 if ($counter == 1) { 113 print "CLIENT::going to identify client to server\n"; 114 } elsif ($counter == 2) { 115 print "CLIENT::confirmed server identity from mutual token\n"; 116 my $server_name; 117 $status = $gss_server_name->display($server_name); 118 $status || gss_exit("CLIENT::Unable to display server name", $status); 119 print "CLIENT::authenticated server name is $server_name\n" if $server_name; 120 121 } else { 122 print "CLIENT::iteration [$counter] successful, but should not be here\n"; 123 } 124 125 if($gss_output_token) { 126 print "CLIENT::have token to send ...\n"; 127 print "CLIENT::GSS token length is " . length($gss_output_token) . "\n"; 128 129 # 130 # $gss_output_token is binary data 131 # 132 133 print $socket encode_base64($gss_output_token, '') . "\n"; 134 print "CLIENT::sent token to server\n"; 135 } 136 137 if ($status->major & GSS_S_CONTINUE_NEEDED) { 138 print "CLIENT::Mutual auth requested ...\n"; 139 $gss_input_token = <$socket>; 140 if ($gss_input_token) { 141 print "CLIENT::got mutual auth token from server\n"; 142 $gss_input_token = decode_base64($gss_input_token); 143 print "CLIENT::mutual auth token length is " . length($gss_input_token) . "\n"; 144 } else { 145 print "CLIENT::server did not send needed continue token back\n"; 146 $error = 1; 147 } 148 } 149} while (!$error and $status->major & GSS_S_CONTINUE_NEEDED); 150 151$socket->shutdown(2); 152 153exit(0); 154 155################################################################################ 156 157sub gss_exit { 158 my $errmsg = shift; 159 my $status = shift; 160 161 my @major_errors = $status->generic_message(); 162 my @minor_errors = $status->specific_message(); 163 164 print STDERR "$errmsg:\n"; 165 foreach my $s (@major_errors) { 166 print STDERR " MAJOR::$s\n"; 167 } 168 foreach my $s (@minor_errors) { 169 print STDERR " MINOR::$s\n"; 170 } 171 exit(1); 172} 173 174