{ lib, stdenv, autoPatchelfHook, cmake, pkg-config, testers, which, fetchgit, fetchpatch, # Xen acpica-tools, bison, bzip2, dev86, e2fsprogs, flex, libnl, libuuid, lzo, ncurses, ocamlPackages, perl, python3Packages, systemdMinimal, xz, yajl, zlib, zstd, # Optional Components seabios, OVMF, ipxe, checkpolicy, binutils-unwrapped-all-targets, # Documentation pandoc, # Scripts bridge-utils, coreutils, diffutils, gawk, gnugrep, gnused, inetutils, iproute2, iptables, multipath-tools, nbd, openvswitch, util-linux, }: { pname, branch ? lib.versions.majorMinor version, version, vendor ? "nixos", withEFI ? true, withFlask ? false, withSeaBIOS ? true, withOVMF ? true, withIPXE ? true, useDefaultPatchList ? true, rev, hash, patches ? [ ], meta ? { }, }: let # Inherit helper functions from lib and builtins. inherit (builtins) elemAt isAttrs; inherit (lib.strings) concatLines enableFeature makeSearchPathOutput optionalString removeSuffix versionOlder ; inherit (lib.platforms) linux aarch64; inherit (lib) teams; inherit (lib.licenses) cc-by-40 gpl2Only lgpl21Only mit ; inherit (lib.meta) getExe'; inherit (lib.lists) count flatten optional optionals range remove zipListsWith ; inherit (lib.attrsets) attrByPath; # Mark versions older than minSupportedVersion as EOL. minSupportedVersion = "4.16"; ## Generic Patch Handling ## upstreamPatches = import ./patches.nix { inherit lib fetchpatch; }; upstreamPatchList = flatten ( with upstreamPatches; [ QUBES_REPRODUCIBLE_BUILDS XSA_460 XSA_461 XSA_462 ] ); ## XSA Patches Description Builder ## # Simple counter for the number of attrsets (patches) in the patches list after normalisation. numberOfPatches = count (patch: isAttrs patch) upstreamPatchList; # builtins.elemAt's index begins at 0, so we subtract 1 from the number of patches in order to # produce the range that will be used in the following builtin.map calls. availablePatchesToTry = range 0 (numberOfPatches - 1); # Takes in an attrByPath input, and outputs the attribute value for each patch in a list. # If a patch does not have a given attribute, returns `null`. Use lib.lists.remove null # to remove these junk values, if necessary. retrievePatchAttributes = attributeName: map (x: attrByPath attributeName null (elemAt upstreamPatchList x)) availablePatchesToTry; # Produces a list of newline-separated strings that lists the vulnerabilities this # Xen is NOT affected by, due to the applied Xen Security Advisory patches. This is # then used in meta.longDescription, to let users know their Xen is patched against # known vulnerabilities, as the package version isn't always the best indicator. # # Produces something like this: (one string for each XSA) # * [Xen Security Advisory #1](https://xenbits.xenproject.org/xsa/advisory-1.html): **Title for XSA.** # >Description of issue in XSA #Extra lines #are not indented, #but markdown should be #fine with it. # Fixes: # * [CVE-1999-00001](https://www.cve.org/CVERecord?id=CVE-1999-00001) # * [CVE-1999-00002](https://www.cve.org/CVERecord?id=CVE-1999-00002) # * [CVE-1999-00003](https://www.cve.org/CVERecord?id=CVE-1999-00003) writeAdvisoryDescription = if (remove null (retrievePatchAttributes [ "xsa" ]) != [ ]) then zipListsWith (a: b: a + b) (zipListsWith (a: b: a + "**" + b + ".**\n >") (zipListsWith (a: b: "* [Xen Security Advisory #" + a + "](" + b + "): ") (remove null (retrievePatchAttributes [ "xsa" ])) ( remove null (retrievePatchAttributes [ "meta" "homepage" ]) ) ) ( remove null (retrievePatchAttributes [ "meta" "description" ]) ) ) ( remove null (retrievePatchAttributes [ "meta" "longDescription" ]) ) else [ ]; #TODO: fix paths instead. scriptEnvPath = makeSearchPathOutput "out" "bin" [ bridge-utils coreutils diffutils gawk gnugrep gnused inetutils iproute2 iptables multipath-tools nbd openvswitch perl util-linux.bin which ]; in stdenv.mkDerivation (finalAttrs: { inherit pname version; # TODO: Split $out in $bin for binaries and $lib for libraries. # TODO: Python package to be in separate output/package. outputs = [ "out" "man" "doc" "dev" "boot" ]; # Main Xen source. src = fetchgit { url = "https://xenbits.xenproject.org/git-http/xen.git"; inherit rev hash; }; patches = optionals useDefaultPatchList upstreamPatchList ++ patches; nativeBuildInputs = [ autoPatchelfHook bison cmake flex pandoc pkg-config python3Packages.setuptools ]; buildInputs = [ # Xen acpica-tools bzip2 dev86 e2fsprogs.dev libnl libuuid lzo ncurses perl python3Packages.python xz yajl zlib zstd # oxenstored ocamlPackages.findlib ocamlPackages.ocaml # Python Fixes python3Packages.wrapPython ] ++ optional withFlask checkpolicy ++ optional (versionOlder version "4.19") systemdMinimal; configureFlags = [ "--enable-systemd" "--disable-qemu-traditional" "--with-system-qemu" (if withSeaBIOS then "--with-system-seabios=${seabios}/share/seabios" else "--disable-seabios") (if withOVMF then "--with-system-ovmf=${OVMF.firmware}" else "--disable-ovmf") (if withIPXE then "--with-system-ipxe=${ipxe}" else "--disable-ipxe") (enableFeature withFlask "xsmpolicy") ]; makeFlags = [ "SUBSYSTEMS=${toString finalAttrs.buildFlags}" "PREFIX=$(out)" "BASH_COMPLETION_DIR=$(PREFIX)/share/bash-completion/completions" "XEN_WHOAMI=${pname}" "XEN_DOMAIN=${vendor}" "GIT=${coreutils}/bin/false" "WGET=${coreutils}/bin/false" ] ++ optionals withEFI [ "EFI_VENDOR=${vendor}" "INSTALL_EFI_STRIP=1" "LD=${getExe' binutils-unwrapped-all-targets "ld"}" ] # These flags set the CONFIG_* options in /boot/xen.config # and define if the default policy file is built. However, # the Flask binaries always get compiled by default. ++ optionals withFlask [ "XSM_ENABLE=y" "FLASK_ENABLE=y" ]; buildFlags = [ "xen" "tools" "docs" ]; enableParallelBuilding = true; env.NIX_CFLAGS_COMPILE = toString [ "-Wno-error=maybe-uninitialized" "-Wno-error=array-bounds" ]; dontUseCmakeConfigure = true; # Remove in-tree QEMU sources, we don't need them in any circumstance. prePatch = "rm --recursive --force tools/qemu-xen tools/qemu-xen-traditional"; postPatch = # The following patch forces Xen to install xen.efi on $out/boot # instead of $out/boot/efi/efi/nixos, as the latter directory # would otherwise need to be created manually. This also creates # a more consistent output for downstreams who override the # vendor attribute above. '' substituteInPlace xen/Makefile \ --replace-fail "\$(D)\$(EFI_MOUNTPOINT)/efi/\$(EFI_VENDOR)/\$(T)-\$(XEN_FULLVERSION).efi" \ "\$(D)\$(BOOT_DIR)/\$(T)-\$(XEN_FULLVERSION).efi" '' # The following patch fixes the call to /bin/mkdir on the # launch_xenstore.sh helper script. + '' substituteInPlace tools/hotplug/Linux/launch-xenstore.in \ --replace-fail "/bin/mkdir" "${coreutils}/bin/mkdir" '' # The following expression fixes the paths called by Xen's systemd # units, so we can use them in the NixOS module. + '' substituteInPlace \ tools/hotplug/Linux/systemd/{xen-init-dom0,xen-qemu-dom0-disk-backend,xenconsoled,xendomains,xenstored}.service.in \ --replace-fail /bin/grep ${gnugrep}/bin/grep substituteInPlace \ tools/hotplug/Linux/systemd/{xen-qemu-dom0-disk-backend,xenconsoled}.service.in \ --replace-fail "/bin/mkdir" "${coreutils}/bin/mkdir" ''; installPhase = '' runHook preInstall mkdir --parents $out $out/share $boot cp -prvd dist/install/nix/store/*/* $out/ cp -prvd dist/install/etc $out cp -prvd dist/install/boot $boot runHook postInstall ''; postInstall = # Wrap xencov_split, xenmon and xentrace_format. '' wrapPythonPrograms '' # We also need to wrap pygrub, which lies in $out/libexec/xen/bin. + '' wrapPythonProgramsIn "$out/libexec/xen/bin" "$out $pythonPath" '' # Fix shebangs in Xen's various scripts. #TODO: Remove any and all usage of `sed` and replace these complicated magic runes with readable code. + '' shopt -s extglob for i in $out/etc/xen/scripts/!(*.sh); do sed --in-place "2s@^@export PATH=$out/bin:${scriptEnvPath}\n@" $i done ''; postFixup = '' addAutoPatchelfSearchPath $out/lib autoPatchelf $out/libexec/xen/bin '' # Flask is particularly hard to disable. Even after # setting the make flags to `n`, it still gets compiled. # If withFlask is disabled, delete the extra binaries. + optionalString (!withFlask) '' rm -f $out/bin/flask-* ''; passthru = { efi = if withEFI then "boot/xen-${version}.efi" else throw "This Xen was compiled without an EFI binary."; flaskPolicy = if withFlask then "boot/xenpolicy-${version}" else throw "This Xen was compiled without FLASK support."; # This test suite is very simple, as Xen's userspace # utilities require the hypervisor to be booted. tests = { pkg-config = testers.hasPkgConfigModules { package = finalAttrs.finalPackage; moduleNames = [ "xencall" "xencontrol" "xendevicemodel" "xenevtchn" "xenforeignmemory" "xengnttab" "xenguest" "xenhypfs" "xenlight" "xenstat" "xenstore" "xentoolcore" "xentoollog" "xenvchan" "xlutil" ]; }; }; }; meta = { inherit branch; description = "Type-1 hypervisor intended for embedded and hyperscale use cases"; longDescription = '' The Xen Project Hypervisor is a virtualisation technology defined as a *type-1 hypervisor*, which allows multiple virtual machines, known as domains, to run concurrently with the host on the physical machine. On a typical *type-2 hypervisor*, the virtual machines run as applications on top of the host. NixOS runs as the privileged **Domain 0**, and can paravirtualise or fully virtualise **Unprivileged Domains**. Use with the `qemu_xen` package. '' # Then, if any of the optional with* components are being built, add the "Includes:" string. + optionalString (withEFI || withFlask) ( "\nIncludes:" # Originally, this was a call for the complicated withPrefetchedSources. Since there aren't # that many optional components, we just use lib.strings.optionalString, because it's simpler. # Optional components that aren't being built are automatically hidden. + optionalString withEFI "\n* `xen.efi`: The Xen Project's [EFI binary](https://xenbits.xenproject.org/docs/${branch}-testing/misc/efi.html), available on the `boot` output of this package." + optionalString withFlask "\n* `xsm-flask`: The [FLASK Xen Security Module](https://wiki.xenproject.org/wiki/Xen_Security_Modules_:_XSM-FLASK). The `xenpolicy-${version}` file is available on the `boot` output of this package." + optionalString withSeaBIOS "\n* `seabios`: Support for the SeaBIOS boot firmware on HVM domains." + optionalString withOVMF "\n* `ovmf`: Support for the OVMF UEFI boot firmware on HVM domains." + optionalString withIPXE "\n* `ipxe`: Support for the iPXE boot firmware on HVM domains." ) # Finally, we write a notice explaining which vulnerabilities this Xen is NOT vulnerable to. # This will hopefully give users the peace of mind that their Xen is secure, without needing # to search the source code for the XSA patches. + optionalString (writeAdvisoryDescription != [ ]) ( "\n\nThis Xen Project Hypervisor (${version}) has been patched against the following known security vulnerabilities:\n" + removeSuffix "\n" (concatLines writeAdvisoryDescription) ); homepage = "https://xenproject.org/"; downloadPage = "https://downloads.xenproject.org/release/xen/${version}/"; changelog = "https://wiki.xenproject.org/wiki/Xen_Project_${branch}_Release_Notes"; license = [ # Documentation. cc-by-40 # Most of Xen is licensed under the GPL v2.0. gpl2Only # Xen Libraries and the `xl` command-line utility. lgpl21Only # Development headers in $dev/include. mit ]; maintainers = teams.xen.members; knownVulnerabilities = optional (versionOlder version minSupportedVersion) "The Xen Project Hypervisor version ${version} is no longer supported by the Xen Project Security Team. See https://xenbits.xenproject.org/docs/unstable/support-matrix.html"; mainProgram = "xl"; #TODO: Migrate meta.platforms to the new lib.systems.inspect.patterns.* format. platforms = linux; badPlatforms = aarch64; } // meta; })