~hexmode/+junk/bugzilla4

« back to all changes in this revision

Viewing changes to attachment.cgi

  • Committer: Frédéric Buclin
  • Author(s): Byron Jones
  • Date: 2011-08-04 20:35:37 UTC
  • Revision ID: lpsolit@gmail.com-20110804203537-h8ddyzqplykevcv6
Bug 637981: (CVE-2011-2379) [SECURITY] "Raw Unified" patch diffs can cause XSS on this domain in IE 6-8 and Safari
r/a=LpSolit

Show diffs side-by-side

added added

removed removed

Lines of Context:
74
74
 
75
75
# Determine whether to use the action specified by the user or the default.
76
76
my $action = $cgi->param('action') || 'view';
 
77
my $format = $cgi->param('format') || '';
77
78
 
78
79
# You must use the appropriate urlbase/sslbase param when doing anything
79
 
# but viewing an attachment.
80
 
if ($action ne 'view') {
 
80
# but viewing an attachment, or a raw diff.
 
81
if ($action ne 'view'
 
82
    && (($action !~ /^(?:interdiff|diff)$/) || $format ne 'raw'))
 
83
{
81
84
    do_ssl_redirect_if_required();
82
85
    if ($cgi->url_is_attachment_base) {
83
86
        $cgi->redirect_to_urlbase;
167
170
    # non-natural, so use the original value from $cgi in our exception
168
171
    # message here.
169
172
    detaint_natural($attach_id)
170
 
     || ThrowUserError("invalid_attach_id", { attach_id => $cgi->param($param) });
 
173
        || ThrowUserError("invalid_attach_id",
 
174
                          { attach_id => scalar $cgi->param($param) });
171
175
  
172
176
    # Make sure the attachment exists in the database.
173
177
    my $attachment = new Bugzilla::Attachment($attach_id)
174
 
      || ThrowUserError("invalid_attach_id", { attach_id => $attach_id });
 
178
        || ThrowUserError("invalid_attach_id", { attach_id => $attach_id });
175
179
 
176
180
    return $attachment if ($dont_validate_access || check_can_access($attachment));
177
181
}
187
191
        && !$user->is_insider) 
188
192
    {
189
193
        ThrowUserError('auth_failure', {action => 'access',
190
 
                                        object => 'attachment'});
 
194
                                        object => 'attachment',
 
195
                                        attach_id => $attachment->id});
191
196
    }
192
197
    return 1;
193
198
}
230
235
  return $context;
231
236
}
232
237
 
233
 
################################################################################
234
 
# Functions
235
 
################################################################################
 
238
# Gets the attachment object(s) generated by validateID, while ensuring
 
239
# attachbase and token authentication is used when required.
 
