summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--backend.py3
-rw-r--r--instdata.py1
-rw-r--r--kickstart.py61
-rw-r--r--livecd.py2
-rw-r--r--storage/__init__.py38
-rw-r--r--storage/devicelibs/crypto.py20
-rw-r--r--storage/formats/luks.py64
-rw-r--r--storage/partitioning.py4
8 files changed, 185 insertions, 8 deletions
diff --git a/backend.py b/backend.py
index 04235a82c..5501ac801 100644
--- a/backend.py
+++ b/backend.py
@@ -31,6 +31,7 @@ from constants import *
import isys
import kickstart
import packages
+import storage
from flags import flags
log = logging.getLogger("anaconda")
@@ -103,6 +104,8 @@ class AnacondaBackend:
for d in glob.glob("/tmp/DD-*"):
shutil.copytree(d, "/root/" + os.path.basename(d))
+ storage.writeEscrowPackets(anaconda)
+
sys.stdout.flush()
if flags.setupFilesystems:
syslog.stop()
diff --git a/instdata.py b/instdata.py
index fdb1a32e4..220b074ab 100644
--- a/instdata.py
+++ b/instdata.py
@@ -78,6 +78,7 @@ class InstallData:
self.upgradeRoot = None
self.rootParts = None
self.upgradeSwapInfo = None
+ self.escrowCertificates = {}
if iutil.isS390() or self.anaconda.isKickstart:
self.firstboot = FIRSTBOOT_SKIP
diff --git a/kickstart.py b/kickstart.py
index 0104d4a18..308866697 100644
--- a/kickstart.py
+++ b/kickstart.py
@@ -33,8 +33,9 @@ from flags import flags
from constants import *
import sys
import string
-import urlgrabber.grabber as grabber
+import urlgrabber
import warnings
+import network
import upgrade
import pykickstart.commands as commands
from storage.devices import *
@@ -131,6 +132,33 @@ class AnacondaKSPackages(Packages):
self.seen = False
+def getEscrowCertificate(anaconda, url):
+ if not url:
+ return None
+
+ if url in anaconda.id.escrowCertificates:
+ return anaconda.id.escrowCertificates[url]
+
+ needs_net = not url.startswith("/") and not url.startswith("file:")
+ if needs_net and not network.hasActiveNetDev():
+ if not anaconda.intf.enableNetwork(anaconda):
+ anaconda.intf.messageWindow(_("No Network Available"),
+ _("Encryption key escrow requires "
+ "networking, but there was an error "
+ "enabling the network on your "
+ "system."), type="custom",
+ custom_icon="error",
+ custom_buttons=[_("_Exit installer")])
+ sys.exit(1)
+
+ log.info("escrow: downloading %s" % (url,))
+ f = urlgrabber.urlopen(url)
+ try:
+ anaconda.id.escrowCertificates[url] = f.read()
+ finally:
+ f.close()
+ return anaconda.id.escrowCertificates[url]
+
###
### SUBCLASSES OF PYKICKSTART COMMAND HANDLERS
###
@@ -153,6 +181,10 @@ class AutoPart(commands.autopart.F12_AutoPart):
if self.encrypted:
self.handler.id.storage.encryptedAutoPart = True
self.handler.id.storage.encryptionPassphrase = self.passphrase
+ self.handler.id.storage.autoPartEscrowCert = \
+ getEscrowCertificate(self.handler.anaconda, self.escrowcert)
+ self.handler.id.storage.autoPartAddBackupPassphrase = \
+ self.backuppassphrase
self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"])
return retval
@@ -459,15 +491,20 @@ class LogVol(commands.logvol.F12_LogVol):
if lvd.passphrase and not storage.encryptionPassphrase:
storage.encryptionPassphrase = lvd.passphrase
+ cert = getEscrowCertificate(self.handler.anaconda, lvd.escrowcert)
if lvd.preexist:
luksformat = format
- device.format = getFormat("luks", passphrase=lvd.passphrase, device=device.path)
+ device.format = getFormat("luks", passphrase=lvd.passphrase, device=device.path,
+ escrow_cert=cert,
+ add_backup_passphrase=lvd.backuppassphrase)
luksdev = LUKSDevice("luks%d" % storage.nextID,
format=luksformat,
parents=device)
else:
luksformat = request.format
- request.format = getFormat("luks", passphrase=lvd.passphrase)
+ request.format = getFormat("luks", passphrase=lvd.passphrase,
+ escrow_cert=cert,
+ add_backup_passphrase=lvd.backuppassphrase)
luksdev = LUKSDevice("luks%d" % storage.nextID,
format=luksformat,
parents=request)
@@ -701,15 +738,20 @@ class Partition(commands.partition.F12_Partition):
if pd.passphrase and not storage.encryptionPassphrase:
storage.encryptionPassphrase = pd.passphrase
+ cert = getEscrowCertificate(self.handler.anaconda, pd.escrowcert)
if pd.preexist:
luksformat = format
- device.format = getFormat("luks", passphrase=pd.passphrase, device=device.path)
+ device.format = getFormat("luks", passphrase=pd.passphrase, device=device.path,
+ escrow_cert=cert,
+ add_backup_passphrase=pd.backuppassphrase)
luksdev = LUKSDevice("luks%d" % storage.nextID,
format=luksformat,
parents=device)
else:
luksformat = request.format
- request.format = getFormat("luks", passphrase=pd.passphrase)
+ request.format = getFormat("luks", passphrase=pd.passphrase,
+ escrow_cert=cert,
+ add_backup_passphrase=pd.backuppassphrase)
luksdev = LUKSDevice("luks%d" % storage.nextID,
format=luksformat,
parents=request)
@@ -837,15 +879,20 @@ class Raid(commands.raid.F12_Raid):
if rd.passphrase and not storage.encryptionPassphrase:
storage.encryptionPassphrase = rd.passphrase
+ cert = getEscrowCertificate(self.handler.anaconda, rd.escrowcert)
if rd.preexist:
luksformat = format
- device.format = getFormat("luks", passphrase=rd.passphrase, device=device.path)
+ device.format = getFormat("luks", passphrase=rd.passphrase, device=device.path,
+ escrow_cert=cert,
+ add_backup_passphrase=rd.backuppassphrase)
luksdev = LUKSDevice("luks%d" % storage.nextID,
format=luksformat,
parents=device)
else:
luksformat = request.format
- request.format = getFormat("luks", passphrase=rd.passphrase)
+ request.format = getFormat("luks", passphrase=rd.passphrase,
+ escrow_cert=cert,
+ add_backup_passphrase=rd.backuppassphrase)
luksdev = LUKSDevice("luks%d" % storage.nextID,
format=luksformat,
parents=request)
diff --git a/livecd.py b/livecd.py
index 73c075cb4..685e172ed 100644
--- a/livecd.py
+++ b/livecd.py
@@ -353,6 +353,8 @@ class LiveCDCopyBackend(backend.AnacondaBackend):
# setup /etc/rpm/ for the post-install environment
iutil.writeRpmPlatform(anaconda.rootPath)
+ storage.writeEscrowPackets(anaconda)
+
# maybe heavy handed, but it'll do
if os.path.exists(anaconda.rootPath + "/usr/bin/rhgb") or os.path.exists(anaconda.rootPath + "/usr/bin/plymouth"):
anaconda.id.bootloader.args.append("rhgb quiet")
diff --git a/storage/__init__.py b/storage/__init__.py
index 200fd6642..57901c792 100644
--- a/storage/__init__.py
+++ b/storage/__init__.py
@@ -26,6 +26,7 @@ import stat
import errno
import sys
+import nss.nss
import parted
import isys
@@ -44,6 +45,7 @@ from formats import get_device_format_class
from formats import get_default_filesystem_type
from devicelibs.lvm import safeLvmName
from devicelibs.dm import name_from_dm_node
+from devicelibs.crypto import generateBackupPassphrase
from udev import *
import iscsi
import fcoe
@@ -164,6 +166,37 @@ def storageComplete(anaconda):
if rc == 0:
return DISPATCH_BACK
+def writeEscrowPackets(anaconda):
+ log.debug("escrow: writeEscrowPackets start")
+
+ wait_win = anaconda.intf.waitWindow(_("Running..."),
+ _("Storing encryption keys"))
+
+ nss.nss.nss_init_nodb() # Does nothing if NSS is already initialized
+
+ backupPassphrase = generateBackupPassphrase()
+ try:
+ for device in anaconda.id.storage.devices:
+ log.debug("escrow: device %s: %s" %
+ (repr(device.path), repr(device.format.type)))
+ if (device.format.type == "luks" and
+ device.format.escrow_cert is not None):
+ device.format.escrow(anaconda.rootPath + "/root",
+ backupPassphrase)
+
+ wait_win.pop()
+ except (IOError, RuntimeError) as e:
+ wait_win.pop()
+ anaconda.intf.messageWindow(_("Error"),
+ _("Error storing an encryption key: "
+ "%s\n") % str(e), type="custom",
+ custom_icon="error",
+ custom_buttons=[_("_Exit installer")])
+ sys.exit(1)
+
+ log.debug("escrow: writeEscrowPackets done")
+
+
def undoEncryption(storage):
for device in storage.devicetree.getDevicesByType("luks/dm-crypt"):
if device.exists:
@@ -196,6 +229,8 @@ class Storage(object):
self.clearPartDisks = []
self.encryptedAutoPart = False
self.encryptionPassphrase = None
+ self.autoPartEscrowCert = None
+ self.autoPartAddBackupPassphrase = False
self.encryptionRetrofit = False
self.reinitializeDisks = False
self.zeroMbr = None
@@ -567,7 +602,8 @@ class Storage(object):
if kwargs.has_key("fmt_type"):
kwargs["format"] = getFormat(kwargs.pop("fmt_type"),
mountpoint=kwargs.pop("mountpoint",
- None))
+ None),
+ **kwargs.pop("fmt_args", {}))
if kwargs.has_key("disks"):
parents = kwargs.pop("disks")
diff --git a/storage/devicelibs/crypto.py b/storage/devicelibs/crypto.py
index 94c208143..5e7a82440 100644
--- a/storage/devicelibs/crypto.py
+++ b/storage/devicelibs/crypto.py
@@ -29,6 +29,26 @@ from ..errors import *
import gettext
_ = lambda x: gettext.ldgettext("anaconda", x)
+# Keep the character set size a power of two to make sure all characters are
+# equally likely
+GENERATED_PASSPHRASE_CHARSET = ("0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "./")
+# 20 chars * 6 bits per char = 120 "bits of security"
+GENERATED_PASSPHRASE_LENGTH = 20
+
+def generateBackupPassphrase():
+ rnd = os.urandom(GENERATED_PASSPHRASE_LENGTH)
+ cs = GENERATED_PASSPHRASE_CHARSET
+ raw = "".join([cs[ord(c) % len(cs)] for c in rnd])
+
+ # Make the result easier to read
+ parts = []
+ for i in xrange(0, len(raw), 5):
+ parts.append(raw[i : i + 5])
+ return "-".join(parts)
+
def askyes(question):
return True
diff --git a/storage/formats/luks.py b/storage/formats/luks.py
index c34868849..15b92b6ba 100644
--- a/storage/formats/luks.py
+++ b/storage/formats/luks.py
@@ -24,6 +24,8 @@
import os
+import volume_key
+
from iutil import log_method_call
from ..errors import *
from ..devicelibs import crypto
@@ -60,6 +62,8 @@ class LUKS(DeviceFormat):
cipher -- cipher mode string
key_size -- key size in bits
exists -- indicates whether this is an existing format
+ escrow_cert -- certificate to use for key escrow
+ add_backup_passphrase -- generate a backup passphrase?
"""
log_method_call(self, *args, **kwargs)
DeviceFormat.__init__(self, *args, **kwargs)
@@ -76,6 +80,8 @@ class LUKS(DeviceFormat):
# FIXME: these should both be lists, but managing them will be a pain
self.__passphrase = kwargs.get("passphrase")
self._key_file = kwargs.get("key_file")
+ self.escrow_cert = kwargs.get("escrow_cert")
+ self.add_backup_passphrase = kwargs.get("add_backup_passphrase", False)
if not self.mapName and self.exists and self.uuid:
self.mapName = "luks-%s" % self.uuid
@@ -239,6 +245,64 @@ class LUKS(DeviceFormat):
key_file=self._key_file,
del_passphrase=passphrase)
+ def _escrowVolumeIdent(self, vol):
+ """ Return an escrow packet filename prefix for a volume_key.Volume. """
+ label = vol.label
+ if label is not None:
+ label = label.replace("/", "_")
+ uuid = vol.uuid
+ if uuid is not None:
+ uuid = uuid.replace("/", "_")
+ # uuid is never None on LUKS volumes
+ if label is not None and uuid is not None:
+ volume_ident = "%s-%s" % (label, uuid)
+ elif uuid is not None:
+ volume_ident = uuid
+ elif label is not None:
+ volume_ident = label
+ else:
+ volume_ident = "_unknown"
+ return volume_ident
+
+ def escrow(self, directory, backupPassphrase):
+ log.debug("escrow: escrowVolume start for %s" % self.device)
+ vol = volume_key.Volume.open(self.device)
+ volume_ident = self._escrowVolumeIdent(vol)
+
+ ui = volume_key.UI()
+ # This callback is not expected to be used, let it always fail
+ ui.generic_cb = lambda unused_prompt, unused_echo: None
+ def known_passphrase_cb(unused_prompt, failed_attempts):
+ if failed_attempts == 0:
+ return self.__passphrase
+ return None
+ ui.passphrase_cb = known_passphrase_cb
+
+ log.debug("escrow: getting secret")
+ vol.get_secret(volume_key.SECRET_DEFAULT, ui)
+ log.debug("escrow: creating packet")
+ default_packet = vol.create_packet_assymetric_from_cert_data \
+ (volume_key.SECRET_DEFAULT, self.escrow_cert, ui)
+ log.debug("escrow: packet created")
+ with open("%s/%s-escrow" % (directory, volume_ident), "wb") as f:
+ f.write(default_packet)
+ log.debug("escrow: packet written")
+
+ if self.add_backup_passphrase:
+ log.debug("escrow: adding backup passphrase")
+ vol.add_secret(volume_key.SECRET_PASSPHRASE, backupPassphrase)
+ log.debug("escrow: creating backup packet")
+ backup_passphrase_packet = \
+ vol.create_packet_assymetric_from_cert_data \
+ (volume_key.SECRET_PASSPHRASE, self.escrow_cert, ui)
+ log.debug("escrow: backup packet created")
+ with open("%s/%s-escrow-backup-passphrase" %
+ (directory, volume_ident), "wb") as f:
+ f.write(backup_passphrase_packet)
+ log.debug("escrow: backup packet written")
+
+ log.debug("escrow: escrowVolume done for %s" % repr(self.device))
+
register_device_format(LUKS)
diff --git a/storage/partitioning.py b/storage/partitioning.py
index 80724d67d..36cb861b4 100644
--- a/storage/partitioning.py
+++ b/storage/partitioning.py
@@ -65,9 +65,13 @@ def _createFreeSpacePartitions(anaconda):
for disk in disks:
if anaconda.id.storage.encryptedAutoPart:
fmt_type = "luks"
+ fmt_args = {"escrow_cert": anaconda.id.storage.autoPartEscrowCert,
+ "add_backup_passphrase": anaconda.id.storage.autoPartAddBackupPassphrase}
else:
fmt_type = "lvmpv"
+ fmt_args = {}
part = anaconda.id.storage.newPartition(fmt_type=fmt_type,
+ fmt_args=fmt_args,
size=1,
grow=True,
disks=[disk])