diff options
author | Jan Safranek <jsafrane@redhat.com> | 2014-02-18 13:48:26 +0100 |
---|---|---|
committer | Jan Safranek <jsafrane@redhat.com> | 2014-02-18 13:48:26 +0100 |
commit | 8f3a6a5c0985a4367b9730b6dc9ad68f88db9fb8 (patch) | |
tree | e4a762034d42e6b58e22876ef2650b355d519e38 /commands/storage/lmi/scripts/storage | |
parent | bb2ad503bb978b53cbf8e423750977d40ff4eaf4 (diff) | |
download | openlmi-scripts-8f3a6a5c0985a4367b9730b6dc9ad68f88db9fb8.tar.gz openlmi-scripts-8f3a6a5c0985a4367b9730b6dc9ad68f88db9fb8.tar.xz openlmi-scripts-8f3a6a5c0985a4367b9730b6dc9ad68f88db9fb8.zip |
Add LUKS scripts.
Diffstat (limited to 'commands/storage/lmi/scripts/storage')
-rw-r--r-- | commands/storage/lmi/scripts/storage/cmd/luks.py | 192 | ||||
-rw-r--r-- | commands/storage/lmi/scripts/storage/luks.py | 213 | ||||
-rw-r--r-- | commands/storage/lmi/scripts/storage/storage_cmd.py | 5 |
3 files changed, 410 insertions, 0 deletions
diff --git a/commands/storage/lmi/scripts/storage/cmd/luks.py b/commands/storage/lmi/scripts/storage/cmd/luks.py new file mode 100644 index 0000000..3d1307a --- /dev/null +++ b/commands/storage/lmi/scripts/storage/cmd/luks.py @@ -0,0 +1,192 @@ +# coding=utf-8 +# Storage Management Providers +# +# Copyright (C) 2014 Red Hat, Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are +# those of the authors and should not be interpreted as representing official +# policies, either expressed or implied, of the FreeBSD Project. +# +# Authors: Jan Safranek <jsafrane@redhat.com> +# +""" +LUKS management + +Usage: + %(cmd)s list + %(cmd)s create [-p <passphrase>] <device> + %(cmd)s open [-p <passphrase>] <device> <name> + %(cmd)s close <device> + %(cmd)s addpass [-p <passphrase>] [-n <new-passphrase>] <device> + %(cmd)s deletepass [-p <passphrase>] <device> + +Commands: + list List available LUKS formats and their clear-text devices + (if any). + + create Format given device with LUKS format. Any data on the device + will be destroyed. + + open Open given device formatted with LUKS and expose its clear-text + data as a new block device. + + close Close given device formatted with LUKS and destroy its + clear-text block device. + + addpass Add new passphrase to given LUKS-formatted device. Each device + can have up to 8 separate passwords and any of them can be used + to decrypt the device. + + deletepass Remove given passphrase from LUKS-formatted device. + +Common options: + -p, --passphrase=passphrase Passphrase. It will be read from the + terminal, if it is not provided on command + line. + + -n, --new-passphrase=passphrase New passphrase. It will be read from the + terminal, if it is not provided on command + line. + +Open options: + <device> Device with LUKS format on it. + + <name> Name of the clear-text block device to create. + +Close options: + <device> Device with LUKS format on it, previously opened by + '%(cmd)s open'. +""" + +from lmi.scripts.common import command +from lmi.scripts.common import get_logger +from lmi.scripts.storage import luks +import getpass + +LOG = get_logger(__name__) + +def read_password(prompt1='Passphrase:', prompt2='Retype the passphrase:'): + """ + Read password from tty without echo. Read it twice and check that they + match. + """ + p1 = getpass.getpass(prompt=prompt1) + p2 = getpass.getpass(prompt=prompt2) + + while p1 != p2: + print "Passphrases do not match" + p1 = getpass.getpass(prompt=prompt1) + p2 = getpass.getpass(prompt=prompt2) + + return p1 + + +class LUKSList(command.LmiLister): + COLUMNS = ("Device name", "Clear-text device name") + + def execute(self, ns): + """ + Implementation of 'luks list' command. + """ + for l in luks.get_luks_list(ns): + clear = luks.get_luks_device(ns, l) + if clear: + clear_name = clear.Name + else: + clear_name = "" + yield (l.ElementName, clear_name) + + +class LUKSCreate(command.LmiCheckResult): + EXPECT = None + OPT_NO_UNDERSCORES = True + + def execute(self, ns, device, passphrase=None): + """ + Implementation of 'luks create' command. + """ + if not passphrase: + passphrase = read_password() + luks.create_luks(ns, device, passphrase) + +class LUKSOpen(command.LmiCheckResult): + EXPECT = None + OPT_NO_UNDERSCORES = True + + def execute(self, ns, device, name, passphrase=None): + """ + Implementation of 'luks open' command. + """ + if not passphrase: + passphrase = getpass.getpass('Passphrase:') + luks.open_luks(ns, device, name, passphrase) + +class LUKSClose(command.LmiCheckResult): + EXPECT = None + OPT_NO_UNDERSCORES = True + + def execute(self, ns, device): + """ + Implementation of 'luks close' command. + """ + luks.close_luks(ns, device) + +class LUKSAddPass(command.LmiCheckResult): + EXPECT = None + OPT_NO_UNDERSCORES = True + + def execute(self, ns, device, passphrase=None, new_passphrase=None): + """ + Implementation of 'luks addpass' command. + """ + if not passphrase: + passphrase = getpass.getpass('Passphrase:') + if not new_passphrase: + new_passphrase = read_password('New passphrase:', + 'Retype the new passphrase:') + luks.add_luks_passphrase(ns, device, passphrase, new_passphrase) + +class LUKSDeletePass(command.LmiCheckResult): + EXPECT = None + OPT_NO_UNDERSCORES = True + + def execute(self, ns, device, passphrase=None): + """ + Implementation of 'luks deletepass' command. + """ + if not passphrase: + passphrase = getpass.getpass('Passphrase:') + luks.delete_luks_passphrase(ns, device, passphrase) + + +class LUKS(command.LmiCommandMultiplexer): + OWN_USAGE = __doc__ + COMMANDS = { + 'list' : LUKSList, + 'create' : LUKSCreate, + 'open' : LUKSOpen, + 'close' : LUKSClose, + 'addpass' : LUKSAddPass, + 'deletepass' : LUKSDeletePass, + } diff --git a/commands/storage/lmi/scripts/storage/luks.py b/commands/storage/lmi/scripts/storage/luks.py new file mode 100644 index 0000000..79b0fe0 --- /dev/null +++ b/commands/storage/lmi/scripts/storage/luks.py @@ -0,0 +1,213 @@ +# Storage Management Providers +# +# Copyright (C) 2014 Red Hat, Inc. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# +# The views and conclusions contained in the software and documentation are +# those of the authors and should not be interpreted as representing official +# policies, either expressed or implied, of the FreeBSD Project. +# +# Authors: Jan Safranek <jsafrane@redhat.com> +# + +""" +LUKS management functions. +""" + +from lmi.scripts.common import get_logger +from lmi.scripts.common.errors import LmiFailed +from lmi.scripts.storage import common, fs + + +LOG = get_logger(__name__) + +def get_luks_list(ns): + """ + Retrieve list of all encrypted devices. + + :rtype: list of LMIInstance/LMI_EncryptionFormat. + """ + # No vgs supplied, list all LVs + for fmt in ns.LMI_EncryptionFormat.instances(): + yield fmt + +def create_luks(ns, device, passphrase): + """ + Format given device with LUKS encryption format. All data on the device + will be deleted! Encryption key and algorithm will be chosen automatically. + + :type device: LMIInstance/CIM_StorageExtent or string + :param device: Device to format with LUKS data + :type passphrase: string + :param passphrase: Password to open the encrypted data. This is not the + encryption key. + :rtype: LMIInstance/LMI_EncryptionFormat + """ + device = common.str2device(ns, device) + service = ns.LMI_ExtentEncryptionConfigurationService.first_instance() + (ret, outparams, err) = service.SyncCreateEncryptionFormat( + InExtent=device, + Passphrase=passphrase) + if ret != 0: + if err: + raise LmiFailed("Cannot create LUKS format: %s." % err) + values = service.CreateEncryptionFormat.CreateEncryptionFormatValues + raise LmiFailed("Cannot create LUKS format: %s." + % (values.value_name(ret),)) + return outparams['Format'] + + +def open_luks(ns, fmt, name, passphrase): + """ + Open encrypted LUKS format and expose it as a clear-text block device. + + :type fmt: LMIInstance/LMI_EncryptionFormat or string + :param fmt: The LUKS format to open. + :type name: string + :param name: Requested name of the clear-text block device. It will be + available as /dev/mapper/<name>. + :type passphrase: string + :param passphrase: Password to open the encrypted data. + :rtype: LMIInstance/LMI_LUKSStorageExtent + :returns: The block device with clear-text data. + """ + fmt = fs.str2format(ns, fmt) + service = ns.LMI_ExtentEncryptionConfigurationService.first_instance() + (ret, outparams, err) = service.SyncOpenEncryptionFormat( + Format=fmt, + ElementName=name, + Passphrase=passphrase) + if ret != 0: + if err: + raise LmiFailed("Cannot open LUKS format: %s." % err) + values = service.OpenEncryptionFormat.OpenEncryptionFormatValues + raise LmiFailed("Cannot open LUKS format: %s." + % (values.value_name(ret),)) + return outparams['Extent'] + +def close_luks(ns, fmt): + """ + Closes clear-text block device previously opened by open_luks(). + + :type fmt: LMIInstance/LMI_EncryptionFormat or string + :param fmt: The LUKS format to close. + """ + fmt = fs.str2format(ns, fmt) + service = ns.LMI_ExtentEncryptionConfigurationService.first_instance() + (ret, outparams, err) = service.SyncCloseEncryptionFormat(Format=fmt) + if ret != 0: + if err: + raise LmiFailed("Cannot close LUKS format: %s." % err) + values = service.CloseEncryptionFormat.CloseEncryptionFormatValues + raise LmiFailed("Cannot close LUKS format: %s." + % (values.value_name(ret),)) + +def add_luks_passphrase(ns, fmt, passphrase, new_passphrase): + """ + Adds new password to LUKS format. Each format can have up to 8 separate + passwords and any of them can be used to open(decrypt) the format. + + Any existing passphrase must be provided to add a new one. This proves + the caller is authorized to add new passphrase (because it already knows + one) and also this 'old' passphrase is used to retrieve encryption keys. + This 'old' passphrase is not removed nor replaced when adding new + passphrase! + + :type fmt: LMIInstance/LMI_EncryptionFormat or string + :param fmt: The LUKS format to modify. + :type passphrase: string + :param passphrase: Existing LUKS passphrase. + :type new_passphrase: string + :param new_passphrase: New passphrase to add to the format. + """ + fmt = fs.str2format(ns, fmt) + service = ns.LMI_ExtentEncryptionConfigurationService.first_instance() + (ret, outparams, err) = service.AddPassphrase( + Format=fmt, + Passphrase=passphrase, + NewPassphrase=new_passphrase) + if ret != 0: + if err: + raise LmiFailed("Cannot add new passphrase: %s." % err) + values = service.AddPassphrase.AddPassphraseValues + raise LmiFailed("Cannot add new passphrase: %s." + % (values.value_name(ret),)) + +def delete_luks_passphrase(ns, fmt, passphrase): + """ + Delete passphrase from LUKS format. + + :type fmt: LMIInstance/LMI_EncryptionFormat or string + :param fmt: The LUKS format to modify. + :type passphrase: string + :param passphrase: The passphrase to remove + """ + fmt = fs.str2format(ns, fmt) + service = ns.LMI_ExtentEncryptionConfigurationService.first_instance() + (ret, outparams, err) = service.DeletePassphrase( + Format=fmt, + Passphrase=passphrase) + if ret != 0: + if err: + raise LmiFailed("Cannot delete passphrase: %s." % err) + values = service.DeletePassphrase.DeletePassphraseValues + raise LmiFailed("Cannot delete passphrase: %s." + % (values.value_name(ret),)) + +def get_luks_device(ns, fmt): + """ + Return clear-text device for given LUKS format. The format must be already + opened by open_luks(). + + :type fmt: LMIInstance/LMI_EncryptionFormat or string + :param fmt: The LUKS format to inspect. + :rtype: LMIInstance/LMI_LUKSStorageExtent + :returns: Block device with clear-text data or None, if the LUKS format is + not open. + """ + + fmt = fs.str2format(ns, fmt) + crypttext_device = fmt.first_associator( + AssocClass="LMI_ResidesOnExtent", + Role="Dependent") + device = crypttext_device.first_associator( + AssocClass="LMI_LUKSBasedOn", + Role="Antecedent") + return device + +def get_passphrase_count(ns, fmt): + """ + Each LUKS format can have up to 8 passphrases. Any of these passphrases can + be used to decrypt the format and create clear-text device. + + This function returns number of passphrases in given LUKS format. + + :type fmt: LMIInstance/LMI_EncryptionFormat or string + :param fmt: The LUKS format to inspect. + :rtype: int + :returns: Number of used passphrases. + """ + + fmt = fs.str2format(ns, fmt) + count = reduce(lambda a, b: a + b, fmt.SlotStatus) + return count diff --git a/commands/storage/lmi/scripts/storage/storage_cmd.py b/commands/storage/lmi/scripts/storage/storage_cmd.py index 77851b7..a0f7373 100644 --- a/commands/storage/lmi/scripts/storage/storage_cmd.py +++ b/commands/storage/lmi/scripts/storage/storage_cmd.py @@ -36,6 +36,7 @@ Basic storage device information. Usage: %(cmd)s fs <cmd> [<args> ...] + %(cmd)s luks <cmd> [<args> ...] %(cmd)s lv <cmd> [<args> ...] %(cmd)s mount <cmd> [<args> ...] %(cmd)s partition <cmd> [<args> ...] @@ -51,6 +52,8 @@ Usage: Commands: fs Filesystem and other data format management. + luks LUKS management. + lv Logical Volume management. mount Mount management. @@ -123,6 +126,7 @@ from lmi.scripts.storage.common import (size2str, get_devices, get_children, get_parents, str2device, str2size, str2vg) import lmi.scripts.storage.cmd.fs +import lmi.scripts.storage.cmd.luks import lmi.scripts.storage.cmd.lv import lmi.scripts.storage.cmd.mount import lmi.scripts.storage.cmd.partition @@ -392,6 +396,7 @@ Storage = command.register_subcommands( 'provides': Provides, 'depends' : Depends, 'fs' : lmi.scripts.storage.cmd.fs.FS, + 'luks' : lmi.scripts.storage.cmd.luks.LUKS, 'lv' : lmi.scripts.storage.cmd.lv.LV, 'mount' : lmi.scripts.storage.cmd.mount.Mount, 'partition': lmi.scripts.storage.cmd.partition.Partition, |