summaryrefslogtreecommitdiffstats
path: root/Bugzilla/Auth/CGI.pm
blob: b7c2e6c4212b756730c3ba57c6953cd8dae65b44 (plain)
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
# -*- 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::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 = 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()");
        my $cookiepath = Param("cookiepath");
        print "Set-Cookie: Bugzilla_login=$userid ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";
        print "Set-Cookie: Bugzilla_logincookie=$logincookie ; path=$cookiepath; expires=Sun, 30-Jun-2029 00:00:00 GMT\n";

        # 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) {
        $::vars->{'authmethod'} = lc($authmethod);
        $::vars->{'userid'} = $userid;
        $::vars->{'auth_err_tag'} = $extra;
        $::vars->{'info'} = $info;

        &::ThrowCodeError("auth_err");
    }

    # 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 "Content-Type: text/html\n\n";

        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
        my $cookiepath = Param("cookiepath");
        print "Set-Cookie: Bugzilla_login= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT\n";
        print "Set-Cookie: Bugzilla_logincookie= ; path=$cookiepath; expires=Sun, 30-Jun-80 00:00:00 GMT\n";
        # 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,
                      }
                     );

}

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.

=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>.

=head1 SEE ALSO

L<Bugzilla::Auth>