2523433811713fe27245259d4f593036663a7ba6
[openafs.git] / src / tests / OpenAFS / Auth.pm
1 # This is -*- perl -*-
2
3 package OpenAFS::Auth;
4 use strict;
5 use warnings;
6 use OpenAFS::Dirpath;
7 use OpenAFS::ConfigUtils;
8
9 my $path = $OpenAFS::Dirpath::openafsdirpath;
10
11 #
12 # Create an auth type for the specified Kerberos implementation.
13 #
14 # parameters:
15 #   type   -- Kerberos implementation: mit, heimdal, kaserver
16 #   keytab -- path and name of the keytab file for mit and heimdal
17 #   cell   -- cell name. if not specified, attempts to find the 
18 #               cell name in the ThisCell configuration file.
19 #   realm  -- realm name. if not specified, assume the realm name
20 #               is the same as the cell name, in uppercase.
21 #
22 # example:
23 #  my $auth = OpenAFS::Auth::create(
24 #               'type'=>'mit', 
25 #               'keytab'=>'/path/to/file/krb5.keytab');
26 #
27 #  $auth->authorize('admin');
28 #
29 sub create {
30   my $self = {
31      # default values
32      'type' => 'MIT',
33      'keytab' =>  "$path->{'afsconfdir'}/krb5.keytab",
34      'cell' => '',
35      'realm' => '', 
36      'debug' => '0',
37      # user specified values
38      @_,
39   };
40
41   # check for supported kerberos type.
42   my $type = $self->{'type'};
43   $self->{'type'} = _check_kerberos_type($type) or 
44     die "Unsupported kerberos type: $type\n";
45
46   # create the sub-class for the kerberos type.
47   my $class = "OpenAFS::Auth::$self->{'type'}";
48   $self = bless($self, $class);
49
50   # attempt get default values.
51   unless ($self->{'cell'}) {
52     eval {
53       $self->{'cell'} = $self->_lookup_cell_name();
54     }
55   }
56   unless ($self->{'realm'}) {
57     if ($self->{'cell'}) {
58       my $cell = $self->{'cell'};
59       ($self->{'realm'} = $cell) =~ tr[a-z][A-Z];
60     }
61   }
62   unless ($self->{'keytab'}) {
63     $self->{'keytab'} = "$path->{'afsconfdir'}/krb5.keytab";
64   }
65
66   # kerberos type specific sanity checks.
67   $self->_sanity_check();
68
69   if ($self->debug) {
70     print "debug: Auth::create()\n";
71     foreach my $k (sort keys(%$self)) {
72       print "debug:  $k => $self->{$k}\n";
73     }
74   }
75   return $self;
76 }
77
78 #
79 # Check for supported kerberos type, and allow for case insensitivity.
80 #
81 sub _check_kerberos_type {
82   my $type = shift;
83   foreach my $supported ('MIT', 'Heimdal', 'Kaserver') {
84      if ($type =~ /^$supported$/i) {
85         return $supported;
86      }
87   }
88   return undef;
89 }
90
91 #
92 # Returns the cell name from the ThisCell configuration file.
93 #
94 sub _lookup_cell_name {
95   my $self = shift;
96   my $cell;
97   open(CELL, "$path->{'afsconfdir'}/ThisCell") 
98     or die "error: Cannot open $path->{'afsconfdir'}/ThisCell: $!\n";
99   $cell = <CELL>;
100   chomp $cell;
101   close CELL;
102   return $cell;
103 }
104
105 #
106 # Placeholder for make_keyfile. Sub-classes should override.
107 #
108 sub make_keyfile {
109   my $self = shift;
110   return;
111 }
112
113
114 # Make the krb.conf file if the realm name is different
115 # than the cell name. The syntax is something like,
116 #
117 #   UMICH.EDU
118 #   UMICH.EDU fear.ifs.umich.edu admin server
119 #   UMICH.EDU surprise.ifs.umich.edu
120 #   UMICH.EDU ruthless.ifs.umich.edu
121 #
122 sub make_krb_config {
123   my $self = shift;
124   my $cell = $self->{'cell'};
125   my $realm = $self->{'realm'};
126
127   if ($realm && $realm ne $cell) {
128     unless ( -d $path->{'afsconfdir'} ) {
129       die "error: OpenAFS configuration directory '$path->{'afsconfdir'}' is missing.\n";
130     }
131     unless ( -w $path->{'afsconfdir'} ) {
132       die "error: Write access to the configuration directory '$path->{'afsconfdir'}' is required.\n";
133     }
134     print "debug: Making $path->{'afsconfdir'}/krb.conf file for realm $realm\n" if $self->{'debug'};
135     open(KRB, "> $path->{'afsconfdir'}/krb.conf") or die "error: Failed to open $path->{'afsconfdir'}/krb.conf, $!\n";
136     print KRB "$realm\n";
137     close KRB;
138   } 
139 }
140
141 #
142 # Enable/disable debug messages.
143 #
144 sub debug {
145   my $self = shift;
146   if (@_) {
147     $self->{'debug'} = shift;
148   }
149   return $self->{'debug'};
150 }
151
152
153 #------------------------------------------------------------------------------------
154 # MIT Kerberos authorization commands.
155 #
156 package OpenAFS::Auth::MIT;
157 use strict;
158 use OpenAFS::Dirpath;
159 use OpenAFS::ConfigUtils;
160 our @ISA = ("OpenAFS::Auth");
161
162 #
163 # Sanity checks before we get started.
164 #
165 sub _sanity_check {
166   my $self = shift;
167   unless (defined $path->{'afssrvbindir'}) {
168     die "error: \$path->{'afssrvbindir'} is not defined.\n";
169   }
170   unless (-f "$path->{'afssrvbindir'}/aklog") {
171     die "error: $path->{'afssrvbindir'}/aklog not found.\n";
172   }
173   unless (-x "$path->{'afssrvbindir'}/aklog") {
174     die "error: $path->{'afssrvbindir'}/aklog not executable.\n";
175   }
176   unless ($self->{'realm'}) {
177     die "error: Missing realm parameter Auth::create().\n";
178   }
179   unless ($self->{'keytab'}) {
180     die "error: Missing keytab parameter Auth::create().\n";
181   }
182   unless ( -f $self->{'keytab'} ) {
183     die "error: Kerberos keytab file not found: $self->{'keytab'}\n";
184   }
185   unless ( -f $self->{'keytab'} ) {
186     die "error: Keytab file not found: $self->{'keytab'}\n";
187   }
188 }
189
190 #
191 # Create the KeyFile from the Kerberos keytab file. The keytab file
192 # should be created using the Kerberos kadmin command (or with the kadmin.local command
193 # as root on the KDC). See the OpenAFS asetkey man page for details.
194
195 sub make_keyfile {
196   my $self = shift;
197
198   # asetkey annoyance. The current asetkey implementation requires the ThisCell and CellServDB files
199   # to be present but they really are not needed to create the KeyFile. This check is done here
200   # rather than in the _sanity_checks() because the ThisCell/CellServerDB are created later in 
201   # the process of creating the new cell.
202   unless ( -f "$path->{'afsconfdir'}/ThisCell" ) {
203     die "error: OpenAFS configuration file is required, $path->{'afsconfdir'}/ThisCell\n";
204   }
205   unless ( -f "$path->{'afsconfdir'}/CellServDB" ) {
206     die "error: OpenAFS configuration file is required, $path->{'afsconfdir'}/CellServDB\n";
207   }
208
209   unless ( -f "$path->{'afssrvbindir'}/asetkey" ) {
210     die "error: $path->{'afssrvbindir'}/asetkey is missing.\nWas OpenAFS built with Kerberos support?\n";
211   }
212   unless ( -x "$path->{'afssrvbindir'}/asetkey" ) {
213     die "error: Do not have execute permissions on $path->{'afssrvbindir'}/asetkey\n";
214   }
215   unless ( -d $path->{'afsconfdir'} ) {
216     die "error: OpenAFS configuration directory '$path->{'afsconfdir'}' is missing.\n";
217   }
218   unless ( -w $path->{'afsconfdir'} ) {
219     die "error: Write access to the OpenAFS configuration directory '$path->{'afsconfdir'}' is required.\n";
220   }
221
222
223   # Run klist to get the kvno of the afs key. Search for afs/cellname@REALM
224   # then afs@REALM. klist must be in the path.
225   my %keys = ();
226   my $kvno;
227   my $principal;
228   my $afs_kvno;
229   my $afs_principal;
230   if ($self->debug) {
231     print "debug: reading $self->{'keytab'} to find afs kvno\n";
232   }
233   open(KLIST, "klist -k $self->{'keytab'} |") or die "make_keyfile: Failed to run klist.";
234   while (<KLIST>) {
235     chomp;
236     next if /^Keytab/;  # skip headers
237     next if /^KVNO/;
238     next if /^----/;
239     ($kvno, $principal) = split;
240     if ($self->debug) {
241       print "debug:  kvno=$kvno principal=$principal\n";
242     }
243     $keys{$principal} = $kvno;
244   }
245   close KLIST;
246   my $cell = $self->{'cell'};
247   my $realm = $self->{'realm'};
248   foreach my $principal ("afs/$cell\@$realm", "afs\@$realm") {
249     if ($self->debug) {
250       print "debug: searching for $principal\n";
251     }
252     if (defined $keys{$principal}) {
253       $afs_principal = $principal;
254       $afs_kvno = $keys{$afs_principal};
255       if ($self->debug) {
256          print "debug: found principal=$afs_principal kvno=$afs_kvno\n";
257       }
258       last;
259     }
260   }
261   unless ($afs_kvno) {
262     die "error: Could not find an afs key matching 'afs/$cell\@$realm' or ".
263       "'afs/$cell' in keytab $self->{'keytab'}\n";
264   }
265
266   # Run asetkey on the keytab to create the KeyFile. asetkey must be in the PATH.
267   run("$path->{'afssrvbindir'}/asetkey add $afs_kvno $self->{'keytab'} $afs_principal");
268 }
269
270 #
271 # Get kerberos ticket and AFS token for the user.
272 #
273 sub authorize {
274   my $self = shift;
275   my $principal = shift || 'admin';
276   my $opt_aklog = "";
277   $opt_aklog .= " -d" if $self->debug;
278
279   run("kinit -k -t $self->{'keytab'} $principal");
280   run("$path->{'afssrvbindir'}/aklog $opt_aklog");
281   run("$path->{'afssrvbindir'}/tokens");
282 }
283
284
285 #------------------------------------------------------------------------------------
286 package OpenAFS::Auth::Heimdal;
287 use strict;
288 use OpenAFS::Dirpath;
289 use OpenAFS::ConfigUtils;
290 our @ISA = ("OpenAFS::Auth");
291
292 #
293 # Various checks during initialization.
294 #
295 sub _sanity_check {
296   my $self = shift;
297   unless ($self->{'realm'}) {
298     die "Missing realm parameter Auth::create().\n";
299   }
300   unless ($self->{'keytab'}) {
301     die "Missing keytab parameter Auth::create().\n";
302   }
303   unless ( -f $self->{'keytab'} ) {
304     die "keytab file not found: $self->{'keytab'}\n";
305   }
306 }
307
308 #
309 # Get kerberos ticket and AFS token for the user.
310 #
311 sub authorize {
312   my $self = shift;
313   my $principal = shift || 'admin';  
314   run("kinit -k -t $self->{'keytab'} $principal\@$self->{'realm'} && afslog");
315 }
316
317 #------------------------------------------------------------------------------------
318 package OpenAFS::Auth::Kaserver;
319 use strict;
320 use OpenAFS::Dirpath;
321 use OpenAFS::ConfigUtils;
322 our @ISA = ("OpenAFS::Auth");
323
324 #
325 # Various checks during initialization.
326 #
327 sub _sanity_check {
328   my $self = shift;
329   unless ($self->{'realm'}) {
330     die "Missing realm parameter Auth::create().\n";
331   }
332 }
333
334 #
335 # Get kerberos ticket and AFS token for the user.
336 #
337 sub authorize {
338   my $self = shift;
339   my $principal = shift || 'admin';
340   run("echo \"Proceeding w/o authentication\"|klog -pipe ${principal}\@$self->{'realm'}");
341 }
342
343 1;