xref: /openbmc/qemu/tests/qemu-iotests/149 (revision 169c4e77640b6ab6d7665ed0839873c1bc8d766d)
17c477526SPhilippe Mathieu-Daudé#!/usr/bin/env python3
29dd003a9SVladimir Sementsov-Ogievskiy# group: rw sudo
36278ae03SDaniel P. Berrange#
46278ae03SDaniel P. Berrange# Copyright (C) 2016 Red Hat, Inc.
56278ae03SDaniel P. Berrange#
66278ae03SDaniel P. Berrange# This program is free software; you can redistribute it and/or modify
76278ae03SDaniel P. Berrange# it under the terms of the GNU General Public License as published by
86278ae03SDaniel P. Berrange# the Free Software Foundation; either version 2 of the License, or
96278ae03SDaniel P. Berrange# (at your option) any later version.
106278ae03SDaniel P. Berrange#
116278ae03SDaniel P. Berrange# This program is distributed in the hope that it will be useful,
126278ae03SDaniel P. Berrange# but WITHOUT ANY WARRANTY; without even the implied warranty of
136278ae03SDaniel P. Berrange# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
146278ae03SDaniel P. Berrange# GNU General Public License for more details.
156278ae03SDaniel P. Berrange#
166278ae03SDaniel P. Berrange# You should have received a copy of the GNU General Public License
176278ae03SDaniel P. Berrange# along with this program.  If not, see <http://www.gnu.org/licenses/>.
186278ae03SDaniel P. Berrange#
196278ae03SDaniel P. Berrange# Creator/Owner: Daniel P. Berrange <berrange@redhat.com>
206278ae03SDaniel P. Berrange#
216278ae03SDaniel P. Berrange# Exercise the QEMU 'luks' block driver to validate interoperability
226278ae03SDaniel P. Berrange# with the Linux dm-crypt + cryptsetup implementation
236278ae03SDaniel P. Berrange
246278ae03SDaniel P. Berrangeimport subprocess
256278ae03SDaniel P. Berrangeimport os
266278ae03SDaniel P. Berrangeimport os.path
276278ae03SDaniel P. Berrange
286278ae03SDaniel P. Berrangeimport base64
296278ae03SDaniel P. Berrange
306278ae03SDaniel P. Berrangeimport iotests
316278ae03SDaniel P. Berrange
326278ae03SDaniel P. Berrange
336278ae03SDaniel P. Berrangeclass LUKSConfig(object):
346278ae03SDaniel P. Berrange    """Represent configuration parameters for a single LUKS
356278ae03SDaniel P. Berrange       setup to be tested"""
366278ae03SDaniel P. Berrange
376278ae03SDaniel P. Berrange    def __init__(self, name, cipher, keylen, mode, ivgen,
386278ae03SDaniel P. Berrange                 ivgen_hash, hash, password=None, passwords=None):
396278ae03SDaniel P. Berrange
406278ae03SDaniel P. Berrange        self.name = name
416278ae03SDaniel P. Berrange        self.cipher = cipher
426278ae03SDaniel P. Berrange        self.keylen = keylen
436278ae03SDaniel P. Berrange        self.mode = mode
446278ae03SDaniel P. Berrange        self.ivgen = ivgen
456278ae03SDaniel P. Berrange        self.ivgen_hash = ivgen_hash
466278ae03SDaniel P. Berrange        self.hash = hash
476278ae03SDaniel P. Berrange
486278ae03SDaniel P. Berrange        if passwords is not None:
496278ae03SDaniel P. Berrange            self.passwords = passwords
506278ae03SDaniel P. Berrange        else:
516278ae03SDaniel P. Berrange            self.passwords = {}
526278ae03SDaniel P. Berrange
536278ae03SDaniel P. Berrange            if password is None:
546278ae03SDaniel P. Berrange                self.passwords["0"] = "123456"
556278ae03SDaniel P. Berrange            else:
566278ae03SDaniel P. Berrange                self.passwords["0"] = password
576278ae03SDaniel P. Berrange
586278ae03SDaniel P. Berrange    def __repr__(self):
596278ae03SDaniel P. Berrange        return self.name
606278ae03SDaniel P. Berrange
616278ae03SDaniel P. Berrange    def image_name(self):
626278ae03SDaniel P. Berrange        return "luks-%s.img" % self.name
636278ae03SDaniel P. Berrange
646278ae03SDaniel P. Berrange    def image_path(self):
656278ae03SDaniel P. Berrange        return os.path.join(iotests.test_dir, self.image_name())
666278ae03SDaniel P. Berrange
676278ae03SDaniel P. Berrange    def device_name(self):
686278ae03SDaniel P. Berrange        return "qiotest-145-%s" % self.name
696278ae03SDaniel P. Berrange
706278ae03SDaniel P. Berrange    def device_path(self):
716278ae03SDaniel P. Berrange        return "/dev/mapper/" + self.device_name()
726278ae03SDaniel P. Berrange
736278ae03SDaniel P. Berrange    def first_password(self):
746278ae03SDaniel P. Berrange        for i in range(8):
756278ae03SDaniel P. Berrange            slot = str(i)
766278ae03SDaniel P. Berrange            if slot in self.passwords:
776278ae03SDaniel P. Berrange                return (self.passwords[slot], slot)
786278ae03SDaniel P. Berrange        raise Exception("No password found")
796278ae03SDaniel P. Berrange
806278ae03SDaniel P. Berrange    def first_password_base64(self):
816278ae03SDaniel P. Berrange        (pw, slot) = self.first_password()
828eb5e674SMax Reitz        return base64.b64encode(pw.encode('ascii')).decode('ascii')
836278ae03SDaniel P. Berrange
846278ae03SDaniel P. Berrange    def active_slots(self):
856278ae03SDaniel P. Berrange        slots = []
866278ae03SDaniel P. Berrange        for i in range(8):
876278ae03SDaniel P. Berrange            slot = str(i)
886278ae03SDaniel P. Berrange            if slot in self.passwords:
896278ae03SDaniel P. Berrange                slots.append(slot)
906278ae03SDaniel P. Berrange        return slots
916278ae03SDaniel P. Berrange
926278ae03SDaniel P. Berrangedef verify_passwordless_sudo():
936278ae03SDaniel P. Berrange    """Check whether sudo is configured to allow
946278ae03SDaniel P. Berrange       password-less access to commands"""
956278ae03SDaniel P. Berrange
966278ae03SDaniel P. Berrange    args = ["sudo", "-n", "/bin/true"]
976278ae03SDaniel P. Berrange
986278ae03SDaniel P. Berrange    proc = subprocess.Popen(args,
996278ae03SDaniel P. Berrange                            stdin=subprocess.PIPE,
1006278ae03SDaniel P. Berrange                            stdout=subprocess.PIPE,
1018eb5e674SMax Reitz                            stderr=subprocess.STDOUT,
1028eb5e674SMax Reitz                            universal_newlines=True)
1036278ae03SDaniel P. Berrange
1046278ae03SDaniel P. Berrange    msg = proc.communicate()[0]
1056278ae03SDaniel P. Berrange
1066278ae03SDaniel P. Berrange    if proc.returncode != 0:
1076278ae03SDaniel P. Berrange        iotests.notrun('requires password-less sudo access: %s' % msg)
1086278ae03SDaniel P. Berrange
1096278ae03SDaniel P. Berrange
1106278ae03SDaniel P. Berrangedef cryptsetup(args, password=None):
1116278ae03SDaniel P. Berrange    """Run the cryptsetup command in batch mode"""
1126278ae03SDaniel P. Berrange
1136278ae03SDaniel P. Berrange    fullargs = ["sudo", "cryptsetup", "-q", "-v"]
1146278ae03SDaniel P. Berrange    fullargs.extend(args)
1156278ae03SDaniel P. Berrange
1166278ae03SDaniel P. Berrange    iotests.log(" ".join(fullargs), filters=[iotests.filter_test_dir])
1176278ae03SDaniel P. Berrange    proc = subprocess.Popen(fullargs,
1186278ae03SDaniel P. Berrange                            stdin=subprocess.PIPE,
1196278ae03SDaniel P. Berrange                            stdout=subprocess.PIPE,
1208eb5e674SMax Reitz                            stderr=subprocess.STDOUT,
1218eb5e674SMax Reitz                            universal_newlines=True)
1226278ae03SDaniel P. Berrange
1236278ae03SDaniel P. Berrange    msg = proc.communicate(password)[0]
1246278ae03SDaniel P. Berrange
1256278ae03SDaniel P. Berrange    if proc.returncode != 0:
1266278ae03SDaniel P. Berrange        raise Exception(msg)
1276278ae03SDaniel P. Berrange
1286278ae03SDaniel P. Berrange
1296278ae03SDaniel P. Berrangedef cryptsetup_add_password(config, slot):
1306278ae03SDaniel P. Berrange    """Add another password to a LUKS key slot"""
1316278ae03SDaniel P. Berrange
1326278ae03SDaniel P. Berrange    (password, mainslot) = config.first_password()
1336278ae03SDaniel P. Berrange
1346278ae03SDaniel P. Berrange    pwfile = os.path.join(iotests.test_dir, "passwd.txt")
1356278ae03SDaniel P. Berrange    with open(pwfile, "w") as fh:
1366278ae03SDaniel P. Berrange        fh.write(config.passwords[slot])
1376278ae03SDaniel P. Berrange
1386278ae03SDaniel P. Berrange    try:
1396278ae03SDaniel P. Berrange        args = ["luksAddKey", config.image_path(),
1406278ae03SDaniel P. Berrange                "--key-slot", slot,
1416278ae03SDaniel P. Berrange                "--key-file", "-",
142307d9991SDaniel P. Berrange                "--iter-time", "10",
1436278ae03SDaniel P. Berrange                pwfile]
1446278ae03SDaniel P. Berrange
1456278ae03SDaniel P. Berrange        cryptsetup(args, password)
1466278ae03SDaniel P. Berrange    finally:
1476278ae03SDaniel P. Berrange        os.unlink(pwfile)
1486278ae03SDaniel P. Berrange
1496278ae03SDaniel P. Berrange
1506278ae03SDaniel P. Berrangedef cryptsetup_format(config):
1516278ae03SDaniel P. Berrange    """Format a new LUKS volume with cryptsetup, adding the
1526278ae03SDaniel P. Berrange    first key slot only"""
1536278ae03SDaniel P. Berrange
1546278ae03SDaniel P. Berrange    (password, slot) = config.first_password()
1556278ae03SDaniel P. Berrange
156da51e998SDaniel P. Berrangé    args = ["luksFormat", "--type", "luks1"]
1576278ae03SDaniel P. Berrange    cipher = config.cipher + "-" + config.mode + "-" + config.ivgen
1586278ae03SDaniel P. Berrange    if config.ivgen_hash is not None:
1596278ae03SDaniel P. Berrange        cipher = cipher + ":" + config.ivgen_hash
1608b7cdba3SDaniel P. Berrange    elif config.ivgen == "essiv":
1618b7cdba3SDaniel P. Berrange        cipher = cipher + ":" + "sha256"
1626278ae03SDaniel P. Berrange    args.extend(["--cipher", cipher])
1636278ae03SDaniel P. Berrange    if config.mode == "xts":
1646278ae03SDaniel P. Berrange        args.extend(["--key-size", str(config.keylen * 2)])
1656278ae03SDaniel P. Berrange    else:
1666278ae03SDaniel P. Berrange        args.extend(["--key-size", str(config.keylen)])
1676278ae03SDaniel P. Berrange    if config.hash is not None:
1686278ae03SDaniel P. Berrange        args.extend(["--hash", config.hash])
1696278ae03SDaniel P. Berrange    args.extend(["--key-slot", slot])
1706278ae03SDaniel P. Berrange    args.extend(["--key-file", "-"])
171307d9991SDaniel P. Berrange    args.extend(["--iter-time", "10"])
1726278ae03SDaniel P. Berrange    args.append(config.image_path())
1736278ae03SDaniel P. Berrange
1746278ae03SDaniel P. Berrange    cryptsetup(args, password)
1756278ae03SDaniel P. Berrange
1766278ae03SDaniel P. Berrange
1776278ae03SDaniel P. Berrangedef chown(config):
1786278ae03SDaniel P. Berrange    """Set the ownership of a open LUKS device to this user"""
1796278ae03SDaniel P. Berrange
1806278ae03SDaniel P. Berrange    path = config.device_path()
1816278ae03SDaniel P. Berrange
1826278ae03SDaniel P. Berrange    args = ["sudo", "chown", "%d:%d" % (os.getuid(), os.getgid()), path]
1836278ae03SDaniel P. Berrange    iotests.log(" ".join(args), filters=[iotests.filter_chown])
1846278ae03SDaniel P. Berrange    proc = subprocess.Popen(args,
1856278ae03SDaniel P. Berrange                            stdin=subprocess.PIPE,
1866278ae03SDaniel P. Berrange                            stdout=subprocess.PIPE,
1876278ae03SDaniel P. Berrange                            stderr=subprocess.STDOUT)
1886278ae03SDaniel P. Berrange
1896278ae03SDaniel P. Berrange    msg = proc.communicate()[0]
1906278ae03SDaniel P. Berrange
1916278ae03SDaniel P. Berrange    if proc.returncode != 0:
192ae50b71dSDaniel P. Berrange        raise Exception(msg)
1936278ae03SDaniel P. Berrange
1946278ae03SDaniel P. Berrange
1956278ae03SDaniel P. Berrangedef cryptsetup_open(config):
1966278ae03SDaniel P. Berrange    """Open an image as a LUKS device"""
1976278ae03SDaniel P. Berrange
1986278ae03SDaniel P. Berrange    (password, slot) = config.first_password()
1996278ae03SDaniel P. Berrange
2006278ae03SDaniel P. Berrange    args = ["luksOpen", config.image_path(), config.device_name()]
2016278ae03SDaniel P. Berrange
2026278ae03SDaniel P. Berrange    cryptsetup(args, password)
2036278ae03SDaniel P. Berrange
2046278ae03SDaniel P. Berrange
2056278ae03SDaniel P. Berrangedef cryptsetup_close(config):
2066278ae03SDaniel P. Berrange    """Close an active LUKS device """
2076278ae03SDaniel P. Berrange
2086278ae03SDaniel P. Berrange    args = ["luksClose", config.device_name()]
2096278ae03SDaniel P. Berrange    cryptsetup(args)
2106278ae03SDaniel P. Berrange
2116278ae03SDaniel P. Berrange
2126278ae03SDaniel P. Berrangedef delete_image(config):
2136278ae03SDaniel P. Berrange    """Delete a disk image"""
2146278ae03SDaniel P. Berrange
2156278ae03SDaniel P. Berrange    try:
2166278ae03SDaniel P. Berrange        os.unlink(config.image_path())
2176278ae03SDaniel P. Berrange        iotests.log("unlink %s" % config.image_path(),
2186278ae03SDaniel P. Berrange                    filters=[iotests.filter_test_dir])
2196278ae03SDaniel P. Berrange    except Exception as e:
2206278ae03SDaniel P. Berrange        pass
2216278ae03SDaniel P. Berrange
2226278ae03SDaniel P. Berrange
2236278ae03SDaniel P. Berrangedef create_image(config, size_mb):
2246278ae03SDaniel P. Berrange    """Create a bare disk image with requested size"""
2256278ae03SDaniel P. Berrange
2266278ae03SDaniel P. Berrange    delete_image(config)
2276278ae03SDaniel P. Berrange    iotests.log("truncate %s --size %dMB" % (config.image_path(), size_mb),
2286278ae03SDaniel P. Berrange                filters=[iotests.filter_test_dir])
2296278ae03SDaniel P. Berrange    with open(config.image_path(), "w") as fn:
2306278ae03SDaniel P. Berrange        fn.truncate(size_mb * 1024 * 1024)
2316278ae03SDaniel P. Berrange
2326278ae03SDaniel P. Berrange
2334dd218fdSHanna Reitzdef check_cipher_support(config, output):
2344dd218fdSHanna Reitz    """Check the output of qemu-img or qemu-io for mention of the respective
2354dd218fdSHanna Reitz    cipher algorithm being unsupported, and if so, skip this test.
2364dd218fdSHanna Reitz    (Returns `output` for convenience.)"""
2374dd218fdSHanna Reitz
2384dd218fdSHanna Reitz    if 'Unsupported cipher algorithm' in output:
2394dd218fdSHanna Reitz        iotests.notrun('Unsupported cipher algorithm '
2404dd218fdSHanna Reitz                       f'{config.cipher}-{config.keylen}-{config.mode}; '
2414dd218fdSHanna Reitz                       'consider configuring qemu with a different crypto '
2424dd218fdSHanna Reitz                       'backend')
2434dd218fdSHanna Reitz    return output
2444dd218fdSHanna Reitz
2456278ae03SDaniel P. Berrangedef qemu_img_create(config, size_mb):
2466278ae03SDaniel P. Berrange    """Create and format a disk image with LUKS using qemu-img"""
2476278ae03SDaniel P. Berrange
2486278ae03SDaniel P. Berrange    opts = [
2496278ae03SDaniel P. Berrange        "key-secret=sec0",
250307d9991SDaniel P. Berrange        "iter-time=10",
2516278ae03SDaniel P. Berrange        "cipher-alg=%s-%d" % (config.cipher, config.keylen),
2526278ae03SDaniel P. Berrange        "cipher-mode=%s" % config.mode,
2536278ae03SDaniel P. Berrange        "ivgen-alg=%s" % config.ivgen,
2546278ae03SDaniel P. Berrange        "hash-alg=%s" % config.hash,
2556278ae03SDaniel P. Berrange    ]
2566278ae03SDaniel P. Berrange    if config.ivgen_hash is not None:
2576278ae03SDaniel P. Berrange        opts.append("ivgen-hash-alg=%s" % config.ivgen_hash)
2586278ae03SDaniel P. Berrange
2596278ae03SDaniel P. Berrange    args = ["create", "-f", "luks",
2606278ae03SDaniel P. Berrange            "--object",
2616278ae03SDaniel P. Berrange            ("secret,id=sec0,data=%s,format=base64" %
2626278ae03SDaniel P. Berrange             config.first_password_base64()),
2636278ae03SDaniel P. Berrange            "-o", ",".join(opts),
2646278ae03SDaniel P. Berrange            config.image_path(),
2656278ae03SDaniel P. Berrange            "%dM" % size_mb]
2666278ae03SDaniel P. Berrange
2676278ae03SDaniel P. Berrange    iotests.log("qemu-img " + " ".join(args), filters=[iotests.filter_test_dir])
26888baae55SJohn Snow    try:
26988baae55SJohn Snow        iotests.qemu_img(*args)
27088baae55SJohn Snow    except subprocess.CalledProcessError as exc:
27188baae55SJohn Snow        check_cipher_support(config, exc.output)
27288baae55SJohn Snow        raise
2736278ae03SDaniel P. Berrange
2746278ae03SDaniel P. Berrangedef qemu_io_image_args(config, dev=False):
2756278ae03SDaniel P. Berrange    """Get the args for access an image or device with qemu-io"""
2766278ae03SDaniel P. Berrange
2776278ae03SDaniel P. Berrange    if dev:
2786278ae03SDaniel P. Berrange        return [
2796278ae03SDaniel P. Berrange            "--image-opts",
2804e04f3d9SKevin Wolf            "driver=host_device,filename=%s" % config.device_path()]
2816278ae03SDaniel P. Berrange    else:
2826278ae03SDaniel P. Berrange        return [
2836278ae03SDaniel P. Berrange            "--object",
2846278ae03SDaniel P. Berrange            ("secret,id=sec0,data=%s,format=base64" %
2856278ae03SDaniel P. Berrange             config.first_password_base64()),
2866278ae03SDaniel P. Berrange            "--image-opts",
2876278ae03SDaniel P. Berrange            ("driver=luks,key-secret=sec0,file.filename=%s" %
2886278ae03SDaniel P. Berrange             config.image_path())]
2896278ae03SDaniel P. Berrange
2906278ae03SDaniel P. Berrangedef qemu_io_write_pattern(config, pattern, offset_mb, size_mb, dev=False):
2916278ae03SDaniel P. Berrange    """Write a pattern of data to a LUKS image or device"""
2926278ae03SDaniel P. Berrange
293ae50b71dSDaniel P. Berrange    if dev:
294ae50b71dSDaniel P. Berrange        chown(config)
2956278ae03SDaniel P. Berrange    args = ["-c", "write -P 0x%x %dM %dM" % (pattern, offset_mb, size_mb)]
2966278ae03SDaniel P. Berrange    args.extend(qemu_io_image_args(config, dev))
2976278ae03SDaniel P. Berrange    iotests.log("qemu-io " + " ".join(args), filters=[iotests.filter_test_dir])
2986dede6a4SJohn Snow    output = iotests.qemu_io(*args, check=False).stdout
2996dede6a4SJohn Snow    iotests.log(check_cipher_support(config, output),
3004dd218fdSHanna Reitz                filters=[iotests.filter_test_dir, iotests.filter_qemu_io])
3016278ae03SDaniel P. Berrange
3026278ae03SDaniel P. Berrange
3036278ae03SDaniel P. Berrangedef qemu_io_read_pattern(config, pattern, offset_mb, size_mb, dev=False):
3046278ae03SDaniel P. Berrange    """Read a pattern of data to a LUKS image or device"""
3056278ae03SDaniel P. Berrange
306ae50b71dSDaniel P. Berrange    if dev:
307ae50b71dSDaniel P. Berrange        chown(config)
3086278ae03SDaniel P. Berrange    args = ["-c", "read -P 0x%x %dM %dM" % (pattern, offset_mb, size_mb)]
3096278ae03SDaniel P. Berrange    args.extend(qemu_io_image_args(config, dev))
3106278ae03SDaniel P. Berrange    iotests.log("qemu-io " + " ".join(args), filters=[iotests.filter_test_dir])
3116dede6a4SJohn Snow    output = iotests.qemu_io(*args, check=False).stdout
3126dede6a4SJohn Snow    iotests.log(check_cipher_support(config, output),
3134dd218fdSHanna Reitz                filters=[iotests.filter_test_dir, iotests.filter_qemu_io])
3146278ae03SDaniel P. Berrange
3156278ae03SDaniel P. Berrange
3166278ae03SDaniel P. Berrangedef test_once(config, qemu_img=False):
3176278ae03SDaniel P. Berrange    """Run the test with a desired LUKS configuration. Can either
3186278ae03SDaniel P. Berrange       use qemu-img for creating the initial volume, or cryptsetup,
3196278ae03SDaniel P. Berrange       in order to test interoperability in both directions"""
3206278ae03SDaniel P. Berrange
3216278ae03SDaniel P. Berrange    iotests.log("# ================= %s %s =================" % (
3226278ae03SDaniel P. Berrange        "qemu-img" if qemu_img else "dm-crypt", config))
3236278ae03SDaniel P. Berrange
3246278ae03SDaniel P. Berrange    oneKB = 1024
3256278ae03SDaniel P. Berrange    oneMB = oneKB * 1024
3266278ae03SDaniel P. Berrange    oneGB = oneMB * 1024
3276278ae03SDaniel P. Berrange    oneTB = oneGB * 1024
3286278ae03SDaniel P. Berrange
3296278ae03SDaniel P. Berrange    # 4 TB, so that we pass the 32-bit sector number boundary.
3306278ae03SDaniel P. Berrange    # Important for testing correctness of some IV generators
3316278ae03SDaniel P. Berrange    # The files are sparse, so not actually using this much space
3326278ae03SDaniel P. Berrange    image_size = 4 * oneTB
3336278ae03SDaniel P. Berrange    if qemu_img:
3346278ae03SDaniel P. Berrange        iotests.log("# Create image")
3359a3a9a63SMax Reitz        qemu_img_create(config, image_size // oneMB)
3366278ae03SDaniel P. Berrange    else:
3376278ae03SDaniel P. Berrange        iotests.log("# Create image")
3389a3a9a63SMax Reitz        create_image(config, image_size // oneMB)
3396278ae03SDaniel P. Berrange
3406278ae03SDaniel P. Berrange    lowOffsetMB = 100
3419a3a9a63SMax Reitz    highOffsetMB = 3 * oneTB // oneMB
3426278ae03SDaniel P. Berrange
3436278ae03SDaniel P. Berrange    try:
3446278ae03SDaniel P. Berrange        if not qemu_img:
3456278ae03SDaniel P. Berrange            iotests.log("# Format image")
3466278ae03SDaniel P. Berrange            cryptsetup_format(config)
3476278ae03SDaniel P. Berrange
3486278ae03SDaniel P. Berrange            for slot in config.active_slots()[1:]:
3496278ae03SDaniel P. Berrange                iotests.log("# Add password slot %s" % slot)
3506278ae03SDaniel P. Berrange                cryptsetup_add_password(config, slot)
3516278ae03SDaniel P. Berrange
3526278ae03SDaniel P. Berrange        # First we'll open the image using cryptsetup and write a
3536278ae03SDaniel P. Berrange        # known pattern of data that we'll then verify with QEMU
3546278ae03SDaniel P. Berrange
3556278ae03SDaniel P. Berrange        iotests.log("# Open dev")
3566278ae03SDaniel P. Berrange        cryptsetup_open(config)
3576278ae03SDaniel P. Berrange
3586278ae03SDaniel P. Berrange        try:
3596278ae03SDaniel P. Berrange            iotests.log("# Write test pattern 0xa7")
3606278ae03SDaniel P. Berrange            qemu_io_write_pattern(config, 0xa7, lowOffsetMB, 10, dev=True)
3616278ae03SDaniel P. Berrange            iotests.log("# Write test pattern 0x13")
3626278ae03SDaniel P. Berrange            qemu_io_write_pattern(config, 0x13, highOffsetMB, 10, dev=True)
3636278ae03SDaniel P. Berrange        finally:
3646278ae03SDaniel P. Berrange            iotests.log("# Close dev")
3656278ae03SDaniel P. Berrange            cryptsetup_close(config)
3666278ae03SDaniel P. Berrange
3676278ae03SDaniel P. Berrange        # Ok, now we're using QEMU to verify the pattern just
3686278ae03SDaniel P. Berrange        # written via dm-crypt
3696278ae03SDaniel P. Berrange
3706278ae03SDaniel P. Berrange        iotests.log("# Read test pattern 0xa7")
3716278ae03SDaniel P. Berrange        qemu_io_read_pattern(config, 0xa7, lowOffsetMB, 10, dev=False)
3726278ae03SDaniel P. Berrange        iotests.log("# Read test pattern 0x13")
3736278ae03SDaniel P. Berrange        qemu_io_read_pattern(config, 0x13, highOffsetMB, 10, dev=False)
3746278ae03SDaniel P. Berrange
3756278ae03SDaniel P. Berrange
3766278ae03SDaniel P. Berrange        # Write a new pattern to the image, which we'll later
3776278ae03SDaniel P. Berrange        # verify with dm-crypt
3786278ae03SDaniel P. Berrange        iotests.log("# Write test pattern 0x91")
3796278ae03SDaniel P. Berrange        qemu_io_write_pattern(config, 0x91, lowOffsetMB, 10, dev=False)
3806278ae03SDaniel P. Berrange        iotests.log("# Write test pattern 0x5e")
3816278ae03SDaniel P. Berrange        qemu_io_write_pattern(config, 0x5e, highOffsetMB, 10, dev=False)
3826278ae03SDaniel P. Berrange
3836278ae03SDaniel P. Berrange
3846278ae03SDaniel P. Berrange        # Now we're opening the image with dm-crypt once more
3856278ae03SDaniel P. Berrange        # and verifying what QEMU wrote, completing the circle
3866278ae03SDaniel P. Berrange        iotests.log("# Open dev")
3876278ae03SDaniel P. Berrange        cryptsetup_open(config)
3886278ae03SDaniel P. Berrange
3896278ae03SDaniel P. Berrange        try:
3906278ae03SDaniel P. Berrange            iotests.log("# Read test pattern 0x91")
3916278ae03SDaniel P. Berrange            qemu_io_read_pattern(config, 0x91, lowOffsetMB, 10, dev=True)
3926278ae03SDaniel P. Berrange            iotests.log("# Read test pattern 0x5e")
3936278ae03SDaniel P. Berrange            qemu_io_read_pattern(config, 0x5e, highOffsetMB, 10, dev=True)
3946278ae03SDaniel P. Berrange        finally:
3956278ae03SDaniel P. Berrange            iotests.log("# Close dev")
3966278ae03SDaniel P. Berrange            cryptsetup_close(config)
3976278ae03SDaniel P. Berrange    finally:
3986278ae03SDaniel P. Berrange        iotests.log("# Delete image")
3996278ae03SDaniel P. Berrange        delete_image(config)
400f03868bdSEduardo Habkost        print()
4016278ae03SDaniel P. Berrange
4026278ae03SDaniel P. Berrange
4036278ae03SDaniel P. Berrange# Obviously we only work with the luks image format
4047d814059SJohn Snowiotests.script_initialize(supported_fmts=['luks'])
4056278ae03SDaniel P. Berrange
4066278ae03SDaniel P. Berrange# We need sudo in order to run cryptsetup to create
4076278ae03SDaniel P. Berrange# dm-crypt devices. This is safe to use on any
4086278ae03SDaniel P. Berrange# machine, since all dm-crypt devices are backed
4096278ae03SDaniel P. Berrange# by newly created plain files, and have a dm-crypt
4106278ae03SDaniel P. Berrange# name prefix of 'qiotest' to avoid clashing with
4116278ae03SDaniel P. Berrange# user LUKS volumes
4126278ae03SDaniel P. Berrangeverify_passwordless_sudo()
4136278ae03SDaniel P. Berrange
4146278ae03SDaniel P. Berrange
4156278ae03SDaniel P. Berrange# If we look at all permutations of cipher, key size,
4166278ae03SDaniel P. Berrange# mode, ivgen, hash, there are ~1000 possible configs.
4176278ae03SDaniel P. Berrange#
4186278ae03SDaniel P. Berrange# We certainly don't want/need to test every permutation
4196278ae03SDaniel P. Berrange# to get good validation of interoperability between QEMU
4206278ae03SDaniel P. Berrange# and dm-crypt/cryptsetup.
4216278ae03SDaniel P. Berrange#
4226278ae03SDaniel P. Berrange# The configs below are a representative set that aim to
4236278ae03SDaniel P. Berrange# exercise each axis of configurability.
4246278ae03SDaniel P. Berrange#
4256278ae03SDaniel P. Berrangeconfigs = [
4266278ae03SDaniel P. Berrange    # A common LUKS default
4276278ae03SDaniel P. Berrange    LUKSConfig("aes-256-xts-plain64-sha1",
4286278ae03SDaniel P. Berrange               "aes", 256, "xts", "plain64", None, "sha1"),
4296278ae03SDaniel P. Berrange
4306278ae03SDaniel P. Berrange
4316278ae03SDaniel P. Berrange    # LUKS default but diff ciphers
4326278ae03SDaniel P. Berrange    LUKSConfig("twofish-256-xts-plain64-sha1",
4336278ae03SDaniel P. Berrange               "twofish", 256, "xts", "plain64", None, "sha1"),
4346278ae03SDaniel P. Berrange    LUKSConfig("serpent-256-xts-plain64-sha1",
4356278ae03SDaniel P. Berrange               "serpent", 256, "xts", "plain64", None, "sha1"),
4366278ae03SDaniel P. Berrange    # Should really be xts, but kernel doesn't support xts+cast5
4376278ae03SDaniel P. Berrange    # nor does it do essiv+cast5
4386278ae03SDaniel P. Berrange    LUKSConfig("cast5-128-cbc-plain64-sha1",
4396278ae03SDaniel P. Berrange               "cast5", 128, "cbc", "plain64", None, "sha1"),
4406278ae03SDaniel P. Berrange    LUKSConfig("cast6-256-xts-plain64-sha1",
4416278ae03SDaniel P. Berrange               "cast6", 256, "xts", "plain64", None, "sha1"),
4426278ae03SDaniel P. Berrange
4436278ae03SDaniel P. Berrange
4446278ae03SDaniel P. Berrange    # LUKS default but diff modes / ivgens
4456278ae03SDaniel P. Berrange    LUKSConfig("aes-256-cbc-plain-sha1",
4466278ae03SDaniel P. Berrange               "aes", 256, "cbc", "plain", None, "sha1"),
4476278ae03SDaniel P. Berrange    LUKSConfig("aes-256-cbc-plain64-sha1",
4486278ae03SDaniel P. Berrange               "aes", 256, "cbc", "plain64", None, "sha1"),
4496278ae03SDaniel P. Berrange    LUKSConfig("aes-256-cbc-essiv-sha256-sha1",
4506278ae03SDaniel P. Berrange               "aes", 256, "cbc", "essiv", "sha256", "sha1"),
4516278ae03SDaniel P. Berrange    LUKSConfig("aes-256-xts-essiv-sha256-sha1",
4526278ae03SDaniel P. Berrange               "aes", 256, "xts", "essiv", "sha256", "sha1"),
4536278ae03SDaniel P. Berrange
4546278ae03SDaniel P. Berrange
4556278ae03SDaniel P. Berrange    # LUKS default but smaller key sizes
4566278ae03SDaniel P. Berrange    LUKSConfig("aes-128-xts-plain64-sha256-sha1",
4576278ae03SDaniel P. Berrange               "aes", 128, "xts", "plain64", None, "sha1"),
4586278ae03SDaniel P. Berrange    LUKSConfig("aes-192-xts-plain64-sha256-sha1",
4596278ae03SDaniel P. Berrange               "aes", 192, "xts", "plain64", None, "sha1"),
4606278ae03SDaniel P. Berrange
4616278ae03SDaniel P. Berrange    LUKSConfig("twofish-128-xts-plain64-sha1",
4626278ae03SDaniel P. Berrange               "twofish", 128, "xts", "plain64", None, "sha1"),
4636278ae03SDaniel P. Berrange    LUKSConfig("twofish-192-xts-plain64-sha1",
4646278ae03SDaniel P. Berrange               "twofish", 192, "xts", "plain64", None, "sha1"),
4656278ae03SDaniel P. Berrange
4666278ae03SDaniel P. Berrange    LUKSConfig("serpent-128-xts-plain64-sha1",
4676278ae03SDaniel P. Berrange               "serpent", 128, "xts", "plain64", None, "sha1"),
4686278ae03SDaniel P. Berrange    LUKSConfig("serpent-192-xts-plain64-sha1",
4696278ae03SDaniel P. Berrange               "serpent", 192, "xts", "plain64", None, "sha1"),
4706278ae03SDaniel P. Berrange
4716278ae03SDaniel P. Berrange    LUKSConfig("cast6-128-xts-plain64-sha1",
4726278ae03SDaniel P. Berrange               "cast6", 128, "xts", "plain", None, "sha1"),
4736278ae03SDaniel P. Berrange    LUKSConfig("cast6-192-xts-plain64-sha1",
4746278ae03SDaniel P. Berrange               "cast6", 192, "xts", "plain64", None, "sha1"),
4756278ae03SDaniel P. Berrange
4766278ae03SDaniel P. Berrange
4776278ae03SDaniel P. Berrange    # LUKS default but diff hash
478a488e71eSDaniel P. Berrange    LUKSConfig("aes-256-xts-plain64-sha224",
479a488e71eSDaniel P. Berrange               "aes", 256, "xts", "plain64", None, "sha224"),
4806278ae03SDaniel P. Berrange    LUKSConfig("aes-256-xts-plain64-sha256",
4816278ae03SDaniel P. Berrange               "aes", 256, "xts", "plain64", None, "sha256"),
482a488e71eSDaniel P. Berrange    LUKSConfig("aes-256-xts-plain64-sha384",
483a488e71eSDaniel P. Berrange               "aes", 256, "xts", "plain64", None, "sha384"),
4846278ae03SDaniel P. Berrange    LUKSConfig("aes-256-xts-plain64-sha512",
4856278ae03SDaniel P. Berrange               "aes", 256, "xts", "plain64", None, "sha512"),
4866278ae03SDaniel P. Berrange    LUKSConfig("aes-256-xts-plain64-ripemd160",
4876278ae03SDaniel P. Berrange               "aes", 256, "xts", "plain64", None, "ripemd160"),
4886278ae03SDaniel P. Berrange
4896278ae03SDaniel P. Berrange    # Password in slot 3
4906278ae03SDaniel P. Berrange    LUKSConfig("aes-256-xts-plain-sha1-pwslot3",
4916278ae03SDaniel P. Berrange               "aes", 256, "xts", "plain", None, "sha1",
4926278ae03SDaniel P. Berrange               passwords={
4936278ae03SDaniel P. Berrange                   "3": "slot3",
4946278ae03SDaniel P. Berrange               }),
4956278ae03SDaniel P. Berrange
4966278ae03SDaniel P. Berrange    # Passwords in every slot
4976278ae03SDaniel P. Berrange    LUKSConfig("aes-256-xts-plain-sha1-pwallslots",
4986278ae03SDaniel P. Berrange               "aes", 256, "xts", "plain", None, "sha1",
4996278ae03SDaniel P. Berrange               passwords={
5006278ae03SDaniel P. Berrange                   "0": "slot1",
5016278ae03SDaniel P. Berrange                   "1": "slot1",
5026278ae03SDaniel P. Berrange                   "2": "slot2",
5036278ae03SDaniel P. Berrange                   "3": "slot3",
5046278ae03SDaniel P. Berrange                   "4": "slot4",
5056278ae03SDaniel P. Berrange                   "5": "slot5",
5066278ae03SDaniel P. Berrange                   "6": "slot6",
5076278ae03SDaniel P. Berrange                   "7": "slot7",
5086278ae03SDaniel P. Berrange               }),
5098b7cdba3SDaniel P. Berrange
5108b7cdba3SDaniel P. Berrange    # Check handling of default hash alg (sha256) with essiv
5118b7cdba3SDaniel P. Berrange    LUKSConfig("aes-256-cbc-essiv-auto-sha1",
5128b7cdba3SDaniel P. Berrange               "aes", 256, "cbc", "essiv", None, "sha1"),
5138b7cdba3SDaniel P. Berrange
5148b7cdba3SDaniel P. Berrange    # Check that a useless hash provided for 'plain64' iv gen
5158b7cdba3SDaniel P. Berrange    # is ignored and no error raised
5168b7cdba3SDaniel P. Berrange    LUKSConfig("aes-256-cbc-plain64-sha256-sha1",
5178b7cdba3SDaniel P. Berrange               "aes", 256, "cbc", "plain64", "sha256", "sha1"),
5188b7cdba3SDaniel P. Berrange
5196278ae03SDaniel P. Berrange]
5206278ae03SDaniel P. Berrange
521*5689d4bdSThomas Huthunsupported_configs = [
5226278ae03SDaniel P. Berrange    # We don't have a cast-6 cipher impl for QEMU yet
5236278ae03SDaniel P. Berrange    "cast6-256-xts-plain64-sha1",
5246278ae03SDaniel P. Berrange    "cast6-128-xts-plain64-sha1",
5256278ae03SDaniel P. Berrange    "cast6-192-xts-plain64-sha1",
5266278ae03SDaniel P. Berrange
5276278ae03SDaniel P. Berrange    # GCrypt doesn't support Twofish with 192 bit key
5286278ae03SDaniel P. Berrange    "twofish-192-xts-plain64-sha1",
5296278ae03SDaniel P. Berrange]
5306278ae03SDaniel P. Berrange
531*5689d4bdSThomas Huth# Optionally test only the configurations in the LUKS_CONFIG
532*5689d4bdSThomas Huth# environment variable
533*5689d4bdSThomas Huthtested_configs = None
5346278ae03SDaniel P. Berrangeif "LUKS_CONFIG" in os.environ:
535*5689d4bdSThomas Huth    tested_configs = os.environ["LUKS_CONFIG"].split(",")
5366278ae03SDaniel P. Berrange
5376278ae03SDaniel P. Berrangefor config in configs:
538*5689d4bdSThomas Huth    if config.name in unsupported_configs:
539*5689d4bdSThomas Huth        iotests.log("Skipping %s (config not supported)" % config.name)
5406278ae03SDaniel P. Berrange        continue
5416278ae03SDaniel P. Berrange
542*5689d4bdSThomas Huth    if tested_configs is not None and config.name not in tested_configs:
543*5689d4bdSThomas Huth        iotests.log("Skipping %s (by user request)" % config.name)
5446278ae03SDaniel P. Berrange        continue
5456278ae03SDaniel P. Berrange
5466278ae03SDaniel P. Berrange    test_once(config, qemu_img=False)
5476278ae03SDaniel P. Berrange
5486278ae03SDaniel P. Berrange    # XXX we should support setting passwords in a non-0
5496278ae03SDaniel P. Berrange    # key slot with 'qemu-img create' in future
5506278ae03SDaniel P. Berrange    (pw, slot) = config.first_password()
5516278ae03SDaniel P. Berrange    if slot == "0":
5526278ae03SDaniel P. Berrange        test_once(config, qemu_img=True)
553