line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Apache2::AuthAny::RequestConfig; |
2
|
|
|
|
|
|
|
|
3
|
1
|
|
|
1
|
|
1397
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
31
|
|
4
|
1
|
|
|
1
|
|
364
|
use Apache2::Module (); |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
use Apache2::Access (); |
6
|
|
|
|
|
|
|
use Apache2::Request (); |
7
|
|
|
|
|
|
|
use URI::Escape; |
8
|
|
|
|
|
|
|
use Digest::MD5 qw(md5_hex); |
9
|
|
|
|
|
|
|
use MIME::Base64; |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
use Apache2::Const -compile => qw(OK DECLINED REDIRECT HTTP_UNAUTHORIZED); |
12
|
|
|
|
|
|
|
use Data::Dumper("Dumper"); |
13
|
|
|
|
|
|
|
use CGI; |
14
|
|
|
|
|
|
|
use CGI::Cookie; |
15
|
|
|
|
|
|
|
use Apache2::AuthAny::Cookie (); |
16
|
|
|
|
|
|
|
use Apache2::AuthAny::DB (); |
17
|
|
|
|
|
|
|
use Apache2::AuthAny::AuthUtil (); |
18
|
|
|
|
|
|
|
our $aaDB; |
19
|
|
|
|
|
|
|
our $VERSION = '0.201'; |
20
|
|
|
|
|
|
|
|
21
|
|
|
|
|
|
|
my @system_skip_auth = qw(/Shibboleth); |
22
|
|
|
|
|
|
|
|
23
|
|
|
|
|
|
|
sub handler { |
24
|
|
|
|
|
|
|
my $r = shift; |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
my $cf = Apache2::Module::get_config('Apache2::AuthAny', |
27
|
|
|
|
|
|
|
$r->server, |
28
|
|
|
|
|
|
|
$r->per_dir_config) || {}; |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
my $uri = $r->uri; |
31
|
|
|
|
|
|
|
my $user_gate = $cf->{AuthAnyGateURL} || ''; |
32
|
|
|
|
|
|
|
my $gate_dir = $user_gate; |
33
|
|
|
|
|
|
|
$gate_dir =~ s{/[^/]*$}{}; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
if ($uri eq $user_gate || ($gate_dir && $uri =~ m{^$gate_dir}) ) { |
36
|
|
|
|
|
|
|
# Prevent any authentication attempt on the gate page. |
37
|
|
|
|
|
|
|
$r->log->info("RequestConfig: On gate page, '$uri'"); |
38
|
|
|
|
|
|
|
$r->set_handlers(PerlAuthenHandler => "sub {Apache2::Const::OK}"); |
39
|
|
|
|
|
|
|
$r->set_handlers(PerlAuthzHandler => "sub {Apache2::Const::OK}"); |
40
|
|
|
|
|
|
|
} elsif ($uri =~ m{/aa_auth/(.*?)/}) { |
41
|
|
|
|
|
|
|
my $provider_string = $1; |
42
|
|
|
|
|
|
|
my ($auth_provider, $logout_key) = split("_aa-key_", $provider_string); |
43
|
|
|
|
|
|
|
$r->log->info("Apache2::AuthAny::RequestConfig: Authenticating with '$auth_provider'"); |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
if (lc($r->auth_type) eq 'auth-any') { |
46
|
|
|
|
|
|
|
# This auth provider does not use the Authen/Authz phases. To prevent |
47
|
|
|
|
|
|
|
# errors from DocumentRoot level Require directives, disable the |
48
|
|
|
|
|
|
|
# Authen/Authz phases |
49
|
|
|
|
|
|
|
$r->set_handlers(PerlAuthenHandler => "sub {Apache2::Const::OK}"); |
50
|
|
|
|
|
|
|
$r->set_handlers(PerlAuthzHandler => "sub {Apache2::Const::OK}"); |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
my $pid = Apache2::AuthAny::Cookie::pid($r); |
54
|
|
|
|
|
|
|
$r->pnotes(pid => $pid); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
if ($auth_provider ne 'google') { # Google auth using PHP |
57
|
|
|
|
|
|
|
$r->handler('perl-script'); |
58
|
|
|
|
|
|
|
$r->set_handlers(PerlResponseHandler => 'Apache2::AuthAny::Cookie::post_login'); |
59
|
|
|
|
|
|
|
} |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
if (lc($r->auth_type) eq 'basic') { |
62
|
|
|
|
|
|
|
# The AuthName randomizer is needed for IE to keep it |
63
|
|
|
|
|
|
|
# from skipping the challenge when a known AuthName is sent. |
64
|
|
|
|
|
|
|
my $auth_name = $r->auth_name() || 'Private'; |
65
|
|
|
|
|
|
|
my $rand_int = int(100000 * (1 + rand(4))); |
66
|
|
|
|
|
|
|
$r->auth_name($auth_name . $rand_int); |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
# Make sure the auth request is going to the current directory |
69
|
|
|
|
|
|
|
if ($logout_key ne $pid->{logoutKey}) { |
70
|
|
|
|
|
|
|
Apache2::AuthAny::AuthUtil::goToGATE($r, 'tech', {msg => "mismatching logout keys."}) |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
# After successful authentication, set a new logoutKey |
74
|
|
|
|
|
|
|
$r->set_handlers(PerlFixupHandler => 'Apache2::AuthAny::FixupHandler::update_logout_key'); |
75
|
|
|
|
|
|
|
|
76
|
|
|
|
|
|
|
# Go to meta redirect to GATE instead of showing ugly browser message |
77
|
|
|
|
|
|
|
# if user chooses "Cancel" on challenge popup. |
78
|
|
|
|
|
|
|
my $req = Apache2::Request->new($r); |
79
|
|
|
|
|
|
|
my $request = $req->param('req'); |
80
|
|
|
|
|
|
|
my $custom_response = <<"RESPONSE"; |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
|
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
RESPONSE |
90
|
|
|
|
|
|
|
$r->custom_response(Apache2::Const::HTTP_UNAUTHORIZED, $custom_response); |
91
|
|
|
|
|
|
|
$r->log->info("Apache2::AuthAny::RequestConfig: Basic custom_response set"); |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
} elsif (lc($r->auth_type) eq 'auth-any') { |
95
|
|
|
|
|
|
|
$aaDB = Apache2::AuthAny::DB->new() unless $aaDB; |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
my $pid; |
98
|
|
|
|
|
|
|
my $scripted_pid = get_scripted_pid($r, $cf); |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# First, check for scripted access by looking in "Authorization" header |
101
|
|
|
|
|
|
|
if ($scripted_pid) { |
102
|
|
|
|
|
|
|
$pid = $scripted_pid; |
103
|
|
|
|
|
|
|
} else { |
104
|
|
|
|
|
|
|
$pid = Apache2::AuthAny::Cookie::pid($r); |
105
|
|
|
|
|
|
|
} |
106
|
|
|
|
|
|
|
$r->pnotes(pid => $pid); |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
my $req = Apache2::Request->new($r); |
109
|
|
|
|
|
|
|
if (defined $req->param('aalogout') ) { |
110
|
|
|
|
|
|
|
return Apache2::AuthAny::AuthUtil::logout($r, $pid); |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
if (defined $req->param('aalogin') ) { |
114
|
|
|
|
|
|
|
return Apache2::AuthAny::AuthUtil::goToGATE($r, 'first_access'); |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
my $skip_patterns = $cf->{AuthAnySkipAuthentication} || []; |
118
|
|
|
|
|
|
|
push @$skip_patterns, @system_skip_auth; |
119
|
|
|
|
|
|
|
my @matching_patterns = grep {$r->uri =~ m!$_!} @$skip_patterns; |
120
|
|
|
|
|
|
|
if (@matching_patterns) { |
121
|
|
|
|
|
|
|
$r->set_handlers(PerlAuthenHandler => "sub {Apache2::Const::OK}"); |
122
|
|
|
|
|
|
|
$r->set_handlers(PerlAuthzHandler => "sub {Apache2::Const::OK}"); |
123
|
|
|
|
|
|
|
} else { |
124
|
|
|
|
|
|
|
$r->set_handlers(PerlAuthenHandler => 'Apache2::AuthAny::AuthenHandler'); |
125
|
|
|
|
|
|
|
$r->set_handlers(PerlAuthzHandler => 'Apache2::AuthAny::AuthzHandler'); |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
# If we make it through authen and authz, update the last access |
128
|
|
|
|
|
|
|
$r->set_handlers(PerlFixupHandler => 'Apache2::AuthAny::FixupHandler'); |
129
|
|
|
|
|
|
|
set_env($r, $pid, $cf); |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
return Apache2::Const::DECLINED; |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
sub set_env { |
135
|
|
|
|
|
|
|
my ($r, $pid, $cf) = @_; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
my ($authId, $authProvider); |
138
|
|
|
|
|
|
|
unless ($pid->{state} eq 'logged_out') { |
139
|
|
|
|
|
|
|
($authId, $authProvider) = ($pid->{authId}, $pid->{authProvider}); |
140
|
|
|
|
|
|
|
} |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
if ($pid->{scripted}) { |
143
|
|
|
|
|
|
|
$r->subprocess_env('AA_SCRIPTED' => 1); |
144
|
|
|
|
|
|
|
} |
145
|
|
|
|
|
|
|
|
146
|
|
|
|
|
|
|
if ($authId && $pid->{SID}) { |
147
|
|
|
|
|
|
|
# login occurred in this browser session |
148
|
|
|
|
|
|
|
$r->subprocess_env('AA_SESSION' => 1); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
# resolve identity if possible |
152
|
|
|
|
|
|
|
my $identifiedUser = $aaDB->getUserByAuthIdAndProvider($authId, $authProvider) || {}; |
153
|
|
|
|
|
|
|
my $user; |
154
|
|
|
|
|
|
|
if ($identifiedUser->{username}) { |
155
|
|
|
|
|
|
|
$user = $identifiedUser->{username}; |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
my $roles = $aaDB->getUserRoles($identifiedUser->{UID}); |
158
|
|
|
|
|
|
|
$r->subprocess_env(AA_ROLES => join(",", @$roles)); |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
# role choices are never used in Require directives |
161
|
|
|
|
|
|
|
my %user_role_choice; |
162
|
|
|
|
|
|
|
my $role_choices = $aaDB->getUserRoleChoices($identifiedUser->{UID}); |
163
|
|
|
|
|
|
|
foreach my $role (@$role_choices) { |
164
|
|
|
|
|
|
|
$user_role_choice{$role} = 1; |
165
|
|
|
|
|
|
|
} |
166
|
|
|
|
|
|
|
my @roles_active = grep { $user_role_choice{$_} } @$roles; |
167
|
|
|
|
|
|
|
$r->subprocess_env(AA_ROLES_ACTIVE => join(",", @roles_active)); |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
my $identities = $aaDB->getUserIdentities($identifiedUser->{UID}); |
170
|
|
|
|
|
|
|
my @idents = map {"$_->{authId}|$_->{authProvider}"} @$identities; |
171
|
|
|
|
|
|
|
$r->subprocess_env(AA_IDENTITIES => join(",", @idents)); |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
foreach my $field (keys %$identifiedUser) { |
174
|
|
|
|
|
|
|
$r->subprocess_env("AA_IDENT_$field" => $identifiedUser->{$field}); |
175
|
|
|
|
|
|
|
} |
176
|
|
|
|
|
|
|
} elsif ($authId && $authProvider) { |
177
|
|
|
|
|
|
|
$user = "$authId|$authProvider"; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
$r->user($user) if $user; |
181
|
|
|
|
|
|
|
$r->subprocess_env(REMOTE_USER => $user); |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
$r->subprocess_env(AA_USER => $authId); |
184
|
|
|
|
|
|
|
$r->subprocess_env(AA_PROVIDER => $authProvider); |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# Timeout |
187
|
|
|
|
|
|
|
my $timeout = 155520000; # defaults to 5 years |
188
|
|
|
|
|
|
|
if (defined $identifiedUser->{timeout}) { |
189
|
|
|
|
|
|
|
$timeout = $identifiedUser->{timeout}; |
190
|
|
|
|
|
|
|
} elsif (defined $cf->{AuthAnyTimeout}) { |
191
|
|
|
|
|
|
|
$timeout = $cf->{AuthAnyTimeout}; |
192
|
|
|
|
|
|
|
} |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
if ($pid->{state} eq 'authenticated' && time() - $pid->{last} < $timeout) { |
195
|
|
|
|
|
|
|
$r->subprocess_env(AA_TIMEOUT => $timeout); |
196
|
|
|
|
|
|
|
} elsif ($authId ) { |
197
|
|
|
|
|
|
|
$aaDB->statePCookie($pid, 'recognized'); |
198
|
|
|
|
|
|
|
} else { |
199
|
|
|
|
|
|
|
$aaDB->statePCookie($pid, 'logged_out'); |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
$r->subprocess_env(AA_STATE => $pid->{state}); |
203
|
|
|
|
|
|
|
# Passing gate for logout convienience |
204
|
|
|
|
|
|
|
$r->subprocess_env(); |
205
|
|
|
|
|
|
|
} |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
sub get_scripted_pid { |
208
|
|
|
|
|
|
|
my $r = shift; |
209
|
|
|
|
|
|
|
my $cf = shift; |
210
|
|
|
|
|
|
|
if ($cf->{AuthAnyBasicAuthUserFile}) { |
211
|
|
|
|
|
|
|
unless (open(HTPASSWD, $cf->{AuthAnyBasicAuthUserFile})) { |
212
|
|
|
|
|
|
|
my $msg = "Cannot read '$cf->{AuthAnyBasicAuthUserFile}' $!"; |
213
|
|
|
|
|
|
|
die $msg; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
my ($http_user, $http_password) = get_user_and_password($r); |
217
|
|
|
|
|
|
|
if ($http_user && $http_password) { |
218
|
|
|
|
|
|
|
|
219
|
|
|
|
|
|
|
my $stored_passwd; |
220
|
|
|
|
|
|
|
while () { |
221
|
|
|
|
|
|
|
chomp; |
222
|
|
|
|
|
|
|
my ($username, $crypt_passwd) = split(":", $_, 2); |
223
|
|
|
|
|
|
|
if ($username eq $http_user) { |
224
|
|
|
|
|
|
|
if (crypt($http_password, $crypt_passwd) eq $crypt_passwd) { |
225
|
|
|
|
|
|
|
$r->log->info("RequestConfig: From HTTP header: $username"); |
226
|
|
|
|
|
|
|
return {PID => 'unused', |
227
|
|
|
|
|
|
|
SID => 'unused', |
228
|
|
|
|
|
|
|
logoutKey => 'unused', |
229
|
|
|
|
|
|
|
state => 'authenticated', |
230
|
|
|
|
|
|
|
scripted => 1, |
231
|
|
|
|
|
|
|
authId => $username, |
232
|
|
|
|
|
|
|
authProvider => 'basic', |
233
|
|
|
|
|
|
|
last => 2298416724, # time in the future |
234
|
|
|
|
|
|
|
}; |
235
|
|
|
|
|
|
|
} else { |
236
|
|
|
|
|
|
|
my $msg = "RequestConfig: Basic user found in " . |
237
|
|
|
|
|
|
|
"$cf->{AuthAnyBasicAuthUserFile}, however password is incorrect"; |
238
|
|
|
|
|
|
|
$r->log->warn($msg); |
239
|
|
|
|
|
|
|
last; |
240
|
|
|
|
|
|
|
} |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
} |
244
|
|
|
|
|
|
|
} |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
sub get_user_and_password { |
248
|
|
|
|
|
|
|
my $r = shift; |
249
|
|
|
|
|
|
|
my $Authorization = $r->headers_in->{Authorization}; |
250
|
|
|
|
|
|
|
if ($Authorization) { |
251
|
|
|
|
|
|
|
my ($type, $hash) = split " ", $Authorization; |
252
|
|
|
|
|
|
|
my $u_and_p = decode_base64($hash); |
253
|
|
|
|
|
|
|
if ($u_and_p) { |
254
|
|
|
|
|
|
|
my ($user, $password) = split(/:/, $u_and_p, 2); |
255
|
|
|
|
|
|
|
return ($user, $password); |
256
|
|
|
|
|
|
|
} |
257
|
|
|
|
|
|
|
} |
258
|
|
|
|
|
|
|
return undef; |
259
|
|
|
|
|
|
|
} |
260
|
|
|
|
|
|
|
|
261
|
|
|
|
|
|
|
1; |