Skip to content

Commit 6f95fbe

Browse files
committed
Merge pull request #5 from OpenMediaVault-Plugin-Developers/testing
Merge recent testing developments into master.
2 parents 4f2aa19 + 9201926 commit 6f95fbe

3 files changed

Lines changed: 235 additions & 107 deletions

File tree

debian/changelog

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,26 @@
1+
openmediavault-luksencryption (2.1.2) stable; urgency=medium
2+
3+
* Bugfix: opening LUKS-on-LVM devices would fail.
4+
5+
-- OpenMediaVault Plugin Developers <plugins@omv-extras.org> Fri, 05 Feb 2016 14:42:59 +0000
6+
7+
openmediavault-luksencryption (2.1.1) unstable; urgency=medium
8+
9+
* Bugfix: no LUKS devices present would cause an error message.
10+
11+
-- OpenMediaVault Plugin Developers <plugins@omv-extras.org> Wed, 13 Jan 2016 10:59:49 +0000
12+
13+
openmediavault-luksencryption (2.1.0) unstable; urgency=high
14+
15+
* Tested on OMV 3.0.
16+
* Activate LVM volume groups after unlocking for LVM-on-LUKS.
17+
* Fix for duplicate entries in the case of, e.g. partitions on RAID devices.
18+
* Refactor some code to avoid duplicated calls that led to typo bug in v2.0.0.
19+
* Tweak description of devices/containers.
20+
* Tweak filename for header backups.
21+
22+
-- OpenMediaVault Plugin Developers <plugins@omv-extras.org> Wed, 06 Jan 2016 14:53:13 +0000
23+
124
openmediavault-luksencryption (2.0.1) unstable; urgency=high
225

326
* Bugfix: typo in createContainer() caused creating containers with passphrases to fail.

usr/share/openmediavault/engined/rpc/luks.inc

Lines changed: 171 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ require_once("openmediavault/functions.inc");
2828
require_once("openmediavault/luks.inc");
2929
require_once("openmediavault/rpcservice.inc");
3030
require_once("openmediavault/notify.inc");
31+
include_once("openmediavault/lvm.inc");
3132

3233
class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
3334

