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