240
sub get_attachment {
 
241
    my @field_names = @_ ? @_ : qw(id);
236
242
 
237
 
# Display an attachment.
238
 
sub view {
239
 
    my $attachment;
 
243
    my %attachments;
240
244
 
241
245
    if (use_attachbase()) {
242
 
        $attachment = validateID(undef, 1);
243
 
        my $path = 'attachment.cgi?id=' . $attachment->id;
244
 
        # The user is allowed to override the content type of the attachment.
245
 
        if (defined $cgi->param('content_type')) {
246
 
            $path .= '&content_type=' . url_quote($cgi->param('content_type'));
 
246
        # Load each attachment, and ensure they are all from the same bug
 
247
        my $bug_id = 0;
 
248
        foreach my $field_name (@field_names) {
 
249
            my $attachment = validateID($field_name, 1);
 
250
            if (!$bug_id) {
 
251
                $bug_id = $attachment->bug_id;
 
252
            } elsif ($attachment->bug_id != $bug_id) {
 
253
                ThrowUserError('attachment_bug_id_mismatch');
 
254
            }
 
255
            $attachments{$field_name} = $attachment;
247
256
        }
 
257
        my @args = map { $_ . '=' . $attachments{$_}->id } @field_names;
 
258
        my $cgi_params = $cgi->canonicalise_query(@field_names, 't',
 
259
            'Bugzilla_login', 'Bugzilla_password');
 
260
        push(@args, $cgi_params) if $cgi_params;
 
261
        my $path = 'attachment.cgi?' . join('&', @args);
248
262
 
249
263
        # Make sure the attachment is served from the correct server.
250
 
        my $bug_id = $attachment->bug_id;
251
264
        if ($cgi->url_is_attachment_base($bug_id)) {
252
265
            # No need to validate the token for public attachments. We cannot request
253
266
            # credentials as we are on the alternate host.
254
 
            if (!attachmentIsPublic($attachment)) {
 
267
            if (!all_attachments_are_public(\%attachments)) {
255
268
                my $token = $cgi->param('t');
256
 
                my ($userid, undef, $token_attach_id) = Bugzilla::Token::GetTokenData($token);
257
 
                unless ($userid
258
 
                        && detaint_natural($token_attach_id)
259
 
                        && ($token_attach_id == $attachment->id))
260
 
                {
 
269
                my ($userid, undef, $token_data) = Bugzilla::Token::GetTokenData($token);
 
270
                my %token_data = unpack_token_data($token_data);
 
271
                my $valid_token = 1;
 
272
                foreach my $field_name (@field_names) {
 
273
                    my $token_id = $token_data{$field_name};
 
274
                    if (!$token_id
 
275
                        || !detaint_natural($token_id)
 
276
                        || $attachments{$field_name}->id != $token_id)
 
277
                    {
 
278
                        $valid_token = 0;
 
279
                        last;
 
280
                    }
 
281
                }
 
282
                unless ($userid && $valid_token) {
261
283
                    # Not a valid token.
262
284
                    print $cgi->redirect('-location' => correct_urlbase() . $path);
263
285
                    exit;
283
305
            # Replace %bugid% by the ID of the bug the attachment 
284
306
            # belongs to, if present.
285
307
            $attachbase =~ s/\%bugid\%/$bug_id/;
286
 
            if (attachmentIsPublic($attachment)) {
 
308
            if (all_attachments_are_public(\%attachments)) {
287
309
                # No need for a token; redirect to attachment base.
288
310
                print $cgi->redirect(-location => $attachbase . $path);
289
311
                exit;
290
312
            } else {
291
313
                # Make sure the user can view the attachment.
292
 
                check_can_access($attachment);
 
314
                foreach my $field_name (@field_names) {
 
315
                    check_can_access($attachments{$field_name});
 
316
                }
293
317
                # Create a token and redirect.
294
 
                my $token = url_quote(issue_session_token($attachment->id));
 
318
                my $token = url_quote(issue_session_token(pack_token_data(\%attachments)));
295
319
                print $cgi->redirect(-location => $attachbase . "$path&t=$token");
296
320
                exit;
297
321
            }
300
324
        do_ssl_redirect_if_required();
301
325
        # No alternate host is used. Request credentials if required.
302
326
        Bugzilla->login();
303
 
        $attachment = validateID();
304
 
    }
 
327
        foreach my $field_name (@field_names) {
 
328
            $attachments{$field_name} = validateID($field_name);
 
329
        }
 
330
    }
 
331
 
 
332
    return wantarray
 
333
        ? map { $attachments{$_} } @field_names
 
334
        : $attachments{$field_names[0]};
 
335
}
 
336
 
 
337
sub all_attachments_are_public {
 
338
    my $attachments = shift;
 
339
    foreach my $field_name (keys %$attachments) {
 
340
        if (!attachmentIsPublic($attachments->{$field_name})) {
 
341
            return 0;
 
342
        }
 
343
    }
 
344
    return 1;
 
345
}
 
346
 
 
347
sub pack_token_data {
 
348
    my $attachments = shift;
 
349
    return join(' ', map { $_ . '=' . $attachments->{$_}->id } keys %$attachments);
 
350
}
 
351
 
 
352
sub unpack_token_data {
 
353
    my @token_data = split(/ /, shift || '');
 
354
    my %data;
 
355
    foreach my $token (@token_data) {
 
356
        my ($field_name, $attach_id) = split('=', $token);
 
357
        $data{$field_name} = $attach_id;
 
358
    }
 
359
    return %data;
 
360
}
 
361
 
 
362
################################################################################
 
363
# Functions
 
364
################################################################################
 
365
 
 
366
# Display an attachment.
 
367
sub view {
 
368
    my $attachment = get_attachment();
305
369
 
306
370
    # At this point, Bugzilla->login has been called if it had to.
307
371
    my $contenttype = $attachment->contenttype;
346
410
 
347
411
sub interdiff {
348
412
    # Retrieve and validate parameters
349
 
    my $old_attachment = validateID('oldid');
350
 
    my $new_attachment = validateID('newid');
351
413
    my $format = validateFormat('html', 'raw');
 
414
    my($old_attachment, $new_attachment);
 
415
    if ($format eq 'raw') {
 
416
        ($old_attachment, $new_attachment) = get_attachment('oldid', 'newid');
 
417
    } else {
 
418
        $old_attachment = validateID('oldid');
 
419
        $new_attachment = validateID('newid');
 
420
    }
352
421
    my $context = validateContext();
353
422
 
354
423
    Bugzilla::Attachment::PatchReader::process_interdiff(
357
426
 
358
427
sub diff {
359
428
    # Retrieve and validate parameters
360
 
    my $attachment = validateID();
361
429
    my $format = validateFormat('html', 'raw');
 
430
    my $attachment = $format eq 'raw' ? get_attachment() : validateID();
362
431
    my $context = validateContext();
363
432
 
364
433
    # If it is not a patch, view normally.