@@ -276,94 +277,155 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
276277
// Check that the container is not already open, then use
277278
// the supplied passphrase or key file to unlock it if not.
278279
if (FALSE === $luks->isOpen()) {
279-
if(isset($params['keyfile']) && !empty($params['keyfile']))
280-
$success = $luks->open($params['keyfile'], TRUE);
281-
else
282-
$success = $luks->open($params['passphrase']);
283-
if ($success === FALSE) {
280+
if(isset($params['keyfile']) && !empty($params['keyfile'])) {
281+
$key = $params['keyfile'];
282+
$keyIsFile = TRUE;
283+
} else {
284+
$key = $params['passphrase'];
285+
$keyIsFile = FALSE;
286+
}
287+
if ($luks->open($key, $keyIsFile) === FALSE) {
284288
throw new OMVException(OMVErrorMsg::E_EXEC_MISC,
285289
sprintf(gettext("Unable to unlock encrypted device: %s"),
286290
$luks->getLastError()));
287291
}
288292
}
293+
/* Downstream operations with the decrypted container */
294+
$sdluks = new OMVStorageDeviceLUKS($luks->getDecryptedDeviceFile());
295+
$df = $sdluks->getDeviceFile();
296+
// If the container contains an LVM physical volume, determine the
297+
// volume group and activate it (otherwise the logical volume and any
298+
// filesystem on it won't be accessible)
299+
if (class_exists("OMVLvmPhysicalVolume")) {
300+
// Use LVM plugin if it's installed to get VG name
301+
$pv = new OMVLvmPhysicalVolume($df);
302+
if (TRUE === $pv->exists()) {
303+
$vgName = $pv->getVGName();
304+
}
305+
} else {
306+
// Fall back to manual method without LVM plugin
307+
$cmd = sprintf("export LANG=C; pvdisplay --noheadings ".
308+
"-C -o vg_name %s ",
309+
escapeshellarg($df));
310+
@OMVUtil::exec($cmd, $output, $result);
311+
if($result === 0) {
312+
$vgName = trim($output[0]);
313+
}
314+
}
315+
// PV/VG was found - activate the VG, set devicefile to LV via
316+
// LVM plugin if available - although LVM/udev seems to
317+
// automatically mount filesystems in fstab from logical
318+
// volumes when activated, so probably not strictly necessary.
319+
if (isset($vgName)) {
320+
$this->debug(sprintf("%s contains an LVM2 PV, part of VG: %s",
321+
$df, $vgName));
322+
$cmd = sprintf("export LANG=C; vgchange -a y %s ",
323+
escapeshellarg($vgName));
324+
@OMVUtil::exec($cmd, $output, $result);
325+
if($result !== 0) {
326+
$this->debug($output);
327+
} else {
328+
if (class_exists("OMVLvmVolumeGroup")) {
329+
$vg = new OMVLvmVolumeGroup($vgName);
330+
$lvNames = $vg->getLVName();
331+
$fsDevs = array_map(function($lv) use ($vgName) {
332+
return "/dev/$vgName-$lv";
333+
}, $lvNames);
334+
}
335+
}
336+
} else {
337+
// If not an LVM2 PV, just pass the container devicefile
338+
$fsDevs = array($df);
339+
}
289340
// If the container contains a (referenced) filesystem, then mount it
290341
// (unless this automounting is disabled by the global configuration
291342
// option, OMV_LUKS_MOUNT_ON_UNLOCK - see initialize() above)
292343
if(TRUE === $this->mountOnUnlock) {
293-
$sdluks = new OMVStorageDeviceLUKS($luks->getDecryptedDeviceFile());
294-
$df = $sdluks->getDeviceFile();
295-
if(FALSE !== OMVRpc::exec("FileSystemMgmt", "hasFilesystem",
296-
array("devicefile" => $df), $context)) {
297-
if(FALSE !== ($meObject = OMVRpc::exec("FsTab", "getByFsName",
298-
array("id" => $df), $context))) {
299-
switch (strtolower($meObject['type'])) {
300-
case "btrfs":
301-
/**
302-
* Check if the unlocked device is part of a multi-
303-
* device BTRFS filesystem, and don't attempt to mount
304-
* it if it's not ready (not all devices are available,
305-
* e.g. if more containers must be unlocked first, wait
306-
* until they are all open).
307-
* Note: using 'btrfs device ready' only works for the
308-
* first time devices are opened - even if LUKS devices
309-
* that are part of a multi-device BTRFS filesystem are
310-
* later closed, some information is cached and so btrfs
311-
* subsequently always reports the filesystem as ready.
312-
* Hence, this workaround checks the number of devices
313-
* online for the filesystem via 'btrfs filesystem show'
314-
* instead, which should be more reliable.
315-
* TODO: submit a patch to the core BTRFS backend that
316-
* exposes this in a class function instead?
317-
*/
318-
// Find out how many devices are in the filesystem
319-
$cmd = sprintf("export LANG=C; btrfs filesystem ".
320-
"show %s | grep 'Total devices' | ".
321-
"awk '{print $3}'",
322-
escapeshellarg($meObject['uuid']));
323-
@OMVUtil::exec($cmd, $output, $result);
324-
if($result !== 0) {
325-
$this->setLastError($output);
326-
continue;
327-
}
328-
$totalDevices = (int)$output[0];
329-
// If the fs has fewer than one device (an error
330-
// of some kind occurred), skip mounting
331-
if($totalDevices < 1)
332-
continue;
333-
unset($cmd, $output, $result);
334-
// Find out how many are online
335-
$cmd = sprintf("export LANG=C; btrfs filesystem ".
336-
"show %s | grep 'devid' | wc -l",
337-
escapeshellarg($meObject['uuid']));
338-
@OMVUtil::exec($cmd, $output, $result);
339-
if($result !== 0) {
340-
$this->setLastError($output);
341-
continue;
342-
}
343-
$availableDevices = (int)$output[0];
344-
// If not all devices are online, skip mounting
345-
if($availableDevices !== $totalDevices)
346-
continue;
347-
unset($cmd, $output, $result);
348-
case "ext2":
349-
case "ext3":
350-
case "ext4":
351-
case "jfs":
352-
case "xfs":
353-
case "hfsplus":
354-
case "reiserfs":
355-
case "iso9660":
356-
case "udf":
357-
case "vfat":
358-
case "ntfs":
359-
default:
360-
OMVRpc::exec("FileSystemMgmt", "mount",
361-
array(
362-
"id" => $meObject['fsname'],
363-
"fstab" => FALSE
364-
),
365-
$context);
344+
foreach ($fsDevs as $dev) {
345+
$this->mountContainerFS($dev, $context);
346+
}
347+
}
348+
}
349+
350+
/**
351+
* Helper function for mounting filesystems inside containers on unlocking.
352+
* @param devicefile The decrypted block special device of the
353+
* filesystem (inside the LUKS container) to mount.
354+
* @param context The context of the caller.
355+
* @return None.
356+
*/
357+
private function mountContainerFS($deviceFile, $context) {
358+
if(FALSE !== OMVRpc::exec("FileSystemMgmt", "hasFilesystem",
359+
array("devicefile" => $deviceFile), $context)) {
360+
if(FALSE !== ($meObject = OMVRpc::exec("FsTab", "getByFsName",
361+
array("id" => $deviceFile), $context))) {
362+
switch (strtolower($meObject['type'])) {
363+
case "btrfs":
364+
/**
365+
* Check if the unlocked device is part of a multi-
366+
* device BTRFS filesystem, and don't attempt to mount
367+
* it if it's not ready (not all devices are available,
368+
* e.g. if more containers must be unlocked first, wait
369+
* until they are all open).
370+
* Note: using 'btrfs device ready' only works for the
371+
* first time devices are opened - even if LUKS devices
372+
* that are part of a multi-device BTRFS filesystem are
373+
* later closed, some information is cached and so btrfs
374+
* subsequently always reports the filesystem as ready.
375+
* Hence, this workaround checks the number of devices
376+
* online for the filesystem via 'btrfs filesystem show'
377+
* instead, which should be more reliable.
378+
* TODO: submit a patch to the core BTRFS backend that
379+
* exposes this in a class function instead?
380+
*/
381+
// Find out how many devices are in the filesystem
382+
$cmd = sprintf("export LANG=C; btrfs filesystem ".
383+
"show %s | grep 'Total devices' | ".
384+
"awk '{print $3}'",
385+
escapeshellarg($meObject['uuid']));
386+
@OMVUtil::exec($cmd, $output, $result);
387+
if($result !== 0) {
388+
$this->setLastError($output);
389+
continue;
390+
}
391+
$totalDevices = (int)$output[0];
392+
// If the fs has fewer than one device (an error
393+
// of some kind occurred), skip mounting
394+
if($totalDevices < 1)
395+
continue;
396+
unset($cmd, $output, $result);
397+
// Find out how many are online
398+
$cmd = sprintf("export LANG=C; btrfs filesystem ".
399+
"show %s | grep 'devid' | wc -l",
400+
escapeshellarg($meObject['uuid']));
401+
@OMVUtil::exec($cmd, $output, $result);
402+
if($result !== 0) {
403+
$this->setLastError($output);
404+
continue;
366405
}
406+
$availableDevices = (int)$output[0];
407+
// If not all devices are online, skip mounting
408+
if($availableDevices !== $totalDevices)
409+
continue;
410+
unset($cmd, $output, $result);
411+
case "ext2":
412+
case "ext3":
413+
case "ext4":
414+
case "jfs":
415+
case "xfs":
416+
case "hfsplus":
417+
case "reiserfs":
418+
case "iso9660":
419+
case "udf":
420+
case "vfat":
421+
case "ntfs":
422+
default:
423+
OMVRpc::exec("FileSystemMgmt", "mount",
424+
array(
425+
"id" => $meObject['fsname'],
426+
"fstab" => FALSE
427+
),
428+
$context);
367429
}
368430
}
369431
}
@@ -482,14 +544,14 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
482544
}
483545
// Create the container.
484546
$luks = new OMVLuksContainer($sd->getDeviceFile());
485-
if(isset($params['keyfile']) && !empty($params['keyfile']))
486-
$success = $luks->create($sd->getDeviceFile(),
487-
$params['keyfile'],
488-
TRUE);
489-
else
490-
$success = $luks->create($sd->getDeviceFile(),
491-
$params['passphrase']);
492-
if ($success === FALSE) {
547+
if(isset($params['keyfile']) && !empty($params['keyfile'])) {
548+
$key = $params['keyfile'];
549+
$keyIsFile = TRUE;
550+
} else {
551+
$key = $params['passphrase'];
552+
$keyIsFile = FALSE;
553+
}
554+
if ($luks->create($key, $keyIsFile) === FALSE) {
493555
throw new OMVException(OMVErrorMsg::E_EXEC_MISC, sprintf(
494556
gettext("Unable to create the encrypted device: %s"),
495557
$luks->getLastError()));
@@ -747,11 +809,14 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
747809
$params['devicefile']));
748810
}
749811
// Remove the key
750-
if(isset($params['keyfile']) && !empty($params['keyfile']))
751-
$success = $luks->removeKey($params['keyfile'], TRUE);
752-
else
753-
$success = $luks->removeKey($params['passphrase']);
754-
if ($success === FALSE) {
812+
if(isset($params['keyfile']) && !empty($params['keyfile'])) {
813+
$key = $params['keyfile'];
814+
$keyIsFile = TRUE;
815+
} else {
816+
$key = $params['passphrase'];
817+
$keyIsFile = FALSE;
818+
}
819+
if ($luks->removeKey($key, $keyIsFile) === FALSE) {
755820
throw new OMVException(OMVErrorMsg::E_EXEC_MISC,
756821
sprintf(gettext("Unable to remove the key from the encrypted device: %s"),
757822
$luks->getLastError()));
@@ -871,12 +936,12 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
871936
"devicefile":{'.$GLOBALS['OMV_JSONSCHEMA_DEVICEFILE'].'}
872937
}
873938
}');
874-
// Check if container exists.
939+
// Validate the container
875940
$luks = new OMVLuksContainer($params['devicefile']);
876-
if (FALSE === $luks->exists()) {
941+
if (is_null($luks) || !$luks->exists()) {
877942
throw new OMVException(OMVErrorMsg::E_MISC_FAILURE,
878-
sprintf(gettext("No encryption found on '%s'"),
879-
$params['devicefile']));
943+
sprintf(gettext("LUKS container on '%s' not found"),
944+
$params['devicefile']));
880945
}
881946
// Extract the header to a temporary location and modify the
882947
// file mode/owner to allow the WebGUI PHP backend to unlink it.
@@ -892,9 +957,19 @@ class OMVRpcServiceLuksMgmt extends OMVRpcServiceAbstract {
892957
chmod($tmpFilePath, 0600);
893958
chgrp($tmpFilePath, $GLOBALS['OMV_WEBGUI_FILE_OWNERGROUP_NAME']);
894959
chown($tmpFilePath, $GLOBALS['OMV_WEBGUI_FILE_OWNERGROUP_NAME']);
960+
// Build filename for file
961+
$fn = preg_replace('/\s+/',
962+
'-',
963+
array(
964+
$luks->getVendor(),
965+
$luks->getModel(),
966+
$luks->getSerialNumber(),
967+
$luks->getUuid()
968+
)
969+
);
895970
// Return values required by generic download RPC implementation.
896971
return array(
897-
"filename" => "LUKS_header_".$luks->getUuid().".bak",
972+
"filename" => "LUKS_header_".implode('_', $fn).".bak",
898973
"filepath" => $tmpFilePath,
899974
"contenttype" => "application/octet-stream",
900975
"unlink" => TRUE

0 commit comments

Comments
 (0)