1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
|
# -*- Mode: perl; indent-tabs-mode: nil -*-
#
# The contents of this file are subject to the Mozilla Public
# License Version 1.1 (the "License"); you may not use this file
# except in compliance with the License. You may obtain a copy of
# the License at http://www.mozilla.org/MPL/
#
# Software distributed under the License is distributed on an "AS
# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
# implied. See the License for the specific language governing
# rights and limitations under the License.
#
# The Original Code is the Bugzilla Bug Tracking System.
#
# The Initial Developer of the Original Code is Netscape Communications
# Corporation. Portions created by Netscape are
# Copyright (C) 1998 Netscape Communications Corporation. All
# Rights Reserved.
#
# Contributor(s): Terry Weissman <terry@mozilla.org>
# Dan Mosedale <dmose@mozilla.org>
# Joe Robins <jmrobins@tgix.com>
# Dave Miller <justdave@syndicomm.com>
# Christopher Aillon <christopher@aillon.com>
# Gervase Markham <gerv@gerv.net>
# Christian Reis <kiko@async.com.br>
# Bradley Baetz <bbaetz@acm.org>
package Bugzilla::Auth::CGI;
use strict;
use Bugzilla::Config;
use Bugzilla::Constants;
use Bugzilla::Error;
use Bugzilla::Util;
sub login {
my ($class, $type) = @_;
# 'NORMAL' logins depend on the 'requirelogin' param
if ($type == LOGIN_NORMAL) {
$type = Param('requirelogin') ? LOGIN_REQUIRED : LOGIN_OPTIONAL;
}
my $cgi = Bugzilla->cgi;
# First, try the actual login method against form variables
my $username = $cgi->param("Bugzilla_login");
my $passwd = $cgi->param("Bugzilla_password");
my $authmethod = Param("loginmethod");
my ($authres, $userid, $extra, $info) =
Bugzilla::Auth->authenticate($username, $passwd);
if ($authres == AUTH_OK) {
# Login via username/password was correct and valid, so create
# and send out the login cookies
my $ipaddr = $cgi->remote_addr;
unless ($cgi->param('Bugzilla_restrictlogin') ||
Param('loginnetmask') == 32) {
$ipaddr = Bugzilla::Auth::get_netaddr($ipaddr);
}
# The IP address is valid, at least for comparing with itself in a
# subsequent login
trick_taint($ipaddr);
my $dbh = Bugzilla->dbh;
$dbh->do("INSERT INTO logincookies (userid, ipaddr) VALUES (?, ?)",
undef,
$userid, $ipaddr);
my $logincookie = $dbh->selectrow_array("SELECT LAST_INSERT_ID()");
$cgi->send_cookie(-name => 'Bugzilla_login',
-value => $userid,
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
-value => $logincookie,
-expires => 'Fri, 01-Jan-2038 00:00:00 GMT');
# compat code. The cookie value is used for logouts, and that
# isn't generic yet.
$::COOKIE{'Bugzilla_logincookie'} = $logincookie;
} elsif ($authres == AUTH_NODATA) {
# No data from the form, so try to login via cookies
$username = $cgi->cookie("Bugzilla_login");
$passwd = $cgi->cookie("Bugzilla_logincookie");
require Bugzilla::Auth::Cookie;
my $authmethod = "Cookie";
($authres, $userid, $extra) =
Bugzilla::Auth::Cookie->authenticate($username, $passwd);
# If the data for the cookie was incorrect, then treat that as
# NODATA. This could occur if the user's IP changed, for example.
# Give them un-loggedin access if allowed (checked below)
$authres = AUTH_NODATA if $authres == AUTH_LOGINFAILED;
}
# Now check the result
# An error may have occurred with the login mechanism
if ($authres == AUTH_ERROR) {
ThrowCodeError("auth_err",
{ authmethod => lc($authmethod),
userid => $userid,
auth_err_tag => $extra,
info => $info
});
}
# We can load the page if the login was ok, or there was no data
# but a login wasn't required
if ($authres == AUTH_OK ||
($authres == AUTH_NODATA && $type == LOGIN_OPTIONAL)) {
# login succeded, so we're done
return $userid;
}
# No login details were given, but we require a login if the
# page does
if ($authres == AUTH_NODATA && $type == LOGIN_REQUIRED) {
# Throw up the login page
print Bugzilla->cgi->header();
my $template = Bugzilla->template;
$template->process("account/auth/login.html.tmpl",
{ 'target' => $cgi->url(-relative=>1),
'form' => \%::FORM,
'mform' => \%::MFORM,
'caneditaccount' => Bugzilla::Auth->can_edit,
}
)
|| ThrowTemplateError($template->error());
# This seems like as good as time as any to get rid of old
# crufty junk in the logincookies table. Get rid of any entry
# that hasn't been used in a month.
Bugzilla->dbh->do("DELETE FROM logincookies " .
"WHERE TO_DAYS(NOW()) - TO_DAYS(lastused) > 30");
exit;
}
# The username/password may be wrong
# Don't let the user know whether the username exists or whether
# the password was just wrong. (This makes it harder for a cracker
# to find account names by brute force)
if ($authres == AUTH_LOGINFAILED) {
ThrowUserError("invalid_username_or_password");
}
# The account may be disabled
if ($authres == AUTH_DISABLED) {
# Clear the cookie
$cgi->send_cookie(-name => 'Bugzilla_login',
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
$cgi->send_cookie(-name => 'Bugzilla_logincookie',
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
# and throw a user error
ThrowUserError("account_disabled",
{'disabled_reason' => $extra});
}
# If we get here, then we've run out of options, which shouldn't happen
ThrowCodeError("authres_unhandled",
{ authres => $authres,
type => $type,
}
);
}
sub logout {
my ($class, $user) = @_;
if ($user) {
# Even though we know the userid must match, we still check it in the
# SQL as a sanity check, since there is no locking here, and if
# the user logged out from two machines simulataniously, while someone
# else logged in and got the same cookie, we could be logging the
# other user out here. Yes, this is very very very unlikely, but why
# take chances? - bbaetz
my $dbh = Bugzilla->dbh;
$dbh->do("DELETE FROM logincookies WHERE cookie = ? AND userid = ?",
undef, $::COOKIE{"Bugzilla_logincookie"}, $user->id);
}
my $cgi = Bugzilla->cgi;
$cgi->send_cookie(-name => "Bugzilla_login",
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
$cgi->send_cookie(-name => "Bugzilla_logincookie",
-expires => "Tue, 15-Sep-1998 21:49:00 GMT");
}
1;
__END__
=head1 NAME
Bugzilla::Auth::CGI - CGI-based logins for Bugzilla
=head1 SUMMARY
This is a L<login module|Bugzilla::Auth/"LOGIN"> for Bugzilla. Users connecting
from a CGI script use this module to authenticate. Logouts are also handled here.
=head1 BEHAVIOUR
Users are first authenticated against the default authentication handler,
using the CGI parameters I<Bugzilla_login> and I<Bugzilla_password>.
If no data is present for that, then cookies are tried, using
L<Bugzilla::Auth::Cookie>.
When a logout is performed, we take care of removing the relevant
logincookie database entry and effectively deleting the client cookie.
=head1 SEE ALSO
L<Bugzilla::Auth>
|