summaryrefslogtreecommitdiffstats
path: root/commands/storage/lmi/scripts/storage
diff options
context:
space:
mode:
authorJan Safranek <jsafrane@redhat.com>2014-02-18 13:48:26 +0100
committerJan Safranek <jsafrane@redhat.com>2014-02-18 13:48:26 +0100
commit8f3a6a5c0985a4367b9730b6dc9ad68f88db9fb8 (patch)
treee4a762034d42e6b58e22876ef2650b355d519e38 /commands/storage/lmi/scripts/storage
parentbb2ad503bb978b53cbf8e423750977d40ff4eaf4 (diff)
downloadopenlmi-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.py192
-rw-r--r--commands/storage/lmi/scripts/storage/luks.py213
-rw-r--r--commands/storage/lmi/scripts/storage/storage_cmd.py5
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,