1 |
95b003ff
|
Origo
|
#
|
2 |
|
|
# Module to generate authentication tickets for mod_auth_tkt apache module.
|
3 |
|
|
#
|
4 |
|
|
|
5 |
|
|
package Apache::AuthTkt;
|
6 |
|
|
|
7 |
|
|
use 5.005;
|
8 |
|
|
use Carp;
|
9 |
|
|
use MIME::Base64;
|
10 |
|
|
#use strict;
|
11 |
|
|
use vars qw($VERSION $AUTOLOAD);
|
12 |
|
|
|
13 |
|
|
$VERSION = 2.1;
|
14 |
|
|
|
15 |
|
|
my $me = 'Apache::AuthTkt';
|
16 |
|
|
my $PREFIX = 'TKTAuth';
|
17 |
|
|
my %DEFAULTS = (
|
18 |
|
|
digest_type => 'MD5',
|
19 |
|
|
cookie_name => 'auth_tkt',
|
20 |
|
|
back_arg_name => 'back',
|
21 |
|
|
timeout => 2 * 60 * 60,
|
22 |
|
|
timeout_min => 2 * 60,
|
23 |
|
|
timeout_refresh => 0.5,
|
24 |
|
|
guest_login => 0,
|
25 |
|
|
guest_user => 'guest',
|
26 |
|
|
ignore_ip => 0,
|
27 |
|
|
require_ssl => 0,
|
28 |
|
|
cookie_secure => 0,
|
29 |
|
|
);
|
30 |
|
|
my %BOOLEAN = map { $_ => 1 } qw(
|
31 |
|
|
TKTAuthGuestLogin TKTAuthIgnoreIP TKTAuthRequireSSL TKTAuthCookieSecure
|
32 |
|
|
);
|
33 |
|
|
# Default TKTAuthDomain to host part of HTTP_HOST, or SERVER_NAME
|
34 |
|
|
($DEFAULTS{TKTAuthDomain}) = split /:/, $ENV{HTTP_HOST} || '';
|
35 |
|
|
$DEFAULTS{TKTAuthDomain} ||= $ENV{SERVER_NAME};
|
36 |
|
|
my %ATTR = map { $_ => 1 } qw(
|
37 |
|
|
conf secret secret_old digest_type
|
38 |
|
|
cookie_name back_cookie_name back_arg_name domain cookie_expires
|
39 |
|
|
login_url timeout_url post_timeout_url unauth_url
|
40 |
|
|
timeout timeout_min timeout_refresh token debug
|
41 |
|
|
guest_login guest_user ignore_ip require_ssl cookie_secure
|
42 |
|
|
);
|
43 |
|
|
#my %TICKET_ARGS = map { $_ => 1 }
|
44 |
|
|
|
45 |
|
|
# digest_type => [ module, function ]
|
46 |
|
|
my %DIGEST_TYPE = (
|
47 |
|
|
MD5 => [ 'Digest::MD5', 'md5_hex' ],
|
48 |
|
|
SHA256 => [ 'Digest::SHA', 'sha256_hex' ],
|
49 |
|
|
SHA512 => [ 'Digest::SHA', 'sha512_hex' ],
|
50 |
|
|
);
|
51 |
|
|
|
52 |
|
|
# Helper routine to convert time units into seconds
|
53 |
|
|
my %units = (
|
54 |
|
|
s => 1,
|
55 |
|
|
m => 60,
|
56 |
|
|
h => 3600,
|
57 |
|
|
d => 86400,
|
58 |
|
|
w => 7 * 86400,
|
59 |
|
|
M => 30 * 86400,
|
60 |
|
|
y => 365 * 86400,
|
61 |
|
|
);
|
62 |
|
|
sub convert_time_seconds
|
63 |
|
|
{
|
64 |
|
|
my $self = shift;
|
65 |
|
|
local $_ = shift;
|
66 |
|
|
return $1 if m/^\s*(\d+)\s*$/;
|
67 |
|
|
my $sec = 0;
|
68 |
|
|
while (m/\G(\d+)([shdwmMy])\b\s*/gc) {
|
69 |
|
|
my $amt = $1;
|
70 |
|
|
my $unit = $2 || 's';
|
71 |
|
|
$sec += $amt * $units{$unit};
|
72 |
|
|
# print STDERR "$amt : $unit : $sec\n";
|
73 |
|
|
}
|
74 |
|
|
return $sec;
|
75 |
|
|
}
|
76 |
|
|
|
77 |
|
|
# Parse (simplistically) the given apache config file for TKTAuth directives
|
78 |
|
|
sub parse_conf
|
79 |
|
|
{
|
80 |
|
|
my $self = shift;
|
81 |
|
|
my ($conf) = @_;
|
82 |
|
|
|
83 |
|
|
my %seen = ();
|
84 |
|
|
open CF, "<$conf" or
|
85 |
|
|
die "[$me] open of config file '$conf' failed: $!";
|
86 |
|
|
|
87 |
|
|
# Take settings from first instance of each TKTAuth directive found
|
88 |
|
|
local $/ = "\n";
|
89 |
|
|
while (<CF>) {
|
90 |
|
|
if (m/^\s*(${PREFIX}\w+)\s+(.*)/) {
|
91 |
|
|
$seen{$1} = $2 unless exists $seen{$1};
|
92 |
|
|
}
|
93 |
|
|
}
|
94 |
|
|
|
95 |
|
|
close CF;
|
96 |
|
|
die "[$me] TKTAuthSecret directive not found in config file '$conf'"
|
97 |
|
|
unless $seen{TKTAuthSecret};
|
98 |
|
|
|
99 |
|
|
# Set directives as $self attributes
|
100 |
|
|
my %merge = ( %seen );
|
101 |
|
|
for my $directive (keys %merge) {
|
102 |
|
|
local $_ = $directive;
|
103 |
|
|
s/^TKTAuth(\w)/\L$1/;
|
104 |
|
|
s/([a-z])([A-Z]+)/\L$1_$2/g;
|
105 |
|
|
$merge{$directive} =~ s/^"([^"]+)"$/$1/ if $merge{$directive};
|
106 |
|
|
if ($BOOLEAN{$directive}) {
|
107 |
|
|
$merge{$directive} = 0
|
108 |
|
|
if $merge{$directive} =~ m/^(off|no|false)$/i;
|
109 |
|
|
$merge{$directive} = 1
|
110 |
|
|
if $merge{$directive} =~ m/^(on|yes|true)$/i;
|
111 |
|
|
}
|
112 |
|
|
elsif (defined $merge{$directive}) {
|
113 |
|
|
$merge{$directive} =~ s/^\s+//;
|
114 |
|
|
$merge{$directive} =~ s/\s+$//;
|
115 |
|
|
}
|
116 |
|
|
if ($directive eq 'TKTAuthCookieExpires' || $directive eq 'TKTAuthTimeout') {
|
117 |
|
|
$self->{$_} = $self->convert_time_seconds($merge{$directive});
|
118 |
|
|
}
|
119 |
|
|
# Don't allow TKTAuthDebug to turn on debugging here
|
120 |
|
|
elsif ($directive ne 'TKTAuthDebug') {
|
121 |
|
|
$self->{$_} = $merge{$directive};
|
122 |
|
|
}
|
123 |
|
|
}
|
124 |
|
|
}
|
125 |
|
|
|
126 |
|
|
# Process constructor args
|
127 |
|
|
sub init
|
128 |
|
|
{
|
129 |
|
|
my $self = shift;
|
130 |
|
|
my %arg = @_;
|
131 |
|
|
|
132 |
|
|
# Check for invalid args
|
133 |
|
|
for (keys %arg) {
|
134 |
|
|
croak "[$me] invalid argument to constructor: $_" unless exists $ATTR{$_};
|
135 |
|
|
}
|
136 |
|
|
|
137 |
|
|
# Parse config file if set
|
138 |
|
|
if ($arg{conf}) {
|
139 |
|
|
$self->parse_conf($arg{conf});
|
140 |
|
|
}
|
141 |
|
|
|
142 |
|
|
# Store/override from given args
|
143 |
|
|
$self->{$_} = $arg{$_} foreach keys %arg;
|
144 |
|
|
|
145 |
|
|
croak "[$me] bad constructor - 'secret' or 'conf' argument required"
|
146 |
|
|
unless $self->{conf} || $self->{secret};
|
147 |
|
|
croak "[$me] invalid digest_type '" . $self->{digest_type} . "'"
|
148 |
|
|
unless $DIGEST_TYPE{ $self->{digest_type } };
|
149 |
|
|
|
150 |
|
|
$self;
|
151 |
|
|
}
|
152 |
|
|
|
153 |
|
|
# Constructor
|
154 |
|
|
sub new
|
155 |
|
|
{
|
156 |
|
|
my $class = shift;
|
157 |
|
|
my $self = { %DEFAULTS };
|
158 |
|
|
bless $self, $class;
|
159 |
|
|
$self->init(@_);
|
160 |
|
|
}
|
161 |
|
|
|
162 |
|
|
# Setup autoload accessors/mutators
|
163 |
|
|
sub AUTOLOAD {
|
164 |
|
|
my $self = shift;
|
165 |
|
|
my $attr = $AUTOLOAD;
|
166 |
|
|
$attr =~ s/.*:://;
|
167 |
|
|
die qq(Can't locate object method "$attr" via package "$self")
|
168 |
|
|
unless $ATTR{$attr};
|
169 |
|
|
@_ and $self->{$attr} = $_[0];
|
170 |
|
|
return $self->{$attr};
|
171 |
|
|
}
|
172 |
|
|
|
173 |
|
|
sub DESTROY {}
|
174 |
|
|
|
175 |
|
|
sub errstr
|
176 |
|
|
{
|
177 |
|
|
my $self = shift;
|
178 |
|
|
$@[0] and $self->{errstr} = join ' ', @_;
|
179 |
|
|
$self->{errstr};
|
180 |
|
|
}
|
181 |
|
|
|
182 |
|
|
# Return a mod_auth_tkt ticket containing the given user details
|
183 |
|
|
sub ticket
|
184 |
|
|
{
|
185 |
|
|
my $self = shift;
|
186 |
|
|
my %DEFAULTS = (
|
187 |
|
|
base64 => 1,
|
188 |
|
|
data => '',
|
189 |
|
|
tokens => '',
|
190 |
|
|
);
|
191 |
|
|
my %arg = ( %DEFAULTS, %$self, @_ );
|
192 |
|
|
$arg{uid} = $self->guest_user unless exists $arg{uid};
|
193 |
|
|
$arg{ip_addr} = $arg{ignore_ip} ? '0.0.0.0' : $ENV{REMOTE_ADDR}
|
194 |
|
|
unless exists $arg{ip_addr};
|
195 |
|
|
# 0 or undef ip_addr treated as 0.0.0.0
|
196 |
|
|
$arg{ip_addr} ||= '0.0.0.0';
|
197 |
|
|
|
198 |
|
|
# Data cleanups
|
199 |
|
|
if ($arg{tokens}) {
|
200 |
|
|
$arg{tokens} =~ s/\s+,/,/g;
|
201 |
|
|
$arg{tokens} =~ s/,\s+/,/g;
|
202 |
|
|
}
|
203 |
|
|
|
204 |
|
|
# Data checks
|
205 |
|
|
if ($arg{ip_addr} !~ m/^([12]?[0-9]?[0-9]\.){3}[12]?[0-9]?[0-9]$/) {
|
206 |
|
|
$self->errstr("invalid ip_addr '$arg{ip_addr}'");
|
207 |
|
|
return undef;
|
208 |
|
|
}
|
209 |
|
|
if ($arg{tokens} =~ m/[!\s]/) {
|
210 |
|
|
$self->errstr("invalid chars in tokens '$arg{tokens}'");
|
211 |
|
|
return undef;
|
212 |
|
|
}
|
213 |
|
|
|
214 |
|
|
# Calculate the hash for the ticket
|
215 |
|
|
my $ts = $arg{ts} || time;
|
216 |
|
|
my $digest = $self->_get_digest($ts, $arg{ip_addr}, $arg{uid}, $arg{tokens},
|
217 |
|
|
$arg{data}, $arg{debug});
|
218 |
|
|
|
219 |
|
|
# Construct the ticket itself
|
220 |
|
|
my $ticket = sprintf "%s%08x%s!", $digest, $ts, $arg{uid};
|
221 |
|
|
$ticket .= $arg{tokens} . '!' if $arg{tokens};
|
222 |
|
|
$ticket .= $arg{data};
|
223 |
|
|
|
224 |
|
|
return $arg{base64} ? encode_base64($ticket, '') : $ticket;
|
225 |
|
|
}
|
226 |
|
|
|
227 |
|
|
sub _get_digest_function
|
228 |
|
|
{
|
229 |
|
|
my $self = shift;
|
230 |
|
|
|
231 |
|
|
die "Invalid digest_type '" . $self->digest_type . "'\n"
|
232 |
|
|
unless $DIGEST_TYPE{ $self->digest_type };
|
233 |
|
|
|
234 |
|
|
my ($module, $func) = @{ $DIGEST_TYPE{ $self->digest_type } };
|
235 |
|
|
eval "require $module";
|
236 |
|
|
return eval "\\&${module}::$func";
|
237 |
|
|
}
|
238 |
|
|
|
239 |
|
|
sub _get_digest
|
240 |
|
|
{
|
241 |
|
|
my ($self, $ts, $ip_addr, $uid, $tokens, $data, $debug) = @_;
|
242 |
|
|
my @ip = split /\./, $ip_addr;
|
243 |
|
|
my @ts = ( (($ts & 0xff000000) >> 24),
|
244 |
|
|
(($ts & 0xff0000) >> 16),
|
245 |
|
|
(($ts & 0xff00) >> 8),
|
246 |
|
|
(($ts & 0xff)) );
|
247 |
|
|
my $ipts = pack("C8", @ip, @ts);
|
248 |
|
|
my $raw = $ipts . $self->secret . $uid . "\0" . $tokens . "\0" . $data;
|
249 |
|
|
my $digest_function = $self->_get_digest_function;
|
250 |
|
|
my $digest0 = $digest_function->($raw);
|
251 |
|
|
my $digest = $digest_function->($digest0 . $self->secret);
|
252 |
|
|
|
253 |
|
|
if ($debug) {
|
254 |
|
|
print STDERR "ts: $ts\nip_addr: $ip_addr\nuid: $uid\ntokens: $tokens\ndata: $data\n";
|
255 |
|
|
print STDERR "secret: " . $self->secret . "\n";
|
256 |
|
|
print STDERR "raw: '$raw'\n";
|
257 |
|
|
my $len = length($raw);
|
258 |
|
|
print STDERR "digest0: $digest0 (input length $len)\n";
|
259 |
|
|
print STDERR "digest: $digest\n";
|
260 |
|
|
}
|
261 |
|
|
|
262 |
|
|
return $digest;
|
263 |
|
|
}
|
264 |
|
|
|
265 |
|
|
# Return a cookie containing a mod_auth_tkt ticket
|
266 |
|
|
sub cookie
|
267 |
|
|
{
|
268 |
|
|
my $self = shift;
|
269 |
|
|
my %DEFAULTS = (
|
270 |
|
|
cookie_name => 'auth_tkt',
|
271 |
|
|
cookie_path => '/',
|
272 |
|
|
);
|
273 |
|
|
my %arg = ( %DEFAULTS, %$self, @_ );
|
274 |
|
|
$arg{cookie_domain} ||= $self->domain;
|
275 |
|
|
|
276 |
|
|
# Get ticket, forcing base64 for cookies
|
277 |
|
|
my $ticket = $self->ticket(@_, base64 => 1) or return;
|
278 |
|
|
|
279 |
|
|
my $cookie_fmt = "%s=%s%s%s%s";
|
280 |
|
|
my $path_elt = "; path=$arg{cookie_path}";
|
281 |
|
|
my $domain_elt = $arg{cookie_domain} ? "; domain=$arg{cookie_domain}" : '';
|
282 |
|
|
my $secure_elt = $arg{cookie_secure} ? "; secure" : '';
|
283 |
|
|
return sprintf $cookie_fmt,
|
284 |
|
|
$arg{cookie_name}, $ticket, $domain_elt, $path_elt, $secure_elt;
|
285 |
|
|
}
|
286 |
|
|
|
287 |
|
|
# Returns a hashref representing the original ticket components
|
288 |
|
|
# Returns undef if there were any errors
|
289 |
|
|
sub validate_ticket
|
290 |
|
|
{
|
291 |
|
|
my $self = shift;
|
292 |
|
|
my $ticket = shift || croak "No ticket passed to validate_ticket";
|
293 |
|
|
my %arg = ( %$self, @_ );
|
294 |
|
|
|
295 |
|
|
$arg{ip_addr} = $arg{ignore_ip} ? '0.0.0.0' : $ENV{REMOTE_ADDR}
|
296 |
|
|
unless exists $arg{ip_addr};
|
297 |
|
|
# 0 or undef ip_addr treated as 0.0.0.0
|
298 |
|
|
$arg{ip_addr} ||= '0.0.0.0';
|
299 |
|
|
|
300 |
|
|
# Parse ticket
|
301 |
|
|
my $info = $self->parse_ticket($ticket);
|
302 |
|
|
|
303 |
|
|
# Validate digest
|
304 |
|
|
my $expected_digest = $self->_get_digest(
|
305 |
|
|
$info->{ts}, $arg{ip_addr}, $info->{uid},
|
306 |
|
|
$info->{tokens}, $info->{data});
|
307 |
|
|
|
308 |
|
|
return $info if $expected_digest eq $info->{digest};
|
309 |
|
|
return undef;
|
310 |
|
|
}
|
311 |
|
|
|
312 |
|
|
sub parse_ticket
|
313 |
|
|
{
|
314 |
|
|
my $self = shift;
|
315 |
|
|
my $ticket = shift or croak "No ticket passed to parse_ticket";
|
316 |
|
|
my $parts = {};
|
317 |
|
|
|
318 |
|
|
# Strip possible quotes
|
319 |
|
|
$ticket =~ s,^"|"$,,g;
|
320 |
|
|
|
321 |
|
|
return if length($ticket) < 40;
|
322 |
|
|
|
323 |
|
|
# Assume $ticket is not URL-escaped but may be base64-escaped
|
324 |
|
|
my $raw = $ticket =~ m/!/ ? $ticket : decode_base64($ticket);
|
325 |
|
|
|
326 |
|
|
# If $raw still doesn't have ! then it is bogus
|
327 |
|
|
return if $raw !~ m/!/;
|
328 |
|
|
|
329 |
|
|
# Deconstruct
|
330 |
|
|
my ($digest,$ts,$uid,$extra) = ($raw =~ m/^(.{32})(.{8})(.+?)!(.*)$/);
|
331 |
|
|
$parts->{digest} = $digest;
|
332 |
|
|
$parts->{ts} = hex($ts);
|
333 |
|
|
$parts->{uid} = $uid;
|
334 |
|
|
$parts->{tokens} = '';
|
335 |
|
|
$parts->{data} = '';
|
336 |
|
|
|
337 |
|
|
# Tokens and data if present
|
338 |
|
|
if (defined $extra) {
|
339 |
|
|
if ($extra =~ m/!/) {
|
340 |
|
|
($parts->{tokens},$parts->{data}) = split m/!/, $extra, 2;
|
341 |
|
|
}
|
342 |
|
|
else {
|
343 |
|
|
$parts->{data} = $extra;
|
344 |
|
|
}
|
345 |
|
|
}
|
346 |
|
|
return $parts;
|
347 |
|
|
}
|
348 |
|
|
|
349 |
|
|
# Alias for compatibility with Jose/Ton's original patch
|
350 |
|
|
*valid_ticket = \&validate_ticket;
|
351 |
|
|
|
352 |
|
|
1;
|
353 |
|
|
|
354 |
|
|
__END__
|
355 |
|
|
|
356 |
|
|
=head1 NAME
|
357 |
|
|
|
358 |
|
|
Apache::AuthTkt - module to generate authentication tickets for
|
359 |
|
|
mod_auth_tkt apache module.
|
360 |
|
|
|
361 |
|
|
|
362 |
|
|
=head1 SYNOPSIS
|
363 |
|
|
|
364 |
|
|
# Constructor - either (preferred):
|
365 |
|
|
$at = Apache::AuthTkt->new(
|
366 |
|
|
conf => '/etc/httpd/conf.d/auth_tkt.conf',
|
367 |
|
|
);
|
368 |
|
|
# OR:
|
369 |
|
|
$at = Apache::AuthTkt->new(
|
370 |
|
|
secret => '818f9c9d-91ed-4b74-9f48-ff99cfe00a0e',
|
371 |
|
|
digest_type => 'MD5',
|
372 |
|
|
);
|
373 |
|
|
|
374 |
|
|
# Generate ticket
|
375 |
|
|
$ticket = $at->ticket(uid => $username, ip_addr => $ip_addr);
|
376 |
|
|
|
377 |
|
|
# Or generate cookie containing ticket
|
378 |
|
|
$cookie = $at->cookie(
|
379 |
|
|
uid => $username,
|
380 |
|
|
cookie_name => 'auth_tkt',
|
381 |
|
|
cookie_domain => 'www.openfusion.com.au',
|
382 |
|
|
);
|
383 |
|
|
|
384 |
|
|
# Access the shared secret
|
385 |
|
|
$secret = $at->secret();
|
386 |
|
|
# If using the 'conf' constructor above, all other TKTAuth attributes
|
387 |
|
|
# are also available e.g.:
|
388 |
|
|
print $at->cookie_name(), $at->ignore_ip(), $at->request_ssl();
|
389 |
|
|
|
390 |
|
|
# Report error string
|
391 |
|
|
print $at->errstr;
|
392 |
|
|
|
393 |
|
|
|
394 |
|
|
=head1 INTRODUCTION
|
395 |
|
|
|
396 |
|
|
Apache::AuthTkt is a module for generating and validating
|
397 |
|
|
authentication tickets used with the 'mod_auth_tkt' apache module.
|
398 |
|
|
Tickets are typically generated by a login web page of some kind
|
399 |
|
|
when a user has been authenticated. The ticket contains a username/uid
|
400 |
|
|
for the authenticated user, and often also the IP address they
|
401 |
|
|
authenticated from, a set of authorisation tokens, and any other user
|
402 |
|
|
data required. The ticket also includes an MD5 hash of all the included
|
403 |
|
|
user data plus a shared secret, so that tickets can be validated by
|
404 |
|
|
mod_auth_tkt without requiring access to the user repository.
|
405 |
|
|
|
406 |
|
|
See http://www.openfusion.com.au/labs/mod_auth_tkt for mod_auth_tkt
|
407 |
|
|
itself.
|
408 |
|
|
|
409 |
|
|
|
410 |
|
|
=head1 DESCRIPTION
|
411 |
|
|
|
412 |
|
|
=head2 CONSTRUCTOR
|
413 |
|
|
|
414 |
|
|
An Apache::AuthTkt object is created via a standard constructor
|
415 |
|
|
with named arguments. The preferred form is to point the constructor
|
416 |
|
|
to the apache config file containing the mod_auth_tkt TKTAuthSecret
|
417 |
|
|
directive, from which Apache::AuthTkt will parse the shared secret
|
418 |
|
|
it needs, as well as any additional TKTAuth* directives it finds:
|
419 |
|
|
|
420 |
|
|
$at = Apache::Tkt->new(
|
421 |
|
|
conf => '/etc/httpd/conf/auth_tkt.conf',
|
422 |
|
|
);
|
423 |
|
|
|
424 |
|
|
Alternatively, you can pass the mod_auth_tkt shared secret (the
|
425 |
|
|
TKTAuthSecret value) and the digest_type to use (default is 'MD5')
|
426 |
|
|
explicitly to the constructor:
|
427 |
|
|
|
428 |
|
|
$at = Apache::AuthTkt->new(
|
429 |
|
|
secret => '818f9c9d-91ed-4b74-9f48-ff99cfe00a0e',
|
430 |
|
|
digest_type => 'SHA256',
|
431 |
|
|
);
|
432 |
|
|
|
433 |
|
|
=head2 ACCESSORS
|
434 |
|
|
|
435 |
|
|
If the 'conf' form of the constructor is used, Apache::AuthTkt parses
|
436 |
|
|
all additional TKTAuth* directives it finds there and stores them in
|
437 |
|
|
additional internal attributes. Those values are available via
|
438 |
|
|
accessors named after the relevant TKTAuth directive (with the 'TKTAuth'
|
439 |
|
|
prefix dropped and converted to lowercase underscore format) i.e.
|
440 |
|
|
|
441 |
|
|
$at->secret()
|
442 |
|
|
$at->secret_old()
|
443 |
|
|
$at->digest_type()
|
444 |
|
|
$at->cookie_name()
|
445 |
|
|
$at->back_cookie_name()
|
446 |
|
|
$at->back_arg_name()
|
447 |
|
|
$at->domain()
|
448 |
|
|
$at->cookie_expires()
|
449 |
|
|
$at->login_url()
|
450 |
|
|
$at->timeout_url()
|
451 |
|
|
$at->unauth_url()
|
452 |
|
|
$at->timeout()
|
453 |
|
|
$at->timeout_refresh()
|
454 |
|
|
$at->token ()
|
455 |
|
|
$at->guest_login()
|
456 |
|
|
$at->ignore_ip()
|
457 |
|
|
$at->require_ssl()
|
458 |
|
|
|
459 |
|
|
|
460 |
|
|
=head2 TICKET GENERATION
|
461 |
|
|
|
462 |
|
|
Tickets are generated using the ticket() method with named parameters:
|
463 |
|
|
|
464 |
|
|
# Generate ticket
|
465 |
|
|
$ticket = $at->ticket(uid => $username);
|
466 |
|
|
|
467 |
|
|
Ticket returns undef on error, with error information available via
|
468 |
|
|
the errstr() method:
|
469 |
|
|
|
470 |
|
|
$ticket = $at->ticket or die $at->errstr;
|
471 |
|
|
|
472 |
|
|
ticket() accepts the following arguments, all optional:
|
473 |
|
|
|
474 |
|
|
=over 4
|
475 |
|
|
|
476 |
|
|
=item uid
|
477 |
|
|
|
478 |
|
|
uid, username, or other user identifier for this ticket. There is no
|
479 |
|
|
requirement that this be unique per-user. Default: 'guest'.
|
480 |
|
|
|
481 |
|
|
=item ip_addr
|
482 |
|
|
|
483 |
|
|
IP address associated with this ticket. Default: if $at->ignore_ip
|
484 |
|
|
is true, then '0.0.0.0', otherwise $ENV{REMOTE_ADDR};
|
485 |
|
|
|
486 |
|
|
=item tokens
|
487 |
|
|
|
488 |
|
|
A comma-separated list of tokens associated with this user. Typically
|
489 |
|
|
only used if you are using the mod_auth_tkt TKTAuthToken directive.
|
490 |
|
|
Default: none.
|
491 |
|
|
|
492 |
|
|
=item data
|
493 |
|
|
|
494 |
|
|
Arbitrary user data to be stored for this ticket. This data is included
|
495 |
|
|
in the MD5 hash check. Default: none.
|
496 |
|
|
|
497 |
|
|
=item base64
|
498 |
|
|
|
499 |
|
|
Flag used to indicate whether to base64-encode the ticket. Default: 1.
|
500 |
|
|
|
501 |
|
|
=item ts
|
502 |
|
|
|
503 |
|
|
Explicitly set the timestamp to use for this ticket. Only for testing!
|
504 |
|
|
|
505 |
|
|
=back
|
506 |
|
|
|
507 |
|
|
|
508 |
|
|
As an alternative to ticket(), the cookie() method can be used to
|
509 |
|
|
return the generated ticket in cookie format. cookie() returns undef
|
510 |
|
|
on error, with error information available via the errstr() method:
|
511 |
|
|
|
512 |
|
|
$cookie = $at->cookie or die $at->errstr;
|
513 |
|
|
|
514 |
|
|
cookie() supports all the same arguments as ticket(), plus the
|
515 |
|
|
following:
|
516 |
|
|
|
517 |
|
|
=over 4
|
518 |
|
|
|
519 |
|
|
=item cookie_name
|
520 |
|
|
|
521 |
|
|
Cookie name. Should match the TKTAuthCookieName directive, if you're
|
522 |
|
|
using it. Default: $at->cookie_name, or 'auth_tkt'.
|
523 |
|
|
|
524 |
|
|
=item cookie_domain
|
525 |
|
|
|
526 |
|
|
Cookie domain. Should match the TKTAuthDomain directive, if you're
|
527 |
|
|
using it. Default: $at->domain.
|
528 |
|
|
|
529 |
|
|
=item cookie_path
|
530 |
|
|
|
531 |
|
|
Cookie path. Default: '/'.
|
532 |
|
|
|
533 |
|
|
=item cookie_secure
|
534 |
|
|
|
535 |
|
|
Flag whether to set the 'secure' cookie flag, so that the cookie is
|
536 |
|
|
returned only in HTTPS contexts. Default: $at->require_ssl, or 0.
|
537 |
|
|
|
538 |
|
|
=back
|
539 |
|
|
|
540 |
|
|
=head2 TICKET PARSING AND VALIDATION
|
541 |
|
|
|
542 |
|
|
You may parse and validate existing tickets with the validate_ticket()
|
543 |
|
|
method. It takes as its first parameter the ticket to be validated, and
|
544 |
|
|
then an optional list of named parameter overrides
|
545 |
|
|
(e.g. ip_addr => 'x.x.x.x'). If the ticket is valid, validate_ticket
|
546 |
|
|
returns a hashref with the following key/value pairs:
|
547 |
|
|
|
548 |
|
|
=over 4
|
549 |
|
|
|
550 |
|
|
=item digest
|
551 |
|
|
|
552 |
|
|
=item ts
|
553 |
|
|
|
554 |
|
|
=item uid
|
555 |
|
|
|
556 |
|
|
=item tokens
|
557 |
|
|
|
558 |
|
|
=item data
|
559 |
|
|
|
560 |
|
|
=back
|
561 |
|
|
|
562 |
|
|
validate_ticket() will return undef if any errors with the ticket value
|
563 |
|
|
are encountered.
|
564 |
|
|
|
565 |
|
|
The validate_ticket() method algorithm is analogous to the function with
|
566 |
|
|
the same name in the mod_auth_tkt C module.
|
567 |
|
|
|
568 |
|
|
There is also a parse_ticket() method available that parses the ticket
|
569 |
|
|
without running it through the validation phase, and returns the same
|
570 |
|
|
data as validate_ticket(). This is only safe to use where you are certain
|
571 |
|
|
that the ticket has been validated elsewhere. In general it's considerably
|
572 |
|
|
safer to just use validate_ticket.
|
573 |
|
|
|
574 |
|
|
|
575 |
|
|
=head2 DIGEST TYPES
|
576 |
|
|
|
577 |
|
|
As of version 2.1.0, mod_auth_tkt supports multiple digest types. The
|
578 |
|
|
following digest_types are currently supported:
|
579 |
|
|
|
580 |
|
|
=over 4
|
581 |
|
|
|
582 |
|
|
=item MD5
|
583 |
|
|
|
584 |
|
|
The current default, for backwards compatibility. Requires the Digest::MD5
|
585 |
|
|
perl module.
|
586 |
|
|
|
587 |
|
|
=item SHA256
|
588 |
|
|
|
589 |
|
|
Requires the Digest::SHA perl module.
|
590 |
|
|
|
591 |
|
|
=back
|
592 |
|
|
|
593 |
|
|
These can be set either via your config (the TKTAuthDigestType directive)
|
594 |
|
|
or by passing a 'digest_type' parameter to the AuthTkt constructor.
|
595 |
|
|
|
596 |
|
|
|
597 |
|
|
=head1 AUTHOR
|
598 |
|
|
|
599 |
|
|
Gavin Carr <gavin@openfusion.com.au>
|
600 |
|
|
|
601 |
|
|
Contributors:
|
602 |
|
|
|
603 |
|
|
Peter Karman <peter@peknet.com>
|
604 |
|
|
|
605 |
|
|
Ton Voon <ton.voon@altinity.com>
|
606 |
|
|
|
607 |
|
|
Jose Luis Martinez <jlmartinez@capside.com>
|
608 |
|
|
|
609 |
|
|
=head1 COPYRIGHT
|
610 |
|
|
|
611 |
|
|
Copyright 2001-2009 Gavin Carr and contributors.
|
612 |
|
|
|
613 |
|
|
This program is free software. You may copy or redistribute it under the
|
614 |
|
|
same terms as perl itself.
|
615 |
|
|
|
616 |
|
|
=cut
|
617 |
|
|
|
618 |
|
|
|
619 |
|
|
# vim:sw=4
|