Issue #12 October 2005

Adding encryption support to HAL: A user's experience with Fedora™ development

Introduction

I was first introduced to the ideas behind D-BUS and the Hardware Abstraction Layer (HAL) during a presentation Havoc Pennington gave at the Free and Open Source Developers' European Meeting 2003. During his lecture, Havoc laid out the goals of his new D-BUS project. He also talked about the concept of what would become HAL. At the time, my experiences with Linux® hardware interfaces were limited to working with the Linux CD-ROM interface and other simple ioctl system calls. I did not realize the true value of HAL until I began seeing software that took advantage of the system. Built around the D-BUS and HAL technologies, recent GNOME distributions are able to interact very well with modern, dynamic hardware. NetworkManager, gnome-volume-manager, and hal-cups-utils have all been developed with HAL and under the mantra of making hardware "Just Work."

This article introduces the reader to how HAL may be extended to support new device types. Specifically, it discusses how HAL was modified to make it easy to use removable, encrypted devices from within the GNOME environment. An understanding of HAL's architecture and its usefulness should be the result. As a non-Red Hat employee, I demonstrate the willingness of the company to support volunteer development within the Fedora Project. HAL's source code may be found at http://cvs.freedesktop.org/hal/hal/ and is an important companion to this article.

The Hardware Abstraction Layer provides software developers with a consistent view of the devices attached to a system. The core of the system is a daemon that watches for new hardware and may be queried to determine the properties of a system's devices. Device properties are simply key/value pairs. David Zeuthen's Desktop and hardware configuration article provides a general overview of the HAL system. Get on D-BUS by John Palmieri introduces one to D-BUS.

On Linux, HAL ties into the hotplug system. Most communication with the HAL daemon is performed using the D-BUS API it provides. Policies exist that determine what actions the HAL daemon will perform when a given device is attached to the system. For example, a HAL device property with the key info.callouts.add may be included in a HAL policy. This property's value is the path to a script that is executed when the device is detected.

David Zeuthen, the primary maintainer of HAL, made a proposal for an encrypted disk system in December 2004. In his proposal, David outlined an addition to HAL that would detect an encrypted disk, prompt the user for a password, and then use that password to unlock the encrypted file system. The system would be built around dm-crypt (See sidebar), the encrypted disk mechanism that is supported by the Fedora Project. A diagram demonstrating David's architecture may be found in Figure 1, “HAL crypto architecture”.

HAL crypto architecture
Figure 1. HAL crypto architecture

After studying David's architecture, I decided that I had the experience necessary to help implement the system. I approached David on the HAL mailing list, and he accepted my offer of help. David was very willing to assist me as I became familiar with the HAL codebase.

David's original proposal called for the development of a system that stores encryption parameters on disk before the file system. This header could then be read by HAL so that HAL could decrypt the disk. David's proposal named this system Sesame. After some research, David and I identified the Linux Unified Key Setup (LUKS) as a candidate to replace Sesame. LUKS (see sidebar) stores encryption parameters, such as the cipher name and mode, as a plaintext header before an encrypted file system. After some experimentation, we decided that LUKS would be adequate and stopped work on Sesame.

HAL recognizes encrypted device

Once the LUKS header format was chosen, we were ready to start modifying HAL. When a removable disk is attached to a Linux system, the kernel identifies it and notifies the HAL daemon. To identify an encrypted disk, the HAL daemon must be able to recognize its LUKS header. This was the first modification to HAL that was required. The gnome-luks-format utility formats an encrypted file system with a LUKS header so that HAL will recognize it. Refer to Figure 2, “gnome-luks-format utility”.

gnome-luks-format utility
Figure 2. gnome-luks-format utility

