Security: Improve checking of build products

[?]
Apr 2, 2013, 9:32 PM
6ZB4CIW66KZMCEBTUWTRRNKQAV5WVPYX4QLFAJT5TTJ3CMS4JMXQC

Dependencies

  • [2] IJSJLRZH Disallow build products that are symlinks
  • [3] HTL6HIBM machine-status: Read /etc/nix.machines instead of using the BuildMachines table
  • [4] PY5GVGC7 Implemented quoted strings support in hydra-build-products to allow file names with spaces + testcase
  • [5] 7UHHF564 Security: Also check paths in the web server
  • [6] YDVFPMKP Security: Ensure that a build product refers to the Nix store
  • [7] GJFYEU3S * Nix now stores logs by default as bzip2, make sure the build page uncompresses before showing.
  • [8] BDSD2JLV * Speed up manifest generation.
  • [9] LBNVQXUB * Build the /build stuff in a separate controller.
  • [10] VH5ZABDR Add a page to show the latest evaluations for the entire server
  • [11] XJFHFZCA * Provide some redirects to build products by type so that we can for
  • [12] VYGMJ33O * Catalyst now escapes slashes to %2f, which broke defaultUriForProduct.
  • [13] NUIKDEHL * A quick hack to list the contents of various types of files (RPM,
  • [14] FM4O2L4M hydra: if evaluator sees cached build, also add the buildproducts
  • [15] PMNWRTGJ Add multiple output support
  • [*] OOQ2D3KC * Refactoring: move fetchInput out of hydra_scheduler into a separate
  • [*] 2GK5DOU7 * Downloading closures.

Change contents

  • replacement in src/lib/Hydra/Controller/Build.pm at line 173
    [5.70][5.70:249](),[5.249][2.0:67]()
    my $storeDir = $Nix::Config::storeDir . "/";
    error($c, "Invalid path in build product.")
    if substr($path, 0, length($storeDir)) ne $storeDir || $path =~ /\/\.\./;
    error($c, "Path ‘$path’ is a symbolic link.") if -l $path;
    [5.70]
    [5.609]
    my $path = pathIsInsidePrefix($path, $Nix::Config::storeDir);
    error($c, "Build product refers outside of the Nix store.") unless defined $path;
    return $path;
  • replacement in src/lib/Hydra/Controller/Build.pm at line 205
    [5.398][5.398:431]()
    checkPath($self, $c, $path);
    [5.398]
    [5.2908]
    $path = checkPath($self, $c, $path);
  • replacement in src/lib/Hydra/Controller/Build.pm at line 249
    [5.433][5.433:466]()
    checkPath($self, $c, $path);
    [5.433]
    [5.601]
    $path = checkPath($self, $c, $path);
  • replacement in src/lib/Hydra/Helper/AddBuilds.pm at line 788
    [5.5600][4.79:180]()
    my $path = File::Spec->canonpath((substr $3, 0, 1) eq "\"" ? substr $3, 1, -1 : $3);
    [5.5600]
    [5.5631]
    my $path = substr($3, 0, 1) eq "\"" ? substr($3, 1, -1) : $3;
  • replacement in src/lib/Hydra/Helper/AddBuilds.pm at line 793
    [5.288][5.288:345](),[5.345][2.68:146]()
    next if $path =~ /\/\.\./; # don't go up
    next unless substr($path, 0, length($storeDir)) eq $storeDir;
    [5.288]
    [5.5669]
    $path = pathIsInsidePrefix($path, $Nix::Config::storeDir);
    next unless defined $path;
  • edit in src/lib/Hydra/Helper/AddBuilds.pm at line 796
    [5.5707][2.147:181]()
    next if -l $path;
  • replacement in src/lib/Hydra/Helper/Nix.pm at line 19
    [5.862][3.392:419]()
    getEvals getMachines);
    [5.862]
    [5.77]
    getEvals getMachines
    pathIsInsidePrefix);
  • edit in src/lib/Hydra/Helper/Nix.pm at line 403
    [3.1399]
    [5.9702]
    }
    # Check whether ‘$path’ is inside ‘$prefix’. In particular, it checks
    # that resolving symlink components of ‘$path’ never takes us outside
    # of ‘$prefix’. We use this to check that Nix build products don't
    # refer to things outside of the Nix store (e.g. /etc/passwd) or to
    # symlinks outside of the store that point into the store
    # (e.g. /run/current-system). Return undef or the resolved path.
    sub pathIsInsidePrefix {
    my ($path, $prefix) = @_;
    my $n = 0;
    $path =~ s/\/+/\//g; # remove redundant slashes
    $path =~ s/\/*$//; # remove trailing slashes
    return undef unless $path eq $prefix || substr($path, 0, length($prefix) + 1) eq "$prefix/";
    my @cs = File::Spec->splitdir(substr($path, length($prefix) + 1));
    my $cur = $prefix;
    foreach my $c (@cs) {
    next if $c eq ".";
    # ‘..’ should not take us outside of the prefix.
    if ($c eq "..") {
    return if length($cur) <= length($prefix);
    $cur =~ s/\/[^\/]*$// or die; # remove last component
    next;
    }
    my $new = "$cur/$c";
    if (-l $new) {
    my $link = readlink $new or return undef;
    $new = substr($link, 0, 1) eq "/" ? $link : "$cur/$link";
    $new = pathIsInsidePrefix($new, $prefix);
    return undef unless defined $new;
    }
    $cur = $new;
    }
    return $cur;