summaryrefslogtreecommitdiffstats
path: root/Bugzilla/Auth
diff options
context:
space:
mode:
authorbbaetz%acm.org <>2003-03-22 13:47:09 +0100
committerbbaetz%acm.org <>2003-03-22 13:47:09 +0100
commit681ce77bc0dc5828eae2bb48471db9e373437e4b (patch)
treea7c8ba0b1e070ea489c96246eca65fc7c36f6235 /Bugzilla/Auth
parent3f1f4e57809b2e3f42e637a86646e806470faec5 (diff)
downloadbugzilla-681ce77bc0dc5828eae2bb48471db9e373437e4b.tar.gz
bugzilla-681ce77bc0dc5828eae2bb48471db9e373437e4b.tar.xz
Bug 180642 - Move authentication code into a module
r=gerv, justdave a=justdave
Diffstat (limited to 'Bugzilla/Auth')
-rw-r--r--Bugzilla/Auth/CGI.pm195
-rw-r--r--Bugzilla/Auth/Cookie.pm119
-rw-r--r--Bugzilla/Auth/DB.pm102
-rw-r--r--Bugzilla/Auth/LDAP.pm185
4 files changed, 601 insertions, 0 deletions
diff --git a/Bugzilla/Auth/CGI.pm b/Bugzilla/Auth/CGI.pm
new file mode 100644
index 000000000..b7c2e6c42
--- /dev/null
+++ b/Bugzilla/Auth/CGI.pm
@@ -0,0 +1,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>
diff --git a/Bugzilla/Auth/Cookie.pm b/Bugzilla/Auth/Cookie.pm
new file mode 100644
index 000000000..7dd2967fb
--- /dev/null
+++ b/Bugzilla/Auth/Cookie.pm
@@ -0,0 +1,119 @@
+# -*- 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::Cookie;
+
+use strict;
+
+use Bugzilla::Auth;
+use Bugzilla::Config;
+use Bugzilla::Constants;
+use Bugzilla::Util;
+
+sub authenticate {
+ my ($class, $login, $login_cookie) = @_;
+
+ return (AUTH_NODATA) unless defined $login && defined $login_cookie;
+
+ my $cgi = Bugzilla->cgi;
+
+ my $ipaddr = $cgi->remote_addr();
+ my $netaddr = Bugzilla::Auth::get_netaddr($ipaddr);
+
+ # Anything goes for these params - they're just strings which
+ # we're going to verify against the db
+ trick_taint($login);
+ trick_taint($login_cookie);
+ trick_taint($ipaddr);
+
+ my $query = "SELECT profiles.userid, profiles.disabledtext " .
+ "FROM logincookies, profiles " .
+ "WHERE logincookies.cookie=? AND " .
+ " logincookies.userid=profiles.userid AND " .
+ " logincookies.userid=? AND " .
+ " (logincookies.ipaddr=?";
+ if (defined $netaddr) {
+ trick_taint($netaddr);
+ $query .= " OR logincookies.ipaddr=?";
+ }
+ $query .= ")";
+
+ my $dbh = Bugzilla->dbh;
+ my ($userid, $disabledtext) = $dbh->selectrow_array($query, undef,
+ $login_cookie,
+ $login,
+ $ipaddr,
+ $netaddr);
+
+ return (AUTH_DISABLED, $userid, $disabledtext)
+ if ($disabledtext);
+
+ if ($userid) {
+ # If we logged in successfully, then update the lastused time on the
+ # login cookie
+ $dbh->do("UPDATE logincookies SET lastused=NULL WHERE cookie=?",
+ undef,
+ $login_cookie);
+
+ # compat code. The cookie value is used for logouts, and that
+ # isn't generic yet. Detaint it so that its usable
+ detaint_natural($::COOKIE{'Bugzilla_logincookie'});
+
+ return (AUTH_OK, $userid);
+ }
+
+ # If we get here, then the login failed.
+ return (AUTH_LOGINFAILED);
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Cookie - cookie authentication for Bugzilla
+
+=head1 SUMMARY
+
+This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
+Bugzilla, which logs the user in using a persistent cookie stored in the
+C<logincookies> table.
+
+The actual password is not stored in the cookie; only the userid and a
+I<logincookie> (which is used to reverify the login without requiring the
+password to be sent over the network) are. These I<logincookies> are
+restricted to certain IP addresses as a security meaure. The exact
+restriction can be specified by the admin via the C<loginnetmask> parameter.
+
+This module does not ever send a cookie (It has no way of knowing when a user
+is successfully logged in). Instead L<Bugzilla::Auth::CGI> handles this.
+
+=head1 SEE ALSO
+
+L<Bugzilla::Auth>, L<Bugzilla::Auth::CGI>
diff --git a/Bugzilla/Auth/DB.pm b/Bugzilla/Auth/DB.pm
new file mode 100644
index 000000000..55e4bc7c0
--- /dev/null
+++ b/Bugzilla/Auth/DB.pm
@@ -0,0 +1,102 @@
+# -*- 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::DB;
+
+use strict;
+
+use Bugzilla::Config;
+use Bugzilla::Constants;
+use Bugzilla::Util;
+
+sub authenticate {
+ my ($class, $username, $passwd) = @_;
+
+ return (AUTH_NODATA) unless defined $username && defined $passwd;
+
+ my $dbh = Bugzilla->dbh;
+
+ # We're just testing against the db, so any value is ok
+ trick_taint($username);
+
+ # Retrieve the user's ID and crypted password from the database.
+ my $sth = $dbh->prepare_cached("SELECT userid,cryptpassword,disabledtext " .
+ "FROM profiles " .
+ "WHERE login_name=?");
+ my ($userid, $realcryptpwd, $disabledtext) =
+ $dbh->selectrow_array($sth,
+ undef,
+ $username);
+
+ # If the user doesn't exist, return now
+ return (AUTH_LOGINFAILED) unless defined $userid;
+
+ # OK, now authenticate the user
+
+ # Get the salt from the user's crypted password.
+ my $salt = $realcryptpwd;
+
+ # Using the salt, crypt the password the user entered.
+ my $enteredCryptedPassword = crypt($passwd, $salt);
+
+ # Make sure the passwords match or return an error
+ return (AUTH_LOGINFAILED, $userid) unless
+ ($enteredCryptedPassword eq $realcryptpwd);
+
+ # Now we know that the user has logged in successfully,
+ # so delete any password tokens for them
+ require Token;
+ Token::DeletePasswordTokens("user logged in");
+
+ # The user may have had their account disabled
+ return (AUTH_DISABLED, $userid, $disabledtext)
+ if $disabledtext ne '';
+
+ # If we get to here, then the user is allowed to login, so we're done!
+ return (AUTH_OK, $userid);
+}
+
+sub can_edit { return 1; }
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::DB - database authentication for Bugzilla
+
+=head1 SUMMARY
+
+This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
+Bugzilla, which logs the user in using the password stored in the C<profiles>
+table. This is the most commonly used authentication module.
+
+=head1 SEE ALSO
+
+L<Bugzilla::Auth>
diff --git a/Bugzilla/Auth/LDAP.pm b/Bugzilla/Auth/LDAP.pm
new file mode 100644
index 000000000..4570bdde9
--- /dev/null
+++ b/Bugzilla/Auth/LDAP.pm
@@ -0,0 +1,185 @@
+# -*- 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::LDAP;
+
+use strict;
+
+use Bugzilla::Config;
+use Bugzilla::Constants;
+
+use Net::LDAP;
+
+sub authenticate {
+ my ($class, $username, $passwd) = @_;
+
+ # If no password was provided, then fail the authentication.
+ # While it may be valid to not have an LDAP password, when you
+ # bind without a password (regardless of the binddn value), you
+ # will get an anonymous bind. I do not know of a way to determine
+ # whether a bind is anonymous or not without making changes to the
+ # LDAP access control settings
+ return (AUTH_NODATA) unless $username && $passwd;
+
+ # We need to bind anonymously to the LDAP server. This is
+ # because we need to get the Distinguished Name of the user trying
+ # to log in. Some servers (such as iPlanet) allow you to have unique
+ # uids spread out over a subtree of an area (such as "People"), so
+ # just appending the Base DN to the uid isn't sufficient to get the
+ # user's DN. For servers which don't work this way, there will still
+ # be no harm done.
+ my $LDAPserver = Param("LDAPserver");
+ if ($LDAPserver eq "") {
+ return (AUTH_ERROR, undef, "server_not_defined");
+ }
+
+ my $LDAPport = "389"; # default LDAP port
+ if($LDAPserver =~ /:/) {
+ ($LDAPserver, $LDAPport) = split(":",$LDAPserver);
+ }
+ my $LDAPconn = Net::LDAP->new($LDAPserver, port => $LDAPport, version => 3);
+ if(!$LDAPconn) {
+ return (AUTH_ERROR, undef, "connect_failed");
+ }
+
+ my $mesg;
+ if (Param("LDAPbinddn")) {
+ my ($LDAPbinddn,$LDAPbindpass) = split(":",Param("LDAPbinddn"));
+ $mesg = $LDAPconn->bind($LDAPbinddn, password => $LDAPbindpass);
+ }
+ else {
+ $mesg = $LDAPconn->bind();
+ }
+ if($mesg->code) {
+ return (AUTH_ERROR, undef,
+ "connect_failed",
+ { errstr => $mesg->err });
+ }
+
+ # We've got our anonymous bind; let's look up this user.
+ $mesg = $LDAPconn->search( base => Param("LDAPBaseDN"),
+ scope => "sub",
+ filter => Param("LDAPuidattribute") . "=$username",
+ attrs => ['dn'],
+ );
+ return (AUTH_LOGINFAILED, undef, "lookup_failure")
+ unless $mesg->count;
+
+ # Now we get the DN from this search.
+ my $userDN = $mesg->shift_entry->dn;
+
+ # Now we attempt to bind as the specified user.
+ $mesg = $LDAPconn->bind( $userDN, password => $passwd);
+
+ return (AUTH_LOGINFAILED) if $mesg->code;
+
+ # And now we're going to repeat the search, so that we can get the
+ # mail attribute for this user.
+ $mesg = $LDAPconn->search( base => Param("LDAPBaseDN"),
+ scope => "sub",
+ filter => Param("LDAPuidattribute") . "=$username",
+ );
+ my $user_entry = $mesg->shift_entry if !$mesg->code && $mesg->count;
+ if(!$user_entry || !$user_entry->exists(Param("LDAPmailattribute"))) {
+ return (AUTH_ERROR, undef,
+ "cannot_retreive_attr",
+ { attr => Param("LDAPmailattribute") });
+ }
+
+ # get the mail attribute
+ $username = $user_entry->get_value(Param("LDAPmailattribute"));
+ # OK, so now we know that the user is valid. Lets try finding them in the
+ # Bugzilla database
+
+ # XXX - should this part be made more generic, and placed in
+ # Bugzilla::Auth? Lots of login mechanisms may have to do this, although
+ # until we actually get some more, its hard to know - BB
+
+ my $dbh = Bugzilla->dbh;
+ my $sth = $dbh->prepare_cached("SELECT userid, disabledtext " .
+ "FROM profiles " .
+ "WHERE login_name=?");
+ my ($userid, $disabledtext) =
+ $dbh->selectrow_array($sth,
+ undef,
+ $username);
+
+ # If the user doesn't exist, then they need to be added
+ unless ($userid) {
+ # We'll want the user's name for this.
+ my $userRealName = $user_entry->get_value("displayName");
+ if($userRealName eq "") {
+ $userRealName = $user_entry->get_value("cn");
+ }
+ &::InsertNewUser($username, $userRealName);
+
+ my ($userid, $disabledtext) = $dbh->selectrow_array($sth,
+ undef,
+ $username);
+ return (AUTH_ERROR, $userid, "no_userid")
+ unless $userid;
+ }
+
+ # we're done, so disconnect
+ $LDAPconn->unbind;
+
+ # Test for disabled account
+ return (AUTH_DISABLED, $userid, $disabledtext)
+ if $disabledtext ne '';
+
+ # If we get to here, then the user is allowed to login, so we're done!
+ return (AUTH_OK, $userid);
+}
+
+sub can_edit { return 0; }
+
+1;
+
+__END__
+
+=head1 NAME
+
+Bugzilla::Auth::LDAP - LDAP based authentication for Bugzilla
+
+This is an L<authentication module|Bugzilla::Auth/"AUTHENTICATION"> for
+Bugzilla, which logs the user in using an LDAP directory.
+
+=head1 DISCLAIMER
+
+B<This module is experimental>. It is poorly documented, and not very flexible.
+Search L<http://bugzilla.mozilla.org/> for a list of known LDAP bugs.
+
+None of the core Bugzilla developers, nor any of the large installations, use
+this module, and so it has received less testing. (In fact, this iteration
+hasn't been tested at all)
+
+Patches are accepted.
+
+=head1 SEE ALSO
+
+L<Bugzilla::Auth>