The most important addition to HAL was a function named volume_id_probe_luks() that is implemented in a new source file named luks.c. This function looks at a volume and determines if it contains a LUKS header. If such a header is found, the HAL daemon will set the HAL daemon's usage variable to VOLUME_ID_CRYPTO, set the volume.uuid property to the UUID read from the LUKS header, and set its volume.fstype to crypto_LUKS. A similar change was necessary in probe_volume.c. The set_volume_id_values() function was modified to set the volume.fsusage property to crypto. Figure 3, “View of an encrypted volume” shows hal-device-manager displaying the properties of an encrypted volume.

HAL required a few other minor modifications so that encrypted volumes could be identified. These were additions to various utility functions.

View of an encrypted volume
Figure 3. View of an encrypted volume

gnome-volume-manager reacts to new encrypted device

Now that the HAL daemon has the ability to identify a volume as a LUKS device, it will publish the device's information using D-BUS. However, before the device may be decrypted, a digital key must be obtained. I chose to modify gnome-volume-manager, a daemon that performs user-level actions on newly connected devices, to perform this work.

As mentioned before, when the HAL daemon detects a new device, it broadcasts the event using D-BUS. This message is picked up by gnome-volume-manager, and eventually the process examines the device's properties. The gnome-volume-manager process was modified to look at the volume.fsusage property of the newly attached device. If the value of this property is crypto, then the device contains an encrypted file system, and gnome-volume-manager will prompt the user for a passphrase.

Once gnome-volume-manager has obtained a valid passphrase, it must ensure that the dm-crypt device is properly set up. Because gnome-volume-manager generally runs with a user's privileges, it cannot perform this action directly. Instead, gnome-volume-manager forwards the passphrase back to the HAL daemon, which normally runs with root privileges, and asks the HAL daemon to set up the dm-crypt device. gnome-volume-manager does this using the HAL daemon's new methods interface.

Once the user provides a passphrase, gnome-volume-manager sends a D-BUS message to the HAL daemon, invoking the HAL daemon's Setup method. The message sent is equivalent to executing dbus-send --system --print-reply --dest="org.freedesktop.Hal" <UDI> org.freedesktop.Hal.Device.Volume.Crypto.Setup string:<PASSWORD>.

The first version of HAL's method interface was announced by David Zeuthen in July of 2005. For the HAL daemon to provide a method such as Setup, two things are required. First, a device information file should define the interface for the method. Second, a script must implement the method. The HAL daemon will invoke the script when called upon to perform the corresponding method.

Device information files belong in a directory such as /usr/share/hal/fdi/. The device information file that defines the Setup interface can be found in Example 1, “Contents of 15-storage-luks.fdi”.

<?xml version="1.0" encoding="ISO-8859-1"?> <!-- -*- SGML -*- -->

<deviceinfo version="0.2">
	<device>
		<match key="volume.fsusage" string="crypto">
			<match key="volume.fstype" string="crypto_LUKS">
				<append key="info.interfaces" type="strlist">org.freedesktop.Hal.Device.Volume.Crypto</append>
				<append key="org.freedesktop.Hal.Device.Volume.Crypto.method_names" type="strlist">Setup</append>
				<append key="org.freedesktop.Hal.Device.Volume.Crypto.method_signatures" type="strlist">s</append>
				<append key="org.freedesktop.Hal.Device.Volume.Crypto.method_execpaths" type="strlist">hal-luks-setup</append>
			</match>
		</match>
	</device>
</deviceinfo>
			
Example 1. Contents of 15-storage-luks.fdi

The implementation scripts are installed at the path listed in the same device information file that defines their interface. HAL properties are provided to the script as environment variables. The method's parameters are read from stdin. The script named hal-luks-setup implements the Setup interface and can be found in Example 2, “Contents of hal-luks-setup”.

read password

LUKSSETUP=@SBINDIR@/luks-setup
HALSETPROPERTY=@BINDIR@/hal-set-property

if [ ! -f $LUKSSETUP ]; then
	echo org.freedesktop.Hal.Device.Volume.Crypto.SetupError <&2
	echo Error setting up $HAL_PROP_BLOCK_DEVICE - $LUKSSETUP not found <&2
	exit 1
