diff --git a/pkgs/applications/virtualization/crosvm/default-seccomp-policy-dir.patch b/pkgs/applications/virtualization/crosvm/default-seccomp-policy-dir.patch new file mode 100644 index 000000000000..46b091b3a353 --- /dev/null +++ b/pkgs/applications/virtualization/crosvm/default-seccomp-policy-dir.patch @@ -0,0 +1,15 @@ +diff --git a/src/main.rs b/src/main.rs +index 81f20a7..481ebd7 100644 +--- a/src/main.rs ++++ b/src/main.rs +@@ -158,7 +158,9 @@ impl Default for Config { + wayland_dmabuf: false, + shared_dirs: Vec::new(), + sandbox: !cfg!(feature = "default-no-sandbox"), +- seccomp_policy_dir: PathBuf::from(SECCOMP_POLICY_DIR), ++ seccomp_policy_dir: PathBuf::from( ++ option_env!("DEFAULT_SECCOMP_POLICY_DIR").unwrap_or(SECCOMP_POLICY_DIR), ++ ), + seccomp_log_failures: false, + cras_audio: false, + cras_capture: false, diff --git a/pkgs/applications/virtualization/crosvm/default.nix b/pkgs/applications/virtualization/crosvm/default.nix new file mode 100644 index 000000000000..106b39b03366 --- /dev/null +++ b/pkgs/applications/virtualization/crosvm/default.nix @@ -0,0 +1,83 @@ +{ stdenv, rustPlatform, fetchgit, runCommand, symlinkJoin +, pkgconfig, minijail, dtc, libusb1, libcap +}: + +let + + upstreamInfo = with builtins; fromJSON (readFile ./upstream-info.json); + + arch = with stdenv.hostPlatform; + if isAarch64 then "arm" + else if isx86_64 then "x86_64" + else throw "no seccomp policy files available for host platform"; + + # used to turn symlinks into real files because write permissions are necessary for the vendoring process + delink = src: runCommand "${src.name}-delinked" { + preferLocalBuild = true; + allowSubstitutes = false; + } '' + cp -prL --reflink=auto ${src} $out + ''; + + # used to place subtrees into the location they have in the Chromium monorepo + move = src: target: runCommand "moved-${src.name}" { + preferLocalBuild = true; + allowSubstitutes = false; + } '' + mkdir -p $(dirname $out/${target}) + ln -s ${src} $out/${target} + ''; + + # used to check out subtrees from the Chromium monorepo + chromiumSource = name: subtrees: delink (symlinkJoin { + inherit name; + paths = stdenv.lib.mapAttrsToList ( + location: { url, rev, sha256, fetchSubmodules, ... }: + move (fetchgit { + inherit url rev sha256 fetchSubmodules; + }) location) subtrees; + }); + +in + + rustPlatform.buildRustPackage rec { + pname = "crosvm"; + inherit (upstreamInfo) version; + + src = chromiumSource "${pname}-sources" upstreamInfo.components; + + sourceRoot = "${src.name}/chromiumos/platform/crosvm"; + + patches = [ + ./default-seccomp-policy-dir.patch + ]; + + cargoSha256 = "16cfp79c13ng5jjcrvz00h3cg7cc9ywhjiq02vsm757knn9jgr1v"; + + nativeBuildInputs = [ pkgconfig ]; + + buildInputs = [ dtc libcap libusb1 minijail ]; + + postPatch = '' + sed -i "s|/usr/share/policy/crosvm/|$out/share/policy/|g" \ + seccomp/*/*.policy + ''; + + preBuild = '' + export DEFAULT_SECCOMP_POLICY_DIR=$out/share/policy + ''; + + postInstall = '' + mkdir -p $out/share/policy/ + cp seccomp/${arch}/* $out/share/policy/ + ''; + + passthru.updateScript = ./update.py; + + meta = with stdenv.lib; { + description = "A secure virtual machine monitor for KVM"; + homepage = "https://chromium.googlesource.com/chromiumos/platform/crosvm/"; + license = licenses.bsd3; + platforms = [ "aarch64-linux" "x86_64-linux" ]; + }; + } diff --git a/pkgs/applications/virtualization/crosvm/update.py b/pkgs/applications/virtualization/crosvm/update.py new file mode 100755 index 000000000000..d00bffce3b8f --- /dev/null +++ b/pkgs/applications/virtualization/crosvm/update.py @@ -0,0 +1,85 @@ +#! /usr/bin/env nix-shell +#! nix-shell -p python3 -p nix-prefetch-git -i python + +import base64 +import csv +import json +import re +import subprocess +import xml.etree.ElementTree as ElementTree +from codecs import iterdecode +from operator import itemgetter +from os.path import dirname, splitext +from urllib.request import urlopen + +# ChromiumOS components required to build crosvm. +components = ['chromiumos/platform/crosvm', 'chromiumos/third_party/adhd'] + +git_root = 'https://chromium.googlesource.com/' +manifest_versions = f'{git_root}chromiumos/manifest-versions' +buildspecs_url = f'{manifest_versions}/+/refs/heads/master/paladin/buildspecs/' + +# CrOS version numbers look like this: +# [.].. +# +# As far as I can tell, branches are where internal Google +# modifications are added to turn Chromium OS into Chrome OS, and +# branch branches are used for fixes for specific devices. So for +# Chromium OS they will always be 0. This is a best guess, and is not +# documented. +with urlopen('https://cros-omahaproxy.appspot.com/all') as resp: + versions = csv.DictReader(iterdecode(resp, 'utf-8')) + stables = filter(lambda v: v['track'] == 'stable-channel', versions) + stable = sorted(stables, key=itemgetter('chrome_version'), reverse=True)[0] + +chrome_major_version = re.match(r'\d+', stable['chrome_version'])[0] +chromeos_tip_build = re.match(r'\d+', stable['chromeos_version'])[0] + +# Find the most recent buildspec for the stable Chrome version and +# Chromium OS build number. Its branch build and branch branch build +# numbers will (almost?) certainly be 0. It will then end with an rc +# number -- presumably these are release candidates, one of which +# becomes the final release. Presumably the one with the highest rc +# number. +with urlopen(f'{buildspecs_url}{chrome_major_version}/?format=TEXT') as resp: + listing = base64.decodebytes(resp.read()).decode('utf-8') + buildspecs = [(line.split('\t', 1)[1]) for line in listing.splitlines()] + buildspecs = [s for s in buildspecs if s.startswith(chromeos_tip_build)] + buildspecs.sort(reverse=True) + buildspec = splitext(buildspecs[0])[0] + +revisions = {} + +# Read the buildspec, and extract the git revisions for each component. +with urlopen(f'{buildspecs_url}{chrome_major_version}/{buildspec}.xml?format=TEXT') as resp: + xml = base64.decodebytes(resp.read()).decode('utf-8') + root = ElementTree.fromstring(xml) + for project in root.findall('project'): + revisions[project.get('name')] = project.get('revision') + +# Initialize the data that will be output from this script. Leave the +# rc number in buildspec so nobody else is subject to the same level +# of confusion I have been. +data = {'version': f'{chrome_major_version}.{buildspec}', 'components': {}} + +# Fill in the 'components' dictionary with the output from +# nix-prefetch-git, which can be passed straight to fetchGit when +# imported by Nix. +for component in components: + argv = ['nix-prefetch-git', + '--url', git_root + component, + '--rev', revisions[component]] + + output = subprocess.check_output(argv) + data['components'][component] = json.loads(output.decode('utf-8')) + +# Find the path to crosvm's default.nix, so the srcs data can be +# written into the same directory. +argv = ['nix-instantiate', '--eval', '--json', '-A', 'crosvm.meta.position'] +position = json.loads(subprocess.check_output(argv).decode('utf-8')) +filename = re.match(r'[^:]*', position)[0] + +# Finally, write the output. +with open(dirname(filename) + '/upstream-info.json', 'w') as out: + json.dump(data, out, indent=2) + out.write('\n') diff --git a/pkgs/applications/virtualization/crosvm/upstream-info.json b/pkgs/applications/virtualization/crosvm/upstream-info.json new file mode 100644 index 000000000000..01921f9460a0 --- /dev/null +++ b/pkgs/applications/virtualization/crosvm/upstream-info.json @@ -0,0 +1,19 @@ +{ + "version": "77.12371.0.0-rc1", + "components": { + "chromiumos/platform/crosvm": { + "url": "https://chromium.googlesource.com/chromiumos/platform/crosvm", + "rev": "f5285c647acacb4f25ef8cf9334254b976e71686", + "date": "2019-07-25T22:15:48+00:00", + "sha256": "1ccjd540xmpad082w9ri13q78wkg95xxmq38b8ybcrj4f7lsxm6w", + "fetchSubmodules": false + }, + "chromiumos/third_party/adhd": { + "url": "https://chromium.googlesource.com/chromiumos/third_party/adhd", + "rev": "a1c0d93d991daffb042b979ac807bbe9c1f9a3ee", + "date": "2019-07-25T20:38:50-07:00", + "sha256": "11bijqd876adarq96syywn6znfbiflqssgb2j4w032iw2vfnnsyy", + "fetchSubmodules": false + } + } +} diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index 292f35962136..e51c7b9ff569 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -1319,6 +1319,8 @@ in crip = callPackage ../applications/audio/crip { }; + crosvm = callPackage ../applications/virtualization/crosvm { }; + crunch = callPackage ../tools/security/crunch { }; crudini = callPackage ../tools/misc/crudini { };