]> git.ipfire.org Git - thirdparty/bugzilla.git/commitdiff
Bug 450013: (CVE-2010-2757) [SECURITY] Can sudo a user without sending email
authorFrédéric Buclin <LpSolit@gmail.com>
Wed, 4 Aug 2010 21:46:06 +0000 (23:46 +0200)
committerFrédéric Buclin <LpSolit@gmail.com>
Wed, 4 Aug 2010 21:46:06 +0000 (23:46 +0200)
r=glob a=LpSolit

Bugzilla.pm
Bugzilla/Constants.pm
relogin.cgi
template/en/default/global/user-error.html.tmpl

index 0969ccedb02a08520c22f07860518a68ad56b6cb..7c15c343f420858a55f477bf9898b747e8e09ac9 100644 (file)
@@ -49,10 +49,12 @@ use Bugzilla::Error;
 use Bugzilla::Util;
 use Bugzilla::Field;
 use Bugzilla::Flag;
+use Bugzilla::Token;
 
 use File::Basename;
 use File::Spec::Functions;
 use DateTime::TimeZone;
+use Date::Parse;
 use Safe;
 
 # This creates the request cache for non-mod_perl installations.
@@ -260,24 +262,37 @@ sub login {
     # 3: There must be a valid value in the 'sudo' cookie
     # 4: A Bugzilla::User object must exist for the given cookie value
     # 5: That user must NOT be in the 'bz_sudo_protect' group
-    my $sudo_cookie = $class->cgi->cookie('sudo');
-    detaint_natural($sudo_cookie) if defined($sudo_cookie);
-    my $sudo_target;
-    $sudo_target = new Bugzilla::User($sudo_cookie) if defined($sudo_cookie);
-    if (defined($authenticated_user)                 &&
-        $authenticated_user->in_group('bz_sudoers')  &&
-        defined($sudo_cookie)                        &&
-        defined($sudo_target)                        &&
-        !($sudo_target->in_group('bz_sudo_protect'))
-       )
-    {
-        $class->set_user($sudo_target);
-        $class->request_cache->{sudoer} = $authenticated_user;
-        # And make sure that both users have the same Auth object,
-        # since we never call Auth::login for the sudo target.
-        $sudo_target->set_authorizer($authenticated_user->authorizer);
+    my $token = $class->cgi->cookie('sudo');
+    if (defined $authenticated_user && $token) {
+        my ($user_id, $date, $sudo_target_id) = Bugzilla::Token::GetTokenData($token);
+        if (!$user_id
+            || $user_id != $authenticated_user->id
+            || !detaint_natural($sudo_target_id)
+            || (time() - str2time($date) > MAX_SUDO_TOKEN_AGE))
+        {
+            $class->cgi->remove_cookie('sudo');
+            ThrowUserError('sudo_invalid_cookie');
+        }
+
+        my $sudo_target = new Bugzilla::User($sudo_target_id);
+        if ($authenticated_user->in_group('bz_sudoers')
+            && defined $sudo_target
+            && !$sudo_target->in_group('bz_sudo_protect'))
+        {
+            $class->set_user($sudo_target);
+            $class->request_cache->{sudoer} = $authenticated_user;
+            # And make sure that both users have the same Auth object,
+            # since we never call Auth::login for the sudo target.
+            $sudo_target->set_authorizer($authenticated_user->authorizer);
 
-        # NOTE: If you want to do any special logging, do it here.
+            # NOTE: If you want to do any special logging, do it here.
+        }
+        else {
+            delete_token($token);
+            $class->cgi->remove_cookie('sudo');
+            ThrowUserError('sudo_illegal_action', { sudoer => $authenticated_user,
+                                                    target_user => $sudo_target });
+        }
     }
     else {
         $class->set_user($authenticated_user);
index 187e18acc24677cc10e43bd12e33bb73f03618b7..1228d841d00a00a1b39cc78102e8a12e8c24bf4c 100644 (file)
@@ -144,6 +144,7 @@ use File::Basename;
 
     MAX_TOKEN_AGE
     MAX_LOGINCOOKIE_AGE
+    MAX_SUDO_TOKEN_AGE
 
     SAFE_PROTOCOLS
 
@@ -371,6 +372,8 @@ use constant FIELD_TYPE_BUG_URLS => 7;
 use constant MAX_TOKEN_AGE => 3;
 # How many days a logincookie will remain valid if not used.
 use constant MAX_LOGINCOOKIE_AGE => 30;
+# How many seconds (default is 6 hours) a sudo cookie remains valid.
+use constant MAX_SUDO_TOKEN_AGE => 21600;
 
 # Protocols which are considered as safe.
 use constant SAFE_PROTOCOLS => ('afs', 'cid', 'ftp', 'gopher', 'http', 'https',
index 40e15ac7e60d0e1856861a4f03dbb36e82d36a73..e85e2ffc0657a6141224cfa44bb7b16d0b8e3e04 100755 (executable)
@@ -147,12 +147,13 @@ elsif ($action eq 'begin-sudo') {
     $reason = substr($reason, $[, 200);
     
     # Calculate the session expiry time (T + 6 hours)
-    my $time_string = time2str('%a, %d-%b-%Y %T %Z', time+(6*60*60), 'GMT');
+    my $time_string = time2str('%a, %d-%b-%Y %T %Z', time + MAX_SUDO_TOKEN_AGE, 'GMT');
 
     # For future sessions, store the unique ID of the target user
+    my $token = Bugzilla::Token::_create_token($user->id, 'sudo', $target_user->id);
     $cgi->send_cookie('-name'    => 'sudo',
                       '-expires' => $time_string,
-                      '-value'   => $target_user->id
+                      '-value'   => $token
     );
     
     # For the present, change the values of Bugzilla::user & Bugzilla::sudoer
@@ -174,6 +175,7 @@ elsif ($action eq 'begin-sudo') {
 # end-sudo: End the current sudo session (if one is in progress)
 elsif ($action eq 'end-sudo') {
     # Regardless of our state, delete the sudo cookie if it exists
+    my $token = $cgi->cookie('sudo');
     $cgi->remove_cookie('sudo');
 
     # Are we in an sudo session?
@@ -182,6 +184,8 @@ elsif ($action eq 'end-sudo') {
     if (defined($sudoer)) {
         Bugzilla->sudo_request($sudoer, undef);
     }
+    # Now that the session is over, remove the token from the DB.
+    delete_token($token);
 
     # NOTE: If you want to log the end of an sudo session, so it here.
     
index 20bda228e40e98d86ff5f8fc688aff1f8bccf449..beea2391482e8aea9b6ed117e7ff5ca4475dd7f0 100644 (file)
       [% END %]
     [% END %]
 
+  [% ELSIF error == "sudo_invalid_cookie" %]
+    [% title = "Invalid Sudo Cookie" %]
+    Your sudo cookie is invalid. Either it expired or you didn't start
+    a sudo session correctly. Refresh the page or load another page
+    to continue what you are doing as yourself.
+
+  [% ELSIF error == "sudo_illegal_action" %]
+    [% title = "Impersonation Not Authorized" %]
+    [% IF NOT sudoer.in_group("bz_sudoers") %]
+      You are not allowed to impersonate users.
+    [% ELSIF target_user AND target_user.in_group("bz_sudo_protect") %]
+      You are not allowed to impersonate [% target_user.identity FILTER html %].
+    [% ELSE %]
+      The user you tried to impersonate doesn't exist.
+    [% END %]
+
   [% ELSIF error == "sudo_in_progress" %]
     [% title = "Session In Progress" %]
     A sudo session (impersonating [% target FILTER html %]) is in progress.