fi

if ! echo $password | $LUKSSETUP $HAL_PROP_BLOCK_DEVICE 2< /dev/null; then
	echo org.freedesktop.Hal.Device.Volume.Crypto.SetupError <&2
	echo Error setting up $HAL_PROP_BLOCK_DEVICE - bad password? <&2
	exit 1
fi

if ! $HALSETPROPERTY --udi="$HAL_PROP_INFO_UDI" --key="info.callouts.remove" --strlist-pre="hal-luks-remove" 2< /dev/null; then
	echo org.freedesktop.Hal.Device.Volume.Crypto.SetupError <&2
	echo Error setting info.callouts.remove <&2
	exit 1
fi

exit 0
			
Example 2. Contents of hal-luks-setup

It may seem odd that HAL reads properties from environment variables and parameters from stdin. David developed HAL's method interface while we were working on HAL's encryption support. For security reasons, parameters are read from stdin instead of the environment. On some flavors of UNIX®, users may view the environment associated with any process. Therefore, it is not safe to pass a passphrase between processes using environment variables. The stdin buffer provides a secure alternative.

After the HAL daemon receives a passphrase from gnome-volume-manager, the process executes hal-luks-setup, providing HAL properties in the script's environment and writing the passphrase to the script's stdin buffer. The hal-luks-setup script also adds the string hal-luks-remove to the device's info.callouts.remove property. This ensures that the dm-crypt device is properly removed when the device is disconnected.

HAL recognizes dm-crypt device

Once gnome-volume-manager and the HAL daemon are able to work together to set up a dm-crypt device, only two issues remain. First, dm-crypt devices are not like normal devices because it is unclear what their parent device is. A USB drive's parent device is the USB bus. On Linux, this relationship is made clear by sysfs. Less information is provided for dm-crypt devices, and the HAL daemon must be modified to discover some relationships on its own. Second, when the HAL daemon process starts up, it must ensure that it processes previously existing device mapper devices after all of the system's block devices have been processed. If this ordering is not enforced, the HAL daemon will not be able to identify key relationships when it starts. This process is called cold-plugging and required the modification of coldplug_compute_visit_device() in coldplug.c.

Modifications to blockdev.c allow the HAL daemon to discover the relationship necessary to properly identify dm-crypt devices. The blockdev_get_luks_uuid() function is a utility that determines the UUID for the backing device of a dm-crypt device. Given a device file such as dm-0, the function looks for the corresponding device in /dev/mapper. When luks-setup creates a device in /dev/mapper, it names it using the convention luks_crypto_12345678-9012-3456-7890-123456789012. Once the appropriate device is found, it is trivial to read the LUKS UUID from its filename. The blockdev_get_luks_parent() function now identifies a dm-crypt device's parent by looking for a volume whose volume.uuid property matches the UUID returned by blockdev_get_luks_uuid(). This volume is the dm-crypt device's backing volume and parent. The property volume.crypto_luks.clear.backing_volume is set to this parent device. Figure 4, “View of a dm-crypt device” shows the properties associated with a dm-crypt device that has been unlocked.

View of a dm-crypt device
Figure 4. View of a dm-crypt device

Conclusion

HAL provides a very robust system for managing devices on a modern computer. The system provides a clear way of notifying applications of the presence of new hardware. Along with D-BUS and the Linux hotplug system, HAL allows for a central location from which to define the policies that determine how newly attached devices are handled. HAL's new methods interface increases the system's flexibility drastically. Furthermore, the developers behind HAL, especially David Zeuthen, are very willing to provide guidance and help to individuals that wish to extend HAL. HAL clearly deserves its place at the center of many modern desktops and does much to accomplish the goal of making hardware "Just Work."

About the author

Mike Petullo is currently serving with the U.S. Army at Ft. Sheridan, Illinois. He is also working towards a M.S. in Computer Science at DePaul University. In the little spare time he has, he enjoys tinkering with Fedora and tending to his lawn.